throw 예외 떠넘기기

Jan 28, 2024
throw 예외 떠넘기기

1. throw란?

예외를 명시적으로 발생시키는 키워드 throw를 하면 오류가 안나도 내가 원하는 오류를 강제로 터뜨릴 수 있음. throw 키워드 다음에 예외 객체를 지정해야 한다. 이렇게 발생시킨 예외는 현재 실행 중인 메서드를 호출한 곳으로 전달되며, 호출된 메서드는 예외를 처리해야 한다.
 

<오류를 강제로 터뜨리는 throw>

notion image
notion image
강제로 오류를 발생시킨 모습 이 throw를 사용해서 오류 메세지를 내가 설정할 수 있음!
💡
throw를 이용해 강제 발생 시키는 건 ‘RuntimeException’ 으로만 가능
 

2. 호출자에게 위임하는 throws

notion image
💡
이렇게 메소드 옆에 throw를 붙이는 건, 그 메소드를 호출하는 애한테 오류 처리를 위임하는 것. 이제 호출자가 예외 처리(try-catch)를 해줘야 한다.
notion image
성의 없이 throws Exception을 걸어서 오류가 떠도 무슨 오류가 뜬건지 모르겠다 ㅠㅠ
notion image
바로 그때! MyZeroDivideException 이라는 클래스를 만든다. 클래스 이름만 봐도 '0을 나누면 문제가 발생하는 예외' 라는 걸 알 수 있다! MyZeroDivideException는 Exception을 상속 받았으니 다형성에 의해 Exception이다. 이렇게 클래스를 만들어서 메소드가 throws MyZeroDivideException를 받으면!
notion image
아!! 0을 나눴을 때 오류가 터지는구나~~~ 하고 바로 알아먹을 수 있는 이름으로 나온다. 이제, divide 메소드를 호출한 c2.divide 이 코드에서 try-catch를 실행하자!
💡
이렇게 해당 오류의 클래스를 만들어서 관리하면 쉽게 알아 먹게 만들 수 있다.
 

 
notion image
해당 코드 alt + enter로 try-catch 감싸주기.
 

 
!! 그런데 !! MyZeroDivideException 로 하니까 RuntimeException이 아니라서 오류가 안 잡힘!!
그래서 이 코드를 사용하기로 했다
package ex08.example2; class Cal2 { // RuntimeException = 엄마가 알려주지 않았을 때 public void divide(int num) throws Exception { System.out.println(10 / num); } } public class TryEx01 { public static void main(String[] args) { Cal2 c2 = new Cal2(); try { c2.divide(0); } catch (Exception e) { System.out.println("0으로 나눌 수 없어요"); } } }
notion image
 

 

3. throws 예외 떠넘기기

notion image
메서드에서 발생한 예외를 해당 메서드를 호출한 곳으로 전달하는 것을 의미 ex) public int divide(int num1, int num2) throws ArithmeticException { >> 해당 메소드를 호출한 곳에서 try-catch를 해야한다.
 

 
class Cal3 { void divide(int num) throws Exception { // 호출자에게 떠넘기는 throws System.out.println(10 / num); } } class Cal4 { void divide(int num) { try { System.out.println(10 / num); } catch (Exception e) { throw new RuntimeException("0 쓰지마라~"); //에러메세지 띄우는 throw } } }
notion image
💡
throws Exception은 메서드 선언부에서 해당 메서드에서 발생할 수 있는 모든 예외를 선언하는 것이며, 호출자에게 예외 처리 책임을 넘기는 역할을 합니다.
💡
throw new RuntimeException("")메서드 내에서 직접 예외를 발생 시키는 것입니다. 이는 예외 상황이 발생하였음을 명시적으로 알리는 역할을 합니다.
 

4. if와 Exception 코드 비교

[ if코드가 얼마나 구린지 보자… 예외 처리만 알았더라면 이렇게 안 짤텐데… ]
package ex08.example2; // 책임 : 데이터베이스 상호작용 (제약 조건 검사) class Repository { String insert(String id, String pw) { System.out.println("레포지토리 insert 호출됨"); if (id.length() < 4) { return "id의 길이가 4자 이상이어야 합니다."; } return "DB에 정상 insert 되었습니다"; } void select() { System.out.println("레포지토리 select 호출됨"); } } // 책임 : 유효성 검사 class Controller { String join(String id, String pw) { System.out.println("컨트롤러 회원가입 호출됨"); if (id.length() < 4) { return "id의 길이가 4자 이상이어야 합니다."; } Repository repo = new Repository(); String message = repo.insert(id, pw); if (!message.equals("DB에 정상 insert 되었습니다")) { return message; } return "회원가입이 완료되었습니다"; } void login() { System.out.println("컨트롤러 로그인 호출됨"); } } public class TryEx03 { public static void main(String[] args) { Controller con = new Controller(); String message = con.join("ssa", "1234"); System.out.println(message); } }
// 약속 : 1은 정상, 2는 id 제약조건 실패, 3은 pw 제약조건 실패 // 책임 : 데이터베이스 상호작용 class Repository { int insert(String id, String pw) { System.out.println("레포지토리 insert 호출됨"); if (id.length() < 4) { return 2; } if (pw.length() > 50) { return 3; } return 1; } } // 책임 : 유효성 검사 class Controller { String join(String id, String pw) { System.out.println("컨트롤러 회원가입 호출됨"); if (id.length() < 4) { return "유효성검사 : id의 길이가 4자 이상이어야 합니다."; } Repository repo = new Repository(); int code = repo.insert(id, pw); if (code == 2) { return "id가 잘못됐습니다"; } if (code == 3) { return "pw가 잘못됐습니다"; } return "회원가입이 완료되었습니다"; } } public class TryEx03 { public static void main(String[] args) { Controller con = new Controller(); String message = con.join("ssar", "123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789"); System.out.println(message);
 

Exception을 통한 예외처리 ★

코드 1 (정상 작동) - 예외처리 없이 정상 작동
💡
데이터베이스와의 상호작용을 가정하고 작성된 코드
package ex08.example2; // 약속 : 1은 정상, 2는 id 제약조건 실패, 3은 pw 제약조건 실패 // 책임 : 데이터베이스 상호작용 (제약 조건 검사) class Repository2 { void insert(String id, String pw) { System.out.println("레포지토리 insert 호출됨"); if (id.length() < 4) { throw new RuntimeException("DB : id의 길이가 4자 이상 이어야 합니다."); } if (pw.length() > 50) { throw new RuntimeException("DB : pw의 길이가 50자 이하 이어야 합니다."); } } //이런 throw 이런게 제약 조건 검사 } // 책임 : 유효성 검사 class Controller2 { void join(String id, String pw) { System.out.println("컨트롤러 회원가입 호출됨"); if (id.length() < 4) { throw new RuntimeException("Controller : id의 길이가 4자 이상 이어야 합니다."); } Repository2 repo = new Repository2(); repo.insert(id, pw); //회원 가입 시 입력한 id와 pw를 인자로 전달 } } public class TryEx04 { public static void main(String[] args) { Controller2 con = new Controller2(); con.join("ssar", "1234"); System.out.println("회원가입 성공"); } }
notion image
 

notion image
 
[ class Repository2 { void insert(String id, String pw) ]
notion image
 
[class Controller2 { void join(String id, String pw) ]
notion image
💡
디버깅용 메시지 소프트웨어 개발 과정에서 버그를 찾고 해결하기 위해 사용되는 메시지 개발 및 테스트 단계에서 주로 사용되며, 실제 배포된 소프트웨어에서는 출력되지 않도록 제거되거나 비활성화되는 경우가 일반적임
 
코드 2 (정상 작동) - 예외처리 O
package ex08.example2; // 약속 : 1은 정상, 2는 id 제약조건 실패, 3은 pw 제약조건 실패 // 책임 : 데이터베이스 상호작용 (제약 조건 검사) class Repository2 { void insert(String id, String pw) throws RuntimeException { System.out.println("레포지토리 insert 호출됨"); if (id.length() < 4) { throw new RuntimeException("DB : id의 길이가 4자 이상 이어야 합니다."); } if (pw.length() > 50) { throw new RuntimeException("DB : pw의 길이가 50자 이하 이어야 합니다."); } } } // 책임 : 유효성 검사 class Controller2 { void join(String id, String pw) throws RuntimeException { System.out.println("컨트롤러 회원가입 호출됨"); if (id.length() < 4) { throw new RuntimeException("Controller : id의 길이가 4자 이상 이어야 합니다."); } Repository2 repo = new Repository2(); repo.insert(id, pw); } } public class TryEx04 { public static void main(String[] args) { Controller2 con = new Controller2(); try { con.join("ssar", "1234"); System.out.println("회원가입 성공"); } catch (RuntimeException e) { System.out.println(e.getMessage()); } } }
notion image
notion image
notion image
차이점은 main메소드에서 try-catch 처리를 했냐, 안했냐네
 
 

 
코드 3 (유효성 검사 에러) - 3번과 비교하기
package ex08.example2; // 약속 : 1은 정상, 2는 id 제약조건 실패, 3은 pw 제약조건 실패 // 책임 : 데이터베이스 상호작용 (제약 조건 검사) class Repository2 { void insert(String id, String pw) throws RuntimeException { System.out.println("레포지토리 insert 호출됨"); if (id.length() < 4) { throw new RuntimeException("DB : id의 길이가 4자 이상 이어야 합니다."); } if (pw.length() > 50) { throw new RuntimeException("DB : pw의 길이가 50자 이하 이어야 합니다."); } } } // 책임 : 유효성 검사 class Controller2 { void join(String id, String pw) throws RuntimeException { System.out.println("컨트롤러 회원가입 호출됨"); if (id.length() < 4) { throw new RuntimeException("Controller : id의 길이가 4자 이상 이어야 합니다."); } Repository2 repo = new Repository2(); repo.insert(id, pw); } } public class TryEx04 { public static void main(String[] args) { Controller2 con = new Controller2(); con.join("ssa", "1234"); System.out.println("회원가입 성공"); } }
notion image
유효성 검사에서 Exception 발생! 아이디를 3글자만 입력했음.
💡
try-catch 덕분에 어디서 에러가 났는지 에러 메세지가 확실하게 보인다!
 
코드 4 (2번 코드에서 예외 처리 추가)
💡
throw, 예외처리 떠넘기기. insert는 join한테 떠넘기고, join은 호출한 애한테 떠넘긴다
package ex08.example2; // 약속 : 1은 정상, 2는 id 제약조건 실패, 3은 pw 제약조건 실패 // 책임 : 데이터베이스 상호작용 (제약 조건 검사) class Repository2 { void insert(String id, String pw) throws RuntimeException { System.out.println("레포지토리 insert 호출됨"); if (id.length() < 4) { throw new RuntimeException("DB : id의 길이가 4자 이상 이어야 합니다."); } if (pw.length() > 50) { throw new RuntimeException("DB : pw의 길이가 50자 이하 이어야 합니다."); } } } // 책임 : 유효성 검사 class Controller2 { void join(String id, String pw) throws RuntimeException { System.out.println("컨트롤러 회원가입 호출됨"); if (id.length() < 4) { throw new RuntimeException("Controller : id의 길이가 4자 이상 이어야 합니다."); } Repository2 repo = new Repository2(); repo.insert(id, pw); } } public class TryEx04 { public static void main(String[] args) { Controller2 con = new Controller2(); try { con.join("ssa", "1234"); } catch (RuntimeException e) { throw new RuntimeException(e); } System.out.println("회원가입 성공"); } }
 
2번 코드와 3번 코드의 차이점은 ‘예외 처리 방식’에 있다.
notion image
notion image
notion image
해당 코드에서는 main 메서드와 join 메서드에서 모두 예외를 잡고 있다. (= 예외가 2번 잡힘) main 메서드에서는 try-catch 문을 사용하여 con.join("ssa", "1234"); 호출 시 발생하는 예외를 잡고, 다시 새로운 예외로 던지고 있다. join 메서드 내부에서도 Repository2 클래스의 insert 메서드 호출 시 발생하는 예외를 잡고 있다.
notion image
 

 
 
throws RuntimeException 을 throws Exception 적어도 O
 

 
💡
throw = 제약 조건 (SQL에서 했던 것 그 제약 조건 같은 것)
 
Share article

codingb