Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3-5 오류 처리 #370

Closed
Tracked by #308
jongfeel opened this issue Mar 11, 2023 · 0 comments
Closed
Tracked by #308

3-5 오류 처리 #370

jongfeel opened this issue Mar 11, 2023 · 0 comments
Assignees
Labels
2023 Hands-on microservices with kotlin 코틀린 마이크로서비스 개발

Comments

@jongfeel
Copy link
Owner

jongfeel commented Mar 11, 2023

3-5 오류 처리

모든 소프트웨어는 오류를 처리해야 한다.
그것이 정의된 비즈니스 규칙에 의해 트리거 되는 것이든,
발생할 수 있는 특수한 상황을 처리하기 위한 것이든 간에 오류를 처리해야 한다.

RESTful API에서는 오류가 발생했을 때 상태코드를 사용하며,
소스 코드에서 이런 시나리오를 처리하고 오류가 정상적으로 처리되도록 해야 한다.

컨트롤러 어드바이스 사용

오류를 사용자 지정하고 메시지 본문을 반환하도록 예외를 직접 처리한다.
그러려면 스프링의 ControllerAdvice와 ExceptionHandler 애노테이션을 사용해 애플리케이션 코드에서 캐치하지 않은 예외를 처리하는 매커니즘을 제공한다.

@ControllerAdvice
class ErrorHandler {
    @ExceptionHandler(JsonParseException::class)
    fun JsonParseExceptionHandler(servletRequest: HttpServletRequest, excpetion: Exception) : ResponseEntity<String> {
        return ResponseEntity("JSON Error", HttpStatus.BAD_REQUEST)
    }
}

POST /customer로 잘못된 JSON 요청을 하면 400 BAD REQUEST가 리턴된다.

실행 결과

image

RESTful API를 만든다면 JSON으로 응답해야 한다. 따라서 오류 응답을 저장할 수 있는 단순한 데이터 클래스를 만들고 사용한다.

data class ErrorResponse(val error: String, val message: String)
@ControllerAdvice
class ErrorHandler {
    @ExceptionHandler(JsonParseException::class)
    fun JsonParseExceptionHandler(servletRequest: HttpServletRequest, exception: Exception) : ResponseEntity<ErrorResponse> {
        return ResponseEntity(ErrorResponse("JSON Error", exception.message ?: "invalid json"), HttpStatus.BAD_REQUEST)
    }
}

실행 결과

image

controller advice에 예외 로그를 남겨서 발생 가능한 문제를 추적할 수 있게 하면 좋다.

비즈니스 예외 생성

Exception을 상속한 CustomerNotFoundException 생성

package com.microservices.chapter3

class CustomerNotFoundException(message: String) : Exception(message)

CustomerController에서 예외 발생

@GetMapping(value = ["/customer/{id}"])
    fun getCustomer(@PathVariable id: Int) : ResponseEntity<Customer?> {
        val customer = customerService.getCustomer(id) ?: throw CustomerNotFoundException("customer '$id' not found")
        return ResponseEntity(customer, HttpStatus.OK)
    }

ErrorHandler에 추가

@ExceptionHandler(CustomerNotFoundException::class)
    fun CustomerNotFoundExceptionHandler(servletRequest: HttpServletRequest, exception: Exception) : ResponseEntity<ErrorResponse> {
        return ResponseEntity(ErrorResponse("Customer Not Found", exception.message!!), HttpStatus.NOT_FOUND)
    }

없는 id를 호출한 실행 결과

image

컨트롤러 어드바이스 피하기

컨트롤러 어드바이스는 훌륭한 도구지만 때로는go to 명령과 동등한 수준으로 사용할 수 있다. 흐름을 통제할 수 없게 되고, 메인 로직 바깥에서 어떤 일이 일어나게 만든다.

컨트롤러 어드바이스는 특별한 상황을 처리해야 하는 경우에만 사용해야 하며, 오류에 응답해야 할 때 비즈니스 로직을 추가해서 컨트롤러 어드바이스의 사용을 피할 수 있다.

컨트롤러 메소드를 다음과 같이 수정할 수 있다

@GetMapping(value = ["/customer/{id}"])
fun getCustomer(@PathVariable id: Int) : ResponseEntity<Any> {
    val customer = customerService.getCustomer(id)
    return if (customer != null)
        ResponseEntity(customer, HttpStatus.OK)
    else
        ResponseEntity(ErrorResponse("Customer Not Found", "customer '$id' not found"), HttpStatus.NOT_FOUND)
    return ResponseEntity(customer, HttpStatus.OK)
}

결과는 동일하지만, 컨트롤러에 로직 추가로 컨트롤러 어드바이스와 예외를 만드는 걸 피한 것이다.

예외적인 오류가 아니라 비즈니스 로직 오류인 경우는 불필요한 예외를 만들지 않도록 하면 실제 문제를 찾는데 도움이 된다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2023 Hands-on microservices with kotlin 코틀린 마이크로서비스 개발
Projects
No open projects
Development

No branches or pull requests

1 participant