28 KiB
가입·로그인 신규 회원 여부 공통 응답 구현 계획
| 문서 항목 | 내용 |
|---|---|
| 상태 | 구현 및 검증 완료 |
| 작성일 | 2026-11-03 |
| 요구사항 기준 | prd.md |
| API 기준 | prd.md §5 인라인 계약 |
| 현재 Phase | Phase 1, 가입·로그인 응답 필드 |
| 현재 활성 Goal | 없음 |
| 다음 Goal | 없음 |
| 완료 Task | 1/1 |
목표
공유 LoginResponse를 사용하는 명시된 가입·로그인 성공 응답은 계정 생성 여부를 required JSON boolean data.isNew로 반환하며 기존 응답 계약과 부수 동작을 유지한다.
현재 상태
| Phase | 상태 | 완료 Task | 활성/다음 Goal | 차단 또는 남은 조건 |
|---|---|---|---|---|
| 1 | 완료 | 1/1 |
없음 | 배포 전 소비자 decoder 호환성 확인 |
P1-T1과 P1-GATE를 완료했다. 구현·계약 검증·독립 리뷰를 마쳤으며 배포와 커밋은 수행하지 않았다.
범위
포함
- 공유
LoginResponse에 trailingisNew: Boolean = false와 명시적 JSON property 이름을 추가한다. - 이메일 signup 두 endpoint는 controller 응답에서
copy(isNew = true), 소셜은copy(isNew = response.isNew)를 적용한다./member/login의 일반·isAdmin·isCreator와/creator-admin/member/login은 기본값false다. - 단일 계약 테스트 클래스에서 HTTP 응답, DTO 기본 생성자 직렬화, side effect 조건을 검증한다.
제외
- 서비스/provider 코드, DB, endpoint, 공개된 다른 응답 필드 변경.
- client/UI, refresh 흐름, 가입 판정 로직, 인증 및 마케팅 동작 변경.
- 무관한 error/auth suite나 입력 파싱 테스트 추가.
- 전용
/admin/member/login또는AdminMemberLoginResponse변경. 관리자 DTO는 삭제하지 않고 기존token,role계약을 유지한다.
전역 제약
- 기술 스택: Kotlin, Java 17, Spring Boot 2.7.14, Gradle Wrapper, Jackson.
- 대상 공유
LoginResponse성공 응답은 nullable이 아닌 JSON BooleanisNew를 포함한다.newalias는 추가하지 않는다. - 공유 DTO 계약은 원래 5개 +
isNew총 6개 key다.AdminMemberLoginResponse와 전용/admin/member/login은 계약에서 제외하며 기존 응답을 변경하지 않는다. - 기존 envelope, fields, 값, message, status, 인증 입력, 로깅 및 side effect를 보존한다.
JsonInclude, 전역 mapper, service/provider, DB 변경과 dependency 추가는 금지한다.- 구현 production 파일은
LoginResponse.kt와MemberController.kt, 정확히 2개다. - 모든 실행 체크박스는 구현 승인 전 미완료로 둔다. 커밋은 별도 요청 없이는 하지 않는다.
파일 책임 및 인터페이스
| 경로 | 역할 |
|---|---|
src/main/kotlin/kr/co/vividnext/sodalive/member/login/LoginResponse.kt |
trailing isNew: Boolean = false와 getter/constructor JsonProperty |
src/main/kotlin/kr/co/vividnext/sodalive/member/MemberController.kt |
두 signup 응답의 true override 및 공통 social response 값 복사 |
src/test/kotlin/kr/co/vividnext/sodalive/member/MemberLoginResponseContractTest.kt |
HTTP와 기본 DTO serialization 계약, side effect 검증 |
Phase 1: 가입·로그인 성공 응답 계약
Phase 결과: 모든 대상 응답의 isNew 값이 endpoint별 계약과 일치하고 기존 응답과 부수 동작은 보존된다.
선행조건: 사용자가 구현을 명시적으로 요청하고 이 PRD/계획을 기준으로 삼음.
Phase 완료 조건: P1-T1 구현·focused/회귀 검증과 P1-GATE 판정이 기록됨.
구현 항목
- Task 1.1: 공통 응답 계약 구현과 검증
Goal P1-T1 objective: 가입은 true, 소셜은 기존 내부 값, /member/login 일반·isAdmin·isCreator 및 creator-admin 로그인은 false를 required boolean data.isNew로 반환하고 기존 계약을 유지한다. 전용 /admin/member/login은 제외한다.
- 시작 조건: 명시적 구현 요청,
prd.md의SOCIAL-001~SOCIAL-004확인. - 완료 증거: 아래 task 체크박스, focused test, 지정 회귀/lint 성공 및 실제 결과가 Progress에 기록됨.
- 범위 밖: production 파일 목록 외 변경, service/provider 수정, DB 변경, client 변경, 새로운 auth/error 입력 시나리오.
Files:
- Modify:
src/main/kotlin/kr/co/vividnext/sodalive/member/login/LoginResponse.kt - Modify:
src/main/kotlin/kr/co/vividnext/sodalive/member/MemberController.kt - Create:
src/test/kotlin/kr/co/vividnext/sodalive/member/MemberLoginResponseContractTest.kt - Verify existing:
src/test/kotlin/kr/co/vividnext/sodalive/member/MemberControllerTest.kt - Verify existing:
src/test/kotlin/kr/co/vividnext/sodalive/member/MemberServiceTest.kt - Verify existing:
src/test/kotlin/kr/co/vividnext/sodalive/creator/admin/member/CreatorAdminMemberControllerTest.kt - Verify existing:
src/test/kotlin/kr/co/vividnext/sodalive/creator/admin/member/CreatorAdminMemberServiceTest.kt
Interfaces:
- Consumes: 현재
SocialLoginResponse.loginResponse: LoginResponse,SocialLoginResponse.isNew: Boolean, 기존 signup 결과 및 controller 응답 경계. - Produces:
LoginResponse의 trailingisNew: Boolean = false; signup 응답copy(isNew = true); social 응답copy(isNew = response.isNew).
테스트 범위와 설계
새 계약 테스트는 @SpringBootTest와 @AutoConfigureMockMvc로 application ObjectMapper와 HTTP converter 결과를 확인한다. 외부 provider와 실제 가입 동작을 호출하지 않도록 경계를 mock한다. 컨텍스트에 필요할 때만 기존 Redis initializer opt-in을 사용한다.
공유 service fixture는 LoginResponse(42L, "issued-token", "member", "", "profile-url")다. token은 가짜 테스트 값이다. 새 Kotlin property를 직접 참조하지 않고 HTTP/mapper JSON을 검증해 구현 전에도 테스트가 컴파일되게 한다.
shouldExposeSocialIsNewAsBooleanForEveryProvider: social provider 4종 × true/false. Google/Kakao는 올바른 Authorization 입력, Apple/LINE은 bodyidentityToken및nonce를 전달한다. 모든 응답은 기존 값,success,member.signup.successmessage를 보존하고datakey가 정확히 기존 shared 5개와isNew인 6개인지 확인한다.isNew는 JSON boolean이며newkey는 없다.shouldReturnNewForBothEmailSignupEndpoints:/member/signup및/member/signup/v2가 fixture service의 기본 false 결과와 무관하게 true를 반환하는지 검증한다. 기존 필드/envelope/message/status도 유지한다.- 회원 login의 일반,
isAdmin,isCreator경로와/creator-admin/member/login은 false를 반환한다. 이들 응답은 공통LoginResponse계약을 따른다. shouldSerializeDefaultLoginResponseWithIsNewFalse: 공유 DTO 기본 생성자 결과가 applicationObjectMapper로 serialize될 때isNew: false가 존재하고 정확히 6 keys를 가진다.- 소셜 8 경우 모두에서
recordAction(42L, false, ActionType.SIGN_UP)은isNew=true일 때만 한 번 호출된다. nonblank PID의AdTrackingHistoryType.SIGNUP은 true/false 모두에서 기존대로 한 번 호출된다. - 신규 가입 HTTP 응답은 외부 가입/DB를 실행하지 않는 fixture/mock으로 만든다. provider 등록이나 signup service 로직을 테스트에서 재구현하지 않는다. 전용 관리자 로그인용 fixture와 HTTP 테스트는 추가하지 않는다.
- 기존 응답 field/envelope/status/message assertions는 변경하지 않는다. 새 auth 입력 suite는 만들지 않는다.
TDD 실행 체크리스트
- RED: 단일 계약 테스트 클래스
MemberLoginResponseContractTest를 작성해 위 소셜, 두 signup, 회원 세 login 분기, creator-admin login, 공유 DTO 기본 생성자 직렬화 및 side effect 조건을 표현한다. - RED 확인: focused 명령을 실행한다. 기대 실패는
isNew누락/값 오류 assertion이어야 하며 컨텍스트·설정 기동 실패는 유효한 RED가 아니다. - GREEN:
LoginResponse에 trailingisNew: Boolean = false와@get:JsonProperty("isNew"),@param:JsonProperty("isNew")를 추가한다.MemberController.kt의 두 signup endpoint 응답은copy(isNew = true), common social response는copy(isNew = response.isNew)로 반환한다. - GREEN 확인: 같은 focused 명령으로 endpoint별 값, 정확한 key 집합, 원래 response 값 및 side effect assertion이 통과하는지 확인한다.
- REFACTOR: 이번 변경에서 만든 중복만 정리하고 focused, 직접 영향 회귀, ktlint를 다시 실행해 결과를 기록한다. 서비스/provider는 변경하지 않는다.
P1-T1 focused 명령:
./gradlew test --tests 'kr.co.vividnext.sodalive.member.MemberLoginResponseContractTest'
회귀 검증 명령:
./gradlew test \
--tests 'kr.co.vividnext.sodalive.member.MemberLoginResponseContractTest' \
--tests 'kr.co.vividnext.sodalive.member.MemberControllerTest' \
--tests 'kr.co.vividnext.sodalive.member.MemberServiceTest' \
--tests 'kr.co.vividnext.sodalive.creator.admin.member.CreatorAdminMemberControllerTest' \
--tests 'kr.co.vividnext.sodalive.creator.admin.member.CreatorAdminMemberServiceTest'
./gradlew ktlintCheck
집중/회귀 test와 ktlint의 기대 결과는 각 명령 exit code 0이다. 전체 test/build는 공통·여러 domain 영향이 새로 확인되거나 targeted 실패로 범위를 판별할 수 없거나 사용자 요청이 있을 때만 실행한다. 그 밖에는 생략 이유와 대체 검증을 기록한다.
Phase 1 Gate P1-GATE
Goal objective: 구현 결과가 PRD의 네 요구사항과 모든 가입·로그인 응답 호환성 기준을 충족하는지 최종 판정한다.
-
시작 조건:
P1-T1의 모든 체크박스와 완료 증거가 충족됨. -
완료 증거: 아래 검증과 code diff/계약 대조 결과가 기록됨.
-
범위 밖: 구현 변경, 새 요구사항, 전체 test/build의 기본 실행.
-
검증 방식: read-only로 계획·PRD·diff·테스트 결과를 대조한다.
-
TDD 예외 사유 및 대체 검증: 동작을 바꾸지 않는 read-only Gate이므로 새 실패 테스트 대신 위 focused/회귀/lint 명령의 실제 결과와 계약 대조표를 확인한다.
-
focused 계약 테스트, 지정 회귀 테스트,
ktlintCheck결과가 성공 기록으로 남았다. -
모든 endpoint-value 매핑이 required JSON boolean이며 원래 field와 값도 보존된다.
-
shared
LoginResponsekey 집합은 정확히 6개며 aliasnew가 없다. 전용 관리자 DTO와 endpoint는 변경 대상에서 제외되었는지 diff를 read-only로 확인한다. -
성공 envelope/message/status, 요청 인증 입력, side effect 경계가 보존된다.
-
변경 production 파일은 지정한 정확히 2개이며 새 테스트는 지정한 계약 클래스 하나다. service/provider 변경은 없다.
-
전체 test/build를 실행해 전체 테스트 3,128건과 빌드 성공을 확인했다. 최초 명령 시간 초과와 후속 성공을 모두 기록했다.
Gate 검증은 구현 요청 후에만 한다. 새 필드의 strict decoder 위험은 공유 LoginResponse 소비자에게만 적용되며 creator-admin 소비자도 포함한다. 전용 /admin/member/login은 제외한다.
실행 순서
| 순서 | Goal | 선행조건 | 상태 |
|---|---|---|---|
| 1 | P1-T1 |
사용자의 명시적 구현 요청 | 완료 |
| 2 | P1-GATE |
P1-T1 완료 증거 |
완료 |
Progress
P1-T1 실행 시작 — 2026-11-03
-
사용자 승인: “문서대로 진행”,
ulw. 최신DEC-005범위를 유지하며 구현을 시작한다. -
사전 대조: P1-T1은 내부 소셜 boolean을 공유 DTO 응답에 연결하고, P1-GATE는 그 계약·테스트 증거를 확인한다. 계약·파일 범위 간 충돌은 없다.
-
검증은 focused 테스트부터 시작한다. 이번 요청의 ULW 기준에 따라 전체 테스트와 빌드도 확인하되 기존 실패는 별도로 기록한다.
-
현재 도구에
lsp_diagnostics가 없어 컴파일·lint로 대체 확인하며 LSP 실행 여부를 가장하지 않는다. -
구현 완료 체크박스는 실제 RED/GREEN/검증 증거 확보 후 갱신한다. 커밋은 별도 요청 없이 하지 않는다.
-
2026-11-03: PRD와 plan 문서 준비 완료. 코드 변경 및 구현 검증은 미착수다. 저장소의 문서 전용 규칙에 따라 테스트/Gradle 실행을 생략했다.
-
계획 근거:
MemberController.processSocialLogin이 provider 응답의isNew를 이미 사용하고, 네findOrRegister경로와 provider 결과 전달이 확인됐다. 제안 DTO annotation 조합은 기존 JSON 패턴에 근거하며 런타임 검증 전이다. -
문서 정합성/공백 검사: 상위 실행자가 실제 확인 후 결과를 추가한다. 현재 통과했다고 기록하지 않는다.
-
역사 기록, 당시의 소셜 전용 nullable 제안에 대한 기록이며 현재 계약의 검증 증거가 아님:
- 2026-11-03 문서 검증: PRD의
SOCIAL-001~SOCIAL-004를 Task·Gate·테스트 범위와 대조했다. 소셜 응답은 boolean, 비소셜 응답은 key 생략이며 운영 코드 변경 예정 범위는 두 파일이다. - 문서 전용 공백 검사 명령은 아래와 같으며 각각 exit code 0이었다.
- 2026-11-03 문서 검증: PRD의
git diff --no-index --check -- /dev/null docs/20261103_소셜로그인_신규회원여부응답/prd.md
git diff --no-index --check -- /dev/null docs/20261103_소셜로그인_신규회원여부응답/plan-task.md
git status --short --untracked-files=all은 신규 문서 두 개만 표시했다. 구현 체크박스는 모두 미완료로 유지하며 Gradle/test/build/lint는 실행하지 않았다.- 위 기록은 당시 결과 그대로 보존하며, 현재 수정에 대한 검증 결과로 간주하지 않는다.
- 2026-11-03 계약 정정: 승인된 uniform required Boolean 계약을 반영했다. 구현은 미승인·미착수이며, 이번 수정으로 새 테스트/구현 검증 결과를 주장하지 않는다.
- 2026-11-03 정정 문서 검증:
SOCIAL-001~SOCIAL-004, endpoint별 값, 두 DTO의 6/3 key 계약과 구현 3개 파일 범위를 대조했다. 현재 두 문서를 대상으로 위git diff --no-index --check를 각각 실행해 exit code 0을 확인했고,git status --short --untracked-files=all은 기존 미추적 문서 두 개만 표시했다. 구현 체크박스는 모두 미완료이며 테스트·빌드·lint는 실행하지 않았다. - 2026-11-03
DEC-005범위 정정: 현재 구현 계획은 공유LoginResponse와MemberController.kt두 운영 파일,MemberLoginResponseContractTest하나만 대상으로 한다. 전용/admin/member/login과AdminMemberLoginResponse는 변경하지 않으며 기존token,role응답을 유지한다. 이전 DEC-003·004 및 진행 기록의 두 DTO/전용 관리자 포함 주장은 역사 기록으로 보존되며 이 범위에서만 최신 정정이 우선한다.P1-T1및 Gate는 미완료, 구현은 미착수다. 이번 수정에서 새 검증을 수행하거나 주장하지 않는다. - 2026-11-03
DEC-005문서 검증: PRD와 현재 계약·파일·테스트·회귀·Gate를 대조했다. 위 공백 검사 명령을 두 문서에 각각 실행해 exit code 0을 확인했고,git status --short --untracked-files=all은 기존 미추적 문서 두 개만 표시했다. 코드 변경 없이 문서만 수정했으므로 테스트·빌드·lint는 실행하지 않았다.
Decision Log
| 날짜 | ID | 상태 | 결정 | 근거 | 영향 |
|---|---|---|---|---|---|
| 2026-11-03 | DEC-001 |
권고, superseded historical | nullable 기본값 isNew와 property-level NON_NULL, 명시적 JSON getter/constructor 이름을 사용한다. |
최초 소셜 전용 제안. 현재 계약은 DEC-003으로 대체한다. | 당시 P1-T1, P1-GATE |
| 2026-11-03 | DEC-002 |
확정 | 공통 처리기에서만 response.isNew를 복사하고 서비스/provider에는 손대지 않는다. |
네 endpoint가 동일 처리기를 사용한다. 기존 부수 동작을 최소 범위로 보존한다. | P1-T1 |
| 2026-11-03 | DEC-003 |
확정 | 두 DTO에 trailing required isNew: Boolean = false를 추가하고 JSON key를 항상 포함한다. JsonInclude, nullable, alias, 전역 mapper 변경은 금지한다. |
승인된 공통 가입·로그인 응답 계약. | P1-T1, P1-GATE |
| 2026-11-03 | DEC-004 |
확정 | signup 두 endpoint는 true, social은 기존 internal 값, 회원·creator·admin 로그인은 false로 응답한다. | signup service가 shared login DTO를 기본값으로 반환하므로 controller 경계 override가 필요하다. | P1-T1, P1-GATE |
| 2026-11-03 | DEC-005 |
확정, DEC-003·004의 관리자 전용 로그인 범위 정정 | isNew는 공유 LoginResponse에만 적용한다. /member/login의 isAdmin 및 isCreator 경로와 /creator-admin/member/login은 공유 계약에 포함하지만, 전용 /admin/member/login과 AdminMemberLoginResponse는 제외한다. 해당 DTO는 삭제하거나 변경하지 않고 기존 token, role 응답을 유지한다. |
사용자 정정: “AdminMemberLoginResponse 는 없어도 될 것 같다. 문서 수정”. 이 결정은 DEC-003·004의 전용 관리자 DTO/endpoint 포함 주장에 대해서만 우선한다. | 현재 API 계약, P1-T1, P1-GATE |
미확정 잔여 위험
- 새 JSON field를 거부하는 strict decoder 위험은 공유
LoginResponse소비자에 적용되며/creator-admin/member/login소비자도 포함한다. 전용/admin/member/login소비자는 이번 변경 대상이 아니다.
P1-T1 구현 실행 증거 - 2026-11-03, 상위 검토 대기
이 절은 위임된 P1-T1 구현과 검증 증거만 추가한다. Task 1.1 및 P1-GATE 완료 판정, 전체 테스트/빌드와 최종 문서 갱신은 상위 실행자에게 남긴다. 기존 상태 및 역사 기록은 변경하지 않았다.
- 사전 baseline: 새 테스트 작성 전 지정된 기존 네 테스트 클래스 9건 통과, exit code 0.
- RED: 새 계약 클래스는 새 Kotlin 필드를 직접 참조하지 않은 상태로 컴파일됐다. 소셜 8건, 이메일 가입 2건, 회원 로그인 3건, creator-admin 1건, 기본 DTO 직렬화 1건이 모두
isNew must be a present JSON booleanassertion으로 실패했다. 15 tests, 15 failures, 0 errors, 0 skipped, exit code 1. HTTP 상태/envelope/message 및 부수 동작 검증은 이 assertion 이전에 통과했다. - GREEN: 운영 코드 정확히 두 파일에 trailing Boolean 기본값 및 getter/parameter JSON 이름, 두 signup의 true 복사, social 내부 값 복사만 추가했다. 같은 focused 명령 15건 통과, exit code 0.
- 회귀 및 형식 정리: 계약 15건과 기존 9건을 합쳐 24건 통과했다. 최초 lint는 새 테스트의 import 정렬 및 CSV 인자 줄바꿈 5건으로 실패했고 해당 부분만 정리했다. 최종 동일 회귀 +
ktlintCheck는 exit code 0, 24 tests, 0 failures/errors/skipped였다. 최종 main/test ktlint report는 모두 비어 있다.
모든 Gradle 실행은 아래 환경을 export하고 --no-daemon을 사용했다. JAVA_HOME은 macOS /usr/libexec/java_home -v 17이 런타임을 발견하지 못해 실제 설치된 mise Java 17 경로를 사용했다.
export TMPDIR=/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/isnew-ulw.nzxcOe/p1-t1.87jfwJ
export NODE_COMPILE_CACHE="$TMPDIR/node-compile-cache"
export BUN_RUNTIME_TRANSPILER_CACHE_PATH="$TMPDIR/bun-transpiler"
export JAVA_HOME=/Users/klaus/.local/share/mise/installs/java/liberica-17.0.15+10
./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.member.MemberControllerTest' --tests 'kr.co.vividnext.sodalive.member.MemberServiceTest' --tests 'kr.co.vividnext.sodalive.creator.admin.member.CreatorAdminMemberControllerTest' --tests 'kr.co.vividnext.sodalive.creator.admin.member.CreatorAdminMemberServiceTest'
./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.member.MemberLoginResponseContractTest'
./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.member.MemberLoginResponseContractTest' --tests 'kr.co.vividnext.sodalive.member.MemberControllerTest' --tests 'kr.co.vividnext.sodalive.member.MemberServiceTest' --tests 'kr.co.vividnext.sodalive.creator.admin.member.CreatorAdminMemberControllerTest' --tests 'kr.co.vividnext.sodalive.creator.admin.member.CreatorAdminMemberServiceTest'
./gradlew --no-daemon ktlintCheck
./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.member.MemberLoginResponseContractTest' --tests 'kr.co.vividnext.sodalive.member.MemberControllerTest' --tests 'kr.co.vividnext.sodalive.member.MemberServiceTest' --tests 'kr.co.vividnext.sodalive.creator.admin.member.CreatorAdminMemberControllerTest' --tests 'kr.co.vividnext.sodalive.creator.admin.member.CreatorAdminMemberServiceTest' ktlintCheck --continue
두 번째 명령은 RED(exit 1)와 GREEN(exit 0)에 같은 조건으로 실행했다. 원문 로그와 단계별 testcase 증거는 위 TMPDIR의 baseline.log, red.log, red-contract-evidence.txt, green.log, green-contract-evidence.txt, regression.log, regression-evidence.txt, ktlint.log, final-validation.log, final-validation-evidence.txt에 보존했다. 전체 위임 보고서는 같은 경로의 p1-t1-report.md다.
검증 표면은 application context의 실제 controller, Jackson HTTP converter 및 application ObjectMapper다. 서비스/provider/비동기 부수 동작 경계만 mock하고 외부 인증 및 실제 회원 생성은 실행하지 않았다. JSON boolean, 정확한 6개 key와 new 부재, 기존 5개 값(빈 email 포함), 200 상태/envelope/message, true 전용 소셜 가입 action, true/false 모두의 nonblank PID SIGNUP 및 이메일 가입 두 경로의 기존 action 차이를 확인했다.
LSP 도구는 없어 실행하지 않았고 compileKotlin, compileTestKotlin 및 ktlintCheck로 대체했다. 테스트 context의 기존 스케줄러가 H2의 FOR UPDATE SKIP LOCKED 비지원 오류를 출력하며 Redis 종료 중 재접속 경고도 남지만 최종 testcase 실패는 없다. 기존 MemberService.kt:617-618 불필요한 !! 컴파일 경고는 수정하지 않았다. 전체 suite/build, 외부 provider 실인증, 배포 소비자 strict decoder 검증은 상위 실행/배포 검증 범위다. 커밋·stage 및 전용 관리자 응답 변경은 하지 않았다.
P1-GATE 최종 검증 - 2026-11-03
자동 검증과 독립 리뷰
./gradlew --no-daemon build --continue: bootJar/assemble까지 완료했지만 전체 테스트 단계에서 shell의 600초 제한으로 중단됐다. 이를 통과로 판정하지 않았다../gradlew --no-daemon -I "$RUN_TMP/full-suite.init.gradle" build --continue: exit code 0,BUILD SUCCESSFUL in 26m 41s. 임시 init script는 테스트별 passed/failed/skipped 출력만 켰고 코드·빌드 설정은 변경하지 않았다. 임시 HTTP QA 소스는 이 빌드에 포함되지 않았다.build/reports/tests/test/index.html: 3,128 tests, 0 failures, 0 ignored, 100% successful. 해당 실행의 계약 클래스 XML도 15 tests, 0 failures/errors/skipped다. Kotlin 컴파일, main/test lint, bootJar를 포함한 빌드가 성공했다.- 독립 read-only reviewer: 스펙 PASS, 품질 PASS, 차단·비차단 finding 0건. 두 운영 파일의 diff, 신규 테스트, DTO 생성자/직접 호출부, 내부 소셜 값 전달과 제외된 관리자 경계를 대조했다. 가입 true override 누락, false 생략 및 alias 오류를 테스트가 검출함을 확인했다. 리뷰 이후 운영/테스트 코드는 추가로 바꾸지 않았다.
실제 소켓 HTTP QA
- 실행:
./gradlew --no-daemon -I "$RUN_TMP/socket-qa.YVsXnB/socket-qa.init.gradle" test --tests 'kr.co.vividnext.sodalive.member.IsNewSocketQaTest'— exit code 0,BUILD SUCCESSFUL in 1m 41s. - 임시 테스트는
@SpringBootTest(RANDOM_PORT)와TestRestTemplate.exchange로127.0.0.1:49841의 실제 Tomcat에 요청했다. 컨트롤러·보안 필터·Jackson은 실제 코드이며, 외부 인증/가입/추적의 서비스 경계는 테스트 mock이다. 임시 Kotlin source와 init script는 저장소 밖의 작업 전용 경로에만 생성했다. - 이메일 가입 2건 true, 소셜 4종 × 신규/기존 8건 true/false,
/member/login일반·isAdmin·isCreator 3건 false, creator-admin 1건 false: 14건 모두 HTTP 200, 원래 다섯 값·빈 email 보존, 정확히 여섯 key, boolean 타입/값 및new부재를 확인했다. 소셜/이메일 기존 tracking/action 조건도 통과했다. - 실제 응답 예시(가짜 fixture token):
{"success":true,"message":"Congratulations on your sign up.","data":{"userId":42,"token":"issued-token","nickname":"member","email":"","profileImage":"profile-url","isNew":true},"errorProperty":null}
{"success":true,"message":"You are logged in.","data":{"userId":42,"token":"issued-token","nickname":"member","email":"","profileImage":"profile-url","isNew":false},"errorProperty":null}
검증 한계와 정리
- LSP 도구는 제공되지 않아 실행하지 못했다. 컴파일·전체 lint는 통과했으며 LSP clean을 가장하지 않는다.
- 외부 provider 실인증, 운영 DB, 소비자 decoder 배포 호환성은 검증하지 않았다. 기존 스케줄러/H2·Redis 종료 경고는 테스트 실패와 구분해 위 기록에 남겼다.
@DirtiesContext에 의한 Tomcat 종료 로그를 확인했다.ps로 QA worker 84785 및 시간 초과했던 81629/81841, 관련 Gradle test/Redis 프로세스가 남지 않음을 확인했고lsof -iTCP:49841 -sTCP:LISTEN과 작업 root의lsof +D도 비어 있었다.$RUN_TMP는/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/isnew-ulw.nzxcOe다. 정리 후 임시 로그/리뷰/하네스 원본은 남기지 않으며 검증 요약과 실제 응답은 본 절, 지속 테스트는MemberLoginResponseContractTest.kt, 전체 결과는build/reports/tests/test/index.html에 남는다. 커밋·stage·배포는 수행하지 않았다.- 정리 영수증: 소유한
$RUN_TMP만 삭제한 뒤test ! -e "$RUN_TMP"가 exit code 0을 반환했다. 구현/리뷰/HTTP QA 하위 경로, notepad, 임시 init script 및 런타임 캐시 정리를 완료했다. 공유 캐시·다른 작업 디렉터리는 변경하지 않았다.
커밋 전 재검증 - 2026-11-03
- 사용자
/commit-with-validation요청으로 변경 5개 파일과 최신 계약을 대조했다. 운영 코드와 테스트는 수정하지 않았다. - 아래 명령은 exit code 0,
BUILD SUCCESSFUL in 50s였다. XML 결과는 5개 클래스 총 24건, 실패·오류·무시 0건이며 lint는UP-TO-DATE였다.
JAVA_HOME=/Users/klaus/.local/share/mise/installs/java/liberica-17.0.15+10 TMPDIR=/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode ./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.member.MemberLoginResponseContractTest' --tests 'kr.co.vividnext.sodalive.member.MemberControllerTest' --tests 'kr.co.vividnext.sodalive.member.MemberServiceTest' --tests 'kr.co.vividnext.sodalive.creator.admin.member.CreatorAdminMemberControllerTest' --tests 'kr.co.vividnext.sodalive.creator.admin.member.CreatorAdminMemberServiceTest' ktlintCheck
- 기존 Gate 이후 구현 변경이 없어 전체 테스트·빌드는 반복하지 않고 직접 영향 회귀를 실행했다. 이번 실행으로
build/reports/tests/test/index.html과 XML은 위 24건 결과로 갱신됐으며, 앞 절의 3,128건은 이전 실행 기록이다. - Redis 종료 중 재접속 경고가 있었으나 테스트 실패는 없었다. 외부 provider 실인증과 소비자 decoder 호환성은 이번에도 검증하지 않았다.