Files
sodalive-backend-spring-boot/docs/20260917_라이브_크리에이터_입장제한/plan-task.md
T

29 KiB

라이브 크리에이터 입장 제한 구현 계획

For agentic workers: REQUIRED SUB-SKILL: Use subagent-driven-development or executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

문서 항목 내용
상태 Phase 1~3 구현 및 검증 완료
작성일 2026-09-17
요구사항 기준 docs/20260917_라이브_크리에이터_입장제한/prd.md
API 기준 기존 API 응답 스키마 변경 없음
현재 Phase 완료
현재 활성 Goal 없음
다음 Goal 없음

목표

다른 크리에이터가 isAvailableJoinCreator = false 라이브 방을 볼 수도, 직접 입장할 수도 없게 하고, 입장 가능 성별(genderRestriction)도 조회와 입장 경계에서 일관되게 적용한다.

현재 상태

Phase 상태 완료 Task 활성/다음 Goal 차단 또는 남은 조건
1 완료 2/2 없음 없음
2 완료 1/1 없음 없음
3 완료 2/2 없음 없음

범위

포함

  • v2 홈 추천/온에어 라이브 조회의 isAvailableJoinCreator 및 genderRestriction 필터 누락 보정.
  • v2 크리에이터 채널 라이브 탭의 조회자 크리에이터 판정 보정.
  • /live/room/enter에서 다른 크리에이터의 제한 방 직접 입장 차단과 기존 성별 입장 차단 회귀 검증.
  • focused test와 직접 영향 회귀 검증.
  • 후속 리뷰 REV-001: /live/room/info/{id}의 토큰 발급 전 크리에이터·성별 제한 적용.
  • 후속 리뷰 REV-002: 채널 소유자 본인의 제한 방 조회를 repository 회귀 테스트로 고정.

제외

  • 공개 API DTO 필드 추가/삭제.
  • DB schema 변경.
  • 기존 성인/성별/차단/강퇴/비공개/결제 정책 재설계.
  • 관리자용 라이브 조회 정책 변경.

기술적 제약

  • 기술 스택: Kotlin, Spring Boot 2.7.14, QueryDSL, Gradle Wrapper.
  • 기존 QueryDSL 조건 패턴을 재사용하고 새 abstraction은 만들지 않는다.
  • 크리에이터 제한 조건은 isViewerCreator && memberId != null일 때만 적용하며, 방 생성자는 liveRoom.member.id.eq(memberId)로 예외 처리한다.
  • 성별 제한 조건은 기존 조회 경로와 같은 정책을 따른다. Gender.MALE은 ALL, MALE_ONLY, Gender.FEMALE은 ALL, FEMALE_ONLY, Gender.NONE 또는 비로그인/null 유효 성별은 필터 없음이다.
  • 방 생성자 본인은 성별 제한 조건에서도 liveRoom.member.id.eq(memberId)로 예외 처리한다.
  • 모든 production 변경 Task는 RED → GREEN → REFACTOR 순서로 진행한다.
  • 공개 API schema 변경은 금지한다.

Phase 1: 리스트 노출 제한 보정

Phase 결과: 다른 크리에이터가 문제 의심 리스트 경로에서 제한 방을 받지 않고, 성별 제한에 맞지 않는 사용자가 v2 홈 추천/온에어 라이브에서 제한 방을 받지 않는다.

선행조건: PRD 확정.

Phase 완료 조건: P1-T1, P1-T2, P1-GATE 완료 및 검증 기록 누적.

구현 항목

Task 1.1 v2 홈 추천/온에어 라이브 필터 적용

Goal 실행 P1-T1: findLiveRecommendations() 계열 호출에서 조회자 크리에이터 여부와 유효 성별을 전달하고 QueryDSL 필터를 적용한다.

  • 시작 조건: LCR-001 확정.
  • 완료 증거: 다른 크리에이터가 제한 방을 받지 않고, 성별 제한에 맞지 않는 사용자가 제한 방을 받지 않으며, 방 생성자 본인은 예외인 focused test 통과.
  • 범위 밖: /live/room/enter 입장 차단.

