예외, 스프링에서 전역 예외 처리 방식, Collectors

최고고·2025년 5월 20일
0

Checked Exception

  • 컴파일 타임에 반드시 처리해야 함 (try-catch나 throws로)
    예) IOException, SQLException, FileNotFoundException

Unchecked Exception

  • 런타임에 발생하며, 꼭 처리하지 않아도 됨 (원하면 try-catch 가능)
    예) NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException

try-with-resources : AutoCloseable을 구현한 객체(FileReader, BufferedReader, Connection 등)

  • try 안에 선언만 해도 자동으로 close() 해줌 -> finally 없이도 자동으로 close

예외 다형성 (Exception 계층 구조)

  • Exception : 모든 예외의 부모 클래스.
    -> catch (Exception e)는 대부분의 예외를 잡을 수 있지만, 특정 예외를 먼저 잡고, 마지막에 Exception을 써야함.
  • 예외 전환 (Wrapping / Re-throwing)
    로우레벨 예외를 좀 더 의미 있는 예외로 바꿔서 던지기
    예 )
try {
    // JDBC 수행
} catch (SQLException e) {
    throw new RuntimeException("DB 처리 중 오류 발생", e); // 원인 예외 e 포함
}
  • catch 블록은 최대한 구체적인 예외부터 → 범용 Exception은 마지막에
    finally는 자원 해제(close(), disconnect() 등)에 자주 쓰임

Throwable : 던질 수 있는 모든 예외 또는 에러의 조상 클래스

         Object
            ▲
       Throwable
       ▲       ▲
  Exception   Error
  • Throwable이 가지는 대표적인 자식들
    클래스 설명
    Exception 예외. 우리가 try-catch 로 잡아서 처리할 수 있음
    Error 시스템/VM 문제. 코드로 처리 안 함 (ex. 메모리 부족 등)

  • 왜 Throwable이 따로 있냐?
    Exception 과 Error 를 하나로 처리할 수 있게 하려고 만든 최상위 클래스
    그래서 catch (Throwable t) 이렇게 쓰면 모든 예외 + 에러까지 다 잡을 수 있어
    (하지만 Error는 웬만하면 안 잡는 게 좋음)

  • 주요 메서드

메서드설명
getMessage()예외 메시지를 리턴
printStackTrace()예외가 어디서 났는지 콘솔에 출력
getCause()원인 예외 리턴 (다른 예외를 감쌌을 경우)
toString()예외 클래스명 + 메시지 문자열 리턴

Exception vs Error vs Throwable

종류설명예시
Exception우리가 자주 쓰는 예외 (파일 못 찾음, DB 오류 등)IOException, NullPointerException
Error시스템 자체 문제 (메모리 부족 등) → 보통 catch 안 함OutOfMemoryError, StackOverflowError
Throwable이 둘의 조상. 모든 예외/에러를 포괄catch (Throwable t) 가능
  • error : 자바에서 시스템 레벨의 치명적인 문제가 생겼을 때 던져지는 클래스
    예외처럼 catch 할 수는 있지만, 웬만하면 처리하지 마라가 기본 원칙.
    JVM이나 시스템 자체 문제
  • 대표적인 Error 종류
    에러 종류 설명
    OutOfMemoryError 메모리가 부족해서 더 이상 객체를 생성할 수 없음
    StackOverflowError 재귀 호출 너무 많이 해서 call stack이 넘침
    NoClassDefFoundError 필요한 클래스 파일을 JVM이 못 찾음
    InternalError JVM 내부에서 심각한 문제가 발생
    AssertionError assert 문 실패 시 발생 (테스트/디버깅용)

Exception vs Error

구분ExceptionError
대상네 코드에서 발생하는 예외시스템/JVM 문제
예시IOException, NullPointerExceptionOutOfMemoryError, StackOverflowError
처리try-catch로 처리 권장처리 안 함이 원칙
목적복구 가능한 문제복구 불가능한 문제

스프링에서 전역 예외 처리 방식

ㄴController단 예외 처리 (@ControllerAdvice, @ExceptionHandler)
1. @RestControllerAdvice 또는 @ControllerAdvice
전역 예외 처리 클래스로 지정하는 어노테이션
@ControllerAdvice + @ResponseBody = @RestControllerAdvice
2. @ExceptionHandler(예외클래스.class)
해당 예외가 발생하면 실행되는 예외 처리 메서드

어노테이션설명
@ExceptionHandler특정 예외를 처리하는 메서드 (컨트롤러 안/밖 모두 사용 가능)
@ControllerAdvice모든 컨트롤러의 예외를 전역적으로 처리
@RestControllerAdvice위 + API 응답용 (JSON 리턴)
  • ResponseEntity : 컨트롤러에서 응답 바디 + HTTP 상태코드 + 헤더를 함께 내려주는 객체

  • 사용자클래스가 Serializable (파일로 저장하거나 네트워크로 보내는 용도)을 구현했는데, serialVersionUID 라는 필드를 선언 안 했다는 경고
    The serializable class 사용자Exception does not declare a static final serialVersionUID field of type long
    ㄴ 직렬화 땜에 명시적으로 넣는게 안정성에좋다.
    serialVersionUID는 뭐임? 직렬화된 객체의 버전 번호임
    왜 넣으라고 경고함? 클래스 구조가 바뀌어도 이전 객체를 제대로 읽기 위해서
    안 넣으면 안됨? 동작은 되지만, 클래스 바뀌면 에러 날 수 있음
    해결 방법은? private static final long serialVersionUID = 1L; 이거 한 줄 넣으면 끝

Collectors

예 )


List<String> names = List.of("이름1", "이름2", "이름3");

List<String> upperNames = names.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList()); // 결과를 List로 수집

자주 쓰는 Collectors

Collectors 메서드결과 타입설명
toList()List<T>리스트로 수집
toSet()Set<T>중복 제거 후 수집
toMap()Map<K, V>키-값으로 변환
joining()String문자열 병합
groupingBy()Map<K, List<V>>그룹핑
partitioningBy()Map<Boolean, List<V>>조건 기반 분리
counting()Long요소 개수
summarizingInt()IntSummaryStatistics합계/평균/최대/최소 다 계산
  • .stream() — 컬렉션에서 스트림 꺼낼 때
    List, Set, Map.keySet(), Map.entrySet() 같은 컬렉션 객체에서 사용
    컬렉션이 이미 있을 때, 스트림 변환용

  • Stream.of(...) — 스트림을 직접 생성할 때
    배열이나 값 나열할 때 바로 스트림 만들고 싶을 때
    컬렉션이 없는 경우에도 사용 가능

0개의 댓글