From 35b7f06c2deddf3a27072132c603311569e74157 Mon Sep 17 00:00:00 2001 From: pricelees Date: Mon, 4 Aug 2025 17:26:41 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20auth=20=EB=8F=84=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=EC=97=90=20=EC=83=88=EB=A1=9C=20=EC=B6=94=EA=B0=80=EB=90=9C=20?= =?UTF-8?q?MemberFinder,=20MemberWriter=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/auth/business/AuthService.kt | 52 ++++++++----------- .../roomescape/auth/web/AuthController.kt | 2 +- .../auth/web/support/AuthInterceptor.kt | 8 +-- .../auth/business/AuthServiceTest.kt | 24 ++++----- .../roomescape/auth/web/AuthControllerTest.kt | 15 +++--- 5 files changed, 48 insertions(+), 53 deletions(-) diff --git a/src/main/kotlin/roomescape/auth/business/AuthService.kt b/src/main/kotlin/roomescape/auth/business/AuthService.kt index b28e6c7c..b1cd6f82 100644 --- a/src/main/kotlin/roomescape/auth/business/AuthService.kt +++ b/src/main/kotlin/roomescape/auth/business/AuthService.kt @@ -3,64 +3,56 @@ package roomescape.auth.business import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import roomescape.auth.exception.AuthErrorCode import roomescape.auth.exception.AuthException import roomescape.auth.infrastructure.jwt.JwtHandler import roomescape.auth.web.LoginCheckResponse import roomescape.auth.web.LoginRequest import roomescape.auth.web.LoginResponse -import roomescape.common.exception.RoomescapeException -import roomescape.member.business.MemberService +import roomescape.member.implement.MemberFinder import roomescape.member.infrastructure.persistence.MemberEntity private val log: KLogger = KotlinLogging.logger {} @Service class AuthService( - private val memberService: MemberService, + private val memberFinder: MemberFinder, private val jwtHandler: JwtHandler, ) { + @Transactional(readOnly = true) fun login(request: LoginRequest): LoginResponse { val params = "email=${request.email}, password=${request.password}" - log.debug { "[AuthService.login] 로그인 시작: $params" } + log.info { "[AuthService.login] 시작: $params" } - val member: MemberEntity = fetchMemberOrThrow(AuthErrorCode.LOGIN_FAILED, params, "login") { - memberService.findByEmailAndPassword(request.email, request.password) + val member: MemberEntity = fetchOrThrow(AuthErrorCode.LOGIN_FAILED) { + memberFinder.findByEmailAndPassword(request.email, request.password) } - val accessToken: String = jwtHandler.createToken(member.id!!) + return LoginResponse(accessToken) - .also { log.info { "[AuthService.login] 로그인 완료: memberId=${member.id}" } } + .also { log.info { "[AuthService.login] 완료: email=${request.email}, memberId=${member.id}" } } } + @Transactional(readOnly = true) fun checkLogin(memberId: Long): LoginCheckResponse { - log.debug { "[AuthService.checkLogin] 로그인 확인 시작: memberId=$memberId" } - val member: MemberEntity = - fetchMemberOrThrow(AuthErrorCode.MEMBER_NOT_FOUND, "memberId=$memberId", "checkLogin") { - memberService.findById(memberId) - } + log.info { "[AuthService.checkLogin] 시작: memberId=$memberId" } + + val member: MemberEntity = fetchOrThrow(AuthErrorCode.MEMBER_NOT_FOUND) { memberFinder.findById(memberId) } return LoginCheckResponse(member.name, member.role.name) - .also { log.info { "[AuthService.checkLogin] 로그인 확인 완료: memberId=$memberId" } } + .also { log.info { "[AuthService.checkLogin] 완료: memberId=$memberId, role=${it.role}" } } + } + + private fun fetchOrThrow(errorCode: AuthErrorCode, block: () -> MemberEntity): MemberEntity { + try { + return block() + } catch (e: Exception) { + throw AuthException(errorCode, e.message ?: errorCode.message) + } } fun logout(memberId: Long) { log.info { "[AuthService.logout] 로그아웃: memberId=$memberId" } } - - private fun fetchMemberOrThrow( - errorCode: AuthErrorCode, - params: String, - calledBy: String, - block: () -> MemberEntity, - ): MemberEntity { - try { - return block() - } catch (e: Exception) { - if (e !is RoomescapeException) { - log.warn(e) { "[AuthService.$calledBy] 회원 조회 실패: $params" } - } - throw AuthException(errorCode) - } - } } diff --git a/src/main/kotlin/roomescape/auth/web/AuthController.kt b/src/main/kotlin/roomescape/auth/web/AuthController.kt index 7f3f1cc8..5b9184f7 100644 --- a/src/main/kotlin/roomescape/auth/web/AuthController.kt +++ b/src/main/kotlin/roomescape/auth/web/AuthController.kt @@ -7,8 +7,8 @@ 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.RestController -import roomescape.auth.docs.AuthAPI import roomescape.auth.business.AuthService +import roomescape.auth.docs.AuthAPI import roomescape.auth.web.support.MemberId import roomescape.common.dto.response.CommonApiResponse diff --git a/src/main/kotlin/roomescape/auth/web/support/AuthInterceptor.kt b/src/main/kotlin/roomescape/auth/web/support/AuthInterceptor.kt index 9b7dadb8..a4f3742b 100644 --- a/src/main/kotlin/roomescape/auth/web/support/AuthInterceptor.kt +++ b/src/main/kotlin/roomescape/auth/web/support/AuthInterceptor.kt @@ -11,14 +11,14 @@ import org.springframework.web.servlet.HandlerInterceptor import roomescape.auth.exception.AuthErrorCode import roomescape.auth.exception.AuthException import roomescape.auth.infrastructure.jwt.JwtHandler -import roomescape.member.business.MemberService +import roomescape.member.implement.MemberFinder import roomescape.member.infrastructure.persistence.MemberEntity private val log: KLogger = KotlinLogging.logger {} @Component class AuthInterceptor( - private val memberService: MemberService, + private val memberFinder: MemberFinder, private val jwtHandler: JwtHandler ) : HandlerInterceptor { override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean { @@ -50,10 +50,10 @@ class AuthInterceptor( private fun findMember(accessToken: String?): MemberEntity { try { val memberId = jwtHandler.getMemberIdFromToken(accessToken) - return memberService.findById(memberId) + return memberFinder.findById(memberId) .also { MDC.put("member_id", "$memberId") } } catch (e: Exception) { - log.info { "[AuthInterceptor] 회원 조회 실패. accessToken = ${accessToken}" } + log.info { "[AuthInterceptor] 회원 조회 실패. accessToken = $accessToken" } val errorCode = AuthErrorCode.MEMBER_NOT_FOUND throw AuthException(errorCode, e.message ?: errorCode.message) } diff --git a/src/test/kotlin/roomescape/auth/business/AuthServiceTest.kt b/src/test/kotlin/roomescape/auth/business/AuthServiceTest.kt index aa570047..8609ba3e 100644 --- a/src/test/kotlin/roomescape/auth/business/AuthServiceTest.kt +++ b/src/test/kotlin/roomescape/auth/business/AuthServiceTest.kt @@ -6,23 +6,21 @@ import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.shouldBe import io.mockk.every import io.mockk.mockk -import org.springframework.data.repository.findByIdOrNull import roomescape.auth.exception.AuthErrorCode import roomescape.auth.exception.AuthException import roomescape.auth.infrastructure.jwt.JwtHandler -import roomescape.member.business.MemberService +import roomescape.member.exception.MemberErrorCode +import roomescape.member.exception.MemberException +import roomescape.member.implement.MemberFinder import roomescape.member.infrastructure.persistence.MemberEntity -import roomescape.member.infrastructure.persistence.MemberRepository import roomescape.util.JwtFixture import roomescape.util.MemberFixture -import roomescape.util.TsidFactory class AuthServiceTest : BehaviorSpec({ - val memberRepository: MemberRepository = mockk() - val memberService = MemberService(TsidFactory, memberRepository) + val memberFinder: MemberFinder = mockk() val jwtHandler: JwtHandler = JwtFixture.create() - val authService = AuthService(memberService, jwtHandler) + val authService = AuthService(memberFinder, jwtHandler) val user: MemberEntity = MemberFixture.user() Given("로그인 요청을 받으면") { @@ -31,7 +29,7 @@ class AuthServiceTest : BehaviorSpec({ Then("회원이 있다면 JWT 토큰을 생성한 뒤 반환한다.") { every { - memberRepository.findByEmailAndPassword(request.email, request.password) + memberFinder.findByEmailAndPassword(request.email, request.password) } returns user val accessToken: String = authService.login(request).accessToken @@ -42,8 +40,8 @@ class AuthServiceTest : BehaviorSpec({ Then("회원이 없다면 예외를 던진다.") { every { - memberRepository.findByEmailAndPassword(request.email, request.password) - } returns null + memberFinder.findByEmailAndPassword(request.email, request.password) + } throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND) val exception = shouldThrow { authService.login(request) @@ -59,7 +57,7 @@ class AuthServiceTest : BehaviorSpec({ val userId: Long = user.id!! Then("회원이 있다면 회원의 이름을 반환한다.") { - every { memberRepository.findByIdOrNull(userId) } returns user + every { memberFinder.findById(userId) } returns user val response = authService.checkLogin(userId) @@ -69,7 +67,9 @@ class AuthServiceTest : BehaviorSpec({ } Then("회원이 없다면 예외를 던진다.") { - every { memberRepository.findByIdOrNull(userId) } returns null + every { + memberFinder.findById(userId) + } throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND) val exception = shouldThrow { authService.checkLogin(userId) diff --git a/src/test/kotlin/roomescape/auth/web/AuthControllerTest.kt b/src/test/kotlin/roomescape/auth/web/AuthControllerTest.kt index 0e895d9d..e5e95461 100644 --- a/src/test/kotlin/roomescape/auth/web/AuthControllerTest.kt +++ b/src/test/kotlin/roomescape/auth/web/AuthControllerTest.kt @@ -4,12 +4,13 @@ import com.ninjasquad.springmockk.SpykBean import io.mockk.every import org.hamcrest.Matchers.equalTo import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest -import org.springframework.data.repository.findByIdOrNull import org.springframework.test.web.servlet.MockMvc import roomescape.auth.business.AuthService import roomescape.auth.exception.AuthErrorCode import roomescape.common.exception.CommonErrorCode import roomescape.common.exception.ErrorCode +import roomescape.member.exception.MemberErrorCode +import roomescape.member.exception.MemberException import roomescape.util.MemberFixture import roomescape.util.RoomescapeApiTest @@ -31,7 +32,7 @@ class AuthControllerTest( val expectedToken = "expectedToken" every { - memberRepository.findByEmailAndPassword(userRequest.email, userRequest.password) + memberFinder.findByEmailAndPassword(userRequest.email, userRequest.password) } returns user every { @@ -52,8 +53,8 @@ class AuthControllerTest( When("회원을 찾지 못하면") { every { - memberRepository.findByEmailAndPassword(userRequest.email, userRequest.password) - } returns null + memberFinder.findByEmailAndPassword(userRequest.email, userRequest.password) + } throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND) Then("에러 응답을 받는다.") { val expectedError = AuthErrorCode.LOGIN_FAILED @@ -111,7 +112,9 @@ class AuthControllerTest( val invalidMemberId: Long = -1L every { jwtHandler.getMemberIdFromToken(any()) } returns invalidMemberId - every { memberRepository.findByIdOrNull(invalidMemberId) } returns null + every { + memberFinder.findById(invalidMemberId) + } throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND) Then("에러 응답을 받는다.") { val expectedError = AuthErrorCode.MEMBER_NOT_FOUND @@ -134,7 +137,7 @@ class AuthControllerTest( } returns 1L every { - memberRepository.findByIdOrNull(1L) + memberFinder.findById(1L) } returns MemberFixture.create(id = 1L) Then("정상 응답한다.") {