본문으로 건너뛰기

펜타 서비스 데이터베이스 스키마 목표 설계서

개요

이 문서는 펜타 서비스의 모든 정책 문서를 기반으로 설계된 완전한 데이터베이스 스키마 목표를 정의합니다. 단순화 버전에서 시작하여 점진적으로 확장 가능한 구조로 설계되었습니다.


1. 사용자 도메인

users (사용자)

컬럼명타입설명제약조건
user_idBIGINT사용자 고유 식별자PK, AUTO_INCREMENT
emailVARCHAR(255)이메일 주소UNIQUE, NOT NULL
nicknameVARCHAR(100)사용자 닉네임NOT NULL
profile_image_urlVARCHAR(500)프로필 이미지 URLNULL 가능
app_languageVARCHAR(10)앱 설정 언어 (ko, en, ja, es, zh)DEFAULT 'ko'
reading_languageVARCHAR(10)독서 언어 설정DEFAULT 'ko'
device_osVARCHAR(50)기기 OS 정보NULL 가능
device_modelVARCHAR(100)기기 모델 정보NULL 가능
app_versionVARCHAR(20)앱 버전NULL 가능
country_codeVARCHAR(2)접속 국가 코드NULL 가능
created_atTIMESTAMP가입일시DEFAULT CURRENT_TIMESTAMP
updated_atTIMESTAMP수정일시ON UPDATE CURRENT_TIMESTAMP
deleted_atTIMESTAMP탈퇴일시 (30일 보관 후 완전 삭제)NULL 가능
last_login_atTIMESTAMP마지막 로그인 일시NULL 가능

user_subscriptions (구독 정보)

컬럼명타입설명제약조건
subscription_idBIGINT구독 고유 식별자PK, AUTO_INCREMENT
user_idBIGINT사용자 IDFK → users.user_id
subscription_typeENUM구독 유형 ('monthly', '6months', 'yearly')NOT NULL
statusENUM구독 상태 ('active', 'cancelled', 'expired', 'pending')NOT NULL
priceDECIMAL(10,2)결제 금액NOT NULL
currencyVARCHAR(3)통화 (KRW, JPY, USD, EUR, CNY)NOT NULL
start_dateDATE구독 시작일NOT NULL
end_dateDATE구독 종료 예정일NOT NULL
auto_renewalBOOLEAN자동 갱신 여부DEFAULT TRUE
payment_methodVARCHAR(50)결제 방식 (app_store, google_play, web)NOT NULL
promo_codeVARCHAR(50)사용한 프로모션 코드NULL 가능
created_atTIMESTAMP생성일시DEFAULT CURRENT_TIMESTAMP

user_settings (사용자 설정)

컬럼명타입설명제약조건
user_idBIGINT사용자 IDPK, FK → users.user_id
auto_scroll_enabledBOOLEAN자동 스크롤 사용 여부DEFAULT FALSE
auto_scroll_speedINT자동 스크롤 속도 (1-10)DEFAULT 5
brightness_levelINT밝기 레벨 (0-100)DEFAULT 50
notification_enabledBOOLEAN알림 수신 여부DEFAULT TRUE
child_ageINT자녀 연령 설정NULL 가능
viewer_font_sizeINT뷰어 폰트 크기DEFAULT 16
bgm_enabledBOOLEANBGM 재생 여부DEFAULT TRUE
bgm_volumeINTBGM 볼륨 (0-100)DEFAULT 50
updated_atTIMESTAMP수정일시ON UPDATE CURRENT_TIMESTAMP

user_devices (사용자 기기)

컬럼명타입설명제약조건
device_idBIGINT기기 고유 식별자PK, AUTO_INCREMENT
user_idBIGINT사용자 IDFK → users.user_id
device_uuidVARCHAR(255)기기 UUIDUNIQUE, NOT NULL
device_nameVARCHAR(100)기기명NULL 가능
last_active_atTIMESTAMP마지막 활성 시간DEFAULT CURRENT_TIMESTAMP
is_activeBOOLEAN활성 상태DEFAULT TRUE

2. 콘텐츠 도메인

books (도서)

