본문 바로가기
개발 언어/JAVA

이펙티브 자바 10장 : 에러

by 코헤0121 2025. 12. 22.
728x90
반응형

 

아이템 69. 예외는 진짜 예외 상황에만 사용하라

  • 쉽게 말하면: 예외를 if나 for문처럼 로직의 일부로 쓰지 마세요.
  • 자세히: 어떤 개발자들은 배열을 돌 때 인덱스 범위를 체크하는 if문 대신, 일부러 범위를 벗어나게 해서 ArrayIndexOutOfBoundsException이 발생하면 루프를 멈추게 설계하기도 합니다.
  • 왜 안 되나요?
    1. 성능: 예외는 발생 시 스택 추적(Stack Trace)을 생성하므로 일반 로직보다 훨씬 느립니다.
    2. 버그 은닉: 루프 안에서 진짜 다른 문제가 생겨서 예외가 발생해도, "아, 루프가 끝났구나"라고 착각하고 넘어가 버릴 수 있습니다.
  • 결론: 예외는 "정말 일어나지 말아야 할 일"이 생겼을 때만 던지세요.

 

아이템 70. 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라

  • 쉽게 말하면: 사용자가 고칠 수 있는 에러면 Checked Exception, 개발자 잘못이면 Unchecked(Runtime) Exception을 쓰세요.
  • 자세히:
    • 검사 예외(Checked): 호출하는 쪽에서 "아, 이건 내가 처리해야겠구나"라고 인지하고 대처할 수 있는 상황(예: 네트워크 일시 단절, 파일 없음)에 사용합니다.
      • 복구가 가능한 경우로 사용해야 함
      • 검사 예외를 던지면 호출자가 이를 catch로 잡아 처리하거나 바깥으로 전파하도록 강제합니다.
      • 설계 의도: API 설계자는 호출자에게 해당 예외 상황에서 회복해내라고 요구하는 것이며, 이는 그 상황이 발생할 수 있는 유력한 결과임을 알려주는 것입니다.
      • 사후 처리: 검사 예외는 일반적으로 복구할 수 있는 조건일 때 발생하므로, 호출자가 예외 상황에서 벗어나는 데 필요한 정보를 얻을 수 있도록 접근자 메서드를 제공하는 것이 중요합니다 (예: 잔액 부족 예외 발생 시 부족한 금액을 알려주는 메서드).
    • 런타임 예외(Unchecked): 코드를 잘못 짠 경우입니다. 예를 들어 null을 넘기면 안 되는 곳에 넘겼을 때 발생하는 NullPointerException이 대표적입니다. 이건 try-catch로 잡는 게 아니라 코드를 고쳐야 하는 문제입니다.
      • 복구가 불가능하거나 더 실행해봐야 득보다 실이 많은 상황을 의미합니다.
      • 설계 의도: 전제조건 위반은 단순히 클라이언트가 API 명세에 기록된 제약을 지키지 못했다는 뜻입니다.
      • 예시: 배열의 인덱스는 0에서 '배열 크기 - 1' 사이여야 하는데, 이 범위를 벗어난 인덱스를 전달하면 ArrayIndexOutOfBoundsException이 발생합니다.
    • 에러(Error): JVM 차원의 문제
      • 주로 JVM이 자원 부족, 불변식 깨짐 등 더 이상 수행을 계속할 수 없는 상황을 나타낼 때 사용합니다.
      • 주의 사항: 자바 언어 명세가 요구하는 것은 아니지만, 업계의 관례상 Error 클래스를 상속해 하위 클래스를 만드는 일은 자제해야 합니다.
      • 결론: 개발자가 구현하는 비검사 throwable은 모두 RuntimeException의 하위 클래스여야 합니다.
  • 복구 가능성 판단이 모호할 때
    • 자원 고갈과 같은 상황은 프로그래밍 오류일 수도 있고, 실제로 복구 가능한 일시적 상황일 수도 있습니다.
    • 만약 복구가 가능하다고 믿는다면 검사 예외를, 그렇지 않다면 런타임 예외를 선택해야 하며, 확신하기 어렵다면 비검사 예외(런타임 예외)를 선택하는 편이 낫습니다.

 

