카테고리 없음

throws 완벽 가이드

ancientib091 2026. 5. 10. 12:52

📌 핵심 답변

throws는 Java 메서드 선언부에 사용하는 키워드로, 해당 메서드가 실행 중 발생시킬 수 있는 checked 예외를 호출자에게 명시적으로 위임하는 역할을 한다. throws는 예외를 직접 처리하지 않고 책임을 상위 메서드로 전파(propagate)하는 Java 예외 처리 메커니즘의 핵심 구성 요소이다.

Java 개발자의 약 90% 이상이 실무에서 예외 처리 코드를 작성하며, 그 중 throws 키워드는 checked exception 관리의 가장 기본적인 도구다. throws를 올바르게 이해하지 못하면 컴파일 에러와 런타임 에러의 차이조차 구분하기 어렵다. 이 글에서는 throws의 개념부터 실전 활용법까지 체계적으로 정리한다.

throws 개요

💡 핵심 요약

throws는 Java 메서드 시그니처에 선언하는 키워드로, 컴파일러에게 "이 메서드는 특정 예외를 발생시킬 수 있으니 호출하는 쪽에서 처리하라"고 알리는 계약(contract) 역할을 한다. throw(예외를 직접 발생)와는 철자와 역할이 다르며 반드시 구별해야 한다.

throws는 1995년 Java 1.0 출시부터 포함된 언어 핵심 키워드다. Java의 예외 계층 구조에서 checked exception(IOException, SQLException 등)은 반드시 try-catch로 처리하거나 throws로 위임해야 컴파일이 통과된다. 반면 unchecked exception(RuntimeException 계열)은 throws 선언 없이도 전파된다. throws는 메서드 선언부에서 반환 타입 뒤, 메서드 바디 앞에 위치하며 쉼표로 구분하여 여러 예외를 동시에 선언할 수 있다. 이 메커니즘은 예외 처리 책임을 명확히 분리하여 코드 가독성과 유지보수성을 높인다.

구분throwthrows
위치메서드 바디 내부메서드 선언부 (시그니처)
역할예외 객체를 직접 발생예외 처리 책임을 호출자에게 위임
대상 예외 수한 번에 1개쉼표로 여러 개 가능
적용 예외 타입checked / unchecked 모두주로 checked exception
  • 예외 계약(Contract): throws 선언은 API 설계 시 호출자와의 명시적 계약이며, 문서화 역할도 겸한다
  • 컴파일러 강제: checked exception을 throws 없이 방치하면 컴파일 에러(unreported exception)가 발생한다
  • 상속 규칙: 오버라이딩 시 자식 메서드는 부모보다 더 넓은 범위의 checked exception을 throws에 추가할 수 없다

throws 특징

💡 핵심 요약

throws의 가장 큰 특징은 예외를 전파(propagate)하는 것이지 처리(handle)하는 것이 아니라는 점이다. throws는 호출 스택(call stack)을 따라 예외 책임을 위로 넘기며, 최종적으로 main 메서드까지 전파되면 JVM이 프로그램을 종료하고 스택 트레이스를 출력한다.

throws는 단순한 문법 요소를 넘어 Java의 예외 처리 철학을 반영한다. 첫째, 예외를 가장 잘 처리할 수 있는 위치에서 처리하도록 유도한다. 하위 레이어(예: DB 접근 계층)에서 발생한 SQLException을 억지로 그 자리에서 처리하는 대신, 비즈니스 로직이나 컨트롤러 레이어로 전파하여 적절한 사용자 피드백을 줄 수 있다. 둘째, 체크 예외 전파 체인을 형성하여 코드의 예외 흐름을 명확하게 추적할 수 있다. 셋째, Java 8 이후 람다와 함수형 인터페이스 환경에서는 checked exception을 throws로 선언하지 못하는 제약이 있어, 이를 우회하기 위한 패턴(sneaky throw 등)이 활용된다.

특징설명실무 영향
예외 전파호출 스택 위로 예외를 전달책임 분리, 계층형 아키텍처에 적합
다중 선언IOException, SQLException 등 복수 가능하나의 메서드가 여러 위험 작업 수행 시 사용
상속 제약오버라이딩 시 예외 범위 축소만 허용인터페이스 설계 시 신중한 예외 선언 필요
Exception 선언throws Exception으로 모든 checked 포괄 가능남용 시 예외 정보 손실, 지양 권장
  • Checked vs Unchecked 구분: throws는 주로 checked exception 선언에 사용되며, RuntimeException 계열은 선언하지 않아도 자동 전파된다
  • 문서화 효과: Javadoc의 @throws 태그와 결합하면 API 사용자에게 예외 처리 가이드를 제공할 수 있다
  • 람다 환경 제약: java.util.function의 표준 함수형 인터페이스는 checked exception을 throws로 선언하지 않아 람다 내부에서 직접 사용이 불가하다

throws 활용법

💡 핵심 요약

throws 활용의 핵심은 "예외를 처리할 능력이 있는 계층에서만 catch하고, 그렇지 않으면 throws로 위임하라"는 원칙이다. 실무에서는 서비스 레이어에서 checked exception을 unchecked exception으로 변환하여 전파하는 패턴이 가장 많이 사용된다.

