hgzero/design/backend/api/user-service-api.yaml
ondal a551235ad7 API 설계 완료
- 5개 마이크로서비스 API 명세 작성 (User, Meeting, STT, AI, Notification)
- OpenAPI 3.0 표준 준수
- 총 47개 API 설계
- 유저스토리 100% 커버리지
- swagger-cli 검증 통과
- 종합 API 설계서 작성

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 10:49:29 +09:00

532 lines
16 KiB
YAML

openapi: 3.0.3
info:
title: User Service API
description: |
회의록 작성 및 공유 개선 서비스의 사용자 인증 전용 서비스
**핵심 기능:**
- LDAP 기반 사용자 인증
- JWT 토큰 발급 및 검증
- 세션 관리 (Access Token + Refresh Token)
**보안:**
- LDAP 인증 (LDAPS, port 636)
- JWT Bearer 토큰
- 계정 잠금 (5회 실패 시 30분)
- Refresh Token (Redis 저장, 7일 TTL)
version: 1.0.0
contact:
name: Backend Team
email: backend@company.com
servers:
- url: https://api.meeting.company.com
description: Production Server
- url: https://dev-api.meeting.company.com
description: Development Server
- url: http://localhost:8081
description: Local Development
tags:
- name: Authentication
description: 사용자 인증 관리 API
paths:
/api/v1/auth/login:
post:
summary: 사용자 로그인
description: |
LDAP 인증을 통한 사용자 로그인 처리
**처리 흐름:**
1. LDAP 서버에 사용자 인증 요청
2. 인증 성공 시 사용자 정보 조회
3. 신규 사용자일 경우 자동 등록
4. JWT Access Token 및 Refresh Token 발급
5. Refresh Token을 Redis에 저장 (7일 TTL)
6. 최종 로그인 일시 업데이트
**인증 실패 처리:**
- 5회 실패 시 계정 30분 잠금
- 잠금 상태에서는 로그인 불가
operationId: login
x-user-story: AFR-USER-010
x-controller: UserController
tags:
- Authentication
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
examples:
normal:
summary: 정상 로그인 요청
value:
username: "user001"
password: "P@ssw0rd123!"
responses:
'200':
description: 로그인 성공
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
examples:
success:
summary: 로그인 성공 응답
value:
accessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
refreshToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
tokenType: "Bearer"
expiresIn: 3600
user:
userId: "user001"
username: "user001"
name: "홍길동"
email: "hong@company.com"
department: "개발팀"
title: "선임"
roles:
- "USER"
'401':
$ref: '#/components/responses/UnauthorizedError'
'403':
description: 계정 잠금
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
accountLocked:
summary: 계정 잠금 응답
value:
error:
code: "ACCOUNT_LOCKED"
message: "계정이 잠겼습니다"
details: "비밀번호 5회 실패로 30분간 계정이 잠겼습니다. 잠금 해제 시간: 2025-10-23T13:30:00Z"
timestamp: "2025-10-23T13:00:00Z"
path: "/api/v1/auth/login"
'500':
$ref: '#/components/responses/InternalServerError'
/api/v1/auth/refresh:
post:
summary: Access Token 갱신
description: |
Refresh Token을 사용하여 새로운 Access Token 발급
**처리 흐름:**
1. Refresh Token 검증
2. Redis에서 Refresh Token 존재 확인
3. 새로운 Access Token 발급
4. Refresh Token 갱신 (옵션)
operationId: refreshToken
x-user-story: AFR-USER-010
x-controller: UserController
tags:
- Authentication
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RefreshTokenRequest'
examples:
normal:
summary: 토큰 갱신 요청
value:
refreshToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
responses:
'200':
description: 토큰 갱신 성공
content:
application/json:
schema:
$ref: '#/components/schemas/RefreshTokenResponse'
examples:
success:
summary: 토큰 갱신 성공 응답
value:
accessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
refreshToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
tokenType: "Bearer"
expiresIn: 3600
'401':
$ref: '#/components/responses/UnauthorizedError'
'500':
$ref: '#/components/responses/InternalServerError'
/api/v1/auth/logout:
post:
summary: 로그아웃
description: |
사용자 로그아웃 처리
**처리 흐름:**
1. Access Token에서 사용자 정보 추출
2. Redis에서 Refresh Token 삭제
3. 로그아웃 완료
operationId: logout
x-user-story: AFR-USER-010
x-controller: UserController
tags:
- Authentication
security:
- BearerAuth: []
responses:
'200':
description: 로그아웃 성공
content:
application/json:
schema:
$ref: '#/components/schemas/LogoutResponse'
examples:
success:
summary: 로그아웃 성공 응답
value:
message: "로그아웃되었습니다"
timestamp: "2025-10-23T14:30:00Z"
'401':
$ref: '#/components/responses/UnauthorizedError'
'500':
$ref: '#/components/responses/InternalServerError'
/api/v1/auth/validate:
get:
summary: 토큰 검증
description: |
JWT Access Token 유효성 검증
**검증 항목:**
- 토큰 서명 검증
- 토큰 만료 시간 확인
- 사용자 정보 조회 (옵션)
**용도:**
- API Gateway에서 인증 검증
- 다른 서비스에서 사용자 정보 조회
operationId: validateToken
x-user-story: AFR-USER-010
x-controller: UserController
tags:
- Authentication
security:
- BearerAuth: []
parameters:
- name: includeUserInfo
in: query
description: 사용자 정보 포함 여부
required: false
schema:
type: boolean
default: false
responses:
'200':
description: 토큰 유효
content:
application/json:
schema:
$ref: '#/components/schemas/ValidateTokenResponse'
examples:
valid:
summary: 토큰 유효 응답
value:
valid: true
userId: "user001"
username: "user001"
roles:
- "USER"
expiresAt: "2025-10-23T15:00:00Z"
validWithUserInfo:
summary: 사용자 정보 포함 응답
value:
valid: true
userId: "user001"
username: "user001"
roles:
- "USER"
expiresAt: "2025-10-23T15:00:00Z"
user:
userId: "user001"
username: "user001"
name: "홍길동"
email: "hong@company.com"
department: "개발팀"
title: "선임"
'401':
$ref: '#/components/responses/UnauthorizedError'
'500':
$ref: '#/components/responses/InternalServerError'
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: JWT Bearer Token
schemas:
LoginRequest:
type: object
required:
- username
- password
properties:
username:
type: string
description: 사용자명 (사번)
minLength: 3
maxLength: 50
example: "user001"
password:
type: string
description: 비밀번호
format: password
minLength: 8
maxLength: 100
example: "P@ssw0rd123!"
LoginResponse:
type: object
required:
- accessToken
- refreshToken
- tokenType
- expiresIn
- user
properties:
accessToken:
type: string
description: JWT Access Token
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMDAxIiwidXNlcm5hbWUiOiJ1c2VyMDAxIiwicm9sZXMiOlsiVVNFUiJdLCJleHAiOjE3MzAwMDAwMDB9.signature"
refreshToken:
type: string
description: Refresh Token (7일 유효)
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMDAxIiwidHlwZSI6InJlZnJlc2giLCJleHAiOjE3MzA2MDQ4MDB9.signature"
tokenType:
type: string
description: 토큰 타입
enum:
- Bearer
example: "Bearer"
expiresIn:
type: integer
description: Access Token 만료 시간 (초)
example: 3600
user:
$ref: '#/components/schemas/UserInfo'
RefreshTokenRequest:
type: object
required:
- refreshToken
properties:
refreshToken:
type: string
description: Refresh Token
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
RefreshTokenResponse:
type: object
required:
- accessToken
- refreshToken
- tokenType
- expiresIn
properties:
accessToken:
type: string
description: 새로운 JWT Access Token
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
refreshToken:
type: string
description: 새로운 Refresh Token (옵션)
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
tokenType:
type: string
description: 토큰 타입
enum:
- Bearer
example: "Bearer"
expiresIn:
type: integer
description: Access Token 만료 시간 (초)
example: 3600
LogoutResponse:
type: object
required:
- message
- timestamp
properties:
message:
type: string
description: 로그아웃 완료 메시지
example: "로그아웃되었습니다"
timestamp:
type: string
format: date-time
description: 로그아웃 시간
example: "2025-10-23T14:30:00Z"
ValidateTokenResponse:
type: object
required:
- valid
- userId
- username
- roles
- expiresAt
properties:
valid:
type: boolean
description: 토큰 유효 여부
example: true
userId:
type: string
description: 사용자 ID
example: "user001"
username:
type: string
description: 사용자명
example: "user001"
roles:
type: array
description: 사용자 권한 목록
items:
type: string
example:
- "USER"
expiresAt:
type: string
format: date-time
description: 토큰 만료 시간
example: "2025-10-23T15:00:00Z"
user:
$ref: '#/components/schemas/UserInfo'
description: 사용자 정보 (includeUserInfo=true 시)
UserInfo:
type: object
required:
- userId
- username
- name
- email
properties:
userId:
type: string
description: 사용자 ID
example: "user001"
username:
type: string
description: 사용자명 (사번)
example: "user001"
name:
type: string
description: 이름
example: "홍길동"
email:
type: string
format: email
description: 이메일
example: "hong@company.com"
department:
type: string
description: 부서
example: "개발팀"
title:
type: string
description: 직급
example: "선임"
roles:
type: array
description: 권한 목록
items:
type: string
example:
- "USER"
ErrorResponse:
type: object
required:
- error
properties:
error:
type: object
required:
- code
- message
- timestamp
- path
properties:
code:
type: string
description: 에러 코드
example: "AUTHENTICATION_FAILED"
message:
type: string
description: 에러 메시지
example: "인증에 실패했습니다"
details:
type: string
description: 상세 에러 정보
example: "사용자명 또는 비밀번호가 올바르지 않습니다"
timestamp:
type: string
format: date-time
description: 에러 발생 시간
example: "2025-10-23T12:00:00Z"
path:
type: string
description: 요청 경로
example: "/api/v1/auth/login"
responses:
UnauthorizedError:
description: 인증 실패
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
authenticationFailed:
summary: 인증 실패
value:
error:
code: "AUTHENTICATION_FAILED"
message: "인증에 실패했습니다"
details: "사용자명 또는 비밀번호가 올바르지 않습니다"
timestamp: "2025-10-23T12:00:00Z"
path: "/api/v1/auth/login"
invalidToken:
summary: 유효하지 않은 토큰
value:
error:
code: "INVALID_TOKEN"
message: "유효하지 않은 토큰입니다"
details: "토큰이 만료되었거나 형식이 올바르지 않습니다"
timestamp: "2025-10-23T12:00:00Z"
path: "/api/v1/auth/validate"
InternalServerError:
description: 서버 오류
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
serverError:
summary: 서버 오류
value:
error:
code: "INTERNAL_SERVER_ERROR"
message: "서버 오류가 발생했습니다"
details: "일시적인 오류입니다. 잠시 후 다시 시도해주세요"
timestamp: "2025-10-23T12:00:00Z"
path: "/api/v1/auth/login"