아이템 71. 필요 없는 검사 예외 사용은 피하라

  • 쉽게 말하면: 무조건 try-catch를 강요하는 것은 사용자에게 짐을 지우는 일입니다.
  • 자세히: 검사 예외는 API 사용자가 이를 반드시 처리하게 강제합니다. 하지만 사용자가 예외를 잡아도 할 수 있는 게 "로그 남기고 종료"뿐이라면, 비검사 예외(Runtime)로 바꾸는 게 낫습니다.
  • 대안: 예외를 던지는 대신 값이 없을 수 있음을 알리는 Optional을 반환하거나, 에러가 날지 미리 확인하는 메서드(예: hasNext())를 제공하세요.

 

아이템 72. 표준 예외를 사용하라

  • 쉽게 말하면: 직접 예외 클래스를 만들기 전에, 자바가 이미 만들어 둔 예외가 있는지 확인하세요.
  • 자세히: 다른 개발자들이 내 코드를 볼 때 IllegalArgumentException(인수가 잘못됨)이나 NullPointerException 같은 표준 예외를 보면 바로 의미를 이해할 수 있습니다.
  • 자주 쓰는 것들:
    • IllegalArgumentException: 허용하지 않는 값을 인수로 건넸을 때.
    • IllegalStateException: 객체 상태가 메서드를 수행하기에 적절하지 않을 때.
    • IndexOutOfBoundsException: 인덱스 범위를 넘어섰을 때.

 

아이템 73. 추상화 수준에 맞는 예외를 던지라

 

1. 왜 예외 번역(Exception Translation)이 필요한가?

이를 해결하기 위해 상위 계층에서는 저수준 예외를 잡아 자신의 추상화 수준에 맞는 예외로 바꿔 던져야 하며, 이를 예외 번역이라 부릅니다.

[예외 번역의 전형적인 형태]

try {
    // 저수준 추상화를 이용한다.
} catch (LowerLevelException e) {
    // 추상화 수준에 맞게 번역한다.
    throw new HigherLevelException(...);
}

자바의 AbstractSequentialList도 이 방식을 사용합니다. 내부적으로 사용하는 반복자가 NoSuchElementException을 던지면, 이를 잡아 해당 리스트의 추상화 수준에 맞는 IndexOutOfBoundsException으로 번역하여 던집니다.

2. 디버깅의 핵심, 예외 연쇄(Exception Chaining)

예외를 번역할 때, 원래 발생한 저수준 예외가 디버깅에 도움이 된다면 이를 버리지 말고 상위 예외에 실어 보내야 합니다. 이를 예외 연쇄라고 합니다.

  • 방법: 저수준 예외(원인, cause)를 고수준 예외의 생성자에 인수로 건넵니다.
  • 결과: 고수준 예외는 상위 클래스인 Throwable의 생성자로 이 원인을 전달하며, 나중에 getCause() 메서드를 통해 언제든 저수준 예외를 꺼내 볼 수 있습니다.

[예외 연쇄의 전형적인 형태]

try {
    // 저수준 추상화를 이용한다.
} catch (LowerLevelException cause) {
    // 저수준 예외를 고수준 예외에 실어 보낸다.
    throw new HigherLevelException(cause);
}

예외 연쇄는 고수준 에러 메시지를 제공하면서도 스택 추적(Stack Trace) 정보에 원인이 된 저수준 예외 정보까지 통합해 주기 때문에 문제 분석에 매우 유용합니다.

3. 무턱대고 예외를 던지기 전의 주의사항

자료에서는 예외 번역이 우수한 방법이긴 하지만, 남용해서는 안 된다고 조언합니다.

  1. 최선책: 가능하면 저수준 메서드가 반드시 성공하도록 하여 아래 계층에서 예외가 발생하지 않도록 하는 것이 가장 좋습니다. 상위 계층에서 매개변수 유효성을 미리 검사하여 예외 발생을 차단하세요.
  2. 차선책: 아래 계층의 예외를 피할 수 없다면, 상위 계층에서 그 예외를 조용히 처리하고 로그(logging)를 남겨 관리자가 분석할 수 있게 하세요. 이렇게 하면 클라이언트까지 에러가 전파되지 않으면서도 문제를 추적할 수 있습니다.

