4장.예외

sylvie·2023년 1월 8일
0

스프링부트

목록 보기
11/11
post-custom-banner

제 4장. 예외

  • 학습 묙표
    • JdbcTemplate을 대표로 하는 스프링의 데이터 엑세스 기능에 담겨있는 예외처리와 관련된 접근 방법에 대해 알아본다.
    • 이를 통해 예외를 처리하는 베스트 프랙티스도 살펴본다.

4.1 사라진 SQLExcption

메소드의 정의를 들여다보면 JdbcTemplate 적용 이전에는 있었던 throws SQLException 선언이 적용 후에는 사라졌음을 알 수 있다.

  • JdbcTemplate 적용 이전

    public void deleteAll() throws SQLExcption{
        this.jdbcContext.executeSql("delete from users");
    }x
  • JdbcTemplate 적용 이전

    public void deleteAll(){
        this.jdbcTemplate.eupdate("delete from users");
    }

4.1.1 초난감 예외처리

  • 학습목표
    • jdbeTemplate이 어떤 짓을 했길래 SQLException이 사라졌는지 알아보기 전에 먼저 개발자들의 코드에서 종종 발견되는 초난감 예외처리의 대표선수들을 살펴보자.

4.1.1.1 예외 블랙홀

  • 예외를 처리할 때 반드시 지켜야 할 핵심 원칙은 아래와 같다.

    • 모든 예외는 적절하게 복구되든지 아니면 작업을 중단시키고 운영자 또는 개발자에게 분명하게 통보돼야 한다.

      1. catch문에 아무것도 안쓰는 건 안됨

      2. system.out.println()이나 log.debug()와 같이 콘솔 이나 로그에 예외메세지만 출력을 해서는 안됨.

        • Why? 콘솔 로그를 누군가가 계속 모니터링하지 않는 한 이 예외 코드는 심각한 폭탄으로 남아 있을 것이다. 예외는 처리돼야 한다.
    • Why? SQLException을 예를 들자면, SQLException이 발생하는 이유는 SQL에 문법 에러가 있거나 DB에서 처리할 수 없 을 정도로 데이터 액세스 로직에 심각한 버그가 있거나, 서버가 죽거나 네트워크가 끊 기는 등의 심각한 상황이 벌어졌기 때문이다

    • 굳이 예외를 잡아서 뭔가 조치를 취할 방법이 없다면?

      • 잡지 말아야 한다.
      • How? 메소드에 throws SQLException을 선언해서 메소드 밖으로 던지고 자신을 호출한 코드에 예외처리 책임을 전가해버려라.

4.1.1.2 무의미하고 무책임한 throws

  • throws Exception이라는, 모든 예외를 무조건 던져버리는 선언을 모든 메소드에 기계적으로 넣는 것이다.

    • 결과 : 의미 있는 정보를 얻을 수 없다. 정말 무엇인가 실행 중에 예외적인 상황이 발생할 수 있다는 것인지, 아니면 그냥 습관적으로 복사해서 붙여 놓은 것인지 알 수가 없다.

4.1.2 예외의 종류와 특징

  • 가장 큰 이슈 : 체크 예외(checked exception)라고 불리는 명시적인 처리가 필요한 예외를 사용하고 다루는 방법.
  • 예외의 종류
    • 자바에서 thr ow 를 통해 발생시킬 수 있는 예외는 크게 세 가지가 있다.
      1. Error
      2. 체크예외 (Checked Exception)
      3. 언체크 예외 (Unchecked Exception)

4.1.3 예외처리 방법

먼저 예외를 처리하는 일반적인 방법을 살펴보고 나서 효과적인 예외처리 전략을 생각 해보겠다.

4.1.3.1 예외 복구

