본문 바로가기
홍TV 홍TV

“Java 예외처리”, e.toString() vs e.getMessage() vs e.printStackTrace() 차이

홍TV 읽는 시간 약 14분
4.5
(300)

들어가며

Java로 개발하다 보면 예외가 발생했을 때 가장 먼저 확인하는 것이 있습니다. 바로 로그입니다.

개발을 하다 보면 e.toString(), e.getMessage(), e.printStackTrace() 같은 코드를 한 번쯤은 사용하게 됩니다.

처음에는 세 가지가 모두 비슷해 보입니다.

어차피 예외가 발생했을 때 에러 내용을 확인하기 위한 코드라고 생각하기 쉽기 때문입니다.

그런데 실제로 출력해 보면 결과가 조금씩 다릅니다.

특히 개발 중에는 큰 차이를 느끼지 못할 수도 있지만, 운영 서버에서 문제가 발생했을 때는 어떤 방식으로 예외를 기록했느냐에 따라 원인을 찾는 시간이 크게 달라질 수 있습니다.

이번 글에서는 Java 예외처리에서 자주 사용하는 e.toString(), e.getMessage(), e.printStackTrace()의 차이를 직접 예제와 함께 정리해보겠습니다.

Java 예외처리 테스트를 위해 일부러 오류 발생시키기

먼저 가장 간단한 예제를 만들어보겠습니다.

public class ExceptionTest {

    public static void main(String[] args) {

        try {
            int result = 10 / 0;
        } catch (Exception e) {
            System.out.println(e.toString());
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
    }
}

위 코드는 10 / 0이라는 연산을 실행하기 때문에 ArithmeticException이 발생합니다.

실행하면 대략 다음과 같은 결과를 확인할 수 있습니다.

java.lang.ArithmeticException: / by zero
/by zero

java.lang.ArithmeticException: / by zero
    at ExceptionTest.main(ExceptionTest.java:7)

같은 예외 객체 e를 사용했는데도 출력되는 내용이 서로 다릅니다.

이제 각각 어떤 차이가 있는지 살펴보겠습니다.

e.toString()은 예외 클래스와 메시지를 출력한다

먼저 e.toString()입니다.

catch (Exception e) {
    System.out.println(e.toString());
}

실행 결과는 다음과 같습니다.

java.lang.ArithmeticException: / by zero

여기에는 예외의 클래스 이름과 메시지가 함께 표시됩니다.

즉,

예외 클래스 + 예외 메시지

라고 생각하면 이해하기 쉽습니다.

예를 들어 직접 예외 메시지를 만들어보겠습니다.

throw new Exception("파일을 찾을 수 없습니다.");

이때 e.toString()을 출력하면 다음과 비슷하게 나타납니다.

java.lang.Exception: 파일을 찾을 수 없습니다.

getMessage()와 비교하면 어떤 종류의 예외가 발생했는지까지 확인할 수 있다는 차이가 있습니다.

다만 toString()만으로는 예외가 정확히 어느 코드에서 발생했는지 알 수 없습니다.

즉 다음과 같은 정보만 확인할 수 있습니다.

java.lang.ArithmeticException: / by zero

실제 프로젝트에서는 이 정보만 가지고 원인을 찾기 어려운 경우가 많습니다.

e.getMessage()는 예외 메시지만 가져온다

다음은 e.getMessage()입니다.

catch (Exception e) {
    System.out.println(e.getMessage());
}

실행 결과는 다음과 같습니다.

/by zero

getMessage()는 말 그대로 예외 객체에 저장되어 있는 메시지를 반환합니다.

예를 들어 다음과 같이 예외를 발생시켰다고 가정해보겠습니다.

throw new Exception("사용자 정보를 찾을 수 없습니다.");

이때 e.getMessage()를 호출하면 다음과 같이 출력됩니다.

사용자 정보를 찾을 수 없습니다.

간단한 메시지만 필요할 때는 상당히 편리합니다.

하지만 여기에도 한 가지 주의할 부분이 있습니다.

getMessage()만 사용하면 어떤 예외 클래스에서 문제가 발생했는지 알 수 없습니다.

예를 들어 로그에 다음 내용만 남았다고 생각해보겠습니다.

사용자 정보를 찾을 수 없습니다.

이 로그만 봐서는 어떤 Exception인지, 어느 클래스에서 발생했는지, 몇 번째 줄에서 문제가 발생했는지 알기 어렵습니다.

따라서 장애 원인을 분석해야 하는 로그라면 getMessage()만 사용하는 것은 정보가 부족할 수 있습니다.

e.printStackTrace()는 예외 발생 위치까지 확인할 수 있다

Java 예외처리를 처음 배울 때 가장 많이 접하는 코드가 바로 printStackTrace()입니다.

catch (Exception e) {
    e.printStackTrace();
}

실행하면 다음과 같은 형태로 출력됩니다.

java.lang.ArithmeticException: / by zero
    at ExceptionTest.main(ExceptionTest.java:7)

앞에서 봤던 toString()이나 getMessage()와 비교하면 출력되는 정보가 훨씬 많습니다.

printStackTrace()는 예외의 Stack Trace를 출력합니다.

일반적으로 다음과 같은 정보를 확인할 수 있습니다.

