본문 바로가기
  • 개발공부 및 일상적인 내용을 작성하는 블로그 입니다.
부트캠프/후기 챌린지

[멋쟁이사자처럼부트캠프] 백엔드 자바 21기 (2026.01.21)

by 방구석 취준생 2026. 3. 9.

* 해당 글은 백엔드 자바 강의이후 회고 글입니다.

https://bootcamp.likelion.net/school/kdt-backendj-21th

 

백엔드 부트캠프 21기: Java : 멋사 부트캠프

실전 스킬 기반 백엔드 개발자 취업 완벽 대비 교육

bootcamp.likelion.net

 

 

예외 클래스(Exception Class)

프로그램에서는 다음과 같은 두 가지 상황에서 오류가 발생한다.

  • 컴파일 오류(compile error)
    • 프로그램 코드 작성 중 실수로 발생한다.
    • 컴파일 오류는 개발환경에서 대부분 원인을 알 수 있다. 발생한 컴파일 오류를 모두 수정해야 프로그램이 정상적으로 실행되므로 문법적으로 오류가 있다는 것을 바로 알 수 있다.
    • 컴파일 시점에서 발생하는 예외 상황은 반드시 try-catch 문으로 명시해주어야 한다.
  • 실행 오류(runtim error)
    • 실행중인 프로그램이 의도하지 않은 동작을 하거나 프로그램이 중지되는 오류이다.
    • 실행 오류 중 프로그램을 잘못 구현하여 의도한 바와 다르게 실행되어 생기는 오류를 버그(bug) 라고한다.
    • 프로그램 실행 중에 발생하는 오류는 예측하기 어려운 경우가 많고 프로그램이 비정상 종료되면서 갑자기 멈춰버린다.

실제 서비스를 제공하고 있는 프로그램의 경우 오류가 생기면 서비스가 중지되므로 문제가 심각해진다. 또한 실행 중에 오류가 발생하면 그 상황을 재현하여 테스트 해야 하는데, 실제 시스템이나 서비스가 운영중인 경우에는 쉽지 않다.

따라서 로그(log) 분석을 통해 원인을 찾을 수 있도록 프로그램을 개발할 때 로그를 정확하게 남기는 것이 중요하다.

* 로그(log) : 소프트웨어 실행 중에 발생하는 여러 상황을 기록한 내용으로서 주로 파일에 기록한다. 이 파일을 로그 파일(log file) 이라고 한다.

 

  • 자바는 비정상 종료를 최대한 줄이기 위해 다양한 예외에 대한 처리 방법을 가지고 있다. 
  • 예외 처리를 하는 목적은 일단 프로그램이 비정상 종료되는 것을 방지하기 위한것이다.
  • 예외가 발생했을 때 로그를 남겨두면 예외 상황을 파악하고 버그를 수정하는데 도움을 받을 수 있다.

 

오류와 예외

실행 오류(Runtime Error) 는 아래와 같은 2가지로 나뉜다.

  • 시스템 오류(error) 
    • 자바 가상머신에서 발생하는 오류이다.
    • 예시로는 사용 가능한 동적 메모리가 없는 경우나, 스택 메모리의 오버플로우가 발생한 경우등을 들 수 있다. 이러한 시스템 오류는 프로그램에서 제어할 수 없다.
  • 예외(Exception)
    • 시스템 오류와는 달리 프로그램에서 제어가능한 실행 오류이다.
    • 프로그램에서 파일을 읽어 사용하려는데 파일이 없는 경우, 네트워크로 데이터를 전송 하려는데 연결이 안 된 경우, 배열 값을 출력하려는데 배열 요소가 없는 경우 등이 있다.
    • 프로그램상 코드에서의 예외상황이 발생해도 이를  try-catch 를 통해 예외 상황 발생 시 동작시킬 로직을 작성하여 예외 상황이 발생해도 프로그램이 비정상 종료되지 않고 정상적으로 진행되게끔 만들 수 있다. 

