Spring Exception

์ด์ข…์ฐฌยท2023๋…„ 2์›” 13์ผ
0
post-custom-banner

๐Ÿ“– Spring Exception

์Šคํ”„๋ง์—์„œ ์ œ๊ณตํ•˜๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์˜ˆ์™ธ ์ฒ˜์น˜ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๋ คํ•ฉ๋‹ˆ๋‹ค.

1. @ControllerAdvice

๋ชจ๋“  ์ปจํŠธ๋กค๋Ÿฌ์— ๋Œ€ํ•ด ์ „์—ญ์ ์œผ๋กœ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ค‘์•™ ์ง‘์ค‘์‹ ์ ‘๊ทผ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด Global ์˜ˆ์™ธ์ฒ˜๋ฆฌ, ํŠน์ • package/Controller๋ฅผ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

2. @ExceptionHandler

ํŠน์ • ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์ •ํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ํŠน์ • Controller์˜ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ• ์ค‘ ์ผ๋ถ€์ด๋ฉฐ ์ ํ•ฉํ•œ ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿค” ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š”?

1. ์ค‘์•™ ์ง‘์ค‘์‹ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ

์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ์˜ˆ์™ธ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•œ ์ค‘์•™ ์ง‘์ค‘์‹ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๊ณ  ๋กœ์ง์„ ๋ถ„๋ฆฌ์‹œ์ผœ ์ •์˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ ํ•˜์ง€ ์•Š๊ณ  ์ผ๊ด€์ ์ธ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ์‘๋‹ต, ๋ณด๋‹ค ์‰ฌ์šด ์œ ์ง€ ๊ด€๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. ์˜ˆ์™ธ ์ถ”๊ฐ€ ์ •๋ณด

๋””๋ฒ„๊น… ๋ฐ ๋ฌธ์ œ ํ•ด๊ฒฐ์— ์œ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ณด๋‹ค ์ƒ์„ธํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๋ฏ€๋กœ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋” ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ‘จโ€๐Ÿ’ป ๊ตฌํ˜„

DTO, Controller

class User {
    @NotEmpty
    @Size(min = 1, max = 10)
    private String name;

    @NotNull
    @Min(value = 1)
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
@RestController
@RequestMapping("/api")
public class ApiController {
@PostMapping("/user")
    public User post(@Valid @RequestBody User user) {
        System.out.println(user);
        return user;
    }
}

GlobalControllerAdvice

@RestControllerAdvice
public class GlobalControllerAdvice {

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity exception(Exception e) {
        System.out.println("msg : " + e.getLocalizedMessage());
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body((""));
    }
}

์˜ˆ์™ธ ๊ด€๋ฆฌ์˜ ๋Œ€ํ•œ ๋กœ์ง์„ ๋”ฐ๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด GlobalControllerAdvice ํด๋ž˜์Šค์— @RestControllerAdvice๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ @RestController์— ๋Œ€ํ•œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ๋ช…์‹œํ•ด์ค๋‹ˆ๋‹ค. @RestControllerAdvice์˜ ๋ฒ”์œ„๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ๊ธฐ๋ณธ ๋ฒ”์œ„๋Š” basePackages์ด๋ฉฐ ์›ํ•˜๋Š” ๋งŒํผ ์„ค์ •ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

@RestControllerAdvice

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {

	/**
	 * Alias for the {@link #basePackages} attribute.
	 * <p>Allows for more concise annotation declarations &mdash; for example,
	 * {@code @ControllerAdvice("org.my.pkg")} is equivalent to
	 * {@code @ControllerAdvice(basePackages = "org.my.pkg")}.
	 * @since 4.0
	 * @see #basePackages
	 */
	@AliasFor("basePackages")
	String[] value() default {};
    
    ...
    
}

์˜ˆ์ œ๋ฅผ ๋ณด๋ฉด Controller์— @Valid๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ @NotEmpty, @Min, @NotEmpty์™€ ๊ฐ™์€ ์–ด๋…ธํ…Œ์ด์…˜๋ฅผ ๋ชจ๋ธ ํด๋ž˜์Šค์— ์‚ฌ์šฉํ–ˆ๋‹ค๋Š” ๊ฒƒ์„ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค. ์‹คํ–‰ ๊ฒฐ๊ณผ๋Š” GlobalControllerAdvice์— ์ •์˜ํ•œ ๋‚ด์šฉ์ด ๋‚˜ํƒ€๋‚˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ controller์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ๊ณ  ์–ด๋“œ๋ฐ”์ด์Šค ํด๋ž˜์Šค์— ํ•ด๋‹นํ•˜๋Š” ์˜ˆ์™ธ๊ฐ€ 2๊ฐœ์ด์ƒ์ธ ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”?

@ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e) {
        System.out.println("-----------global MethodArgumentNotValid----------");
        System.out.println("msg : " + e.getLocalizedMessage());
        System.out.println("---------------------------");
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
    }

ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ์ด์ „์— ๊ตฌํ˜„ํ•œ Exception.class ๋ณด๋‹ค ๋” ์ƒ์„ธํ•œ ์˜ˆ์™ธ์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ์ฝ”๋“œ๊ฐ€ ์ž‘๋™๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด GlobalControllerAdvice์— ์ž‘์„ฑํ•œ ์˜ˆ์™ธ์™€ Controller์— ์ž‘์„ฑํ•œ ์˜ˆ์™ธ๊ฐ€ ๊ฐ™๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”?

