본문으로 건너뛰기
버전: 0.64.0

사용자 로그인 API

사용자 로그인 API는 등록된 사용자가 시스템에 접근하기 위한 인증 과정을 처리합니다. 성공적인 로그인 시, 액세스 토큰과 리프레시 토큰이 발급됩니다.

사용자 로그인​

이메일과 해시된 비밀번호를 사용하여 사용자 로그인을 처리하고, 성공 시 토큰을 발급합니다.

  • HTTP Method: POST
  • Path: /de/v1/auth/login
  • 인증: 없음 (이 API는 토큰을 발급받기 위해 사용됨)

Headers​

HeaderTypeDescriptionRequired
Content-Typeapplication/json요청 본문 형식을 지정합니다.Yes

Request Body (LoginDto)​

비밀번호는 클라이언트에서 해싱하여 전송해야 합니다 (예: SHA-256).

{
"email": "user@example.com",
"passwordHash": "hashed_password_example",
"deviceUuid": "unique-device-id-12345"
}
필드타입설명필수 (Yes/No)
emailstring사용자 이메일 주소Yes
passwordHashstring클라이언트에서 해싱된 사용자 비밀번호Yes
deviceUuidstring디바이스 고유 UUID (선택 사항)No

Responses​

HTTP Status Code설명Error Code(s)
200 OK로그인 성공-
400 Bad Request잘못된 요청 (예: 잘못된 자격 증명, 계정 잠김)2010, 2004
401 Unauthorized앱 토큰 인증 실패2051
404 Not Found리소스 없음 (예: 사용자 주기 없음)7003
500 Internal Server Error서버 내부 오류2000
200 OK - 로그인 성공

성공적으로 로그인하면 LoginResponseDto에 정의된 전체 사용자 세션 정보가 반환됩니다. 여기에는 인증 토큰, 사용자 정보, 현재 활성 주기, 프로필 정보, 역할, 권한 및 동의한 약관 목록이 포함됩니다.

{
"tokens": [
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"type": "ACCESS_TOKEN",
"expiresIn": 1800,
"issuedAt": 1714523700000
},
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"type": "REFRESH_TOKEN",
"expiresIn": 604800,
"issuedAt": 1714523700000
}
],
"user": {
"id": "c1e7c5cf-6fc2-4f4f-8e2f-9b8a3c5e2d1b",
"email": "user@example.com",
"questionnaireBundleId": "bundle-v1.2.0-20240301",
"suspensionEndAt": 1678972800000,
"createdAt": 1678886400000
},
"userCycle": {
"id": "d2f8e5c1-9b3a-4f8c-8e2f-1b9a3c5e2d7c",
"status": "ACTIVE",
"startedAt": 1678886400000,
"count": 3,
"treatmentDurationDays": 90
},
"profile": {
"language": "de-DE",
"timezone": {
"id": "Europe/Berlin",
"offsetInMinutes": 60
},
"userName": "John Doe"
},
"roles": ["healthcare.patientUser", "content.editor"],
"permissions": ["sleep.log.create", "sleep.log.read", "sleep.goal.update"],
"agreements": [
{
"versionId": "some-version-id",
"type": "CONSENT",
"isRequired": false,
"title": "Datenverarbeitung zur Verbesserung",
"text": "Ich erlaube die Verarbeitung der zu der dauerhaften Gewährleistung der technischen Funktionsfähigkeit...",
"detailsUrl": "https://www.weltcorp.com",
"isAgreed": false,
"agreedAt": 1699981800000
}
]
}
필드타입설명예시필수 (Yes/No)
tokensarray인증 토큰 정보 배열 (TokenResponseDto[]). 정확히 2개의 토큰(access token과 refresh token)을 포함해야 합니다.Yes
tokens[].tokenstring토큰 값eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Yes
tokens[].typestring토큰 타입 (TokenType 참조)ACCESS_TOKEN 또는 REFRESH_TOKENYes
tokens[].expiresInnumber토큰 만료 시간(초)1800Yes
tokens[].issuedAtnumber토큰 발급 시간 (밀리초 단위 타임스탬프)1714523700000Yes
userobject사용자 정보 (UserDto)Yes
user.idstring사용자 IDYes
user.emailstring사용자 이메일Yes
user.questionnaireBundleIdstring사용자의 활성 설문지 번들 IDbundle-v1.2.0-20240301Yes
user.suspensionEndAtnumber일시정지 종료일 (Unix timestamp in milliseconds, Kotlin: Long, Swift: Int64, isSuspended가 true일 때만 제공)1678972800000No
user.createdAtnumber사용자 생성일 (Unix timestamp in milliseconds, Kotlin: Long, Swift: Int64)Yes
userCycleobject사용자 현재 활성 주기 정보 (UserCycleDto)Yes
userCycle.idstring사용자 주기 IDYes
userCycle.statusstring사용자 주기 상태 (UserCycleStatus 참조)ACTIVE (그 외: PENDING, COMPLETED, CANCELLED)Yes
userCycle.startedAtnumber주기 시작일 (Unix timestamp in milliseconds, Kotlin: Long, Swift: Int64)Yes
userCycle.countnumber해당 사용자의 총 주기 개수Yes
profileobject사용자 프로필 정보 (UserProfileDto)Yes
profile.languagestring사용자 언어 설정 (LanguageCodes 참조) (예: 'de-DE')Yes
profile.timezoneobject사용자 시간대 정보 (TimezoneDto)Yes
profile.timezone.idstring사용자 시간대 ID (예: 'Europe/Berlin')Yes
profile.timezone.offsetInMinutesnumberUTC와의 시간차(분) (예: 60)Yes
profile.userNamestring사용자 이름 (예: 'John Doe') (선택 사항)No
rolesstring[]사용자 역할 이름 목록["healthcare.patientUser", "content.editor"]Yes
permissionsstring[]사용자 권한 문자열 목록["sleep.log.create", "sleep.log.read"]Yes
agreementsobject[]사용자 동의 항목 목록 (UserAgreementItemDto[])Yes
agreements[].versionIdstring동의한 약관 버전의 고유 IDYes
agreements[].typestring약관 타입 (AgreementType 참조)CONSENTYes
agreements[].isRequiredboolean필수 약관 여부Yes
agreements[].textstring약관 내용 요약Yes
agreements[].detailsUrlstring약관 상세 내용 URLYes
agreements[].isAgreedboolean동의 여부Yes
agreements[].agreedAtnumber사용자 동의 시각 (Unix timestamp in milliseconds, Kotlin: Long, Swift: Int64)Yes
400 Bad Request - 잘못된 요청