* JVM 동작 중 Throwable 클래스가 실행되었을 때 Error 로 인해 발생한 것인지 Exception 으로 인해 발생한것인지 확인해야 한다.

  • Error 인 경우 프로그램 비정상 종류, Exception 인 경우 코드를 통해 예외처리를 하면서 프로그램 정상진행 가능
  • Exception 은 프로그램 실행 중 발생할 수 있는 각종 예외 상황을 모아놓은것
  • Error, Exception 는 Throwable 클래스를 상속받고 있기 때문에 다형성의 특징을 가진다. 이를 통해 각 경우의 수에 따라 분류가 가능하다.

 

예외 클래스의 종류

프로그램에서 처리하는 예외 클래스의 최상위 클래스는 Exception 클래스이다.

(상속 관계 : Java.lang.Object -> Java.lang.Throwable -> Java.lang.Exception)

 

Exception 클래스의 하위에는 많은 클래스가 있으나 대표적인 대분류는 다음과 같다.

  • IOException
    • 입출력에 대한 예외 상황이 발생할 경우 동작하는 익셉션 클래스이다.
    • 대표적으로 FileNotFoundException, SocketException 등이 있다.
  • RuntimeException
    • 프로그램 실행 중 발생할 수 있는 오류에 대한 예외를 처리한다.
    • 대표적으로 ArithmeticException, IndexOutofBoundsException 등이 있다.
    • RuntimeException 으로 발생하는 예외 상황은 try-catch 문을 사용하여 예외 처리를 하지 않아도 컴파일 오류가 발생하지 않는다.
    • 이러한 컴파일러에 의해 체크되지 않는 예외는 프로그래머가 알아서 처리해야 하므로 주의해야 한다.

* checked exceptoin vs unchecked exception

- checked exception : 프로그램 실행 전 컴파일러에 의해 발견 및 컴파일 시점에 발생하여 수정할 수 있는 예외, 반드시 try-catch 문으로 명시해주어야 한다.

 - unchecked exception : 컴파일러에 의해 발견되지 않는 예외, try-catch 문을 사용하지 않아도 발견되지 않으므로 주의가 필요하다.

- RuntimeException 클래스의 자식 클래스들만 unchecked exception 에 해당된다. 그외 나머지는 checked exception 에 해당된다.

 

try - catch 를 통한 예외처리

아래의 코드를 확인해보자.

public class ArrayExceptionHandling {
    public static void main(String[] args) {
        int[] arr = new int[5];
        
        try{
            for(int i = 0; i <= 5; i++){
                // 예외가 발생할 수 있으므로 try 블록에 작성
                arr[i] = i;
                System.out.println(arr[i]);
            }
        }catch(ArrayIndexOutOfBoundsException e){
            // 예외가 발생하면 catch 블록 수행
            System.out.println(e);
            System.out.println("예외 처리부분");
        }
        System.out.println("프로그램 종료");
    }
}

 

위의 코드에서는 배열에 저장하려는 값의 갯수가 배열의 범위를 벗어났기 때문에 예외가 발생한다.

여기서 발생하는 예외는 RuntimeException 의 하위 클래스인 ArrayIndexOutofBoundsException 으로 처리하는데 이 클래스는 예외 처리를 하지 않아도 컴파일 오류가 나지 않는다.

따라서 프로그래머가 직접 예외 처리를 해주지 않으면 예외가 잡히지 않아서 예외가 발생하는 순간에 프로그램이 갑자기 멈춘다.

그러므로 예외가 발생하는 순간 프로그램이 비정상 종료되지 않도록 예외 처리를 해주어야 한다.

 

컴파일러에 의해 예외가 체크되는 경우

앞에서 확인한 코드는 try-catch 예외 처리를 하지 않아도 컴파일 오류가 나지 않지만, 자바에서 제공하는 많은 예외 클래스들은 컴파일러에 의해 처리된다.

이런 경우 자바에서는 예외 처리를 하지 않으면 컴파일 오류가 계속 남게된다. 

그 중 대표적인 예시가 파일 입출력과 관련된 코드이다. 

 

파일 입출력에서 발생하는 예외 처리

자바에서는 파일을 읽고 쓰는데 스트림(stream) 객체를 사용한다. 스트림 종류는 여러가지가 있지만 이번에는 파일에서 데이터를 바이트 단위로 읽어 들이는 FileInputStream 을 사용해보자.