요약하자면

  • 예외 번역: 내부 구현 에러를 사용자에게 직접 보여주지 말고, API 수준에 맞는 의미 있는 예외로 바꿔서 던지세요.
  • 예외 연쇄: 번역할 때는 원래 에러(원인)를 함께 담아서 던져야 나중에 개발자가 원인을 분석하기 좋습니다.
  • 근본 해결: 예외를 던지기 전에 애초에 예외가 발생하지 않도록 방어 로직을 짜는 것이 가장 훌륭한 설계입니다.

 레스토랑(고수준 API)에서 손님이 요리를 주문했는데, 주방 기구가 고장 났다고 해서 지배인이 손님에게 "오븐 제어 보드의 전압 회로가 탔습니다"라고 말하는 것은 부적절합니다 [Item 73]. 지배인은 손님에게 "주방 사정으로 현재 요리 제공이 불가능합니다(예외 번역)"라고 안내해야 합니다. 다만, 나중에 수리 기사(개발자)가 오면 "전압 회로가 탔던 것이 원인입니다(예외 연쇄)"라는 기록을 보여주어야 정확히 수리할 수 있는 것과 같습니다.

 

아이템 74. 메서드가 던지는 모든 예외를 문서화하라

  • 쉽게 말하면: 내 메서드에서 어떤 에러가 날 수 있는지 친절하게 주석(Javadoc)으로 적어주세요.
  • 자세히: 검사 예외뿐만 아니라 비검사 예외(Runtime)도 @throws 태그를 써서 문서화해야 합니다. 그래야 내 코드를 쓰는 동료가 "아, 이럴 땐 이런 에러가 나니까 조심해야겠구나"라고 알 수 있습니다. 단, 비검사 예외는 메서드 선언(throws 절)에는 적지 않는 것이 관례입니다.

1. 왜 모든 예외를 문서화해야 하나요?

메서드가 던지는 예외는 그 메서드를 올바르게 사용하는 데 필요한 아주 중요한 정보입니다. 어떤 상황에서 에러가 발생하는지 모른다면, 사용자는 그 메서드를 안전하게 호출할 수 없습니다. 따라서 각 예외가 발생하는 상황을 자바독(Javadoc)의 @throws 태그를 사용해 정확히 기록해야 합니다.

2. 검사 예외(Checked)와 비검사 예외(Unchecked) 기록법

자바에서는 예외의 종류에 따라 기록하는 관례가 다릅니다. 이 부분을 잘 지켜야 "자바 좀 할 줄 아는" 개발자라는 인상을 줄 수 있습니다.

  • 검사 예외 (Checked Exception):
    • 방법: @throws 태그에도 적고, 메서드 선언부의 throws 절에도 명시합니다.
    • 이유: 자바 언어 자체가 이 예외를 처리하도록 강제하기 때문입니다.
  • 비검사 예외 (Unchecked/Runtime Exception):
    • 방법: @throws 태그에는 정성껏 적되, 메서드 선언부의 throws 절에는 적지 않습니다.
    • 이유 (중요!): 이렇게 구분해서 적어야 사용자가 "아, 이 예외는 내가 반드시 처리해야 하는 것(검사 예외)이고, 저 예외는 내 코드의 버그를 고쳐야 하는 것(비검사 예외)이구나"라고 시각적으로 바로 구분할 수 있기 때문입니다.

3. 문서화할 때 주의할 점 (꿀팁)

  • 뭉뚱그리지 말고 구체적으로 적으세요: @throws Exception이나 @throws Throwable처럼 광범위한 상위 클래스를 던진다고 선언해서는 안 됩니다. 메서드 사용자가 각 에러 상황에 맞춰 대처할 수 있도록 IllegalArgumentException이나 NullPointerException처럼 가장 구체적인 예외를 명시하세요.
  • 인터페이스 메서드라면 더 꼼꼼히! 인터페이스 메서드에서 비검사 예외를 문서화하는 것은 매우 중요합니다. 이는 해당 인터페이스를 구현하는 모든 클래스가 지켜야 할 **'일반 규약'**이 되어, 시스템 전체의 동작을 일관되게 만들어주기 때문입니다.
  • 클래스 수준 문서화 활용하기: 만약 클래스 안의 모든 메서드가 똑같은 이유로 같은 예외를 던진다면(예: 모든 메서드에 null을 넣으면 NullPointerException이 발생하는 경우), 각 메서드마다 적는 대신 클래스 설명 주석에 한 번만 적어도 됩니다.

요약

구분Javadoc (@throws)메서드 선언 (throws)

검사 예외 작성 필수 작성 필수
비검사 예외 작성 권장 작성하지 않음

비유 (Analogy): 이 원칙은 마치 전자제품 설명서와 같습니다.

  • 검사 예외 문서화: "배터리가 다 되면 빨간 불이 들어오니 충전하세요(복구 가능)"라고 안내하는 것과 같습니다.
  • 비검사 예외 문서화: "물에 넣으면 고장 납니다(사용자 주의)"라고 경고문을 적어주는 것과 같습니다.

