🚨 REST API μ˜ˆμ™Έμ²˜λ¦¬ 정리

EthAnalogΒ·2025λ…„ 8μ›” 21일

Spring Boot

λͺ©λ‘ 보기
15/16
post-thumbnail

βœ… μ–΄λ–€ κΈ°λŠ₯을 λ§Œλ“œλŠ” 것인가?

  • λ°μ΄ν„°λ§Œ μ£Όκ³ λ°›λŠ” REST APIμ—μ„œ λ°œμƒν•˜λŠ” λ‹€μ–‘ν•œ μ—λŸ¬(μž…λ ₯ 였λ₯˜, νƒ€μž… 뢈일치, μ„œλ²„ λ‚΄λΆ€ 였λ₯˜)λ₯Ό μΌκ΄€λœ 포맷과 μƒνƒœμ½”λ“œλ‘œ 응닡
  • νƒ€μž„λ¦¬ν”„(λ·° λ Œλ”λ§) ν™˜κ²½μ˜ error.html λŒ€μ‹ , JSON/문자 응닡 + HTTP Status둜 처리

πŸ‘‰ μ™œ 이걸 λ°°μ›Œμ•Ό ν•˜μ§€?

  • ν”„λ‘ νŠΈλŠ” HTML이 μ•„λ‹ˆλΌ μƒνƒœμ½”λ“œ + λ©”μ‹œμ§€λ₯Ό 보고 λΆ„κΈ°ν•œλ‹€.
  • μ˜ˆμ™Έλ₯Ό 쀑앙집쀑 μ²˜λ¦¬ν•˜λ©΄ 쀑볡 μ½”λ“œλ₯Ό μ œκ±°ν•˜κ³ , 운영 이슈λ₯Ό λΉ λ₯΄κ²Œ νŒŒμ•…ν•  수 μžˆλ‹€.
  • λ©΄μ ‘/싀무 단골: ResponseEntity, @ExceptionHandler, @ControllerAdvice, HTTP status 섀계

πŸ“š κ°œλ… 정리

κ°œλ…μ„€λͺ…
try { } catch블둝 λ‚΄λΆ€μ—μ„œ μ˜ˆμ™Έκ°€ λ‚˜λ©΄ catch둜 λΆ„κΈ°ν•˜μ—¬ 볡ꡬ/응닡 처리
throws Exceptionλ©”μ„œλ“œκ°€ μ˜ˆμ™Έλ₯Ό 던질 수 μžˆμŒμ„ μ„ μ–Έ(둜컬 처리 없을 λ•Œ 경고용)
ResponseEntity<T>μƒνƒœμ½”λ“œ + 헀더 + λ°”λ””λ₯Ό ν•¨κ»˜ μ œμ–΄ν•˜λŠ” 응닡 μ»¨ν…Œμ΄λ„ˆ
@ExceptionHandlerν•΄λ‹Ή 컨트둀러 클래슀 λ²”μœ„μ—μ„œ λ°œμƒν•œ μ˜ˆμ™Έλ₯Ό ν•œ κ³³μ—μ„œ 처리
@ControllerAdviceμ „μ—­(λͺ¨λ“  컨트둀러) μ˜ˆμ™Έ 처리. μŠ€μ½”ν”„λ₯Ό νŒ¨ν‚€μ§€/μ–΄λ…Έν…Œμ΄μ…˜ κΈ°μ€€μœΌλ‘œ μ œν•œ κ°€λŠ₯
HTTP Status2xx 성곡, 4xx ν΄λΌμ΄μ–ΈνŠΈ 였λ₯˜, 5xx μ„œλ²„ 였λ₯˜ λ“± 응닡 의미 체계

νƒ€μž„λ¦¬ν”„ μ‚¬μš© μ‹œ μ„œλ²„ μ—λŸ¬ β†’ templates/error.html둜 μžλ™ 이동(λ·° 응닡).
REST APIλŠ” λ·°λ₯Ό μ—΄μ§€ μ•ŠμœΌλ―€λ‘œ μƒνƒœμ½”λ“œ/문자(JSON)둜 응닡해야 함.


βš™οΈ κ΅¬ν˜„ 흐름 및 μ½”λ“œ (μ‹€μŠ΅ μˆœμ„œ)

1) κ°€μž₯ λ‹¨μˆœν•œ 둜컬 처리: try / catch

@GetMapping("/detail/{id}")
@ResponseBody
public String detail() {
    try {
        // μ—λŸ¬ κ°€λŠ₯ μ½”λ“œ
        throw new Exception("μ΄λŸ°μ €λŸ°μ—λŸ¬");
    } catch (Exception e) {
        System.out.println(e.getMessage()); // μ„œλ²„ 둜그
        return "μ—λŸ¬λ‚¨ γ……γ„±";                // λ‹¨μˆœ λ¬Έμžμ—΄ 응닡
    }
}
  • μž₯점: λΉ λ₯΄κ³  지역적
  • 단점: 쀑볡/λ‚œλ¦½. μƒνƒœμ½”λ“œ μ œμ–΄ λΆˆκ°€(문자만 응닡)

2) μƒνƒœμ½”λ“œκΉŒμ§€ λͺ…μ‹œ: ResponseEntity