throws의 올바른 활용을 위해 세 가지 상황을 명확히 구분해야 한다. 첫째, 파일 I/O, 네트워크, DB 접근 등 외부 자원을 다루는 메서드는 IOException, SQLException을 throws로 선언한다. 둘째, 예외를 catch한 뒤 다시 RuntimeException으로 감싸 throw하는 예외 전환 패턴은 Spring Framework에서 표준으로 사용된다. 셋째, 인터페이스 메서드에 throws를 선언하면 구현체가 유연하게 예외를 다룰 수 있다. throws Exception처럼 최상위 예외를 무분별하게 선언하는 것은 호출자가 어떤 예외를 처리해야 하는지 알 수 없게 만들므로 지양해야 한다.

활용 패턴코드 예시사용 시점
단순 위임void read() throws IOException호출자가 처리 가능할 때
다중 예외 선언throws IOException, SQLException여러 위험 작업을 하나의 메서드에서 처리할 때
예외 전환catch(SQLException e) { throw new RuntimeException(e); }checked를 unchecked로 변환 시 (Spring 패턴)
인터페이스 선언interface Repository { void save() throws DataException; }구현체에 예외 처리 유연성을 줄 때
  • try-with-resources 결합: AutoCloseable 자원은 try-with-resources와 throws를 함께 사용하면 자원 누수 없이 예외를 안전하게 위임할 수 있다
  • 커스텀 예외 설계: 도메인별 커스텀 checked exception을 만들고 throws로 선언하면 비즈니스 예외를 의미 있게 표현할 수 있다
  • throws 남용 주의: 모든 메서드에 throws Exception을 선언하면 컴파일러의 예외 체크 기능을 무력화시켜 오히려 코드 품질이 저하된다

마무리

✅ 3줄 요약

  1. throws는 Java 메서드 선언부에 사용하여 checked exception의 처리 책임을 호출자에게 위임하는 키워드이며, throw(예외 발생)와는 역할이 완전히 다르다.
  2. throws의 핵심 원칙은 예외를 가장 잘 처리할 수 있는 계층에서 catch하고, 그렇지 않은 하위 레이어는 throws로 위임하여 책임을 명확히 분리하는 것이다.
  3. 실무에서는 throws Exception처럼 포괄적인 선언을 지양하고, 구체적인 예외 타입을 선언하거나 커스텀 예외와 결합하여 코드의 명확성과 유지보수성을 높여야 한다.

FAQ

Q. throws와 throw의 차이점은 무엇인가요?
A. throw는 예외 객체를 직접 생성하고 발생시키는 명령문이고, throws는 메서드 선언부에 붙여 예외 처리 책임을 호출자에게 위임하는 선언이다. throw는 메서드 바디 안에서 한 번에 하나의 예외를 발생시키며, throws는 시그니처에서 여러 예외를 쉼표로 나열할 수 있다.
Q. throws로 RuntimeException도 선언할 수 있나요?
A. 기술적으로는 가능하지만, RuntimeException(unchecked exception)은 throws 선언 없이도 자동으로 전파되므로 선언이 강제되지 않는다. 다만 API 문서화 목적으로 명시적으로 선언하는 경우도 있으며, 이때는 Javadoc @throws 태그와 함께 사용하면 효과적이다.
Q. throws Exception을 선언하면 모든 예외를 처리할 수 있나요?
A. 컴파일 에러는 피할 수 있지만, 실무에서는 throws Exception 남용을 강하게 지양한다. 호출자가 어떤 예외를 처리해야 하는지 알 수 없게 되어 예외 처리의 의미가 사라지고, 코드의 안정성과 가독성이 크게 저하된다.
Q. 메서드 오버라이딩 시 throws 선언에 제약이 있나요?
A. 오버라이딩 메서드는 부모 메서드보다 더 넓은 범위의 checked exception을 throws에 추가할 수 없다. 예를 들어 부모가 throws IOException을 선언하면, 자식은 IOException의 하위 클래스(FileNotFoundException 등)만 선언하거나 아예 생략할 수 있다. 더 넓은 Exception을 추가하면 컴파일 에러가 발생한다.
Q. 람다 표현식 안에서 checked exception이 발생하면 어떻게 하나요?
A. Java 표준 함수형 인터페이스(Function, Consumer 등)는 checked exception을 throws로 선언하지 않아 람다 내부에서 직접 던질 수 없다. 이를 해결하려면 checked exception을 catch하여 RuntimeException으로 감싸거나, checked exception을 허용하는 커스텀 함수형 인터페이스를 직접 정의해야 한다.
Q. throws를 선언한 메서드를 호출할 때 반드시 try-catch를 써야 하나요?
A. 반드시 try-catch를 쓸 필요는 없으며, 호출하는 메서드에서 다시 throws로 위임하는 것도 허용된다. 즉, try-catch로 직접 처리하거나 현재 메서드의 throws 선언으로 상위로 전파하는 두 가지 선택지 중 하나를 반드시 선택해야 한다. 아무것도 하지 않으면 컴파일 에러가 발생한다.