๐[Spring] API์์ ์์ธ ๋์ง๊ธฐ.
Java ๋ฐฑ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์์ API ํธ์ถ ์ค ์์ธ๋ฅผ ๋์ง๋ฉด, ์์ธ๊ฐ ์ ์ ํ๊ฒ ์ฒ๋ฆฌ๋์ง ์๋ ํ ํด๋ผ์ด์ธํธ์ ์ค๋ฅ ์๋ต์ด ๋ฐํ๋ฉ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ์์ธ๊ฐ ๋ฐ์ํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ธ๊ฐ ๋ฐ์ํ ์์ ์์ ์คํ์ ์ค๋จํ๊ณ , ์์ธ์ ๋ํ ์ฒ๋ฆฌ๋ฅผ ํ์ง ์์ผ๋ฉด ๋ด๋ถ ์๋ฒ ์ค๋ฅ(HTTP 500)๋ฅผ ํด๋ผ์ด์ธํธ์ ๋ฐํํ๊ฒ ๋ฉ๋๋ค.
1๏ธโฃ ์์ธ ์ฒ๋ฆฌ ํ๋ฆ.
1. ์์ธ ๋ฐ์.
- API์ ์ปจํธ๋กค๋ฌ๋ ์๋น์ค ๋ ์ด์ด์์ ํน์ ๋ก์ง์ ์ํํ๋ ๋์ค ์์ธ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
- ์์ธ๊ฐ ๋ฐ์ํ๋ฉด ์คํ ํ๋ฆ์ด ์์ธ ์ฒ๋ฆฌ ๋ธ๋ก์ผ๋ก ๋์ด๊ฐ๊ฑฐ๋, ์์ธ๊ฐ ํธ์ถ๋ ๋ฉ์๋๋ฅผ ํตํด ์์๋ก ์ ํ๋ฉ๋๋ค.
2. ์์ธ ์ ํ.
- ๋ง์ฝ ํด๋น ์์ธ๋ฅผ
try-catch
๋ธ๋ก ๋ด์์ ์ฒ๋ฆฌํ์ง ์์ผ๋ฉด ์์ธ๋ ํธ์ถ๋ ๋ฉ์๋๋ฅผ ๋ฐ๋ผ ์์๋ก ์ ํ๋ฉ๋๋ค. - ์ด ๊ณผ์ ์์ ์ต์ข ์ ์ผ๋ก ์ปจํธ๋กค๋ฌ ๋ ๋ฒจ์ด๋ ๊ทธ ์ด์์์ ์์ธ๋ฅผ ์ก์ง ์์ผ๋ฉด, Spring Framework์ ๊ฐ์ ๋ฐฑ์๋ ํ๋ ์์ํฌ๊ฐ ์์ธ๋ฅผ ๋ฐ์ ์ฒ๋ฆฌํ๊ฒ ๋ฉ๋๋ค.
3. Spring์ ๊ธฐ๋ณธ ๋์.
- Spring MVC์์๋ ์์ธ๊ฐ ์ ํ๋์ด ์ปจํธ๋กค๋ฌ๊น์ง ๋๋ฌํ๊ณ ๋ ์ฒ๋ฆฌ๋์ง ์์ผ๋ฉด, Spring์ด
DefaultHandlerExceptionResolver
๋ฅผ ํตํด ๊ธฐ๋ณธ์ ์ธ ์์ธ ์ฒ๋ฆฌ๋ฅผ ์ํํฉ๋๋ค. - ์ฒ๋ฆฌ๋์ง ์์ ์์ธ๋ ๊ธฐ๋ณธ์ ์ผ๋ก HTTP ์ํ ์ฝ๋ 500(๋ด๋ถ ์๋ฒ ์ค๋ฅ)๋ก ๋งคํ๋์ด ํด๋ผ์ด์ธํธ์ ๋ฐํ๋ฉ๋๋ค.
- ํด๋ผ์ด์ธํธ๋ ์ด ์ํ ์ฝ๋๋ฅผ ํตํด ์๋ฒ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ์์ ์ธ์ํ๊ฒ ๋ฉ๋๋ค.
2๏ธโฃ ์์ธ ์ฒ๋ฆฌ๋ฅผ ์ํ ๋ฐฉ๋ฒ.
1. @ExceptionHandler
์ฌ์ฉ.
- Spring MVC์์๋ ์ปจํธ๋กค๋ฌ ๋ ๋ฒจ์์ ์์ธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด
@ExceptionHandler
์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. - ํน์ ์์ธ๊ฐ ๋ฐ์ํ์ ๋ ํด๋น ๋ฉ์๋๊ฐ ํธ์ถ๋์ด ์์ธ๋ฅผ ์ฒ๋ฆฌํ๊ณ , ์ ์ ํ ์๋ต์ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐํํ ์ ์์ต๋๋ค.
@RestController
public class MyController {
@GetMapping("/example")
public String example() {
if (someConditionFails()) {
throw new MyCustomException("Something went wrong!");
}
return "success";
}
@ExceptionHandler(MyCustomException.class)
public ResponseEntity<String> handleMyCustomException(MyCustomException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
์ด ๊ฒฝ์ฐ MyCustomException
์ด ๋ฐ์ํ๋ฉด HTTP ์ํ ์ฝ๋ 400๊ณผ ํจ๊ป ์์ธ ๋ฉ์์ง๊ฐ ๋ฐํ๋ฉ๋๋ค.
2. @ControllerAdvice
์ฌ์ฉ.
-
@ControllerAdvice
๋ ์ ์ญ ์์ธ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ์ ์ํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. - ์ด๋ฅผ ํตํด ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์ ๋ฐ์ํ๋ ์์ธ๋ฅผ ์ค์์์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MyCustomException.class)
public ResponseEntity<String> handleMyCustomException(MyCustomException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleGeneralException(Exception ex) {
return new ResponseEntity<>("An unexpected error occurred.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
์ด ๊ฒฝ์ฐ ํน์ ์์ธ๋ฟ๋ง ์๋๋ผ ๋ชจ๋ ์ผ๋ฐ์ ์ธ ์์ธ๋ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
3. ResponseEntity์ ํจ๊ป ์์ธ๋ฅผ ๋ฐํ.
-
ResponseEntity
๋ฅผ ์ฌ์ฉํ์ฌ ์ง์ ์์ธ ๋ฐ์ ์ HTTP ์๋ต๊ณผ ์ํ ์ฝ๋๋ฅผ ๋ฐํํ ์๋ ์์ต๋๋ค.
@GetMapping("/example")
public ResponseEntity<String> example() {
if (someConditionFails()) {
return new ResponseEntity<>("Custom error message", HttpStatus.BAD_REQUEST);
}
return new ResponseEntiry<>("success", HttpStatus.OK);
}
3๏ธโฃ ์ค์ ์ฝ๋ ์ฌ๋ก.
// UserUpdateRequest - DTO
public class UserUpdateRequest {
private long id;
private String name;
public long getId() {
return id;
}
public String getName() {
return name;
}
}
// UserController - Controller
@PutMapping("/user")
public void updateUser(@RequestBody UserUpdateRequest request) {
String sql = "UPDATE user SET name = ? WHERE id = ?";
jdbcTemplate.update(sql, request.getName(), request.getId());
}
@DeleteMapping("/user")
public void deleteUser(@RequestParam String name) {
String sql = "DELETE FROM user WHERE name = ?";
jdbcTemplate.update(sql, name);
}
1. ๋ฌธ์ ์ํฉ.
- ์ ์ฝ๋์์์ ๋ฌธ์ ์ํฉ์ ์๋ ์ ์ ๋ฅผ ์ ๋ฐ์ดํธ ํ๊ฑฐ๋ ์ญ์ ํ๋ ค ํด๋ 200 OK๊ฐ ๋์จ๋ค๋ ์ ์ ๋๋ค.
2. ํด๊ฒฐ ๋ฐฉ์.
- API์์ ์์ธ๋ฅผ ๋์ 500 Internal Server Error์ด ๋์ค๊ฒ ํ๋ฉด๋ฉ๋๋ค.
@GetMapping("/user/error-test")
public void errorTest() {
throw new IllegalArgumentException();
}
- POSTMAN์ ํ์ฉํ์ฌ
http://localhost:8080/user/error-test
๋ก GET ํธ์ถ์ ๋ณด๋ด๋ฉด 500 Internal Server Error์ด ๋ฐ์ํฉ๋๋ค.
4๏ธโฃ ํ์ฉ ์์.
// UserUpdateRequest - DTO
public class UserUpdateRequest {
private long id;
private String name;
public long getId() {
return id;
}
public String getName() {
return name;
}
}
// UserController - Controller
@PutMapping("/user")
public void updateUser(@RequestBody UserUpdateRequest request) {
String readSql = "SELECT * FROM user WHERE id = ?";
boolean isUserExist = jdbcTemplate.query(readSql, (rs, rowNum) -> 0 request.getId()).isEmpty();
if (isUserNotExist) {
throw new IllegalArgumentException();
}
String sql = "UPDATE user SET name = ? WHERE id = ?";
jdbcTemplate.update(sql, request.getName(), request.getId());
}
@DeleteMapping("/user")
public void deleteUser(@RequestParam String name) {
String readSql = "SELECT * FROM user WHERE name = ?";
boolean isUserExist = jdbcTemplate.query(readSql (rs, rowNum) -> 0, name).isEmpty();
if (isUserNotExist) {
throw new IllegalArgumentException();
}
String sql = "DELETE FROM user WHERE name = ?";
jdbcTemplate.update(sql, name);
}
- ์์ ๊ฐ์ด API์์ ๋ฐ์ดํฐ ์กด์ฌ ์ฌ๋ถ๋ฅผ ํ์ธํด ์์ธ๋ฅผ ๋์ง๋ฉด ๋ฉ๋๋ค.
String readSql = "SELECT * FROM user WHERE id = ?";
- id๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ์ ๊ฐ ์กด์ฌํ๋์ง ํ์ธํ๊ธฐ ์ํด SELECT ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ์ต๋๋ค.
boolean isUserExist = jdbcTemplate.query(readSql, (rs, rowNum) -> 0 request.getId()).isEmpty();
- SQL์ ๋ ๋ ค DB์ ๋ฐ์ดํฐ๊ฐ ์๋์ง ํ์ธํฉ๋๋ค.
- SELECT SQL์ ๊ฒฐ๊ณผ๊ฐ ์์ผ๋ฉด 0์ผ๋ก ๋ณํ๋ฉ๋๋ค.
- ์ต์ข
์ ์ผ๋ก List๋ก ๋ฐํ๋ฉ๋๋ค.
- ์ฆ, ํด๋น id๋ฅผ ๊ฐ์ง ์ ์ ๊ฐ ์์ผ๋ฉด 0์ด ๋ด๊ธด List๊ฐ ๋์ค๊ณ , ์๋ค๋ฉด ๋น List๊ฐ ๋์ค๊ฒ ๋ฉ๋๋ค.
- jdbcTemplate.query()์ ๊ฒฐ๊ณผ์ธ List๊ฐ ๋น์ด์๋ค๋ฉด, ์ ์ ๊ฐ ์๋ค๋ ๋ป์ ๋๋ค.
if (isUserNotExist) {
throw new IllegalArgumentException();
}
- ๋ง์ฝ ์ ์ ๊ฐ ์กด์ฌํ์ง ์๋๋ค๋ฉด IllegalArgumentException์ ๋์ง๋๋ค.