예시: 잘못된 자격 증명 (이메일 또는 비밀번호 오류)

{
"code": 2010, // AuthErrorCode.INVALID_CREDENTIALS
"message": "INVALID_CREDENTIALS",
"detail": "이메일 또는 비밀번호가 올바르지 않습니다",
"metadata": {
"remainingAttempts": 4
}
}

이 오류는 InvalidCredentialsError에 해당하며, authentication.service.ts에서 사용자를 찾지 못하거나 비밀번호가 일치하지 않을 때 발생합니다. 이메일 또는 비밀번호 형식이 잘못된 경우에도 이 오류가 발생할 수 있습니다.

예시: 계정 잠김

{
"code": 2004, // AuthErrorCode.ACCOUNT_LOCKED
"message": "ACCOUNT_LOCKED",
"detail": "계정이 잠겨 있습니다",
"metadata": {
"remainingLockoutSeconds": 58
}
}

이 오류는 AccountLockedError에 해당하며, 로그인 시도 실패 횟수 초과 등으로 계정이 잠겼을 때 발생합니다.

401 Unauthorized - 앱 토큰 인증 실패
{
"code": 2051,
"message": "INVALID_TOKEN",
"detail": "토큰이 유효하지 않습니다"
}
404 Not Found - 리소스 없음

예시: 사용자 활성 주기(UserCycle)를 찾을 수 없음

{
"code": 7003, // UserErrorCode.USER_CYCLE_NOT_FOUND
"message": "USER_CYCLE_NOT_FOUND",
"detail": "사용자 주기를 찾을 수 없습니다.",
"metadata": {
"userId": "user-id-example"
}
}

이 오류는 UserCycleNotFoundError에 해당하며, authentication.service.ts에서 사용자의 활성 UserCycle ID를 조회할 수 없을 때 발생합니다.

500 Internal Server Error - 서버 내부 오류
{
"code": 2000, // AuthErrorCode.SERVER_ERROR
"message": "SERVER_ERROR",
"detail": "서버 내부 오류"
}

이 오류는 ServerError에 해당하며, 처리 중 예기치 않은 서버 내부 문제가 발생했을 때 반환될 수 있습니다.

설명​

  • 이 API는 libs/feature/auth/src/lib/infrastructure/services/authentication.service.ts의 login 메서드의 로직을 기반으로 합니다.
  • 주요 검증 단계 (서비스 로직 내):
    1. 이메일로 사용자 조회 (authRepository.findUserByEmail).
    2. 사용자의 활성 userCycleId 확인 (authRepository.getUserActiveUserCycleId).
    3. 사용자 계정 상태 확인 (user.canLogin(), user.isLocked()).
    4. 사용자 passwordHash 존재 여부 확인.
    5. 제공된 hashedPassword와 저장된 user.passwordHash 비교 (passwordService.comparePassword).
  • 로그인 실패 시, 실패 횟수가 누적되며 (authRepository.incrementFailedLoginAttempts), 설정된 최대 실패 횟수 도달 시 계정이 잠길 수 있습니다 (authRepository.lockAccount).
  • 성공적인 로그인 시, 실패 횟수가 초기화되고 (authRepository.resetFailedLoginAttempts), 사용자의 마지막 로그인 시간이 업데이트됩니다 (authRepository.updateUserLastLogin).
  • deviceUuid는 토큰 생성 시 (tokenService.generateTokens) 사용될 수 있습니다.
  • 발급되는 토큰에는 사용자의 역할(roles), 권한(permissions), 동의한 약관(agreements) 정보가 포함될 수 있습니다.

