네가 싼 똥, 네가 치워.
오늘은 Spring에서 예외가 발생했을 때 어디에서 이 예외를 처리해야할지에 대해 글을 써보려고 한다.
사실 이 주제는 답이 정해져있지 않기 때문에 사람마다 생각이 다르고 각자의 논리도 다를 것이다.
나 또한 어떠한 정답을 제시하기 보다는 나의 논리를 제시해보려고 한다.
이 글을 보면서 여러분도 각자의 논리를 세워 더 합리적인 정답을 유추해보면 좋을 것 같다.
나는 예외가 터진 곳에서 예외 처리를 해야한다고 생각한다.
예시 코드로 살펴보자.
호텔을 조회하는 API가 있다. 이를 스프링 3계층으로 나누어 코드를 작성해봤다.
@RestController
public class HotelController {
private final HotelService hotelService;
@Autowired
public HotelController(HotelService hotelService) {
this.hotelService = hotelService;
}
@RequestMapping(value = "/hotels/{id}", method = RequestMethod.GET)
public ResponseEntity<HotelDetailResponse> getHotel(@PathVariable int id) {
return new ResponseEntity<>(hotelService.getHotel(id), HttpStatus.OK);
}
}
@Repository
public class HotelService {
private final HotelRepository hotelRepository;
@Autowired
public HotelService(HotelRepository hotelRepository) {
this.hotelRepository = hotelRepository;
}
public HotelDetailResponse getHotel(int hotelId) {
Hotel hotel = hotelRepository.findById(hotelId);
return new HotelDetailResponse().from(hotel);
}
}
@Repository
public class HotelRepository {
Map<Integer, Hotel> trip_table = new HashMap<>();
int idx = 1;
public Hotel findById(int id) {
return trip_table.get(id);
}
}
여기서 내가 만약에 trip_table에 없는 id로 호텔을 조회하려고 한다면 어떤 문제가 생길까?
Postman에서 해당 API를 호출해보겠다.

500 에러가 뜨는 것을 확인해볼 수 있다.
만약 이런 에러를 받게 된다면 어떤 에러인지 뭐가 잘못된 것인지 알기 어렵다.
이런 문제를 개선하기 위해서 우리는 예외 처리를 해주어야 한다.
여기서 문제는 HotelRepository에서 findById(int id)를 한 결과가 null이기 때문이다.
null인 객체에게서 getName()을 하려고 하니 NPE(NullPointerException)가 터져버렸다.
나는 예외 처리를 할 때, 각 클래스의 책임과 역할을 고려하는 것이 중요하다고 생각한다.
따라서 나는 이 문제는 DB에서 이상한 값을 가져온 Repository의 책임이 크다고 생각했고, 따라서 Repository에서 null에 대한 처리를 해야한다고 생각했다.
Repository에서 처리를 하기 위해 우리는 다양한 예외 처리 방법에 대해 생각해볼 수 있다.
대표적으로는 try-catch, throw, throws가 있다.
나는 여기서 if-else와 throw를 써보겠다.
@Repository
public class HotelRepository {
Map<Integer, Hotel> trip_table = new HashMap<>();
int idx = 1;
public Hotel findById(int id) {
Hotel foundHotel = trip_table.get(id);
if (foundHotel == null) throw new NoSuchElementException("그런 호텔 없으세요");
else return trip_table.get(id);
}
}
여기서 보면 나는 먼저 trip_table에서 Hotel객체를 가져오고, 만약 이 객체가 null이라면 NoSuchElementException을 던져버렸다.
이제 다시 이 API를 호출해보자.

그러면 위처럼 내가 설정한 NoSuchElementException과 함께 "그런 호텔 없으세요"라는 메시지가 출력된다.
이런 에러를 보고 우리는 '아~ 내가 없는 호텔을 조회하려고 했구나"라고 바로 인지할 수 있다.
나는 각 계층과 클래스의 책임과 역할을 인지하고 각 역할에 맞게 예외 처리를 해줘야 한다고 생각한다.
따라서 비즈니스 로직과 관련한 예외는 Service 계층에서, DB와 관련한 예외는 Repository 계층에서 처리하는 것이 좋다고 본다!
'백엔드 > Spring' 카테고리의 다른 글
| Entity와 영속화 (2) | 2024.07.29 |
|---|---|
| 예외 처리를 위한 어노테이션 (0) | 2024.07.25 |
| DTO에 대해 (1) | 2024.07.22 |
| 직렬화 vs 역직렬화 (1) | 2024.07.19 |
| 필드, Setter가 아닌 생성자로 의존성을 주입받는 이유 (0) | 2024.07.19 |