Skip to content

T-27 선행: 트랙 페이지 편집 화면 조회 집계 보완 - #74

Open
ff1451 wants to merge 2 commits into
feat/t26-backend-corsfrom
feat/t27-backend-detail-aggregate
Open

T-27 선행: 트랙 페이지 편집 화면 조회 집계 보완#74
ff1451 wants to merge 2 commits into
feat/t26-backend-corsfrom
feat/t27-backend-detail-aggregate

Conversation

@ff1451

@ff1451 ff1451 commented Aug 20, 2026

Copy link
Copy Markdown

배경

05-api-spec.mdGET /v1/admin/track-pages/{id}를 "편집 화면 집계(헤더+studyPoints+techStacks+members)"로 정의했지만, T-08(study-points)·T-09(tech-stacks)·T-18(members) 각각을 구현할 때 이 집계가 실제로 채워지지 않은 채 남아 있었다(AdminTrackPageDetailResponse에 주석만 "해당 티켓이 필드를 추가한다"고 남겨두고 실제로는 아무도 추가하지 않음).

인터널 프론트 T-27(트랙 페이지 편집 화면)이 이 GET 하나로 5개 탭(기본·헤더/WHAT WE STUDY/기술스택/멤버/SEO)을 채워야 하는데, study-points와 tech-stacks에는 조회용 GET이 따로 없고 전체 교체 PUT만 있어서, 이 gap이 그대로면 화면을 열 때 기존 값을 보여줄 방법이 없었다. T-27 프론트 작업을 시작하기 전에 먼저 고쳤다.

변경

  • AdminTrackPageDetailResponsestudyPoints/techStacks/members 필드를 추가했다.
  • from(trackPage)는 헤더만 바꾸는 응답(생성·헤더 수정·slug 변경)에 계속 쓰고 세 목록은 빈 배열로 둔다 — 그 호출들은 프런트가 이미 들고 있는 상태라 다시 받을 필요가 없다.
  • of(trackPage, studyPoints, techStacks, members)는 편집 화면을 여는 GET 조회에서만 전체 집계를 채운다.
  • 관리자 편집 화면은 저트래픽이라 부원 조회의 N+1을 감수한다(T-11 관리자 커리큘럼 트리와 같은 이유).

검증

  • ./gradlew test 전체 통과, 3회 연속.
  • GET 응답에 studyPoints/techStacks/members가 함께 담기는지 새 통합 테스트로 확인.

Refs BCSDLab/BCSD_INTERNAL_RECODE_V2#8(T-27), 브랜치는 feat/t26-backend-cors(#73) 위에 스택.

머지하지 않습니다. 부원 리뷰 후 직접 머지 예정입니다.

- 05-api-spec.md는 애초에 GET /v1/admin/track-pages/{id}를 "편집 화면 집계
  (헤더+studyPoints+techStacks+members)"로 정의했지만, T-08/T-09/T-18에서 각각
  study-points·tech-stacks·members 엔드포인트를 만들 때 이 집계가 실제로 구현되지
  않은 채 남아 있었다(응답 레코드 주석만 "해당 티켓이 필드를 추가한다"고 남겨둠).
  인터널 프론트 T-27(트랙 페이지 편집 화면)이 이 GET 하나로 탭 5개를 채워야 하는데
  study-points/tech-stacks에는 별도 GET이 없어(전체 교체 PUT뿐) 이 gap이 그대로면
  화면을 열 때 기존 값을 보여줄 방법이 없었다.
- AdminTrackPageDetailResponse에 studyPoints/techStacks/members 필드를 추가했다.
  `from(trackPage)`는 헤더만 바꾸는 응답(생성·헤더 수정·slug 변경)에 계속 쓰고 빈
  배열을 채운다 — 그 호출들은 프런트가 이미 들고 있는 상태라 다시 받을 필요가 없다.
  `of(...)`는 GET 조회에서만 전체 집계를 채운다.
- 관리자 편집 화면은 저트래픽이라 부원 조회의 N+1을 감수한다(T-11 관리자 커리큘럼
  트리와 같은 이유).

검증: ./gradlew test 전체 통과, 3회 연속. GET 응답에 세 목록이 함께 담기는지
통합 테스트로 확인.
@coderabbitai

coderabbitai Bot commented Aug 20, 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: 3d1cf504-f1de-4545-8448-b780d84babba

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.

@ff1451

ff1451 commented Aug 20, 2026

Copy link
Copy Markdown
Author

## 1) 컬럼 3개 제거 (V11)

팀 결정으로 인터널 화면에서 히어로 이미지·OG 이미지·meta description을 모두 뺐다
(시안에도 없다). 설정할 수단이 없으니 값은 영구히 NULL인 죽은 컬럼이고,
ddl-auto: validate가 엔티티와 계속 대조하므로 컬럼째 정리한다.

되돌릴 수 없는 변경이다. 아직 운영 콘텐츠 투입(T-20 시드) 전이라 값이 있는 행은 없다.
activity_category.hero_image_url은 건드리지 않았다 — 활동 화면에서 실제로 쓴다.