Files:

  • Modify: src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/port/out/HomeRecommendationQueryPort.kt
  • Modify: src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/HomeRecommendationQueryService.kt
  • Modify: src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/adapter/out/persistence/DefaultHomeRecommendationQueryRepository.kt
  • Modify: src/main/kotlin/kr/co/vividnext/sodalive/v2/api/home/application/HomeRecommendationFacade.kt
  • Modify: src/main/kotlin/kr/co/vividnext/sodalive/v2/api/home/live/application/HomeOnAirLiveFacade.kt
  • Test: repository/service 기존 테스트 위치를 우선 확인하고, 없으면 src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/adapter/out/persistence/DefaultHomeRecommendationQueryRepositoryTest.kt

Interfaces:

  • findLiveRecommendations(offset, limit, memberId, includeAdultLives, isViewerCreator, effectiveViewerGender) 형태로 파라미터를 추가한다.

  • isViewerCreator 기본값은 false로 둬 snapshot refresh 등 기존 호출을 보존한다.

  • effectiveViewerGender 기본값은 null로 둬 비로그인/성별 미설정과 snapshot refresh 등 기존 호출을 보존한다.

  • HomeRecommendationFacade와 HomeOnAirLiveFacade는 member.auth?.gender 우선, 없으면 member.gender로 기존 경로와 같은 유효 성별을 계산해 전달한다.

  • RED: 크리에이터 조회자에게 isAvailableJoinCreator = false 라이브가 제외되는 테스트, 성별 제한에 맞지 않는 조회자에게 genderRestriction 제한 라이브가 제외되는 테스트, 방 생성자 본인은 포함되는 테스트를 작성한다.

  • RED 확인: ./gradlew test --tests '*DefaultHomeRecommendationQueryRepositoryTest'를 실행해 필터 미구현으로 크리에이터 제한 방 또는 성별 제한 방이 포함되는 assertion 실패를 확인한다.

  • GREEN: HomeRecommendationQueryPort, HomeRecommendationQueryService, DefaultHomeRecommendationQueryRepository, HomeRecommendationFacade, HomeOnAirLiveFacade에 isViewerCreator, effectiveViewerGender 전달과 조건을 최소 추가한다.

  • GREEN 확인: ./gradlew test --tests '*DefaultHomeRecommendationQueryRepositoryTest'를 다시 실행해 성공을 확인한다.

  • REFACTOR: 이번 Task가 만든 중복만 정리하고 ./gradlew ktlintCheck 및 focused test 결과를 Progress에 기록한다.

Task 1.2 v2 크리에이터 채널 라이브 탭 판정 보정

Goal 실행 P1-T2: 크리에이터 채널 라이브 탭에서 “조회자가 크리에이터인지” 기준으로 제한 방 노출을 막는다.

  • 시작 조건: LCR-002 확정.
  • 완료 증거: 다른 크리에이터는 제한 방을 받지 않고 방 생성자 본인은 받을 수 있는 focused test 통과.
  • 범위 밖: 크리에이터 채널 홈은 이미 viewer.role == MemberRole.CREATOR를 사용하므로 변경하지 않는다.

Files:

  • Modify: src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/application/CreatorChannelLiveQueryService.kt
  • Test: 기존 테스트 위치를 우선 확인하고, 없으면 src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/application/CreatorChannelLiveQueryServiceTest.kt

Interfaces:

  • queryPort.findCurrentLive(..., isViewerCreator = viewer.role == MemberRole.CREATOR, ...)로 전달한다.

  • repository의 기존 creatorJoinLiveCondition(viewerId, isViewerCreator)와 본인 예외는 유지한다.

  • RED: 다른 크리에이터 viewer가 isAvailableJoinCreator = false 현재 라이브를 받지 않는 service test를 작성한다.

  • RED 확인: ./gradlew test --tests '*CreatorChannelLiveQueryServiceTest'를 실행해 기존 viewerId == creatorId 판정 때문에 제한 방이 반환되는 assertion 실패를 확인한다.

  • GREEN: CreatorChannelLiveQueryService의 isViewerCreator 계산을 viewer.role == MemberRole.CREATOR로 변경한다.

  • GREEN 확인: 같은 focused test를 다시 실행해 성공을 확인한다.

  • REFACTOR: 중복 정리 없이 끝낼 수 있으면 그대로 두고 focused test 및 ./gradlew ktlintCheck 결과를 Progress에 기록한다.

