예외 처리 2

이동건 (불꽃냥펀치)·2024년 11월 20일
0

예외 처리 도입

시작

package exception.ex2;
public class NetworkClientExceptionV2 extends Exception {
    private String errorCode;
    public NetworkClientExceptionV2(String errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }
    public String getErrorCode() {
        return errorCode;
	}
}
  • 예외도 객체이므로 필요에 따라서 필드와 메서드를 가질 수 있다.
  • 어떤 종류의 오류가 발생했는지 구분하기 위해 예외 안에 오류 코드를 보관한다.
  • 오류 메시지에는 어떤 오류가 발생했는지 개발자가 보고 이해할 수 있는 설명을 담아둔다.
package exception.ex2;
public class NetworkClientV2 {
    private final String address;
    public boolean connectError;
    public boolean sendError;
    
    public NetworkClientV2(String address) {
        this.address = address;
	}
    
    public void connect() throws NetworkClientExceptionV2 {
        if (connectError) {
			
            throw new NetworkClientExceptionV2("connectError", address +
            " 서버 연결 실패"); 
            }
		//연결 성공
		System.out.println(address + " 서버 연결 성공"); 
    }
    
    public void send(String data) throws NetworkClientExceptionV2 {
        if (sendError) {
			throw new NetworkClientExceptionV2("sendError", address + 
            " 서버에 데이 터 전송 실패: " + data);
		}
		//전송 성공
		System.out.println(address + " 서버에 데이터 전송: " + data); 
     }
     
	public void disconnect() { 
    	System.out.println(address + " 서버 연결 해제");
    }
    
    public void initError(String data) {
    
		if (data.contains("error1")) {
    		connectError = true;
		}
		if (data.contains("error2")) {
    		sendError = true;
		}
    }
}
  • 예외처리 1 코드와 대부분 같지만 오류가 발생했을 때 오류 코드를 반환하는 것이 아니라 예외를 던진다.
  • 이전에는 반환값으로 성공/실패를 확인해야 했지만, 예외 처리 덕분에 메서드가 정상 종료되면 성공이다.
  • 오류가 발생하면 예외 객체를 만들고 거기에 오류 코드와 오류 메시지를 담아둔다. 그리고 만든 예외 객체를 throw를 통해 던진다.
package exception.ex2;
public class NetworkServiceV2_1 {
    public void sendMessage(String data) throws NetworkClientExceptionV2 {
        String address = "http://example.com";
        NetworkClientV2 client = new NetworkClientV2(address);
        client.initError(data);
        client.connect();
        client.send(data);
        client.disconnect();
	} 
}
  • 여기서는 예외를 별도로 처리하지 않고, throws를 통해 밖으로 던진다.
package exception.ex2;
import java.util.Scanner;
public class MainV2 {
    public static void main(String[] args) throws NetworkClientExceptionV2 {
        NetworkServiceV2_1 networkService = new NetworkServiceV2_1();
        Scanner scanner = new Scanner(System.in);
        while (true) {
			System.out.print("전송할 문자: "); 
            String input = scanner.nextLine(); 
            if (input.equals("exit")) {
				  break; 
             }
            networkService.sendMessage(input);
            System.out.println();
        }
		System.out.println("프로그램을 정상 종료합니다."); 
     }
}	
  • 여기서도 예외를 처리하지 않고 throws를 통해 밖으로 던진다.
  • 예외 처리를 도입했지만 아직 예외가 복구되지 않는다.
  • 예외가 발생하여 프로그램이 종료되고, disconnect() 호출이 안되는 문제가 남아있다.
package exception.ex2;
 public class NetworkServiceV2_4 {
     public void sendMessage(String data) {
         String address = "http://example.com";
         NetworkClientV2 client = new NetworkClientV2(address);
         client.initError(data);
         
         try {
         
             client.connect();
             client.send(data);
             
		} catch (NetworkClientExceptionV2 e) {
			
            System.out.println("[오류] 코드: " + e.getErrorCode() +
            ", 메시지: " +  e.getMessage());
       }
		//NetworkClientException이 아닌 다른 예외가 발생해서 예외가 밖으로 던져지면 무시
         client.disconnect();
     }
}
  • 이 코드를 보면 예외 처리가 끝난 다음에 정상 흐름의 마지막에 client.disconnect()를 호출하여 예외가 처리되면 항상 client.disconnect()가 호출된다.
  • 하지만 이 코드에서는 NetworkClientExceptionV2가 아닌 다른 오류가 생기면 client.disconnect()가 호출되지 않는다.

finally

try {
//정상 흐름
} catch { 
//예외 흐름 
} finally {
//반드시 호출해야 하는 마무리 흐름 
}
  • try~catch~finally 구조는 정상 흐름, 예외 흐름, 마무리 흐름을 제공한다.
  • 여기서 try를 시작하기만 하면, finally 코드 블럭은 어떤 경우라도 반드시 호출된다.
  • 심지어 try,catch 안에서 잡을 수 없는 예외가 발생해도 finally는 반드시 호출된다.

finally 블럭은 반드시 호출되어 주로 try에서 사용한 자원을 해제할 때 주로 사용한다.