예외상황을 파악하고 문제를 해결해서 정상 상태로 돌려놓는 것이다.

  • 예외로 인해 기본 작업 흐름이 불가능하면 다른 작업 흐름으로 자연스럽게 유도해주는 것이다. 이런 경우 예외상황은 다시 정상으로 돌아오고 예외를 복구했다고 볼 수 있다.
  • 예외처리 코드를 강제하는 체크 예외들은 이렇게 예외를 어떤 식으로든 복구할 가능 성이 있는 경우에 사용한다. A P I 를 사용하는 개발자로 하여금 예외상황이 발생할 수 있 음을 인식하도록 도와주고 이에 대한 적절한 처리를 시도해보도록 요구하는 것이다.

4.1.3.2 예외처리 회피

예외를 자신이 처리하지 않고 회피하는 방법이다.

  • 예외처리를 자신이 담당하지 않고 자신을 호출한 쪽으로 던져버리는 것.

  • throws 문으로 선언해서 예외가 발생하면 알아서 던져지게 하거나 cat ch 문으로 일단 예외를 잡은 후에 로그를 남기고 다시 예외를 던지는 (rethrow) 것.

  • 예외처리를 회피하려면 반드시 다른 오브젝트나 메소드가 예외를 대신 처리할 수 있도록 아래와 같이 던져줘야 한다.

    1. 방법 1

      public void add() throws SQLException{
        //JDBC API
      }
    2. 방법 2

      pulic void add() throws SQLException{
        try{
          //JDBC API
        }catch(SQLException e){
          //로그 출력
          throws e;
        }
      }
      • 콜백 오브젝트의 메소드는 SQLException에 대한 예외를 회피하고 템플릿 레벨에서 처리하도록 던져 준다.
      • JdbcContext 나 JdbcTemplate이 사용하는 콜백 오브젝트는 ResultSet 이나 PreparedStatement 등을 이용해서 작업하다 발생하는SQLException을 자신이 처리하지 않고 템플릿으로 던져버린다.
        • Why? 콜백 오브젝트의 메소드는 모두throwsSQLException이 붙어 있다. SQLException을 처리하는 일은 콜백 오브젝트의 역할이 아니라고 보기 때문이다.
        • 긴밀하게 역할을 분담하고 있는 관계가 아니라면 자신 의 코드에서 발생하는 예외를 그냥 던져버리는 건 무책임한 책임회피일 수 있다.

4.1.3.3 예외 전환

예외 회피와 비슷하게 예외를 복구해서 정상적인 상태로는 만들 수 없기 때문에 예외를 메소드 밖으로 던지는 것.

  • 예외 회피와의 차별점 : 예외 회피와 달리, 발생한 예외를 그대로 넘기는 게 아니 라 적절한 예외로 전환해서 던진다는 특징.
  • 예외 전환을 사용하는 목적
    1. 내부에서 발생한 예외를 그대로 던지는 것이 그 예외상황에 대한 적절한 의미를 부여해주지 못하는 경우에, 의미를 분명하게 해줄 수 있는 예외로 바꿔주기 위해 서다. API 가 발생하는 기술적인 로우레벨을 상황에 적합한 의미를 가진 예외로 변경하는 것.
    2. 예외를 처리하기 쉽고 단순하게 만들기 위해 포장(wrap)하는 것.

4.1.4 예외처리 전략

  • 학습목표
    • 지금까지 살펴본 예외의 종류와 처리 방법 등을 기준으로 일관된 예외처리 전략을 정리.