검증 방법

Phase 1 Gate

Goal 실행 P1-GATE: 리스트 노출 제한과 성별 제한 경로가 PRD 요구사항과 일치하는지 판정한다.

  • 시작 조건: P1-T1, P1-T2 완료.
  • 완료 증거: 아래 명령 통과와 수동 대조 기록.
  • 범위 밖: 입장 차단 구현.
./gradlew test --tests '*DefaultHomeRecommendationQueryRepositoryTest' --tests '*CreatorChannelLiveQueryServiceTest'
./gradlew ktlintCheck

Expected: focused test와 ktlint가 0 exit code로 종료한다.

수동 대조:

  • HomeFollowingQueryService와 CreatorChannelHomeQueryService의 기존 정상 조건은 변경하지 않았음을 확인한다.
  • HomeRecommendationFacade.getHomeRecommendations, HomeRecommendationFacade.getLives, HomeOnAirLiveFacade.getOnAirLives가 모두 조회자 크리에이터 여부와 유효 성별을 전달함을 확인한다.

Phase 2: 직접 입장 차단

Phase 결과: 다른 크리에이터가 제한 방 ID를 알고 있어도 /live/room/enter로 입장할 수 없고, 성별 제한에 맞지 않는 사용자의 직접 입장도 기존 정책대로 차단된다.

선행조건: P1-GATE 완료.

Phase 완료 조건: P2-T1, P2-GATE 완료 및 검증 기록 누적.

구현 항목

Task 2.1 /live/room/enter 크리에이터 제한 차단 및 성별 제한 회귀 검증

Goal 실행 P2-T1: LiveRoomService.enterLive()에서 다른 크리에이터의 제한 방 입장을 결제/roomInfo 변경 전 차단하고, 기존 성별 제한 입장 차단을 회귀 테스트로 고정한다.

  • 시작 조건: LCR-003 확정, P1-GATE 완료.
  • 완료 증거: 다른 크리에이터는 예외, 성별 제한에 맞지 않는 사용자는 live.room.gender_restricted 예외, 방 생성자 본인과 일반 유저는 기존 정책 유지 test 통과.
  • 범위 밖: error envelope schema 변경.

Files:

  • Modify: src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt
  • Test: 기존 테스트 위치를 우선 확인하고, 없으면 src/test/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomServiceTest.kt
  • Optional Modify: 메시지 key 추가가 필요하다고 확인될 때만 src/main/resources/messages*.properties 계열 파일

Implementation rule:

enterLive()에서 room 조회 후, 결제나 LiveRoomInfo 변경 전에 아래 의미의 조건을 추가한다.

if (
    member.role == MemberRole.CREATOR &&
    room.member!!.id!! != member.id!! &&
    !room.isAvailableJoinCreator
) {
    throw SodaException(messageKey = "live.room.not_found")
}

기존 message key 재사용이 부적절하다고 확인되면 PRD와 plan-task를 먼저 갱신한 뒤 새 message key를 추가한다.

성별 제한은 현재 구현된 아래 의미의 조건을 유지한다. 이 작업에서는 해당 조건을 약화하지 말고 회귀 테스트로 고정한다.

if (room.member!!.id!! != member.id!! && !member.canEnter(room.genderRestriction)) {
    throw SodaException(messageKey = "live.room.gender_restricted")
}
  • RED: 다른 크리에이터가 isAvailableJoinCreator = false 방에 입장하면 예외가 발생하고 roomInfoRepository.save와 canPaymentService.spendCan이 호출되지 않는 테스트를 작성한다. 성별 제한에 맞지 않는 사용자가 입장하면 live.room.gender_restricted 예외가 발생하는 회귀 테스트도 작성한다.
  • RED 확인: ./gradlew test --tests '*LiveRoomServiceTest'를 실행해 기존 구현이 입장을 허용하거나 후속 저장을 호출하는 assertion 실패를 확인한다.
  • GREEN: LiveRoomService.enterLive()에 최소 조건을 추가한다.
  • GREEN 확인: 같은 focused test를 다시 실행해 성공을 확인한다.
  • REFACTOR: 방 생성자 본인, 일반 유저, Gender.NONE 사용자 회귀 테스트를 함께 실행하고 ./gradlew ktlintCheck 결과를 Progress에 기록한다.

