-
[Spring Boot] REST API의 예외처리 방법 / Service 레이어로 분리Spring | SpringBoot 2024. 8. 16. 17:02
[ REST API의 예외처리 방법 ]
타임리프 쓰는경우엔 에러시 자동으로 error.html로 이동해주기 때문에 이게 에러처리 끝이지만,
서버에서 데이터만 보내주는 REST API들을 만드는경우에는 html 에러페이지를 보내봤자 아무 의미가 없습니다.
( 서버와 유저가 데이터만 주고받는 관계라서 html 보내봤자 열지도 못함 )
- try catch
"try 안에 있는 코드에서 에러가 나면 catch 안에 있는 코드 실행해주세요~" 라는 뜻의 간단한 문법
@GetMapping("/detail/{id}") @ResponseBody String detail() { try { throw new Exception("이런저런에러"); } catch(Exception e){ System.out.println(e.getMessage()); return "에러남 ㅅㄱ"; } }
에러를 강제로 발생시켜보고 싶으면 throw new Exception() 이런 코드 쓰면 강제로 에러발생이 가능
데이터만 주고받는 REST API 만드는 경우엔 에러페이지가 안뜨니 이런거 사용 가능
실제 배포후에는 에러시 이거저거 출력보다는 로깅 라이브러리 쓰는게 낫다고함
- ResponseEntity
REST API에서 에러가 났을 때 문자만 달랑보내기보다는 더 정확하게 에러 코드도 기입해서 보내줌
@GetMapping("/detail/{id}") ResponseEntity<String> detail() { try { throw new Exception("이런저런에러"); } catch(Exception e){ return ResponseEntity.status(에러코드).body("에러이유"); } }
위처럼 ResponseEntity라는걸 쓰면 에러코드는 뭔지, 에러이유는 뭔지 맘대로 작성해서 보내줄 수 있다
.status() 안에 에러코드를 숫자로 입력하면 되는데
- 유저가 잘못해서 에러나는거면 400
- 서버가 잘못해서 에러나는거면 500
- 에러없이 정상작동하는 경우엔 200
을 관습적으로 넣어줌
ResponseEntity.status(HttpStatus.NOT_FOUND).body("에러남");
숫자찾기 귀찮으면 에러코드 모아둔 클래스가 하나 있는데 거기서 점찍어서 뽑아써도 됨
- @ExceptionHandler
같은 클래스에 있는 API 들에서 에러가 나면 @ExceptionHandler 안에 있는 코드를 실행
여기서 return써서 유저에게 메세지보내거나 그러면 된다
@ExceptionHandler(Exception.class) public ResponseEntity<String> exceptionHandler() { return ResponseEntity.status().body(""); }
- @ControllerAdvice
모든 컨트롤러에서 에러가 나는 경우 여기 있는 코드가 대신 실행
@ControllerAdvice public class MyExceptionHandler { @ExceptionHandler(Exception.class) public ResponseEntity<String> handler() { return ResponseEntity.status(400).body("모든 컨트롤러 에러시 발동"); } }
- 특정에러에만 실행하기
@ControllerAdvice public class MyExceptionHandler { @ExceptionHandler(MethodArgumentTypeMismatchException.class) public ResponseEntity<String> handler() { return ResponseEntity.status(400).body("특정 에러시 발동"); } }
MethodArgumentTypeMismatchException 이런 이름의 에러가 발생시에는 다른 코드를 실행하고 싶다면
위처럼 에러 이름을 @ExceptionHandler() 안에 적으면 된다
정리
=======>
Thymeleaf 사용시엔 에러나는거 처리는 error.html 만들면 끝인데
REST API 만들 때는 에러나는거 처리는 @ControllerAdvice 만들어놓는게 편리함
[ Service 레이어로 분리 ]
하나의 함수 안에는 하나의 기능만 담는게 좋고 그래야 나중에 관리가 편해진다
@Controller 안에는 데이터나 html 보내주는 기능을 보통 넣기 때문에
DB 입출력하는 코드들은 다른 함수로 빼는게 좋다
(하나의 클래스에도 비슷한 기능의 함수들만 보관하는게 좋은 관습)
- 다른 클래스의 함수 사용하려면
1. 클래스에 @Service 붙여놓고
2. 이 함수들 쓰고싶은 곳에 가서 변수로 등록하고
3. 원하는 곳에서 변수.함수() 쓰면 된다
// 비즈니스 로직 담는 클래스는 Service라고 부름 @Service @RequiredArgsConstructor public class ItemService { private final ItemRepository itemRepository; public void saveItem(String title, Integer price){ Item item = new Item(); item.setTitle(title); item.setPrice(price); itemRepository.save(item); } }
@Controller @RequiredArgsConstructor public class ItemController { private final ItemRepository itemRepository; private final ItemService itemService; // 함수 하나에는 한가지 기능만 담기 권장 @PostMapping("/add") String addPost(String title, Integer price) { // 아래 코드 ItemService로 옮기기 // Item item = new Item(); // item.setTitle(title); // item.setPrice(price); // itemRepository.save(item); // => service에서 불러와서 사용 itemService.saveItem(title, price); return "redirect:/list"; }
- dependency injection
다른 클래스의 변수, 함수를 사용할 때 new 클래스().함수() 이렇게 매번 쓰는게 아니라
그걸 new 클래스()를 다른데서 미리 뽑은 다음에
그 결과만 파라미터로 집어넣어서 쓰게 만드는걸 dependency injection이라고 부른다
@Autowired는 ItemRepository, ItemService 이름의 타입만 붙여둔 자리에
new ItemRepository(), new ItemService() 알아서 찾아와서 넣으라는 스프링 문법
@Controller // @RequiredArgsConstructor // 아래 ItemRepository 만드려면 써야함 public class ItemController { // public 없으면 같은 폴더 안에서만 사용 가능 // new ItemRepository()하나 뽑아서 itemRepository 변수에 넣으라고 시키는거 private final ItemRepository itemRepository; // 클래스 사용할 곳에서 아래처럼 변수로 등록하기 private final ItemService itemService; // 위에 @RequiredArgsConstructor 안쓰려면 아래처럼 써줘야함 // "Dependency Injection" // 1. object 여러개 안뽑아도 되어서 효율적임 // 2. 클래스간의 커플링을 줄일 수 있음 @Autowired public ItemController(ItemRepository itemRepository, ItemService itemService) { this.itemRepository = itemRepository; this.itemService = itemService; }
- dependency injection 쓰는 이유
장점1. 매번 object를 뽑아쓰지 않아도 되니까 성능상 효율적임
장점2. 클래스간의 연결고리를 줄일 수 있습니다.
- Container, Bean
Container : 스프링이 object를 뽑아서 보관하는 공간
Bean : 거기 안에 들어있는 object들
참고로 @Service, @Component 이런걸 클래스에 붙여놓으면
이 클래스의 object를 자동으로 뽑아서 Container안에 담아놓으라는 뜻
- Service layer 예외처리 방법
throw new Exception() 코드를 실행하면 에러를 강제로 낼 수 있고 소괄호 안에 에러 이유도 맘대로 적을 수 있다
그렇게 해두면 타임리프 쓰는 경우 error.html로 자동 이동하고
REST API의 경우엔 자동으로 @ExceptionHandler 만들어놓은게 동작
@Service @RequiredArgsConstructor public class ItemService { private final ItemRepository itemRepository; public void saveItem(String title, Integer price) { if (price < 0) { throw new RuntimeException("음수 안됩니다."); }else { Item item = new Item(); item.setTitle(title); item.setPrice(price); itemRepository.save(item); }
아니면 에러를 발생시킬 때 더 정확하게 에러코드도 적고 싶으면
Exception() 말고 ResponseStatusException() 이런 것도 사용가능
throw new ResponseStatusException( HttpStatus.NOT_FOUND, "어쩌구 저쩌구해서 404에러남" );
* 이글은 아래 강의를 참고해 작성했습니다.
https://codingapple.com/course/spring-boot-jpa/
쉽게 배우는 Spring Boot & JPA - 코딩애플 온라인 강좌
Next.js는 프론트엔드부터 서버까지 만들 수 있는 React기반 프레임워크입니다. 이것만 사용해도 풀스택 웹개발이 가능합니다. Next.js 사용시 서버사이드 렌더링이 쉽기 때문에 React, Vue만 사
codingapple.com
'Spring | SpringBoot' 카테고리의 다른 글
[Spring Boot] AJAX / 삭제 기능 (0) 2024.08.16 [Spring Boot] 수정기능 (0) 2024.08.16 [Spring Boot] 상세페이지 만들기 / Optional / 예외처리 (0) 2024.08.12 [Spring Boot] 상품 추가기능 / th:fragment / th:replace (0) 2024.08.10 [Spring Boot] JPA / DB 데이터 출력 / HTML에 서버 데이터 넣기 (0) 2024.08.05