설명서에 "고장 날 수 있음"이라고만 적혀 있다면(상위 클래스로 뭉뚱그리기), 사용자는 겁나서 제품을 쓰지 못할 것입니다. 구체적으로 어떤 상황에서 어떤 문제가 생기는지 적어주는 것이 진정으로 친절한 개발자의 태도입니다.

 

아이템 75. 예외의 상세 메시지에 실패 관련 정보를 담으라

  • 쉽게 말하면: 에러 메시지에 "뭐가 잘못됐는지" 구체적인 숫자나 값을 적어주세요.
  • 자세히: 단순히 "인덱스 오류"라고만 하면 디버깅이 힘듭니다. "인덱스는 10인데, 배열 크기는 5입니다"라고 메시지를 남기면 문제를 훨씬 빨리 고칠 수 있습니다.
  • 주의: 비밀번호나 계좌번호 같은 민감한 정보는 절대 메시지에 담으면 안 됩니다.

1. 예외 상세 메시지의 역할

프로그램이 실패했을 때 자바 시스템은 예외의 스택 추적(stack trace) 정보를 자동으로 출력합니다. 이때 출력되는 정보 중 하나가 예외 객체의 toString 메서드가 반환하는 메시지인데, 이 메시지는 사후 분석을 위해 실패 원인을 파악할 수 있는 유일한 정보인 경우가 많습니다. 따라서 이 메시지 안에 실패 당시의 상황을 정확히 담는 것이 매우 중요합니다.

2. 무엇을 담아야 하는가?

  • 실패 원인과 관련된 모든 데이터: 예외를 일으킨 상황에 관여된 모든 매개변수와 필드의 값을 상세 메시지에 담아야 합니다.
  • 예시 (인덱스 오류): IndexOutOfBoundsException이 발생했다면, 단순히 "인덱스 오류"라고 할 것이 아니라 다음의 세 가지 정보를 모두 포함해야 합니다:
    1. 범위의 최솟값 (예: 0)
    2. 범위의 최댓값 (예: 배열 크기 - 1)
    3. 실제로 입력된 잘못된 인덱스 값
  • 이렇게 구체적인 숫자가 있으면 개발자는 "인덱스가 최댓값보다 1 크네?" 혹은 "인덱스가 음수네?"와 같은 정보를 보고 문제를 즉시 고칠 수 있습니다.

3. 주의사항: 보안과 가독성

  • 민감 정보 노출 금지: 보안을 위해 비밀번호, 암호 키, 계좌번호와 같은 정보는 절대 메시지에 담아서는 안 됩니다. 스택 추적 정보는 여러 사람이 볼 수 있기 때문입니다.
  • 장황함보다는 데이터 중심: 소스 코드가 이미 존재하므로 메시지를 길게 늘어놓을 필요는 없습니다. 설명보다는 실패와 관련된 핵심 데이터를 보여주는 데 집중하세요.

4. 더 좋은 예외 설계 방법: 전용 생성자 활용

메시지를 매번 수동으로 작성하다 보면 실수로 중요한 정보를 빠뜨릴 수 있습니다. 이를 방지하기 위해 예외 클래스에 데이터를 직접 받는 생성자를 만드는 것을 강력히 권장합니다.

  • 나쁜 예: throw new IndexOutOfBoundsException("범위 초과: 10"); (최솟값, 최댓값 정보 누락 위험)
  • 좋은 예:
    /**
     * IndexOutOfBoundsException을 생성한다.
     * @param lowerBound 인덱스의 최솟값
     * @param upperBound 인덱스의 최댓값 + 1
     * @param index 실제 입력된 인덱스 값
     */
    public IndexOutOfBoundsException(int lowerBound, int upperBound, int index) {
        // 상세 메시지를 자동으로 생성하도록 설계
        super(String.format("최솟값: %d, 최댓값: %d, 인덱스: %d", lowerBound, upperBound, index));
    
        // 사후 분석을 위해 실패 정보를 따로 저장해둠
        this.lowerBound = lowerBound;
        this.upperBound = upperBound;
        this.index = index;
    }
    
  • 이렇게 설계하면 개발자가 실패 메시지를 작성하는 수고를 덜어줄 뿐 아니라, 반드시 필요한 정보를 담도록 강제하는 효과가 있습니다.

5. 프로그래밍 방식으로 접근하기 (Item 70 연계)

