From b69b87c408c53f8f92ffbb8f64d8fd56794e74fa Mon Sep 17 00:00:00 2001 From: hyxklee Date: Tue, 31 Mar 2026 12:27:58 +0900 Subject: [PATCH 1/6] =?UTF-8?q?fix:=20=EC=95=A0=ED=94=8C=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/command/SocialLoginUseCase.kt | 32 ++++- .../domain/user/domain/port/SocialAuthPort.kt | 3 + .../infrastructure/AppleSocialAuthAdapter.kt | 18 ++- .../presentation/AppleCallbackController.kt | 114 ++++++++++++++++++ .../com/weeth/global/config/SecurityConfig.kt | 2 + .../config/properties/OAuthProperties.kt | 2 + src/main/resources/application.yml | 1 + 7 files changed, 163 insertions(+), 9 deletions(-) create mode 100644 src/main/kotlin/com/weeth/domain/user/presentation/AppleCallbackController.kt diff --git a/src/main/kotlin/com/weeth/domain/user/application/usecase/command/SocialLoginUseCase.kt b/src/main/kotlin/com/weeth/domain/user/application/usecase/command/SocialLoginUseCase.kt index c16ce401..9d695c8d 100644 --- a/src/main/kotlin/com/weeth/domain/user/application/usecase/command/SocialLoginUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/user/application/usecase/command/SocialLoginUseCase.kt @@ -34,18 +34,44 @@ class SocialLoginUseCase( fun socialLoginByApple(request: SocialLoginRequest): SocialLoginResponse = socialLogin(SocialProvider.APPLE, request) + /** + * Apple form_post 콜백 전용 로그인. + * id_token을 직접 검증하여 code 교환 과정을 생략하고, + * Apple이 최초 인가 시에만 전달하는 user JSON의 이름을 반영한다. + * + * TODO: 탈퇴 기능 구현 시 Apple 계정 연결 해제(revoke)를 위해 + * 콜백의 code를 Apple 토큰 엔드포인트에 교환하여 refresh token을 받고 DB에 저장해야 한다. + * (Apple Revoke Tokens API: POST https://appleid.apple.com/auth/revoke) + */ + @Transactional + fun socialLoginByAppleCallback( + idToken: String, + userName: String?, + ): SocialLoginResponse { + val authResult = socialAuthPortRegistry.get(SocialProvider.APPLE).authenticateWithIdToken(idToken) + val effectiveResult = + if (!userName.isNullOrBlank() && authResult.name.isNullOrBlank()) { + authResult.copy(name = userName) + } else { + authResult + } + return processLogin(effectiveResult) + } + private fun socialLogin( provider: SocialProvider, request: SocialLoginRequest, - ): SocialLoginResponse { - val user = findOrCreateUser(authResult = socialAuthPortRegistry.get(provider).authenticate(request.authCode)) + ): SocialLoginResponse = processLogin(socialAuthPortRegistry.get(provider).authenticate(request.authCode)) + + private fun processLogin(authResult: SocialAuthResult): SocialLoginResponse { + val user = findOrCreateUser(authResult) if (user.isBannedOrLeft()) throw UserInActiveException() val tokenType = if (user.isRegistered()) TokenType.ACCESS else TokenType.TEMPORARY val token = jwtManageUseCase.create(user.id, user.emailValue, tokenType) - return userMapper.toSocialLoginResponse(token, user.isRegistered()) + return userMapper.toSocialLoginResponse(user.name, token, user.isRegistered()) } // TODO: 실제 서비스 출시 시 이메일 기반 기존 사용자 연동 및 유저 알림 기능 필요 diff --git a/src/main/kotlin/com/weeth/domain/user/domain/port/SocialAuthPort.kt b/src/main/kotlin/com/weeth/domain/user/domain/port/SocialAuthPort.kt index 40cc5b54..9e0cf052 100644 --- a/src/main/kotlin/com/weeth/domain/user/domain/port/SocialAuthPort.kt +++ b/src/main/kotlin/com/weeth/domain/user/domain/port/SocialAuthPort.kt @@ -7,4 +7,7 @@ interface SocialAuthPort { fun provider(): SocialProvider fun authenticate(authCode: String): SocialAuthResult + + fun authenticateWithIdToken(idToken: String): SocialAuthResult = + throw UnsupportedOperationException("${provider()}은(는) ID token 직접 인증을 지원하지 않습니다") } diff --git a/src/main/kotlin/com/weeth/domain/user/infrastructure/AppleSocialAuthAdapter.kt b/src/main/kotlin/com/weeth/domain/user/infrastructure/AppleSocialAuthAdapter.kt index c2f416b5..318bbfde 100644 --- a/src/main/kotlin/com/weeth/domain/user/infrastructure/AppleSocialAuthAdapter.kt +++ b/src/main/kotlin/com/weeth/domain/user/infrastructure/AppleSocialAuthAdapter.kt @@ -4,6 +4,7 @@ import com.weeth.domain.user.domain.enums.SocialProvider import com.weeth.domain.user.domain.port.SocialAuthPort import com.weeth.domain.user.domain.vo.SocialAuthResult import com.weeth.global.auth.apple.AppleAuthService +import com.weeth.global.auth.apple.dto.AppleUserInfo import org.springframework.stereotype.Component @Component @@ -15,15 +16,20 @@ class AppleSocialAuthAdapter( override fun authenticate(authCode: String): SocialAuthResult { val appleToken = appleAuthService.getAppleToken(authCode) val userInfo = appleAuthService.verifyAndDecodeIdToken(appleToken.idToken) - val email = userInfo.email?.trim()?.lowercase() ?: "" - val providerName = userInfo.name?.trim()?.takeIf { it.isNotBlank() } + return toSocialAuthResult(userInfo) + } + + override fun authenticateWithIdToken(idToken: String): SocialAuthResult { + val userInfo = appleAuthService.verifyAndDecodeIdToken(idToken) + return toSocialAuthResult(userInfo) + } - return SocialAuthResult( + private fun toSocialAuthResult(userInfo: AppleUserInfo): SocialAuthResult = + SocialAuthResult( provider = SocialProvider.APPLE, providerUserId = userInfo.appleId, - email = email, + email = userInfo.email?.trim()?.lowercase() ?: "", emailVerified = userInfo.emailVerified, - name = providerName, + name = userInfo.name?.trim()?.takeIf { it.isNotBlank() }, ) - } } diff --git a/src/main/kotlin/com/weeth/domain/user/presentation/AppleCallbackController.kt b/src/main/kotlin/com/weeth/domain/user/presentation/AppleCallbackController.kt new file mode 100644 index 00000000..0d224d3e --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/user/presentation/AppleCallbackController.kt @@ -0,0 +1,114 @@ +package com.weeth.domain.user.presentation + +import com.fasterxml.jackson.databind.ObjectMapper +import com.weeth.domain.user.application.usecase.command.SocialLoginUseCase +import com.weeth.global.auth.jwt.application.service.TokenCookieProvider +import com.weeth.global.config.properties.OAuthProperties +import io.swagger.v3.oas.annotations.Hidden +import org.slf4j.LoggerFactory +import org.springframework.http.HttpHeaders +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController +import org.springframework.web.util.UriComponentsBuilder + +/** + * Apple Sign in with Apple의 form_post 콜백을 처리하는 컨트롤러. + * + * Apple 인가 요청 예시: + * https://appleid.apple.com/auth/authorize + * ?response_type=code id_token + * &response_mode=form_post + * &client_id=com.weeth.web + * &redirect_uri={서버 콜백 URL} + * &scope=name email + * + * Apple이 redirect_uri로 POST (application/x-www-form-urlencoded) 요청을 보내며, + * id_token을 직접 검증(code 교환 불필요)하고 user JSON에서 이름을 추출한 뒤 + * 프론트엔드로 리다이렉트한다. + */ +@Hidden +@RestController +class AppleCallbackController( + private val socialLoginUseCase: SocialLoginUseCase, + private val objectMapper: ObjectMapper, + private val tokenCookieProvider: TokenCookieProvider, + oAuthProperties: OAuthProperties, +) { + private val log = LoggerFactory.getLogger(javaClass) + private val frontendRedirectUri = oAuthProperties.apple.frontendRedirectUri + + @PostMapping( + "/api/v4/users/social/apple/callback", + consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], + ) + fun handleCallback( + @RequestParam("id_token", required = false) idToken: String?, + @RequestParam("user", required = false) userJson: String?, + @RequestParam("error", required = false) error: String?, + ): ResponseEntity { + if (error != null || idToken.isNullOrBlank()) { + log.warn("Apple 콜백 오류: error={}", error) + return redirect( + UriComponentsBuilder + .fromUriString(frontendRedirectUri) + .queryParam("error", error ?: "unknown") + .toUriString(), + ) + } + + return try { + val userName = parseAppleUserName(userJson) + val response = socialLoginUseCase.socialLoginByAppleCallback(idToken, userName) + + val redirectUri = + UriComponentsBuilder + .fromUriString(frontendRedirectUri) + .queryParam("registered", response.registered) + .queryParam("name", response.name) + .toUriString() + + ResponseEntity + .status(HttpStatus.FOUND) + .header(HttpHeaders.LOCATION, redirectUri) + .header( + HttpHeaders.SET_COOKIE, + tokenCookieProvider.createAccessTokenCookie(response.accessToken).toString(), + ).header( + HttpHeaders.SET_COOKIE, + tokenCookieProvider.createRefreshTokenCookie(response.refreshToken).toString(), + ).build() + } catch (e: Exception) { + log.error("Apple 콜백 처리 중 오류 발생", e) + redirect( + UriComponentsBuilder + .fromUriString(frontendRedirectUri) + .queryParam("error", "login_failed") + .toUriString(), + ) + } + } + + private fun redirect(uri: String): ResponseEntity = + ResponseEntity + .status(HttpStatus.FOUND) + .header(HttpHeaders.LOCATION, uri) + .build() + + private fun parseAppleUserName(userJson: String?): String? { + if (userJson.isNullOrBlank()) return null + return try { + val node = objectMapper.readTree(userJson) + val nameNode = node["name"] ?: return null + val firstName = nameNode["firstName"]?.asText()?.trim() ?: "" + val lastName = nameNode["lastName"]?.asText()?.trim() ?: "" + "$lastName$firstName".trim().takeIf { it.isNotBlank() } + } catch (e: Exception) { + log.warn("Apple user JSON 파싱 실패: {}", e.message) + null + } + } +} diff --git a/src/main/kotlin/com/weeth/global/config/SecurityConfig.kt b/src/main/kotlin/com/weeth/global/config/SecurityConfig.kt index d1c02a2b..c6aaff46 100644 --- a/src/main/kotlin/com/weeth/global/config/SecurityConfig.kt +++ b/src/main/kotlin/com/weeth/global/config/SecurityConfig.kt @@ -44,6 +44,7 @@ class SecurityConfig( .requestMatchers( "/api/v4/users/social/kakao", "/api/v4/users/social/apple", + "/api/v4/users/social/apple/callback", "/api/v4/users/social/refresh", ).permitAll() .requestMatchers("/health-check") @@ -87,6 +88,7 @@ class SecurityConfig( "http://127.0.0.1:*", "https://13.124.170.169.nip.io", "https://develop.d2o3vlabneheuu.amplifyapp.com", + "https://appleid.apple.com", ) allowedMethods = listOf("GET", "POST", "PATCH", "DELETE", "OPTIONS") allowedHeaders = listOf("*") diff --git a/src/main/kotlin/com/weeth/global/config/properties/OAuthProperties.kt b/src/main/kotlin/com/weeth/global/config/properties/OAuthProperties.kt index 9d845342..d96ea1c4 100644 --- a/src/main/kotlin/com/weeth/global/config/properties/OAuthProperties.kt +++ b/src/main/kotlin/com/weeth/global/config/properties/OAuthProperties.kt @@ -40,5 +40,7 @@ data class OAuthProperties( val keysUri: String, @field:NotBlank val privateKeyPath: String, + @field:NotBlank + val frontendRedirectUri: String, ) } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 535ad46d..ee058161 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -28,6 +28,7 @@ auth: token_uri: https://appleid.apple.com/auth/token keys_uri: https://appleid.apple.com/auth/keys private_key_path: ${APPLE_PRIVATE_KEY_PATH} + frontend_redirect_uri: ${APPLE_FRONTEND_REDIRECT_URI} management: endpoints: From df403ce6a167855787eec10dca1c8a91b4017db4 Mon Sep 17 00:00:00 2001 From: hyxklee Date: Tue, 31 Mar 2026 12:28:10 +0900 Subject: [PATCH 2/6] =?UTF-8?q?refactor:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=8B=9C=20=EC=9C=A0=EC=A0=80=20=EC=9D=B4=EB=A6=84=20=EB=B0=98?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/application/dto/response/SocialLoginResponse.kt | 2 ++ .../com/weeth/domain/user/application/mapper/UserMapper.kt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/main/kotlin/com/weeth/domain/user/application/dto/response/SocialLoginResponse.kt b/src/main/kotlin/com/weeth/domain/user/application/dto/response/SocialLoginResponse.kt index 43fcbc3b..91c29170 100644 --- a/src/main/kotlin/com/weeth/domain/user/application/dto/response/SocialLoginResponse.kt +++ b/src/main/kotlin/com/weeth/domain/user/application/dto/response/SocialLoginResponse.kt @@ -3,6 +3,8 @@ package com.weeth.domain.user.application.dto.response import io.swagger.v3.oas.annotations.media.Schema data class SocialLoginResponse( + @field:Schema(description = "사용자 이름") + val name: String, @field:Schema(description = "액세스 토큰") val accessToken: String, @field:Schema(description = "리프레시 토큰") diff --git a/src/main/kotlin/com/weeth/domain/user/application/mapper/UserMapper.kt b/src/main/kotlin/com/weeth/domain/user/application/mapper/UserMapper.kt index deee9cc8..4a317510 100644 --- a/src/main/kotlin/com/weeth/domain/user/application/mapper/UserMapper.kt +++ b/src/main/kotlin/com/weeth/domain/user/application/mapper/UserMapper.kt @@ -10,10 +10,12 @@ class UserMapper( private val fileAccessUrlPort: FileAccessUrlPort, ) { fun toSocialLoginResponse( + userName: String, token: JwtDto, registered: Boolean, ): SocialLoginResponse = SocialLoginResponse( + name = userName, accessToken = token.accessToken, refreshToken = token.refreshToken, registered = registered, From c0419edb5fa3124977633730e7aa250a8e48f9d6 Mon Sep 17 00:00:00 2001 From: hyxklee Date: Tue, 31 Mar 2026 12:33:35 +0900 Subject: [PATCH 3/6] =?UTF-8?q?refactor:=20=EC=A3=BC=EC=84=9D=20=EB=B0=8F?= =?UTF-8?q?=20=ED=8C=8C=EC=9D=BC=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ckController.kt => SocialCallbackController.kt} | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) rename src/main/kotlin/com/weeth/domain/user/presentation/{AppleCallbackController.kt => SocialCallbackController.kt} (89%) diff --git a/src/main/kotlin/com/weeth/domain/user/presentation/AppleCallbackController.kt b/src/main/kotlin/com/weeth/domain/user/presentation/SocialCallbackController.kt similarity index 89% rename from src/main/kotlin/com/weeth/domain/user/presentation/AppleCallbackController.kt rename to src/main/kotlin/com/weeth/domain/user/presentation/SocialCallbackController.kt index 0d224d3e..c6caf139 100644 --- a/src/main/kotlin/com/weeth/domain/user/presentation/AppleCallbackController.kt +++ b/src/main/kotlin/com/weeth/domain/user/presentation/SocialCallbackController.kt @@ -17,22 +17,10 @@ import org.springframework.web.util.UriComponentsBuilder /** * Apple Sign in with Apple의 form_post 콜백을 처리하는 컨트롤러. - * - * Apple 인가 요청 예시: - * https://appleid.apple.com/auth/authorize - * ?response_type=code id_token - * &response_mode=form_post - * &client_id=com.weeth.web - * &redirect_uri={서버 콜백 URL} - * &scope=name email - * - * Apple이 redirect_uri로 POST (application/x-www-form-urlencoded) 요청을 보내며, - * id_token을 직접 검증(code 교환 불필요)하고 user JSON에서 이름을 추출한 뒤 - * 프론트엔드로 리다이렉트한다. */ @Hidden @RestController -class AppleCallbackController( +class SocialCallbackController( private val socialLoginUseCase: SocialLoginUseCase, private val objectMapper: ObjectMapper, private val tokenCookieProvider: TokenCookieProvider, From 2272572b376250e340ef9e1c8395c424b01459e4 Mon Sep 17 00:00:00 2001 From: hyxklee Date: Tue, 31 Mar 2026 15:58:56 +0900 Subject: [PATCH 4/6] =?UTF-8?q?refactor:=20=ED=8C=8C=EC=8B=B1=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/command/SocialLoginUseCase.kt | 22 ++++++++++++++++++- .../presentation/SocialCallbackController.kt | 20 +---------------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/user/application/usecase/command/SocialLoginUseCase.kt b/src/main/kotlin/com/weeth/domain/user/application/usecase/command/SocialLoginUseCase.kt index 9d695c8d..deddd8e3 100644 --- a/src/main/kotlin/com/weeth/domain/user/application/usecase/command/SocialLoginUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/user/application/usecase/command/SocialLoginUseCase.kt @@ -1,5 +1,6 @@ package com.weeth.domain.user.application.usecase.command +import com.fasterxml.jackson.databind.ObjectMapper import com.weeth.domain.user.application.dto.request.SocialLoginRequest import com.weeth.domain.user.application.dto.response.SocialLoginResponse import com.weeth.domain.user.application.exception.EmailNotFoundException @@ -15,6 +16,7 @@ import com.weeth.domain.user.domain.vo.SocialAuthResult import com.weeth.domain.user.infrastructure.SocialAuthPortRegistry import com.weeth.global.auth.jwt.application.usecase.JwtManageUseCase import com.weeth.global.auth.jwt.domain.enums.TokenType +import org.slf4j.LoggerFactory import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -25,7 +27,10 @@ class SocialLoginUseCase( private val socialAuthPortRegistry: SocialAuthPortRegistry, private val jwtManageUseCase: JwtManageUseCase, private val userMapper: UserMapper, + private val objectMapper: ObjectMapper, ) { + private val log = LoggerFactory.getLogger(javaClass) + @Transactional fun socialLoginByKakao(request: SocialLoginRequest): SocialLoginResponse = socialLogin(SocialProvider.KAKAO, request) @@ -46,9 +51,10 @@ class SocialLoginUseCase( @Transactional fun socialLoginByAppleCallback( idToken: String, - userName: String?, + userJson: String?, ): SocialLoginResponse { val authResult = socialAuthPortRegistry.get(SocialProvider.APPLE).authenticateWithIdToken(idToken) + val userName = parseAppleUserName(userJson) val effectiveResult = if (!userName.isNullOrBlank() && authResult.name.isNullOrBlank()) { authResult.copy(name = userName) @@ -105,4 +111,18 @@ class SocialLoginUseCase( return user } + + private fun parseAppleUserName(userJson: String?): String? { + if (userJson.isNullOrBlank()) return null + return try { + val node = objectMapper.readTree(userJson) + val nameNode = node["name"] ?: return null + val firstName = nameNode["firstName"]?.asText()?.trim() ?: "" + val lastName = nameNode["lastName"]?.asText()?.trim() ?: "" + "$lastName$firstName".trim().takeIf { it.isNotBlank() } + } catch (e: Exception) { + log.warn("Apple user JSON 파싱 실패: {}", e.message) + null + } + } } diff --git a/src/main/kotlin/com/weeth/domain/user/presentation/SocialCallbackController.kt b/src/main/kotlin/com/weeth/domain/user/presentation/SocialCallbackController.kt index c6caf139..4f80505c 100644 --- a/src/main/kotlin/com/weeth/domain/user/presentation/SocialCallbackController.kt +++ b/src/main/kotlin/com/weeth/domain/user/presentation/SocialCallbackController.kt @@ -1,6 +1,5 @@ package com.weeth.domain.user.presentation -import com.fasterxml.jackson.databind.ObjectMapper import com.weeth.domain.user.application.usecase.command.SocialLoginUseCase import com.weeth.global.auth.jwt.application.service.TokenCookieProvider import com.weeth.global.config.properties.OAuthProperties @@ -22,7 +21,6 @@ import org.springframework.web.util.UriComponentsBuilder @RestController class SocialCallbackController( private val socialLoginUseCase: SocialLoginUseCase, - private val objectMapper: ObjectMapper, private val tokenCookieProvider: TokenCookieProvider, oAuthProperties: OAuthProperties, ) { @@ -39,7 +37,6 @@ class SocialCallbackController( @RequestParam("error", required = false) error: String?, ): ResponseEntity { if (error != null || idToken.isNullOrBlank()) { - log.warn("Apple 콜백 오류: error={}", error) return redirect( UriComponentsBuilder .fromUriString(frontendRedirectUri) @@ -49,8 +46,7 @@ class SocialCallbackController( } return try { - val userName = parseAppleUserName(userJson) - val response = socialLoginUseCase.socialLoginByAppleCallback(idToken, userName) + val response = socialLoginUseCase.socialLoginByAppleCallback(idToken, userJson) val redirectUri = UriComponentsBuilder @@ -85,18 +81,4 @@ class SocialCallbackController( .status(HttpStatus.FOUND) .header(HttpHeaders.LOCATION, uri) .build() - - private fun parseAppleUserName(userJson: String?): String? { - if (userJson.isNullOrBlank()) return null - return try { - val node = objectMapper.readTree(userJson) - val nameNode = node["name"] ?: return null - val firstName = nameNode["firstName"]?.asText()?.trim() ?: "" - val lastName = nameNode["lastName"]?.asText()?.trim() ?: "" - "$lastName$firstName".trim().takeIf { it.isNotBlank() } - } catch (e: Exception) { - log.warn("Apple user JSON 파싱 실패: {}", e.message) - null - } - } } From c780891d3a457a701f55d98840afc97932737bce Mon Sep 17 00:00:00 2001 From: hyxklee Date: Tue, 31 Mar 2026 16:39:38 +0900 Subject: [PATCH 5/6] =?UTF-8?q?refactor:=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/account/presentation/AccountAdminController.kt | 2 +- .../domain/account/presentation/AccountController.kt | 2 +- .../domain/account/presentation/ReceiptAdminController.kt | 6 +++--- .../attendance/presentation/AttendanceController.kt | 4 ++-- .../weeth/domain/club/presentation/ClubAdminController.kt | 2 +- .../domain/penalty/presentation/PenaltyAdminController.kt | 8 ++++---- .../domain/penalty/presentation/PenaltyUserController.kt | 2 +- .../global/common/controller/ExceptionDocController.kt | 1 - 8 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/account/presentation/AccountAdminController.kt b/src/main/kotlin/com/weeth/domain/account/presentation/AccountAdminController.kt index ffbbc65b..d44b4a7b 100644 --- a/src/main/kotlin/com/weeth/domain/account/presentation/AccountAdminController.kt +++ b/src/main/kotlin/com/weeth/domain/account/presentation/AccountAdminController.kt @@ -26,7 +26,7 @@ class AccountAdminController( private val manageAccountUseCase: ManageAccountUseCase, ) { @PostMapping - @Operation(summary = "회비 총 금액 기입") + @Operation(summary = "회비 총 금액 기입", hidden = true) fun save( @TsidParam @TsidPathVariable clubId: Long, diff --git a/src/main/kotlin/com/weeth/domain/account/presentation/AccountController.kt b/src/main/kotlin/com/weeth/domain/account/presentation/AccountController.kt index 6bc7e7a3..36035ab5 100644 --- a/src/main/kotlin/com/weeth/domain/account/presentation/AccountController.kt +++ b/src/main/kotlin/com/weeth/domain/account/presentation/AccountController.kt @@ -25,7 +25,7 @@ class AccountController( private val getAccountQueryService: GetAccountQueryService, ) { @GetMapping("/{cardinal}") - @Operation(summary = "회비 내역 조회") + @Operation(summary = "회비 내역 조회", hidden = true) fun find( @TsidParam @TsidPathVariable clubId: Long, diff --git a/src/main/kotlin/com/weeth/domain/account/presentation/ReceiptAdminController.kt b/src/main/kotlin/com/weeth/domain/account/presentation/ReceiptAdminController.kt index 9de17985..86cb2267 100644 --- a/src/main/kotlin/com/weeth/domain/account/presentation/ReceiptAdminController.kt +++ b/src/main/kotlin/com/weeth/domain/account/presentation/ReceiptAdminController.kt @@ -32,7 +32,7 @@ class ReceiptAdminController( private val manageReceiptUseCase: ManageReceiptUseCase, ) { @PostMapping - @Operation(summary = "회비 사용 내역 기입") + @Operation(summary = "회비 사용 내역 기입", hidden = true) fun save( @TsidParam @TsidPathVariable clubId: Long, @@ -44,7 +44,7 @@ class ReceiptAdminController( } @DeleteMapping("/{receiptId}") - @Operation(summary = "회비 사용 내역 취소") + @Operation(summary = "회비 사용 내역 취소", hidden = true) fun delete( @TsidParam @TsidPathVariable clubId: Long, @@ -56,7 +56,7 @@ class ReceiptAdminController( } @PatchMapping("/{receiptId}") - @Operation(summary = "회비 사용 내역 수정") + @Operation(summary = "회비 사용 내역 수정", hidden = true) fun update( @TsidParam @TsidPathVariable clubId: Long, diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceController.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceController.kt index 912e045c..8212ff76 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceController.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceController.kt @@ -41,7 +41,7 @@ class AttendanceController( } @GetMapping - @Operation(summary = "출석 메인페이지") + @Operation(summary = "내 출석 요약 조회") fun find( @TsidParam @TsidPathVariable clubId: Long, @@ -53,7 +53,7 @@ class AttendanceController( ) @GetMapping("/detail") - @Operation(summary = "출석 내역 상세조회") + @Operation(summary = "내 출석 상세 내역 조회") fun findAll( @TsidParam @TsidPathVariable clubId: Long, diff --git a/src/main/kotlin/com/weeth/domain/club/presentation/ClubAdminController.kt b/src/main/kotlin/com/weeth/domain/club/presentation/ClubAdminController.kt index 75997340..753d6b7f 100644 --- a/src/main/kotlin/com/weeth/domain/club/presentation/ClubAdminController.kt +++ b/src/main/kotlin/com/weeth/domain/club/presentation/ClubAdminController.kt @@ -182,7 +182,7 @@ class ClubAdminController( } @PatchMapping("/members/apply-ob") - @Operation(summary = "멤버 OB 기수 등록") + @Operation(summary = "멤버 OB 기수 등록", deprecated = true) fun applyOb( @Parameter(hidden = true) @CurrentUser userId: Long, @TsidParam diff --git a/src/main/kotlin/com/weeth/domain/penalty/presentation/PenaltyAdminController.kt b/src/main/kotlin/com/weeth/domain/penalty/presentation/PenaltyAdminController.kt index a119f5d7..fc6d75d6 100644 --- a/src/main/kotlin/com/weeth/domain/penalty/presentation/PenaltyAdminController.kt +++ b/src/main/kotlin/com/weeth/domain/penalty/presentation/PenaltyAdminController.kt @@ -37,7 +37,7 @@ class PenaltyAdminController( private val getPenaltyQueryService: GetPenaltyQueryService, ) { @PostMapping - @Operation(summary = "패널티 부여") + @Operation(summary = "패널티 부여", hidden = true) fun assignPenalty( @TsidParam @TsidPathVariable clubId: Long, @@ -49,7 +49,7 @@ class PenaltyAdminController( } @PatchMapping - @Operation(summary = "패널티 수정") + @Operation(summary = "패널티 수정", hidden = true) fun update( @TsidParam @TsidPathVariable clubId: Long, @@ -61,7 +61,7 @@ class PenaltyAdminController( } @GetMapping - @Operation(summary = "전체 패널티 조회") + @Operation(summary = "전체 패널티 조회", hidden = true) fun findAll( @TsidParam @TsidPathVariable clubId: Long, @@ -74,7 +74,7 @@ class PenaltyAdminController( ) @DeleteMapping - @Operation(summary = "패널티 삭제") + @Operation(summary = "패널티 삭제", hidden = true) fun delete( @TsidParam @TsidPathVariable clubId: Long, diff --git a/src/main/kotlin/com/weeth/domain/penalty/presentation/PenaltyUserController.kt b/src/main/kotlin/com/weeth/domain/penalty/presentation/PenaltyUserController.kt index 7f2baa53..55898566 100644 --- a/src/main/kotlin/com/weeth/domain/penalty/presentation/PenaltyUserController.kt +++ b/src/main/kotlin/com/weeth/domain/penalty/presentation/PenaltyUserController.kt @@ -23,7 +23,7 @@ class PenaltyUserController( private val getPenaltyQueryService: GetPenaltyQueryService, ) { @GetMapping - @Operation(summary = "본인 패널티 조회") + @Operation(summary = "본인 패널티 조회", hidden = true) fun findAllPenalties( @TsidParam @TsidPathVariable clubId: Long, diff --git a/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt b/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt index 1bc82ac0..60e2f038 100644 --- a/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt +++ b/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt @@ -56,7 +56,6 @@ class ExceptionDocController { fun userErrorCodes() { } - // todo: SAS 관련 예외도 추가 @GetMapping("/auth") @Operation(summary = "인증/인가 에러 코드 목록") @ApiErrorCodeExample(JwtErrorCode::class) From 5f1ad28d0e23e2c2332b1d22a6f0aa5b83aa12f7 Mon Sep 17 00:00:00 2001 From: hyxklee Date: Tue, 31 Mar 2026 16:46:13 +0900 Subject: [PATCH 6/6] =?UTF-8?q?test:=20=EC=9D=98=EC=A1=B4=EC=84=B1=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/usecase/command/SocialLoginUseCaseTest.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/kotlin/com/weeth/domain/user/application/usecase/command/SocialLoginUseCaseTest.kt b/src/test/kotlin/com/weeth/domain/user/application/usecase/command/SocialLoginUseCaseTest.kt index bbc853ca..14a4bac5 100644 --- a/src/test/kotlin/com/weeth/domain/user/application/usecase/command/SocialLoginUseCaseTest.kt +++ b/src/test/kotlin/com/weeth/domain/user/application/usecase/command/SocialLoginUseCaseTest.kt @@ -1,5 +1,6 @@ package com.weeth.domain.user.application.usecase.command +import com.fasterxml.jackson.databind.ObjectMapper import com.weeth.domain.file.domain.port.FileAccessUrlPort import com.weeth.domain.user.application.dto.request.SocialLoginRequest import com.weeth.domain.user.application.exception.EmailNotFoundException @@ -7,7 +8,6 @@ import com.weeth.domain.user.application.mapper.UserMapper import com.weeth.domain.user.domain.entity.User import com.weeth.domain.user.domain.entity.UserSocialAccount import com.weeth.domain.user.domain.enums.SocialProvider -import com.weeth.domain.user.domain.enums.Status import com.weeth.domain.user.domain.port.SocialAuthPort import com.weeth.domain.user.domain.repository.UserRepository import com.weeth.domain.user.domain.repository.UserSocialAccountRepository @@ -35,6 +35,7 @@ class SocialLoginUseCaseTest : val jwtManageUseCase = mockk() val fileAccessUrlPort = mockk() val userMapper = UserMapper(fileAccessUrlPort) + val objectMapper = mockk() val useCase = SocialLoginUseCase( @@ -43,6 +44,7 @@ class SocialLoginUseCaseTest : socialAuthPortRegistry = socialAuthPortRegistry, jwtManageUseCase = jwtManageUseCase, userMapper = userMapper, + objectMapper, ) beforeTest {