검증 방법

Phase 2 Gate

Goal 실행 P2-GATE: 입장 차단, 리스트 노출 차단, 성별 제한 전체 흐름을 최종 판정한다.

  • 시작 조건: P2-T1 완료.
  • 완료 증거: 아래 명령 통과와 계획 문서 Progress 기록.
  • 범위 밖: 실패 test 삭제·완화, 무관한 리팩터링.
./gradlew test --tests '*DefaultHomeRecommendationQueryRepositoryTest' --tests '*CreatorChannelLiveQueryServiceTest' --tests '*LiveRoomServiceTest'
./gradlew ktlintCheck
./gradlew test

Expected: focused test, ktlint, 전체 test가 0 exit code로 종료한다. 크리에이터 제한과 성별 제한이 모두 조회/입장 경계에서 검증된다. 전체 test가 환경 문제로 실패하면 실패 원인과 focused/영향 범위 대체 검증을 Progress에 기록한다.

Phase 3: 후속 리뷰 항목 구현

근거: reviews/현재변경사항-review.md의 REV-001, REV-002 및 두 항목을 후속 구현사항으로 추가하라는 사용자 요청.

Phase 결과: 제한 사용자는 방 정보 API로 토큰을 발급받지 못하고, 채널 소유자 본인의 제한 방 조회는 실제 repository 테스트로 보장된다.

선행조건: Phase 1~2 완료 및 후속 리뷰 확인. 기존 완료 체크박스와 검증 기록은 유지한다.

Phase 완료 조건: P3-T1, P3-T2, P3-GATE 완료와 실행 증거 기록.

Task 3.1 토큰 발급 경계의 입장 제한 보강

  • Task 3.1 완료

Goal 실행 P3-T1: REV-001에 따라 getRoomInfo()에서 토큰 생성 전에 크리에이터·성별 제한을 적용하고 허용 사용자 회귀를 검증한다.

  • 시작 조건: REV-001의 제한 검사 누락 확인. 실제 RTC 우회 접속은 아직 재현되지 않았으므로 서버의 토큰 생성 호출 여부부터 검증한다.
  • 완료 증거: 제한 사용자에 대한 실패 테스트 → 최소 수정 후 예외 key 및 RTC/RTM 생성기 무호출 검증 → 소유자·허용 사용자 회귀 통과.
  • 범위 밖: 새로운 입장 완료 여부 검사, 성인/비공개/결제 정책 재설계, 기존 발급 토큰 회수, Agora SDK 변경, API/DB 스키마 변경.

Files:

  • Modify: docs/20260917_라이브_크리에이터_입장제한/prd.md — LCR-003 및 성공 기준에 방 정보 API의 토큰 발급 경계를 명시.
  • Modify: src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt — getRoomInfo().
  • Test: src/test/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomServiceTest.kt.
  • Read: src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomController.kt, src/main/kotlin/kr/co/vividnext/sodalive/member/Member.kt.

구현 기준: 기존 방/roomInfo 존재 및 상호 차단 검사를 유지하고 첫 토큰 생성 호출 전에 /enter와 동일한 조건을 적용한다. 다른 크리에이터의 제한 방 접근은 live.room.not_found, 다른 사용자의 성별 불일치는 live.room.gender_restricted로 거절한다. 두 제한이 겹치면 크리에이터 제한을 먼저 판정한다. 생성자는 두 제한의 예외이며 성별은 Member.canEnter()를 재사용해 인증 성별 우선 및 Gender.NONE 허용 정책을 유지한다. RTC, RTM, v2v 토큰 모두 제한 검사를 통과한 뒤에만 생성한다.

  • 문서 정합성: production 수정 전에 기존 PRD의 LCR-003, 관련 흐름·성공 기준에 토큰 발급 제한을 반영하고 후속 검증 항목은 미완료로 둔다.
  • RED: roomInfo와 room이 존재하고 차단 관계가 없는 fixture에서 다른 크리에이터의 제한 방 접근 및 성별 불일치 접근을 테스트한다. 토큰 생성기는 테스트용 문자열을 반환하도록 준비하고 정상 응답 구성에 필요한 의존성도 설정해, 무관한 null 오류가 아닌 예상 예외 미발생으로 실패하는지 확인한다.
  • GREEN: 위 조건을 최소 추가하고 예외 key, rtcTokenBuilder와 rtmTokenBuilder의 무호출을 검증한다. /enter 거절 후 /info/{id}에 해당하는 서비스 호출도 토큰을 생성하지 않는지 확인한다.
  • 회귀: 소유자는 크리에이터·성별 제한이 모두 걸려도 허용되고, 일반 사용자와 허용 방의 다른 크리에이터는 성별이 맞으면 응답을 받는다. 인증 성별 우선, Gender.NONE 허용, 기존 상호 차단 거절을 확인한다.
  • REFACTOR/기록: 불필요한 추상화 없이 기존 스타일을 유지하고 아래 명령의 결과와 무엇을/왜/어떻게 검증했는지를 이 Task 아래에 누적한다.
