백엔드/Spring

예외 처리 위치에 대한 고민

삐삐에스 2024. 7. 24. 16:49

 

네가 싼 똥, 네가 치워.

 

 

오늘은 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