Skip to content

T-05 홈페이지 CMS 스키마 (V9__create_homepage_content.sql) - #59

Open
ff1451 wants to merge 1 commit into
feat/t35-track-promotionfrom
feat/t05-homepage-content-schema
Open

T-05 홈페이지 CMS 스키마 (V9__create_homepage_content.sql)#59
ff1451 wants to merge 1 commit into
feat/t35-track-promotionfrom
feat/t05-homepage-content-schema

Conversation

@ff1451

@ff1451 ff1451 commented Aug 19, 2026

Copy link
Copy Markdown

Refs #17
Base: feat/t35-track-promotion (#58) — T-01/T-04/T-35 위에 stack. track 테이블(V7)에 의존한다.

스키마

track_page(트랙 1:0..1) → track_study_point / tech_stack + track_page_tech_stack / track_page_member, curriculumcurriculum_weekcurriculum_topiccurriculum_topic_detail, activity_categoryactivityactivity_image, image_asset. 총 12개 테이블. 05-api-spec.md §1.2 설계를 그대로 옮기되 기존 마이그레이션(V1~V8) 관례에 맞춰 대문자 SQL 키워드로 작성했다.

이 PR은 스키마만 만든다. JPA 엔티티·리포지토리·API는 T-06 이후 도메인 티켓의 몫이다.

설계에서 지킨 것

  • track_pagetrack(V7, 인터널 트랙 마스터)을 참조하되 완전히 분리된 테이블이다(ADR-002/003) — 트랙 표시명·slug·소개 같은 "홈페이지에 어떻게 보이는가"는 "부원이 어디에 속하는가"와 변경 이유가 다르다.
  • uq_track_page_slug, uq_track_page_track, uq_curriculum_published — 전부 partial unique index(WHERE deleted_at IS NULL / WHERE is_published AND deleted_at IS NULL)다. soft delete·"공개는 1개만" 규칙을 애플리케이션이 아니라 DB가 보장한다(INV-1, INV-2).
  • curriculum_weekweek_to >= week_from CHECK, activitymonth/year 범위 CHECK(INV-5, INV-6).
  • soft delete는 최상위(track_page/curriculum/activity_category/activity)만. 하위 트리는 ON DELETE CASCADE로 물리 삭제(INV-7, ADR-006).
  • activity → activity_category에는 의도적으로 CASCADE를 걸지 않았다. 활동이 남아 있는 카테고리를 삭제하려는 시도는 FK 위반으로 거부되어야 한다(AC-3.8) — 기본 RESTRICT 동작을 그대로 쓴다.
  • image_asset은 콘텐츠 테이블과 FK로 묶지 않는다(ADR-009) — 이미지 삭제가 콘텐츠를 깨뜨리지 않게.
  • track_page_member.member_id는 FK를 즉시 건다(ADR-012) — 명부가 이미 구현되어 있어 후행 마이그레이션으로 미룰 이유가 없다.

테스트

아직 JPA 엔티티가 없어서(도메인 티켓 몫) HomepageContentSchemaIntegrationTest(신규, IntegrationTestSupport 세 번째 소비자)가 raw JDBC로 DDL 자체를 직접 검증한다 — partial unique index·CHECK·CASCADE는 오타 하나로도 조용히 깨지는데, 엔티티가 생기기 전에 지금 굳혀 두는 게 낫다고 판단했다.

검증 방법
INV-1 slug 유일성 (삭제된 행 제외) 같은 slug 중복 삽입 실패 → soft delete 후 재사용 성공
INV-2 공개 커리큘럼 1개 두 번째 공개 삽입 실패, 비공개는 여러 개 허용
INV-5 주차 범위 week_to < week_from 거부
INV-6 활동 연월 범위 month=13, year=1999 거부
INV-7 cascade (커리큘럼) 삭제 시 주차·토픽·세부항목 모두 삭제 확인
INV-7 cascade (활동) 삭제 시 사진 삭제 확인
AC-3.8 근거 활동이 남은 카테고리 삭제 시 FK 위반

AC-7.3 (빈 DB / 기존 DB 양쪽 적용) — 두 경로로 확인

  • Testcontainers: 매번 빈 DB에 V1~V9 순차 적용 (자동화 테스트로 매 실행 검증)
  • 실제 로컬 Postgres(수동): T-04에서 V1~V6까지 이미 적용해 둔 DB에 이어서 부팅
    Current version of schema "public": 6
    Migrating schema "public" to version "7 - create track"
    Migrating schema "public" to version "8 - add member track id"
    Migrating schema "public" to version "9 - create homepage content"
    Successfully applied 3 migrations ... now at version v9
    
    curl /healthOK (200).

./gradlew test40개 전체 통과, 실패 0.

track_page, curriculum(주차>토픽>세부항목), activity(카테고리>활동>사진),
image_asset까지 12개 테이블. 엔티티는 아직 없다 — T-06 이후 도메인 티켓의 몫이다.

- track_page: track(V7) 1 : 0..1, slug/track_id 각각 partial unique index(INV-1)
- curriculum: 트랙당 공개 세트 1개(INV-2, partial unique index)
- curriculum_week: week_to >= week_from CHECK(INV-5)
- activity: month 1~12, year 2000~2100 CHECK(INV-6)
- 최상위(track_page/curriculum/activity_category/activity)만 soft delete,
  하위 트리는 ON DELETE CASCADE(INV-7)
- activity -> activity_category는 CASCADE를 걸지 않았다 — 활동이 남은 카테고리
  삭제는 FK 위반으로 거부되어야 한다(AC-3.8)
- image_asset은 콘텐츠 테이블과 FK로 묶지 않는다(ADR-009)
- track_page_member.member_id에 FK 즉시 적용(ADR-012, 명부가 이미 있으므로)

테스트: HomepageContentSchemaIntegrationTest(신규, IntegrationTestSupport
세 번째 소비자) — raw JDBC로 partial unique index 2개, CHECK 제약 2개,
CASCADE 2개, RESTRICT 1개를 직접 검증한다. 아직 엔티티가 없어 JPA로는
검증할 수 없는 DDL 자체의 정확성을 여기서 굳힌다.

AC-7.3(빈 DB / 기존 DB 양쪽 적용)을 두 경로로 확인했다:
- Testcontainers: 항상 빈 DB에 V1~V9 순차 적용 (자동화 테스트)
- 실제 로컬 Postgres: T-04에서 V1~V6까지 적용해 뒀던 DB에 이어서 부팅
  → "Current version of schema: 6" → V7,V8,V9 적용 → v9, /health 200 (수동 확인)

./gradlew test 전체 통과(40개, 실패 0).

Refs #17
@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: df19ee04-5414-4edd-8c64-d9c15dfceead

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant