개발에 AtoZ까지

[SpringBoot] HTTP Status Code 제어 중요성 및 방법 본문

백엔드/REST API

[SpringBoot] HTTP Status Code 제어 중요성 및 방법

AtoZ 개발자 2021. 7. 17. 16:15
반응형

◆목표

HTTP Status Code란
HTTP Status Code의 중요성
HTTP Status Code 제어 및 Spring 예외처리 방법(SpringBoot)
SpringBoot에서 적용방법

 


1. HTTP Status Code란?

HTTP Status Code(HTTP 상태 코드)는 클라이언트가 보낸 HTTP 요청에 대한 서버의 응답을 코드로 표현한 것으로 해당 코드로 요청의 성공 / 실패 / 실패요인등을 알 수 있다.

 

2. HTTP Status Code의 중요성

아래와 같이 URI에 USERS/ID 입력하고 GET 메소드로 조회하는 스프링 예제가 있다.

아래 예제는 Users에 ID값이 1~3까지 밖에 없는 상황에서 ID값이 100인 사용자를 조회한 결과이다.

호출 시 분명 없는 사용자를 조회했으니 없다는 내용 또는 에러가 반환해야 할 것 같지만, 결과는 그렇지 않다. 왜냐하면 HTTP 입장에서는 해당 URI 호출이 문법적으로 오류가 없고 정상적으로 서버에서 응답을 줬기 때문에 HTTP 상태코드는 200인 것이다.


이런 경우는 REST API 설계 시 올바르지 않은 설계 중 하나다.
왜냐하면 직관적이지 않고 명확하지 않기 때문이다. 그러면 어떻게 올바르게 수정할 수 있을까?
아래와 같이 HTTP 상태코드를 변경하여 응답해주는것이 좋은 방법이라고 생각한다.

 

 

추가적으로 POST인 경우도 살펴보겠다.

아래 예제는 User Create하는 API를 POST 메소드로 호출하는 예제이다.


HTTP 상태코드를 보았을 때 200으로 성공이라는 뜻을 가지고 있지만 명확하지는 않다고 생각한다.
그래서 실제 클라이언트가 요청한 Create 작업이 정상적으로 됐는지 더 직관적으로 알 수 있도록 해주는 것이 좋을 것 같다.

적용을 하게 되면 아래와 같이 HTTP 상태코드를 201 Create로 변경하여 응답하는것이 더 좋은 방법이라고 생각한다.

 

 

3. HTTP Status Code 제어 및 Spring 예외처리 방법(SpringBoot)

스프링 부트 환경에서 예외처리에 따른 HTTP 상태 코드를 관리하는 방법은 2가지 정도가 있을 것 같다.


첫 번째, 각각의 상황에 따라 Exception Class을 만들어서 해당 클래스에서 관리하는 방법
두 번째, 첫 번째 방법에서 만든 Exception Class를 가지고 중앙에서 관리하는 방법

 

첫 번째와 두 번째를 비교했을때 가장 큰 차이점은 Exception 관리를 한곳에서 할 수 있냐 없냐는 차이 일것이다.

조금 더 쉽게 설명해보자면 두번째 방법에서 "중앙에서 관리한다"라는 말은 SpringBoot에서 지원하는 @controllerAdvice 어노테이션을 사용해 Exception 발생 시 해당 어노테이션이 있는 클래스에서 예외처리를 해줄 수가 있다.

한두 개의 Exception class가 있다면 상관없지만 다수의 Exception Class를 관리하거나 유지 보수하려고 한다면 두 번째 방법을 사용하는 것이 좋을 것이다.

 

그럼 실제 예제 코드를 보면서 확인해보겠다.
먼저 첫 번째 방법의 코드이다.

 @GetMapping("/users/{id}")
    public User retrieveUser(@PathVariable int id){
        User user = service.findOne(id);

        if(user==null){
            throw new UserNotFoundException(String.format("%s not found",id));
        }
        return user;
    }

 

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException{

    public UserNotFoundException(String message){
        super(message);
    }
}

 

결과 화면

위에서 보는 것과 같이 사용자가 없는 상황일 때 명확하게 HTTPSTATUS.NOT_FOUND(404) 로 반환하여 프론트개발자 또는 클라이언트에게 명확하게 반환하는 것이 명시적이고 좋은 REST API 설계라고 생각한다. 그런데 만약 이런 Exception Class들이 엄청 많아진다면 어떻게 관리하면 좋을까? 그 방법이 두번째 방법이라고 생각한다.

 

두 번째 예제코드이다.

package com.ksh.restfulapiexam.Exceptions;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.util.Date;

@RestController
@ControllerAdvice
public class customizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

//Default exception
    @ExceptionHandler(Exception.class)
    public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request){
        ExceptionResponse exceptionResponse = new ExceptionResponse().builder()
                .timestamp(new Date())
                .message(ex.getMessage())
                .details(request.getDescription(false))
                .build();
        return new ResponseEntity(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);

    }

    @ExceptionHandler(UserNotFoundException.class)
    public final ResponseEntity<Object> handleUserNotFoundExceptions(Exception ex, WebRequest request){

        ExceptionResponse exceptionResponse = new ExceptionResponse().builder()
                .timestamp(new Date())
                .message(ex.getMessage())
                .details(request.getDescription(false))
                .build();
        return new ResponseEntity(exceptionResponse, HttpStatus.NOT_FOUND);

    }
}

 

결과 화면

두 번째 방법을 봤을 때 @ControllerAdvice 어노테이션으로 구성된 Class만 보더라고 이 프로젝트에서 어떤 예외처리를 하고 있는지 한눈에 볼 수 있다는 점에서도 좋은 설계라고 생각한다.

 

 

 


📌기재한 내용 중 잘못된 내용이나 보충설명이 필요한 부분이 있다면 주저하지 마시고 피드백 부탁드리겠습니다.🙏

반응형
Comments