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

요약

변경 배경

신규 API

# 엔드포인트 설명
1 POST /v3/mydata/certifications/request 중계시스템 인증 요청 (UUID 발급 + 인증창 데이터 반환)
2 POST /v3/auth/sign-up 회원가입 (디스탯 + 마이데이터 동시 가입)
3 POST /v3/auth/sign-in 통합 로그인 (EMAIL / SOCIAL / ONE_TIME_UUID)

회원 가입 흐름

(SNS 인증 선택) → 이메일/비밀번호 입력 → 약관 동의 → 중계시스템 인증창 호출 → PASS 인증
→ 중계시스템 → 백엔드 서버로 콜백 (certResultData) → 프론트 서버로 리다이렉트 (certResultData 전달)
→ 프론트에서 회원가입 API 호출 (certUuid + certResultData)
→ 중계시스템 인증 결과 조회 (CI, 개인정보 획득) → 디스탯 회원 생성 → 마이데이터 가입 (MYMD_COMPLETED)
→ oneTimeLoginUuid로 로그인 → 전송요구권 등록 (전자서명) → 회원 상태 업데이트 (COMPLETED)

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

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

1. 중계시스템 인증 요청

POST /v3/mydata/certifications/request

요청

필드 타입 회원가입 (토큰 없음) 마이데이터만 (토큰 있음) 설명
socialLoginUuid String 소셜 시 필수 - 소셜 로그인 uuid
email String 필수 -
password String 필수 -
dstatTermsSeqArr Long[] 필수 - 동의한 디스탯 약관 SEQ 배열
mydataTermsSeqArr Long[] 필수 필수 동의한 마이데이터 약관 SEQ 배열

인증

Authorization 헤더 유무로 가입 케이스를 판별한다.

Authorization 헤더 동작 설명
없음 디스탯 + 마이데이터 가입 비로그인 상태의 신규 회원가입
Bearer {token} 마이데이터만 가입 로그인 상태의 기존 회원 (상태: NONE)
Authorization 헤더에 토큰이 있으면 기존 회원의 마이데이터 가입, 없으면 신규 회원가입으로 처리한다. 별도의 type 필드는 받지 않는다.

응답

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

프론트 처리

  1. certUuid를 저장 (이후 회원가입에서 사용)
  2. apiUrl + encryptData로 동적 form 생성 후 submit → 중계시스템 인증 페이지로 이동
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로 전달해야 한다.

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

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

브라우저 흐름

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

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

3. 회원가입

POST /v3/auth/sign-up

요청

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

응답 — nextAction

nextAction에 따라 프론트의 다음 동작이 결정된다.

nextAction 발생 조건 프론트 처리
SIGN_IN_ONE_TIME_UUID 가입 성공 oneTimeLoginUuid로 로그인 API 호출 (ONE_TIME_UUID)
DUPLICATED_USER CI 중복 (이메일/애플만 발생) 로그인 페이지로 이동 (기존 계정으로 로그인 유도)
LINK_SOCIAL_ACCOUNT 기가입 + 애플 미연동 socialLoginUuid로 애플 계정 연동 → 로그인

응답 예시

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"
}

DUPLICATED_USER, LINK_SOCIAL_ACCOUNT는 언제 발생하나?

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

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

에러 케이스

에러 상황 처리
ALREADY_EXISTED_USER_EMAIL 다른 CI의 사용자가 동일 이메일 사용 중 이메일 변경 안내
NOT_ALLOWED_SIGN_UP_UNDER_14 14세 미만 가입 불가 안내

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회성 로그인 (certUuid 기반) 신규

응답

{
  "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 certUuid로 MydataCertRequest 조회 → 만료/사용 여부 검증 → 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/mydata/certifications/request MM->>MM: cert_uuid 생성 + DB 저장 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/.../result/cert?certResultData=xxx MM-->>Front: 302 Redirect (certResultData) Note over User,Relay: ── Step 6. 회원가입 ── Front->>MM: POST /v3/auth/sign-up MM->>Relay: 중계시스템 인증 결과 조회 (certResultData) Relay-->>MM: CI, 이름, 성별, 생년월일, 휴대폰번호 MM->>DB: 검증 + User 생성 + SNS 연동 + 약관 저장 MM->>Relay: 마이데이터 가입 (platformUserSeq, CI, ...) Relay-->>MM: patientSeq 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/mydata/certifications/request MM->>MM: cert_uuid 생성 + DB 저장 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/.../result/cert?certResultData=xxx MM-->>Front: 302 Redirect (certResultData) Note over User,Relay: ── Step 5. 회원가입 ── Front->>MM: POST /v3/auth/sign-up MM->>Relay: 중계시스템 인증 결과 조회 (certResultData) Relay-->>MM: CI, 이름, 성별, 생년월일, 휴대폰번호 MM->>DB: 검증 + User 생성 + 약관 저장 MM->>Relay: 마이데이터 가입 (platformUserSeq, CI, ...) Relay-->>MM: patientSeq 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}