generated from pricelees/issue-pr-template
[#30] 코드 구조 개선 #31
@ -3,64 +3,56 @@ package roomescape.auth.business
|
|||||||
import io.github.oshai.kotlinlogging.KLogger
|
import io.github.oshai.kotlinlogging.KLogger
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
import roomescape.auth.exception.AuthErrorCode
|
import roomescape.auth.exception.AuthErrorCode
|
||||||
import roomescape.auth.exception.AuthException
|
import roomescape.auth.exception.AuthException
|
||||||
import roomescape.auth.infrastructure.jwt.JwtHandler
|
import roomescape.auth.infrastructure.jwt.JwtHandler
|
||||||
import roomescape.auth.web.LoginCheckResponse
|
import roomescape.auth.web.LoginCheckResponse
|
||||||
import roomescape.auth.web.LoginRequest
|
import roomescape.auth.web.LoginRequest
|
||||||
import roomescape.auth.web.LoginResponse
|
import roomescape.auth.web.LoginResponse
|
||||||
import roomescape.common.exception.RoomescapeException
|
import roomescape.member.implement.MemberFinder
|
||||||
import roomescape.member.business.MemberService
|
|
||||||
import roomescape.member.infrastructure.persistence.MemberEntity
|
import roomescape.member.infrastructure.persistence.MemberEntity
|
||||||
|
|
||||||
private val log: KLogger = KotlinLogging.logger {}
|
private val log: KLogger = KotlinLogging.logger {}
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class AuthService(
|
class AuthService(
|
||||||
private val memberService: MemberService,
|
private val memberFinder: MemberFinder,
|
||||||
private val jwtHandler: JwtHandler,
|
private val jwtHandler: JwtHandler,
|
||||||
) {
|
) {
|
||||||
|
@Transactional(readOnly = true)
|
||||||
fun login(request: LoginRequest): LoginResponse {
|
fun login(request: LoginRequest): LoginResponse {
|
||||||
val params = "email=${request.email}, password=${request.password}"
|
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") {
|
val member: MemberEntity = fetchOrThrow(AuthErrorCode.LOGIN_FAILED) {
|
||||||
memberService.findByEmailAndPassword(request.email, request.password)
|
memberFinder.findByEmailAndPassword(request.email, request.password)
|
||||||
}
|
}
|
||||||
|
|
||||||
val accessToken: String = jwtHandler.createToken(member.id!!)
|
val accessToken: String = jwtHandler.createToken(member.id!!)
|
||||||
|
|
||||||
return LoginResponse(accessToken)
|
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 {
|
fun checkLogin(memberId: Long): LoginCheckResponse {
|
||||||
log.debug { "[AuthService.checkLogin] 로그인 확인 시작: memberId=$memberId" }
|
log.info { "[AuthService.checkLogin] 시작: memberId=$memberId" }
|
||||||
val member: MemberEntity =
|
|
||||||
fetchMemberOrThrow(AuthErrorCode.MEMBER_NOT_FOUND, "memberId=$memberId", "checkLogin") {
|
val member: MemberEntity = fetchOrThrow(AuthErrorCode.MEMBER_NOT_FOUND) { memberFinder.findById(memberId) }
|
||||||
memberService.findById(memberId)
|
|
||||||
}
|
|
||||||
|
|
||||||
return LoginCheckResponse(member.name, member.role.name)
|
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) {
|
fun logout(memberId: Long) {
|
||||||
log.info { "[AuthService.logout] 로그아웃: memberId=$memberId" }
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,8 +7,8 @@ import org.springframework.web.bind.annotation.GetMapping
|
|||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
import org.springframework.web.bind.annotation.RequestBody
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
import roomescape.auth.docs.AuthAPI
|
|
||||||
import roomescape.auth.business.AuthService
|
import roomescape.auth.business.AuthService
|
||||||
|
import roomescape.auth.docs.AuthAPI
|
||||||
import roomescape.auth.web.support.MemberId
|
import roomescape.auth.web.support.MemberId
|
||||||
import roomescape.common.dto.response.CommonApiResponse
|
import roomescape.common.dto.response.CommonApiResponse
|
||||||
|
|
||||||
|
|||||||
@ -11,14 +11,14 @@ import org.springframework.web.servlet.HandlerInterceptor
|
|||||||
import roomescape.auth.exception.AuthErrorCode
|
import roomescape.auth.exception.AuthErrorCode
|
||||||
import roomescape.auth.exception.AuthException
|
import roomescape.auth.exception.AuthException
|
||||||
import roomescape.auth.infrastructure.jwt.JwtHandler
|
import roomescape.auth.infrastructure.jwt.JwtHandler
|
||||||
import roomescape.member.business.MemberService
|
import roomescape.member.implement.MemberFinder
|
||||||
import roomescape.member.infrastructure.persistence.MemberEntity
|
import roomescape.member.infrastructure.persistence.MemberEntity
|
||||||
|
|
||||||
private val log: KLogger = KotlinLogging.logger {}
|
private val log: KLogger = KotlinLogging.logger {}
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class AuthInterceptor(
|
class AuthInterceptor(
|
||||||
private val memberService: MemberService,
|
private val memberFinder: MemberFinder,
|
||||||
private val jwtHandler: JwtHandler
|
private val jwtHandler: JwtHandler
|
||||||
) : HandlerInterceptor {
|
) : HandlerInterceptor {
|
||||||
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
|
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
|
||||||
@ -50,10 +50,10 @@ class AuthInterceptor(
|
|||||||
private fun findMember(accessToken: String?): MemberEntity {
|
private fun findMember(accessToken: String?): MemberEntity {
|
||||||
try {
|
try {
|
||||||
val memberId = jwtHandler.getMemberIdFromToken(accessToken)
|
val memberId = jwtHandler.getMemberIdFromToken(accessToken)
|
||||||
return memberService.findById(memberId)
|
return memberFinder.findById(memberId)
|
||||||
.also { MDC.put("member_id", "$memberId") }
|
.also { MDC.put("member_id", "$memberId") }
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
log.info { "[AuthInterceptor] 회원 조회 실패. accessToken = ${accessToken}" }
|
log.info { "[AuthInterceptor] 회원 조회 실패. accessToken = $accessToken" }
|
||||||
val errorCode = AuthErrorCode.MEMBER_NOT_FOUND
|
val errorCode = AuthErrorCode.MEMBER_NOT_FOUND
|
||||||
throw AuthException(errorCode, e.message ?: errorCode.message)
|
throw AuthException(errorCode, e.message ?: errorCode.message)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,23 +6,21 @@ import io.kotest.core.spec.style.BehaviorSpec
|
|||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import org.springframework.data.repository.findByIdOrNull
|
|
||||||
import roomescape.auth.exception.AuthErrorCode
|
import roomescape.auth.exception.AuthErrorCode
|
||||||
import roomescape.auth.exception.AuthException
|
import roomescape.auth.exception.AuthException
|
||||||
import roomescape.auth.infrastructure.jwt.JwtHandler
|
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.MemberEntity
|
||||||
import roomescape.member.infrastructure.persistence.MemberRepository
|
|
||||||
import roomescape.util.JwtFixture
|
import roomescape.util.JwtFixture
|
||||||
import roomescape.util.MemberFixture
|
import roomescape.util.MemberFixture
|
||||||
import roomescape.util.TsidFactory
|
|
||||||
|
|
||||||
class AuthServiceTest : BehaviorSpec({
|
class AuthServiceTest : BehaviorSpec({
|
||||||
val memberRepository: MemberRepository = mockk()
|
val memberFinder: MemberFinder = mockk()
|
||||||
val memberService = MemberService(TsidFactory, memberRepository)
|
|
||||||
val jwtHandler: JwtHandler = JwtFixture.create()
|
val jwtHandler: JwtHandler = JwtFixture.create()
|
||||||
|
|
||||||
val authService = AuthService(memberService, jwtHandler)
|
val authService = AuthService(memberFinder, jwtHandler)
|
||||||
val user: MemberEntity = MemberFixture.user()
|
val user: MemberEntity = MemberFixture.user()
|
||||||
|
|
||||||
Given("로그인 요청을 받으면") {
|
Given("로그인 요청을 받으면") {
|
||||||
@ -31,7 +29,7 @@ class AuthServiceTest : BehaviorSpec({
|
|||||||
|
|
||||||
Then("회원이 있다면 JWT 토큰을 생성한 뒤 반환한다.") {
|
Then("회원이 있다면 JWT 토큰을 생성한 뒤 반환한다.") {
|
||||||
every {
|
every {
|
||||||
memberRepository.findByEmailAndPassword(request.email, request.password)
|
memberFinder.findByEmailAndPassword(request.email, request.password)
|
||||||
} returns user
|
} returns user
|
||||||
|
|
||||||
val accessToken: String = authService.login(request).accessToken
|
val accessToken: String = authService.login(request).accessToken
|
||||||
@ -42,8 +40,8 @@ class AuthServiceTest : BehaviorSpec({
|
|||||||
|
|
||||||
Then("회원이 없다면 예외를 던진다.") {
|
Then("회원이 없다면 예외를 던진다.") {
|
||||||
every {
|
every {
|
||||||
memberRepository.findByEmailAndPassword(request.email, request.password)
|
memberFinder.findByEmailAndPassword(request.email, request.password)
|
||||||
} returns null
|
} throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND)
|
||||||
|
|
||||||
val exception = shouldThrow<AuthException> {
|
val exception = shouldThrow<AuthException> {
|
||||||
authService.login(request)
|
authService.login(request)
|
||||||
@ -59,7 +57,7 @@ class AuthServiceTest : BehaviorSpec({
|
|||||||
val userId: Long = user.id!!
|
val userId: Long = user.id!!
|
||||||
|
|
||||||
Then("회원이 있다면 회원의 이름을 반환한다.") {
|
Then("회원이 있다면 회원의 이름을 반환한다.") {
|
||||||
every { memberRepository.findByIdOrNull(userId) } returns user
|
every { memberFinder.findById(userId) } returns user
|
||||||
|
|
||||||
val response = authService.checkLogin(userId)
|
val response = authService.checkLogin(userId)
|
||||||
|
|
||||||
@ -69,7 +67,9 @@ class AuthServiceTest : BehaviorSpec({
|
|||||||
}
|
}
|
||||||
|
|
||||||
Then("회원이 없다면 예외를 던진다.") {
|
Then("회원이 없다면 예외를 던진다.") {
|
||||||
every { memberRepository.findByIdOrNull(userId) } returns null
|
every {
|
||||||
|
memberFinder.findById(userId)
|
||||||
|
} throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND)
|
||||||
|
|
||||||
val exception = shouldThrow<AuthException> {
|
val exception = shouldThrow<AuthException> {
|
||||||
authService.checkLogin(userId)
|
authService.checkLogin(userId)
|
||||||
|
|||||||
@ -4,12 +4,13 @@ import com.ninjasquad.springmockk.SpykBean
|
|||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import org.hamcrest.Matchers.equalTo
|
import org.hamcrest.Matchers.equalTo
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
|
||||||
import org.springframework.data.repository.findByIdOrNull
|
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
import org.springframework.test.web.servlet.MockMvc
|
||||||
import roomescape.auth.business.AuthService
|
import roomescape.auth.business.AuthService
|
||||||
import roomescape.auth.exception.AuthErrorCode
|
import roomescape.auth.exception.AuthErrorCode
|
||||||
import roomescape.common.exception.CommonErrorCode
|
import roomescape.common.exception.CommonErrorCode
|
||||||
import roomescape.common.exception.ErrorCode
|
import roomescape.common.exception.ErrorCode
|
||||||
|
import roomescape.member.exception.MemberErrorCode
|
||||||
|
import roomescape.member.exception.MemberException
|
||||||
import roomescape.util.MemberFixture
|
import roomescape.util.MemberFixture
|
||||||
import roomescape.util.RoomescapeApiTest
|
import roomescape.util.RoomescapeApiTest
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ class AuthControllerTest(
|
|||||||
val expectedToken = "expectedToken"
|
val expectedToken = "expectedToken"
|
||||||
|
|
||||||
every {
|
every {
|
||||||
memberRepository.findByEmailAndPassword(userRequest.email, userRequest.password)
|
memberFinder.findByEmailAndPassword(userRequest.email, userRequest.password)
|
||||||
} returns user
|
} returns user
|
||||||
|
|
||||||
every {
|
every {
|
||||||
@ -52,8 +53,8 @@ class AuthControllerTest(
|
|||||||
|
|
||||||
When("회원을 찾지 못하면") {
|
When("회원을 찾지 못하면") {
|
||||||
every {
|
every {
|
||||||
memberRepository.findByEmailAndPassword(userRequest.email, userRequest.password)
|
memberFinder.findByEmailAndPassword(userRequest.email, userRequest.password)
|
||||||
} returns null
|
} throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND)
|
||||||
|
|
||||||
Then("에러 응답을 받는다.") {
|
Then("에러 응답을 받는다.") {
|
||||||
val expectedError = AuthErrorCode.LOGIN_FAILED
|
val expectedError = AuthErrorCode.LOGIN_FAILED
|
||||||
@ -111,7 +112,9 @@ class AuthControllerTest(
|
|||||||
val invalidMemberId: Long = -1L
|
val invalidMemberId: Long = -1L
|
||||||
|
|
||||||
every { jwtHandler.getMemberIdFromToken(any()) } returns invalidMemberId
|
every { jwtHandler.getMemberIdFromToken(any()) } returns invalidMemberId
|
||||||
every { memberRepository.findByIdOrNull(invalidMemberId) } returns null
|
every {
|
||||||
|
memberFinder.findById(invalidMemberId)
|
||||||
|
} throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND)
|
||||||
|
|
||||||
Then("에러 응답을 받는다.") {
|
Then("에러 응답을 받는다.") {
|
||||||
val expectedError = AuthErrorCode.MEMBER_NOT_FOUND
|
val expectedError = AuthErrorCode.MEMBER_NOT_FOUND
|
||||||
@ -134,7 +137,7 @@ class AuthControllerTest(
|
|||||||
} returns 1L
|
} returns 1L
|
||||||
|
|
||||||
every {
|
every {
|
||||||
memberRepository.findByIdOrNull(1L)
|
memberFinder.findById(1L)
|
||||||
} returns MemberFixture.create(id = 1L)
|
} returns MemberFixture.create(id = 1L)
|
||||||
|
|
||||||
Then("정상 응답한다.") {
|
Then("정상 응답한다.") {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user