API 스펙: 가입 & 로그인 V3 medic-mydata

요약

변경 배경

신규 API

# 엔드포인트 설명
1 POST /v3/mymd/certifications/request 중계시스템 인증 세션 생성 (UUID 발급 + 인증창 데이터 반환)
2 GET /v1/mydata/certifications/result/cert 중계시스템 인증 결과 콜백 (302 리다이렉트, 기존 V1 재활용)
3 POST /v3/auth/sign-up 회원가입 (디스탯 + 중계시스템 동시 가입 / 중계시스템만 가입)
4 POST /v3/auth/sign-in 통합 로그인 (EMAIL / SOCIAL / ONE_TIME_UUID)

가입 케이스 (MymdCertConst.CertType)

CertType 대상 디스탯 가입 중계시스템 가입
SIGN_UP 신규 사용자 (이메일) O O
SIGN_UP_WITH_SOCIAL 신규 사용자 (소셜) O O
MYMD_ONLY 기존 회원 (상태: NONE) - O

프론트 호출 순서 (회원가입)

POST /v3/mymd/certifications/request → certUuid, apiUrl, encryptData 수신
form submit (apiUrl + encryptData) → 중계시스템 인증 페이지 이동
중계시스템 인증 완료 → 콜백 → 프론트 서버로 302 리다이렉트 (certResultData 수신)
POST /v3/auth/sign-up → nextAction에 따라 분기
POST /v3/auth/sign-in (ONE_TIME_UUID, oneTimeLoginUuid) → 토큰 수신

1. 중계시스템 인증 세션 생성

POST /v3/mymd/certifications/request

구현: MymdCertV3Controller → MymdCertSessionService

요청 (MymdCertRequestDto.Req)

필드 타입 SIGN_UP SIGN_UP_WITH_SOCIAL MYMD_ONLY 설명
socialLoginUuid String - 필수 - 소셜 로그인 uuid
email String 필수 필수 -
password String 필수 필수 -
dstatTermsSeqArr Long[] 필수 필수 - 동의한 디스탯 약관 SEQ 배열
marketingChannelTypes Channel.Type[] 선택 선택 - APP_PUSH, EMAIL, SMS
mydataTermsSeqArr Long[] 필수 필수 필수 동의한 중계시스템 약관 SEQ 배열

인증

Authorization 헤더 유무 + socialLoginUuid로 CertType을 판별한다.

Authorization 헤더 socialLoginUuid CertType 설명
없음 없음 SIGN_UP 이메일 신규 회원가입
없음 있음 SIGN_UP_WITH_SOCIAL 소셜 신규 회원가입
Bearer {token} - MYMD_ONLY 기존 회원 중계시스템만 가입
MYMD_ONLY는 이미 중계시스템에 가입된 회원인지 검증한다. 가입 완료 상태면 ALREADY_MYMD_SIGN_UP 예외 발생.

서버 처리

  1. CertType 판별
  2. 검증 (SIGN_UP: 이메일/비밀번호/약관 필수, SIGN_UP_WITH_SOCIAL: 소셜 로그인 결과 검증, MYMD_ONLY: 중계시스템 미가입 검증)
  3. cert_uuid 생성
  4. tb_mymd_cert_session INSERT (비밀번호 Pbkdf2SHA512 인코딩, MYMD_ONLY는 userSeq 저장)
  5. 중계시스템 인증창 요청 (Feign)
  6. 응답 반환

응답 (MymdCertRequestDto.Res)

{
  "certUuid": "발급된 UUID",
  "apiUrl": "중계시스템 인증 페이지 URL",
  "encryptData": "암호화된 인증 요청 데이터"
}

프론트 처리

sessionStorage.setItem('certUuid', certUuid);

const form = document.createElement('form');
form.method = 'POST';
form.action = apiUrl;

const input = document.createElement('input');
input.type = 'hidden';
input.name = 'encryptData';
input.value = encryptData;

form.appendChild(input);
document.body.appendChild(form);
form.submit();
encryptData는 민감 데이터이므로 반드시 POST body로 전달해야 한다.

에러 코드

