74 KiB
Legacy HTTP·검색 크리에이터 이름 적용 목록
COR021 최신 범위
- ContentSeriesRepository의 목록 이름JOIN5곳과 ContentSeriesService 상세이름batch1곳을 HEAD로 되돌린다. 이 레거시 서비스 재사용자인 Home/Explorer의 시리즈 작성자 이름도 원본으로 돌아가며 기존 제목/장르/요일 번역은 유지한다.
- 공통 이름 기반과 별도v2·관리자 조회는 유지한다. 직접 관련된 AudioContentRepositoryLocalizedNameTest의 시리즈case는 이름단언만 정리하고 제목 번역/fallback을 보존한다. 최신 검증은 plan-task.md SERIES-NAME-TRIM-T1을 따른다.
- 적용 완료: 운영2파일HEAD일치, 제목검증case유지, 실제 시리즈·Home HTTP 및 전체2270건/lint통과를 확인했다. 영구테스트수증감은 없고 임시검증코드는 제거했다.
COR020 최신 범위
- L42 CharacterCommentService/Repository 이름 변경을 HEAD로 되돌리고 해당 이름 대체 통합테스트1개만 제거한다. 댓글 작성·삭제·신고·권한·커서·건수·언어 감지와 다른 테스트는 유지한다.
- 실제 제거 대상은 LegacyCurrentNameReadIntegrationTest의 shouldLocalizeCharacterCommentWritersWithoutChangingCharacterNameOrUserReplies이며 T20 신규 예정 별도 파일은 생성되지 않았다. 아래 이력과 구분하고 최신 검증은 plan-task.md CHARACTER-COMMENT-TRIM-T1을 따른다.
- 적용 완료: 운영2파일HEAD일치, 전용test1건 제거 후 전체2270건/lint통과. 실제 댓글·답글·상세HTTP10요청도 확인했고 임시 검증 코드는 제거했다.
COR019 최신 범위
- AudioContentMainController/Service의 이름 변경을 롤백하고 두 파일의 HEAD diff를 제거한다. L24의 전용 findByThemeFor2Weeks/findAudioContentByCurationId 이름 추가분과 L25·메인 큐레이션의 controller cache-hit 후 이름 batch를 제거한다.
- shared findByTheme는 AudioContentThemeService가 계속 사용하므로 유지한다. 전용 Main 테스트는 현재 없으며 T09는 계획 이력이다. 다른 실제 조회의 테스트는 삭제하지 않는다. 자세한 검증은 plan-task.md LEGACY-MAIN-T1을 따른다.
- 적용 완료: 두 메인 파일 HEAD와 동일, repository 전용2메서드 롤백. 임시 실제 HTTP5요청 통과 후 검증 코드는 제거했고 기존 전체2271건 및 lint 통과를 확인했다. 영구 테스트 증감은 없다.
COR017 최신 범위
- AudioContentMainTab*은 사용자 확인상 미사용이다. 해당 7개 서비스 파일은 HEAD와 같은 상태로 되돌리고 이름 원본 보장을 위한 null 인자·공유 raw 모드·전용 검증을 제거한다. 공유 조회 변경의 미사용 탭 간접 영향은 허용한다.
- 실제 사용 중인 Main/Theme/Home 조회의 언어별 이름과 제목 번역은 유지한다. AudioContentCuration 및 레거시 커뮤니티·팬톡 등 나머지 COR015 범위는 바꾸지 않는다.
- 아래 COR015의 탭 raw 보장 규칙은 당시 이력이며 이 절로 대체한다. 구체 파일·검증은 plan-task.md UNUSED-TAB-T1을 따른다.
COR015 현재 범위 보정
- 아래 COR012/COR013 표와 실행 기록은 과거 이력이다. 현재 범위는 PRD COR015가 우선한다. 이번 후속 변경은 코드·테스트 정리 및 정적 검토만 하며 실행 검증은 금지되어 있다.
- L13/L14 레거시 팬톡, L26
L30 큐레이션·탭, L37L39 레거시 커뮤니티의 이름 표시 적용을 제거한다. API·기존 번역·권한·저장·알림 업무는 유지한다. - L17/L18/L24/L31의 공유 조회는 호출자별로 구분한다. 제외된 탭/큐레이션은 원본 이름을 받고, 다른 API의 언어별 이름과 제목·본문 번역은 유지한다. 원본 경계는 KO 언어를 의미하지 않는다.
LegacyCreatorNameRenderer는 제거하고 필요한 소비자는CreatorLocalizedNameService를 직접 주입한다. T22 래퍼 전용 테스트는 제거하되 실제 core batch의 SQL 수·빈 입력·역할·현재 원본 fallback 회귀는 core 테스트에 보존한다.- T04/T17의 레거시 별칭 기대값은 원본 경계에 맞춰 최소 보정한다. 검색·프로필·후원·기타 유지 대상의 별칭 테스트, characterization·번역·권한 테스트, v2 테스트는 삭제하지 않는다. 불필요한 테스트 설정 import와 제거된 생성자 인자만 정리한다.
단계와 계약
- 기준: 같은 디렉터리
prd.md의 NAME002~006 및 COR012,plan-task.md의 P2-T1/P2-T2. - 현재 단계는 legacy GREEN 소스 연결 및 정적 계약 감사, 부모 직렬 검증 대기다. 아래 첫 테스트 단계 기록은 보존하며 구현 연결 결과는 마지막 절에 누적한다. runtime PASS나 제품 완료 기록이 아니다.
- 첫 단계에서는
src/main을 수정하지 않았다. 후속 부모 지시로 이번에는 legacy 소스·constructor fixture·추가 회귀를 작성했다. main PRD/plan, foundation/v2/async 소스, 운영 DDL은 수정하지 않았고 Gradle·커밋·푸시도 수행하지 않았다. - 범위: non-v2 현재 앱 HTTP 표시·검색.
content/main/tab/replay는 라이브 오디오 콘텐츠 탭이므로 포함한다. - 별도 slice:
fcm/*,live/*,message/*와 비동기 발송.admin/*,creator/admin/*,partner/*, auth/login 원본 이름은 수정 제외한다. 공유Series엔티티를 읽는 앱 서비스는 포함하되 creator-admin 응답은 바꾸지 않는다. - 공통 구현은
kr.co.vividnext.sodalive.v2.creatorname.application.CreatorLocalizedNameService.findLocalizedNames(Collection<Long>, Lang): Map<Long, String>이다. JA 활성 CREATOR만 이름 map에 포함하고 나머지는 기존 원본을 사용한다. - 응답 조립 직전 실제 owner member IDs를 모아 batch 조회한다.
LangContext는 HTTP 경계에서만 읽고 내부 호출에는 명시Lang을 전달한다. 행별 이름 조회,Member.nickname변이, principal 변이, 공통 entity getter, 전역 Jackson 변환은 금지한다. - 공개 DTO field 이름·타입은 보존한다. content/series/cheers/post/order/receipt/character ID를 이름 map key로 쓰지 않는다. owner ID가 없는 projection은 내부 record로 보충하고 HTTP에는 새 필드를 노출하지 않는다.
- 검색은 요청 언어와 무관하게 기존 nickname 조건 OR 활성 JA alias EXISTS다. alias는 owner
member_id,lang=JA,is_active=true, ownerrole=CREATOR로 상관시킨다. 제목·소개·본문·태그·장르 조건과 기존 visibility/block/requester/count/page 조건은 바꾸지 않는다.
이번에 작성한 테스트와 RED 인계
아래 경로는 저장소 루트 기준이다. 모두 기존 실제 API/repository를 호출하며 미래 production helper나 reflection 없이 동작한다. 별칭 fixture는 회원을 flush한 뒤 JdbcTemplate으로 creator_localized_name(member_id, lang, display_name, is_active, created_at, updated_at)에 넣는다. foundation의 entity 매핑으로 생성되는 테스트 테이블을 사용하며 수동 CREATE TABLE을 하지 않는다. USER/EN 행은 관리 API로 생성할 수 없는 오염 데이터까지 읽기 경계가 방어하는지 검사하는 fixture다.
| Owner | 실제 테스트 파일 | 사례 수 | 보호하는 동작 / 의도된 RED |
|---|---|---|---|
| T01 | src/test/kotlin/kr/co/vividnext/sodalive/search/SearchRepositoryLocalizedNameTest.kt |
5 | original OR JA alias, containsIgnoreCase, 동일 회원 양쪽 매칭 중복 없음, 중복 별칭 owner 분리, list/count/page 일치, soft clear 및 동일 행 재활성화. creator 활성/role/태그 활성/양방향 차단, content 성인/활성/duration/limited/차단, series 성인/episode 성인/활성/duration/차단/조인 중복 유지. content/series title 불변 및 series owner와 episode owner 구분. 미구현 alias list/count assertion에서 실패해야 한다. |
| T02 | src/test/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerQueryRepositoryLocalizedNameTest.kt |
2 | KO 채널 검색에서도 JA alias, 원본 유지, CREATOR/self/isActive 필터 및 중복 없음, soft clear/EN 제외와 재활성화. 미구현 alias-only hit에서 실패해야 한다. |
| T03 | src/test/kotlin/kr/co/vividnext/sodalive/member/MemberRepositoryLocalizedNameTest.kt |
3 | CREATOR requester의 USER 원본 매칭 유지, USER requester의 USER 제외, ADMIN/AGENT/BOT/self 제외, USER+AI_CHARACTER 별칭 제외, soft clear/EN 제외 및 원본 변경 후 재활성화. CREATOR alias hit 누락에서 실패해야 한다. 해당 repository에는 현재 count API가 없으므로 가상의 count API를 만들지 않는다. |
| T04 | src/test/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerLocalizedNameEndToEndTest.kt |
3 | 실제 MockMvc HTTP: JA profile/detail 이름, CREATOR 팬톡 부모/답글과 USER 작성자 혼합, KO/EN 원본, 원본 변경 뒤 별칭 유지 및 clear/missing fallback. /search creator title+nickname JA, content/series 제목 불변·실제 owner 이름 JA, KO /search/creators alias hit. 기존 원본 vs 별칭 응답 assertion에서 실패해야 한다. |
| T05 | src/test/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepositoryLocalizedNameTest.kt |
2 | 실제 getLatestContentByTheme(locale=ja)가 content ID와 다른 owner ID로 JA creatorNickname을 반환, USER 유지, KO/EN 원본, 제목 불변. 공개/미래/성인/활성/duration/차단 필터 유지와 원본 변경 후 soft clear fallback. 현재 원본 vs 별칭 ownername assertion에서 실패해야 한다. |
부모 실행 명령
아래 명령은 아직 실행하지 않았다. foundation 파일 저장 및 foundation 검증이 끝난 뒤 부모가 순서대로 실행한다. RED는 compile/context/fixture 오류가 아니라 위 요구 동작 assertion 실패여야 한다.
./gradlew test --tests 'kr.co.vividnext.sodalive.search.SearchRepositoryLocalizedNameTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.explorer.ExplorerQueryRepositoryLocalizedNameTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.member.MemberRepositoryLocalizedNameTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.content.AudioContentRepositoryLocalizedNameTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.explorer.ExplorerLocalizedNameEndToEndTest'
T01/T02는 기존 constructor로 QueryDSL repository를 직접 만든다. GREEN에서 dependency가 추가되면 테스트 constructor도 명시적으로 맞춘다. T03/T05는 JPA slice다. GREEN에서 공통 service/adapter dependency를 주입할 경우 해당 slice의 @Import를 명시적으로 보완한다. T04는 @SpringBootTest+MockMvc이며 기존 EmbeddedRedisInitializer opt-in, rollback 트랜잭션과 독립 H2 DB를 사용한다. T05도 독립 H2 DB로 owner/resource ID fixture 충돌을 피한다.
전체 source edit map
아래 모든 소스 경로의 공통 prefix는 src/main/kotlin/kr/co/vividnext/sodalive/다. 각 셀은 그 prefix 아래 정확한 경로이며 wildcard 변경 지시가 아니다. CURRENT는 요청 시 현재 이름 조회, NEUTRAL-CACHE는 언어 중립 응답 캐시, RAW는 원본 편집·인증 경계, IMMUTABLE은 기존 역사 값/발송물을 뜻한다. Test owner T01~T05는 이번에 작성했고 T06 이후는 후속 GREEN 전에 추가할 명시적 테스트 소유자다.
검색·회원·탐색
| ID | 실제 경로와 심볼 / caller | 응답 타입·이름 및 실제 owner | 유형·언어 / 최소 수정 경계 | Test owner |
|---|---|---|---|---|
| L01 | search/SearchRepository.kt: searchCreatorList, searchCreatorTotalCount; search/SearchService.kt: searchUnified, searchCreatorList; search/SearchController.kt |
SearchResponseItem.id=member.id; creator branch의 title와 nickname 모두 이름 |
CURRENT. list/count에 같은 alias EXISTS. service 응답 조립에서 Lang별 batch map으로 두 이름 field만 대체. | T01, T04; 기존 search/SearchServiceTest.kt constructor 회귀 |
| L02 | 같은 search/SearchRepository.kt: searchContentList, searchContentTotalCount; search/SearchService.kt: searchContentList, searchUnified |
SearchResponseItem.id=audioContent.id; nickname=audioContent.member.nickname. owner ID projection 없음 |
CURRENT. 원본 nickname branch만 OR alias로 확장. 내부 owner ID projection 추가; content title/detail/theme/hashTag predicates·adult/gender/duration/limited/block/count/group/page 유지. | T01, T04 |
| L03 | 같은 search/SearchRepository.kt: searchSeriesList, searchSeriesTotalCount; search/SearchService.kt: searchSeriesList, searchUnified |
id=series.id; nickname=series.member.nickname. owner ID projection 없음, episode owner와 series owner는 다를 수 있음 |
CURRENT. series owner 상관 alias; episode content visibility/gender 조건과 title/introduction/genre/keyword/group/count/page 유지. 내부 owner ID 필요. | T01, T04 |
| L04 | explorer/ExplorerQueryRepository.kt: getSearchChannel; explorer/ExplorerService.kt: getSearchChannel; explorer/ExplorerController.kt |
GetRoomDetailUser.nickname, 반환 Member의 id |
CURRENT. query original OR alias, 활성 CREATOR/self 제외 유지; service의 양방향 차단 후 응답 mapping에서 batch. DTO 파일이 live/room/detail에 있어도 전역 constructor는 변경하지 않는다. |
T02, T04 후속 channel HTTP 사례 |
| L05 | member/MemberRepository.kt: MemberQueryRepositoryImpl.findByNicknameAndOtherCondition; member/MemberService.kt: searchMember; member/MemberController.kt |
혼합 GetRoomDetailUser.nickname, 결과 Member id |
CURRENT. creator alias만 OR, ADMIN/AGENT/BOT/self 제외 및 requester USER의 USER 제외 유지. 현재 query에 isActive/count 조건은 없으므로 이번 작업에서 임의 추가하지 않음. service 양방향 block 유지·배치 표시. | T03, T06 |
| L06 | member/MemberRepository.kt: getMemberProfile; member/MemberService.kt: getMemberProfile |
GetMemberProfileResponse.nickname; 대상 memberId가 owner, myMemberId는 차단 조건 |
CURRENT. 기존 대상 ID로 batch하여 응답 대체. | T06 |
| L07 | member/MemberService.kt: getMyPage; member/myPage/MyPageResponse.kt |
MyPageResponse.nickname, 인증 Member id |
CURRENT display. principal을 바꾸지 말고 DTO에서만 alias 적용. raw editor의 ProfileResponse와 구분. |
T06 |
| L08 | member/block/BlockMemberRepository.kt: getBlockedMemberList; member/MemberService.kt: getBlockedMemberList |
GetBlockedMemberListItem.nickname, blockedMember id |
CURRENT 혼합 목록. blocked member owner IDs로 batch, 기존 block 활성·member 활성/count 유지. | T06 |
| L09 | explorer/ExplorerService.kt: getExplorer, getCreatorRank; explorer/ExplorerQueryRepository.kt: getCreatorRankings, getNewCreators, findCreatorByGender |
GetExplorerSectionCreatorResponse.nickname, 해당 Member id |
CURRENT. CreatorRanking은 순위/member 참조만 저장하며 nickname snapshot 아님. 기존 Member.toExplorerSectionCreator 전역 변경 대신 service 조립 DTO에서 batch. |
T04 후속 섹션 사례; 기존 explorer/ExplorerServiceTest.kt |
| L10 | explorer/ExplorerService.kt: getCreatorProfile, getCreatorDetail |
CreatorResponse.nickname, GetCreatorDetailResponse.nickname, creatorAccount.id |
CURRENT. 기존 langContext.lang을 명시 Lang로 공통 service에 전달. profile creator와 중첩 하위 service 결과를 각각 실제 owner로 처리. 이름을 넣는 blocked-access 메시지도 같은 creator 표시 계약 대상, auth 식별값 아님. |
T04 |
| L11 | explorer/ExplorerQueryRepository.kt: getSimilarCreatorList, getRandomCreatorList; explorer/ExplorerService.kt: getCreatorProfile |
SimilarCreatorResponse.nickname, userId=후보 creator member.id |
CURRENT. 이미 owner ID 보유. profile assembly에서 batch; random/order/tag/fallback query 로직 변경 없음. | T04 후속 similar creator 사례 |
| L12 | explorer/ExplorerQueryRepository.kt: getFollowerList; explorer/ExplorerService.kt: getFollowerList |
GetFollowerListDto / GetFollowerListResponseItem.nickname; userId=팔로워 member.id, creatorId 아님 |
CURRENT 혼합 목록. role/ID 보유; follower DTO 조립에서 batch. count·block·follow/notify 의미 유지. | T04 후속 follower 사례 |
| L13 | explorer/ExplorerQueryRepository.kt: getCheersList; explorer/ExplorerService.kt: getCreatorProfileCheers, getCreatorProfile |
GetCheersResponseItem.nickname, 부모·답글 각각 memberId=writer.id; cheersId/creatorId 아님 |
CURRENT 혼합 작성자. 부모·답글 ID를 한 번에 모아 batch, removeDeletedNicknamePrefix() 원본 fallback 유지. 날짜·parent·block·count·pagination 유지. |
T04 |
| L14 | explorer/ExplorerService.kt: writeCheers, modifyCheers |
HTTP CreatorChannelFanTalkResponse.writerNickname, creatorReplies[].writerNickname; 각각 writer member ID |
CURRENT HTTP 쓰기 응답. 공유 v2 from factory 전역 수정하지 말고 legacy caller에서 응답 대체; async event는 후속 slice. |
T04 후속 쓰기 응답 사례; 기존 explorer/ExplorerServiceTest.kt, explorer/ExplorerControllerTest.kt |
홈·랭킹·추천·캐시
| ID | 실제 경로와 심볼 / caller | 응답 타입·이름 및 실제 owner | 유형·언어 / 최소 수정 경계 | Test owner |
|---|---|---|---|---|
| L15 | api/home/HomeService.kt: fetchData; rank/RankingRepository.kt: getCreatorRankings |
GetHomeResponse.creatorRanking[].nickname, 순위 Member id |
CURRENT. HomeService에 이미 LangContext 있음. follow state 유지, home의 creator DTO만 batch. |
T07 |
| L16 | rank/RankingService.kt: getCreatorRanking, fetchCreatorBySellContentCountRankTop20, fetchCreatorBySeriesRevenueRankTop20; rank/RankingRepository.kt: 같은 이름의 query |
explorer creator DTO 또는 ContentCreatorResponse.creatorNickname, creatorId=member.id |
CURRENT 순위 materialization/집계의 현재 이름. 순위 데이터가 과거 기간 기준이어도 query가 현재 member를 join하므로 이름은 역사 snapshot 아님. service에서 batch. | T08 |
| L17 | rank/RankingRepository.kt: getAudioContentRanking, fetchCreatorContentBySalesCountTop4, fetchCreatorContentByLikeCountTop4, fetchFreeContentByCreatorIdTop4; rank/RankingService.kt: 대응 메서드 |
GetAudioContentRankingItem.creatorNickname, creatorId=member.id; contentId는 별도 |
CURRENT. repository에서 title 번역만 기존 locale로 처리, service에서 owner 이름 batch. api/home/HomeService.getContentRankingBySort와 모든 content tab caller까지 도달. |
T08, T07, T09 |
| L18 | rank/RankingService.kt: getSeriesRanking, getSeriesAllRankingByGenre, getCompleteSeriesRanking, fetchCreatorSeriesBySales, seriesToSeriesListItem; rank/RankingRepository.kt: 대응 query |
GetSeriesListResponse.SeriesListItem.creator.nickname, series.member.id, seriesId 아님 |
CURRENT. 공통 series response assembly에서 batch, creator-admin Series entity·writer/studio 변경 없음. | T08, T09 |
| L19 | query/recommend/RecommendChannelQueryRepository.kt: getRecommendChannelList; query/recommend/RecommendChannelQueryService.kt: getRecommendChannel; api/home/HomeService.kt: fetchData |
RecommendChannelResponse.creatorNickname, channelId=member.id; contentList resource ID와 별도 |
NEUTRAL-CACHE. @Cacheable(default) key는 recommendChannel:{memberId 또는 guest}:{isAdult}:{contentType}로 Lang 없음. cached raw response는 공유 DTO 변이 없이 cache hit 이후 caller에서 copy+batch하여 현재 이름 적용. 캐시 안에 JA를 저장하거나 cached DTO를 mutate하면 KO/JA 오염. 이미 존재하는 content title locale cache 문제와 이름 변경을 혼동하지 않음. |
T07, T10 |
| L20 | explorer/profile/CreatorDonationRankingQueryRepository.kt: getMemberDonationRanking; explorer/profile/CreatorDonationRankingService.kt: getMemberDonationRanking; explorer/ExplorerService.kt: profile/donation ranking |
MemberDonationRankingResponse.nickname, donor 응답 userId(query record는 memberId), 순위 대상 creatorId 아님 |
NEUTRAL-CACHE. query→response→Redis list write→cache hit 반환 양쪽 확인. key creator_donation_ranking_v2:{creatorId}:{period}:{offset}:{limit}:{withDonationCan}, TTL 다음 월요일, Lang 없음. legacy 응답 조립에서 hit/miss 모두 donor IDs로 copy+batch. count 캐시는 숫자뿐이므로 이름 변경 없음. v2 adapter consumer는 별도 slice. |
T11, T04 후속 donor 사례 |
| L21 | explorer/ExplorerQueryRepository.kt: getMemberDonationRanking |
MemberDonationRankingResponse.nickname, donor account.id |
CURRENT. 현재 ExplorerService는 L20의 service를 호출하므로 이 메서드는 legacy direct query 경로의 남은 raw projection이다. 실제 앱 caller가 추가되지 않으면 불필요한 독립 수정은 하지 않음. | T11, source caller 확인 |
L19/L20의 cache hit에서는 alias map에 값이 없을 때 cached nickname을 곧바로 현재 원본이라고 간주하면 안 된다. 원본 변경 후 soft clear가 일어나면 cached 원본도 stale할 수 있다. 후속 구현은 cache hit 이후 owner IDs로 현재 Member 원본을 batch 재조회하는 경계(member/MemberRepository.kt의 scalar findNicknamesByIds)를 두고 현재 원본 fallback과 alias map을 함께 응답에 적용한다. 캐시 entry/과거 record를 재작성하지 않는다. T10/T11에 원본 변경→alias clear→같은 cache hit의 current fallback 사례를 포함한다.
오디오·시리즈·메인 탭·주문·플레이리스트
| ID | 실제 경로와 심볼 / caller | 응답 타입·이름 및 실제 owner | 유형·언어 / 최소 수정 경계 | Test owner |
|---|---|---|---|---|
| L22 | content/AudioContentService.kt: getDetail; content/AudioContentRepository.kt: findByIdAndActive; explorer/ExplorerQueryRepository.kt: getMember |
GetAudioContentDetailResponse.creator.nickname / AudioContentCreator, audioContent.member.id |
CURRENT. 상세 조립에서 creator owner batch, 기존 title/detail/theme 번역과 접근/구매/visibility 유지. | T12; 기존 content/AudioContentServiceTest.kt |
| L23 | content/AudioContentRepository.kt: getLatestContentByTheme; content/AudioContentService.kt: getLatestContentByTheme; api/home/HomeService.kt: latest/free/point/recommend buckets |
AudioContentMainItem.creatorNickname, creatorId=member.id, contentId와 별도 |
CURRENT. query method가 locale를 이미 받으며 translated-title tuple와 locale-null projection 두 경로 모두 이름 대응 필요. tuple 응답 조립에 명시 Lang의 batch map 사용; service/홈 caller는 현재 locale 전달 유지. title 번역과 owner name 분리. |
T05, T07 |
| L24 | content/AudioContentRepository.kt: findByTheme, findAlarmContentByTheme, findByThemeFor2Weeks, findAudioContentByCurationId, findAudioContentByCurationIdV2, findContentByCurationId, getAudioContentRanking; content/main/AudioContentMainService.kt: getNewContentByTheme, getNewContentFor2WeeksByTheme, getAudioContentMainBannerList, getAudioContentCurationListWithPaging; content/AudioContentService.kt: getAudioContentRanking |
GetAudioContentMainItem.creatorNickname / GetAudioContentRankingItem.creatorNickname, creatorId=member.id |
CURRENT. 각 owner ID는 기존 projection 보유. 해당 service assembly에서 batch 또는 명시 Lang를 query 응답 조립까지 전달. 필터/count/정렬 변경 금지. | T09, T12; T05는 대표 query이고 이 행 전체 검증을 대체하지 않음 |
| L25 | content/AudioContentRepository.kt: getNewContentUploadCreatorList; content/main/AudioContentMainService.kt: getNewContentUploadCreatorList |
ContentCreatorResponse.creatorNickname, creatorId=member.id |
CURRENT. service에서 batch, creator별 contentList 기존 로직 유지. | T09 |
| L26 | content/main/curation/AudioContentCurationQueryRepository.kt: findByCurationId; content/main/curation/AudioContentCurationService.kt: getCurationContent, getContentCurationList |
GetAudioContentMainItem.creatorNickname, creatorId=member.id |
CURRENT. curation ID를 owner로 쓰지 않음. service에서 batch; active/genre/adult/block/count 유지. | T09 |
| L27 | content/main/tab/content/ContentMainTabTagCurationRepository.kt: getTagCurationContentList; content/main/tab/content/ContentMainTabTagCurationService.kt: getTagCurationContentList |
GetAudioContentMainItem.creatorNickname, creatorId=member.id |
CURRENT. tag curation content query caller에서 명시 Lang+batch. | T09 |
| L28 | content/main/tab/AudioContentMainTabRepository.kt: findCreatorByThemeContent, findCreatorWithHasFreeContent |
ContentCreatorResponse.creatorNickname, creatorId=member.id |
CURRENT. caller는 content/main/tab/asmr/AudioContentMainTabAsmrService.kt, content/main/tab/replay/AudioContentMainTabLiveReplayService.kt, content/main/tab/free/AudioContentMainTabFreeService.kt의 fetchData. 배치 표시 후 creator별 인기 콘텐츠는 L17을 재사용. |
T09 |
| L29 | content/main/tab/RecommendSeriesRepository.kt: getNewSeriesList, getRecommendSeriesList; content/main/tab/free/AudioContentMainTabFreeService.kt, content/main/tab/series/AudioContentMainTabSeriesService.kt: fetchData |
GetRecommendSeriesListResponse.creatorNickname, creatorId=series.member.id |
CURRENT. 추천 row의 ID/seriesId가 아닌 creatorId로 service batch. | T09 |
| L30 | content/main/tab/home/AudioContentMainTabHomeService.kt, content/main/tab/content/AudioContentMainTabContentService.kt, content/main/tab/alarm/AudioContentMainTabAlarmService.kt, content/main/tab/series/AudioContentMainTabSeriesService.kt 및 L28의 세 service: fetchData와 개별 content/rank/creator/series 조회 메서드 |
중첩 content creatorNickname / series.creator.nickname / ContentCreatorResponse.creatorNickname | CURRENT. 각 nested producer L17/L18/L24~29/L31 적용 누락 확인. 기존 반환된 공통 DTO를 재로컬라이즈하거나 parent/tab ID로 lookup하지 않음. 별도 wildcard 리팩터링 없음. | T09 |
| L31 | content/series/ContentSeriesRepository.kt: getSeriesListV2, getSeriesByGenreListV2, getRecommendSeriesListV2, getOriginalAudioDramaList, findByCurationIdV2, getDayOfWeekSeriesListV2; content/series/ContentSeriesService.kt: getSeriesList, getSeriesListByGenre, getRecommendSeriesList, getOriginalAudioDramaList, fetchSeriesByCurationId, getDayOfWeekSeriesList |
GetSeriesListResponse.SeriesListItem.creator.nickname, creatorId=series.member.id; seriesId는 resource |
CURRENT. V2라는 메서드 이름이어도 non-v2 파일의 레거시 caller이므로 포함. 기존 locale은 series title 번역용. 각 반환 목록 조립에서 owner batch; title/genre/price/adult/block/count/요일/정렬 유지. | T13, T07, T09; 기존 content/series/main/SeriesMainControllerTest.kt |
| L32 | content/series/ContentSeriesService.kt: getSeriesDetail; content/series/ContentSeriesRepository.kt: getSeriesDetail |
GetSeriesDetailResponse.creator.nickname, series.member.id |
CURRENT. 상세 DTO 조립에서 batch. Series.writer, studio, title/introduction/keyword는 creator nickname 아님. |
T13 |
| L33 | content/comment/AudioContentCommentRepository.kt: findByContentId, getAudioContentCommentReplyList; content/comment/AudioContentCommentService.kt: getCommentList, getCommentReplyList; content/AudioContentService.kt: getDetail |
GetAudioContentCommentListItem.nickname, writerId=comment.member.id |
CURRENT 혼합 작성자. parent/child 각 writer ID batch; 상세의 commentList도 포함. deleted-prefix fallback, active/block/secret/count/page 유지. blocked-by-creator HTTP 에러의 이름도 creator owner로 표시, FCM args는 제외. | T14, T12 |
| L34 | content/order/OrderRepository.kt: getAudioContentOrderList, getAudioContentMainOrderList; content/order/OrderService.kt: 대응 메서드 |
GetAudioContentOrderListItem.creatorNickname / GetAudioContentMainItem.creatorNickname, audioContent owner creatorId |
CURRENT. 주문 날짜는 역사지만 이름은 query가 현재 Member를 join함. service 응답 batch, KEEP/RENTAL/expiry/isActive/count/page 유지. | T15 |
| L35 | content/order/OrderRepository.kt: findBuyerListByContentId; content/AudioContentService.kt: getDetail; content/GetAudioContentDetailResponse.kt: ContentBuyer |
ContentBuyer.nickname, order.member.id가 buyer owner, 현재 projection은 nickname/profile만 있음 |
CURRENT 혼합 구매자. content creator ID로 조회하면 오동작. 내부 buyer member ID record를 추가하고 상세 assembly batch, 공개 ContentBuyer 타입 보존. | T12, T15 |
| L36 | content/AudioContentRepository.kt: fetchContentForPlaylist; content/playlist/AudioContentPlaylistService.kt: getPlaylistDetail; content/playlist/AudioContentPlaylist.kt: AudioContentPlaylistContent |
AudioContentPlaylistContent.creatorNickname, projection owner ID 없음; id=content.id |
CURRENT. Redis playlist는 content IDs/order/endDate만 저장. getPlaylistDetail에서 SQL 현재 이름 재조회하므로 historical name snapshot 아님. 내부 memberId 추가하여 query→service batch. Redis 저장 record 이름 rewrite/새 필드 없음. | T16 |
커뮤니티·후원·사용 내역·캐릭터 작성자·오디션
| ID | 실제 경로와 심볼 / caller | 응답 타입·이름 및 실제 owner | 유형·언어 / 최소 수정 경계 | Test owner |
|---|---|---|---|---|
| L37 | explorer/profile/creatorCommunity/CreatorCommunityRepository.kt: getCommunityPostList, getCommunityPost, getLatestPostListFromCreatorsYouFollow; explorer/profile/creatorCommunity/CreatorCommunityService.kt: getCommunityPostList, getCommunityPostDetail, getLatestPostListFromCreatorsYouFollow; explorer/profile/creatorCommunity/SelectCommunityPostResponse.kt: toCommunityPostListResponse |
GetCommunityPostListResponse.creatorNickname / SelectCommunityPostResponse.creatorNickname, post owner creatorId |
CURRENT. 본문 번역과 별개로 이름 batch. postId/firstComment writer를 owner와 혼동하지 않음. paid mask/adult/pin/block/count 유지. 상세·구매 시 blocked message의 creator 이름도 HTTP current-display. | T17 |
| L38 | explorer/profile/creatorCommunity/CreatorCommunity.kt: toCommunityPostListResponse; explorer/profile/creatorCommunity/CreatorCommunityService.kt: purchasePost |
같은 GetCommunityPostListResponse.creatorNickname, entity member.id |
CURRENT 구매 HTTP 응답. entity builder의 모든 호출을 전역 변경하지 말고 legacy service의 반환 DTO에서 override. v2/admin consumers는 별도 scope. | T17 |
| L39 | explorer/profile/creatorCommunity/comment/CreatorCommunityCommentRepository.kt: findByPostId, getCommunityCommentReplyList; explorer/profile/creatorCommunity/CreatorCommunityService.kt: getCommunityPostCommentList, getCommentReplyList 및 post list/detail의 firstComment |
GetCommunityPostCommentListItem.nickname, writerId=comment.member.id |
CURRENT mixed writer. 부모·reply·firstComment 각각 writer IDs batch, deleted-prefix/secret/block/count/page 유지. | T17 |
| L40 | explorer/profile/channelDonation/ChannelDonationMessageRepository.kt: getChannelDonationMessageList; explorer/profile/channelDonation/ChannelDonationService.kt: getChannelDonationList, getChannelDonationListForProfile |
GetChannelDonationListItem.nickname, donor memberId, creatorId는 목록 filter |
CURRENT mixed donor. 기존 secret/default message/삭제 prefix/adult/visibility/cursor 유지, donor member IDs batch. HTTP recipient render이며 FCM 전달은 제외. | T18 |
| L41 | can/CanRepository.kt: CanQueryRepositoryImpl.getCanUseStatus; can/UseCanQueryDto.kt; can/CanService.kt: getCanUseStatus |
GetCanUseStatusResponseItem.title 안 room/audio/community/recipient creator name. UseCanQueryDto.id=useCan.id; 각 owner ID 없음 |
CURRENT receipt: 현재 member를 join하며 이름 snapshot column 없음. qRoomMember.id, qAudioContentMember.id, qCommunityPostMember.id, qRecipientMember.id 내부 projection을 추가하고 service가 title를 만들기 전에 한 번 batch. payment gateway/refund/amount/distinct/page·resource title 우선 fallback 유지. live 이름도 이 HTTP receipt의 조회는 포함하되 live/* production 수정은 하지 않음. |
T19 |
| L42 | chat/character/comment/CharacterCommentService.kt: listComments, getReplies, getLatestComment, toCommentResponse, toReplyResponse; chat/character/comment/CharacterCommentDto.kt |
CharacterCommentResponse.memberNickname, CharacterReplyResponse.memberNickname, entity writer member.id |
CURRENT mixed writer. characterId는 owner 아님. 부모·답글 목록+original 및 latest에서 writer IDs batch하고 Lang 명시 전달. 캐릭터 자체 name/본문 번역은 변경 없음. |
T20 |
| L43 | audition/applicant/AuditionApplicantRepository.kt: getAuditionApplicantList; audition/applicant/AuditionApplicantService.kt: getAuditionApplicantList; audition/applicant/AuditionApplicantController.kt |
GetAuditionRoleApplicantItem.nickname, applicant memberId, applicantId는 resource |
CURRENT mixed applicant. service assembly batch, applicant/member 활성·role filter·vote count·sort/page 유지. admin audition은 제외. | T21 |
원본·불변·비이름 경계
| 경로와 심볼 | 분류와 결정 | 보존 검증 owner |
|---|---|---|
member/Member.kt: nickname, toExplorerSectionCreator; member/MemberAdapter.kt; member/MemberService.kt: loadUserByUsername |
RAW. entity/principal 원본 불변. toExplorerSectionCreator는 앱/관리/기타 공용이므로 이름 getter/global mapper를 변형하지 않고 앱 caller의 DTO를 변경한다. |
T04, T06; 기존 Member 서비스·보안 테스트 |
member/MemberController.kt: getMember (GET /member), changeNickname, profile PUT; member/MemberService.kt: getMember, updateNickname, profileUpdate; member/ProfileResponse.kt, member/ProfileUpdateRequest.kt |
RAW EDITOR 경계. GET·PUT가 동일 ProfileResponse를 쓰고 nickname editing input을 제공한다. 기존 raw 응답/입력을 임의 로컬라이즈하지 않는다. 외부 client가 GET을 표시에도 쓰는지는 EXT001 확인 전 blocked이며 서버 구현 완료로 raw editor 안전을 증명하지 않는다. 별도 current display는 L06/L07. | T06; 외부 EXT001 |
member/MemberRepository.kt: findByNickname, existsByNickname, findNicknamesWithPrefix; member/MemberService.kt: duplicateCheckNickname, validateNickname; member/nickname/NicknameGenerateService.kt |
RAW 식별·중복·생성. alias search 대상은 findByNicknameAndOtherCondition만, raw unique 검사나 자동 닉네임 생성은 변경 없음. |
T06 |
member/nickname/NicknameChangeLog.kt: prevNickname; member/MemberService.kt: updateNickname |
IMMUTABLE 변경 전 이름 기록. 기존 row rewrite 없음. 별칭 해제 시 과거 이름이 아니라 현재 Member.nickname으로 fallback. | T03, T04 |
member/login/LoginResponse.kt; member/MemberService.kt: login/signup/findOrRegister; member/auth/*, member/social/*, admin/*, creator/admin/*, partner/* |
RAW/제외. login·인증·원본 식별·관리/파트너 계약을 바꾸지 않는다. role+AI_CHARACTER인 CREATOR 표시는 포함, AI_CHARACTER kind만인 USER는 불변. | T03; 기존 auth/admin 테스트 |
chat/character/service/ChatCharacterCreatorMemberService.kt: ensureAiCharacterCreatorMember, syncAiCharacterCreatorMemberDisplayFields, syncDisplayFields |
RAW 동기화. ChatCharacter.name에서 원본 Member.nickname을 만든다. 별칭은 별도 테이블이므로 이 producer를 로컬라이즈하거나 원본 sync를 중단하지 않음. | 기존 chat/character/service/ChatCharacterCreatorMemberServiceTest.kt, ChatCharacterCreatorMemberServiceIntegrationTest.kt |
content/AudioContentService.kt: publishContentUploadedAfterCommit; explorer/profile/creatorCommunity/CreatorCommunityService.kt: publishCommunityPostCreatedAfterCommit |
신규 v2 following event producer. 과거 following rows는 rewrite하지 않는다. v2 producer/consumer inventory slice의 책임이며 이 단계에서 원본 args를 변형하지 않음. | v2 following slice |
content/AudioContentService.kt: uploadComplete/releaseContent; explorer/ExplorerService.kt: saveNotice; community/comment FcmEvent args |
새 async recipient payload는 이후 P3 slice. HTTP 이름 변경을 FCM title/args 변경으로 확대하지 않음. | async slice |
content/AudioContentRepository.kt: findByCreatorId, other/previous/next-content; content/series/content/ContentSeriesContentRepository.kt; content/series banner, theme/category/tag/genre title |
비이름 field. GetAudioContentListItem, OtherContentResponse, series-content item에는 creator nickname 없음. existing content title/본문/character.name·Series.writer/studio는 creator localized-name으로 대체하지 않음. |
T01, T04, T05, T12, T13 |
| L19/L20 cached nickname, Redis playlist detail | NEUTRAL-CACHE/current lookup, 과거 이름 snapshot 면제 아님. cache hit 뒤 표시 대체하고 shared cache DTO 원본 유지. playlist 이름은 SQL current 조회. | T10, T11, T16 |
후속 테스트 소유자
아래 경로의 공통 prefix는 src/test/kotlin/kr/co/vividnext/sodalive/다. 신규 예정 파일은 아직 생성하지 않았다. 이번 대표 15개 사례가 inventory 전체 GREEN 검증을 대신하지 않으며 해당 표면을 구현하기 전에 이 소유자의 RED 사례를 보강한다.
| Owner | 정확한 테스트 경로 | 상태·핵심 사례 |
|---|---|---|
| T06 | member/MemberServiceTest.kt, member/MemberControllerTest.kt; 신규 예정 member/MemberLocalizedNameEndToEndTest.kt |
기존 두 파일 있음. profile/myPage/search/block mixed USER, raw ProfileResponse·nickname edit·auth 불변. |
| T07 | api/home/HomeServiceTest.kt; 신규 예정 api/home/HomeLocalizedNameEndToEndTest.kt |
기존 있음. creator ranking·중첩 content/series/recommend/current names, guest KO/JA. |
| T08 | 신규 예정 rank/RankingLocalizedNameEndToEndTest.kt |
creator/content/series/current owner 및 period 결과·title 불변. |
| T09 | 신규 예정 content/main/LegacyContentMainLocalizedNameEndToEndTest.kt |
main/curation/tag/free/content/home/alarm/asmr/replay/series 탭의 creator와 ownernames. |
| T10 | 신규 예정 query/recommend/RecommendChannelLocalizedNameCacheTest.kt |
같은 member KO→JA→KO cache hit, alias update 후 current name, 원본 변경+clear 후 현재 원본 fallback, cached raw DTO 불변. |
| T11 | 신규 예정 explorer/profile/CreatorDonationRankingLocalizedNameCacheTest.kt |
donor CREATOR/USER 혼합, cache hit/miss와 Lang 순서·current alias 갱신·원본 변경+clear fallback, count cache 불변. |
| T12 | content/AudioContentServiceTest.kt; 신규 예정 content/AudioContentLocalizedNameEndToEndTest.kt |
기존 있음. 상세 creator·mixed buyers·comments, owner ID와 contentId 구분, 원본/번역 title 유지. |
| T13 | 신규 예정 content/series/ContentSeriesLocalizedNameEndToEndTest.kt; 기존 content/series/main/SeriesMainControllerTest.kt |
목록/genre/recommend/original/curation/dayOfWeek/detail ownernames, writer/studio/title 불변. |
| T14 | 신규 예정 content/comment/AudioContentCommentLocalizedNameEndToEndTest.kt |
mixed 부모/답글 및 상세 nested 목록, deleted original fallback/secret/block/page. |
| T15 | 신규 예정 content/order/OrderLocalizedNameEndToEndTest.kt |
주문 목록 현재 creator와 상세 mixed buyer, rental/expiry/count/public schema. |
| T16 | 신규 예정 content/playlist/AudioContentPlaylistLocalizedNameEndToEndTest.kt |
Redis metadata는 그대로, detail SQL 이름만 current owner alias·원본 fallback·순서. |
| T17 | explorer/profile/creatorCommunity/CreatorCommunityServiceTest.kt, CreatorCommunityTranslationEndToEndTest.kt; 신규 예정 explorer/profile/creatorCommunity/CreatorCommunityLocalizedNameEndToEndTest.kt |
기존 두 파일 있음. post/detail/following/firstComment/comment/reply mixed owners·본문 번역 유지. |
| T18 | explorer/profile/channelDonation/ChannelDonationServiceTest.kt, ChannelDonationMessageRepositoryTest.kt; 신규 예정 explorer/profile/channelDonation/ChannelDonationLocalizedNameEndToEndTest.kt |
기존 두 파일 있음. donor aliases는 channel creator와 독립; USER·secret/default text 불변. |
| T19 | can/CanServiceTest.kt, can/CanControllerIntegrationTest.kt; 신규 예정 can/CanReceiptLocalizedNameEndToEndTest.kt |
기존 두 파일 있음. recipient/audio/community/room owner ID 구분, title 우선순위와 원본 resource title·receipt 금액 유지. |
| T20 | 신규 예정 chat/character/comment/CharacterCommentLocalizedNameEndToEndTest.kt |
mixed comment/reply/latest/original, character name과 writer creator name 구분. |
| T21 | 신규 예정 audition/applicant/AuditionApplicantLocalizedNameEndToEndTest.kt |
CREATOR/USER applicant owner, applicantId≠memberId, likes/count/order 유지. |
정적 확인과 남은 검증
rg로 non-v2 nickname/projection 사용을 전체 검색하고 실제 caller/service/DTO를 읽어 목록에 연결했다. 추가 발견은 blocked member, audition applicant, buyerList, recommend series, receipt의 owner ID 누락이다.- foundation
CreatorLocalizedNameEntity.kt의 실제 table/column names와 fixture SQL을 대조했다.JAenum NAME·member FK·timestamp 값을 일치시켰다. - 신규 테스트의 130자 초과/trailing whitespace 검색 결과 0건. production helper, reflection, 강제 실패 placeholder는 추가하지 않았다.
- Gradle/test/build/lint와 실제 HTTP 실행은 의도적으로 미실행이다. 부모의 serialized RED가 다음 검증 단계이며 이 문서는 RED 성공 또는 GREEN 완료를 주장하지 않는다.
- 현재 도구 목록에
lsp_diagnostics가 없어 Kotlin LSP 진단을 수행하지 못했다. 컴파일/context 오류가 보이면 부모 RED에서 먼저 수정하고 요구 동작 assertion 실패를 별도로 기록해야 한다. - 외부 EXT001 raw editor/client display/cache 계약과 운영 DDL/MySQL collation은 이 테스트 단계에서 검증하지 않았다. 기존 문서의 승인 이전 문구를 덮어쓰지 않고 COR012를 기준으로 삼았다.
후속 GREEN 구현 연결 기록
실행 근거와 상태
- 부모 제공 RED:
/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/creatorname-consumer-red.log. foundation 이후 기존 consumer 테스트가 실행됐고 별칭 hit/표시 assertion 실패를 확인했다. 이 worker는 Gradle을 실행하지 않았다. - HTTP fixture 오류도 별도로 확인했다.
build/test-results/test/TEST-kr.co.vividnext.sodalive.explorer.ExplorerLocalizedNameEndToEndTest.xml의 profile와 unified 요청은success=false, 로그인 오류와data=null이다. stack은MemberContentPreferenceService.initializeDefaultPreference→resolveForQuery(REQUIRES_NEW)다. 미커밋 viewer가 별도 트랜잭션에서 보이지 않았으며 JSON field명이 잘못된 것이 아니다.TestTransaction으로 fixture를 먼저 커밋하고 다음 검증 트랜잭션을 시작하도록 수정했다. 별칭/원본/title/owner ID 기대값을 완화하지 않았다. - 부모 lint 로그
creatorname-foundation-final-lint.log에 나온 5개 신규 테스트의 JDBC 인자를 각각 별도 줄로 수정했다. main 새 import 순서와 130자 길이도 정적으로 대조했다. v2/creatorname/application/LegacyCreatorNameRenderer.kt는 명시 Lang의 페이지 batch/copy와 중립 캐시의 현재 원본+별칭 map을 제공한다. 내부에서 foundation의 정확한findLocalizedNames(Collection<Long>, Lang)를 호출한다. 요청 컨텍스트/기본-null service/전역 serializer/Member 변이는 없다.v2/creatorname/adapter/out/persistence/CreatorNamePredicates.kt의 owner 상관EXISTS를 SearchRepository 6개 list/count 조건, Explorer channel, MemberQuery 검색에 적용했다. 원본 containsIgnoreCase 및 기존 다른 검색 조건·필터·group/count/page는 유지했다.
L01~L43 연결 결과
| Inventory | 구현 경계 / 정적 판단 | runtime 상태 |
|---|---|---|
| L01~L03 | SearchService가 Lang을 명시 전달. creator title+nickname 대체. search/SearchResponse.kt의 SearchNameRow에 content/series 실제 ownerId를 내부 projection으로 추가하고 공개 SearchResponseItem으로 변환. |
부모 GREEN 대기 |
| L04~L08 | Explorer/Member 검색·타 회원 profile·myPage·blocked list의 response assembly에 batch/copy 적용. 원본 편집 GET/PUT·login·nickname 식별/변경은 기존 raw 그대로. | 부모 GREEN 대기 |
| L09~L14 | 탐색 섹션, profile/detail/blocked error, similar creator, follower, 부모/답글 cheers, fanTalk 쓰기 응답을 owner별 batch/copy로 연결. profile 내 live manager는 같은 profile creator의 이름을 복사하되 live 소스/저장 객체는 수정하지 않음. | 부모 GREEN 대기 |
| L15~L18 | Home creator ranking, RankingService의 creator/content/series 공통 assembly와 각각의 Top4/Top20 소비자를 연결. 순위/가격/수량/소유/요일/title fields는 그대로. | 부모 GREEN 대기 |
| L19~L20 | HomeService와 ExplorerService에서 neutral cache hit 뒤 현재 Member 원본을 batch 재조회하고 alias를 합쳐 새 DTO로 반환. recommend/donation cache producer·key·TTL·저장 entry는 그대로. | 부모 GREEN 대기 |
| L21 | ExplorerQueryRepository.getMemberDonationRanking의 실제 앱 caller가 없음을 rg로 다시 확인. 현재 앱은 cached CreatorDonationRankingService를 사용하므로 사용되지 않는 query를 별도로 수정하지 않음. |
연결 제외: 사용되지 않는 projection |
| L22~L23 | Audio 상세 creator/comment/buyer IDs 합집합, latest-content locale query의 owner IDs로 batch. title/detail/theme 번역은 기존 로직. ContentBuyerRow는 내부 projection이며 공개 ContentBuyer에는 ID를 추가하지 않음. |
부모 GREEN 대기 |
| L24~L30 | main/theme/curation/tag 및 alarm/content/free/asmr/replay/series 탭에 전체 응답 owner IDs batch 적용. Home tab은 이미 수정된 RankingService를 재사용하여 별도 중복 렌더링하지 않음. cached main 목록은 controller에서 cache hit 뒤 처리한다. | 부모 GREEN 대기 |
| L31~L32 | ContentSeriesService의 모든 목록·genre/recommend/original/curation/dayOfWeek/detail 경계를 연결. 공유 repository/Series entity/admin output을 전역 변경하지 않음. | 부모 GREEN 대기 |
| L33 | Audio 댓글/답글은 명시 Lang 인자를 추가. 앱 AudioContentCommentController만 요청 Lang을 전달하고 기존 shared ADMIN 호출은 기본 KO로 raw 이름 보존. 상세 nested comments도 포함. | 부모 GREEN 대기 |
| L34~L36 | Order response assembly, 내부 ContentBuyerRow, 내부 PlaylistContentRow로 실제 owner IDs 확보. Redis playlist metadata 및 공개 playlist/buyer DTO는 그대로. | 부모 GREEN 대기 |
| L37~L39 | Community 목록/detail/following/purchase, firstComment, 댓글/답글을 연결. 댓글 shared ADMIN consumer가 있어 기본 KO/앱 controller 명시 Lang 경계를 분리. entity/v2 DTO factory 및 번역/paid mask는 그대로. | 부모 GREEN 대기 |
| L40~L43 | donor 목록, Can internal DTO의 room/audio/community/recipient IDs, character comment/reply/latest writers, audition applicant memberIds를 batch로 연결. receipt title 우선순위·금액·resource 제목, USER 이름, count/page/filter는 그대로. | 부모 GREEN 대기 |
GREEN 감사에서 추가 확인한 캐시·공유 경계
- L24/L25의
content/main/AudioContentMainService.kt는newContentUploadCreatorList와getAudioContentCurationListWithPaging의 raw creator 이름 포함 결과를defaultcache에 저장한다.content/main/AudioContentMainController.kt에서 cache hit 뒤 전체 owner IDs의 현재 이름을 조회해 복사했다. content/AudioContentService.kt.getAudioContentRanking은 Lang 없는contentRankingkey와cache_ttl_3_days에 이름 포함 결과를 저장한다.content/AudioContentController.kt에서 cache hit 뒤 현재 이름을 batch/copy 처리했다.- L24에서 사용하는
content/theme/AudioContentThemeService.kt.getContentByTheme에도 이름 포함 item이 있어 연결했다. theme/title 자체의 번역 계약은 변경하지 않았다. - v2 ADMIN
AiCharacterAdminCommunityPostFacade.comments/commentReplies,AiCharacterAdminAudioContentFacade.comments/commentReplies가 legacy comment 서비스를 실제 재사용한다. 이 service 읽기의 기본 Lang은 KO이고 앱 controller만 요청 Lang을 명시한다. v2 ADMIN 파일을 수정하거나 route/principal로 언어를 추론하지 않았다. - 기존 neutral cache의 콘텐츠 제목/날짜 locale 문제나 visibility 수명은 이번 이름 변경으로 확장 수정하지 않았다. aliases와 현재 원본의 표시만 cache 밖에서 갱신한다.
소유한 FCM 호출 metadata
delivery-name-inventory.md #9/#10/#11/#12/#14/#15의 6곳에 titleMemberId를 명시했다. Explorer notice/community post/comment는 실제 작성 member ID, audio upload/release는 audio owner ID, audio reply는 parent?.member?.id ?: member.id다. 기존 title/args/content 제목·deepLink·senderMemberId는 그대로다. 특히 reply snapshot의 sender는 실제 commenter이며 title owner와 다를 수 있다. 중앙 FcmEvent/async/live/message/v2 소스는 수정하지 않았다.
추가 회귀와 constructor fixture
추가 테스트는 아직 실행하지 않았다. 이번 원래 15개와 아래 13개를 합쳐 대상 이름 회귀는 28개 실행 사례다. 새 회귀의 RED/GREEN을 관찰했다고 주장하지 않으며 부모가 별도 검증 기록을 남긴다.
| Owner | 정확한 테스트 파일 | 사례 / 보호 경계 |
|---|---|---|
| T22 | src/test/kotlin/kr/co/vividnext/sodalive/v2/creatorname/application/LegacyCreatorNameRendererTest.kt |
기존 2 + 리뷰 회귀 4: 실제 foundation/JPA batch의 1 select(작은/중복 큰 페이지), KO 0 alias select, USER/missing/원본 DTO 보존; neutral cache의 원본 변경+clear 현재 fallback. 현재 이름 경로는 서로 다른 1/4명에서 JA 2 SQL, KO/EN 1 SQL, entityLoadCount 0 및 빈 입력 0 SQL. |
| T23 | src/test/kotlin/kr/co/vividnext/sodalive/v2/creatorname/application/LegacyCurrentNameReadIntegrationTest.kt |
9: profile/myPage/blocked/raw editor, 주문+Redis playlist, mixed buyer 상세, community post/firstComment/reply+shared raw default, donor, receipt 실제 projection, character writers, applicant memberId, donor neutral Redis cache hit/원본 변경+clear/과거 DTO·cache entry 불변. |
| T24 | src/test/kotlin/kr/co/vividnext/sodalive/content/comment/AudioContentCommentNameMetadataTest.kt |
2 parameterized: parent CREATOR/commenter USER와 반대 조합. 실제 JPA/댓글 service를 호출하고 outbound event publisher만 capture하여 title owner≠snapshot sender, 원본 title 및 content title args/empty name slots 유지. |
기존 직접 constructor fixture는 새 dependency를 명시적으로 추가했다. legacy JPA slice에는 src/test/kotlin/kr/co/vividnext/sodalive/support/LegacyCreatorNameTestConfiguration.kt를 import했다. 이 configuration은 renderer, foundation service, adapter를 제공한다. 기존 isolated unit fixture의 originalLegacyNameRenderer()는 실제 renderer와 이름 없는 dependency doubles를 사용하며 production default-null 호환성 경로가 아니다.
소유 범위 밖의 v2/foundation/admin/partner/async JPA slice도 AudioContent custom repository를 스캔한다면 위 test configuration import가 필요하다. 해당 파일은 직접 수정하지 않았다. 직접 constructor가 있는 v2/api/admin/aicharacter/community/LegacyCommunityPostCharacterizationTest.kt와 v2/api/admin/aicharacter/fantalk/LegacyFanTalkReplyCharacterizationTest.kt도 소유자가 nameRenderer 인자를 명시해야 한다. 이 항목은 부모에 전달했으며 해당 소유자 수정 여부는 부모의 최종 compile에서 확인한다.
정확한 변경 소스 manifest
아래 공통 prefix는 src/main/kotlin/kr/co/vividnext/sodalive/다. 기존 row의 query producer는 current/raw 데이터를 유지하고 실제 response assembly만 수정한 경우도 포함해 연결 경계를 표시했다.
v2/creatorname/application/LegacyCreatorNameRenderer.kt
v2/creatorname/adapter/out/persistence/CreatorNamePredicates.kt
search/SearchRepository.kt
search/SearchResponse.kt
search/SearchService.kt
member/MemberRepository.kt
member/MemberService.kt
explorer/ExplorerQueryRepository.kt
explorer/ExplorerService.kt
explorer/profile/creatorCommunity/CreatorCommunityService.kt
explorer/profile/creatorCommunity/CreatorCommunityController.kt
explorer/profile/channelDonation/ChannelDonationService.kt
api/home/HomeService.kt
rank/RankingService.kt
content/AudioContentRepository.kt
content/AudioContentService.kt
content/AudioContentController.kt
content/GetAudioContentDetailResponse.kt
content/series/ContentSeriesService.kt
content/comment/AudioContentCommentService.kt
content/comment/AudioContentCommentController.kt
content/order/OrderRepository.kt
content/order/OrderService.kt
content/playlist/AudioContentPlaylist.kt
content/playlist/AudioContentPlaylistService.kt
content/main/AudioContentMainService.kt
content/main/AudioContentMainController.kt
content/main/curation/AudioContentCurationService.kt
content/main/tab/content/ContentMainTabTagCurationService.kt
content/main/tab/content/AudioContentMainTabContentService.kt
content/main/tab/free/AudioContentMainTabFreeService.kt
content/main/tab/alarm/AudioContentMainTabAlarmService.kt
content/main/tab/asmr/AudioContentMainTabAsmrService.kt
content/main/tab/replay/AudioContentMainTabLiveReplayService.kt
content/main/tab/series/AudioContentMainTabSeriesService.kt
content/theme/AudioContentThemeService.kt
can/CanRepository.kt
can/CanService.kt
can/UseCanQueryDto.kt
chat/character/comment/CharacterCommentService.kt
audition/applicant/AuditionApplicantService.kt
변경 테스트 manifest의 공통 prefix는 src/test/kotlin/kr/co/vividnext/sodalive/다. 기존 JPA 파일의 변경은 필요한 test configuration import만이며 본래 테스트 로직을 정리하거나 완화하지 않았다.
support/LegacyCreatorNameTestConfiguration.kt
v2/creatorname/application/LegacyCreatorNameRendererTest.kt
v2/creatorname/application/LegacyCurrentNameReadIntegrationTest.kt
content/comment/AudioContentCommentNameMetadataTest.kt
search/SearchRepositoryLocalizedNameTest.kt
search/SearchServiceTest.kt
explorer/ExplorerQueryRepositoryLocalizedNameTest.kt
explorer/ExplorerLocalizedNameEndToEndTest.kt
explorer/ExplorerServiceTest.kt
member/MemberRepositoryLocalizedNameTest.kt
member/MemberServiceContentPreferenceTest.kt
member/MemberServiceCacheEvictionTest.kt
content/AudioContentRepositoryLocalizedNameTest.kt
content/AudioContentServiceTest.kt
api/home/HomeServiceTest.kt
can/CanServiceTest.kt
explorer/profile/creatorCommunity/CreatorCommunityServiceTest.kt
explorer/profile/creatorCommunity/CreatorCommunityTranslationEndToEndTest.kt
explorer/profile/channelDonation/ChannelDonationServiceTest.kt
explorer/profile/channelDonation/ChannelDonationMessageRepositoryTest.kt
content/CreatorCommunityLanguageDetectTest.kt
content/CreatorCommunityLanguageDetectionCacheConcurrencyTest.kt
content/main/banner/AudioContentBannerRepositoryTest.kt
event/EventRepositoryTest.kt
member/contentpreference/MemberContentPreferenceIntegrationTest.kt
osiv/OsivLazyLoadingRegressionTest.kt
부모 전용 GREEN 명령과 검증 한계
./gradlew test --tests 'kr.co.vividnext.sodalive.search.SearchRepositoryLocalizedNameTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.explorer.ExplorerQueryRepositoryLocalizedNameTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.member.MemberRepositoryLocalizedNameTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.content.AudioContentRepositoryLocalizedNameTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.explorer.ExplorerLocalizedNameEndToEndTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.v2.creatorname.application.LegacyCreatorNameRendererTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.v2.creatorname.application.LegacyCurrentNameReadIntegrationTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.content.comment.AudioContentCommentNameMetadataTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.search.SearchServiceTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.content.AudioContentServiceTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.explorer.ExplorerServiceTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunityServiceTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunityTranslationEndToEndTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.explorer.profile.channelDonation.ChannelDonationServiceTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.api.home.HomeServiceTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.member.MemberServiceContentPreferenceTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.member.MemberServiceCacheEvictionTest'
./gradlew test --tests 'kr.co.vividnext.sodalive.can.CanServiceTest'
./gradlew ktlintCheck
./gradlew assemble
- 현재 확인은 source caller/constructor/DTO/owner ID/Lang 및 cache producer/consumer 대조다. public response field 이름·타입과 Member/principal/original editor 불변을 소스에서 점검했다.
- 변경 코드의 길이/후행 공백과 신규 테스트의 placeholder/Disabled/reflection을
rg로 점검했다. lint 로그의 인자 줄바꿈 수정은 실제 ktlint 재실행 PASS를 뜻하지 않는다. - kapt가 생성할
QSearchNameRow,QContentBuyerRow,QPlaylistContentRow및 변경 QUseCanQueryDto constructor는 부모 compile/test가 검증한다. LSP 도구는 제공되지 않았다. - Gradle, build, lint, 실제 HTTP/SDK/Redis QA는 이번 GREEN worker에서 실행하지 않았다. 앱 전체/외부 cache/editor/RTC/provider 및 운영 DB는 기존 blocker를 유지한다.
COR013: DB 직접 이름 JOIN 전환
근거와 검증 상태
- 기준은 PRD 10절과 JOIN-T1~JOIN-T3다. 위의 이전 JA 전용 batch 구현·검증 기록은 당시 이력이며 아래 분류가 현재 조회 경계다.
- 부모 제공 실제 RED 로그는
/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/creatorname-join-consumer-red.log다.SearchRepositoryLocalizedNameTest.shouldSearchContentOwnerAliasWithMatchingCountAndExistingContentFilters의 표시 값은 맞았지만 standalone alias SELECT가 0이 아닌 1이었다. 해당 SQL assertion은 그대로 유지했다. - 공통
CreatorNameJoin(owner, lang, alias)의 member ID·요청 Lang·활성 이름·CREATOR 조건을 ON으로 연결한다. 목록 SQL이 COALESCE 이름을 반환하며 count와 후보 ID 쿼리에는 표시 JOIN을 추가하지 않는다. JOIN별 alias는 owner별로 분리했다. - 이 worker는 Gradle, 테스트, build, ktlint, 실제 HTTP를 실행하지 않았다. LSP 도구도 제공되지 않았다. 아래는 소스·호출자·fixture 연결 및 정적 점검 결과이며 runtime GREEN 판정은 부모의 직렬 실행에 남긴다.
L01~L43 현재 분류
| Inventory | 현재 구현 / 실제 쿼리와 경계 |
|---|---|
| L01~L03 | SearchRepository.searchCreatorList/searchContentList/searchSeriesList가 요청 Lang JOIN으로 공개 SearchResponseItem을 직접 투영한다. creator title+nickname을 함께 선택하고 content/series title은 그대로다. SearchNameRow와 후속 alias batch를 제거했다. JA EXISTS 검색 및 count·필터·page는 유지한다. |
| L04~L06, L08 | ExplorerQueryRepository.getSearchChannel, MemberQueryRepositoryImpl.findByNicknameAndOtherCondition/getMemberProfile, BlockMemberQueryRepositoryImpl.getBlockedMemberList에서 표시 이름을 함께 선택한다. 검색 결과는 Member를 변이하지 않는 기존 GetRoomDetailUser로 반환한다. 앱 caller는 요청 Lang을 명시한다. |
| L07 | MemberService.getMyPage는 인증 principal의 raw Member를 그대로 사용하므로 DTO 경계 sparse batch를 유지한다. 원본 편집·login·nickname 식별은 raw다. |
| L09, L11~L13 | ExplorerQueryRepository.getCreatorRankings/getNewCreators/findCreatorByGender/getSimilarCreatorList/getRandomCreatorList/getFollowerList/getCheersList에 JOIN을 적용했다. creator DTO 및 cheers 부모·답글 이름은 쿼리에서 선택한다. cheers 답글은 페이지 부모 IDs의 별도 원래 데이터 조회에 같은 JOIN을 적용하며 단독 alias 조회를 하지 않는다. count·차단·부모/답글 노출 의미는 유지한다. |
| L10, L14 | profile/detail은 role·차단·노출 및 중첩 조립에 필요한 raw Member 경계, fanTalk 작성/수정은 저장 entity 반환 경계다. 기존 sparse batch로 HTTP DTO/차단 오류에만 표시한다. FCM 원본 args/title과 principal은 바꾸지 않는다. |
| L15~L18 | RankingRepository.getCreatorRankings/getAudioContentRanking/getSeriesRanking/getCompleteSeriesRanking/getSeriesAllRankingByGenre/fetchCreatorBySellContentCountRankTop20/fetchCreatorContentBySalesCountTop4/fetchCreatorBySeriesRevenueRankTop20/fetchCreatorSeriesBySales/fetchFreeContentByCreatorIdTop4/fetchCreatorContentByLikeCountTop4에서 이름을 선택한다. creator는 기존 DTO, series는 원본 entity+SQL 표시 이름 tuple로 조립하고 RankingService/HomeService의 이름 전용 batch를 제거했다. 집계·요일·후속 content count/new 판정은 그대로다. |
| L19~L20 | 추천 채널·donation ranking의 cache producer, DTO, key, TTL은 raw·언어 중립으로 유지한다. HomeService.fetchData, ExplorerService.renderDonationRanking이 cache hit 뒤 공통 단일 JOIN current-name batch를 호출한다. cached 원본을 현재 fallback으로 간주하지 않는다. |
| L21 | ExplorerQueryRepository.getMemberDonationRanking도 명시 Lang이 있으면 donor JOIN을 선택하도록 전환했다. 실제 앱 caller는 여전히 L20 cached service다. null Lang의 기존 raw 모드를 보존하고 새 caller는 만들지 않았다. |
| L22~L23 | Audio 상세는 권한·구매·번역에 필요한 원본 audio/member 경계의 creator sparse batch만 유지한다. buyer와 nested comment는 각 SQL에서 이름을 선택한다. AudioContentQueryRepositoryImpl.getLatestContentByTheme의 locale 경로는 같은 title tuple SQL에 JOIN을 추가했고 repository renderer 의존성을 제거했다. locale=null은 기존 raw query 모드다. |
| L24~L27 | findByTheme/findAlarmContentByTheme/findByThemeFor2Weeks/findAudioContentByCurationId/findAudioContentByCurationIdV2/findContentByCurationId, AudioContentCurationQueryRepository.findByCurationId, ContentMainTabTagCurationRepository.getTagCurationContentList가 명시 Lang JOIN을 지원한다. 앱 main/theme/curation/tag caller는 Lang을 전달하고 후속 이름 copy/batch를 제거했다. 중립 cache를 만드는 findAudioContentByCurationId의 null Lang과 getAudioContentRanking raw producer는 유지한다. |
| L25 | getNewContentUploadCreatorList는 이름 포함 중립 cache producer이므로 raw다. AudioContentMainController의 cache-hit 이후 current-name batch를 유지한다. |
| L28~L30 | AudioContentMainTabRepository.findCreatorByThemeContent/findCreatorWithHasFreeContent, RecommendSeriesRepository.getNewSeriesList/getRecommendSeriesList가 JOIN으로 반환한다. alarm/content/free/asmr/replay/series의 모든 직접 content/curation/creator/series caller에 Lang을 전달했고 탭 전체 응답을 다시 복사하던 alias batch를 제거했다. home 탭은 변경된 RankingService/ContentSeriesService를 그대로 재사용한다. |
| L31~L32 | ContentSeriesQueryRepositoryImpl.getSeriesListV2/getSeriesByGenreListV2/getRecommendSeriesListV2/getOriginalAudioDramaList/findByCurationIdV2/getDayOfWeekSeriesListV2의 기존 locale tuple SQL에 JOIN을 추가했다. 목록 service의 renderSeries를 제거했다. 상세는 접근/번역/가격/요일에 필요한 raw Series entity 경계의 sparse batch를 유지한다. writer/studio/title과 공용 entity는 그대로다. |
| L33 | AudioContentCommentQueryRepositoryImpl.findByContentId/getAudioContentCommentReplyList가 writer 이름을 선택한다. 앱 controller 및 Audio 상세는 요청 Lang, 기존 shared ADMIN 호출은 lang=null의 명시 raw 모드다. KO를 raw의 대용으로 사용하지 않는다. secret/block/count/page와 원본 deleted-prefix fallback은 보존한다. |
| L34~L36 | OrderQueryRepositoryImpl.getAudioContentOrderList/getAudioContentMainOrderList/findBuyerListByContentId, AudioContentQueryRepositoryImpl.fetchContentForPlaylist에서 이름을 선택한다. 공개 buyer/playlist DTO로 직접 투영해 ContentBuyerRow/PlaylistContentRow와 이름 전용 owner IDs·copy/batch를 제거했다. Redis playlist metadata, 순서, KEEP/RENTAL/expiry 조건은 그대로다. |
| L37~L39 | CreatorCommunityQueryRepositoryImpl.getCommunityPostList/getCommunityPost/getLatestPostListFromCreatorsYouFollow, CreatorCommunityCommentQueryRepositoryImpl.findByPostId/getCommunityCommentReplyList가 JOIN 이름을 선택한다. firstComment에도 요청 Lang을 전달한다. 목록/detail의 renderPosts를 제거했고 구매 쓰기 반환의 creator sparse batch만 유지한다. shared ADMIN 댓글 기본값은 null raw, 앱만 요청 Lang이다. 본문 번역·유료 마스크·secret/filter는 그대로다. |
| L40 | ChannelDonationMessageQueryRepositoryImpl.getChannelDonationMessageList가 donor entity+SQL 표시 이름 tuple을 반환한다. service의 donor alias batch를 제거했다. 원본 이름의 deleted-prefix fallback, secret/default message·날짜·정렬·count는 보존한다. |
| L41 | CanQueryRepositoryImpl.getCanUseStatus의 room/audio/community/recipient owner 4개에 각각 고유 JOIN을 연결했다. CanService는 받은 이름으로 기존 title 우선순위를 적용한다. 후속 이름 map, copy와 UseCanQueryDto의 이름 batch 전용 owner IDs를 제거했다. 금액·결제/refund/distinct/page 및 resource 제목은 그대로다. |
| L42 | CharacterCommentQueryRepositoryImpl.findDisplayComments/findDisplayReplies/findDisplayComment가 writer 이름을 같은 SQL에서 선택한다. list/latest 및 replies의 original도 이 query를 사용하며 service의 alias batch를 제거했다. 원본 entity를 변경하지 않고 기존 comment/reply DTO로 조립한다. cursor·active·정렬·reply count와 캐릭터 자체 이름은 그대로다. |
| L43 | AuditionApplicantQueryRepositoryImpl.getAuditionApplicantList가 applicant member JOIN 이름을 투영한다. service에 Lang을 명시하고 alias batch를 제거했다. vote 집계·role·활성·page 조건은 유지한다. |
남은 이름 batch 호출: 정확히 15곳
아래는 non-v2 담당 소스에서 nameRenderer.findCurrentNames/findLocalizedNames/render를 검색한 결과다. 직접 DB 목록을 이미 조회했다는 이유로 남긴 batch는 없다.
| 분류 | 실제 호출 위치 | 이유 |
|---|---|---|
| NEUTRAL-CACHE, current 5곳 | api/home/HomeService.fetchData; explorer/ExplorerService.renderDonationRanking; content/main/AudioContentMainController.getNewContentUploadCreatorList/getAudioContentCurationListWithPaging; content/AudioContentController.getAudioContentRanking |
cache hit 이후 현재 원본+요청 Lang 별칭을 공통 단일 JOIN batch로 읽고 새 응답을 만든다. 원본 cache payload는 유지한다. |
| PRINCIPAL, sparse 1곳 | member/MemberService.getMyPage |
인증 principal을 변이하지 않고 표시 DTO만 렌더한다. |
| RAW ENTITY DETAIL, sparse 4곳 | explorer/ExplorerService.getCreatorDetail/getCreatorProfile; content/AudioContentService.getDetail; content/series/ContentSeriesService.getSeriesDetail |
role/접근/구매/가격/번역 등 기존 비즈니스 조립에 필요한 raw entity의 이름을 덮어쓰지 않는다. |
| WRITE / ERROR, sparse 5곳 | explorer/ExplorerService.writeCheers의 차단 오류, renderFanTalk의 작성/수정 반환; content/comment/AudioContentCommentService.registerComment의 차단 오류; explorer/profile/creatorCommunity/CreatorCommunityService.purchasePost의 차단 오류 및 구매 반환 |
저장·검증에 사용한 raw entity와 기존 event args/title을 유지하고 HTTP 반환/오류만 렌더한다. |
별칭 표시가 없는 원본 인증/editor/admin/partner 및 역사 저장 이름은 기존 계약을 유지한다. FCM metadata 변경 이력은 위 절 그대로이며 COR013에서 해당 event title/args를 다시 수정하지 않았다.
이번 source manifest
공통 prefix는 src/main/kotlin/kr/co/vividnext/sodalive/다. 공유 core/helper/test support, v2, live/message/fcm, chat room repository 및 부모 소유 PRD/plan은 수정하지 않았다.
search/SearchRepository.kt
search/SearchResponse.kt
member/MemberRepository.kt
member/MemberService.kt
member/block/BlockMemberRepository.kt
explorer/ExplorerQueryRepository.kt
explorer/ExplorerService.kt
explorer/profile/creatorCommunity/CreatorCommunityRepository.kt
explorer/profile/creatorCommunity/CreatorCommunityService.kt
explorer/profile/creatorCommunity/comment/CreatorCommunityCommentRepository.kt
explorer/profile/channelDonation/ChannelDonationMessageRepository.kt
explorer/profile/channelDonation/ChannelDonationService.kt
api/home/HomeService.kt
rank/RankingRepository.kt
rank/RankingService.kt
content/AudioContentRepository.kt
content/AudioContentService.kt
content/GetAudioContentDetailResponse.kt
content/comment/AudioContentCommentRepository.kt
content/comment/AudioContentCommentService.kt
content/series/ContentSeriesRepository.kt
content/series/ContentSeriesService.kt
content/order/OrderRepository.kt
content/order/OrderService.kt
content/playlist/AudioContentPlaylist.kt
content/playlist/AudioContentPlaylistService.kt
content/main/AudioContentMainService.kt
content/main/curation/AudioContentCurationQueryRepository.kt
content/main/curation/AudioContentCurationService.kt
content/main/tab/AudioContentMainTabRepository.kt
content/main/tab/RecommendSeriesRepository.kt
content/main/tab/content/ContentMainTabTagCurationRepository.kt
content/main/tab/content/ContentMainTabTagCurationService.kt
content/main/tab/content/AudioContentMainTabContentService.kt
content/main/tab/free/AudioContentMainTabFreeService.kt
content/main/tab/alarm/AudioContentMainTabAlarmService.kt
content/main/tab/asmr/AudioContentMainTabAsmrService.kt
content/main/tab/replay/AudioContentMainTabLiveReplayService.kt
content/main/tab/series/AudioContentMainTabSeriesService.kt
content/theme/AudioContentThemeService.kt
can/CanRepository.kt
can/CanService.kt
can/UseCanQueryDto.kt
chat/character/comment/CharacterCommentRepository.kt
chat/character/comment/CharacterCommentService.kt
audition/applicant/AuditionApplicantRepository.kt
audition/applicant/AuditionApplicantService.kt
이번 test caller manifest와 부모 직렬 검증
공통 prefix는 src/test/kotlin/kr/co/vividnext/sodalive/다.
search/SearchRepositoryLocalizedNameTest.kt
can/CanServiceTest.kt
api/home/HomeServiceTest.kt
osiv/OsivLazyLoadingRegressionTest.kt
explorer/profile/channelDonation/ChannelDonationMessageRepositoryTest.kt
explorer/profile/channelDonation/ChannelDonationServiceTest.kt
explorer/profile/creatorCommunity/CreatorCommunityServiceTest.kt
- Search 직접 constructor의 renderer 인자를 제거했다. 기존 standalone alias SELECT=0 assertion은 완화하지 않았고 같은 콘텐츠 사례에 EN 표시 이름 선택 및 EN 이름으로 검색하지 않는 JA 검색 계약 assertion을 추가했다.
- Can fixture의 제거된 내부 owner ID 인자와 renderer constructor 인자를 제거했다. Home/Community stub은 앱이 전달하는 실제 Lang을 명시했다. donation fixture는 entity+이름 tuple과 Lang matcher로 적응했다. 기존 name/filter/secret/USER/count/page 기대값은 유지했다.
- donation repository의 기존 fetch-member 사례는
deleted_viewer3원본 entity가 그대로이고 SQL 표시 이름만viewer3임을 함께 확인하도록 보강했다. OSIV 사례는 repository가 반환하는 기존 explorer DTO의 tags를 검증하며 세션 밖 사용 경계는 유지한다. AudioContentQueryRepositoryImpl은 더 이상 renderer 주입을 요구하지 않는다. 기존 JPA test configuration import는 다른 service/core fixture까지 함께 제공하는 경우가 있어 일괄 삭제하지 않았다. 다른 owner 소스·테스트를 수정해야 하는 확인된 컴파일 변경은 없다. 전체 compile과 v2 ADMIN의 null raw 호출 회귀는 부모가 확인한다.rg로 전환 query/caller, 제거된 내부 row/ID 참조, 15개 batch 경계, 추가 import 정렬·사용 및 130자/후행 공백을 대조했다. Kotlin compile/LSP 또는 runtime 검증을 대신하지 않는다.- 추가 정적 delimiter 검사 대상 74개 repository/service 파일에서 괄호 불일치 0건, 담당 Kotlin 소스·변경 테스트의 130자 초과/후행 공백 검색 0건이었다. 삭제한
SearchNameRow/ContentBuyerRow/PlaylistContentRow및 이름 batch 전용 Can owner ID의 Kotlin 참조도 0건이다.
아래는 이 worker에서 실행하지 않은 부모 직렬 검증 명령이다. 모든 parallel writer가 종료된 뒤 실행한다.
./gradlew test --tests 'kr.co.vividnext.sodalive.search.SearchRepositoryLocalizedNameTest' --tests 'kr.co.vividnext.sodalive.content.AudioContentRepositoryLocalizedNameTest' --tests 'kr.co.vividnext.sodalive.explorer.ExplorerQueryRepositoryLocalizedNameTest' --tests 'kr.co.vividnext.sodalive.member.MemberRepositoryLocalizedNameTest' --offline --console=plain
./gradlew test --tests 'kr.co.vividnext.sodalive.explorer.ExplorerLocalizedNameEndToEndTest' --tests 'kr.co.vividnext.sodalive.v2.creatorname.application.LegacyCurrentNameReadIntegrationTest' --tests 'kr.co.vividnext.sodalive.explorer.profile.channelDonation.*' --tests 'kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.*' --tests 'kr.co.vividnext.sodalive.can.CanServiceTest' --tests 'kr.co.vividnext.sodalive.api.home.HomeServiceTest' --tests 'kr.co.vividnext.sodalive.osiv.OsivLazyLoadingRegressionTest' --offline --console=plain
./gradlew test build ktlintCheck --continue --offline --console=plain