검사 예외(Checked Exception)의 경우, 호출자가 예외 상황에서 복구할 수 있도록 실패 정보를 알려주는 접근자 메서드(Getter)를 함께 제공하는 것이 좋습니다. 예를 들어, "잔액 부족" 예외가 발생했을 때 부족한 금액이 얼마인지 프로그래밍적으로 알 수 있게 해주면 클라이언트 코드가 더 똑똑하게 대처할 수 있습니다.

이 원칙은 블랙박스(비행기 사고 기록 장치)와 같습니다. 비행기가 추락했을 때 "추락함"이라는 메시지만 남긴 블랙박스는 아무런 도움이 되지 않습니다. "고도 1000m에서 엔진 온도 500도, 연료 잔량 5리터였다"라는 구체적인 수치가 담겨 있어야 사고 원인을 밝히고 다음 사고를 막을 수 있는 것과 같습니다. 다만, 그 기록에 승객의 개인정보(민감 정보)를 담을 필요는 없는 것과 같은 원리입니다.

 

아이템 76. 가능한 한 실패 원자적으로 만들라

1. '실패 원자성(Failure-Atomic)'이란?

호출된 메서드가 실패하더라도, 해당 객체는 호출 전 상태를 유지해야 한다는 성질을 말합니다. 즉, 작업이 "전부 성공하거나, 아니면 아예 시작도 안 한 것처럼" 남아야 한다는 뜻입니다. 만약 메서드 실행 중간에 예외가 발생했는데 객체 내부 값이 일부만 바뀌어 버린다면, 그 객체는 앞으로 어떻게 동작할지 예측할 수 없는 위험한 상태가 됩니다.

2. 실패 원자성을 만드는 4가지 방법

① 객체를 '불변(Immutable)'으로 설계하기

가장 쉬운 방법입니다. 불변 객체는 태생적으로 실패 원자적입니다.

  • 이유: 객체가 생성된 후에는 상태가 절대 변하지 않기 때문에, 메서드 호출이 실패하더라도 기존 상태가 변할 리가 없습니다. 새로운 객체를 만드는 과정에서 예외가 발생할 수는 있지만, 기존에 있던 객체는 여전히 안전합니다.

② 작업 전 '유효성'부터 검사하기

가변 객체를 다룰 때 가장 많이 쓰이는 방법입니다. 객체 내부 상태를 바꾸기 전에, 들어온 값이 올바른지 먼저 확인하는 것입니다.

  • 사례: Stack.pop() 메서드를 생각해 봅시다.
    public Object pop() {
        if (size == 0) // 유효성 검사!
            throw new EmptyStackException();
        Object result = elements[--size];
        elements[size] = null;
        return result;
    }
    
    이 코드에서 if문 검사가 없다면, 스택이 비어있을 때 elements[--size]를 실행하다가 ArrayIndexOutOfBoundsException이 발생합니다. 문제는 이때 size 값이 이미 -1로 줄어들어 버려, 다음 메서드 호출 시에도 계속 에러를 일으키게 된다는 점입니다. 미리 검사하면 예외가 발생해도 size 값은 변하지 않습니다.

③ 실패할 가능성이 있는 코드를 수정 코드보다 앞에 두기

계산 로직 중 에러가 날 만한 부분을 수정 로직보다 먼저 실행하는 방식입니다.

  • 사례: TreeMap은 원소를 추가하기 전에 해당 원소가 TreeMap의 정렬 기준에 맞는 타입인지 먼저 확인합니다. 만약 타입이 맞지 않아 ClassCastException이 발생하더라도, 트리의 구조는 전혀 바뀌지 않은 상태로 유지됩니다.

④ '임시 복사본'에서 작업하고 성공하면 교체하기

데이터를 직접 건드리지 않고, 일단 복사본을 만들어 거기서 모든 작업을 다 해보는 것입니다.

  • 방법:
    1. 데이터를 임시 자료구조(복사본)에 저장합니다.
    2. 복사본에서 작업을 수행합니다.
    3. 작업이 에러 없이 완벽히 끝났을 때만 실제 데이터와 교체합니다.
  • 사례: 리스트를 정렬할 때, 정렬 전 리스트를 배열로 복사하여 정렬을 수행한 후 다시 리스트로 옮기는 방식입니다. 정렬 중에 문제가 생겨도 원래 리스트는 그대로 보존됩니다.

3. 주의할 점

