Framework/예외처리2009. 3. 10. 02:25


예외 처리에 관해 알아야 할 중요한 사실 중 하나는 예외 처리는 비즈니스 로직이라는 것이다.
if (출금액() > 임금액) throw new 충분한돈이없음예외 와 같은 태생이 비즈니스 로직뿐 아니라 일반적으로 많이 나오는 IOException, SQLEXception등의 java Exception도 어떻게 예외 처리를 하는가는 프로그램마다 다르고 비지니스 로직마다 다르다.

그 전에 프로그래밍에서 예외란 에러와 달리 Alternative Path의 한 종류이고 이도 정상적인 하나의 프로그래밍 로직으로 인정해야 한다.

try {
    ....
} catch(IOException ex){
   log.warn(ex) ;
}

위 구문에서 예외 처리는 중복일까 아닐까? 프레임워크의 시야로 본다면 위 구문이 여러번 나온다면 그건 중복이다. 앞서 다른 글에서 밝힌바와 같이 두 줄이상의 코드가 동일할때만 중복이라고 하는게 아니다. 위와 같은 코드는 구조적인 중복의 2번째 케이스이다. 그런데 단 한줄인데 더 이상 어떻게 하라는 말인가? 한줄을 줄여봤자 한줄인데 이게 왜 중복이고 더 이상 어떻게 줄일 수 있다는 건지 의문을 가질 수 있다.

아마도 객체지향 책을 몇권 읽었다면 구체적인 것에 의지하지 말아라 라는 말을 듯었을 것이다. 구체적인 것에 의존하지 않기 위해 우리는 컨크리트 클래스보다는 추상클래스를 추상클래스보다는 인터페이스를 써야 한다는 것을 이미 알고 있다. 그리고 여기서의 log은 아마도 Apache의 common log같은걸 사용했다면 인터페이스 일테니 충분하지 않은가? 그리고 물론 충분하지 않다. 객체지향에서 구체적인 것이란 단순히 컨크리트 클래스만을 의미하는 것이 아니다. 처음에 언급한대로 예외처리는 비지니스 로직이다라는걸 인정한다면 log를 남긴다는 것 자체가 충분히 구체적이다.

보통의 경우 비지니스 로직과 일반 로직은 수명과 변화의 속도가 다르기 때문에 가능하면 분리해서 구성해야 한다. 잘 알다시피 프로그래머는 CPU가 아니기 때문에 멀티 테스킹의 CPU처럼 유연한 상태전환이 쉽지않고 인터럽트에 취약하기 때문에 로직 중간 catch 코드에 log를 쓸지 warn을 쓸지 info를 쓸지 LogLevel은 몇레벨을 쓸것인지 따위에 고민하게 만들어서는 안된다.

function xxx() throws IOException {
     ,,,,,,,
}

대부분의 코드에서 catch문을 작성하지 않는다면 어느순간 예외들을 던지는 구문이 메소드 내용보다 길어질 것이라고 걱정할지 모르지만 앞서의 조언들을 잘 따라서 인프라를 담당하고 있는 시스템 프레임워크들이 runtime exception을 던지게 하거나 걱정할 정도로 많은 예외를 던져야 한다면 코드를 다시한번 변화의 속도를 기준으로 나눠서 한번정도 Wrapping하는것도 고려한다면 그다지 큰 문제는 아니다.

그마저도 싫다면 혹은 어렵다면
try {
    ....
} catch(IOException ex){
   exManager.resolve(ex) ;
}
와 같이 처리 하는게 낫다 한다. resolve이란 메소드는 log 메소드 보다 더 추상적이고 확장과 구성의 포인트가 많다.

계획대로 모든 예외처리를 미루었다면 FrontController에서
,,,,,
catch (Throwable e) {
     Throwable ex = e ;
     if (e instanceof ServletException && ((ServletException) e).getRootCause() != null) {
        ex = ((ServletException)e).getRootCause() ;
     }
     getExceptionHandler().resolve(new HttpExceptionInfo(request, form, ex));
}
와 같이 한번에 처리해 버릴수 있다.

실제로 예외 처리의 예을 다이어그램으로 그려본다면



와 같이 Chain Of Responsibility 패턴 정도가 나올것이다. Chain Of Responsibility는 어떤 사람에게 요구가 들어왔을때 그 사람이 그것을 처리할 수 있으면 처리하고, 처리할 수 없으면 다음사람에게 넘긴다. 다음 사람이 그것을 처리할 수 있으면 처리하고, 처리할 수 없으면 또 다음 사람에게 넘긴다.

이 패턴은 요구를 하는 사람과 요구를 처리하는 사람을 느슨하게 연결한다. 만약 이 패턴을 사용하지 않으면 이 요구는 이 사람이 처리해야 한다. 라는 지식을 누군가 중앙 집중적으로 가지고 있거나 개별적으로 각각 처리해야한다. 이 지식을 요구를 하는 사람(Client)에게 맡기는 것은 현명하지 않다. 요구를 하는 사람이 처리자들의 역할 분담까지 상세하게 알아야 한다면 부품으로서의 독립성이 손상되고 자신의 일에 집중할 수 없게 되기 때문이다.

Client가 예외를 던지면 예외는 자신의 어떤 종류의 예외인지 예외 코드는 무엇인지 그리고 예외가 발생한 클래스와 메소드의 위치 등 예외와 관련한 모든 정보를 자체적으로 담고 있기 때문에 Handler들은 내가 담당할 예외인지를 판단해서 맞다면 자신이 처리하고 그렇지 않다면 다음 핸들러에 넘겨주는 방식을 취한다. 실제로 변할 가능성이 높은 - 혹은 변화의 속도가 다른 -  구체적으로 파일에 로그를 남길것인지 따라 중복된 예외들을 제외하고 에러데이타베이스를 통해 관리할 것인지 혹은 자고 있을 가엾은 개발자에게 문자를 날릴것인지는 각각의 Handler Class 들이 결정하게 된다. 그리고 컨피그 파일 등에서 아래와 같이 구성파일에서 핸들러의 종류와 순서를 지정할 수 있게 한다면 예외 처리의 변동에 유연하게 대처할 수 있다.

 <!--
  Exception Handler
 -->
 <exception-handler>
        <configured-object>
            <class-name>com.bleujin.webapp.exception.DupValOnIndexExceptionHandler</class-name>
            <constructor>
                <constructor-param>
                    <description>contact Message</description>
                    <type>java.lang.String</type>
                    <value> Please, check ID value</value>
                </constructor-param>
            </constructor>
        </configured-object>
        <configured-object>
            <class-name>com.bleujin.webapp.exception.InterMediaExceptionHandler</class-name>
        </configured-object>
        <configured-object>
            <class-name>com.bleujin.webapp.exception.ReferenceExceptionHandler</class-name>
        </configured-object>
        <configured-object>
            <class-name>com.bleujin.webapp.exception.LockExceptionHandler</class-name>
            <constructor>
                <constructor-param>
                    <description>contact Message</description>
                    <type>java.lang.String</type>
                    <value> Please, Confirm Lock Tab Of Admin's Menu</value>
                </constructor-param>
            </constructor>
        </configured-object>
        <configured-object>
            <class-name>com.bleujin.webapp.exception.LastOutExceptionHandler</class-name>
        </configured-object>
 </exception-handler>

'Framework > 예외처리' 카테고리의 다른 글

GUI TEST  (0) 2009.06.08
예외 수집기  (0) 2009.03.26
checked vs runtime  (0) 2009.03.07
checked exception의 문제  (0) 2009.02.09
예외 처리 격언  (2) 2009.02.09
Posted by bleujin