작품 뷰어 세부 정책 문서
(2026년 3월 11일 updated)
1. 목적
도서를 읽을 수 있는 화면을 뷰어라고 칭함. 사용자가 다양한 언어로 그림책을 열람하고, 뷰어 내에서 밝기 조절, 자동 스크롤, 녹음, 오디오 재생 등의 기능을 활용할 수 있도록, 작품 뷰어의 UI 구성 및 인터랙션 원칙을 정의한다.
2. 뷰어 아키텍처
2.1 ViewerBinding 구조
뷰어는 ViewerBinding 패턴을 통해 초기화되며, 각 서비스가 독립적으로 관리된다. bookId와 episodeId 조합으로 고유 태그를 생성하여 다중 뷰어 인스턴스를 지원한다.
| 서비스 | 역할 |
|---|---|
| ViewerDataService | 도서/에피소드 데이터 로딩 및 관리 |
| ViewerScrollManager | 스크롤 위치 관리 및 진행률 계산 |
| ViewerUIStateManager | UI 상태 관리 (메뉴 표시/숨김, 터치 이벤트 처리) |
| ViewerSettingsManager | 뷰어 설정 관리 (밝기, 자동 스크롤, 언어 등) |
| EpisodeRewardManager | 에피소드 완독 시 스티커 리워드 처리 |
| SubscriptionGateManager | 비구독자 콘텐츠 접근 제한 관리 |
2.2 서비스 생명주기
- 각 서비스는 뷰어 진입 시
Get.lazyPut()으로 등록되며, bookId-episodeId 태그로 고유 인스턴스 보장 - 뷰어 종료 시 자동 해제 (
fenix: false) SubscriptionGateManager는ViewerController와 연결되어 구독 상태에 따른 게이트 동작을 제어
3. 기본 구조
-
뷰어 형식: 세로 스크롤 기반
-
초기 언어 설정:
- 구독자 및 비구독자 모두 처음으로 작품 뷰어에 진입할 때 독서 언어 설정 모달이 노출됨
- 설정한 언어는 앱 전체에 공통 적용되며, 이후 작품에서도 그대로 유지됨
- 언어 변경은 뷰어 내 [설정] 팝업 또는 [마이 > 앱 언어 설정]에서 가능함
-
언어 스와이프 구조:
- 설정된 언어 간에는 스와이프 제스처로 전환 가능함
- 화면 중앙: 주언어 (주언어 기본 or 유저가 가운데 카드에 설정한 언어)
- 왼쪽/오른쪽: 부언어 1~2개 (작품이 해당 언어를 지원할 경우에만)
- 제공되지 않는 언어는 dim 처리
- 언어 재설정은 하단 설정 메뉴나 마이의 뷰어 설정에서 가능함.
-
언어 상태 표시:
- 언어를 2개 이상 설정한 사용자가 뷰어에 진입할 경우, 설정된 언어 목록을 안내하는 토스트 메시지가 노출됩니다.
4. 구독 게이트 정책
비구독자 또는 비로그인 사용자가 콘텐츠를 열람할 때, 읽기 진행률이 95% 이상에 도달하면 구독 유도 처리가 적용된다.
4.1 그라데이션 오버레이
- 발동 조건: 비구독자/비로그인 상태에서
readingProgress >= 0.95이고 하단 내비게이션이 표시 중일 때 - 시각 효과: 하단 내비게이션 위에 그라데이션 오버레이 표시
- 상단 → 하단 방향의 Linear Gradient
- 상단 2%: 완전 투명 (rgba(255,255,255,0))
- 하단 73%: 완전 불투명 (rgba(255,255,255,1))
- 높이: 600px
- 터치 이벤트: 오버레이 자체는 터치 이벤트를 통과시킴 (
IgnorePointer)
4.2 하단 내비게이션 (비구독자)
비구독자에게도 구독자와 동일한 하단 메뉴 버튼을 제공하며, 구독 유도는 개별 기능 접근 시 처리된다.
5. 상단 영역 구성
-
노출 방식: 스크롤과 무관하게 상단 고정 표시, 상/하단 15% 위치 탭하면 상/하 영역 동시 노출.
-
구성:
- 작품 상세로 가는 x 버튼
- 작품명
- 찜하기 버튼
6. 하단 영역 구성
-
노출 방식:
- 상단과 동일하게 화면 하단에 고정된 형태로 노출됨
- 스크롤과 무관하게 하단 고정 표시, 상/하단 15% 위치 탭하면 상/하 영역 동시 노출.
-
구성:
-
진행률 표시 바: 현재 읽기 진행률을 퍼센트(%)와 프로그레스 바로 표시. 드래그로 위치 이동 가능.
-
뷰어 설정 버튼: 클릭 시 2뎁스로 진입하며, 2뎁스는 창 크기가 변함
- 구성 항목:
- 자동 스크롤 ON/OFF 토글: ON 시 뷰어 우측에 속도 조절 버튼 노출
- 밝기 조절: 수평 슬라이더로 밝기 조정
- 구성 항목:
-
읽기/듣기 모드 토글 버튼: 읽기 모드와 듣기 모드 간 전환 (슬라이딩 애니메이션)
- 읽기 모드: 기본 읽기 UI
- 듣기 모드: 오디오 플레이리스트 다이얼로그 표시
-
녹음 버튼: 클릭 시 녹음 다이얼로그 표시
-
에피소드가 있는 작품은 에피소드 드롭다운 표시 (이전 화/다음 화 이동)
-
-
위 메뉴들은 모두 하단 고정형 1뎁스 UI에서 진입하며, 각 항목 클릭 시 개별 2뎁스 화면으로 전환됨
7. 오디오/녹음/듣기 다이얼로그
뷰어 내에서 오버레이 형태로 표시되는 다이얼로그는 다음 3가지이며, 동시에 하나만 표시된다.
7.1 오디오 플레이리스트 다이얼로그 (AudioPlaylistDialog)
- 진입: 듣기 모드 전환 시 표시
- 구성: 오디오 트랙 목록, 재생 컨트롤, 플레이어 뷰
- 배경: 반투명 딤 오버레이 (하단 내비게이션 영역 제외)
- 닫기: 딤 영역 탭 시 읽기 모드로 전환 또는 듣기 모드 진입
7.2 녹음 다이얼로그 (RecordDialog)
- 진입: 녹음 버튼 클릭 시 표시
- 구성: 녹음/일시정지/재생/완료/나가기 버튼, 녹음 시간 표시, 파형 애니메이션
- 제한: 녹음 모드에서는 전역 탭 오버레이 비활성화 (다이얼로그 버튼 우선)
- 녹음 중 뷰어 제한: 세로 스크롤만 허용, 언어 전환 및 기타 설정 비활성화
7.3 듣기 다이얼로그 (AudioListeningDialog)
- 진입: 플레이리스트에서 플레이어 뷰 활성화 후 딤 영역 탭 시
- 구성: 미니 플레이어 형태로 오디오 재생 상태 유지
- 동작: 듣기 모드 진입 시 자동 표시
7.4 뷰어 설정 다이얼로그 (ViewerSettingDialog)
- 진입: 설정 버튼 클릭 시 표시
- 구성: 자동 스크롤 토글, 밝기 조절 슬라이더
- 배경: 반투명 딤 오버레이 (전체 화면)
- 위치: 화면 하단에 고정
8. 녹음 모드
-
진입 조건: 하단 녹음 버튼 클릭 시 (로그인/구독 상태는 컨트롤러에서 확인)
-
화면 변경: 기존 하단 설정이 녹음 전용 상태창으로 전환 (RecordDialog)
-
상태창 구성:
- 녹음 / 일시정지 / 재생 / 녹음 완료 / 나가기(X) 버튼
- 녹음 진행 시간 표시 (HH:MM)
- 녹음 볼륨 및 진행상황 시각화 표시: 파형 애니메이션
- 볼륨은 따로 두지 않고 적정 볼륨을 자동으로 설정하여 녹음함
-
뷰어 제한: 세로 스크롤만 허용, 언어 전환 및 기타 설정은 비활성화
-
오디오 처리: FFmpeg를 사용한 오디오 트리밍 및 노이즈 미터 지원
-
저장 방식: 서버 업로드 방식으로 구현 완료 (S3 스토리지).
Recording모델을 통해 서버에 저장되며, 사용자/도서/에피소드/언어별로 관리된다. -
Recording 모델 필드:
필드 타입 설명 userForeignKey 녹음한 사용자 bookForeignKey 대상 도서 episodeForeignKey (nullable) 대상 에피소드 language_codeCharField 녹음 언어 file_urlURLField 녹음 파일 URL (S3) durationIntegerField 녹음 길이 (초) -
저장 후: 안내 문구 출력 후 상태창이 플레이어 모드로 전환
-
안내 문구 예시: "000님만의 오디오북이 완성되었습니다! 내 계정 > 나만의 오디오북에 저장 되었어요."
-
저장된 녹음 관리: "내 계정 > 내가 녹음한 책"에서 조회/삭제 가능
9. 언어 미제공 시 예외 처리
- 사용자가 설정한 언어가 해당 작품에서 제공되지 않는 경우:
- 토스트: "이 작품은 00어를 제공하지 않습니다."
- 초기 화면은 우선순위에 따라 우측 언어 → 좌측 언어 순으로 자동 지정 (제공되지 않는 언어는 dimmed 처리)
10. 기타 UI/UX 정책
-
탭 전환 애니메이션: 부드러운 스와이프 전환 효과
-
상단/하단 영역 자동 숨김:
- 뷰어 첫 진입시 상 하단 영역 노출
- 첫 진입 후 10초간 아무 동작없거나 / 스크롤, 스와이프시 상하단영역 숨김
- UI가 숨겨진 상태에서는 화면 상단/하단 15% 영역을 터치했을 때 상단/하단 UI가 다시 노출됨
- 전체 화면 아무 곳이나 터치 시에는 동작하지 않음 (아이들 몰입 방해 방지 목적)
- 99% 이상 읽기 진행 시 터치 오버레이 비활성화 (하단 버튼 클릭 가능하도록)
-
읽기 진행률 자동 저장:
- 앱이 백그라운드로 전환될 때 자동 저장
- 뷰어 닫기 시 마지막 스크롤 지점, 언어 상태, 밝기 설정 등 자동 저장
-
태블릿/데스크톱 대응:
- 넓은 화면에서는 콘텐츠 영역 너비 제한 적용 (
ViewerLayoutConstants.contentWidth) - 하단 내비게이션 및 다이얼로그 모두 중앙 정렬로 표시
- 넓은 화면에서는 콘텐츠 영역 너비 제한 적용 (