영향: TrackPage 엔티티/빌더/updateHeader, TrackPageCreateRequest,
TrackPageUpdateRequest, AdminTrackPageDetailResponse, 공개 TrackDetailResponse.
공개 응답에서 heroImageUrl이 사라지므로 홈페이지 연동(T-22) 시 이 필드를 기대하면
안 된다 — 지금은 홈페이지가 정적 데이터라 런타임 의존이 없다.

## 2) 한글 트랙명 slug 버그

SlugGenerator가 [^a-z0-9]+를 하이픈으로 바꾸고 양끝을 지우므로 "백엔드" → "" 였다.
slug는 NOT NULL이고 uq_track_page_slug(부분 유니크)가 걸려 있어서:
  - 첫 한글 트랙은 빈 slug로 만들어지고
  - 두 번째 한글 트랙은 빈 slug 중복으로 409가 되어 아예 생성 불가
  - 공개 API GET /v1/tracks/{slug}로 조회 불가
실제로 로컬 DB에 slug=""인 행이 남아 있었다(사용자가 트랙명 "ㅇ"으로 만든 것).

팀에서 "트랙명 === 표시명, slug는 lowercase"로 정했으니 한글 트랙명이 정상 입력이다.
그래서 생성이 실패하지 않도록 3단 폴백을 둔다:
  표시명 → 트랙 코드(BACKEND) → track-{trackId}
track.code에는 패턴 검증이 없어 코드도 한글일 수 있으므로 마지막 후보는 항상 영숫자인
track-{id}로 둔다(uq_track_page_track이 트랙당 1개를 보장하므로 이 값도 유일하다).
마음에 안 드는 주소는 기존 PATCH …/slug로 바꾼다.

AC-1.1("Data Analyst" → "data-analyst")은 그대로 유지된다 — 표시명 파생이 1순위다.

## 검증

- `./gradlew test` 전체 3회 통과
- V11을 로컬 Postgres에 적용 → 부팅 → /health 200, ddl-auto: validate 통과
- `\d track_page`로 컬럼 3개가 실제로 사라진 것 확인
- 한글 트랙명 2개("백엔드"/"프론트엔드")를 연달아 생성 → 각각 slug backend/frontend,
  이전에는 두 번째가 409였다. GET /v1/tracks/backend도 정상 조회.

## 추가한 테스트

- SlugGeneratorTest(컨테이너 없는 단위): 파생, 한글→빈 문자열, fromOrFallback 3단 폴백
- AdminTrackIntegrationTest: 한글 트랙명 2개 연달아 생성 + 공개 API 조회
@ff1451

ff1451 commented Aug 20, 2026

Copy link
Copy Markdown
Author

커밋 2건 추가했습니다 (새 PR 대신 이 PR에 넣었습니다 — 둘 다 T-27 때문에 나온 같은 도메인 변경이라).

1. V11 — track_page의 hero_image_url · og_image_url · seo_description 제거
팀 결정으로 인터널 화면에서 세 항목을 다 뺐고(시안에도 없음), 설정할 수단이 없어 영구 NULL인 죽은 컬럼이라 정리했습니다. 되돌릴 수 없는 변경이지만 T-20 시드 이전이라 값이 있는 행은 없습니다. activity_category.hero_image_url은 활동 화면에서 실제로 쓰므로 그대로 둡니다.

⚠️ 공개 응답 GET /v1/tracks/{slug}에서 heroImageUrl이 사라집니다. 홈페이지 연동(T-22) 때 이 필드를 기대하면 안 됩니다.

2. 한글 트랙명 slug 버그 수정
SlugGenerator[^a-z0-9]+만 남기므로 "백엔드" → ""가 됐습니다. slug는 NOT NULL + uq_track_page_slug라서:

  • 첫 한글 트랙은 빈 slug로 생성
  • 두 번째 한글 트랙은 빈 slug 중복으로 409 → 생성 불가
  • 공개 API /v1/tracks/{slug} 조회 불가

실제로 로컬 DB에 slug=""인 행이 남아 있었습니다. 슬랙에서 "트랙명 === 표시명, slug는 lowercase"로 정한 이상 한글 트랙명이 정상 입력이므로, 생성이 실패하지 않게 3단 폴백을 뒀습니다: 표시명 → 트랙 코드(BACKEND) → track-{id}. track.code에 패턴 검증이 없어 코드도 한글일 수 있으니 마지막 후보는 항상 영숫자입니다. AC-1.1(Data Analystdata-analyst)은 그대로 유지됩니다.

검증: ./gradlew test 전체 3회 통과 / V11 로컬 적용 후 부팅·/health 200·ddl-auto: validate 통과 / \d track_page로 컬럼 제거 확인 / 한글 트랙명 2개 연달아 생성해 각각 backend·frontend slug 확인(이전엔 두 번째가 409).

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