@GetMapping("/detail/{id}")
public ResponseEntity<String> detail() {
    try {
        // ...
        throw new Exception("μ΄λŸ°μ €λŸ°μ—λŸ¬");
    } catch (Exception e) {
        return ResponseEntity
                .status(400)                     // HttpStatus.BAD_REQUEST 도 κ°€λŠ₯
                .body("잘λͺ»λœ μš”μ²­: " + e.getMessage());
    }
}
  • ν”„λ‘ νŠΈκ°€ status둜 λΆ„κΈ° κ°€λŠ₯ β†’ ν˜‘μ—… μΉœν™”
  • 정상 응닡도 ResponseEntity.ok(body)처럼 톡일해두면 μ’‹μŒ

자주 μ“°λŠ” μƒνƒœμ½”λ“œ: 200 OK, 201 Created, 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 500 Internal Server Error


3) 컨트둀러 λ‹¨μœ„ 곡톡 처리: @ExceptionHandler

@Controller
public class ItemController {

    @GetMapping("/items/{id}")
    public ResponseEntity<String> get(@PathVariable Long id) {
        if (id < 0) throw new IllegalArgumentException("음수 ID λΆˆκ°€");
        return ResponseEntity.ok("OK");
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handle(Exception e) {
        return ResponseEntity.status(400).body("컨트둀러 곡톡 μ—λŸ¬: " + e.getMessage());
    }
}
  • 같은 컨트둀러 λ‚΄ APIλ“€μ˜ μ˜ˆμ™Έλ₯Ό ν•œ λ²ˆμ— 처리
  • 더 ꡬ체적인 μ˜ˆμ™Έ νƒ€μž…μ„ μ—¬λŸ¬ 개 λ©”μ„œλ“œλ‘œ λ‚˜λˆ μ„œ λ§€ν•‘ κ°€λŠ₯

4) μ „μ—­ 곡톡 처리: @ControllerAdvice

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<String> handleBadRequest(IllegalArgumentException e) {
        return ResponseEntity.status(400).body("잘λͺ»λœ μš”μ²­: " + e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGeneral(Exception e) {
        return ResponseEntity.status(500).body("μ„œλ²„ 였λ₯˜: μž μ‹œ ν›„ λ‹€μ‹œ μ‹œλ„ν•΄μ£Όμ„Έμš”");
    }
}
  • λͺ¨λ“  μ»¨νŠΈλ‘€λŸ¬μ— μ μš©λ˜λŠ” μ „μ—­ μ˜ˆμ™Έ 처리기
  • μš°μ„ μˆœμœ„: 둜컬 @ExceptionHandler > μ „μ—­ @ControllerAdvice

싀무 팁

  • 곡톡 μ—λŸ¬ 응닡 DTO(예: {code, message, path, timestamp})λ₯Ό λ§Œλ“€μ–΄ ν˜•μ‹ 톡일
  • λ‘œκΉ…μ€ Slf4j/Logback으둜 남기고, 민감정보 응닡 κΈˆμ§€

5) νƒ€μž… 뢈일치 λ“± νŠΉμ • μ˜ˆμ™Έ 핸듀링

import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

@ControllerAdvice
public class SpecificHandler {
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseEntity<String> mismatch(MethodArgumentTypeMismatchException e) {
        return ResponseEntity.status(400).body("νŒŒλΌλ―Έν„° νƒ€μž… 였λ₯˜");
    }
}
  • 예: /detail/abc β†’ Long λ³€ν™˜ μ‹€νŒ¨ μ‹œ λ§€ν•‘λ˜λŠ” μ˜ˆμ™Έ

πŸ’‘ 이런 곳에 ν™œμš©ν•  수 μžˆμ–΄μš”

  • REST 기반 μƒν’ˆ/κ²Œμ‹œκΈ€/νšŒμ› API μ „λ°˜
  • μœ νš¨μ„± 검증 μ‹€νŒ¨, κΆŒν•œ λΆ€μ‘±, λ¦¬μ†ŒμŠ€ μ—†μŒ(404), μ„œλ²„ μž₯μ• 
  • 곡톡 μ—λŸ¬ 포맷 제곡으둜 ν”„λ‘ νŠΈ/μ•±/μ™ΈλΆ€ νŒŒνŠΈλ„ˆμ™€ μ•ˆμ •μ  톡신

✍️ 개인 정리 및 회고

  • νƒ€μž„λ¦¬ν”„ ν™˜κ²½μ˜ error.html은 λ·° 응닡일 뿐, RESTμ—λŠ” λ§žμ§€ μ•ŠλŠ”λ‹€.
  • 응닡은 μƒνƒœμ½”λ“œκ°€ μ–Έμ–΄λ‹€. 2xx/4xx/5xxλ₯Ό μ •ν™•νžˆ λ‚˜λˆ λΌ.
  • μ „μ—­ μ˜ˆμ™Έ μ²˜λ¦¬μ™€ 곡톡 응닡 포맷을 λ„μž…ν•˜λ‹ˆ μ½”λ“œκ°€ 단정해진닀.

πŸ”‘ 였늘 배운 핡심 3쀄 μš”μ•½

  1. REST API μ˜ˆμ™ΈλŠ” λ·° λŒ€μ‹  ResponseEntity(μƒνƒœμ½”λ“œ+λ©”μ‹œμ§€) 둜 응닡
  2. 곡톡 μ²˜λ¦¬λŠ” @ExceptionHandler β†’ @ControllerAdvice 순으둜 ν™•μž₯
  3. μƒνƒœμ½”λ“œ κ·œμΉ™(200/201/400/401/403/404/500)을 νŒ€ ν‘œμ€€μœΌλ‘œ κ³ μ •

0개의 λŒ“κΈ€