./gradlew test --tests '*LiveRoomServiceTest' --tests '*LiveRoomServiceAdultVisibilityPolicyTest'

Expected: RED 단계는 제한 검사 누락으로 실패하고, GREEN 이후는 exit 0. 차단 경로에서는 토큰이 생성되지 않는다.

검증 기록 (2026-09-17): LiveRoomServiceTest에 정상 응답 fixture와 제한·허용 회귀 10건을 추가했다. RED에서 다른 크리에이터, 성별 불일치, 중첩 제한, /enter 거절 후 정보 조회 테스트가 모두 예상 SodaException 미발생으로 실패했다. getRoomInfo()의 기존 상호 차단 검사 뒤와 모든 RTC/RTM/v2v 토큰 생성 앞에 /enter와 동일한 크리에이터·성별 조건을 추가한 뒤 ./gradlew test --rerun-tasks --tests '*LiveRoomServiceTest' --tests '*LiveRoomServiceAdultVisibilityPolicyTest'가 20건 모두 통과하며 BUILD SUCCESSFUL로 종료했다. 거절 경로의 token builder 무호출과 소유자·성별 일치 사용자·허용 방 크리에이터·인증 성별 우선·Gender.NONE·기존 상호 차단 회귀를 확인했다. 실제 HTTP/RTC 연결은 검증하지 않았다. P3-T1 리뷰 결과 Blocker는 0건이다.

Task 3.2 채널 소유자 제한 방 조회 회귀 테스트

  • Task 3.2 완료

Goal 실행 P3-T2: REV-002에 따라 채널 소유자 본인이 isAvailableJoinCreator = false인 현재 방을 조회할 수 있음을 H2 repository 테스트로 고정한다.

  • 시작 조건: REV-002 확인. 기본 실행 순서는 P3-T1 이후이며 파일 변경은 독립적이다.
  • 완료 증거: 실제 repository의 findCurrentLive() 결과가 생성한 제한 방 ID와 일치하고 기존 타인 필터 테스트도 통과한다.
  • 범위 밖: production 조회 조건 수정, 채널 API/DTO 변경, 조회 정책 재설계.

Files:

  • Test: src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/adapter/out/persistence/DefaultCreatorChannelLiveQueryRepositoryTest.kt.
  • Read: src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/adapter/out/persistence/DefaultCreatorChannelLiveQueryRepository.kt.

TDD 예외 사유: 소유자 예외는 이미 구현돼 있고 이번 Task는 누락된 테스트만 추가한다. RED를 만들기 위해 정상 production 코드를 변경하지 않는다. 대체 검증은 실제 H2 조회 결과 assertion과 기존 타인 제외 테스트의 동시 통과다.

  • 테스트 추가: 기존 fixture로 활성·비성인·유효 channelName의 현재 방을 생성하고 isAvailableJoinCreator = false로 설정한다. flushAndClear() 후 viewerId == creatorId, isViewerCreator = true, 성별 제한과 일치하는 유효 성별로 조회해 방 ID를 단정한다.
  • GREEN: 아래 focused test로 소유자 조회와 기존 타인 제외 동작이 함께 통과하는지 확인한다.
  • REFACTOR/기록: 기존 fixture를 재사용하고 이 Task 아래에 명령과 검증 결과를 누적한다. 예상 밖 실패는 원인을 판정한 뒤 production 수정이 필요하면 먼저 Task 범위를 갱신한다.