컬럼명타입설명제약조건
book_idBIGINT도서 고유 식별자PK, AUTO_INCREMENT
titleVARCHAR(500)도서 제목NOT NULL
authorVARCHAR(200)글 작가NULL 가능
illustratorVARCHAR(200)그림 작가NULL 가능
publisherVARCHAR(100)출판사 (disney, pixar)NOT NULL
synopsisTEXT줄거리NULL 가능
age_range_minINT최소 연령NOT NULL
age_range_maxINT최대 연령NOT NULL
lexile_score_minINT렉사일 지수 최소값NULL 가능
lexile_score_maxINT렉사일 지수 최대값NULL 가능
lexile_typeENUM렉사일 유형 ('estimated', 'official')NULL 가능
page_countINT총 페이지 수NOT NULL
has_bgmBOOLEANBGM 포함 여부DEFAULT FALSE
has_ostBOOLEANOST 포함 여부DEFAULT FALSE
has_narrationBOOLEAN내레이션 포함 여부DEFAULT FALSE
published_dateDATE서비스 공개일NOT NULL
metadataJSON추가 메타데이터NULL 가능
created_atTIMESTAMP생성일시DEFAULT CURRENT_TIMESTAMP
updated_atTIMESTAMP수정일시ON UPDATE CURRENT_TIMESTAMP

book_languages (도서 지원 언어)

컬럼명타입설명제약조건
book_language_idBIGINT고유 식별자PK, AUTO_INCREMENT
book_idBIGINT도서 IDFK → books.book_id
language_codeVARCHAR(10)언어 코드 (ko, en, ja, es, zh)NOT NULL
title_translatedVARCHAR(500)번역된 제목NOT NULL
cover_image_urlVARCHAR(500)언어별 표지 이미지 URLNOT NULL
content_urlVARCHAR(500)언어별 콘텐츠 URLNOT NULL
narration_urlVARCHAR(500)내레이션 URLNULL 가능
is_availableBOOLEAN서비스 가능 여부DEFAULT TRUE

book_series (시리즈)

컬럼명타입설명제약조건
series_idBIGINT시리즈 고유 식별자PK, AUTO_INCREMENT
series_nameVARCHAR(200)시리즈명NOT NULL
series_typeENUM시리즈 유형 ('franchise', 'collection')NOT NULL
descriptionTEXT시리즈 설명NULL 가능
display_orderINT노출 순서DEFAULT 0
created_atTIMESTAMP생성일시DEFAULT CURRENT_TIMESTAMP

book_series_mapping (도서-시리즈 매핑)

컬럼명타입설명제약조건
book_idBIGINT도서 IDPK, FK → books.book_id
series_idBIGINT시리즈 IDPK, FK → book_series.series_id
order_in_seriesINT시리즈 내 순서DEFAULT 0

episodes (에피소드)

컬럼명타입설명제약조건
episode_idBIGINT에피소드 고유 식별자PK, AUTO_INCREMENT
book_idBIGINT도서 IDFK → books.book_id
episode_numberINT회차 번호NOT NULL
episode_titleVARCHAR(500)에피소드 제목NOT NULL
thumbnail_urlVARCHAR(500)에피소드 썸네일 URLNULL 가능
created_atTIMESTAMP생성일시DEFAULT CURRENT_TIMESTAMP

book_tags (도서 태그)

컬럼명타입설명제약조건
tag_idBIGINT태그 고유 식별자PK, AUTO_INCREMENT
tag_nameVARCHAR(100)태그명NOT NULL, UNIQUE
tag_typeENUM태그 유형 ('theme', 'character', 'feature', 'category')NOT NULL
tag_translationsJSON다국어 태그명 {"ko": "...", "en": "...", "ja": "...", "es": "...", "zh": "..."}NULL 가능

book_tag_mapping (도서-태그 매핑)

컬럼명타입설명제약조건
book_idBIGINT도서 IDPK, FK → books.book_id
tag_idBIGINT태그 IDPK, FK → book_tags.tag_id

characters (캐릭터)

컬럼명타입설명제약조건
character_idBIGINT캐릭터 고유 식별자PK, AUTO_INCREMENT
character_nameVARCHAR(200)캐릭터명NOT NULL
character_typeENUM캐릭터 유형 ('main', 'supporting', 'minor')DEFAULT 'main'
descriptionTEXT캐릭터 설명NULL 가능
image_urlVARCHAR(500)캐릭터 이미지 URLNULL 가능
name_translationsJSON다국어 캐릭터명NULL 가능
display_orderINT노출 순서DEFAULT 0
created_atTIMESTAMP생성일시DEFAULT CURRENT_TIMESTAMP

