마음 일기 API 명세서

Auth 서버 및 연관 API 중심 - Enhanced

버전: v1.2 | 최종 업데이트: 2025년 1월 | 상태: 안정 버전

API 개요

항목 설명
기본 URL https://your-api-domain.com
현재 버전 v1.2
인증 방식 Bearer Token (JWT)
응답 형식 JSON
문자 인코딩 UTF-8
타임존 UTC (ISO 8601 형식)

버전 관리 전략

마음 일기 API는 의미론적 버전 관리(Semantic Versioning)를 따릅니다.

버전 형식 설명 하위 호환성
Major (v2.0) 호환되지 않는 API 변경 ❌ 호환되지 않음
Minor (v1.3) 하위 호환되는 기능 추가 ✅ 하위 호환
Patch (v1.2.1) 하위 호환되는 버그 수정 ✅ 하위 호환
📋 버전 지원 정책
  • 현재 Major 버전: 최소 2년간 지원
  • 이전 Major 버전: 1년간 지원 (deprecated)
  • 새로운 기능은 최신 버전에만 추가
  • 중요한 보안 업데이트는 모든 지원 버전에 적용

인증 방식

API는 JWT(JSON Web Token) 기반의 Bearer Token 인증을 사용합니다.

토큰 구조

토큰 타입 유효 기간 용도
Access Token 30분 API 요청 인증
Refresh Token 7일 Access Token 갱신

인증 헤더 형식

Authorization: Bearer {access_token}
🔒 보안 고려사항
  • Access Token은 메모리에만 저장 (localStorage 사용 금지)
  • Refresh Token은 HttpOnly 쿠키로 관리
  • HTTPS 연결에서만 토큰 전송
  • 토큰 만료 시 자동 갱신 로직 구현 권장

인증 API

인증 상태 확인 🔐 인증 필요

GET
/auth_check

현재 요청이 유효한 인증 토큰을 가지고 있는지 확인합니다.

항목 설명
인증 Bearer Token 필요
응답 코드
200: 인증됨 401: 인증되지 않음
⚠️ 오류 응답 예시
{ "timestamp": "2024-01-15T10:30:00Z", "status": 401, "error": "Unauthorized", "message": "JWT token is expired", "path": "/auth_check" }

JWT 토큰 재발급 🔐 인증 필요

POST
/auth/api/protected/refresh

만료된 Access Token과 유효한 Refresh Token을 사용하여 새로운 Access Token을 발급받습니다.

요청 필드

필드명 타입 필수/선택 제약 조건 설명
expiredToken string 필수 JWT 형식 만료된 Access Token
provider string 필수 "server", "google", "kakao", "naver" 인증 제공자

요청 본문

{ "expiredToken": "eyJhbGciOiJIUzUxMiJ9...", "provider": "server" }

응답 본문

{ "access_token": "eyJhbGciOiJIUzUxMiJ9...", "expires_in": 1800, "token_type": "Bearer" }
200: 토큰 재발급 성공 401: Refresh Token 무효 406: 요청 유효하지 않음
⚠️ 오류 응답 예시
{ "timestamp": "2024-01-15T10:30:00Z", "status": 401, "error": "Unauthorized", "message": "Refresh token not found or expired", "path": "/auth/api/protected/refresh" }

사용자 관리 API

회원 가입 🌐 공개

POST
/api/public/join

새로운 사용자를 등록합니다. 이메일 인증 코드가 사전에 검증되어야 합니다.

요청 필드

필드명 타입 필수/선택 제약 조건 설명
userId string 필수 4-20자, 영문+숫자 사용자 ID
userPw string 필수 8-50자, 영문+숫자+특수문자 비밀번호
userName string 필수 2-20자 실명
nickname string 필수 2-15자, 중복 불가 닉네임
phone string 선택 010-XXXX-XXXX 형식 휴대폰 번호
email string 필수 유효한 이메일 형식 이메일 주소
birthDate string 선택 YYYY-MM-DD 형식 생년월일
gender string 선택 "male", "female", "other" 성별
isPrivate boolean 선택 기본값: false 프로필 공개 여부
profile string 선택 유효한 URL 프로필 이미지 URL
code string 필수 8자리 영문+숫자 이메일 인증 코드
📋 비밀번호 정책
  • 최소 8자, 최대 50자
  • 영문 대소문자, 숫자, 특수문자 중 3종류 이상 포함
  • 연속된 문자 3개 이상 사용 금지
  • 사용자 ID와 동일하거나 포함 금지

요청 본문

{ "userId": "newUser123", "userPw": "password123!", "userName": "홍길동", "nickname": "쾌활한다람쥐", "phone": "010-1234-5678", "email": "[email protected]", "role": "USER", "birthDate": "1990-01-01", "gender": "male", "isPrivate": false, "profile": "https://example.com/profile.jpg", "code": "A1B2C3D4" }

응답 본문