์œ„์˜ ์ฝ”๋“œ๋ฅผ ์ปจํŠธ๋กค๋Ÿฌ์— ๊ทธ๋Œ€๋กœ ์ž‘์„ฑํ•˜๊ฒŒ ๋˜๋ฉด ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ž‘์„ฑ๋œ ์ฝ”๋“œ๋งŒ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์šฐ์„ ์ˆœ์œ„๋Š” ์ปจํŠธ๋กค๋Ÿฌ์— ์ž‘์„ฑํ•œ ์˜ˆ์™ธ๊ฐ€ ๋จผ์ €๋ผ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์˜ˆ์™ธ๋ฅผ ์‹คํ–‰ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์™ธ๋ถ€์— ์žˆ๋Š” ๊ฐ™์€ ์ฝ”๋“œ๋Š” ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

โœ… ์š”์•ฝ

  • ์Šคํ”„๋ง์—์„œ ์ œ๊ณตํ•˜๋Š” ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ํ”„๋กœ์„ธ์Šค๋Š” ์ค‘์•™ ์ง‘์ค‘ํ˜•์œผ๋กœ ์žฌ์‚ฌ์šฉ๊ณผ ๋กœ์ง ๋ถ„๋ฆฌ๋ฅผ ํ•˜์—ฌ ์ฝ”๋“œ ํ’ˆ์งˆ์„ ๋†’์—ฌ์ค๋‹ˆ๋‹ค.
  • ํ•˜๋‚˜์˜ ๋ฉ”์„œ๋“œ์— ํ•ด๋‹น ๋˜๋Š” ์˜ˆ์™ธ๊ฐ€ 2๊ฐœ๋ผ๋ฉด ๊ฐ€์žฅ ๊ทผ์ ‘ํ•œ ์˜ˆ์™ธ๋งŒ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • Advice์™€ Controller์—์„œ ๊ฐ™์€ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค๋ฉด ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋ฉฐ, Advice์— ์žˆ๋Š” ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ฝ”๋“œ๊ฐ€ ๊ฐ™์•„๋„ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
profile
์™œ? ๋ผ๋Š” ์งˆ๋ฌธ์ด ์‚ฌ๋ผ์งˆ ๋•Œ๊นŒ์ง€
post-custom-banner

0๊ฐœ์˜ ๋Œ“๊ธ€