book_characters (도서-캐릭터 매핑)

컬럼명타입설명제약조건
book_idBIGINT도서 IDPK, FK → books.book_id
character_idBIGINT캐릭터 IDPK, FK → characters.character_id
role_in_bookVARCHAR(100)작품 내 역할NULL 가능

categories (카테고리)

컬럼명타입설명제약조건
category_idBIGINT카테고리 고유 식별자PK, AUTO_INCREMENT
nameVARCHAR(100)카테고리명 (기본)NOT NULL
typeVARCHAR(50)카테고리 유형NOT NULL
parent_idBIGINT상위 카테고리FK → categories.category_id, NULL 가능
translationsJSON다국어 번역NULL 가능

book_categories (도서-카테고리 매핑)

컬럼명타입설명제약조건
book_idBIGINT도서 IDPK, FK → books.book_id
category_idBIGINT카테고리 IDPK, FK → categories.category_id

3. 점수 및 통계 도메인

book_scores (도서 점수)

홈 화면 및 검색의 정렬을 위한 복합 점수 시스템입니다.

컬럼명타입설명제약조건
book_idBIGINT도서 IDPK, FK → books.book_id
pv_scoreINT페이지뷰 점수DEFAULT 0
bookmark_scoreINT찜하기 점수 (count * 2)DEFAULT 0
read_50_scoreINT50% 열람 점수 (count * 2)DEFAULT 0
read_90_scoreINT90% 열람 점수 (count * 3)DEFAULT 0
repeat_view_scoreINT재열람 점수DEFAULT 0
search_click_scoreINT검색 클릭 점수 (count * 3)DEFAULT 0
total_scoreINT총 점수GENERATED ALWAYS AS (pv_score + bookmark_score + read_50_score + read_90_score + repeat_view_score + search_click_score) STORED
last_calculatedTIMESTAMP마지막 계산일시DEFAULT CURRENT_TIMESTAMP

daily_stats (일별 통계)

컬럼명타입설명제약조건
stat_dateDATE통계일PK
book_idBIGINT도서 IDPK, FK → books.book_id
country_codeVARCHAR(2)국가 코드PK
viewsINT조회수DEFAULT 0
bookmarksINT찜하기수DEFAULT 0
completionsINT완독수DEFAULT 0
read_50_percentINT50% 이상 열람수DEFAULT 0
read_90_percentINT90% 이상 열람수DEFAULT 0
repeat_viewsINT재열람수DEFAULT 0
search_clicksINT검색 클릭수DEFAULT 0
stickers_earnedINT스티커 획득수DEFAULT 0

hourly_stats (시간별 통계)

실시간 랭킹을 위한 시간별 집계 테이블입니다.

컬럼명타입설명제약조건
stat_hourDATETIME통계 시간 (YYYY-MM-DD HH:00:00)PK
book_idBIGINT도서 IDPK, FK → books.book_id
country_codeVARCHAR(2)국가 코드PK
hour_viewsINT시간별 조회수DEFAULT 0
hour_bookmarksINT시간별 찜하기수DEFAULT 0
hour_completionsINT시간별 완독수DEFAULT 0
hour_stickersINT시간별 스티커 획득수DEFAULT 0

realtime_rankings (실시간 랭킹)

컬럼명타입설명제약조건
ranking_idBIGINT랭킹 고유 식별자PK, AUTO_INCREMENT
ranking_typeENUM랭킹 유형 ('top10', 'popular_sticker')NOT NULL
book_idBIGINT도서 IDFK → books.book_id
rankINT순위NOT NULL
scoreINT점수NOT NULL
country_codeVARCHAR(2)국가 코드NOT NULL
calculated_atTIMESTAMP집계일시DEFAULT CURRENT_TIMESTAMP
INDEX idx_country_time(country_code, calculated_at DESC, rank)

4. 리워드 도메인

stickers (스티커)

컬럼명타입설명제약조건
sticker_idBIGINT스티커 고유 식별자PK, AUTO_INCREMENT
episode_idBIGINT에피소드 IDFK → episodes.episode_id, UNIQUE
nameVARCHAR(200)스티커명 (기본)NOT NULL
name_translationsJSON다국어 스티커명NULL 가능
image_urlVARCHAR(500)이미지 URLNOT NULL
created_atTIMESTAMP생성일시DEFAULT CURRENT_TIMESTAMP