public class ExceptionHandling1{
    public static void main(String[] args){
        FileInputStream fls = new FileInputStream("a.txt");
    }
}

 

위의 코드는 a.txt 파일에서 데이터를 읽어 들이기 위해 스트림 객체를 생성한다는 의미이다.

이렇게 코드를 작성하면 new FileInputStream("a.txt"); 부분에 Unhandled exception type FileNotFoundException 과 같은 오류가 발생하고 있는것을 확인할 수 있다.

이는 'FileNotFoundException 이 처리되지 않았다.' 는 의미인데, 위의 코드를 예시로 들어 좀 더 자세히 설명하자면

'a.txt 파일을 열어서 내용을 읽으려고 FileInputStream 클래스를 생성하는데, a.txt 파일이 존재하지 않는 경우 오류가 발생할 수 있다.' 라는 뜻이다.

읽으려는 파일이 없는 경우 JVM 에서는 FileNotFoundException 예외 클래스가 생성된다.

 

이렇게 컴파일 시점에 발생한 오류를 해결하기 위해서는 try-catch 를 활용하여 다음과 같이 예외처리를 해주어야 한다.

public class ExceptionHandling1{
    public static void main(String[] args){
        try {
            FileInputStream fis = new FileInputStream("a.txt");
        } catch(FileNotFoundException e){
            System.out.println(e); // 예외 클래스의 toString() 메서드 호출
        }
        System.out.println("여기도 수행된다."); // 정상 출력
    }
}

 

위의 코드는 a.txt 파일이 없어 FileNotFoundException 이 발생할 경우 FileNotFoundException 클래스로 만든 객체 e 의 toString() 메서드가 호출되도록 작성된 코드이다.

위의 코드를 실행해보면 다음과 같은 출력이 나타나는것을 확인해볼 수 있다.

java.io.Exception: a.txt (지정된 파일을 찾을 수 없습니다)
여기도 수행된다.

 

만약 위의 코드에서 FileNotFoundException 익셉션이 발생하여 프로그램이 비정상 종료되었다면 catch 문 이후의 내용은 실행되지 않았을 것이다.

하지만 위의 출력문에서 '여기도 수행된다.' 라는 출력문이 나타났다는 것은 FileNotFoundException 예외 처리 이후에도 프로그램이 정상적으로 계속 수행되었음을 의미한다.

 

예외 처리를 한다고 해서 프로그램의 예외 상황 자체를 막을 수는 없다. 하지만 예외 처리를 하면 예외 상황을 알려주는 메시지를 볼 수 있고, 프로그램이 비정상 종료되지 않고 계속 수행될 수 있도록 만들 수 있다.

 

try - catch - finally

프로그램에서 사용한 리소스는 프로그램이 종료되면 자동으로 해제된다. 예를 들어 네트워크가 연결되었을 경우, 채팅 프로그램이 종료될 때 연결도 닫힌다.

그러나 끝나지 않고 계속 수행되는 서비스 같은 경우 리소스를 여러번 반복해서 열기만 하고 닫지 않는다면 문제가 발생하게 된다.

시스템에서 허용하는 자원은 한계가 있기 때문이다.

따라서 사용한 시스템 리소스는 사용 후 반드시 close() 메서드로 닫아주어야 한다.

 

아래의 코드를 잠시 살펴보자.

try{
    fis = new FileInputStream("a.txt");
    if(fis != null){
        try{
            fis.close(); // try 블록에서 파일 리소스를 닫는 close() 메서드 호출
        } catch(IOException e){
            e.printStackTrace();
        }
    }
} catch(FileNotFoundException e){
    System.out.println(e);
}

 

  • 현재는 try 블록에서만 파일 리소스를 닫았다. 그런데 프로그램이 정상적으로 종료된 경우에도 열어놓은 파일 리소스를 닫아야 하고, 비정상 종료된 경우에도 리소스를 닫아야 한다.
  • 따라서 try 블록 뿐만이 아니라 catch 블록에도 close() 메소드를 사용해야 한다.
  • 만약 try 블록안에서 발생할 수 있는 예외 상황이 여러개라면 catch 블록을 예외 상황 수만큼 구현해야 한다.
  • 그런데 한번 열어놓은 리소스를 해제하는 코드를 try - catch - catch ... 각 블록에 모두 작성해야 한다면 정말 번거로워 진다.
  • 이때 사용하는 블록이 finally 이다.
  • finally 블록이 있을 경우 try 블록이 수행되면 붙어있는 finally 블록은 어떤 경우에도 반드시 수행된다.
  • 이를 테면 try 나 catch 문에 return 문이 있어도 수행된다.
  • 따라서 try - catch - catch ... 각 블록마다 리소스를 해제하지 않고 finally 블록에서 한번만 해제해주면 된다. 