./gradlew test --tests '*DefaultCreatorChannelLiveQueryRepositoryTest' --tests '*CreatorChannelLiveQueryServiceTest'

Expected: exit 0. 소유자에게 제한 방이 반환되고 다른 크리에이터에게는 기존대로 제외된다.

검증 기록 (2026-09-17): production 조건은 변경하지 않고 DefaultCreatorChannelLiveQueryRepositoryTest.shouldReturnRestrictedCurrentLiveForChannelOwner를 추가했다. 활성·비성인·유효 channelName·isAvailableJoinCreator = false·MALE_ONLY 방을 실제 H2에 저장하고 viewerId == creatorId, isViewerCreator = true, Gender.MALE로 조회해 생성한 방 ID를 단정했다. ./gradlew test --rerun-tasks --tests '*DefaultCreatorChannelLiveQueryRepositoryTest' --tests '*CreatorChannelLiveQueryServiceTest'가 기존 다른 크리에이터 제외 테스트와 함께 BUILD SUCCESSFUL로 종료했다. git diff --check도 통과했다.

Phase 3 Gate

Goal 실행 P3-GATE: 후속 두 항목과 기존 목록·입장 정책의 회귀 검증을 완료한다.

  • 시작 조건: P3-T1, P3-T2 완료.
  • 완료 증거: 아래 명령 exit 0, 토큰 생성 전 제한 검사 수동 대조, 소유자 조회 assertion 확인, PRD·리뷰·계획의 상태 및 검증 기록 갱신.
  • 범위 밖: 실제 RTC 접속을 검증하지 않고 검증했다고 기록하는 행위, 무관한 리팩터링.
./gradlew test --tests '*LiveRoomServiceTest' --tests '*LiveRoomServiceAdultVisibilityPolicyTest' --tests '*DefaultCreatorChannelLiveQueryRepositoryTest' --tests '*CreatorChannelLiveQueryServiceTest' --tests '*DefaultHomeRecommendationQueryRepositoryTest'
./gradlew ktlintCheck
./gradlew tasks --all
  • 위 명령을 실행하고 각 Task의 완료 증거를 대조한다.
  • 실제 HTTP/RTC 연결 검증 여부와 환경 한계를 별도로 기록한다. 서비스 테스트 통과를 실제 RTC 접속 검증으로 간주하지 않는다.
  • 전체 회귀는 공통 인증·예외·설정까지 변경하거나 focused test로 영향 범위를 판단할 수 없는 경우 또는 사용자 요청 시 실행한다. 생략 시 이유와 위 대체 회귀 명령을 Progress에 남긴다.
  • reviews/현재변경사항-review.md에 수정 후 검증을 누적하고, PRD의 후속 성공 기준 및 Phase 3 상태를 실제 결과에 맞춰 갱신한다.

실행 순서와 의존성

순서 Goal 선행조건 병행 가능 차단 시 다음 행동
1 P1-T1 PRD 확정 아니요 호출부/port signature 대조 후 문서 갱신
2 P1-T2 PRD 확정 예, P1-T1과 파일 충돌 없음 service test 작성 방식 확정
3 P1-GATE P1-T1, P1-T2 아니요 실패 소유 Task로 회귀 수정
4 P2-T1 P1-GATE 아니요 오류 message key 결정 후 문서 갱신
5 P2-GATE P2-T1 아니요 실패 소유 Task로 회귀 수정
6 P3-T1 P2-GATE, REV-001 기본 순차 토큰 생성 전 거절 재현과 PRD 정합성 확인
7 P3-T2 REV-002 P3-T1과 파일 독립 기존 fixture·현재 방 조건 확인
8 P3-GATE P3-T1, P3-T2 아니요 실패 소유 Task로 회귀 수정
P1-T1 ─┐
       ├→ P1-GATE → P2-T1 → P2-GATE
P1-T2 ─┘

P2-GATE → P3-T1 ─┐
          P3-T2 ─┴→ P3-GATE