에러 코드 상황
INVALID_PARAM 필수 파라미터 누락
NOT_FOUND_SOCIAL_LOGIN_RESULT 소셜 로그인 결과 없음
EXPIRED_SOCIAL_LOGIN_RESULT 소셜 로그인 만료
ALREADY_USED_USER_SOCIAL_LOGIN_RESULT 이미 사용된 소셜 로그인
ALREADY_MYMD_SIGN_UP 이미 중계시스템 가입된 회원 (MYMD_ONLY)
FAIL_REQUEST_MYDATA_API 중계시스템 API 호출 실패

2. 중계시스템 인증 결과 콜백

GET /v1/mydata/certifications/result/cert?resultData={resultData}

구현: MydataCertWebController (기존 V1 엔드포인트 재활용)

브라우저 흐름

프론트 서버 → (form submit) → 중계시스템 인증 → (콜백) → medic-mydata (V1) → (302) → 프론트 서버?certResultData=xxx

프론트는 리다이렉트로 수신한 certResultData를 저장한다.

3. 회원가입

POST /v3/auth/sign-up

구현: AuthV3Controller → SignUpV3UseCase → DstatSignUpService + MymdSignUpService

요청 (SignUpV3Dto.Req)

필드 타입 필수 설명
certUuid String O 1번 API에서 발급받은 UUID
certResultData String O 콜백으로 수신한 중계시스템 인증 결과

서버 처리 흐름

1 MymdCertSession 조회 및 검증 (만료/사용 여부)
2 중계시스템 인증 결과 조회 (MymdCertResultService → CI, 개인정보)
3 CertType 분기:
    SIGN_UP / SIGN_UP_WITH_SOCIAL → DstatSignUpService + MymdSignUpService
    MYMD_ONLY → MymdSignUpService만

DstatSignUpService 처리 순서

  1. SNS CI 대조 (카카오/네이버만. 애플은 CI 미제공으로 생략)
  2. CI 중복 확인 → 기가입이면 DUPLICATED_USER 또는 LINK_SOCIAL_ACCOUNT 반환 (200 OK)
  3. 검증: 14세 미만 (SignUpAgePolicy), 이메일 중복 (SignUpEmailPolicy), 필수 약관 (SignUpTermAgreementPolicy)
  4. 디스탯 회원 생성 (CI/이름/휴대폰 암호화, 비밀번호는 세션에서 인코딩된 값 사용)
  5. 후처리: 약관 동의 저장, 알림 설정, 소셜 계정 연동, 알림톡 발송
  6. OneTimeLogin UUID 발행

응답 — nextAction (SignUpV3Dto.Res)

nextAction 발생 조건 프론트 처리
SIGN_IN_ONE_TIME_UUID 가입 성공 oneTimeLoginUuid로 로그인 API 호출 (ONE_TIME_UUID)
DUPLICATED_USER CI 중복 (이메일/애플만 발생) 로그인 페이지로 이동 (기존 계정으로 로그인 유도). certSession 즉시 만료.
LINK_SOCIAL_ACCOUNT 기가입 + 애플 미연동 socialLoginUuid로 애플 계정 연동 → 로그인. SocialLoginResult에 기존 유저 seq 세팅됨.
REGISTER_PATIENT_CONSENT 기존 회원 중계시스템 가입 완료 전송요구권 등록 플로우로 이동

응답 예시

SIGN_IN_ONE_TIME_UUID — 정상 가입 완료

{
  "nextAction": "SIGN_IN_ONE_TIME_UUID",
  "user": {
    "seq": 1,
    "email": "[email protected]",
    "socialAccounts": [
      { "seq": 1, "provider": "APPLE", "linkedDtm": "2026-03-18T12:00:00" }
    ]
  },
  "oneTimeLoginUuid": "xxx"
}

DUPLICATED_USER — CI 중복

{
  "nextAction": "DUPLICATED_USER",
  "user": {
    "seq": 2,
    "email": "[email protected]",
    "socialAccounts": [
      { "seq": 1, "provider": "APPLE", "linkedDtm": "2025-01-01T00:00:00" }
    ]
  }
}

LINK_SOCIAL_ACCOUNT — 애플 미연동

{
  "nextAction": "LINK_SOCIAL_ACCOUNT",
  "user": {
    "seq": 2,
    "email": "[email protected]",
    "socialAccounts": [
      { "seq": 1, "provider": "KAKAO", "linkedDtm": "2025-01-01T00:00:00" }
    ]
  },
  "socialLoginUuid": "yyy"
}