4.1.5 SQLException은 어떻게 됐나?

  • 학습목표

    • 지금까지 살펴본 예외처리에 관한 내용 을 바탕으로, DAO에 존재하는 S Q LExcept i on에 대해 생각해보자.
      • 지금까지 다룬 예외처리에 대한 내용은 JdbcTemplate을 적용하는 중에 throws S QLException 선언이 왜 사라졌는가를 설명하는 데 필요한 것이었다.
        • Why? 스프링의 예외처리 전략과 원칙을 알고 있어야 하기 때문이다.
  • 먼저 생각해볼 사항

    • SQLException은 과연 복구가 가능한 예외인가?
      • 99%의 SQLExcept i on은 코드 레벨에서는 복구할 방법이 없다.
        • 프로그램의 오류 또는 개발자의 부주의 때문에 발생하는 경우이거나, 통제할 수 없는 외부상황 때문에 발생하는 것이 다. 예를 들어 SQL 문법이 틀렸거나, 제약조건을 위반했거나, DB 서버가 다운됐다거 나, 네트워크가 불안정하거나, DB 커넥션 풀이 꽉 차서 DB 커넥션을 가져올 수 없는 경우 등이다.
      • 시스템의 예외라면 당연히 애플리케이션 레벨에서 복구할 방법이 없다. 관리자나 개 발자에게 빨리 예외가 발생했다는 사실이 알려지도록 전달하는 방법밖에는 없다.
      • 애플리케이션 코드의 버그나 미처 다루지 않았던 범위를 벗어난 값 때문에 발생 한 예외도 역시 복구할 방법이 없다.
      • DAO 밖에서 SQLException을 다룰 수 있는 가능성은 거의 없다.
    • 결론 : 예외처리 전략을 적용해야 한다.
      • 필요도 없는 기계적인 t hr ow s 선언이 등장하도록 방치하지 말고 가능한 한 빨리 언체크/런타임 예외로 전환해줘야 한다.
  • 스프링의 jdbeTemplate 적용 이후, DAO 메소드에서 SQLException이 모두 사라진 이유?

    • 스프링의 jdbeTemplate은 바로 예외처리 전략을 따르고 있다.

      • jdbeTemplate 템플 릿과 콜백 안에서 발생하는 모든 SQLException을 런타임 예외인 DataAccessException 으로 포장해서 던져준다.
      • jdbeTemplate을 사용하는 UserDao 메소드에선 꼭 필요 한 경우에만 런타임 예외인 DataAccessException을 잡아서 처리하면 되고 그 외의 경우 에는 무시해도 된다.
    • jdbeTemplate의 update(), queryForInt ( ) , quer y( ) 메소드 선언을 잘 살펴보면 모두 throws D ataAccessException이라고 되어 있음.

      public int  update(final  String sql ) throws DataAccessException { ... }

4.2. 예외 전환

  • 스프링의 JdbcTemplate이 던지는 DataAccessException의 역할
    • 런타임 예외로 SQLException을 포장.
      • 대부분 복구가 불가능한 예외인 SQLException에 대해 애플리케이션 레벨에서는 신경 쓰지 않도록 해주는 것.
    • SQLException에 담긴 다루기 힘든 상세한 예외정보를 의미있고 일관성 있는 예외로 전환해서 추상화해주려는 용도.

예외 전환의 목적

  1. 런타임 예외로 포장해서 굳이 필요하지 않은 catch/throws를 줄여주는 것.
  2. 로우레벨의 예외를 좀 더 의미 있고 추상화된 예외로 바 꿔서 던져주는 것'

4.2.1 JDBC의 한계

4.2.1.1 비표준 SQL

  • 원인 : JDBC 코드에서 사용하는 SQL.
    • DB의 변경 가능성을 고려해서 유연하게 만들어야 한다면 SQL은 제법 큰 걸림돌이 된다.
    • 비표준 SQL은 결국 DAO 코드 에 들어가고, 해당 DAO는 특정 우에 대해 종속적인 코드가 되고 만다. 다른 우로 변 경하려면 DAO에 담긴 SQL을 적지 않게 수정해야 한다.
  • 해결책?
    • 호환 가능한 표준 SQL만 사용하는 방법.
      • 현실성 X
        • 표준 SQL만을 사용할 경우, 당장에 웹 프로그램에서 자주 필요로 하는 페이징 쿼리에서부터 문제가 된다. 따라서 표준 SQL만 사용하는 방법은 간단한 예제 프로 그램이라면 모를까 그다지 현실성이 없다.
    • DB별 로 별도의 DAO를 만들거나 SQL을 외부에 독립시켜서 DB에 따라 변경해 사용하는 방법