user_stickers (획득 스티커)

컬럼명타입설명제약조건
user_idBIGINT사용자 IDPK, FK → users.user_id
sticker_idBIGINT스티커 IDPK, FK → stickers.sticker_id
earned_atTIMESTAMP획득일시DEFAULT CURRENT_TIMESTAMP
view_time_secondsINT뷰어 체류 시간NOT NULL

sticker_earning_logs (스티커 획득 로그)

컬럼명타입설명제약조건
log_idBIGINT로그 고유 식별자PK, AUTO_INCREMENT
user_idBIGINT사용자 IDFK → users.user_id
sticker_idBIGINT스티커 IDFK → stickers.sticker_id
book_idBIGINT도서 IDFK → books.book_id
episode_idBIGINT에피소드 IDFK → episodes.episode_id
earned_atTIMESTAMP획득일시 (UTC+9)DEFAULT CURRENT_TIMESTAMP
scroll_speedDECIMAL(10,2)스크롤 속도 (px/초)NULL 가능
total_view_timeINT총 열람 시간 (초)NOT NULL
is_validBOOLEAN정상 획득 여부DEFAULT TRUE
country_codeVARCHAR(2)사용자 국가 코드NOT NULL
INDEX idx_earned_date(earned_at, sticker_id)
INDEX idx_user_earned(user_id, earned_at)
컬럼명타입설명제약조건
sticker_idBIGINT스티커 IDPK, FK → stickers.sticker_id
calculation_dateDATE집계일PK
hourINT집계 시간 (0-23)PK
earned_countINT해당 시간 획득 수DEFAULT 0
daily_totalINT24시간 누적 획득 수DEFAULT 0
rankINT인기 순위NULL 가능
country_codeVARCHAR(2)국가 코드NOT NULL
updated_atTIMESTAMP갱신일시 (UTC+9)DEFAULT CURRENT_TIMESTAMP

user_theme_preferences (사용자 테마 선호도)

컬럼명타입설명제약조건
user_idBIGINT사용자 IDPK, FK → users.user_id
theme_tagVARCHAR(100)테마 태그PK
recent_view_scoreINT최근 열람 점수DEFAULT 0
bookmark_scoreINT북마크 점수DEFAULT 0
completion_scoreINT완독 점수DEFAULT 0
frequency_scoreINT반복 열람 점수DEFAULT 0
total_preference_scoreINT총 선호도 점수DEFAULT 0
last_updatedTIMESTAMP마지막 갱신일시DEFAULT CURRENT_TIMESTAMP

user_sticker_recommendations (나만 없는 스티커)

컬럼명타입설명제약조건
recommendation_idBIGINT추천 고유 식별자PK, AUTO_INCREMENT
user_idBIGINT사용자 IDFK → users.user_id
sticker_idBIGINT추천 스티커 IDFK → stickers.sticker_id
book_idBIGINT도서 IDFK → books.book_id
theme_scoreINT테마 기반 점수DEFAULT 0
popularity_scoreINT인기도 점수DEFAULT 0
total_scoreINT총 점수DEFAULT 0
is_excludedBOOLEAN인기 Top10 제외 여부DEFAULT FALSE
calculated_atTIMESTAMP계산일시DEFAULT CURRENT_TIMESTAMP
UNIQUE KEY(user_id, sticker_id)

upcoming_stickers (공개 예정 스티커)

컬럼명타입설명제약조건
upcoming_idBIGINT예정 스티커 고유 식별자PK, AUTO_INCREMENT
book_idBIGINT도서 IDFK → books.book_id
sticker_idBIGINT스티커 IDFK → stickers.sticker_id, NULL 가능
scheduled_dateDATE공개 예정일NOT NULL
week_start_dateDATE주 시작일 (월요일)NOT NULL
country_codeVARCHAR(2)국가 코드NOT NULL
display_orderINT노출 순서DEFAULT 0
preview_image_urlVARCHAR(500)미리보기 이미지 URLNULL 가능
is_publishedBOOLEAN공개 완료 여부DEFAULT FALSE
created_atTIMESTAMP생성일시DEFAULT CURRENT_TIMESTAMP
INDEX idx_week_country(week_start_date, country_code)

5. 사용자 활동 도메인

user_reading_history (독서 기록)