public class ExceptionHandling3{
    public static void main(String[] args){
        FileInputStream fis = null;
        
        try{
            fis = new FileInputStream("a.txt");
        } catch(FileNotFoundException e){
            System.out.println(e);
            return;
        } finally {
            if(fis != null){
                try{
                    fis.close(); // 파일 입력 스트림 닫기
                } catch(IOException e){
                    e.printStackTrace();
                }
            }
            System.out.println("항상 수행됩니다.");
        }
        System.out.println("여기도 수행된다.");
    }
}

 

 

try - with - resources

JDK7+ 버전부터는 try - with - resources 문을 제공하여 close() 메서드를 명시적으로 호출하지 않아도 try 블록 내에서 열린 리소스가 자동으로 닫히도록 만들 수 있다.

try - with - resources 문법을 사용하려면 해당 리소스가 AutoCloseable 인터페이스를 구현해야 한다.

AutoCloseable 인터페이스에는 close() 메서드가 있고, 이를 구현한 클래스는 close() 를 명시적으로 호출하지 않아도 close() 메서드 부분이 호출된다.

* 앞서 살펴봤던 FileInputStream 클래스 또한 내부적으로 Closeable 과 AutoClosable 인터페이스를 구현하고 있다.

* FileInputStream 클래스 뿐만 아니라 네트워크(socket) 와 데이터베이스(connection) 관련 클래스도 AutoCloseable 인터페이스를 구현하고 있다.

 

 

AutoCloseable 인터페이스

AutoCloseable 인터페이스를 구현하는 클래스를 직접 만들어보고 close() 메서드가 잘 호출되는지 살펴보자.

public class AutoCloseObj implements AutoCloseable {
    
    // AutoCloseable 인터페이스의 close 메서드 구현
    @Override
    public void close() throws Exception {
        System.out.println("리소스가 close() 되었습니다."); 
    }
}

 

public class AutoCloseTest{
    public static void main(String[] args){
        try(AutoCloseObj obj = new AutoCloseObj()){ // 사용할 리소스 선언
        
        } catch(Exception e){
            System.out.println("예외 부분입니다.");
        }
    }
}

 

위의 AutoCloseTest 클래스를 실행시켜 보면 아래와 같은 결과가 출력되는 것을 확인해볼 수 있다.

리소스가 close() 되었습니다.

 

  • try - with - resources 문을 사용할 때 try 문의 괄호() 안에 리소스를 선언한다.
  • 위의 예제는 예외가 발생하지 않고 정상 출력되는데 출력결과를 보면 close() 메서드가 호출되어 '리소스가 close() 되었습니다.' 가 출력된것을 확인할 수 있다.
  • 리소스를 여러개 생성해야 한다면 세미콜론(;) 으로 구분할 수 있다.
try(A a = new A(); B b = new B()){

} catch(Exception e){
}

 

예외가 발생하여 종료되는 경우 또한 close() 메서드가 정상적으로 호출된다.

 

public class AutoCloseTest{
    public static void main(String[] args){
        try(AutoCloseObj obj = new AutoCloseObj()){ // 사용할 리소스 선언
            throw new Exception(); // 강제 예외 발생
        } catch(Exception e){
            System.out.println("예외 부분입니다.");
        }
    }
}

 

  • 위의 코드는 throw new Exception() 을 이용하여 강제로 예외 상황을 발생시켰다. 그 결과로 catch 블록이 수행된다.
  • 출력 결과를 보면 리소스의 close() 메서드가 먼저 호출되고 그 이후에 예외 블록이 수행되는 것을 알 수 있다.
  • 이처럼 try - with - resources 문을 사용하면 close() 메서드를 명시적으로 호출하지 않아도 정상 종료된 경우와 예외가 발생한 경우 모두 리소스가 잘 해제된다.
