반응형

[Spring Boot 실습 #11] 에러 처리(Exception Handling) - 정상 범위의 response code로 반환

 

1. 정상 범위의 response code로 반환

2. 개발환경

    2.1. 개발환경

    2.2. 라이브러리

3. 구현

    3.1. 프로젝트 구조

    3.2. 구현 - 소스코드

4. 결과

    4.1. request param 값에 따라 throw error 결과

    4.2. http method error

 

 

1. 정상 범위의 response code로 반환

http 호출로 에러 발생시 정상 범위로 판단하여 정상범위의 http status code 반환

 

2. 개발환경

 

2.1. 개발환경

MacOS M1 - macOS Monterey 12.0.1

IntelliJ IDEA 2021.2 (Community Edition)

 

2.2. 라이브러리

JDK 11

spring-boot-2.6.6

 

 

3. 구현

3.1. 프로젝트 구조

 

3.2. 구현 - 소스코드

 

[ pom.xml ]

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>io.home.test</groupId>
    <artifactId>spring-boot-exception-translator</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-exception-translator</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
 
</project>

 

 

[ ErrorModel.java ] 반환할 에러 모델

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package io.home.test.base.exception;
 
import java.io.Serializable;
 
public class ErrorModel implements Serializable {
    private static final long serialVersionUID = 1L;
 
    private final String message;
    private final String description;
 
    public ErrorModel(String message) {
        this(message, null);
    }
 
    public ErrorModel(String message, String description) {
        this.message = message;
        this.description = description;
    }
 
    public String getMessage() {
        return message;
    }
 
    public String getDescription() {
        return description;
    }
}

 

[ ErrorMessage.java ] 반환할 에러 모델의 메세지

1
2
3
4
5
6
7
8
9
10
11
12
package io.home.test.base.exception;
 
public final class ErrorMessage {
 
    public static final String ERROR_NOT_FOUND = "error.notFound";
    public static final String ERROR_METHOD_NOT_ALLOWED = "error.methodNotAllowed";
    public static final String ERROR_INTERNAL_SERVER_ERROR = "error.internalServerError";
    public static final String ERROR_BAD_REQUEST = "error.badRequest";
 
    public ErrorMessage() {
    }
}

 

[ BadRequestException.java ] 내부 커스텀 에러 예시: HttpStatus.BAD_REQUEST 대체

1
2
3
4
5
6
7
package io.home.test.base.exception.exceptions;
 
public class BadRequestException extends Exception {
    public BadRequestException(String message) {
        super(message);
    }
}

 

[ NotFoundException.java ] 내부 커스텀 에러 예시: HttpStatus.NOT_FOUND 대체

1
2
3
4
5
6
7
package io.home.test.base.exception.exceptions;
 
public class NotFoundException extends Exception {
    public NotFoundException(String message) {
        super(message);
    }
}

 

[ CustomExceptionTranslator.java ] ExceptionHandler를 활용하여 특정 에러 발생시 지정한 '에러 모델', '에러 메세지', '에러 코드'로 반환

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package io.home.test.base.exception;
 
import io.home.test.base.exception.exceptions.BadRequestException;
import io.home.test.base.exception.exceptions.NotFoundException;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
 
@ControllerAdvice
public class CustomExceptionTranslator {
 
    @ExceptionHandler(NotFoundException.class)
    @ResponseStatus(HttpStatus.ACCEPTED)
    @ResponseBody
    public ErrorModel processNotFoundException(NotFoundException ex) {
        return new ErrorModel(ErrorMessage.ERROR_NOT_FOUND, ex.getMessage());
    }
 
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.ACCEPTED)
    public ErrorModel processMethodNotAllowedException(HttpRequestMethodNotSupportedException exception) {
        return new ErrorModel(ErrorMessage.ERROR_METHOD_NOT_ALLOWED, exception.getMethod());
    }
 
    @ExceptionHandler(BadRequestException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.PARTIAL_CONTENT)
    public ErrorModel processBadRequestException(BadRequestException exception) {
        return new ErrorModel(ErrorMessage.ERROR_BAD_REQUEST, exception.getMessage());
    }
 
}

 

 

[ StatusController.java ] http 호출로 error code 테스트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package io.home.test.base.web;
 
import io.home.test.base.exception.exceptions.BadRequestException;
import io.home.test.base.exception.exceptions.NotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
 
@RequestMapping(value = "/api")
@RestController
public class StatusController {
 
    @GetMapping(value = "")
    public ResponseEntity<?> paramCode(@RequestParam(value = "code", required = false, defaultValue = "0") Integer code) throws NotFoundException, BadRequestException {
        if (code == 0return ResponseEntity.ok().body("required integer parameter 'code' is not present");
        if (code == 200return ResponseEntity.ok().body(HttpStatus.OK.name());
        if (code == 302throw new NotFoundException("code: " + code);
        if (code == 400throw new BadRequestException("code: " + code);
 
        return ResponseEntity.ok().body(HttpStatus.OK.name());
    }
}

 

[ SpringBootExceptionTranslatorApplication.java ] 실행

1
2
3
4
5
6
7
8
9
10
11
12
13
package io.home.test;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
@SpringBootApplication
public class SpringBootExceptionTranslatorApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(SpringBootExceptionTranslatorApplication.class, args);
    }
 
}

 

 

4. 결과

4.1. request param 값에 따라 throw error 결과

4.1.1 200

GET

http://localhost:8080/api?code=200

 

4.1.2 302

GET

http://localhost:8080/api?code=302

 

4.1.3 400

GET

http://localhost:8080/api?code=400

 

 

4.2. http method error

POST

http://localhost:8080/api

 

 

4.3. 결과

  • 임의로 '에러 모델', '에러 메세지', '에러 코드' 반환 확인
  • 에러 처리시 HttpStatus Code를 '정상' 영역으로 처리하기 위해 필요
  • 사용자 정의 response code로 반환하기 위해서는 별도의 ResponseEntity 생성하여 반환 필요

 

반응형