{ "message": "join successfully", "userId": "newUser123", "createdAt": "2024-01-15T10:30:00Z" }
200: 회원 가입 성공 400: 잘못된 요청 409: 이미 존재하는 아이디/닉네임 500: 서버 오류
⚠️ 유효성 검사 오류 예시
{ "timestamp": "2024-01-15T10:30:00Z", "status": 400, "error": "Bad Request", "message": "Validation failed", "path": "/api/public/join", "validationErrors": [ { "field": "userPw", "message": "비밀번호는 8자 이상이어야 합니다" }, { "field": "email", "message": "유효한 이메일 형식이 아닙니다" } ] }

로그인 🌐 공개

POST
/api/auth/login

사용자 ID와 비밀번호로 로그인하고 JWT 토큰을 발급받습니다.

요청 필드

필드명 타입 필수/선택 제약 조건 설명
userId string 필수 4-20자 사용자 ID
password string 필수 8-50자 비밀번호

요청 본문

{ "userId": "newUser123", "password": "password123!" }

응답 본문

{ "access_token": "eyJhbGciOiJIUzUxMiJ9...", "expires_in": 1800, "token_type": "Bearer", "user": { "userId": "newUser123", "nickname": "쾌활한다람쥐", "role": "USER" } }
200: 로그인 성공 400: 잘못된 요청 401: 로그인 실패
🔒 로그인 보안 정책
  • 5회 연속 실패 시 계정 잠금 (30분)
  • 로그인 성공 시 이전 세션 무효화
  • Refresh Token은 HttpOnly 쿠키로 설정

프로필 이미지 업로드 🌐 공개

POST
/api/public/profileUpload

사용자 프로필 이미지를 업로드하고 이미지 URL을 반환받습니다.

항목 설명
요청 형식 multipart/form-data
파일 필드명 profile
지원 형식 JPG, PNG, GIF
최대 크기 5MB
권장 해상도 500x500px

응답 본문

{ "fileName": "https://your-file-server.com/attach/profile/xxxx_profile.jpg", "originalName": "profile.jpg", "size": 1024000, "uploadedAt": "2024-01-15T10:30:00Z" }
200: 업로드 성공 400: 잘못된 파일 500: 서버 오류

사용자 ID 중복 체크 🌐 공개

POST
/api/public/check/userId/IsDuplicate

제공된 사용자 ID가 이미 사용 중인지 확인합니다.

요청 본문

{ "userId": "newUser123" }

응답 본문

{ "isDuplicate": true, "message": "이미 사용 중인 아이디입니다" }
200: 중복 체크 완료

닉네임 중복 체크 🌐 공개

POST
/api/public/check/nickname/IsDuplicate

제공된 닉네임이 이미 사용 중인지 확인합니다.

요청 본문

{ "nickname": "쾌활한다람쥐" }

응답 본문

{ "isDuplicate": false, "message": "사용 가능한 닉네임입니다" }
200: 중복 체크 완료

로그아웃 (토큰 정리) 🌐 공개

POST
/api/public/clean/userTokenCookie

클라이언트의 refreshToken 쿠키를 만료시켜 제거합니다.

응답 본문

{ "message": "refreshToken deleted", "loggedOutAt": "2024-01-15T10:30:00Z" }
200: 로그아웃 성공

일기 API

일기 작성 🔐 인증 필요

POST
/api/diaries

새로운 일기를 작성하고 저장합니다.

요청 필드

필드명 타입 필수/선택 제약 조건 설명
title string 선택 1-100자 일기 제목
content string 필수 10-5000자 일기 내용

요청 본문

{ "title": "오늘의 일기", "content": "오늘은 정말 즐거운 하루였다. 친구들과 함께 카페에서 즐거운 시간을 보냈고, 새로운 책도 읽기 시작했다." }

응답 본문

{ "id": 1, "userId": 123, "title": "오늘의 일기", "content": "오늘은 정말 즐거운 하루였다...", "createdAt": "2024-01-15T10:30:00Z", "updatedAt": "2024-01-15T10:30:00Z", "wordCount": 45, "isAnalyzed": false }
201: 일기 작성 성공 400: 잘못된 요청 401: 인증 실패
📝 일기 작성 가이드
  • 하루에 최대 5개의 일기 작성 가능
  • 내용은 최소 10자 이상 작성 필요
  • 제목이 없을 경우 자동으로 날짜로 설정
  • 작성 후 감정 분석을 위해 별도 API 호출 필요

일기 목록 조회 🔐 인증 필요

GET
/api/diaries

사용자가 작성한 일기 목록을 페이지네이션하여 조회합니다.

쿼리 파라미터

파라미터명 타입 필수/선택 기본값 설명
page integer 선택 0 페이지 번호 (0부터 시작)
size integer 선택 10 페이지 당 항목 수 (최대 50)
sort string 선택 createdAt,desc 정렬 기준
startDate string 선택 - 시작 날짜 (YYYY-MM-DD)
endDate string 선택 - 종료 날짜 (YYYY-MM-DD)

응답 본문

{ "diaries": [ { "id": 1, "title": "오늘의 일기", "createdAt": "2024-01-15T10:30:00Z", "emotionStatus": "POSITIVE", "wordCount": 45, "isAnalyzed": true } ], "pageInfo": { "currentPage": 0, "totalPages": 5, "totalElements": 42, "hasNext": true, "hasPrevious": false } }
200: 일기 목록 조회 성공 401: 인증 실패

