CAFE

☆ 프로그래밍 팁

TRY CATCH 문을 생활화하자

작성자천동이|작성시간04.10.26|조회수517 목록 댓글 2
 보통 실무에서의 프로젝트는 공부할 때와는 달리 크기가 방대하다. 따라서, 시간이 지나면 지날수록 그 복잡성은 증가할 수 밖에 없다. 이 방대함과 복잡성에서 버그를 줄이기 위해 프로그래머들이 택할 수 있는 유일한 방법은 잘 정리 정돈하는 것, 그리고 주석과 진단 매크로 등으로 도배하는 것이다. 그 중 아주 효율적인 방법중의 하나가 try catch 문을 생활화하는 것이다.

 

  C 에 입문하는 모든 사람에게 GO TO 문은 사용하지 말아야 하는 것으로 거의 불문율처럼 여겨진다. 이는 GOTO 문의 남발이 코드의 복잡성을 증가시키기 때문이다. 그러나, 참 아이러니하게도 GOTO 문은 코드를 대폭 간결하게 처리할 수 있는 방법이기도 하다. 특히 에러처리에 있어서는 GOTO 문은 그 진가를 발휘한다. 그래서 그 대안으로 try catch 문을 제공하는 것이다.

 

다음 문장을 살펴보자.

 

A 수행;

If (A 수행이 성공하면)

{

    B 수행;

    If (B 수행이 성공하면)

    {

        C 수행;

        If (C 수행이 성공하면)

        {

            OK;

        }

        else

        {

            에러처리;

        }

    }

    else

    {

       에러처리;

    }

}

else

{

   에러처리;

}

 

위 문장을 try catch 문으로 다시 정리해 보면 아래와 같다.

 

try

{

    A 수행;

    If (A 수행 실패)

        ThrowException ();

 

    B 수행;

    If (B 수행 실패)

        ThrowException ();

 

    C 수행;

    If (C 수행 실패)

        ThrowException ();

 

    D 수행;   

}

catch

{

    에러 처리;

}

 

  코드의 양도 양이지만, A->B->C->D 로 작업이 진행된다는 코드의 판독성이 상당히 증가하는 걸 알 수 있다.

 

  일단 예외처리에 관한 기본적 지식은 관련 서적이 많으니 참고하길 바라고, 여기에서는 MFC에서 제공하지만 쓰기에는 약간 불편한 CException 클래스를 확장하여 좀더 편리하게 사용할 수 있는 방법에 대해 알아보기로 한다.

 

  보통 try catch 문을 사용하면 아래와 같다.

    try

    {

        // Do something to throw an exception.

        throw "Alarm Error !!";

    }

    catch (char* szExcepy)

    {

        ::AfxMessageBox (szExcepy);

    }

 

  그런데, 이를 MFC적 사고에서 MFC가 제공하는 CException 클래스를 사용하면,

    TRY

    {

        // Do something to throw an exception.

        CException* const e = new CException (TRUE);

        THROW (e);

    }

    CATCH_ALL (e)   

    {

        e->ReportError ();

    }

    END_CATCH_ALL

 

  오류 메시지는 “사용가능한 오류메세지가 없습니다.” 이다. 오히려, 사용하기 더 어렵게 되었다는 것을 느낄 것이다.   그러나 CException 는 기초클래스일 뿐이고 ReportError () 함수 또한 가상함수로 만들어져 있어 사용자가 이를 확장하여 쉽게 처리할 수 있음을 암시하고 있다.

 

  자 이제 우리는 CException 클래스에서 상속받아 확장된 CExceptionEx 를 만들것이다.

  목적은 코드를 좀더 간결하게 처리하고, 원하는 에러 메시지를 효과적으로 발생시킬 수 있도록 하는것이다.

 

  간략히 처리하기 위해서는 CExceptionEx* const e = new CExceptionEx (TRUE, “에러메세지”); 로 처리할 수 있도록 클래스를 정의해야 한다.

  먼저 에러 메시지를 담을 CString 멤버 변수를 하나 갖고, 생성자를 추가한다.

  다음 가상함수 ReportError () 에서 추가한 CString 변수를 출력하도록 처리한다.

 