4.2.1.2 호환성 없는 SQLException의 DB 에러정보

  • 원인 : SQLException
  • JDBC API는 이 SQLException 한 가지만 던지도록 설계되어 있 다.
  • But, SQLException의 getErrorCode() 로 가져올 수 있는 D B 에러 코드 는 DB별로 모두 다르다.
    • DB 벤더가 정의한 고유한 에러 코드를 사용하기 때문이다.

4.2.2 DB 에러 코드 매핑을 통한 전환

DB 종류가 바뀌더라도 DAO를 수정하지 않으려면 두가지 해결방안이있는데, 여기서는 SQLException의 비표준 에러 코드와 SQL 상태정보에 대한 해결책을 알아보자.

  • DB별 에러 코드를 참고해서 발생한 예외의 원인이 무엇인지 해석해 주는 기능을 만드는 것이다.
  • 문제점: SQLException의 서브클래스이므로 여전히 체크 예외라는 점과 그 예외를 세 분화하는 기준이 SQL 상태정보를 이용한다

4.2.3 DAO 인터페이스와 DataAccessException 계층구조

  • 학습목표

    • 스프링이 왜 이렇게 D at aA ccessExcept i on 계층구조를 이용해 기술에 독립적인 예외를 정의하고 사용하게 하는지 생각해보자.

      • DataAccessException은 JDBC의 SQLException을 전환하는 용도로만 만들어진 건 아 니다.

      • D at aA ccessExcept i on은 의미가 같은 예외라면 데이터 액세스 기술의 종류와 상관없 이 일관된 예외가 발생하도록 만들어준다. 데이터 액세스 기술에 독립적인 추상화된 예 외를 제공하는 것이다.

4.2.3.1 DAO 인터페이스와 구현의 분리.

  • D A O 를 굳이 따로 만들어서 사용하는 이유는 무엇일까?
    • 데이터 액세스 로직을 담은 코드를 성격이 다른 코드에서 분리해놓기 위해서다
    • 분리된 D A O 는 전략 패턴을 적용해 구현 방법을 변경해서 사용할 수 있게 만들기 위해서
  • DAO를 사용하는 클라이언트 입장에서는 DAO의 사용 기술에 따라서 예외 처 리 방법이 달라져야 한다. 결국 클라이언트가 DAO의 기술에 의존적이 될 수밖에 없다.
  • 단지 인터페이스로 추상화하고, 일부 기술에서 발생하는 체크 예외를 런타임 예외로 전환하는 것만으론 불충분하다.

4.2.3.2 데이터 액세스 예외 추상화와DataAccessException 계층구조

  • 스프링은 자바의 다양한 데이터 액세스 기술을 사용할 때 발생하는 예외들을 추 상화해서 D at aA ccessExcept i on 계층구조 안에 정리

  • 인터페이스 사용. 런타임 예외 전환과 함께 D at aA ccessExcept i on 예외 추상화를 적용하면 데이터 액세스 기술과 구현 방법에 독립적인 이상적인 DAO를 만들 수가 있다.

4.2.4 기술에 독립적인 UserDao 만들기

DataAccessException 활용 시 주의사항

  • DataAccessException을 잡아서 처리하는 코드를 만들려고 한다면 미리 학습 테스트를 만들어서 실제로 전환되는 예외의 종류를 확인해둘 필요가 있다.
  • DAO에서 사용하는 기술의 종류와 상관없이 동일한 예외를 얻고 싶다면 DuplicatedUserldException처럼 직접 예외를 정의해두고, 각 DAO의 add( ) 메소드에 서 좀 더 상세한 예외 전환을 해줄 필요가 있다
post-custom-banner

0개의 댓글