리소스가 close() 되었습니다.
예외 부분입니다.

 

 

향상된 try - with - resources 문

JDK9+ 버전 이후로 위의 예제에서 사용된 try - with - resources 문을 아래와 같이 작성할 수 있게되었다.

public class AutoCloseTest{
    public static void main(String[] args){
    
        AutoCloseObj obj = new AutoCloseObj()
        
        try(obj){ // 외부에서 선언한 변수를 그대로 사용가능 
            throw new Exception();
        } catch(Exception e){
            System.out.println("예외 부분입니다.");
        }
    }
}

 

 

예외 처리 미루기

예외 처리를 미루는 throws 에 대해 알아보자.

throws 는 예외를 해당 메서드에서 처리하지 않고 미룬 후, 메서드를 호출하여 사용하는 부분에서 예외를 처리하도록 만드는 방법이다.

public class ThrowsException {
    // throws : FileNotFoundException, ClassNotFoundException 두 예외를
    //  메서드가 호출될 때 처리하도록 미룸
    public Class loadClass(String fileName, String ClassName) throws FileNotFoundException, 
                                       ClassNotFoundException {
        // FileNotFoundException 발생 가능
        FileInputStream fis = new FileInputStream(fileName);
        
        // ClassNotFoundException 발생 가능
        Class c = Class.forName(className);
        return c;
    }
    
    public static void main(String[] args){
        ThrowsException test = new ThrowsException();
        test.loadClass("a.txt", "java.lang.String"); // 메서드 호출할 때 예외 처리
    }
}

 

  • 위 코드에서 정의한 loadClass() 메서드는 FileInputStream 을 열고 Class 를 동적으로 로딩하여 반환한다.
  • 파일을 열 때는 FileNotFoundException 이 발생할 수 있고, 클래스를 로딩할 시에는 ClassNotFoundException 이 발생할 수 있다.
  • 하지만 throws 를 통해 두 예외의 처리를 미루겠다는 것을 명시했다.
  • 예외를 처리하지 않고 미룬다고 throws 를 통해 선언하면 그 메서드를 호출하여 사용하는 부분에서 예외 처리를 해야한다.
  • main() 함수를 보면 loadClass() 메서드를 호출하는 부분이 있다. 따라서 위의 코드의 경우 main() 함수에서 loadClass() 메서드를 호출하는 부분에서 예외가 처리되지 않은 부분들에 때문에 컴파일 에러가 발생하고 있는것을 확인할 수 있다.
  • 그런데 여기서 만약에 컴파일 에러를 해결하기 위해 main() 함수에도 throws FileNotFoundException, ClassNotFoundException 을 사용한다면 main() 함수에서 미뤄진 예외 처리는 main() 함수를 호출하는 JVM 으로 보내진다.
  • 즉, 예외를 처리하는 것이 아니라 대부분의 프로그램이 비정상 종료된다.
  • 따라서 throws 로 미뤄진 예외를 메서드 호출 시점에 처리해주기 위해서는 try - catch 를 활용해야 한다.   
// 하나의 catch 문 만으로 예외 처리를 하는 경우
public static void main(String[] args){
    ThrowsException test = new ThrowException();
    
    try{
        test.loadClass("a.txt", "java.lang.String");
    } catch(FileNotFoundException | ClassNotFoundException){
        e.printStackTrace(); // 여러 예외를 한 문장으로 처리
    }
}

// 여러 예외를 각자에 맞게끔 처리할 경우
// 각 예외 상황마다 다른 방식으로 처리해야 하고 로그도 남겨놓아야 하는 경우 사용 
public static void main(String[] args){
    ThrowsException test = new ThrowException();
    
    try{
        test.loadClass("a.txt", "java.lang.String");
    } catch(FileNotFoundException e){
        e.printStackTrace(); 
    } catch(ClassNotFoundException e){
        e.printStackTrace();
    }
}

 