  • 예외 클래스
  • 예외 메시지
  • 예외가 발생한 코드 위치
  • 메서드 호출 경로
  • 예외가 전달된 호출 과정

실제로 프로젝트에서 문제가 발생하면 이 Stack Trace가 상당히 중요합니다.

예를 들어 애플리케이션의 호출 구조가 다음과 같다고 가정해보겠습니다.

Controller
    ↓
Service
    ↓
Repository
    ↓
Database

Repository에서 예외가 발생했다면 Stack Trace를 통해 어떤 메서드를 거쳐 해당 코드까지 실행됐는지 추적할 수 있습니다.

그래서 개발 과정에서 예외의 원인을 찾을 때 printStackTrace()가 유용하게 사용됩니다.

e.toString(), e.getMessage(), e.printStackTrace() 차이

세 가지를 표로 정리하면 훨씬 간단합니다.

방법출력되는 주요 정보
e.toString()예외 클래스 + 메시지
e.getMessage()예외 메시지
e.printStackTrace()예외 정보 + Stack Trace

예를 들어 다음과 같은 예외가 발생했다고 가정해보겠습니다.

throw new NullPointerException("사용자 정보가 없습니다.");

e.toString()은 다음과 같습니다.

java.lang.NullPointerException: 사용자 정보가 없습니다.

e.getMessage()는 다음과 같습니다.

사용자 정보가 없습니다.

그리고 e.printStackTrace()는 다음과 비슷하게 출력됩니다.

java.lang.NullPointerException: 사용자 정보가 없습니다.
    at UserService.findUser(UserService.java:25)
    at UserController.getUser(UserController.java:18)
    ...

이렇게 비교해보면 세 가지의 차이가 확실히 보입니다.

Java 실무에서는 어떤 방식으로 예외를 처리할까?

여기서 한 가지 더 생각해볼 부분이 있습니다.

개발하면서 간단하게 예외를 확인할 때는 다음과 같이 작성하는 경우가 많습니다.

catch (Exception e) {
    e.printStackTrace();
}

하지만 실제 운영 환경에서는 printStackTrace()를 무조건 사용하는 것보다 로그 프레임워크를 사용하는 것이 일반적입니다.

예를 들어 SLF4J를 사용하는 경우 다음과 같이 작성할 수 있습니다.

private static final Logger log =
        LoggerFactory.getLogger(MyService.class);

try {

    // 작업 수행

} catch (Exception e) {

    log.error("사용자 조회 중 예외가 발생했습니다.", e);
}

여기서 중요한 부분은 마지막에 e를 전달하는 것입니다.

log.error("사용자 조회 중 예외가 발생했습니다.", e);

이렇게 하면 단순히 문자열만 기록하는 것이 아니라 예외 객체를 함께 전달하기 때문에 Stack Trace까지 로그로 남길 수 있습니다.

log.error()에서 getMessage()만 사용하는 경우

다음과 같은 코드도 자주 볼 수 있습니다.

log.error("사용자 조회 실패 : {}", e.getMessage());

이 방식은 예외 메시지만 로그에 남깁니다.

예를 들어 다음과 같은 로그가 만들어질 수 있습니다.

사용자 조회 실패 : Connection failed

간단한 메시지를 남기는 용도라면 괜찮지만, 장애 원인을 추적해야 하는 상황에서는 부족할 수 있습니다.

반면 다음과 같이 작성하면,

log.error("사용자 조회 중 예외 발생", e);

예외 메시지뿐만 아니라 Stack Trace까지 확인할 수 있습니다.

실제 운영 환경에서는 어떤 정보가 필요한지 판단해서 로그를 남기는 것이 중요합니다.

e.getMessage()만 사용하면 문제가 되는 이유

개발을 하다 보면 다음과 같은 코드를 작성할 때가 있습니다.

catch (Exception e) {
    log.error(e.getMessage());
}

문법적으로 잘못된 코드는 아닙니다.

하지만 나중에 운영 서버에서 장애가 발생했을 때 문제가 될 수 있습니다.

예를 들어 로그가 다음처럼 남는다고 생각해보겠습니다.

Connection failed

이것만 가지고는 정확한 원인을 파악하기 어렵습니다.

어느 클래스에서 발생했는지, 어느 메서드에서 발생했는지, 어떤 호출 과정을 거쳐서 문제가 발생했는지 알 수 없기 때문입니다.

반면 다음과 같이 예외 객체를 함께 전달하면,

catch (Exception e) {
    log.error("DB 연결 중 예외 발생", e);
}

Stack Trace를 통해 문제 발생 위치를 추적할 수 있습니다.

물론 모든 로그에 무조건 Stack Trace를 남겨야 한다는 뜻은 아닙니다.

단순한 안내성 메시지나 예상 가능한 상황에서는 메시지만 기록하는 것이 더 적절할 수도 있습니다.

결국 중요한 것은 어떤 목적으로 로그를 남기는가입니다.

처음에는 헷갈리지만 알고 보면 간단하다

처음 Java 예외처리를 공부할 때는 e.toString()e.getMessage()의 차이가 생각보다 헷갈립니다.

둘 다 예외와 관련된 문자열을 반환하기 때문입니다.

하지만 직접 비교해보면 상당히 단순합니다.

e.toString()

예외 클래스 + 메시지

입니다.

e.getMessage()

예외 메시지

입니다.

그리고

e.printStackTrace()

예외 정보 + 예외 발생 경로

라고 이해하면 됩니다.

이렇게 기억해두면 나중에 Java 코드를 보다가 예외처리 부분을 만났을 때도 각각 어떤 목적으로 사용됐는지 쉽게 이해할 수 있습니다.

Java 예외처리 최종 정리

마지막으로 세 가지를 다시 정리해보겠습니다.

e.toString();

예외 클래스와 메시지를 확인하고 싶을 때 사용할 수 있습니다.

e.getMessage();

예외 메시지만 가져오고 싶을 때 사용할 수 있습니다.

e.printStackTrace();

예외 발생 위치와 호출 경로까지 확인하고 싶을 때 사용할 수 있습니다.

그리고 실무에서는 단순히 System.out.println()이나 e.printStackTrace()를 사용하는 것보다는 SLF4J, Logback 등의 로깅 환경을 구성하고 적절한 로그 레벨에 맞춰 예외를 기록하는 방식이 좋습니다.

특히 다음 두 코드는 비슷해 보이지만 실제로 남는 정보에는 차이가 있습니다.

log.error("처리 중 오류 : {}", e.getMessage());

그리고

log.error("처리 중 오류", e);

첫 번째는 예외 메시지를 중심으로 기록하는 방식이고, 두 번째는 예외 객체를 함께 전달해 Stack Trace까지 확인할 수 있는 방식입니다.

Java 예외처리에서 중요한 것은 특정 메서드를 무조건 사용하는 것이 아닙니다.

개발 단계인지 운영 환경인지, 단순히 메시지만 필요한 것인지, 아니면 장애의 원인을 추적해야 하는 것인지에 따라 적절한 방법을 선택하는 것이 중요합니다.

결국 예외 로그는 단순히 “에러가 발생했다”는 것을 알려주는 용도가 아니라 문제가 발생했을 때 개발자가 원인을 찾아낼 수 있도록 도와주는 기록이라고 생각하면 됩니다.

그래서 Java 개발을 하면서 e.toString(), e.getMessage(), e.printStackTrace()의 차이를 확실하게 알아두면 예외처리 코드를 작성할 때도 훨씬 수월해집니다.

이 게시물이 얼마나 유용했나요?

별점을 클릭하여 평가하세요!

평균 평점 4.5 / 5. 투표 수: 300

아직 투표가 없습니다! 첫 번째로 평가해보세요.

홍TV

홍TV
함께 보면 좋은 글

댓글 0

첫 댓글을 남겨보세요.

error: Content is protected !!

광고 차단 알림

광고 클릭 제한을 초과하여 광고가 차단되었습니다.

단시간에 반복적인 광고 클릭은 시스템에 의해 감지되며, IP가 수집되어 사이트 관리자가 확인 가능합니다.