REGISTER_PATIENT_CONSENT — 중계시스템만 가입 완료

{
  "nextAction": "REGISTER_PATIENT_CONSENT"
}

DUPLICATED_USER, LINK_SOCIAL_ACCOUNT는 언제 발생하나?

카카오/네이버는 로그인 시점에 CI를 수신하여 가입 전에 회원 여부를 판별할 수 있다.
하지만 이메일 가입애플 로그인은 CI를 미리 알 수 없어, 중계시스템 인증 후에야 CI 중복이 확인된다.

nextAction 발생 케이스
DUPLICATED_USER 이메일 가입 — CI로 조회 시 기가입 회원 존재
DUPLICATED_USER 애플 로그인 — CI로 조회 시 기가입 회원 존재 + 이미 다른 애플 계정 연동됨
LINK_SOCIAL_ACCOUNT 애플 로그인 — CI로 조회 시 기가입 회원 존재 + 애플 미연동 (연동 가능)

에러 코드

에러 코드 상황
NOT_FOUND_MYMD_CERT_SESSION 인증 세션 없음
EXPIRED_MYMD_CERT_SESSION 인증 세션 만료
ALREADY_USED_MYMD_CERT_SESSION 이미 사용된 인증 세션
FAIL_SOCIAL_CI_MISMATCH SNS CI와 중계시스템 CI 불일치
NOT_ALLOWED_SIGN_UP_UNDER_14 14세 미만 가입 불가
ALREADY_EXISTED_USER_EMAIL 이메일 중복
REQUIRED_TERMS_NOT_AGREED 필수 약관 미동의
FAIL_REQUEST_MYDATA_API 중계시스템 API 호출 실패

4. 로그인

POST /v3/auth/sign-in

기존 POST /v2/auth/sign-in (이메일)과 POST /v2/oauth2/sign-in (소셜)을 통합하고, ONE_TIME_UUID 타입을 추가한다.

요청

필드 타입 EMAIL SOCIAL ONE_TIME_UUID 설명
type SignInType 필수 필수 필수
email String 필수 - -
password String 필수 - -
socialLoginUuid String - 필수 -
oneTimeLoginUuid String - - 필수 회원가입 시 발급된 1회성 로그인 UUID
clientType ClientType 기본 WEB 기본 WEB 기본 WEB
deviceId String - - -

SignInType

설명 V2 대응
EMAIL 이메일 + 비밀번호 로그인 POST /v2/auth/sign-in
SOCIAL 소셜 로그인 (uuid 기반) POST /v2/oauth2/sign-in
ONE_TIME_UUID 회원가입 후 1회성 로그인 (oneTimeLoginUuid 기반) 신규

응답

{
  "accessToken": "JWT 액세스 토큰",
  "accessTokenExpirationDtm": "2026-03-18T12:00:00",
  "refreshToken": "JWT 리프레시 토큰",
  "refreshTokenExpirationDtm": "2026-04-18T12:00:00"
}

type별 서버 처리

type 처리
EMAIL email/password 인증 → UserToken 생성
SOCIAL socialLoginUuid로 SocialLoginResult 조회 → 검증 → UserToken 생성
ONE_TIME_UUID oneTimeLoginUuid로 UserOneTimeLogin 조회 → 만료/사용 여부 검증 → user_seq로 회원 조회 → UserToken 생성 → 사용 처리 (used_dtm)

전체 플로우 시퀀스

소셜 회원가입