예외가 발생한 메서드에서 그 예외를 바로 처리할 것인지, 아니면 미루어서 그 메서드를 호출하여 사용하는 곳에서 처리할 것인지는 만들고자 하는 프로그램 상황에 따라 다를 수 있다.

만약 어떤 메서드가 다른 여러 코드에서 호출되어 사용된다면 호출하는 코드의 상황에 맞게 로그를 남기거나 예외 처리를 하는것이 좋다.

따라서 이런 경우에는 메서드를 호출하는 부분에서 예외 처리를 하도록 미루는 것이 합리적이다.

 

다중 예외처리

RuntimeException 의 경우 컴파일 시점에서는 언제 어떻게 발생하게 될지 알 수 없다. 

그럼에도 불구하고 모든 예외 상황을 처리하고자 한다면 맨 마지막 부분에 Exception 클래스를 활용하여 catch 블록을 추가할 수 있다.

public class ThrowsException {
    public Class loadClass(String fileName, String className)
            throws FileNotFoundException, ClassNotFoundException {
    
        FileInputStream fis = new FileInputStream(fileName);
        Class c = Class.forName(className);
        return c;
    }
    
    public static void main(String[] args){
        ThrowsException test = new ThrowsException();
        
        try{
            test.loadClass("a.txt", "java.lang.String");
        } catch(FileNotFoundException e){
            e.printStackTrace();
        } catch(ClassNotFoundException e){
            e.printStackTrace();
        } catch(Exception e){
            e.printStackTrace();
        } // Exception 클래스로 예상되는 것 이외의 예외 상황 처리
    }
}

 

Exception 클래스는 모든 예외 클래스의 최상위 클래스이다. 따라서 다른 catch 블록에 선언한 것 이외의 예외가 발생하더라도 Exception 클래스로 자동 형 변환된다.

 

* 다중 예외 처리에서 주의사항

아래의 코드를 한번 보자.

public static void main(String[] args){
    ThrowsException test = new ThrowsException();
    
    try{
        test.loadClass("a.txt", "java.lang.String");
    } catch(Exception e){ 
        // 맨 위에 Exception 을 활용한 catch 블록을 사용할 경우 이후 예외 클래스에 오류 발생
        e.printStackTrace();
    } catch(FileNotFoundException e){
        e.printStackTrace();
    } catch(ClassNotFoundException e){
        e.printStackTrace();
    }
}

 

  • 예외는 catch 문을 선언한 순서대로 검사한다. 따라서 맨 위에 catch(Exception e) 를 사용하면 발생하는 모든 예외 클래스는 Exception 상위 클래스로 자동 형 변환되어 오류가 발생한다.
  • 위의 코드의 경우 기본 예외 처리를 하는 Exception 클래스에 의해 모든 예외가 처리되므로 ClassNotFoundException, FileNotFoundException 문장에는 예외가 도달할 일이 없어 컴파일 오류가 발생한다.
  • 따라서 기본 예외 처리를 하는 Exception 클래스 블록은 여러 예외 처리 블록의 가장 아래에 놓여야 한다.

 

사용자 정의 예외

자바에서 제공하는 예외처리 클래스 이외에 개발하는 프로그램에 따라 다양한 예외 상황이 발생할 수 있다. 이는 개발중인 각 프로그램에 어떤 기능들이 구현되느냐에 따라 예외 처리에 대한 조건이 천차만별로 달라진다.

그런 경우 조건을 체크하는 작업을 자바 프로그램에서 한다면 예외 클래스를 직접 만들어 예외를 발생시키고 예외 처리 코드를 구현할 수 있다.

실무에서 프로젝트를 진행할 때에도 예외 클래스를 직접 만들어 사용하는 경우가 종종 있으므로 사용자 정의 예외 클래스를 어떻게 구현하는지 알아두면 좋다.

 

사용자 정의 예외 클래스 구현하기

사용자 정의 예외 클래스를 구현할 때는 기존 JDK 에서 제공하는 예외 클래스 중 가장 유사한 클래스를 상속 받는것이 좋다. 

유사한 예외 클래스를 잘 모르겠다면 가장 상위 클래스인 Exception 클래스에서 상속 받으면 된다.

 

