Skip to content

Commit bdebb66

Browse files
authored
refactor: 인증 도메인 리팩토링 (#42)
* [BOOK-140] chore: apis - 패키지 이동 * [BOOK-140] refactor: apis - vo가 아닌 dto를 반화하도록 변경 * [BOOK-140] chore: apis - 개행 삭제 * [BOOK-140] chore: apis - 필요없는 명세 삭제 * [BOOK-140] feat: apis - 애플리케이션 레이어에 사용될 DTO 정의 * [BOOK-140] feat: domain - 도메인 전용 예외 클래스 구현 * [BOOK-140] refactor: domain - 역할 부여 여부에 따라 정적 팩토리 메서드 분리 * [BOOK-140] chore: apis - 패키지 이동 * [BOOK-140] chore: apis - 패키지 이동 * [BOOK-140] refactor: apis - 정적팩토리 메서드 인자로 vo를 받도록 변경 * [BOOK-140] refactor: apis - 컨트롤러에서 usecase로 갈때 dto를 받도록 변경 * [BOOK-140] refactor: apis - valid 로직 도입 * [BOOK-140] refactor: domain - 인증 로직에 사용되는 vo 구현 * [BOOK-140] refactor: domain, infra - 도메인 서비스에서 해당하는 vo를 리턴하도록 변경 * [BOOK-140] chore: infra - 새로운 메서드 추가 * [BOOK-140] refactor: apis - 인증 로직을 클린 아키텍처에 맞게 리팩토링 * [BOOK-140] chore: apis - dto 필드 검증 NotBlank로 변경 * [BOOK-140] chore: apis - 가시성을 위한 개행 추가 * [BOOK-140] refactor: apis, domain - 코드리뷰 반영 * [BOOK-140] refactor: apis, domain, infra - 코드리뷰 반영 * [BOOK-140] refactor: domain - UuidGenerator 유틸 클래스로 아이디 생성 방식 변경 * fix: sub 클레임 값을 UUID로 변환해 Authentication 객체의 principal로 설정하도록 수정 (#44) * [BOOK-143] fix: gateway - sub 클레임을 UUID 타입으로 변환하여 인증 객체의 principal로 설정하도록 수정 * [BOOK-143] fix: gateway - SecurityConfig에서 의존성 타입 일치하도록 수정 * [BOOK-140] refactor: domain, infra - jpa 메서드 재정의 * [BOOK-140] feat: domain - 리프레쉬 토큰 관련 도메인 에러 클래스 생성 * [BOOK-140] chore: domain, apis - 메서드 네이밍 변경 * [BOOK-140] chore: domain - 필요 없는 중괄호 제거 * [BOOK-140] feat: doamin, infra - RefreshToken 도메인에 Value Class 적용 및 생성자 검증 추가 - RefreshToken 내 id, token, userId를 각각 Value Class(Id, Token, UserId)로 분리 - Value Class 내 newInstance 정적 팩토리 메서드 구현 및 검증 로직 추가 * [BOOK-140] feat: apis - 비즈니스 로직에 사용될 요청 및 응답 dto 구현 * [BOOK-140] refactor: domain - 무조건 true인 검증 제거 * [BOOK-140] refactor: requestDTO를 받도록 변경 * [BOOK-140] refactor: apis - redis 관련 인증도메인 리팩토링 * [BOOK-140] refactor: apis, infra - 값 기반 비교를 위해 객체들을 VO(Value Object)로 리팩토링 - ID, EMAIL, PROVIDERID는 동일한 값을 가지면 같은 객체임 * [BOOK-140] refactor: infra - this 추가 * [BOOK-140] refactor: apis, domain - vo 매핑으로 인한 변동사항 반영 * [BOOK-140] refactor: domain, infra - 값 기반 비교를 위해 객체들을 VO(Value Object)로 리팩토링 * [BOOK-140] refactor: apis, domain - 값 객체 매핑으로 인한 변동사항 반영 * [BOOK-140] feat: domain - 책 관련 도메인 예외 클래스 생성 * [BOOK-140] feat: domain - 책 관련 도메인 예외 클래스 생성 * [BOOK-140] refactor: domain, infra - 생성된 도메인 예외를 이용해 로직 리팩토링 * [BOOK-140] chore: apis, domain - vo 이름 구체화 * [BOOK-140] chore: infra - 패키지 변경 * [BOOK-140] refactor: domain, infra - 값 기반 비교를 위해 객체들을 VO(Value Object)로 리팩토링 * [BOOK-140] refactor: domain, apis - VO 래핑으로 인한 변동사항 반영 * [BOOK-140] refactor: apis, domain, infra - 코드레빗 리뷰 반영 * [BOOK-140] refactor: apis - 코드레빗 리뷰 반영 * [BOOK-140] chore: domain - 메서드 위치 변경 * [BOOK-140] chore: apis - 가독성을 위한 개행 추가 * [BOOK-140] feat: global-utils - email, isbn 전역 validator 구현 * [BOOK-140] refactor: domain - email, isbn 전역 validator 적용
1 parent 00bd424 commit bdebb66

File tree

66 files changed

+1021
-406
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+1021
-406
lines changed

apis/src/main/kotlin/org/yapp/apis/auth/controller/AuthController.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,13 @@ class AuthController(
2222

2323
@PostMapping("/signin")
2424
override fun signIn(@RequestBody @Valid request: SocialLoginRequest): ResponseEntity<AuthResponse> {
25-
val credentials = SocialLoginRequest.toCredentials(request)
26-
val tokenPair = authUseCase.signIn(credentials)
25+
val tokenPair = authUseCase.signIn(request)
2726
return ResponseEntity.ok(AuthResponse.fromTokenPair(tokenPair))
2827
}
2928

3029
@PostMapping("/refresh")
3130
override fun refreshToken(@RequestBody @Valid request: TokenRefreshRequest): ResponseEntity<AuthResponse> {
32-
val tokenPair = authUseCase.reissueTokenPair(request.validRefreshToken())
31+
val tokenPair = authUseCase.reissueTokenPair(request)
3332
return ResponseEntity.ok(AuthResponse.fromTokenPair(tokenPair))
3433
}
3534

apis/src/main/kotlin/org/yapp/apis/auth/controller/AuthControllerApi.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,14 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal
1212
import org.springframework.web.bind.annotation.GetMapping
1313
import org.springframework.web.bind.annotation.PostMapping
1414
import org.springframework.web.bind.annotation.RequestBody
15-
import org.springframework.web.bind.annotation.RequestMapping
1615
import org.yapp.apis.auth.dto.request.SocialLoginRequest
1716
import org.yapp.apis.auth.dto.request.TokenRefreshRequest
1817
import org.yapp.apis.auth.dto.response.AuthResponse
1918
import org.yapp.apis.auth.dto.response.UserProfileResponse
2019
import org.yapp.globalutils.exception.ErrorResponse
21-
import java.util.UUID
20+
import java.util.*
2221

2322
@Tag(name = "Authentication", description = "Authentication API")
24-
@RequestMapping("/api/v1/auth")
2523
interface AuthControllerApi {
2624

2725
@Operation(

apis/src/main/kotlin/org/yapp/apis/auth/dto/UserCreateInfo.kt

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.yapp.apis.auth.dto.request
2+
3+
import io.swagger.v3.oas.annotations.media.Schema
4+
import jakarta.validation.constraints.NotBlank
5+
import org.yapp.apis.auth.dto.response.RefreshTokenResponse
6+
7+
@Schema(
8+
name = "DeleteTokenRequest",
9+
description = "Request DTO for deleting a refresh token"
10+
)
11+
data class DeleteTokenRequest(
12+
@field:NotBlank(message = "Refresh token must not be blank.")
13+
@Schema(description = "Refresh token to be deleted", example = "eyJhbGciOiJIUz...")
14+
val refreshToken: String? = null
15+
) {
16+
fun validRefreshToken() = refreshToken!!
17+
18+
companion object {
19+
fun from(refreshTokenResponse: RefreshTokenResponse): DeleteTokenRequest {
20+
return DeleteTokenRequest(refreshToken = refreshTokenResponse.refreshToken)
21+
}
22+
}
23+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package org.yapp.apis.auth.dto.request
2+
3+
import io.swagger.v3.oas.annotations.media.Schema
4+
import jakarta.validation.constraints.NotBlank
5+
import org.yapp.apis.auth.dto.response.UserCreateInfoResponse
6+
import org.yapp.apis.util.NicknameGenerator
7+
import org.yapp.domain.user.ProviderType
8+
9+
@Schema(
10+
name = "FindOrCreateUserRequest",
11+
description = "Request DTO for finding an existing user or creating a new one during social login"
12+
)
13+
data class FindOrCreateUserRequest private constructor(
14+
@Schema(
15+
description = "사용자 이메일",
16+
example = "user@example.com", nullable = true)
17+
val email: String? = null,
18+
19+
@Schema(
20+
description = "사용자 닉네임",
21+
example = "코딩하는곰",
22+
nullable = true
23+
)
24+
val nickname: String? = null,
25+
26+
@Schema(
27+
description = "사용자 프로필 이미지 URL",
28+
example = "https://example.com/image.jpg",
29+
nullable = true
30+
)
31+
val profileImageUrl: String? = null,
32+
33+
@Schema(
34+
description = "소셜 로그인 제공자",
35+
example = "KAKAO"
36+
)
37+
@field:NotBlank(message = "providerType은 필수입니다.")
38+
val providerType: ProviderType? = null,
39+
40+
@Schema(
41+
description = "소셜 제공자에서 발급한 식별자",
42+
example = "12345678901234567890"
43+
)
44+
@field:NotBlank(message = "providerId는 필수입니다.")
45+
val providerId: String? = null
46+
) {
47+
fun getOrDefaultEmail(): String {
48+
return email?.takeIf { it.isNotBlank() } ?: "${validProviderId()}@${validProviderType().name.lowercase()}.local"
49+
}
50+
51+
fun getOrDefaultNickname(): String {
52+
return nickname?.takeIf { it.isNotBlank() } ?: NicknameGenerator.generate()
53+
}
54+
55+
fun validProviderType(): ProviderType = providerType!!
56+
fun validProviderId(): String = providerId!!
57+
58+
companion object {
59+
fun from(
60+
userCreateInfoResponse: UserCreateInfoResponse
61+
): FindOrCreateUserRequest {
62+
return FindOrCreateUserRequest(
63+
email = userCreateInfoResponse.email,
64+
nickname = userCreateInfoResponse.nickname,
65+
profileImageUrl = userCreateInfoResponse.profileImageUrl,
66+
providerType = userCreateInfoResponse.providerType,
67+
providerId = userCreateInfoResponse.providerId
68+
)
69+
}
70+
}
71+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.yapp.apis.auth.dto.request
2+
3+
import io.swagger.v3.oas.annotations.media.Schema
4+
import jakarta.validation.constraints.NotNull
5+
import org.yapp.apis.auth.dto.response.UserIdResponse
6+
import java.util.*
7+
8+
@Schema(
9+
name = "FindUserIdentityRequest",
10+
description = "Request DTO to retrieve user identity information using userId"
11+
)
12+
data class FindUserIdentityRequest(
13+
@Schema(
14+
description = "User ID (UUID format)",
15+
example = "a1b2c3d4-e5f6-7890-1234-56789abcdef0"
16+
)
17+
@field:NotNull(message = "userId must not be null")
18+
val userId: UUID? = null
19+
) {
20+
fun validUserId(): UUID = userId!!
21+
22+
companion object {
23+
fun from(userIdResponse: UserIdResponse): FindUserIdentityRequest {
24+
return FindUserIdentityRequest(userIdResponse.userId)
25+
}
26+
}
27+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.yapp.apis.auth.dto.request
2+
3+
import io.swagger.v3.oas.annotations.media.Schema
4+
import jakarta.validation.constraints.NotBlank
5+
import org.yapp.apis.auth.dto.response.CreateUserResponse
6+
import org.yapp.apis.auth.dto.response.UserAuthInfoResponse
7+
import org.yapp.globalutils.auth.Role
8+
import java.util.UUID
9+
10+
@Schema(
11+
name = "GenerateTokenPairRequest",
12+
description = "Request DTO to generate a new pair of access and refresh tokens"
13+
)
14+
data class GenerateTokenPairRequest private constructor(
15+
@Schema(
16+
description = "User ID",
17+
example = "a1b2c3d4-e5f6-7890-1234-56789abcdef0"
18+
)
19+
@field:NotBlank(message = "userId must not be null")
20+
val userId: UUID? = null,
21+
22+
@Schema(
23+
description = "User role",
24+
example = "USER"
25+
)
26+
@field:NotBlank(message = "role must not be null")
27+
val role: Role? = null
28+
) {
29+
30+
fun validUserId(): UUID = userId!!
31+
fun validRole(): Role = role!!
32+
33+
companion object {
34+
fun from(response: CreateUserResponse): GenerateTokenPairRequest {
35+
return GenerateTokenPairRequest(
36+
userId = response.id,
37+
role = response.role
38+
)
39+
}
40+
41+
fun from(response: UserAuthInfoResponse): GenerateTokenPairRequest {
42+
return GenerateTokenPairRequest(
43+
userId = response.id,
44+
role = response.role
45+
)
46+
}
47+
}
48+
}

apis/src/main/kotlin/org/yapp/apis/auth/dto/request/SocialLoginRequest.kt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ package org.yapp.apis.auth.dto.request
22

33
import io.swagger.v3.oas.annotations.media.Schema
44
import jakarta.validation.constraints.NotBlank
5-
import org.yapp.apis.auth.dto.AppleAuthCredentials
6-
import org.yapp.apis.auth.dto.AuthCredentials
7-
import org.yapp.apis.auth.dto.KakaoAuthCredentials
5+
import org.yapp.apis.auth.strategy.AppleAuthCredentials
6+
import org.yapp.apis.auth.strategy.AuthCredentials
7+
import org.yapp.apis.auth.strategy.KakaoAuthCredentials
88
import org.yapp.apis.auth.exception.AuthErrorCode
99
import org.yapp.apis.auth.exception.AuthException
1010
import org.yapp.domain.user.ProviderType
@@ -14,11 +14,19 @@ import org.yapp.domain.user.ProviderType
1414
description = "DTO for social login requests"
1515
)
1616
data class SocialLoginRequest private constructor(
17-
@Schema(description = "Type of social login provider", example = "KAKAO", required = true)
17+
@Schema(
18+
description = "Type of social login provider",
19+
example = "KAKAO",
20+
required = true
21+
)
1822
@field:NotBlank(message = "Provider type is required")
1923
val providerType: String? = null,
2024

21-
@Schema(description = "OAuth token issued by the social provider", example = "eyJ...", required = true)
25+
@Schema(
26+
description = "OAuth token issued by the social provider",
27+
example = "eyJ...",
28+
required = true
29+
)
2230
@field:NotBlank(message = "OAuth token is required")
2331
val oauthToken: String? = null
2432
) {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package org.yapp.apis.auth.dto.request
2+
3+
import io.swagger.v3.oas.annotations.media.Schema
4+
import jakarta.validation.constraints.NotBlank
5+
import jakarta.validation.constraints.NotNull
6+
import java.util.*
7+
8+
@Schema(
9+
name = "TokenGenerateRequest",
10+
description = "DTO containing information required to save the generated refresh token"
11+
)
12+
data class TokenGenerateRequest(
13+
@field:NotNull(message = "userId must not be null")
14+
@Schema(description = "User ID", example = "f6b7d490-1b1a-4b9f-8e8e-27f8e3a5dafa")
15+
val userId: UUID? = null,
16+
17+
@field:NotBlank(message = "refreshToken must not be blank")
18+
@Schema(description = "Generated refresh token", example = "eyJhbGciOiJIUzI1NiIsInR...")
19+
val refreshToken: String? = null,
20+
21+
@field:NotNull(message = "expiration must not be null")
22+
@Schema(description = "Refresh token expiration time (in seconds)", example = "2592000")
23+
val expiration: Long? = null
24+
) {
25+
fun validUserId() = userId!!
26+
fun validRefreshToken() = refreshToken!!
27+
fun validExpiration() = expiration!!
28+
29+
companion object {
30+
fun of(userId: UUID, refreshToken: String, expiration: Long): TokenGenerateRequest {
31+
require(refreshToken.isNotBlank()) { "Refresh token must not be blank." }
32+
require(expiration > 0) { "Expiration must be greater than 0." }
33+
34+
return TokenGenerateRequest(
35+
userId = userId,
36+
refreshToken = refreshToken,
37+
expiration = expiration
38+
)
39+
}
40+
}
41+
}

apis/src/main/kotlin/org/yapp/apis/auth/dto/request/TokenRefreshRequest.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,12 @@ import jakarta.validation.constraints.NotBlank
88
description = "DTO for requesting an access token using a refresh token"
99
)
1010
data class TokenRefreshRequest private constructor(
11-
12-
@field:NotBlank(message = "Refresh token is required")
1311
@Schema(
1412
description = "Valid refresh token issued during previous authentication",
1513
example = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
1614
required = true
1715
)
16+
@field:NotBlank(message = "Refresh token is required")
1817
val refreshToken: String? = null
1918
) {
2019
fun validRefreshToken(): String = refreshToken!!

0 commit comments

Comments
 (0)