[Java 개념노트 45] throws와 throw 이해하기
안녕하세요. Java 개념노트 시리즈 마흔다섯 번째 글입니다.
지난 글에서는 프로그램 실행 중 발생할 수 있는 문제를 try-catch-finally로 안전하게 처리하는 방법을 정리했습니다. 이번 글에서는 예외 처리에서 자주 헷갈리는 throws와 throw에 대해 알아보겠습니다.
throws와 throw는 이름이 비슷하지만 역할이 다릅니다. throws는 예외 처리를 호출한 쪽으로 넘길 때 사용하고, throw는 개발자가 직접 예외를 발생시킬 때 사용합니다.
1. throws와 throw를 왜 배울까?
예외가 발생할 수 있는 코드는 직접 try-catch로 처리할 수도 있지만, 경우에 따라 다른 메서드에게 처리를 맡길 수도 있습니다. 또한 잘못된 값이 들어왔을 때 개발자가 직접 예외를 발생시켜 흐름을 막아야 할 때도 있습니다.
이때 사용하는 문법이 throws와 throw입니다.
쉽게 말하면throws는 “이 메서드에서 예외가 생길 수 있으니 호출한 쪽에서 처리해 주세요”라는 뜻이고, throw는 “지금 여기서 예외를 발생시키겠습니다”라는 뜻입니다.
2. throws란?
throws는 메서드 선언부에 작성해서, 해당 메서드에서 발생할 수 있는 예외를 호출한 쪽으로 넘기는 문법입니다.
반환타입 메서드이름() throws 예외타입 {
예외가 발생할 수 있는 코드;
}
예를 들어 다음과 같이 작성할 수 있습니다.
public static void test() throws Exception {
// 예외가 발생할 수 있는 코드
}
이 코드는 test() 메서드 안에서 예외가 발생할 수 있고, 그 예외를 호출한 곳에서 처리해야 한다는 의미입니다.
3. throws 기본 예제
먼저 throws를 사용하는 간단한 예제를 보겠습니다.
public class Main {
public static void main(String[] args) {
try {
divide();
} catch (Exception e) {
System.out.println("예외 처리: " + e.getMessage());
}
}
public static void divide() throws Exception {
int result = 10 / 0;
System.out.println(result);
}
}
실행 결과는 다음과 같습니다.
예외 처리: / by zero
divide() 메서드에서 예외가 발생했습니다. 하지만 divide() 안에서 직접 처리하지 않고 throws Exception으로 호출한 쪽인 main()에 넘겼습니다. 그래서 main()의 try-catch에서 예외를 처리했습니다.
4. throws는 예외를 없애는 문법이 아니다
throws를 사용하면 예외가 사라지는 것이 아닙니다. 예외 처리를 다른 곳으로 넘기는 것입니다.
divide()에서 예외 발생
↓
divide()가 throws로 예외를 넘김
↓
main()에서 try-catch로 처리
주의throws는 예외를 해결하는 것이 아니라 예외 처리를 호출한 쪽으로 미루는 문법입니다. 결국 어딘가에서는 처리해야 합니다.
5. throws를 사용하는 위치
throws는 메서드 이름과 매개변수 괄호 뒤에 작성합니다.
public static void methodName() throws Exception {
// 코드
}
| 부분 | 설명 |
public static void |
메서드의 접근 제어자, static 여부, 반환타입입니다. |
methodName() |
메서드 이름과 매개변수 부분입니다. |
throws Exception |
이 메서드에서 예외가 발생할 수 있음을 선언합니다. |
6. 여러 예외를 throws로 넘기기
메서드에서 여러 종류의 예외가 발생할 수 있다면 쉼표로 구분해서 여러 예외를 선언할 수 있습니다.
public static void method() throws NumberFormatException, ArithmeticException {
String text = "0";
int number = Integer.parseInt(text);
int result = 100 / number;
System.out.println(result);
}
위 메서드는 문자열 변환 과정에서 NumberFormatException이 발생할 수 있고, 나눗셈 과정에서 ArithmeticException이 발생할 수 있습니다.
7. Exception 하나로 넓게 넘기기
Exception은 여러 예외의 부모 클래스입니다. 그래서 여러 예외를 한 번에 넘길 수도 있습니다.
public static void method() throws Exception {
String text = "0";
int number = Integer.parseInt(text);
int result = 100 / number;
System.out.println(result);
}
다만 모든 예외를 무조건 Exception으로 넓게 넘기면 어떤 예외가 발생할 수 있는지 코드만 보고 파악하기 어려울 수 있습니다. 가능하면 상황에 맞는 예외 타입을 명확하게 사용하는 것이 좋습니다.
8. throws를 받은 쪽은 어떻게 해야 할까?
throws로 예외를 넘긴 메서드를 호출하는 쪽은 보통 두 가지 중 하나를 선택해야 합니다.
try-catch로 직접 처리한다.- 자신도
throws로 다시 넘긴다.
try-catch로 처리하기
public class Main {
public static void main(String[] args) {
try {
run();
} catch (Exception e) {
System.out.println("main에서 처리합니다.");
}
}
public static void run() throws Exception {
throw new Exception("예외 발생");
}
}
다시 throws로 넘기기
public class Main {
public static void main(String[] args) throws Exception {
run();
}
public static void run() throws Exception {
throw new Exception("예외 발생");
}
}
두 번째 방식은 main()까지 예외를 넘기는 형태입니다. 입문 단계에서는 예외가 어디서 처리되는지 흐름을 반드시 확인해야 합니다.
9. throw란?
throw는 개발자가 직접 예외를 발생시킬 때 사용하는 키워드입니다.
throw new 예외클래스("예외 메시지");
예를 들어 다음처럼 작성할 수 있습니다.
throw new IllegalArgumentException("잘못된 값입니다.");
이 코드는 IllegalArgumentException이라는 예외를 직접 발생시킵니다.
핵심 포인트throw는 예외를 직접 발생시키는 문법입니다. 보통 잘못된 값이 들어왔을 때 흐름을 막기 위해 사용합니다.
10. throw 기본 예제
나이가 음수로 들어오면 예외를 직접 발생시키는 예제를 보겠습니다.
public class Main {
public static void main(String[] args) {
try {
checkAge(-1);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
public static void checkAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("나이는 0 이상이어야 합니다.");
}
System.out.println("나이: " + age);
}
}
실행 결과는 다음과 같습니다.
나이는 0 이상이어야 합니다.
age가 -1이므로 조건문 안에서 throw가 실행됩니다. 그 결과 예외가 발생하고, catch에서 메시지를 출력합니다.
11. throw 이후 코드는 실행되지 않는다
throw로 예외가 발생하면 그 아래의 코드는 실행되지 않습니다.
public class Main {
public static void main(String[] args) {
try {
test();
} catch (Exception e) {
System.out.println("catch 실행");
}
}
public static void test() {
System.out.println("throw 전");
throw new RuntimeException("예외 발생");
// System.out.println("throw 후"); // 실행될 수 없음
}
}
실행 결과는 다음과 같습니다.
throw 전
catch 실행
throw가 실행되는 순간 예외 흐름으로 넘어가기 때문에 아래 코드는 실행되지 않습니다.
12. throws와 throw 차이
throws와 throw는 이름이 비슷하지만 역할이 완전히 다릅니다.
| 구분 | throws | throw |
| 역할 | 예외를 호출한 쪽으로 넘김 | 예외를 직접 발생시킴 |
| 작성 위치 | 메서드 선언부 | 메서드 내부 코드 |
| 형태 | void method() throws Exception |
throw new Exception() |
| 뒤에 오는 것 | 예외 타입 이름 | 예외 객체 |
| 예시 | throws IOException |
throw new IOException() |
기억하기throws는 메서드 선언부에서 예외를 넘기는 표시이고, throw는 메서드 안에서 예외 객체를 직접 던지는 코드입니다.
13. throws와 throw 함께 사용하기
throw로 예외를 직접 발생시키고, 그 예외를 throws로 호출한 쪽에 넘길 수도 있습니다.
public class Main {
public static void main(String[] args) {
try {
validateScore(120);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
public static void validateScore(int score) throws Exception {
if (score < 0 || score > 100) {
throw new Exception("점수는 0부터 100 사이여야 합니다.");
}
System.out.println("점수: " + score);
}
}
실행 결과는 다음과 같습니다.
점수는 0부터 100 사이여야 합니다.
validateScore() 안에서 throw로 예외를 발생시켰습니다. 그리고 메서드 선언부의 throws Exception으로 그 예외를 호출한 쪽에 넘겼습니다.
14. RuntimeException은 throws를 생략할 수 있다
RuntimeException 계열 예외는 메서드 선언부에 throws를 꼭 작성하지 않아도 됩니다.
public class Main {
public static void main(String[] args) {
checkName("");
}
public static void checkName(String name) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("이름은 비어 있을 수 없습니다.");
}
System.out.println("이름: " + name);
}
}
IllegalArgumentException은 RuntimeException 계열입니다. 그래서 checkName() 메서드에 throws IllegalArgumentException을 꼭 작성하지 않아도 됩니다.
하지만 예외가 발생할 수 있다는 사실을 코드 흐름상 명확히 이해하고 있어야 합니다.
15. checked exception과 unchecked exception
자바 예외는 크게 checked exception과 unchecked exception으로 나눌 수 있습니다.
| 구분 | 설명 | 예시 |
| checked exception | 컴파일 단계에서 예외 처리 여부를 확인합니다. | IOException, SQLException |
| unchecked exception | 실행 중 발생할 수 있으며, 컴파일러가 강제하지 않습니다. | RuntimeException, NullPointerException, IllegalArgumentException |
입문 단계에서는 우선 checked exception은 처리나 throws가 필요할 수 있고, RuntimeException 계열은 강제되지 않는다 정도로 이해하면 됩니다.
16. checked exception 예제
Exception을 직접 던지면 호출한 쪽에서 처리하거나 다시 throws 해야 합니다.
public class Main {
public static void main(String[] args) {
try {
test();
} catch (Exception e) {
System.out.println("예외 처리: " + e.getMessage());
}
}
public static void test() throws Exception {
throw new Exception("checked exception 예제");
}
}
실행 결과는 다음과 같습니다.
예외 처리: checked exception 예제
test() 메서드는 Exception을 던지므로 throws Exception을 선언했습니다. 호출한 main()에서는 try-catch로 처리했습니다.
17. unchecked exception 예제
RuntimeException 계열은 throws를 생략해도 컴파일 오류가 발생하지 않습니다.
public class Main {
public static void main(String[] args) {
checkNumber(-10);
}
public static void checkNumber(int number) {
if (number < 0) {
throw new IllegalArgumentException("음수는 사용할 수 없습니다.");
}
System.out.println(number);
}
}
실행 결과는 다음과 비슷합니다.
Exception in thread "main" java.lang.IllegalArgumentException: 음수는 사용할 수 없습니다.
IllegalArgumentException은 unchecked exception이므로 throws를 강제로 작성하지 않아도 됩니다. 다만 처리하지 않으면 프로그램이 종료될 수 있습니다.
18. 잘못된 값 검증에 throw 사용하기
throw는 잘못된 값이 들어왔을 때 자주 사용합니다. 예를 들어 상품 가격이 음수이면 예외를 발생시킬 수 있습니다.
public class Main {
public static void main(String[] args) {
Product product = new Product();
try {
product.setPrice(-1000);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}
class Product {
private int price;
public void setPrice(int price) {
if (price < 0) {
throw new IllegalArgumentException("가격은 0 이상이어야 합니다.");
}
this.price = price;
}
}
실행 결과는 다음과 같습니다.
가격은 0 이상이어야 합니다.
잘못된 가격이 들어오면 객체 상태가 이상해질 수 있습니다. 그래서 setter에서 throw로 예외를 발생시켜 잘못된 값 저장을 막았습니다.
19. throws와 throw 전체 예제
이번에는 문자열 점수를 숫자로 바꾸고, 점수 범위를 검사하는 예제를 보겠습니다.
public class Main {
public static void main(String[] args) {
try {
int score = parseScore("120");
validateScore(score);
System.out.println("점수: " + score);
} catch (NumberFormatException e) {
System.out.println("숫자로 변환할 수 없습니다.");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
public static int parseScore(String text) throws NumberFormatException {
return Integer.parseInt(text);
}
public static void validateScore(int score) {
if (score < 0 || score > 100) {
throw new IllegalArgumentException("점수는 0부터 100 사이여야 합니다.");
}
}
}
실행 결과는 다음과 같습니다.
점수는 0부터 100 사이여야 합니다.
parseScore()는 문자열을 숫자로 변환합니다. validateScore()는 점수 범위를 검사하고, 범위를 벗어나면 throw로 예외를 직접 발생시킵니다.
20. throws와 throw 사용 시 자주 하는 실수
throws와 throw를 처음 배울 때는 아래와 같은 실수를 자주 합니다.
| 실수 | 문제점 | 해결 방법 |
throws와 throw 혼동 |
예외 넘기기와 직접 발생을 헷갈림 | throws는 선언부, throw는 코드 내부로 기억 |
throw 뒤에 예외 타입만 작성 |
예외 객체가 필요함 | throw new Exception() 형태 사용 |
throws를 쓰면 예외가 처리된다고 생각 |
예외 처리가 호출한 쪽으로 미뤄질 뿐임 | 최종적으로 try-catch 처리 위치 확인 |
| 잘못된 값인데 조용히 무시 | 객체 상태가 이상해질 수 있음 | 필요한 경우 throw로 명확히 막기 |
모든 예외를 무조건 Exception으로 던짐 |
예외 의미가 흐려짐 | 상황에 맞는 구체적인 예외 사용 |
21. 직접 연습해보기
아래 코드를 직접 작성하고 실행해보세요.
public class Main {
public static void main(String[] args) {
String[] inputs = {"80", "120", "Java"};
for (int i = 0; i < inputs.length; i++) {
try {
int score = parseScore(inputs[i]);
validateScore(score);
System.out.println(inputs[i] + "은 올바른 점수입니다.");
} catch (NumberFormatException e) {
System.out.println(inputs[i] + "은 숫자가 아닙니다.");
} catch (IllegalArgumentException e) {
System.out.println(inputs[i] + ": " + e.getMessage());
}
}
}
public static int parseScore(String text) throws NumberFormatException {
return Integer.parseInt(text);
}
public static void validateScore(int score) {
if (score < 0 || score > 100) {
throw new IllegalArgumentException("점수는 0부터 100 사이여야 합니다.");
}
}
}
실행 결과는 다음과 같습니다.
80은 올바른 점수입니다.
120: 점수는 0부터 100 사이여야 합니다.
Java은 숫자가 아닙니다.
22. 이번 글 정리
이번 글에서는 예외를 넘기거나 직접 발생시키는 throws와 throw에 대해 정리했습니다. 핵심 내용은 다음과 같습니다.
throws는 메서드에서 발생할 수 있는 예외를 호출한 쪽으로 넘기는 문법이다.throws는 메서드 선언부에 작성한다.throws는 예외를 없애는 것이 아니라 처리를 미루는 것이다.throw는 개발자가 직접 예외를 발생시킬 때 사용한다.throw는 메서드 내부 코드에서 사용한다.throw new 예외클래스("메시지")형태로 작성한다.throw이후의 코드는 정상 흐름으로 실행되지 않는다.throw와throws는 함께 사용할 수도 있다.- checked exception은 처리하거나
throws로 넘겨야 할 수 있다. - RuntimeException 계열은
throws를 강제하지 않는다. - 잘못된 값이 들어왔을 때
throw로 명확하게 막을 수 있다.
한 줄 요약throws는 예외 처리를 호출한 쪽으로 넘기는 선언이고, throw는 코드 안에서 예외를 직접 발생시키는 문법입니다.
다음 글 예고
다음 글에서는 [Java 개념노트 46] 사용자 정의 예외 이해하기라는 주제로 직접 예외 클래스를 만들어 상황에 맞는 예외를 사용하는 방법을 정리해보겠습니다.
GWDEVELBlog Java 개념노트 시리즈

'Computer Science > Java' 카테고리의 다른 글
| [Java ] 개념노트 47 컬렉션 프레임워크란 무엇인가? (0) | 2026.06.16 |
|---|---|
| [Java ] 개념노트 46 사용자 정의 예외 이해하기 (0) | 2026.06.16 |
| [Java] 개념노트 44 예외 처리란 무엇인가? (0) | 2026.06.15 |
| [Java ] 개념노트 43 날짜와 시간 API 이해하기 (0) | 2026.06.15 |
| [Java ] 개념노트 42 Random 클래스 이해하기 (0) | 2026.06.15 |