sequenceDiagram participant User as 사용자 participant Front as 프론트 서버 participant MM as medic-mydata participant DB as DB participant SNS as SNS 제공자 participant Relay as mydata-relay Note over User,Relay: ── Step 1. 소셜로그인 ── User->>Front: SNS 가입 선택 Front->>SNS: OAuth2 인증 요청 SNS-->>User: 로그인 및 권한 동의 SNS->>MM: 콜백 (socialId, email, accessToken) alt 카카오/네이버 MM->>SNS: accessToken으로 userinfo API 호출 SNS-->>MM: CI 반환 Note over MM: CI 기반 회원 여부 확인 alt CI 없음 (미가입) MM->>MM: nextAction = SIGN_UP else CI 있음 + SNS 미연동 MM->>MM: nextAction = LINK_SOCIAL_ACCOUNT else CI 있음 + SNS 연동됨 MM->>MM: nextAction = SIGN_IN end else 애플 (CI 미제공) Note over MM: socialId 기반 회원 여부 확인 alt socialId 연동 없음 MM->>MM: nextAction = SIGN_UP else socialId 연동 있음 MM->>MM: nextAction = SIGN_IN end end MM->>DB: SocialLoginResult 저장 (uuid 발급) MM-->>Front: 리다이렉트 (socialLoginUuid, nextAction) Note over User,Relay: ── Step 2. 이메일/비밀번호 입력 + 약관 동의 ── Note over Front: nextAction = SIGN_UP인 경우 User->>Front: 이메일/비밀번호 입력 User->>Front: 약관 동의 Note over User,Relay: ── Step 3. 중계시스템 인증 세션 생성 ── Front->>MM: POST /v3/mymd/certifications/request MM->>MM: cert_uuid 생성 MM->>DB: tb_mymd_cert_session INSERT MM->>Relay: 중계시스템 인증 요청 Relay-->>MM: apiUrl + encryptData MM-->>Front: {certUuid, apiUrl, encryptData} Note over User,Relay: ── Step 4. 중계시스템 인증 ── Front->>Relay: form submit (POST apiUrl + encryptData) Relay-->>User: 중계시스템 로그인 화면 User->>Relay: PASS 인증 Note over User,Relay: ── Step 5. 인증 결과 콜백 ── Relay->>MM: GET /v1/mydata/certifications/result/cert?resultData=xxx MM-->>Front: 302 Redirect (certResultData) Note over User,Relay: ── Step 6. 회원가입 (SignUpV3UseCase) ── Front->>MM: POST /v3/auth/sign-up MM->>Relay: 중계시스템 인증 결과 조회 (MymdCertResultService) Relay-->>MM: CI, 이름, 성별, 생년월일, 휴대폰번호 MM->>DB: 검증 + User 생성 + SNS 연동 + 약관 저장 (DstatSignUpService) MM->>Relay: 중계시스템 가입 (MymdSignUpService) Relay-->>MM: 완료 MM-->>Front: {nextAction, user, oneTimeLoginUuid} Note over User,Relay: ── Step 7. 로그인 ── Front->>MM: POST /v3/auth/sign-in (ONE_TIME_UUID, oneTimeLoginUuid) MM-->>Front: {accessToken, refreshToken}

이메일 회원가입

sequenceDiagram participant User as 사용자 participant Front as 프론트 서버 participant MM as medic-mydata participant DB as DB participant Relay as mydata-relay Note over User,Relay: ── Step 1. 이메일/비밀번호 입력 + 약관 동의 ── User->>Front: 이메일로 가입 선택 User->>Front: 이메일/비밀번호 입력 User->>Front: 약관 동의 Note over User,Relay: ── Step 2. 중계시스템 인증 세션 생성 ── Front->>MM: POST /v3/mymd/certifications/request MM->>MM: cert_uuid 생성 MM->>DB: tb_mymd_cert_session INSERT MM->>Relay: 중계시스템 인증 요청 Relay-->>MM: apiUrl + encryptData MM-->>Front: {certUuid, apiUrl, encryptData} Note over User,Relay: ── Step 3. 중계시스템 인증 ── Front->>Relay: form submit (POST apiUrl + encryptData) Relay-->>User: 중계시스템 로그인 화면 User->>Relay: PASS 인증 Note over User,Relay: ── Step 4. 인증 결과 콜백 ── Relay->>MM: GET /v1/mydata/certifications/result/cert?resultData=xxx MM-->>Front: 302 Redirect (certResultData) Note over User,Relay: ── Step 5. 회원가입 ── Front->>MM: POST /v3/auth/sign-up MM->>Relay: 중계시스템 인증 결과 조회 Relay-->>MM: CI, 이름, 성별, 생년월일, 휴대폰번호 MM->>DB: 검증 + User 생성 + 약관 저장 MM->>Relay: 중계시스템 가입 Relay-->>MM: 완료 MM-->>Front: {nextAction, user, oneTimeLoginUuid} Note over User,Relay: ── Step 6. 로그인 ── Front->>MM: POST /v3/auth/sign-in (ONE_TIME_UUID, oneTimeLoginUuid) MM-->>Front: {accessToken, refreshToken}