package exception.ex2;
public class NetworkServiceV2_5 {
    public void sendMessage(String data) {
        String address = "https://example.com";
        NetworkClientV2 client = new NetworkClientV2(address);
        client.initError(data);
        
        try {
        
            client.connect();
            client.send(data);
            
         } catch (NetworkClientExceptionV2 e) {
			
            System.out.println("[오류] 코드: " + e.getErrorCode() + 
            ", 메시지: " + e.getMessage());
            
         } finally {
         
             client.disconnect();
		} 
	}
}
  • try~catch 안에서 처리할 수 없는 예외가 발생해도 finally는 반드시 호출된다.
  • 위의 코드에서 NetworkClientExceptionV2에서 RuntimeException으로 오류가 변경되면 try~catch로 잡지 못해 오류가 발생할텐데그럼에도 finally 블럭의 코드는 실행된다.
  • catch에서 잡을 수 없는 예외가 발생해서, 예외를 밖으로 던지는 경우에도 finally를 먼저 호출하고서 예외를 밖으로 던진다.

try ~ finally

다음과 같이 catch 없이 try~finally만 사용할 수 있다.

 try {
     client.connect();
     client.send(data);
 } finally {
     client.disconnect();
}



예외의 계층

예외를 오류 코드로 분류하는 것이 아니라, 예외를 계층화해서 다양하게 만들면 더 세밀하게 예외를 처리 할 수 있다.

  • 예외의 계층화 장점
    • 자바에서 예외는 개체이다. 따라서 부모 예외를 잡거나 던지면 자식 예외도 함께 잡거나 던질 수 있다.
    • 특정 예외를 처리하고자 한다면 하위 예외를 잡아서 처리하면 된다.
package exception.ex3.exception;
 public class NetworkClientExceptionV3 extends Exception {
     public NetworkClientExceptionV3(String message) {
           super(message);
    }
}
package exception.ex3.exception;
public class ConnectExceptionV3 extends NetworkClientExceptionV3 {
    private final String address;
    
    public ConnectExceptionV3(String address, String message) {
        super(message);
        this.address = address;
    }
    
    public String getAddress() {
        return address;
	} 
}
package exception.ex3.exception;
public class SendExceptionV3 extends NetworkClientExceptionV3 {
    private final String sendData;
    public SendExceptionV3(String sendData, String message) {
        super(message);
        this.sendData = sendData;
    }
    public String getSendData() {
        return sendData;
	} 
}
  • NetworkClientExceptionV3를 상속받아 2개의 자식 예외를 만들었다.
package exception.ex3;
import exception.ex3.exception.ConnectExceptionV3;
import exception.ex3.exception.SendExceptionV3;
public class NetworkClientV3 {
    private final String address;
    public boolean connectError;
    public boolean sendError;
    public NetworkClientV3(String address) {
        this.address = address;
	}
    public void connect() throws ConnectExceptionV3 {
        if (connectError) {
			throw new ConnectExceptionV3(address, address + " 서버 연결 실패"); 
        }
		System.out.println(address + " 서버 연결 성공"); 
    }
    
    public void send(String data) throws SendExceptionV3 {
        if (sendError) {
			throw new SendExceptionV3(data, address + " 서버에 데이터 전송 실패: " + data)
            }
		System.out.println(address + " 서버에 데이터 전송: " + data);
    }
    
	public void disconnect() { 
    	System.out.println(address + " 서버 연결 해제");
	}
    
    public void initError(String data) {
        if (data.contains("error1")) {
            connectError = true;
        }
           if (data.contains("error2")) {
            sendError = true;
		}
	}
}
  • 예외 클래스를 각각의 예외 상황에 맞춰 만들면, 각 필요에 맞는 예외를 잡아서 처리할 수 있다.
  • 예를 들어 각 예외에 맞는 getAddress() getSendData()와 같이 예외 클래스가 가지는 고유의 기능을 활용할 수 있다.

여러 예외를 한번에 잡기

아래와 같이 | 를 사용해서 여러 예외를 한번에 잡을 수 있다.

try {
     client.connect();
     client.send(data);
 	} catch (ConnectExceptionV3 | SendExceptionV3 e) {
 	System.out.println("[연결 또는 전송 오류] 주소: , 메시지: " + e.getMessage()); 
    } finally {
     client.disconnect();
 }

try-with-resource

애플리케이션에서 외부 자원을 사용하는 경우 반드시 외부 자원을 해제해야한다.
따라서 finally 구문을 반드시 사용해야한다.
try에서 외부 자원을 사용하고 try가 끝나면 외부 자원을 반납하는 패턴이 반복되면서 Try with resource라는 편의 기능을 자바에서 도입했다.

  • 이 기능을 사용하려면 먼저 AutoCloseable인터페이스를 구현해야 한다.
  • close(): AutoCloseable 인터페이스가 제공하는 이 메서드는 try가 끝나면 자동으로 호출된다. 종료 시점에 자원을 반납하는 방법을 여기에 정의하면 된다.
   try (NetworkClientV5 client = new NetworkClientV5(address)) {
             client.initError(data);
             client.connect();
             client.send(data);
	} catch (Exception e) {
			System.out.println("[예외 확인]: " + e.getMessage()); 
            throw e;
	}
  • Try with resource 구문은 try안에 사용할 자원을 명시한다.
profile
자바를 사랑합니다

0개의 댓글

관련 채용 정보