토비의 스프링 chaper 4 요약

Reading time ~3 minutes

토비의 스프링 Chpater 4

어려운 토비의 스프링 3장이 너무 힘들었지만 4장도 하나하나 천천히 읽어나가야 겠다. 4장에서는 놓치기 쉬운 db관련 예외처리를 다루고 있다.

4장

난감 예외처리

흔히 할 수 있는 예외처리

try {
    // ...
} catch (SQLException e) {
    // do noting..
}

try-catch로 감싸면 컴파일 에러도 안나고 문제 없이 동작하니까 그냥 예외를 잡는 코드만 작성하기 쉽다.. 나도 이제까지 그랬고, 하더라도 스택 프린트하는것 밖에 안했으니 ..ㅠ

여기선 이 문제를 아주 강력하게 집어주고 있다 잡아내는 것까지 했으면 무시하지 말고 처리를 하라고 말이다.

catch(Exception e) {
    e.printStackTrace();
    System.exit(1);
}

이렇게라도 하는게 백배 낫다고 한다. 자숙 많이 해야겠다.

또한 책에서는 무책임하게 예외를 메소드 밖으로, 던져버리는 throw 를 비판한다. 심지어 예외가 발생할 만한 메소드에는 각각의 정확안 예외명을 작성하기도 귀찮아 throw Exception 으로 뭉퉁그려 던지는 걸 극혐하고 있다.

예외의 종류와 특징

  1. Error java.lang.Error 서브크래스로 시스템에 뭔가 비정상적인 상황이 발생했을 경우 ex) OutOfMemoryError, ThreadDeath

  2. Exception과 체크 예외 java.lang.Exception 클래스, 서브클래스로 정의되는 예외로 코드작업중 예외상황 발생할 경우 ex) IOException, SQLException

  3. RuntimeException과 언체크/런타임 예외 java.lang.RuntimeException 클래스를 상속한 예외로 명시적으로 잡아주지 않아도 될만한 예외들 ex) NullPointerException, IllegalArgumentException

예외처리 방법

예외 복구

예외로 인해 기본 작업 흐름이 불가능하면 다른 작업 흐름으로 자연스럽게 유도해주는 것. 그렇다면 예외인 상황은 다시 정상으로 돌어오고 복구 했다고 볼 수 있다.

예외처리 회피

예외처리를 자신이 담당하지 않고 자신을 호출한 쪽으로 던져버리는 것. throws 문으로 선언해서 예외가 발생하면 던져버리기. 아니면 catch로 우선 잡고 로그 출력하고 던지기.

public void someMethod() throws SomeException {
    try {
        // ..
    } catch (SomeException e) {
        printLog();
        throw e;
    }
}

이때 주의할건 내가 처리하는게 아니라 다른 쪽에서 예외를 처리하게끔 하는게 타당하고 분명한 이유가 있어야 한다.

예외전환

예외 전환도 예외를 복구해서 정상으로 만들 수 없기에 메소드 밖으로 던져버리는 방식인데 예외처리 회피랑은 다른 점은 발생한 예외를 좀 더 구체적인 예외로 명시해서 던진다는 것이다.

예를 들면 회원 가입시 아이디가 중복되면 sql 예외를 발생시키는데 이대로 던지면 서비스 계층에선 왜 때문에 예외가 발생했는지 모른다.

그러니까 catch로 우선 잡고 분석해보고 아 사용자 중복이구나 라고 판단한다. 그런 다음 좀 더 구체적인 예외를 메소드 밖으로 던지는 것이다.

그리고 보통 구체적인 예외를 던질 때 원래 발생한 예외를 담아서 중첩 예외로 만드는 것이 좋다.

