[Java/SpringBoot] Spring에서 예외를 던지지 않고 API 응답은 유지하며 로그는 ERROR로 남기지 않도록 처리하는 방법

2025. 6. 28. 17:51·개발 (Development)/Java

Spring 기반의 백엔드 개발을 하다 보면 다음과 같은 요구사항이 생길 수 있습니다.

  • 클라이언트에게는 일정한 응답 포맷을 유지하면서
  • 내부적으로는 경고 상황임을 감지해야 하며
  • 로그에는 ERROR 레벨이 아닌 WARN 또는 INFO 수준으로만 기록하고 싶다

이 글에서는 위와 같은 상황에서 예외 처리 방식을 어떻게 구성하면 좋을지 실제 구현 예제를 기반으로 정리하였습니다.

문제 상황

예외가 발생할 때 다음과 같이 @ResponseStatus가 지정되어 있는 커스텀 예외를 사용하고 있었습니다.

@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "custom error")
public class CustomException extends RuntimeException {
    public CustomException(ErrorCode code, String message) {
        super(message);
    }
}

서비스 로직에서는 특정 조건에서 이 예외를 던지고 있었습니다:

if (!inputData.isValid()) {
    throw new CustomException(ErrorCode.INVALID_INPUT, "Invalid input detected");
}

문제점

  • @ResponseStatus로 인해 예외 발생 시 자동으로 HTTP 400 응답이 반환됨
  • Spring 내부적으로 ERROR 로그가 남게 됨
  • 단순한 경고 상황임에도 과도한 로그 기록이 발생

해결 방안

1. @ResponseStatus 제거

예외 클래스에서 @ResponseStatus 어노테이션을 제거합니다.

- @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "custom error")
public class CustomException extends RuntimeException {
    // ...
}

이제 이 예외는 HTTP 상태코드에 자동 매핑되지 않으며, 전역 예외 처리기에서 직접 제어할 수 있습니다.

2. 전역 예외 처리기 구성

@ControllerAdvice를 통해 전역 예외 처리기를 생성하고, 로그 레벨을 warn 또는 info로 지정합니다.

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(CustomException.class)
    public ResponseEntity<?> handleCustomException(CustomException ex) {
        // ERROR 로그 대신 WARN으로 기록
        log.warn("CustomException 발생: {}", ex.getMessage());

        Map<String, Object> response = new HashMap<>();
        response.put("code", "INVALID_INPUT");
        response.put("message", ex.getMessage());

        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
    }
}

이제 예외가 발생해도 로그에는 ERROR가 아닌 WARN 수준으로만 기록됩니다.

3. 서비스 로직에서 예외를 던지지 않고 메시지로만 처리

예외를 실제로 throw하지 않고, 메시지를 수집해 API 응답에 포함시키는 방식으로 전환합니다.

if (!inputData.isValid()) {
    Map<String, Object> warningDetails = new HashMap<>();
    warningDetails.put("field", "inputData");
    warningDetails.put("reason", "Validation failed");

    log.warn("입력값 유효성 검사 실패: {}", warningDetails);

    warningMessages.add(new WarningMessage(
        "INVALID_INPUT", 
        "Validation failed on inputData",
        warningDetails
    ));

    // throw 없이 처리 계속 진행
}

이렇게 하면 클라이언트에게는 경고 메시지를 포함한 정상적인 응답을 보낼 수 있고, 로그에도 ERROR 없이 필요한 정보만 남길 수 있습니다.

정리

항목 처리 내용
예외 발생 시 ERROR 로그 제거 @ResponseStatus 제거 및 전역 예외 처리기에서 로그 레벨 조정
API 응답 포맷 유지 예외 대신 경고 메시지를 수집하여 응답에 포함
서비스 흐름 제어 throw 없이 흐름을 유지하거나 선택적으로 종료

마무리하며

이 글을 작성하게 된 직접적인 계기는, 매주 서비스 운영 리포트를 작성하는 과정에서 발생한 불편함 때문이었습니다. 저희 팀은 주기적으로 서버 로그를 분석해 에러 로그를 취합하고, 그 결과를 정리해 보고하는 업무를 진행하고 있습니다.

