
📌 핵심 답변
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는 메서드 선언부에서 반환 타입 뒤, 메서드 바디 앞에 위치하며 쉼표로 구분하여 여러 예외를 동시에 선언할 수 있다. 이 메커니즘은 예외 처리 책임을 명확히 분리하여 코드 가독성과 유지보수성을 높인다.
| 구분 | throw | throws |
|---|---|---|
| 위치 | 메서드 바디 내부 | 메서드 선언부 (시그니처) |
| 역할 | 예외 객체를 직접 발생 | 예외 처리 책임을 호출자에게 위임 |
| 대상 예외 수 | 한 번에 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줄 요약
- throws는 Java 메서드 선언부에 사용하여 checked exception의 처리 책임을 호출자에게 위임하는 키워드이며, throw(예외 발생)와는 역할이 완전히 다르다.
- throws의 핵심 원칙은 예외를 가장 잘 처리할 수 있는 계층에서 catch하고, 그렇지 않은 하위 레이어는 throws로 위임하여 책임을 명확히 분리하는 것이다.
- 실무에서는 throws Exception처럼 포괄적인 선언을 지양하고, 구체적인 예외 타입을 선언하거나 커스텀 예외와 결합하여 코드의 명확성과 유지보수성을 높여야 한다.