API 스펙: 가입 & 로그인 V3 medic-mydata
요약
변경 배경
- 중계시스템 가입이 필수로 변경됨
- 회원가입 + 중계시스템 가입을 하나의 플로우로 통합
- WebView 제거, 프론트 서버가 플로우를 주도하는 구조로 전환
신규 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 예외 발생.
서버 처리
- CertType 판별
- 검증 (SIGN_UP: 이메일/비밀번호/약관 필수, SIGN_UP_WITH_SOCIAL: 소셜 로그인 결과 검증, MYMD_ONLY: 중계시스템 미가입 검증)
- cert_uuid 생성
- tb_mymd_cert_session INSERT (비밀번호 Pbkdf2SHA512 인코딩, MYMD_ONLY는 userSeq 저장)
- 중계시스템 인증창 요청 (Feign)
- 응답 반환
응답 (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 엔드포인트 재활용)
- 기존 V1 콜백 엔드포인트를 그대로 활용
- 중계시스템이 인증 완료 후
resultData 파라미터로 호출
- 서버가
resultData를 certResultData로 매핑하여 프론트 서버 URL로 302 리다이렉트
브라우저 흐름
프론트 서버 → (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 처리 순서
- SNS CI 대조 (카카오/네이버만. 애플은 CI 미제공으로 생략)
- CI 중복 확인 → 기가입이면 DUPLICATED_USER 또는 LINK_SOCIAL_ACCOUNT 반환 (200 OK)
- 검증: 14세 미만 (SignUpAgePolicy), 이메일 중복 (SignUpEmailPolicy), 필수 약관 (SignUpTermAgreementPolicy)
- 디스탯 회원 생성 (CI/이름/휴대폰 암호화, 비밀번호는 세션에서 인코딩된 값 사용)
- 후처리: 약관 동의 저장, 알림 설정, 소셜 계정 연동, 알림톡 발송
- 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}