catch (SQLException e {
    throw SomeException(e);
}

예외처리 전략

런타임 예외의 보편화

체크 예외의 문제는 복구할 가능성이 조금이라도 있다고 보고 개발자로 하여금 catch블록이나 throws 선언을 강제하는 것이다.

하지만 자바 엔터프라이즈 서버환경은 독립적인 애플리케이션과는 다르게 수 많은 요청을 감당할 만한 예외처리를 할 수 없다.정확하겐 예외가 발행했을 때 이걸 복구 할 수 있는게 안된다. 그래서 예외가 발생한 요청을 중단하고 관리자나 사용자에게 통보하는게 더 낫다.

요즘은 항상 복구할 수 있는 예외가 아니라면 .. 일단 언체크로 만들고 런타임 예외로 포장해서 던저버리는게 보편화 됐다. 정말 이해하기 어렵다..

또하나 throws에 복잡하고 여러 예외상황을 나열하지 않고 구체적인 런타임 에러로 전환해서 던지는 편이 낫다.

애플리케이션 예외

시스템 또는 외부의 예외상황이 아니라 애플리케이션 자체의 로직에 의해 의도적으로 발생시키고, catch로 잡아서 무언가 조치를 취하려는 예외도 있다.

정상적으로 동작할 때의 로직을 예쁘게 작성한다. 하지만 여기엔 예외가 발생할 가능성이 있다. 그러고 예외가 발생했을 때 필요한 로직을 catch로 따로 작성한다. 의도적으로 발생할 수 있는 가능성을 열어두고 발생하면 그에 맞는 다른 로직을 실행하는 것이다.

시스템 예외라면 애플리케이션 레벨에서 복구할 방법이 없다!

이 처럼 복구 할 수있는 방법이 없는데 예외를 어떻게 다루냔 말이다. 그럼에도 불구하고 예외를 처리하라고 하니 뭐 어쩌라고.. 답은 예외처리 전략을 사용해야 한다. 필요도 없는 무의미한 throws 선언을 나두지 말고 가능한 빨리 언체크/런타임 예외로 전환해야 한다는 소리이다!

예를 들면 jdbcTemplate이 처리 불가능한 SQLException을 런타임 예외인 DataAccessException 으로 포장해서 던저 준다. 요런 방식을 적용하란 소리다.

예외전환

사용이유

  1. 런타임 예외로 포장해서 불필요한 catch/throw를 줄여주는 것
  2. 좀 더 의미있는 예외로 바꿔 주는 것

JDBC

jdbc로 인해db 프로그램 개발방법을 학습하는데 부담이 확실히 줄었다. 각각의 db의 구체적인 동작 방식을 몰라도 jdbc를 통하면 쉽게 사용이 가능하기 때문이다. 하지만 db를 자유롭게 변경해서 사용할 수 있는 유연한 코드를 보장해 주진 못한다.

jdbc의 한계는 2가지가 있다. SQL은 대체적으로 표준화 되어 있지만 각 DB들 마다 최적화등을 위한 비표준 SQL이 존재한다. 프로그램의 성능을 위해 불가피하게 비표준 SQL이 dao에 적힐 것이고, 특정 db에 종속될 것이다. db 변경 가능성을 염두한다면 작지 않은 걸림돌이 된다.

다른 하나는 SQLException의 에러정보가 모두 db마다 다르다는 것이다. jdbc는 데이터 처리 도중 다양한 예외 발생시 모두 SQLException으로 던져 버린다… 무책임하게도. 그래서 예외 원인을 알아보려면 예외 정보 를 확인해야 하는데 이게 db마다 모두 다른 예외 코드로 나타난다.

에러코드 문제

위에서 엄청난 걱정을 하게 만들었지만 jdbcTemplate 을 이용하면 DB관련 예외는 거의 신경쓰지 않아도 된다.

이미 DB별로 예외코드를 예외 클래스랑 Mapping을 시켜놨기 때문..

DAO 와 DataAccessException

SQLExeption을 DataAccessException으로 전환하는 이유가 또 있다. 런타임으로 바꾸면서 얻는 이점이 굉장히 많은 것 같다.

public interface UserDao {
    public void add(User user) // 이게 가능할까?
}

userDao 구현체에서 add 를 구현할 때 jdbc를 사용할 텐데 거기에선 분명 SqlExeption을 처리해야 한다. 그럼 interface에서 선언하지 않은 throw가 생길테고 유연함이 목적인 UserDao에서 종속성이 발생한다.

이때 SqlException을 런타임 예외로 포장해주면 이 문제는 해결된다. 하지만 data access 기술이 달라지면 > 같은 상황인데 다른 예외가 발생하니까.. 이걸 그냥 모두 런타임으로 바꾸기만 한다고 근본적인 해결은 안된다.


출처 :

  • 이 글은 이일민 지음의 ‘토비의 스프링 3.1 vol.1’ 을 읽고 작성되었습니다.

Dooray!

Dooray CalDav, IMAP 사용법 Continue reading

Vue.js

Published on February 10, 2018

Java_tuning5

Published on March 06, 2017