컬럼명타입설명제약조건
history_idBIGINT기록 고유 식별자PK, AUTO_INCREMENT
user_idBIGINT사용자 IDFK → users.user_id
book_idBIGINT도서 IDFK → books.book_id
episode_idBIGINT에피소드 IDFK → episodes.episode_id, NULL 가능
language_codeVARCHAR(10)열람 언어NOT NULL
last_positionINT마지막 읽은 위치 (픽셀)DEFAULT 0
scroll_percentageINT스크롤 진행률 (0-100)DEFAULT 0
is_completedBOOLEAN완독 여부DEFAULT FALSE
total_view_timeINT총 열람 시간 (초)DEFAULT 0
scroll_speed_avgDECIMAL(10,2)평균 스크롤 속도NULL 가능
started_atTIMESTAMP열람 시작일시DEFAULT CURRENT_TIMESTAMP
last_viewed_atTIMESTAMP마지막 열람일시ON UPDATE CURRENT_TIMESTAMP

user_bookmarks (북마크/찜)

컬럼명타입설명제약조건
user_idBIGINT사용자 IDPK, FK → users.user_id
book_idBIGINT도서 IDPK, FK → books.book_id
bookmarked_atTIMESTAMP북마크일시DEFAULT CURRENT_TIMESTAMP

user_recordings (녹음)

컬럼명타입설명제약조건
recording_idBIGINT녹음 고유 식별자PK, AUTO_INCREMENT
user_idBIGINT사용자 IDFK → users.user_id
book_idBIGINT도서 IDFK → books.book_id
episode_idBIGINT에피소드 IDFK → episodes.episode_id, NULL 가능
language_codeVARCHAR(10)녹음 언어NOT NULL
recording_urlVARCHAR(500)녹음 파일 URLNOT NULL
duration_secondsINT녹음 길이 (초, 최대 1800)NOT NULL
recorded_atTIMESTAMP녹음일시DEFAULT CURRENT_TIMESTAMP
UNIQUE KEY(user_id, book_id, episode_id, language_code)

user_behavior_logs (사용자 행동 로그)

컬럼명타입설명제약조건
log_idBIGINT로그 고유 식별자PK, AUTO_INCREMENT
user_idBIGINT사용자 IDFK → users.user_id
action_typeVARCHAR(50)행동 유형NOT NULL
target_typeVARCHAR(50)대상 유형NOT NULL
target_idBIGINT대상 IDNOT NULL
contextJSON추가 컨텍스트 정보NULL 가능
created_atTIMESTAMP로그 생성일시DEFAULT CURRENT_TIMESTAMP
INDEX idx_user_action(user_id, action_type, created_at)

6. 콘텐츠 노출 도메인

content_lists (콘텐츠 리스트)

컬럼명타입설명제약조건
list_idBIGINT리스트 고유 식별자PK, AUTO_INCREMENT
list_typeVARCHAR(50)리스트 유형NOT NULL
nameVARCHAR(200)리스트명 (기본)NOT NULL
name_translationsJSON다국어 리스트명NULL 가능
criteriaJSON선정 기준NULL 가능
display_locationVARCHAR(50)노출 위치NOT NULL
display_orderINT노출 순서DEFAULT 0
min_itemsINT최소 아이템 수DEFAULT 5
is_activeBOOLEAN활성화 여부DEFAULT TRUE
updated_atTIMESTAMP갱신일시DEFAULT CURRENT_TIMESTAMP

content_list_items (리스트 아이템)

컬럼명타입설명제약조건
list_idBIGINT리스트 IDPK, FK → content_lists.list_id
book_idBIGINT도서 IDPK, FK → books.book_id
positionINT순서NOT NULL
is_manualBOOLEAN수동 정렬 여부DEFAULT FALSE
added_atTIMESTAMP추가일시DEFAULT CURRENT_TIMESTAMP

banners (배너)

컬럼명타입설명제약조건
banner_idBIGINT배너 고유 식별자PK, AUTO_INCREMENT
banner_typeVARCHAR(50)배너 유형NOT NULL
image_urlVARCHAR(500)이미지 URLNOT NULL
target_typeVARCHAR(50)대상 유형NULL 가능
target_idBIGINT대상 IDNULL 가능
target_urlVARCHAR(500)연결 URLNULL 가능
positionINT순서DEFAULT 0
is_activeBOOLEAN활성화 여부DEFAULT TRUE
start_dateDATETIME시작일시NOT NULL
end_dateDATETIME종료일시NULL 가능

