Files

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 레거시 팬톡, L26L30 큐레이션·탭, 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, owner role=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을 대조했다. JA enum 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 이름 포함 결과를 default cache에 저장한다. content/main/AudioContentMainController.kt에서 cache hit 뒤 전체 owner IDs의 현재 이름을 조회해 복사했다.
  • content/AudioContentService.kt.getAudioContentRanking은 Lang 없는 contentRanking key와 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