그런데 실제 시스템 상에서 큰 문제가 아닌 단순 경고 상황조차도 ERROR 레벨 로그로 남아 있다 보니, 매주 리포트 작성 시 다음과 같은 문제가 발생했습니다.

  • 실제로 중요한 오류 로그를 선별하는 데 많은 시간이 소요되고
  • 단순 경고임에도 불필요하게 심각한 이슈처럼 분류되며
  • 반복적으로 의미 없는 항목을 필터링하는 데 추가적인 인적 리소스가 들어가게 됨

이를 해결하기 위해, 예외 처리를 단순히 throw하는 방식이 아니라, 경고 메시지로 기록하고 흐름을 유지하는 방식으로 리팩토링을 시도하게 되었습니다.
그 결과, API 응답의 일관성은 유지하면서도, 운영 리포트 작성 시 정확하고 효율적인 로그 분류가 가능해졌습니다.

이러한 개선 경험을 공유드리며, 같은 고민을 하시는 분들께 도움이 되었으면 합니다.

반응형

'개발 (Development) > Java' 카테고리의 다른 글

[Java] LinkedHashMap에서 특정 값을 가진 항목 제거하는 방법  (0) 2025.07.05
[Java/SpringBoot] Spring OAuth2 시스템에서 발생한 Access Token 만료 및 인증 오류 대응 기록  (2) 2025.06.28
[Java] printStackTrace() 경고 해결 및 로깅 적용하기  (0) 2025.06.28
[Java/Spring Boot] AWS 환경에서 DB 직접 접근이 어려운 경우: Java 애플리케이션에서 SQL 파일을 실행하여 데이터 삽입하기  (2) 2025.05.18
[Java] Java jar 파일에서 리소스 파일 경로 사용하는 법  (0) 2025.05.18
'개발 (Development)/Java' 카테고리의 다른 글
  • [Java] LinkedHashMap에서 특정 값을 가진 항목 제거하는 방법
  • [Java/SpringBoot] Spring OAuth2 시스템에서 발생한 Access Token 만료 및 인증 오류 대응 기록
  • [Java] printStackTrace() 경고 해결 및 로깅 적용하기
  • [Java/Spring Boot] AWS 환경에서 DB 직접 접근이 어려운 경우: Java 애플리케이션에서 SQL 파일을 실행하여 데이터 삽입하기
LoopThinker
LoopThinker
모르는 것을 알아가고, 아는 것을 더 깊게 파고드는 공간
  • LoopThinker
    CodeMemoir
    LoopThinker
  • 전체
    오늘
    어제
    • 분류 전체보기 (238) N
      • 개발 (Development) (171) N
        • Algorithm (1)
        • Angular (1)
        • AWS (7)
        • DeepSeek (2)
        • Docker (7)
        • Git (3)
        • Java (36)
        • JavaScript (4)
        • Kafka (5)
        • Kubernetes (4)
        • Linux (7)
        • PostgreSQL (38)
        • Python (34) N
        • React (3)
        • TypeScript (3)
        • Vue.js (5)
        • General (11)
      • 데이터 분석 (Data Analysis) (1)
      • 알고리즘 문제 풀이 (Problem Solving.. (27)
      • 자격증 (Certifications) (24)
        • ADsP (14)
        • 정보처리기사 (4)
        • Linux Master (5)
        • SQLD (1)
      • 기술 동향 (Tech Trends) (12)
      • 기타 (Others) (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    timescaledb
    백준온라인저지
    ADsP
    java
    JSON
    JPA
    Linux master
    python
    DevOps
    백준
    오답노트
    AWS
    docker
    Kafka
    springboot
    pandas
    파이썬
    리눅스 마스터 2급
    Vue.js
    데이터분석
    javascript
    PostgreSQL
    deepseek
    Linux
    Kubernetes
    MyBatis
    자바
    백준자바
    리눅스 마스터 2급 2차
    백준알고리즘
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
LoopThinker
[Java/SpringBoot] Spring에서 예외를 던지지 않고 API 응답은 유지하며 로그는 ERROR로 남기지 않도록 처리하는 방법
상단으로

티스토리툴바