변경 금지 항목

  • 공개 API 응답 DTO를 변경하지 않는다.
  • isAvailableJoinCreator의 DB schema나 기본값을 변경하지 않는다.
  • 기존 성인/성별/차단/강퇴/비공개/결제 조건을 완화하지 않는다.
  • 테스트를 삭제·skip·완화하지 않는다.
  • as any, 타입 억제, 불필요한 새 abstraction을 만들지 않는다.

Progress

  • 2026-09-17: PRD와 계획 문서 작성. 구현은 아직 시작하지 않음.
  • 2026-09-17: v2 홈 추천/온에어 라이브의 genderRestriction 조회 필터와 /live/room/enter 성별 제한 회귀 검증을 PRD/계획에 반영. 구현은 아직 시작하지 않음.
  • 2026-09-17: P1-T1 RED 확인. 신규 인자 추가 전 compileTestKotlin에서 isViewerCreator/effectiveViewerGender 미정의로 실패했고, 시그니처 연결 후 repository focused test 79건 중 크리에이터 제한 1건과 성별 제한 2건이 assertion 실패했다. facade 전달 전 HomeRecommendationControllerTest.shouldLogHomeRecommendationPageFailure도 신규 인자 불일치로 실패했다.
  • 2026-09-17: P1-T1 GREEN 확인. ./gradlew test --rerun-tasks --tests '*DefaultHomeRecommendationQueryRepositoryTest' --tests '*HomeRecommendationQueryServiceTest' --tests '*HomeRecommendationFacadeTest' --tests '*HomeOnAirLiveFacadeTest'가 BUILD SUCCESSFUL로 종료했다. 컴파일 경고는 기존 deprecated API 및 기존 테스트 경고이며 신규 실패는 없다.
  • 2026-09-17: P1-T1 REFACTOR 확인. 추가 추상화 없이 기존 QueryDSL 패턴을 재사용했고 ./gradlew ktlintCheck가 BUILD SUCCESSFUL로 종료했다.
  • 2026-09-17: P1-T2 RED 확인. 다른 ID의 MemberRole.CREATOR viewer 테스트에서 currentLiveIsViewerCreator가 기대값 true, 실제값 false로 실패했다.
  • 2026-09-17: P1-T2 GREEN 확인. CreatorChannelLiveQueryService가 viewer.role == MemberRole.CREATOR를 전달하도록 최소 수정한 뒤 ./gradlew test --rerun-tasks --tests '*CreatorChannelLiveQueryServiceTest'가 BUILD SUCCESSFUL로 종료했다.
  • 2026-09-17: P1-T2 REFACTOR 확인. 별도 추상화 없이 기존 역할 enum을 사용했고 ./gradlew ktlintCheck가 BUILD SUCCESSFUL로 종료했다.
  • 2026-09-17: P1-GATE 자동 검증. ./gradlew test --rerun-tasks --tests '*DefaultHomeRecommendationQueryRepositoryTest' --tests '*HomeRecommendationQueryServiceTest' --tests '*HomeRecommendationFacadeTest' --tests '*HomeOnAirLiveFacadeTest' --tests '*CreatorChannelLiveQueryServiceTest'와 ./gradlew ktlintCheck가 모두 BUILD SUCCESSFUL로 종료했다. 출력된 deprecated API 및 unchecked cast 경고는 변경 범위 밖의 기존 경고다.
  • 2026-09-17: P1-GATE 수동 대조. HomeFollowingQueryService와 CreatorChannelHomeQueryService는 변경하지 않았고, 홈 추천·라이브·온에어 facade 세 경로가 조회자 역할과 유효 성별을 전달함을 확인했다.
  • 2026-09-17: P1-GATE 리뷰 승인. Phase 1 전체 변경 범위의 PRD/계획 위반과 기존 필터 약화가 없음을 확인했으며 Blocker는 0건이다.
  • 2026-09-17: P2-T1 RED 확인. 다른 크리에이터의 제한 방 입장 테스트가 SodaException을 기대했지만 예외가 발생하지 않아 실패했다.
  • 2026-09-17: P2-T1 GREEN 확인. LiveRoomService.enterLive()의 결제 및 room-info 접근 전에 역할·방 생성자·isAvailableJoinCreator 조건을 추가한 뒤 ./gradlew test --rerun-tasks --tests '*LiveRoomServiceTest'가 5건 모두 통과하며 BUILD SUCCESSFUL로 종료했다.
  • 2026-09-17: P2-T1 REFACTOR 확인. 방 생성자, 일반 사용자, Gender.NONE, 성별 불일치 회귀 테스트와 차단 경로의 무부수효과 검증을 유지했고 ./gradlew ktlintCheck가 BUILD SUCCESSFUL로 종료했다.
  • 2026-09-17: P2-GATE 자동 검증. ./gradlew test --rerun-tasks --tests '*DefaultHomeRecommendationQueryRepositoryTest' --tests '*CreatorChannelLiveQueryServiceTest' --tests '*LiveRoomServiceTest', ./gradlew ktlintCheck, ./gradlew test --rerun-tasks가 모두 BUILD SUCCESSFUL로 종료했다.
  • 2026-09-17: P2-GATE 최종 리뷰 승인. 구현·테스트·문서의 PRD/계획 일치와 API/스키마 유지 여부를 재검토했으며 Blocker는 0건이다.
  • 2026-09-17: 현재 변경사항 후속 리뷰. focused 140건 및 직접 영향 회귀 42건, 총 182건 통과. ./gradlew ktlintCheck tasks --all 성공(ktlint UP-TO-DATE). 소스 수정 없이 목록·입장 경계 중심의 검토이므로 전체 회귀는 재실행하지 않았다. 명시된 구현 범위는 적합하나, 기존 /live/room/info/{id} 토큰 발급 경계의 제한 누락과 채널 소유자 조회 테스트 보완점을 reviews/현재변경사항-review.md에 기록했다. 실제 HTTP/RTC 연결은 미검증이며, 제품 전체의 입장 차단 완결성 승인은 보류한다. 기존 완료 기록은 유지한다.
  • 2026-09-17: 사용자 요청에 따라 REV-001을 토큰 발급 제한 보강 P3-T1, REV-002를 채널 소유자 조회 테스트 P3-T2로 구체화하고 P3-GATE를 추가했다. 파일·조건·예외 key·회귀 시나리오·명령·완료 증거를 명시했으며 기존 Phase 1~2 완료 기록은 유지했다. ./gradlew tasks --all은 exit 0, BUILD SUCCESSFUL; git diff --check도 통과했다. 이번 변경은 계획 문서만 갱신하므로 코드 테스트는 실행하지 않았다. 후속 구현은 미착수이며 다음 Goal은 P3-T1이다.
  • 2026-09-17: P3-T1 시작 전 문서 정합성 완료. PRD의 관련 review, 문제·목표·사용자 흐름, LCR-003, API 계약, 보안 기준과 미완료 성공 기준에 /live/room/info/{id} 토큰 발급 경계 및 REV-002 회귀 검증을 반영했다.
  • 2026-09-17: P3-GATE 자동 검증. ./gradlew test --rerun-tasks --tests '*LiveRoomServiceTest' --tests '*LiveRoomServiceAdultVisibilityPolicyTest' --tests '*DefaultCreatorChannelLiveQueryRepositoryTest' --tests '*CreatorChannelLiveQueryServiceTest' --tests '*DefaultHomeRecommendationQueryRepositoryTest'가 119건, 실패·오류·skip 0으로 BUILD SUCCESSFUL 종료했다. ./gradlew ktlintCheck, ./gradlew tasks --all, git diff --check도 모두 성공했다.
  • 2026-09-17: 토큰 발급 경계는 보안 관련 변경이므로 조건부 전체 회귀를 실행했다. ./gradlew test --rerun-tasks가 BUILD SUCCESSFUL로 종료했다. 실제 HTTP 인증/직렬화 및 Agora RTC/RTM 연결은 환경상 실행하지 않았고, 실제 서비스 메서드 호출과 token builder 무호출 assertion 및 H2 repository 조회를 대체 실행 증거로 사용했다.
  • 2026-09-17: P3-GATE 최종 reviewer gate 승인. Phase 3 구현·테스트·PRD·계획·후속 리뷰 해소 기록을 전체 diff 기준으로 검토했으며 Blocker는 0건이다.