7. 검색 도메인

search_logs (검색 로그)

컬럼명타입설명제약조건
log_idBIGINT로그 고유 식별자PK, AUTO_INCREMENT
user_idBIGINT사용자 IDFK → users.user_id
search_queryVARCHAR(500)검색어NOT NULL
search_typeVARCHAR(50)검색 유형NULL 가능
result_countINT검색 결과 수DEFAULT 0
clicked_book_idBIGINT클릭한 도서 IDFK → books.book_id, NULL 가능
click_positionINT클릭 위치NULL 가능
searched_atTIMESTAMP검색일시DEFAULT CURRENT_TIMESTAMP
컬럼명타입설명제약조건
search_termVARCHAR(500)검색어PK
search_countINT검색 횟수DEFAULT 1
click_countINT클릭 횟수DEFAULT 0
last_searched_atTIMESTAMP마지막 검색일시DEFAULT CURRENT_TIMESTAMP

search_suggestions (검색어 추천)

컬럼명타입설명제약조건
suggestion_idBIGINT추천 고유 식별자PK, AUTO_INCREMENT
keywordVARCHAR(200)키워드NOT NULL, INDEX
suggestionVARCHAR(500)추천 검색어NOT NULL
suggestion_typeVARCHAR(50)추천 유형NOT NULL
weightINT가중치DEFAULT 1
is_activeBOOLEAN활성화 여부DEFAULT TRUE

8. 알림 도메인

notifications (알림)

컬럼명타입설명제약조건
notification_idBIGINT알림 고유 식별자PK, AUTO_INCREMENT
notification_typeENUM알림 유형NOT NULL
target_book_idBIGINT관련 도서 IDFK → books.book_id, NULL 가능
target_event_idBIGINT관련 이벤트 IDFK → events.event_id, NULL 가능
titleJSON다국어 제목NOT NULL
messageJSON다국어 내용NOT NULL
image_urlVARCHAR(500)알림 이미지NULL 가능
action_urlVARCHAR(500)액션 URLNULL 가능
created_atTIMESTAMP생성일시DEFAULT CURRENT_TIMESTAMP
expire_atTIMESTAMP만료일시NOT NULL

user_notifications (사용자별 알림)

컬럼명타입설명제약조건
user_idBIGINT사용자 IDPK, FK → users.user_id
notification_idBIGINT알림 IDPK, FK → notifications.notification_id
is_readBOOLEAN읽음 여부DEFAULT FALSE
is_clickedBOOLEAN클릭 여부DEFAULT FALSE
received_atTIMESTAMP수신일시DEFAULT CURRENT_TIMESTAMP
read_atTIMESTAMP읽은 일시NULL 가능
clicked_atTIMESTAMP클릭 일시NULL 가능

push_tokens (푸시 토큰)

컬럼명타입설명제약조건
token_idBIGINT토큰 고유 식별자PK, AUTO_INCREMENT
user_idBIGINT사용자 IDFK → users.user_id
device_idBIGINT기기 IDFK → user_devices.device_id
push_tokenVARCHAR(500)푸시 토큰UNIQUE, NOT NULL
platformVARCHAR(20)플랫폼 (ios, android)NOT NULL
is_activeBOOLEAN활성 상태DEFAULT TRUE
created_atTIMESTAMP생성일시DEFAULT CURRENT_TIMESTAMP
updated_atTIMESTAMP수정일시ON UPDATE CURRENT_TIMESTAMP

9. 이벤트 도메인

events (이벤트)

컬럼명타입설명제약조건
event_idBIGINT이벤트 고유 식별자PK, AUTO_INCREMENT
event_typeENUM이벤트 유형 ('event', 'news', 'update')NOT NULL
titleVARCHAR(500)이벤트 제목NOT NULL
contentTEXT이벤트 내용NOT NULL
thumbnail_urlVARCHAR(500)대표 이미지 URLNULL 가능
banner_urlVARCHAR(500)배너 이미지 URLNULL 가능
target_countriesJSON대상 국가 목록NULL 가능
start_dateDATETIME시작일시NOT NULL
end_dateDATETIME종료일시NULL 가능
is_activeBOOLEAN활성화 여부DEFAULT TRUE
view_countINT조회수DEFAULT 0
created_atTIMESTAMP생성일시DEFAULT CURRENT_TIMESTAMP