LoginResponseDto 상세​

최상위 응답 객체인 LoginResponseDto에 대한 상세 설명입니다.

필드타입설명예시 (일부)필수
tokensTokenResponseDto[]인증 토큰 정보 배열(TokenResponseDto 참조)Yes
userUserDto사용자 정보(UserDto 참조)Yes
userCycleUserCycleDto사용자 현재 활성 주기 정보(UserCycleDto 참조)Yes
profileUserProfileDto사용자 프로필 정보(UserProfileDto 참조)Yes
rolesstring[]사용자 역할 이름 목록["healthcare.patientUser", "content.editor"]Yes
permissionsstring[]사용자 권한 문자열 목록["sleep.log.create", "sleep.log.read"]Yes
agreementsUserAgreementItemDto[]사용자 동의 항목 목록(UserAgreementItemDto 배열 참조)Yes

TokenResponseDto 상세​

LoginResponseDto의 tokens 배열에 있는 각 객체에 대한 상세 설명입니다. 이 배열은 반드시 정확히 2개의 토큰(access token과 refresh token)을 포함해야 합니다.

필드타입설명예시필수
tokenstring토큰 값eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Yes
typestring토큰 타입 (TokenType 참조)ACCESS_TOKEN 또는 REFRESH_TOKENYes
expiresInnumber토큰 만료 시간 (초, Kotlin: Int, Swift: Int)1800Yes
issuedAtnumber토큰 발급 시간 (Unix timestamp in milliseconds, Kotlin: Long, Swift: Int64)1714523700000Yes

UserDto 상세​

LoginResponseDto의 user 객체에 대한 상세 설명입니다.

필드타입설명예시필수 (Yes/No)
idstring사용자 IDc1e7c5cf-6fc2-4f4f-8e2f-9b8a3c5e2d1bYes
emailstring사용자 이메일user@example.comYes
questionnaireBundleIdstring사용자의 활성 설문지 번들 IDbundle-v1.2.0-20240301Yes
suspensionEndAtnumber일시정지 종료일 (Unix timestamp in milliseconds, Kotlin: Long, Swift: Int64, isSuspended가 true일 때만 제공)1678972800000No
createdAtnumber사용자 생성일 (Unix timestamp in milliseconds, Kotlin: Long, Swift: Int64)1678886400000Yes

UserCycleDto 상세​

LoginResponseDto의 userCycle 객체에 대한 상세 설명입니다.

필드타입설명예시필수 (Yes/No)
idstring사용자 주기 IDd2f8e5c1-9b3a-4f8c-8e2f-1b9a3c5e2d7cYes
statusstring사용자 주기 상태ACTIVE (그 외: PENDING, COMPLETED, CANCELLED)Yes
startedAtnumber주기 시작일 (Unix timestamp in milliseconds, Kotlin: Long, Swift: Int64)1678886400000Yes
countnumber해당 사용자의 총 주기 개수 (Kotlin: Int, Swift: Int)3Yes
treatmentDurationDaysnumber치료 주기의 총 기간 (일수, Kotlin: Int, Swift: Int)90Yes

UserProfileDto 상세​

LoginResponseDto의 profile 객체에 대한 상세 설명입니다.

필드타입설명예시필수 (Yes/No)
languagestring사용자 언어 설정 (LanguageCodes 참조)deYes
timezoneTimezoneDto사용자 시간대 정보 (TimezoneDto 참조)(TimezoneDto 상세 섹션 참조)Yes
userNamestring사용자 이름 (예: 'John Doe') (선택 사항)John DoeNo

TimezoneDto 상세​

UserProfileDto 내의 timezone 객체에 대한 상세 설명입니다.

필드타입설명예시필수 (Yes/No)
idstring사용자 시간대 ID (예: 'Europe/Berlin')Europe/BerlinYes
offsetInMinutesnumberUTC와의 시간차 (분, Kotlin: Int, Swift: Int) (예: 60)60Yes

변경 이력​

버전날짜작성자변경 내용
0.1.02025-05-19elizabeth@weltcorp.com최초 문서 작성
0.2.02025-05-27elizabeth@weltcorp.com401 Unauthorized 오류 추가 (2051 INVALID_TOKEN)
0.3.02025-06-24elizabeth@weltcorp.comUserDto에 questionnaireBundleId 필드 추가
0.4.02025-06-25elizabeth@weltcorp.comUserDto에 suspensionEndAt 필드 추가
0.5.02025-07-15bok@weltcorp.comUserCycleDto에 treatmentDurationDays 필드 추가 (치료 주기 총 기간)