본문 바로가기
스파르타 내일배움캠프/TIL(Today I learned)

25.03.10 TIL - 예외처리

by pandastic 2025. 3. 10.
반응형

 

 

목차

     

    Chapter 3 - 1 : 예외와 예외처리

    1. 예외

         - 프로그램 실행 중 예상하지 못한 상황이 발생하는 것.

         - 의도적으로 예외를 발생시킬 때에는 throw 키워드를 통해 발생시킴.

         - 예외를 처리하지 않으면 프로그램이 중단될 수 있음.

         - 예외처리(try~catch)를 통해 프로그램이 안정적으로 실행되게 할 수 있음.

     

     

     * 예외 발생 예제

         - 의도하지 않은 예외.

    public class Main {
        public static void main(String[] args) {
            System.out.println("프로그램 시작");
            int result = 10 / 0; // ❌ 예외 발생 (ArithmeticException)
            System.out.println("이 문장은 실행되지 않음");
        }
    }

         - 예외를 처리하지 않으면 이후 코드는 실행되지 않음.

     


    * 의도한 예외

    public class Main {
        public static void main(String[] args) {
            int age = 10;
            if (age < 18) {
    		        //✅의도적으로 예외를 발생시키는 부분
                throw new IllegalArgumentException("미성년자는 접근할 수 없습니다!");
            }
            System.out.println("....");
        }
    }

     

     

     

    2. 예외 구조와 종류

    • RuntimeException - UncheckedException
      • RuntimeException을 상속받는 모든 예외를 UncheckedException이라고 함.
      • 예외처리를 컴파일러가 확인하지 않음.
    • Exception - CheckedException
      • Exception  클래스를 직접 상속받는 모든 예외를 CheckedException 이라고 함.
      • RuntimeException과 RuntimeException을 상속받은 예외는 제외.
      • 예외처리를 컴파일러가 확인함.

          - Exception 과 RuntimeException 모두 Throwable 클래스를 상속받고 있음.

          - Runtime Exception도 Exception을 상속 받지만 따로 놓고 봐야함

            → NullPointerException, ArithmeticException

     

     

     

    3. 예외 전파

        - 메서드에서 발생한 예외가 해당 메서드 내에서 처리되지 않았을 때 메서드를 호출한 상위 메서드로 전달되는 과정.

        - 예외가 프로그램 시작 지점(main())까지 전파되고 끝내 처리되지 않으면 프로그램이 비정상 종료됨.

     

     

    A) RuntimeException - UncheckedException

       - 컴파일러가 예외 처리를 강제하지 않는 예외.

       - 예외 처리를 하지 않아도 컴파일 오류가 발생하지 않음.

       - 처리되지 않은 예외는 계속 프로그램 시작 지점까지 전파됨.

       - 끝내 예외가 처리되지 않으면 프로그램이 비정상적으로 종료됨.

       - RuntimeException을 상속받는 모든 예외를 UncheckedException 이라고 함.

     

     

    1) try - catch 활용

     

    예시)

    public class ExceptionPractice {
        // 1. 언체크 예외 호출 예시
        /*
        public void callUncheckedException(){
            if(true){
                System.out.println("언체크 예외 발생");
                throw new RuntimeException();
            }
        }
        */
        public void callUncheckedException(){
            // try블럭 안에 예외를 발생시킬 수 있는 로직을 넣음.
            // 예외 발생 시 상위 메서드로 바로 전파되는 것이 아니라 그 전에 catch 블럭으로 이동.
            try{
                if(true){
                    System.out.println("언체크 예외 발생");
                    throw new RuntimeException();
                }
            // catch 블럭 안에 예외를 처리할 수 있는 게 존재하는지 체크하기.
            // 현재 catch 블럭 안에 같은 예외가 작성되어 있음. -> 이대로 예외처리.
            // catch 블럭은 여러 개가 있을 수 있음.
            // try 문 안에서 발생하는 예외 2개를 처리하겠다.
            }catch(RuntimeException e){
                // 예외 처리 방식을 정의해두었기 때문에 에러는 발생하지 않게 됨.
                System.out.println("언체크 예외 처리");
            }/*catch (Exception e){
                System.out.println("체크 예외 처리");
            }*/
        }
    }
    public class Main {
        public static void main(String[] args) {
            // 1. 의도하지 않은 예외
            /*int ret = 10 / 0;
            System.out.println("ret = "  + ret);
            System.out.println("프로그램 종료");*/
            // 2. 의도적인 예외 - throw
            /*int age = 10;
            if(age < 18){
                throw new IllegalArgumentException("미성년자는 접근할 수 없습니다.");
            }*/
            // 3. 언체크 예외 호출
            // 예외가 발생한 시점에 프로그램이 비정상적 종료가 됨. -> 예외를 처리해주지 않았기 때문.
            // 예외 발생 시 상위로 전파됨. 먼저 메서드 내에서 예외가 발생했기 때문에 메서드 내에서 예외처리를 해야하는데 하지 않았음.
            // 그러면 메서드를 부른 main으로 전파하게 되는데 main 쪽에서조차 예외처리를 하지 않음.
            // 그러면 이제 예외가 발생하면서 프로그램이 비정상적으로 종료되게 됨.
            ExceptionPractice exceptionPractice = new ExceptionPractice();
            exceptionPractice.callUncheckedException();
            //프로그램이 종료되지 않고 정상흐름으로 돌아와서 출력됨.
            System.out.println("프로그램 종료");
        }
    }

     

        - 메서드 내부에서 예외 처리하지 않고 바로 상위 메서드에서 처리도 가능함.

    public class Main {
        public static void main(String[] args) {
            // 1. 의도하지 않은 예외
            /*int ret = 10 / 0;
            System.out.println("ret = "  + ret);
            System.out.println("프로그램 종료");*/
            // 2. 의도적인 예외 - throw
            /*int age = 10;
            if(age < 18){
                throw new IllegalArgumentException("미성년자는 접근할 수 없습니다.");
            }*/
            // 3. 언체크 예외 호출
    ExceptionPractice exceptionPractice = new ExceptionPractice();
            try{
                    exceptionPractice.callUncheckedException();
            } catch(RuntimeException e){
                System.out.println("언체크 예외 처리");
            }
            //프로그램이 종료되지 않고 정상흐름으로 돌아와서 출력됨.
            System.out.println("프로그램 종료");
        }
    }

     

    * 예외를 메서드 내에서 처리해주지 않아 예 외가 발생하면 상위 메서드로 전파된다.

    * 예외는 try ~ catch 문으로 처리할 수 있음.

     

     

    B) Exception - CheckedException

       - Exception 클래스를 직접 상속받는 모든 예외를 CheckedException 이라고 함.

          ※ RuntimeException을 상속받는 예외는 제외.

       - 컴파일러가 예외 처리를 강제하는 예외임.

       - 예외 처리를 하지 않으면 "컴파일 오류가 발생한다." (코드에 빨간줄)

       - 반드시 try - catch로 예외를 처리하거나 throws 키워드를 사용해야함.

          → throws로 예외 처리의 책임을 호출자에게 전가할 수 있음.

     

    1) try - catch 활용

        - CheckedException을 try - catch 를 사용하여 직접 처리하는 방식.

     

    ExceptionPractice.java

    // 2. 체크 예외 호출 예시
    public void callCheckedException(){
        try{
            if(true){
                System.out.println("체크 예외 발생");
                // Exception은 체크 예외라서 컴파일이 확인하기 때문에 빨간색으로 오류가 뜸!!
                throw new Exception();
            }
        }catch(Exception e){
            System.out.println("체크 예외 처리");
        }

     

     

    Main.java

    // 4. 체크 예외 호출
    exceptionPractice.callCheckedException();
    System.out.println("프로그램 종료");

     

    2) throws 활용

         - throws 키워드를 사용하여 예외를 호출한 곳에서 처리하도록 강제하는 방식 → (책임 전가)

     

    ExceptionPractice.java

    // 2. 체크 예외 호출 예시
    // 이 예외는 메서드를 호출한 상위 메서드에서 직접 예외처리를 할 것이라고 선언하는 것.
    public void callCheckedException() throws Exception{
        if(true){
            System.out.println("체크 예외 발생");
            throw new Exception();
        }

     

    Main.java

    // 4. 체크 예외 호출
    try{
        exceptionPractice.callCheckedException();
    }catch(Exception e){
        System.out.println("체크 예외 처리");
    }
    System.out.println("프로그램 종료");

     

     

    [정리]

        - 예외가 발생하고 처리되지 않으면 프로그램이 비정상적으로 종료될 수 있기 때문에 꼭 예외 처리는 필수.

        - CheckedException은 컴파일러를 통해 개발자에게 반드시 처리해야 하는 예외를 알려줄 수 있음.

        - UncheckedException은 개발자가 충분히 예측하고 방지할 수 있는 경우 사용.

          → 숫자를 0으로 나누는 오류 등은 코드 검토로 충분히 예방 가능함.

          → 이런 예외 처리까지CheckedException으로 처리하도록 강제한다면 모든 예외 상황을 처리해야 하는 답답한 상황이 벌어져 개발 생산성이 저하되고 불필요한 코드가 많아질 수 있음.

     

     

    [실습 과제]

    더보기

    💬 Q1. 로그인 예외처리

    💡요구사항

    • 사용자로부터 아이디와 비밀번호를 입력받습니다.
    • 아이디가 “admin”, 비밀번호가 “1234” 가 아니면 로그인 실패 예외를 발생시킵니다.
    • 로그인 실패 시 “로그인 실패! 아이디 또는 비밀번호가 잘못되었습니다.” 출력 후 다시 입력을 받습니다.

     

    package chapter3.p1;
    import java.util.Scanner;
    public class Main {
        public static void main(String[] args) {
            Scanner input = new Scanner(System.in);
            while(true){
                try{
                    System.out.print("아이디를 입력하세요: ");
                    String userId = input.next();
                    System.out.print("비밀번호를 입력하세요: ");
                    String userPw = input.next();
                    login(userId, userPw);
                    System.out.println("로그인 성공!");
                    break;
                }catch(Exception e){
                    System.out.println("로그인 실패! 아이디 또는 비밀번호가 잘못되었습니다.");
                    continue;
                }
            }
        }
        public static void login(String userId, String userPw)throws Exception{
            if(!userId.equals("admin") || !userPw.equals("1234")){
                throw new Exception();
            }
        }
    }

     

     

    정답)

    import java.util.Scanner;
    public class Main {
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            while (true) {
                try {
                    System.out.print("아이디 입력: ");
                    String username = scanner.next();
                    System.out.print("비밀번호 입력: ");
                    String password = scanner.next();
                    
                    login(username, password); // 예외 발생 가능
                    System.out.println("로그인 성공!");
                    break;
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                }
            }
        }
        public static void login(String username, String password) throws Exception {
            if (!username.equals("admin") || !password.equals("1234")) {
                throw new Exception("로그인 실패! 아이디 또는 비밀번호가 잘못되었습니다.");
            }
        }
    }
    반응형