실패 원자성은 항상 달성할 수 있는 것은 아닙니다.

  • 복구 불가능한 에러: 두 스레드가 동기화 없이 같은 객체를 수정하다가 ConcurrentModificationException이 발생한 경우, 이미 객체는 오염되었을 가능성이 큽니다.
  • 비용 문제: 실패 원자성을 위해 데이터를 매번 복사하거나 복잡한 로직을 추가하는 것이 성능에 큰 지장을 준다면 과감히 포기해야 할 수도 있습니다.
  • 문서화 필수: 만약 실패 원자성을 지키지 못한다면, 예외 발생 시 객체의 상태가 어떻게 변하는지 API 설명에 반드시 명시해야 합니다.

이 원칙은 은행 송금 시스템과 같습니다. 상대방에게 10만 원을 보내는데, 내 통장에서 돈만 빠져나가고(수정 시작) 상대방 통장에 입금되는 과정에서 서버 에러가 났을 때(예외 발생), 내 통장 잔액이 그대로 10만 원이 줄어든 상태로 남는다면 대재앙이겠죠? 은행 시스템은 이럴 때 송금 전 상태로 완벽히 되돌려놓아야(실패 원자성) 합니다. 그래야 사용자가 다시 송금을 시도하거나 안심하고 다른 일을 할 수 있기 때문입니다.

 

아이템 77. 예외를 무시하지 말라

1. 빈 catch 블록은 '시한폭탄'과 같습니다

API 설계자가 메서드 선언에 예외를 명시한 이유는, 그 상황을 적절히 조치해달라고 호출자에게 알리는 것입니다. 그런데 catch 블록을 비워두면 예외가 존재할 이유가 사라집니다.

  • 문제점: 에러가 발생해도 프로그램은 아무 일 없다는 듯 계속 실행됩니다. 하지만 내부적으로는 이미 문제가 생긴 상태(오염된 상태)일 확률이 높습니다.
  • 결과: 진짜 문제는 나중에 엉뚱한 곳에서 터지게 되며, 이때는 이미 원인이 된 지점과 멀어져 있어 버그의 원인을 찾기가 극도로 어려워집니다.

화재 경보기를 끄지 마세요 (비유)

소스에서는 예외를 화재 경보에 비유합니다.

  • 경보기가 시끄럽다고 꺼버리면(예외 무시), 당장은 조용해서 좋을지 모릅니다.
  • 하지만 실제로 불이 났을 때 아무도 알지 못하게 되어 결국 건물 전체가 타버리는(시스템 붕괴) 참사로 이어질 수 있습니다.

3. 어쩔 수 없이 무시해야 할 때의 올바른 대처법

물론 모든 예외를 완벽히 처리할 수 없는 경우도 있습니다. 예를 들어, 파일을 닫는(close) 과정에서 발생하는 예외는 이미 필요한 정보를 다 읽었기 때문에 복구할 것이 없을 때가 있습니다.

만약 예외를 무시하기로 결정했다면, 다음의 두 가지 수칙을 반드시 지켜야 합니다:

  1. 변수 이름 변경: 예외 변수의 이름을 e 대신 ignored로 바꿉니다.
  2. 주석 추가: 왜 이 예외를 무시해도 괜찮은지 그 이유를 명확한 주석으로 남깁니다.

[올바른 예시 코드]

try {
    // ... 로직
} catch (TimeoutException ignored) {
    // 기본값을 사용하도록 설계되었으므로 타임아웃 예외를 무시함
}

4. 최소한의 조치: 로그라도 남기세요

예외를 어떻게 처리할지 당장 떠오르지 않는다면, 최소한 로그(log)라도 남겨야 합니다.

  • 비검사 예외(Runtime Exception)라 할지라도 무시하고 지나치면 프로그램이 나중에 원인 모를 동작을 하게 됩니다.
  • 적절히 처리할 수 없다면 차라리 예외를 밖으로 던져서 프로그램이 즉시 중단되게 하는 것이 낫습니다. 그래야 나중에 디버깅 정보를 보고 문제를 바로잡을 수 있기 때문입니다.

요약

예외 처리가 귀찮아서 혹은 당장 에러를 안 보고 싶어서 catch 블록을 비워두는 습관은 나중에 감당할 수 없는 큰 비용으로 돌아옵니다. 무시할 가치가 있는 예외는 거의 없으며, 정말 무시해야 한다면 반드시 주석과 변수명으로 그 의도를 문서화해야 합니다.

728x90
반응형