Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0c66457
[BOOK-96] feat: infra - UserBookEntity 구현 (#29)
minwoo1999 Jul 7, 2025
e36fcf9
[BOOK-96] feat: infra - UserBookRepository 구현 (#29)
minwoo1999 Jul 7, 2025
374356d
[BOOK-96] refactor: infra - UserBookEntity 수정 (#29)
minwoo1999 Jul 7, 2025
7be19e0
[BOOK-96] feat: domain - book,userbook domainservice 분리 (#29)
minwoo1999 Jul 7, 2025
8ac5039
[BOOK-96] feat: domain - UserBook Domain 모델 구현 (#29)
minwoo1999 Jul 7, 2025
d0722ff
[BOOK-96] refactor: domain - Book Domain 모델 수정 (#29)
minwoo1999 Jul 7, 2025
a0882aa
[BOOK-96] feat: apis - 도서등록,내서재 Usecase 구현 (#29)
minwoo1999 Jul 7, 2025
7241fdb
[BOOK-96] feat: apis - Book Exception 정의 (#29)
minwoo1999 Jul 7, 2025
e63d655
[BOOK-96] feat: apis - UserBook Dto 구현 (#29)
minwoo1999 Jul 7, 2025
cbced98
[BOOK-96] refactor: apis - BookDetail DTO 수정 (#29)
minwoo1999 Jul 7, 2025
5cc8b78
[BOOK-96] feat: apis - Controller 도서등록, 내서재 구현 (#29)
minwoo1999 Jul 7, 2025
352a221
[BOOK-96] chore: infra,domain,apis import * 제거
minwoo1999 Jul 10, 2025
d635d9f
[BOOK-96] refator: infra - 상태 변경이 필요한 필드를 var + protected set (#29)
minwoo1999 Jul 10, 2025
1077b85
[BOOK-96] feat: domain - userBook 도메인계층 기능개발 (#29)
minwoo1999 Jul 10, 2025
ea6f796
[BOOK-96] refactor: apis - service계층 분리 (#29)
minwoo1999 Jul 10, 2025
e744548
[BOOK-96] feat: apis - dto 정의 (#29)
minwoo1999 Jul 10, 2025
8772208
[BOOK-96] refactor: apis - usecase service 분리 (#29)
minwoo1999 Jul 10, 2025
d70529f
[BOOK-96] refactor: apis -  AuthenticationPrincipal를 통해 userId 주입 …
minwoo1999 Jul 10, 2025
31b643c
[BOOK-96] refactor: apis - api swagger 명세서 response 수정
minwoo1999 Jul 10, 2025
3be48ab
[BOOK-96] refactor: apis,domain,infra - user validate 메소드 별도로 분리
minwoo1999 Jul 11, 2025
753d309
[BOOK-96] chore: apis - 불필요한 코드 제거 (#29)
minwoo1999 Jul 11, 2025
2931e91
[BOOK-96] refactor: apis,domain - 패키지구조 변경 (#29)
minwoo1999 Jul 12, 2025
e66845a
[BOOK-96] refactor: apis- 패키지구조 변경 (#29)
minwoo1999 Jul 12, 2025
91d69cf
[BOOK-96] refactor: apis- 외부 API 인터페이스 추상화 (#29)
minwoo1999 Jul 12, 2025
3911657
[BOOK-96] refactor: apis- book도메인 객체 domainservice 내부에서 생성 (#29)
minwoo1999 Jul 12, 2025
c1cfc11
[BOOK-96] refactor: apis- BookQueryServiceQualifier 상수처리 (#29)
minwoo1999 Jul 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package org.yapp.apis.auth.controller

import jakarta.validation.Valid
import org.springframework.http.ResponseEntity
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.*
import org.yapp.apis.auth.dto.request.SocialLoginRequest
import org.yapp.apis.auth.dto.request.TokenRefreshRequest
import org.yapp.apis.auth.dto.response.AuthResponse
import org.yapp.apis.auth.dto.response.UserProfileResponse
import org.yapp.apis.auth.usecase.AuthUseCase
import org.yapp.apis.util.AuthUtils
import java.util.*

/**
* Implementation of the authentication controller API.
Expand All @@ -33,15 +34,13 @@ class AuthController(
}

@PostMapping("/signout")
override fun signOut(@RequestHeader("Authorization") authorization: String): ResponseEntity<Unit> {
val userId = AuthUtils.extractUserIdFromAuthHeader(authorization, authUseCase::getUserIdFromAccessToken)
override fun signOut(@AuthenticationPrincipal userId: UUID): ResponseEntity<Unit> {
authUseCase.signOut(userId)
return ResponseEntity.noContent().build()
}

@GetMapping("/me")
override fun getUserProfile(@RequestHeader("Authorization") authorization: String): ResponseEntity<UserProfileResponse> {
val userId = AuthUtils.extractUserIdFromAuthHeader(authorization, authUseCase::getUserIdFromAccessToken)
override fun getUserProfile(@AuthenticationPrincipal userId: UUID): ResponseEntity<UserProfileResponse> {
val userProfile = authUseCase.getUserProfile(userId)
return ResponseEntity.ok(userProfile)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.validation.Valid
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.yapp.apis.auth.dto.request.SocialLoginRequest
import org.yapp.apis.auth.dto.request.TokenRefreshRequest
import org.yapp.apis.auth.dto.response.AuthResponse
import org.yapp.apis.auth.dto.response.UserProfileResponse
import org.yapp.globalutils.exception.ErrorResponse
import java.util.UUID

/**
* API interface for authentication controller.
*/
@Tag(name = "Authentication", description = "Authentication API")
@RequestMapping("/api/v1/auth")
interface AuthControllerApi {
Expand All @@ -34,11 +37,13 @@ interface AuthControllerApi {
),
ApiResponse(
responseCode = "400",
description = "Invalid request or credentials"
description = "Invalid request or credentials",
content = [Content(schema = Schema(implementation = ErrorResponse::class))]
),
ApiResponse(
responseCode = "409",
description = "Email already in use with a different account"
description = "Email already in use with a different account",
content = [Content(schema = Schema(implementation = ErrorResponse::class))]
)
]
)
Expand All @@ -47,7 +52,7 @@ interface AuthControllerApi {

@Operation(
summary = "Refresh token",
description = "Refresh an access token using a refresh token. Returns both a new access token and a new refresh token. The client MUST use the new refresh token for subsequent refreshes, as the old refresh token is deleted from the server."
description = "Refresh an access token using a refresh token. Returns both a new access token and a new refresh token."
)
@ApiResponses(
value = [
Expand All @@ -58,41 +63,34 @@ interface AuthControllerApi {
),
ApiResponse(
responseCode = "400",
description = "Invalid refresh token"
description = "Invalid refresh token",
content = [Content(schema = Schema(implementation = ErrorResponse::class))]
),
ApiResponse(
responseCode = "404",
description = "Refresh token not found"
description = "Refresh token not found",
content = [Content(schema = Schema(implementation = ErrorResponse::class))]
)
]
)
@PostMapping("/refresh")
fun refreshToken(@RequestBody @Valid request: TokenRefreshRequest): ResponseEntity<AuthResponse>

@Operation(
summary = "Sign out",
description = "Sign out a user by invalidating their refresh token"
)
@Operation(summary = "Sign out", description = "Sign out a user by invalidating their refresh token")
@ApiResponses(
value = [
ApiResponse(
responseCode = "204",
description = "Successful sign out"
),
ApiResponse(responseCode = "204", description = "Successful sign out"),
ApiResponse(
responseCode = "400",
description = "Invalid user ID"
description = "Invalid user ID",
content = [Content(schema = Schema(implementation = ErrorResponse::class))]
)
]
)
@PostMapping("/signout")
fun signOut(@RequestHeader("Authorization") authorization: String): ResponseEntity<Unit>
fun signOut(@AuthenticationPrincipal userId: UUID): ResponseEntity<Unit>


@Operation(
summary = "Get user profile",
description = "Retrieves profile information for the given user ID."
)
@Operation(summary = "Get user profile", description = "Retrieves profile information for the given user ID.")
@ApiResponses(
value = [
ApiResponse(
Expand All @@ -102,12 +100,11 @@ interface AuthControllerApi {
),
ApiResponse(
responseCode = "404",
description = "User not found"
description = "User not found",
content = [Content(schema = Schema(implementation = ErrorResponse::class))]
)
]
)
@GetMapping("/me")
fun getUserProfile(
@RequestHeader("Authorization") authorization: String
): ResponseEntity<UserProfileResponse>
fun getUserProfile(@AuthenticationPrincipal userId: UUID): ResponseEntity<UserProfileResponse>
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.yapp.apis.auth.dto

import org.yapp.domain.auth.ProviderType
import org.yapp.domain.user.ProviderType

sealed class AuthCredentials {
abstract fun getProviderType(): ProviderType
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.yapp.apis.auth.dto

import org.yapp.domain.auth.ProviderType
import org.yapp.domain.user.ProviderType

data class UserCreateInfo private constructor(
val email: String?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import org.yapp.apis.auth.dto.AuthCredentials
import org.yapp.apis.auth.dto.KakaoAuthCredentials
import org.yapp.apis.auth.exception.AuthErrorCode
import org.yapp.apis.auth.exception.AuthException
import org.yapp.domain.auth.ProviderType
import org.yapp.domain.user.ProviderType

@Schema(
name = "SocialLoginRequest",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.yapp.apis.auth.dto.response

import io.swagger.v3.oas.annotations.media.Schema
import org.yapp.domain.auth.ProviderType
import org.yapp.domain.user.ProviderType
import java.util.*

@Schema(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.yapp.apis.auth.service
import org.springframework.stereotype.Service
import org.yapp.apis.auth.exception.AuthErrorCode
import org.yapp.apis.auth.exception.AuthException
import org.yapp.domain.service.redis.TokenDomainRedisService
import org.yapp.domain.token.TokenDomainRedisService
import org.yapp.domain.token.RefreshToken
import java.util.*

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import org.yapp.apis.auth.dto.UserCreateInfo
import org.yapp.apis.auth.exception.AuthErrorCode
import org.yapp.apis.auth.exception.AuthException
import org.yapp.apis.util.NicknameGenerator
import org.yapp.domain.service.domain.UserDomainService
import org.yapp.domain.user.UserDomainService
import org.yapp.domain.user.User
import org.yapp.domain.user.vo.SocialUserProfile
import java.util.*
Expand Down Expand Up @@ -56,4 +56,10 @@ class UserAuthService(

return userDomainService.create(profile)
}

fun validateUserExists(userId: UUID) {
if (!userDomainService.existsById(userId)) {
throw AuthException(AuthErrorCode.USER_NOT_FOUND, "User not found: $userId")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.yapp.apis.auth.exception.AuthErrorCode
import org.yapp.apis.auth.exception.AuthException
import org.yapp.apis.auth.helper.AppleJwtHelper
import org.yapp.apis.util.NicknameGenerator
import org.yapp.domain.auth.ProviderType
import org.yapp.domain.user.ProviderType

/**
* Implementation of AuthStrategy for Apple authentication.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package org.yapp.apis.auth.strategy

import org.yapp.apis.auth.dto.AuthCredentials
import org.yapp.apis.auth.dto.UserCreateInfo
import org.yapp.domain.auth.ProviderType
import org.yapp.domain.user.ProviderType

/**
* Strategy interface for authentication.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.yapp.apis.auth.exception.AuthErrorCode
import org.yapp.apis.auth.exception.AuthException
import org.yapp.apis.auth.helper.KakaoApiHelper
import org.yapp.apis.util.NicknameGenerator
import org.yapp.domain.auth.ProviderType
import org.yapp.domain.user.ProviderType
import org.yapp.infra.external.oauth.kakao.response.KakaoUserInfo

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,4 @@ class AuthUseCase(
provider = user.providerType
)
}

@Transactional(propagation = Propagation.NOT_SUPPORTED)
fun getUserIdFromAccessToken(accessToken: String): UUID {
return authTokenHelper.getUserIdFromAccessToken(accessToken)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.yapp.apis.book.constant

object BookQueryServiceQualifier {
const val ALADIN = "aladinBookQueryService"
}
Comment on lines +3 to +5
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

문자열 Qualifier 대신 전용 애노테이션 고려

상수 문자열로 Qualifier 를 관리하면 오타로 인한 런타임 오류 가능성이 남아 있습니다.
전용 애노테이션을 정의하면 컴파일타임 안전성을 확보할 수 있으니 검토해 보세요.

-package org.yapp.apis.book.constant
-
-object BookQueryServiceQualifier {
-    const val ALADIN = "aladinBookQueryService"
-}
+package org.yapp.apis.book.constant
+
+import org.springframework.beans.factory.annotation.Qualifier
+
+@Qualifier
+@Target(
+    AnnotationTarget.FUNCTION,
+    AnnotationTarget.CLASS,
+    AnnotationTarget.VALUE_PARAMETER,
+    AnnotationTarget.FIELD
+)
+@Retention(AnnotationRetention.RUNTIME)
+annotation class AladinBookQuery

서비스/주입부에서는 @AladinBookQuery 로 대체하면 됩니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
object BookQueryServiceQualifier {
const val ALADIN = "aladinBookQueryService"
}
package org.yapp.apis.book.constant
import org.springframework.beans.factory.annotation.Qualifier
@Qualifier
@Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.CLASS,
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.FIELD
)
@Retention(AnnotationRetention.RUNTIME)
annotation class AladinBookQuery
🤖 Prompt for AI Agents
In apis/src/main/kotlin/org/yapp/apis/book/constant/BookQueryServiceQualifier.kt
around lines 3 to 5, replace the string constant qualifier with a dedicated
annotation to improve compile-time safety and avoid runtime errors from typos.
Define a new annotation class named AladinBookQuery and use it as a qualifier in
service and injection points instead of the string "aladinBookQueryService".

Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,26 @@ package org.yapp.apis.book.controller

import jakarta.validation.Valid
import org.springframework.http.ResponseEntity
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ModelAttribute
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.yapp.apis.book.dto.request.BookDetailRequest
import org.yapp.apis.book.dto.request.BookSearchRequest
import org.yapp.apis.book.dto.request.UserBookRegisterRequest
import org.yapp.apis.book.dto.response.BookDetailResponse
import org.yapp.apis.book.dto.response.BookSearchResponse
import org.yapp.apis.book.dto.response.UserBookResponse
import org.yapp.apis.book.usecase.BookUseCase

import java.util.UUID

@RestController
@RequestMapping("/api/v1/books")
class BookController(
private val bookUseCase: BookUseCase
private val bookUseCase: BookUseCase,
) : BookControllerApi {

@GetMapping("/search")
Expand All @@ -32,4 +37,22 @@ class BookController(
val response = bookUseCase.getBookDetail(request)
return ResponseEntity.ok(response)
}

@PutMapping("/upsert")
override fun upsertBookToMyLibrary(
@AuthenticationPrincipal userId: UUID,
@Valid @RequestBody request: UserBookRegisterRequest
): ResponseEntity<UserBookResponse> {
val response = bookUseCase.upsertBookToMyLibrary(userId, request)
return ResponseEntity.ok(response)
}

@GetMapping("/my-library")
override fun getUserLibraryBooks(
@AuthenticationPrincipal userId: UUID
): ResponseEntity<List<UserBookResponse>> {

val response = bookUseCase.getUserLibraryBooks(userId)
return ResponseEntity.ok(response)
}
}
Loading