아래는 아이디가 null 값이거나 지정 범위를 벗어나는 경우의 예외 처리 클래스이다.

public class IDFormatException extends Exception {
    public IDFormatException(String message) { // 생성자의 매개변수로 예외 상황 메세지를 받음
        super(message);
    }
}

 

  • 위의 클래스는 Exception 클래스에서 상속을 받아 구현했다.
  • 예외 상황 메세지를 생성자에서 입력받는다. Exception 클래스에서 이미 메세지 생성자, 멤버 변수와 메서드를 제공하고 있으므로 super(message) 를 사용하여 예외 메세지를 설정한다.
  • 나중에 getMessage() 메서드를 호출하면 메세지의 내용을 확인해볼 수 있다.

위의 사용자 예외 클래스를 이용해 예외를 한번 발생시켜 보자.

public class IDFormatTest{
    private String userID;
    
    public String getUserID(){
        return userID;
    }
    
    // 아이디에 대한 제약조건 구현
    // throws : IDFormatException 예외를 setUserID 메서드가 호출될 때 처리하도록 미룸
    public void setUserID(String userID) throws IDFormatException {
        if(userID == null) {
            // 강제로 예외 발생
            throw new IDFormatException("아이디는 null 일 수 없습니다.");
        }
        else if(userID.length() < 8 || userID.length() > 20) {
            // 강제로 예외 발생
            throw new IDFormatException("아이디는 8자 이상, 20자 이하로 쓰세요");
        }
        this.userID = userID;
    }
    
    public static void main(String[] args){
        IDFormatTest test = new IDFormatTest();
        
        // 아이디 값이 null 인 경우
        String userID = null;
        try{
            test.setUserID(userID);
        } catch(IDFormatException e){
            System.out.println(e.getMessage());
        }
        
        // 아이디 값이 8자 이하인 경우
        userID = "1234567";
        try{
            test.setUserID(userID);
        } catch(IDFormatException e){
            System.out.println(e.getMessage());
        }
    }
}
  • IDFormatTest 클래스에서 setUserID() 메서드는 아이디에 대한 제약 조건을 구현한다. 이 제약 조건이 지켜지지않으면 예외를 발생시킨다.
  • 여기서 발생하는 예외는 자바에서 제공하는 예외가 아니기 때문에 예외 클래스를 직접 생성하여 예외를 발생시켜야 한다.
  • userID 가 null 인 경우 예외 메세지를 생성자에 넣어 예외 클래스를 생성한 후 throw 문으로 직접 예외를 발생시킨다.
  • 아이디가 8자 미만, 20자 초과인 경우에도 길이 관련 예외를 발생시킨다.
  • setUserID() 메서드는 IDFormatException 예외 처리를 해야한다. 이 예외는 메서드를 호출하는 부분에서 처리하도록 throws 예약어를 선언해준다. 
  • throws 키워드를 통해 main 메서드에서 setUserID 메서드가 호출될 때 메서드 내부에 작성되어 있는 조건에 해당되어 IDFormatException 이 발생하는 경우 main 메서드에 작성된 try - catch 문에서 해당 예외가 처리된다.

 


* 추신 : 예외 처리를 할 때는 로그를 잘 남겨야 한다.

회사에서 개발하여 구축한 어떤 시스템에 대해 오류가 발생했다면 개발자는 어떤 조치를 취할 수 있을까?

어떤 상황에서 오류가 났는지, 또 시스템에서 어떤 메서드를 호출하고 어떻게 매개변수를 전달했는지 오류 현상만 보고는 알 수 없다.

따라서 프로그램을 개발 할 때는 로그(log) 를 남기는것이 매우 중요하다. 오류가 발생했을 때 로그를 보고 오류가 발생하는 코드를 순서대로 따라가며 확인할 수 있고 원인을 찾을 수 있다.

로그는 정보 의미에 따라 레벨을 나누어 관리한다. 

간단한 정보 의미를 가진 로그부터 심각한 예외가 발생했을 때의 로그까지 여러 레벨이 존재할 수 있다.

이러한 로그를 체계적이고 의미있게 남겨서 시스템에서 오류가 났을 때 그 원인을 유추해볼 수 있어야 한다.