[SpringBoot] Rest Api Sample ๋งŒ๋“ค๊ธฐ #2

๋ฐ˜์‘ํ˜•

https://lucete-stellae.tistory.com/96

 

[SpringBoot] Rest Api Sample ๋งŒ๋“ค๊ธฐ #1

๊ธฐ์กด์— ๊ธ‰ํ•˜๊ฒŒ ์ง„ํ–‰ํ•œ ์‚ฌ๋‚ด SMS API ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค๊ณ  ๋‚˜์„œ, ์ •๋ฆฌ๋„ ํ•  ๊ฒธ ์ƒ˜ํ”Œ๋กœ REST API ์ƒ˜ํ”Œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค. ๊ธฐ๋ณธ์ ์ธ ๋ฒ„์ „ ์ •๋ณด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. 1. ๊ธฐ๋ณธ ํ”„๋กœ์ ํŠธ ์„ธํŒ… JDK11 Spring Bo

lucete-stellae.tistory.com

#1์— ์ด์–ด์„œ ์ž‘์—…ํ•œ ๋‚ด์šฉ์„ ๊ธฐ์ˆ ํ•ฉ๋‹ˆ๋‹ค.

 

API ์—๋Ÿฌ ์ฒ˜๋ฆฌ

์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ๋ถ€ํ„ฐ ์ •์˜ํ•ด๋ณด์ž

  1. ์„œ๋น„์Šค ๋กœ์ง ๋‚ด์—์„œ Exception ์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ.
  2. ๋กœ์ง ์™ธ ์ ์ธ ๋ถ€๋ถ„ ( ์ž˜๋ชป๋œ URL ํ˜ธ์ถœ, ๋“ฑ ) ์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ.

์œ„ ๋‘๊ฐ€์ง€ ์ผ€์ด์Šค์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋”๋ผ๋„, ๋™์ผํ•œ ํฌ๋งท์œผ๋กœ ๋‚˜๊ฐ€๊ฒŒ๋” ์œ ๋„ํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

์šฐ์„ , ๊ธฐ๋ณธ์ ์œผ๋กœ ์ •์˜ํ•œ ํผ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

{
 "result_status": "success",
 "result_code": 200,
 "result_message": "์š”์ฒญ์— ์„ฑ๊ณตํ•˜์˜€์Šต๋‹ˆ๋‹ค.",
 "result": { ... }
}

 

๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ํฌ๋งท์„ ์œ„ํ•œ ์‘๋‹ต ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์‘๋‹ต์„ ๋ฆฌํ„ดํ•  ๋•Œ๋Š” return new ResponseEntity<ApiResFormat>(new ApiResFormat(),HttpStatus.OK); ๊ณผ ๊ฐ™์ด ๋ฆฌํ„ดํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

package rest.api.sample.response;

import java.util.HashMap;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class ApiResFormat {
    // result_status -> fail / success
    private String result_status;
    // result_code -> 200 / 4XX / 5XX
    private int result_code;
    // result_message -> NOT ACCEPTED , EXCEPTION ...
    private String result_message;
    // result -> result obj
    private HashMap<String, Object> result = new HashMap<String, Object>();

    /* ๊ฒฐ๊ณผ ์˜ˆ์‹œ */
    /* 
    {
        "result_status": "success",
        "result_code": 200,
        "result_message": "์š”์ฒญ์— ์„ฑ๊ณตํ•˜์˜€์Šต๋‹ˆ๋‹ค.",
        "result": {
            ...
        }
    } 
    */

    public ApiResFormat() {

    }

    public ApiResFormat(String status, int resultCode, String resultMessage, Object resultObj) {
        this.result_status = status;
        this.result_code = resultCode;
        this.result_message = resultMessage;
        result.put("data", resultObj);
    }

    public ApiResFormat(int resultCode, Object resultObj) {
        this.result_status = "success";
        this.result_code = resultCode;
        this.result_message = "์š”์ฒญ์— ์„ฑ๊ณตํ•˜์˜€์Šต๋‹ˆ๋‹ค.";
        result.put("data", resultObj);
    }

    public ApiResFormat(int resultCode, String resultMessage, Object resultObj) {
        this.result_status = "success";
        this.result_code = resultCode;
        this.result_message = resultMessage;
        result.put("data", resultObj);
    }

    public ApiResFormat(String resultMessage, Object resultObj) {
        this.result_status = "success";
        this.result_code = 200;
        this.result_message = resultMessage;
        result.put("data", resultObj);
    }

    public ApiResFormat(Object resultObj) {
        this.result_status = "success";
        this.result_code = 200;
        this.result_message = "์š”์ฒญ์— ์„ฑ๊ณตํ•˜์˜€์Šต๋‹ˆ๋‹ค.";
        result.put("data", resultObj);
    }
}