event_participants (이벤트 참여자)

컬럼명타입설명제약조건
participant_idBIGINT참여 고유 식별자PK, AUTO_INCREMENT
event_idBIGINT이벤트 IDFK → events.event_id
user_idBIGINT사용자 IDFK → users.user_id
participated_atTIMESTAMP참여일시DEFAULT CURRENT_TIMESTAMP
participation_dataJSON참여 데이터NULL 가능

10. 결제 도메인

promo_codes (프로모션 코드)

컬럼명타입설명제약조건
codeVARCHAR(50)프로모션 코드PK
code_typeENUM코드 유형NOT NULL
discount_typeENUM할인 유형NOT NULL
discount_valueDECIMAL(10,2)할인값NOT NULL
max_usesINT최대 사용 횟수NULL 가능
used_countINT사용된 횟수DEFAULT 0
valid_fromDATE유효 시작일NOT NULL
valid_untilDATE유효 종료일NULL 가능
created_byBIGINT생성자FK → users.user_id, NULL 가능

payment_logs (결제 로그)

컬럼명타입설명제약조건
payment_idBIGINT결제 고유 식별자PK, AUTO_INCREMENT
user_idBIGINT사용자 IDFK → users.user_id
subscription_idBIGINT구독 IDFK → user_subscriptions.subscription_id
payment_typeENUM결제 유형NOT NULL
amountDECIMAL(10,2)금액NOT NULL
currencyVARCHAR(3)통화NOT NULL
platform_transaction_idVARCHAR(200)플랫폼 거래 IDUNIQUE
platformVARCHAR(50)결제 플랫폼NOT NULL
statusENUM결제 상태NOT NULL
refund_reasonTEXT환불 사유NULL 가능
processed_atTIMESTAMP처리일시DEFAULT CURRENT_TIMESTAMP

referral_rewards (추천 리워드)

컬럼명타입설명제약조건
reward_idBIGINT리워드 고유 식별자PK, AUTO_INCREMENT
referrer_idBIGINT추천인 IDFK → users.user_id
referee_idBIGINT피추천인 IDFK → users.user_id
reward_typeVARCHAR(50)리워드 유형NOT NULL
reward_valueINT리워드 값NOT NULL
statusVARCHAR(20)상태NOT NULL
created_atTIMESTAMP생성일시DEFAULT CURRENT_TIMESTAMP

11. 운영 관리 도메인

admin_users (관리자)

컬럼명타입설명제약조건
admin_idBIGINT관리자 고유 식별자PK, AUTO_INCREMENT
emailVARCHAR(255)이메일UNIQUE, NOT NULL
nameVARCHAR(100)이름NOT NULL
roleENUM권한NOT NULL
permissionsJSON세부 권한NULL 가능
is_activeBOOLEAN활성화 여부DEFAULT TRUE
last_login_atTIMESTAMP마지막 로그인NULL 가능
created_atTIMESTAMP생성일시DEFAULT CURRENT_TIMESTAMP

content_schedule (콘텐츠 공개 일정)

컬럼명타입설명제약조건
schedule_idBIGINT일정 고유 식별자PK, AUTO_INCREMENT
book_idBIGINT도서 IDFK → books.book_id
scheduled_dateDATE공개 예정일NOT NULL
country_codeVARCHAR(2)국가 코드NOT NULL
is_publishedBOOLEAN공개 완료 여부DEFAULT FALSE
published_atTIMESTAMP실제 공개일시NULL 가능
created_atTIMESTAMP생성일시DEFAULT CURRENT_TIMESTAMP
UNIQUE KEY(scheduled_date, country_code)

operation_logs (운영 로그)

컬럼명타입설명제약조건
log_idBIGINT로그 고유 식별자PK, AUTO_INCREMENT
admin_idBIGINT관리자 IDFK → admin_users.admin_id
action_typeVARCHAR(100)액션 유형NOT NULL
target_typeVARCHAR(50)대상 유형NOT NULL
target_idBIGINT대상 IDNOT NULL
before_dataJSON변경 전 데이터NULL 가능
after_dataJSON변경 후 데이터NULL 가능
ip_addressVARCHAR(45)IP 주소NULL 가능
created_atTIMESTAMP생성일시DEFAULT CURRENT_TIMESTAMP

