주의

  • 본 게시글은 Claude로 작성되었습니다. 잘못된 정보가 있을 수 있습니다.

개요

이 문서에서는 Java 개발에서 흔히 볼 수 있는 관행 중 하나인 “모든 메서드에 throws Exception을 사용하는 것”의 문제점에 대해 살펴보겠습니다. 이러한 방식이 왜 적절하지 않은지, 그리고 더 나은 예외 처리 방법은 무엇인지 알아보겠습니다.

상세 설명

throws Exception의 의미

Java에서 throws Exception은 메서드가 예외를 발생시킬 수 있음을 선언하는 방식입니다. 이는 메서드 내에서 발생할 수 있는 모든 종류의 체크 예외(Checked Exception)를 호출자에게 전파할 수 있다는 것을 의미합니다.

모든 메서드에 throws Exception을 사용하는 것의 문제점

  1. 예외 정보의 구체성 상실

    • Exception은 모든 예외의 최상위 클래스입니다. 이를 사용하면 어떤 구체적인 예외가 발생할 수 있는지 알 수 없습니다.
    • 예외 처리의 목적 중 하나는 특정 상황에 대해 적절한 대응을 하는 것인데, 이렇게 하면 그 목적을 달성하기 어렵습니다.
  2. 코드의 안정성 저하

    • 구체적인 예외를 선언하지 않으면, 컴파일러가 예외 처리의 누락을 체크해주지 못합니다.
    • 이는 런타임 시 예상치 못한 예외로 인한 프로그램 중단 가능성을 높입니다.
  3. 유지보수의 어려움

    • 메서드가 어떤 예외를 발생시키는지 명확하지 않아, 코드를 이해하고 유지보수하는 데 어려움이 있습니다.
    • 새로운 개발자가 코드를 접했을 때, 어떤 예외 상황을 고려해야 하는지 파악하기 어렵습니다.
  4. 불필요한 예외 전파

    • 모든 예외를 상위로 전파하면, 실제로 처리해야 할 곳에서 예외를 처리하지 못하고 계속 상위로 전파될 수 있습니다.
    • 이는 결국 예외 처리의 본질적인 목적을 달성하지 못하게 합니다.
  5. 성능 저하

    • 예외를 생성하고 전파하는 것은 상대적으로 비용이 큰 작업입니다.
    • 불필요한 예외 전파는 애플리케이션의 전반적인 성능을 저하시킬 수 있습니다.

적절한 예외 처리 방법

  1. 구체적인 예외 사용

    • 가능한 한 구체적인 예외를 사용하여 throws를 선언합니다.
    • 예: throws IOExceptionthrows SQLException 등
  2. 예외의 범위 좁히기

    • 메서드에서 발생할 수 있는 예외만을 선언합니다.
    • 불필요하게 넓은 범위의 예외를 선언하지 않습니다.
  3. 예외 래핑(Wrapping)

    • 저수준의 예외를 잡아서 더 의미 있는 고수준의 예외로 감싸 던집니다.
    • 이는 추상화 수준을 일관되게 유지하는 데 도움이 됩니다.
  4. 실행 시간 예외(Runtime Exception) 활용

    • 체크 예외를 남용하지 않고, 적절한 경우 실행 시간 예외를 사용합니다.
    • 이는 throws 선언을 줄이고 코드를 더 깔끔하게 만들 수 있습니다.
  5. try-catch 블록 사용

    • 예외를 처리할 수 있는 가장 적절한 위치에서 try-catch 블록을 사용합니다.
    • 이를 통해 예외 상황에 대한 구체적인 처리가 가능해집니다.

사용 예제

부적절한 예외 처리 (개선 전)

// 컨트롤러
@PostMapping("/sample/detail")
public String detail(@ModelAttribute SampleVO sampleVO, @RequestParam String id, Model model) throws Exception {
    sampleVO.setId(id);
    SampleVO detail = this.sampleService.selectSample(sampleVO);
    model.addAttribute("sampleVO", detail);
    return "egovSampleRegister";
}
 
// 서비스
@Override
public SampleVO selectSample(SampleVO vo) throws Exception {
    SampleVO resultVO = sampleDAO.selectSample(vo);
    if (resultVO == null)
        throw new Exception("info.nodata.msg");
    return resultVO;
}
 
// DAO
public SampleVO selectSample(SampleVO vo) throws Exception {
    return sqlSession.selectOne("Sample.selectSample", vo);
}

개선된 예외 처리 (개선 후)

// 컨트롤러
@PostMapping("/sample/detail")
public String detail(@ModelAttribute SampleVO sampleVO, @RequestParam String id, Model model) {
    sampleVO.setId(id);
    try {
        SampleVO detail = this.sampleService.selectSample(sampleVO);
        model.addAttribute("sampleVO", detail);
        return "egovSampleRegister";
    } catch (NoDataException e) {
        // 데이터가 없는 경우에 대한 처리
        model.addAttribute("errorMessage", "요청하신 데이터를 찾을 수 없습니다.");
        return "errorPage";
    }
}
 
// 서비스
@Override
public SampleVO selectSample(SampleVO vo) throws NoDataException {
    SampleVO resultVO = sampleDAO.selectSample(vo);
    if (resultVO == null) {
        throw new NoDataException("요청한 데이터가 존재하지 않습니다.");
    }
    return resultVO;
}
 
// DAO
public SampleVO selectSample(SampleVO vo) throws DataAccessException {
    return sqlSession.selectOne("Sample.selectSample", vo);
}
 
// 사용자 정의 예외
public class NoDataException extends RuntimeException {
    public NoDataException(String message) {
        super(message);
    }
}

개선 사항 설명

  1. 구체적인 예외 사용Exception 대신 NoDataException과 같은 구체적인 예외를 사용했습니다.

  2. 예외의 범위 좁히기: 각 계층에서 발생할 수 있는 구체적인 예외만을 선언했습니다.

  3. 실행 시간 예외 활용NoDataException을 RuntimeException의 하위 클래스로 정의하여, 불필요한 throws 선언을 줄였습니다.

  4. 예외 처리 위치: 예외를 처리하기 가장 적절한 위치인 컨트롤러에서 try-catch 블록을 사용했습니다.

  5. Spring의 DataAccessException 활용: DAO 계층에서 Spring의 DataAccessException을 사용하여 데이터 접근 관련 예외를 처리했습니다.

참고 자료

FAQ

Q: 모든 예외를 Exception으로 처리하면 코드가 더 간단해지지 않나요?

  • A: 단기적으로는 간단해 보일 수 있지만, 장기적으로는 코드의 안정성과 유지보수성을 저하시킵니다. 구체적인 예외 처리를 통해 더 견고한 코드를 작성할 수 있습니다.

Q: 실행 시간 예외만 사용하는 것은 어떤가요?

  • A: 실행 시간 예외만 사용하는 것도 극단적인 방법일 수 있습니다. 체크 예외와 실행 시간 예외를 상황에 맞게 적절히 사용하는 것이 좋습니다. 회복 가능한 상황에는 체크 예외를, 프로그래밍 오류와 같은 경우에는 실행 시간 예외를 사용하는 것이 일반적인 관행입니다.

관련 질문 및 추가 정보