Files
T

13 KiB

가입·로그인 신규 회원 여부 공통 응답 PRD

문서 정보

항목 내용
문서 상태 구현 및 검증 완료
작성일 2026-11-03
최종 수정일 2026-11-03
대상 기능 가입·로그인 신규 회원 여부 공통 응답
관련 구현 계획 plan-task.md
관련 API Contract 본 문서 §5에 인라인으로 기록
관련 review 없음

1. 개요와 문제

가입과 로그인 성공 응답에서 클라이언트가 이번 요청 중 계정이 생성됐는지 알 수 있도록 data.isNew를 공통 제공한다. 소셜 로그인은 이미 결정된 isNew를 반환하며, 가입 요청은 생성 성공에 true, 계정 생성을 수행하지 않는 로그인은 false를 반환한다.

isNew는 오직 계정이 이번 요청에서 생성됐는지를 나타낸다. 온보딩 상태, 최초 기기 로그인, 이벤트의 exactly-once 보장을 뜻하지 않는다.

2. 목표와 제외 범위

목표

  • 공유 LoginResponse를 사용하는 대상 가입·로그인 성공 응답의 data.isNew를 누락 없는 JSON boolean으로 제공한다.
  • 기존 응답 필드, envelope, 인증 및 회원 생성 동작을 유지한다.

제외

  • 회원 조회·생성 기준, 토큰 발급, 계정 연결, 마케팅 및 가입 로그 변경.
  • DB 스키마, 로그인 요청, 오류·메시지·HTTP 상태 변경.
  • 신규 refresh API, 다른 응답 필드, 클라이언트/UI 변경.
  • 기존 가입 로직 또는 서비스 구조 리팩터링.
  • 전용 관리자 로그인 /admin/member/login 응답 변경. 기존 AdminMemberLoginResponse의 token, role 계약을 유지하며 isNew를 추가하지 않는다.

3. 구현 전 근거와 동작

  • src/main/kotlin/kr/co/vividnext/sodalive/member/social/SocialLoginResponse.kt: 소셜 인증 결과가 isNew: Boolean을 보유한다.
  • src/main/kotlin/kr/co/vividnext/sodalive/member/social/MemberResolveResult.kt: 회원 해석 결과에도 같은 값이 있다.
  • src/main/kotlin/kr/co/vividnext/sodalive/member/MemberService.kt: 네 findOrRegister overload는 활성 기존 회원을 false, 생성한 회원을 true로 반환한다. 두 이메일 signup service 메서드는 shared login 흐름을 사용하므로 signup 응답 경계의 override가 필요하다.
  • src/main/kotlin/kr/co/vividnext/sodalive/member/MemberController.kt: 소셜 공통 처리기에서 기존 isNew를 가입 로그에 쓰지만 응답에는 복사하지 않는다. signup 두 endpoint도 응답 경계에서 true로 덮어써야 한다.
  • src/main/kotlin/kr/co/vividnext/sodalive/member/login/LoginResponse.kt: 현재 공개 필드는 userId, token, nickname, email, profileImage다.

4. 기능 요구사항

ID 상태 요구사항 수용 기준 계획 연결
SOCIAL-001 확정 표에 명시한 공유 LoginResponse 기반 가입·로그인 성공 응답에 required isNew를 제공한다. endpoint-value 표의 지정 값이 JSON boolean으로 존재하며 null 또는 누락이 없다. P1-T1, P1-GATE
SOCIAL-002 확정 기존 응답을 보존한다. 기존 필드와 값, envelope, 성공 message 및 HTTP 동작이 그대로 유지된다. P1-T1, P1-GATE
SOCIAL-003 확정 가입 성공은 true, 계정 생성을 하지 않는 로그인 성공은 false를 제공한다. 아래 endpoint-value 표의 모든 경로에서 매핑 값이 일치한다. P1-T1, P1-GATE
SOCIAL-004 확정 기존 부수 동작을 바꾸지 않는다. 토큰·인증·계정 조회/생성·마케팅·가입 로그의 조건과 순서가 변경되지 않는다. 소셜 가입 action은 기존처럼 true에서만 기록되며 소셜 nonblank PID의 SIGNUP 추적은 기존처럼 true/false 모두 유지된다. 이메일 두 가입 경로의 기존 로그 차이도 변경하지 않는다. P1-T1, P1-GATE

5. API 계약