์‹คํŒจ์˜ ๊ฒฝ์šฐ์—๋Š” ์‹คํŒจ์— ๋งž๊ฒŒ ๋ฐ”๊ฟ” ์ค„ ์ƒ๊ฐ์ด๋‹ค. ์ด์ œ 1๋ฒˆ ์ผ€์ด์Šค์˜ ๊ฒฝ์šฐ์—๋Š” ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•  ๊ฒƒ์ธ๊ฐ€์— ๋Œ€ํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

@RestControllerAdvice, @ExceptionHandler

RestControllerAdvice ๋Š” ์ปจํŠธ๋กค๋Ÿฌ ๋‹จ์˜ ์„ค์ •๋“ค์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ControllerAdvice ์˜ ํ™•์žฅ ๋ฒ„์ „์œผ๋กœ ์‘๋‹ต ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ํŠน์ง•์ด๊ณ , ExceptionHandler ๋Š” Controller ๋‹จ์—์„œ ๋ฐœ์ƒํ•˜๋Š” Exception์„ ํ•ธ๋“ค๋ง ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. 

๋”ฐ๋ผ์„œ, ์—”๋“œํฌ์ธํŠธ ์ปจํŠธ๋กค๋Ÿฌ ๋‹จ์—์„œ ๋ฐœ์ƒํ•˜๋Š” Exception์„ ์บ์น˜ํ•˜์—ฌ ์›ํ•˜๋Š” ํฌ๋งท์œผ๋กœ ๋ฆฌํ„ดํ•˜๊ธฐ ์œ„ํ•ด ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ • ํ–ˆ์Šต๋‹ˆ๋‹ค.

package rest.api.sample.error;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import rest.api.sample.response.ApiResFormat;

@RestControllerAdvice
public class GlobalExceptionHandler extends RuntimeException {

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ApiResFormat> globalException(Exception ex) {
        return new ResponseEntity<ApiResFormat>(new ApiResFormat("fail", 500, "์š”์ฒญ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค.", null),
                HttpStatus.INTERNAL_SERVER_ERROR);
    }

}

 

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ†ตํ•ด ๋กœ์ง ์ˆ˜ํ–‰ ์ค‘ ๋ฐœ์ƒํ•˜๋Š” Exception ์„ ์บ์น˜ํ•˜์—ฌ ๋ฆฌํ„ดํ•ด์ค„ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€, ๋กœ์ง ์™ธ ์—๋Ÿฌ ์ƒํ™ฉ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ์ž…๋‹ˆ๋‹ค.

DefaultErrorAttributes.class

DefaultErrorAttributes ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ๋ฆฌํ„ดํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ์ปจํŠธ๋กค ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ, JSON ํ˜•ํƒœ๋กœ ์—๋Ÿฌ๋ฅผ ๋ฆฌํ„ดํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ๊ทธ ๋‚ด๋ถ€์˜ ํ•ญ๋ชฉ๋“ค์„ ํ•ด๋‹น ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

package rest.api.sample.error;

import java.util.HashMap;
import java.util.Map;

import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;

@Component
public class RestResponseEntityExceptionHandler extends DefaultErrorAttributes {

    /**
     * @apiNote ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ, repsonse ํ†ต์ผ์„ ์œ„ํ•œ ๋ฉ”์†Œ๋“œ
     * @param webRequest
     * @param options
     * @return
     *         result_status = FAIL
     *         result_message = ์ž˜๋ชป๋œ ์š”์ฒญ URL ์ž…๋‹ˆ๋‹ค.
     */
    @Override
    public Map<String, Object> getErrorAttributes(
            WebRequest webRequest, ErrorAttributeOptions options) {
        HashMap<String, Object> result = new HashMap<String, Object>();
        result.put("data", null);
        Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options);
        errorAttributes.put("result_status", "fail");
        errorAttributes.put("result_code", Integer.parseInt(errorAttributes.get("status").toString()));
        errorAttributes.put("result_message", "ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ์ ‘๊ทผ ์ž…๋‹ˆ๋‹ค.");
        errorAttributes.put("result", result);
        errorAttributes.remove("timestamp");
        errorAttributes.remove("status");
        errorAttributes.remove("message");
        errorAttributes.remove("error");
        errorAttributes.remove("path");
        if (errorAttributes.containsKey("trace"))
            errorAttributes.remove("trace");
        return errorAttributes;
    }

}

 

๋”ฐ๋ผ์„œ, ์œ„ ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ํ•„์š”ํ•œ ํ•ญ๋ชฉ์€ ์ถ”๊ฐ€ํ•˜๊ณ  ๋ณด์—ฌ์ค„ ํ•„์š”๊ฐ€ ์—†๋Š” trace ํ•ญ๋ชฉ์ด๋‚˜ ๋‹ค๋ฅธ ํ•ญ๋ชฉ๋“ค์€ ์ œ์™ธ๋ฅผ ์‹œ์ผœ ์—๋Ÿฌ ๋ฐœ์ƒ์‹œ response ํ˜•ํƒœ๋ฅผ ์ตœ๋Œ€ํ•œ ํ†ต์ผ ์‹œํ‚ค๋„๋ก ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ๋ฆฌํ„ด ๊ฐ์ฒด

 

 

728x90
๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€