우선 CExceptionEx 의 클래스 정의를 살펴보면 다음과 같다.

class CExceptionEx : public CException

{

protected:

    DECLARE_DYNAMIC(CExceptionEx)

 

private:

    CString m_strMessage;

 

public:

    CExceptionEx ();

    CExceptionEx (const BOOL bAutoDelete, LPCTSTR lpszMessage);

 

public:

    virtual ~CExceptionEx ();

    virtual int  ReportError (UINT nType = MB_OK, UINT nError = 0 );

};

 

다음으로 ::AtxThrowXXXException () 처럼 전역함수를 만들어 사용해야 한다.

 

이는 다음과 같이 정의하였다.

void TfcThrowException (const UINT nFormatID, ...);

void TfcThrowException (const LPCTSTR lpszFormat, ...);

void TfcThrowNotSupportedException (void);

이는 리소스의 스트링 테이블의 문자열도 이용할 수 있고, 인자를 갖는 문자열을 사용할 수 있도록 처리하였다.

 

에러 메시지 박스 출력에 관련된 함수도 자신만의 예쁜 메시지 박스나, 스트링 테이블을 이용할 수 있고, 인자를 갖는 문자열을 이용할 수 있도록 전역적으로 정의하자.

이와 관련된 함수 정의는 아래와 같다.

void TfcMessageBox (const UINT nFormatID, ...);

void TfcMessageBox (const LPCTSTR lpszFormat, ...);

int  TfcMessageBox (LPCTSTR lpszMsg, UINT nType, LPCTSTR lpszTitle = NULL);

int  TfcMessageBox (const UINT nFormatID, UINT nType, LPCTSTR lpszTitle = NULL);

CString GetStringFromID (const UINT uFormatID, ...);

 

구현부는 별로 어렵지 않게 만들어져 있으므로 아래에서 다운 받아 확인해 보면 된다.

http://myhome.konetic.or.kr/UserUploadData/gobuksun/ExceptionEx.cpp

http://myhome.konetic.or.kr/UserUploadData/gobuksun/ExceptionEx.h

 

 

자, 그럼 어떻게 TRY CATCH 문을 사용하게 되었는지 예로 살펴보자.

 

만일 리소스 스트링 테이블에

IDS_USERID_NOT_EXIST 는 “아이디 %s는 존재하지 않습니다.”

IDS_INVALID_PASSWORD 는 “%s님의 비밀번호가 다릅니다.”라고 정의되어 있다고 가정한다.

 

    TRY

    {

        CString strUserID = _T(“거북선”);

        BOOL bSuccessLogin = FALSE;

 

        // strUserID 아이디 검색

        if (FALSE == bSuccessLogin)

            ::TfcThrowException (IDS_USERID_NOT_EXIST, strUserID);

 

        // 패스워드 체크

        if (FALSE == bSuccessLogin)

            ::TfcThrowException (IDS_INVALID_PASSWORD, strUserID);

    }

    CATCH_ALL (e)       // 로그인 실패

    {

        // e->ReportError (REPORT_AS_TRACE); 이면 메시지 박스로 출력하지

        // 않고 TRACE 처럼 결과창에만 보여준다.

        e->ReportError ();

    }

    END_CATCH_ALL

 

이와 같이 사용 할 수 있다.

 

자신이 맡은 프로젝트의 성격에 따라, 에러 메시지 뿐만 아니라 에러 번호 같은 것을 멤버변수로 두어 각기 다르게도 처리할 수 있을 것이다.

 

실무에서 수많은 버그를 방지하는 방법은 빠져 나갈 수 있는 구멍을 철저히 막아버리는 것이다. 약간 귀찮아 보이지만 버그와 에러를 방지하는 방법으로써 Stdafx.h 파일에 #include "ExceptionEx.h"를 추가하고 TRY CATCH 문을 생활화하자.

 

다음검색
현재 게시글 추가 기능 열기

댓글

댓글 리스트
  • 작성자취직하자 | 작성시간 04.10.26 오호~ 정보 감사합니다..
  • 작성자Nautes | 작성시간 04.10.26 헉 제길.. 처음들어보는문;;
댓글 전체보기
맨위로

카페 검색

카페 검색어 입력폼