주요 인덱스 전략

-- 사용자 도메인
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_country ON users(country_code);
CREATE INDEX idx_user_subscriptions_status ON user_subscriptions(user_id, status);

-- 콘텐츠 도메인
CREATE INDEX idx_books_published ON books(published_date DESC);
CREATE INDEX idx_book_languages ON book_languages(language_code);
CREATE INDEX idx_book_series_type ON book_series(series_type, display_order);
CREATE INDEX idx_book_tags_type ON book_tags(tag_type, tag_name);
CREATE INDEX idx_characters_order ON characters(display_order, character_name);

-- 점수 및 통계
CREATE INDEX idx_book_scores_total ON book_scores(total_score DESC);
CREATE INDEX idx_daily_stats_date ON daily_stats(stat_date, country_code);
CREATE INDEX idx_hourly_stats ON hourly_stats(stat_hour, country_code);
CREATE INDEX idx_realtime_rankings ON realtime_rankings(country_code, calculated_at DESC, rank);

-- 사용자 활동
CREATE INDEX idx_reading_history_user ON user_reading_history(user_id, last_viewed_at DESC);
CREATE INDEX idx_reading_history_book ON user_reading_history(book_id, is_completed);
CREATE INDEX idx_bookmarks_user ON user_bookmarks(user_id, bookmarked_at DESC);

-- 리워드
CREATE INDEX idx_user_stickers ON user_stickers(user_id, earned_at DESC);
CREATE INDEX idx_sticker_logs_date ON sticker_earning_logs(earned_at, sticker_id);
CREATE INDEX idx_popular_stickers ON popular_stickers(calculation_date, rank);
CREATE INDEX idx_theme_preferences ON user_theme_preferences(user_id, total_preference_score DESC);

-- 검색
CREATE INDEX idx_search_logs_user ON search_logs(user_id, searched_at DESC);
CREATE INDEX idx_search_logs_query ON search_logs(search_query);
CREATE INDEX idx_popular_searches ON popular_searches(search_count DESC);

-- 콘텐츠 노출
CREATE INDEX idx_content_lists_active ON content_lists(is_active, display_location, display_order);
CREATE INDEX idx_content_list_items ON content_list_items(list_id, position);
CREATE INDEX idx_banners_active ON banners(is_active, start_date, end_date);

데이터 보관 및 삭제 정책

  1. 사용자 데이터

    • 탈퇴 후 30일간 보관
    • 30일 후 개인정보 완전 삭제
    • 통계 데이터는 익명화하여 보관
  2. 알림 데이터

    • 생성 후 14일 자동 삭제
    • 읽음 상태와 관계없이 삭제
  3. 로그 데이터

    • 검색 로그: 90일 보관
    • 행동 로그: 180일 보관
    • 결제 로그: 영구 보관
    • 운영 로그: 영구 보관
  4. 통계 데이터

    • 시간별 집계: 30일 보관
    • 일별 집계: 1년 보관
    • 월별 집계: 영구 보관

성능 최적화 고려사항

  1. 파티셔닝 전략

    • daily_stats: 날짜별 파티셔닝
    • hourly_stats: 월별 파티셔닝
    • user_behavior_logs: 월별 파티셔닝
  2. 캐싱 전략

    • Redis를 활용한 실시간 랭킹 캐싱
    • 인기 검색어 캐싱
    • 사용자 선호도 캐싱
  3. 비동기 처리

    • 점수 계산 배치 작업
    • 통계 집계 배치 작업
    • 추천 시스템 갱신

확장 계획

Phase 1 (MVP - 단순화 스키마)

  • 핵심 기능만 구현
  • 기본 통계만 수집
  • 단순 정렬 기준

Phase 2 (점수 시스템 도입)

  • 복합 점수 시스템 구현
  • 실시간 랭킹 고도화
  • 개인화 추천 시작

Phase 3 (고급 기능)

  • 상세 행동 추적
  • A/B 테스트 시스템
  • 머신러닝 기반 추천

Phase 4 (글로벌 확장)

  • 다중 리전 지원
  • 실시간 동기화
  • 글로벌 통계 시스템

이 목표 스키마는 단순화 버전에서 시작하여 서비스 성장에 따라 점진적으로 확장 가능한 구조로 설계되었습니다.