감정 분석 API

일기 감정 분석 요청 🔐 인증 필요

POST
/api/diaries/{diaryId}/analysis

특정 일기에 대한 AI 감정 분석을 비동기적으로 요청합니다.

경로 파라미터

파라미터명 타입 필수/선택 제약 조건 설명
diaryId integer 필수 양의 정수 분석할 일기의 ID

응답 본문

{ "message": "Emotion analysis request accepted. Processing in background.", "diaryId": 1, "trackingId": "kafka-message-id-12345", "estimatedTime": "2-5분", "requestedAt": "2024-01-15T10:30:00Z" }
202: 분석 요청 성공 401: 인증 실패 403: 접근 권한 없음 404: 일기를 찾을 수 없음 409: 이미 분석 중이거나 완료됨
🤖 AI 분석 정보
  • 분석 소요 시간: 평균 2-5분
  • 일기 내용이 10자 미만일 경우 분석 불가
  • 하루 최대 20회 분석 요청 가능
  • 분석 결과는 SSE를 통해 실시간 알림

일기 감정 분석 결과 조회 🔐 인증 필요

GET
/api/diaries/{diaryId}/analysis

특정 일기의 AI 감정 분석 결과를 조회합니다.

응답 본문 (분석 완료)

{ "diaryId": 1, "analysis": { "id": 42, "emotionDetection": { "joy": 80, "sadness": 10, "surprise": 5, "calm": 5 }, "emotionSummary": "기쁨 80%, 슬픔 10%, 놀람 5%, 평온 5%", "automaticThought": "나는 항상 실패한다.", "promptForChange": "정말 항상 실패했나요? 성공했던 경험을 떠올려볼까요?", "alternativeThought": "과거에 몇 번 실패했지만, 그것이 항상 실패한다는 의미는 아니다. 이번에는 다른 방법을 시도해볼 수 있다.", "status": "POSITIVE", "confidence": 0.85, "analyzedAt": "2024-01-15T10:35:00Z" } }

응답 본문 (분석 진행 중)

{ "message": "Analysis is still in progress.", "diaryId": 1, "progress": 65, "estimatedRemaining": "1-2분" }
200: 분석 결과 조회 성공 202: 분석 진행 중 401: 인증 실패 404: 분석 결과를 찾을 수 없음

오류 처리

표준 오류 응답 형식

모든 API 오류는 다음과 같은 표준 형식으로 응답됩니다.

{ "timestamp": "2024-01-15T10:30:00Z", "status": 400, "error": "Bad Request", "message": "상세 오류 메시지", "path": "/api/endpoint", "traceId": "abc123def456", "validationErrors": [ { "field": "필드명", "message": "필드별 오류 메시지", "rejectedValue": "거부된 값" } ] }

주요 오류 코드

상태 코드 오류 타입 설명 해결 방법
400 Bad Request 잘못된 요청 형식 또는 유효성 검사 실패 요청 데이터 확인 및 수정
401 Unauthorized 인증 토큰이 없거나 유효하지 않음 로그인 후 유효한 토큰으로 재요청
403 Forbidden 접근 권한이 없음 권한 확인 또는 관리자 문의
404 Not Found 요청한 리소스를 찾을 수 없음 URL 및 리소스 ID 확인
409 Conflict 리소스 충돌 (중복 등) 중복 데이터 확인 및 수정
429 Too Many Requests 요청 한도 초과 잠시 후 재시도
500 Internal Server Error 서버 내부 오류 관리자 문의

오류 처리 권장사항

💡 클라이언트 오류 처리 가이드
  • 401 오류: 자동으로 토큰 갱신 시도 후 재요청
  • 429 오류: Exponential Backoff 알고리즘 사용
  • 5xx 오류: 사용자에게 친화적인 메시지 표시
  • 네트워크 오류: 재시도 로직 구현 (최대 3회)
  • 타임아웃: 30초 이상 응답 없을 시 요청 취소

오류 코드별 상세 예시

유효성 검사 실패 (400)

{ "timestamp": "2024-01-15T10:30:00Z", "status": 400, "error": "Bad Request", "message": "Validation failed for multiple fields", "path": "/api/public/join", "traceId": "abc123def456", "validationErrors": [ { "field": "userPw", "message": "비밀번호는 8자 이상이어야 합니다", "rejectedValue": "123" }, { "field": "email", "message": "유효한 이메일 형식이 아닙니다", "rejectedValue": "invalid-email" } ] }

인증 실패 (401)

{ "timestamp": "2024-01-15T10:30:00Z", "status": 401, "error": "Unauthorized", "message": "JWT token is expired", "path": "/api/diaries", "traceId": "def456ghi789", "details": { "tokenExpiredAt": "2024-01-15T10:00:00Z", "refreshTokenAvailable": true } }

리소스 "2024-01-15T10:00:00Z", "refreshTokenAvailable": true } }