기존 ApiResponse envelope와 endpoint를 유지한다. 공유 LoginResponse에 trailing isNew: Boolean = false를 추가하고 생성자 parameter와 getter에 각각 @param:JsonProperty("isNew"), @get:JsonProperty("isNew")를 지정한다. JsonInclude 및 전역 mapper 설정은 변경하지 않는다. 아래 endpoint-value 표의 성공 응답에서 isNew key는 존재하고 JSON boolean이어야 한다. null, 누락, 새 alias new는 허용하지 않는다. 전용 관리자 로그인 /admin/member/login은 기존 AdminMemberLoginResponse를 유지하며 isNew를 포함하지 않는다.

성공 endpoint data.isNew 근거
POST /member/signup true 이번 요청에서 회원 생성
POST /member/signup/v2 true 이번 요청에서 회원 생성
POST /member/login/google 기존 내부 isNew 소셜 회원 해석 결과
POST /member/login/kakao 기존 내부 isNew 소셜 회원 해석 결과
POST /member/login/apple 기존 내부 isNew 소셜 회원 해석 결과
POST /member/login/line 기존 내부 isNew 소셜 회원 해석 결과
POST /member/login 일반 로그인 false 계정을 생성하지 않는 로그인
POST /member/login isAdmin 로그인 false 계정을 생성하지 않는 로그인
POST /member/login isCreator 로그인 false 계정을 생성하지 않는 로그인
POST /creator-admin/member/login false 계정을 생성하지 않는 로그인

이메일 signup controller는 shared MemberService 가입 메서드가 내부에서 기본 LoginResponse를 만들어도 응답 경계에서 copy(isNew = true)로 덮어쓴다. 소셜 성공 경로는 copy(isNew = response.isNew)를 사용한다. /member/login의 일반, isAdmin, isCreator 로그인과 /creator-admin/member/login은 공유 LoginResponse 기본값 false를 사용한다. 전용 /admin/member/login은 기존 응답을 유지한다.

공유 LoginResponse의 JSON은 원래 다섯 key에 isNew를 더한 정확히 여섯 key다. 전용 관리자 로그인 응답은 이 계약에서 제외한다.

{
  "data": {
    "userId": 123,
    "token": "synthetic-token-not-valid",
    "nickname": "sample",
    "email": "",
    "profileImage": "https://example.invalid/profile.png",
    "isNew": true
  }
}

6. 호환성 및 위험

  • 서버를 먼저 배포하면 새 JSON 필드를 strict decoder로 읽는 기존 클라이언트가 거부할 수 있다. 위험은 공유 LoginResponse를 소비하는 클라이언트에 해당하며 /creator-admin/member/login 소비자도 포함된다. 전용 /admin/member/login 소비자는 이번 변경의 대상이 아니다. 배포 전 공유 응답 소비자의 호환성을 확인한다.
  • 모든 명시 대상 성공 응답에서 isNew는 required boolean이다. null 값, key 생략, alias new는 계약 위반이다.

7. 완료 기준 및 추적성

  • endpoint-value 표의 모든 공유 LoginResponse 성공 응답에서 data.isNew가 누락 없는 JSON boolean이며 지정 값과 일치한다. (SOCIAL-001, P1-T1)
  • 기존 응답 필드, 성공 envelope/message/HTTP 동작 및 provider별 인증 입력이 유지된다. (SOCIAL-002, P1-T1)
  • 가입은 true, 소셜은 기존 내부 값, /member/login 일반·isAdmin·isCreator 및 creator-admin 로그인은 false다. (SOCIAL-003, P1-T1)
  • 소셜 가입 action과 nonblank PID의 SIGNUP 추적, 이메일 각 가입 경로의 로그 등 기존 side effect 조건이 바뀌지 않는다. (SOCIAL-004, P1-T1)
  • 구현 Task와 Phase Gate가 계획대로 검증되고 결과가 계획 문서에 기록된다. (P1-GATE)

8. 미결 사항

없음. strict decoder 호환성은 구현을 막는 설계 질문이 아니라 공유 LoginResponse 소비자에 대한 배포 전 잔여 위험이다.

9. Decision Log

