[#20] 도메인별 예외 분리 #21

Merged
pricelees merged 37 commits from refactor/#20 into main 2025-07-24 02:48:53 +00:00
2 changed files with 35 additions and 41 deletions
Showing only changes of commit 16426c9f99 - Show all commits

View File

@ -2,7 +2,6 @@ package roomescape.common.exception
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.http.HttpStatus
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.http.converter.HttpMessageNotReadableException import org.springframework.http.converter.HttpMessageNotReadableException
import org.springframework.web.bind.MethodArgumentNotValidException import org.springframework.web.bind.MethodArgumentNotValidException
@ -20,8 +19,8 @@ class ExceptionControllerAdvice(
logger.error(e) { "message: ${e.message}" } logger.error(e) { "message: ${e.message}" }
val errorCode: ErrorCode = e.errorCode val errorCode: ErrorCode = e.errorCode
val httpStatus: Int = errorCode.httpStatus.value() return ResponseEntity
return ResponseEntity.status(httpStatus) .status(errorCode.httpStatus)
.body(CommonErrorResponseV2(errorCode, e.message)) .body(CommonErrorResponseV2(errorCode, e.message))
} }
@ -35,32 +34,35 @@ class ExceptionControllerAdvice(
} }
@ExceptionHandler(value = [HttpMessageNotReadableException::class]) @ExceptionHandler(value = [HttpMessageNotReadableException::class])
fun handleHttpMessageNotReadableException(e: HttpMessageNotReadableException): ResponseEntity<CommonErrorResponse> { fun handleHttpMessageNotReadableException(e: HttpMessageNotReadableException): ResponseEntity<CommonErrorResponseV2> {
logger.error(e) { "message: ${e.message}" } logger.error(e) { "message: ${e.message}" }
val errorCode: ErrorCode = CommonErrorCode.INVALID_INPUT_VALUE
return ResponseEntity return ResponseEntity
.status(HttpStatus.BAD_REQUEST) .status(errorCode.httpStatus)
.body(CommonErrorResponse(ErrorType.INVALID_REQUEST_DATA_TYPE)) .body(CommonErrorResponseV2(errorCode, e.message ?: errorCode.message))
} }
@ExceptionHandler(value = [MethodArgumentNotValidException::class]) @ExceptionHandler(value = [MethodArgumentNotValidException::class])
fun handleMethodArgumentNotValidException(e: MethodArgumentNotValidException): ResponseEntity<CommonErrorResponse> { fun handleMethodArgumentNotValidException(e: MethodArgumentNotValidException): ResponseEntity<CommonErrorResponseV2> {
val messages: String = e.bindingResult.allErrors val message: String = e.bindingResult.allErrors
.mapNotNull { it.defaultMessage } .mapNotNull { it.defaultMessage }
.joinToString(", ") .joinToString(", ")
logger.error(e) { "message: $messages" } logger.error(e) { "message: $message" }
val errorCode: ErrorCode = CommonErrorCode.INVALID_INPUT_VALUE
return ResponseEntity return ResponseEntity
.status(HttpStatus.BAD_REQUEST) .status(errorCode.httpStatus)
.body(CommonErrorResponse(ErrorType.INVALID_REQUEST_DATA, messages)) .body(CommonErrorResponseV2(errorCode, message))
} }
@ExceptionHandler(value = [Exception::class]) @ExceptionHandler(value = [Exception::class])
fun handleException(e: Exception): ResponseEntity<CommonErrorResponse> { fun handleException(e: Exception): ResponseEntity<CommonErrorResponseV2> {
logger.error(e) { "message: ${e.message}" } logger.error(e) { "message: ${e.message}" }
val errorCode: ErrorCode = CommonErrorCode.UNEXPECTED_SERVER_ERROR
return ResponseEntity return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR) .status(errorCode.httpStatus)
.body(CommonErrorResponse(ErrorType.UNEXPECTED_ERROR)) .body(CommonErrorResponseV2(errorCode))
} }
} }

View File

@ -4,18 +4,19 @@ import com.ninjasquad.springmockk.SpykBean
import io.mockk.every import io.mockk.every
import org.hamcrest.Matchers.containsString import org.hamcrest.Matchers.containsString
import org.hamcrest.Matchers.equalTo import org.hamcrest.Matchers.equalTo
import org.springframework.beans.factory.annotation.Autowired
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.data.repository.findByIdOrNull
import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.MockMvc
import roomescape.auth.service.AuthService import roomescape.auth.service.AuthService
import roomescape.common.exception.CommonErrorCode
import roomescape.common.exception.ErrorCode
import roomescape.common.exception.ErrorType import roomescape.common.exception.ErrorType
import roomescape.util.MemberFixture import roomescape.util.MemberFixture
import roomescape.util.RoomescapeApiTest import roomescape.util.RoomescapeApiTest
@WebMvcTest(controllers = [AuthController::class]) @WebMvcTest(controllers = [AuthController::class])
class AuthControllerTest( class AuthControllerTest(
@Autowired mockMvc: MockMvc val mockMvc: MockMvc
) : RoomescapeApiTest() { ) : RoomescapeApiTest() {
@SpykBean @SpykBean
@ -71,32 +72,23 @@ class AuthControllerTest(
} }
} }
} }
When("입력 값이 잘못되면") {
val expectedErrorCode: ErrorCode = CommonErrorCode.INVALID_INPUT_VALUE
When("잘못된 요청을 보내면 400 에러를 응답한다.") { Then("400 에러를 응답한다") {
listOf(
Then("이메일 형식이 잘못된 경우") { userRequest.copy(email = "invalid"),
val invalidRequest: LoginRequest = userRequest.copy(email = "invalid") userRequest.copy(password = " "),
"{\"email\": \"null\", \"password\": \"null\"}"
runPostTest( ).forEach {
mockMvc = mockMvc, runPostTest(
endpoint = endpoint, mockMvc = mockMvc,
body = invalidRequest, endpoint = endpoint,
) { body = it,
status { isBadRequest() } ) {
jsonPath("$.message", containsString("이메일 형식이 일치하지 않습니다.")) status { isEqualTo(expectedErrorCode.httpStatus.value()) }
} jsonPath("$.code", equalTo(expectedErrorCode.errorCode))
} }
Then("비밀번호가 공백인 경우") {
val invalidRequest = userRequest.copy(password = " ")
runPostTest(
mockMvc = mockMvc,
endpoint = endpoint,
body = invalidRequest,
) {
status { isBadRequest() }
jsonPath("$.message", containsString("비밀번호는 공백일 수 없습니다."))
} }
} }
} }