날짜 ID 상태 결정 근거 영향
2026-11-03 DEC-001 권고, superseded historical 공유 LoginResponse에 nullable 기본값 isNew를 추가하고 getter/constructor JSON 이름을 명시한다. 최초 소셜 전용 제안. 현재 계약은 DEC-003으로 대체한다. 당시 SOCIAL-001~SOCIAL-003, P1-T1
2026-11-03 DEC-002 확정 공통 processSocialLogin에서 response.isNew를 복사해 반환한다. 서비스·provider 서명과 부수 동작은 변경하지 않는다. 네 endpoint가 공통 처리기를 사용하며 가입 로깅은 이미 같은 값을 이용한다. SOCIAL-001, SOCIAL-004, P1-T1
2026-11-03 DEC-003 확정 두 응답 DTO에 required non-null Boolean = false를 추가하고 JSON key isNew를 항상 포함한다. null 생략이나 별도 alias는 허용하지 않는다. 승인된 공통 가입·로그인 계약은 모든 성공 응답에서 값이 분명해야 한다. SOCIAL-001~SOCIAL-003, P1-T1
2026-11-03 DEC-004 확정 이메일 가입 두 경로는 응답 경계에서 true, 소셜은 기존 내부 값, 일반·creator·admin 로그인의 성공은 false로 매핑한다. 공유 서비스의 가입 결과 DTO 기본값은 false이므로 signup controller의 override가 필요하다. SOCIAL-001, SOCIAL-003, P1-T1
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

10. 문서 진행 기록

  • 2026-11-03 구현 완료: LoginResponse.kt, MemberController.kt에만 운영 변경을 적용했다. 계약 테스트 15건, 직접 회귀 합계 24건 및 lint 통과 후 실제 loopback HTTP 요청 14건이 모두 200과 계약에 맞는 JSON을 반환했다. 전체 빌드와 테스트 3,128건도 실패·무시 0건으로 통과했고 독립 리뷰는 스펙·품질 PASS였다. 자세한 명령과 증거는 plan-task.md의 P1-GATE 최종 검증 기록에 있다.

  • 검증 경계: 실제 controller·Jackson·HTTP 서버를 사용하되 외부 인증/회원 생성은 서비스 경계 mock으로 격리했다. LSP 도구가 없어 Kotlin 컴파일과 lint로 확인했으며 실제 소셜 provider 인증 및 클라이언트 decoder 호환성은 배포 전 확인 항목이다. 아래 기록은 각 시점의 이력이다.

  • 문서 준비만 완료했다. 구현, 테스트, 빌드, lint는 실행하지 않았다.

  • docs/agent-guides/문서유지보수.md에 따라 문서 전용 작업에서 Gradle 검증은 생략한다. 실제 문서 간 정합성과 공백 검사 결과만 기록한다.

  • 역사 기록, 당시의 소셜 전용 nullable 제안에 대한 기록이며 현재 계약의 검증 증거가 아님:

    • 2026-11-03 문서 검증: PRD와 계획의 네 요구사항, API 필드, 제외 범위, Goal 연결을 대조했다.
    • 이 디렉터리의 prd.md, plan-task.md 각각을 /dev/null과 git diff --no-index --check로 비교해 exit code 0을 확인했다.
    • git status --short --untracked-files=all에서 신규 문서 두 개만 확인했다. 구현·테스트·빌드 검증 결과는 아니다.
  • 위 기록은 당시 결과 그대로 보존하며, 현재 수정에 대한 검증 결과로 간주하지 않는다.

  • 2026-11-03 계약 정정: 승인된 uniform required Boolean 계약을 반영했다. DEC-001의 nullable/생략 방침은 superseded historical이며, 구현은 미승인·미착수다. 이번 갱신으로 새 테스트/구현 검증 결과를 주장하지 않는다.

  • 2026-11-03 정정 문서 검증: endpoint별 값, 두 DTO의 필수 boolean, 운영 코드 3개 파일과 테스트 계획을 PRD/계획 간 대조했다. 두 문서의 git diff --no-index --check는 각각 exit code 0이며, 작업 트리는 기존 미추적 문서 두 개만 표시했다. 테스트·빌드·lint는 문서 전용 규칙에 따라 실행하지 않았다.

  • 2026-11-03 DEC-005 범위 정정: 현재 계약은 공유 LoginResponse만 대상으로 한다. /admin/member/login 및 AdminMemberLoginResponse는 기존 token, role을 유지하고 isNew 적용에서 제외한다. 이전 DEC-003·004 및 진행 기록의 두 DTO/전용 관리자 포함 주장은 역사 기록으로 보존되며 이 범위에서만 최신 정정이 우선한다. 이번 수정은 문서만 변경했으며 새 테스트나 구현 검증은 수행하지 않았다.

  • 2026-11-03 DEC-005 문서 검증: 제외 경로·DTO, 운영 코드 2개 파일, 테스트·회귀 목록을 계획과 대조했다. 두 문서의 git diff --no-index --check는 각각 exit code 0이고 작업 트리는 기존 미추적 문서 두 개만 표시했다. 코드·테스트는 변경하지 않았으며 Gradle 검증은 실행하지 않았다.