[Java] 자바 중급(9) 예외 처리2

wony·2024년 4월 2일

Java

목록 보기
16/30

0.개요

주제 : 김영한님의 자바 중급 1편 총 정리
내용 : 예외 처리(실습)에 대해 공부

1. 예외 처리 도입1 - 시작


예외 처리1에서 만든 프로그램은 반환 값을 사용해서 예외를 처리했다. 이런 경우 다음과 같은 문제가 발생한다.

  • 정상 흐름과 예외 흐름이 섞여 있기 때문에 코드를 한눈에 이해하기 어렵다. 즉, 가장 중요한 정상 흐름이 한눈에 들어오지 않는다.
  • 심지어 예외 흐림이 더 많은 코드 분량을 차지한다.

public class NetworkClientExceptionV2 extends Exception{
    private String errorCode;
    public NetworkClientExceptionV2(String errorCode, String message){
        super(message);
        this.errorCode = errorCode;
    }
    public String getErrorCode(){
        return errorCode;
    }
}

예외도 객체이다. 따라서, 필요한 필드와 메서드를 가질 수 있다.

오류코드

  • 이전에는 오류 코드(errorCode)를 반환 값으로 리턴해서 어떤 오류가 발생했는지 구분했다.
  • 여기서는 어떤 종류의 오류가 발생했는지 구분하기 위해 예외 안에 오류 코드를 보관한다.

오류 메시지

  • 오류 메시지(messsage)에는 어떤 오류가 발생했는지 개발자가 보고 이해할 수 있는 설명을 담아둔다.
  • 오류 메시지는 상위 클래스인 Throwable에서 기본으로 제공하는 기능을 사용한다.
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;
        }
    }
}
  • 기존의 코드와 대부분 같지만, 오류가 발생했을 때 오류 코드를 반환하는 것이 아니라 예외를 던진다.
  • 따라서, 반환 값을 사용하지 않아도 된다. 여기서는 반환 값을 void로 처리한다.
  • 이전에는 반환 값으로 성공, 실패 여부를 확인해야 했지만, 예외 처리 덕분에 메서드가 정상 종료되면 성공이고, 예외가 던져지면 예외를 통해 실패를 확인할 수 있다.
  • 오류가 발생하면, 예외 객체를 만들고 거기에 오류 코드와 오류 메시지를 담아둔다. 그리고 만든 예외 객체를 throw 를 통해 던진다.
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를 통해 밖으로 던진다.
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를 통해 밖으로 던진다.
  • helloe 이면 잘 작동한다.
  • error1 이면 연결 실패가 발생한다.
  • 모든 곳에서 발생한 예외를 잡지 않았기 때문에 결과적으로 main() 밖으로 예외가 던져진다.
  • main() 밖으로 예외가 던져지면 예외 메시지와 예외를 추적할 수 있는 스택 트레이스를 출력하고 프로그램을 종료한다.

남은 문제

  • 예외 처리를 도입했지만, 아직 예외가 복귀되지 않았다. 따라서, 예외가 발생하면 프로그램이 종료된다.
  • 사용 후에는 반드시 disconnect()를 호출해서 연결을 해제해야 한다.

2. 예외 처리 도입2 - 예외 복구

이번에는 예외를 잡아서 예외 흐름을 정상 흐름으로 복구해보자

public class NetworkServiceV2_2 {
    public void sendMessage(String data){
        String address = "http://example.com";
        NetworkClientV2 client = new NetworkClientV2(address);
        client.initError(data);
        try {
            client.connect();
        } catch (NetworkClientExceptionV2 e) {
            System.out.println("[오류] 코드: " + e.getErrorCode() + ", 메시지: " +
                    e.getMessage());
            return;
        }
        try {
            client.send(data);
        } catch (NetworkClientExceptionV2 e) {
            System.out.println("[오류] 코드: " + e.getErrorCode() + ", 메시지: " +
                    e.getMessage());
            return;
        }
        client.disconnect();
    }
}
  • connect() , send() 와 같이 예외가 발생할 수 있는 곳을 try ~ catch 를 사용해서 NetworkClientExceptionV2 예외를 잡았다.
  • 여기서는 예외를 잡으면 오류 코드와 예외 메시지를 출력한다.
  • 예외를 잡아서 처리했기 때문에 이후에는 정상 흐름으로 복귀한다. 여기서는 리턴을 사용해서 sendMessage() 메서드를 정상적으로 빠져나간다.

해결된 문제

  • 예외를 잡아서 처리했다. 따라서 예외가 복구 되고, 프로그램도 계속 수행할 수 있다.

남은 문제

  • 예외 처리를 했지만 정상 흐름과 예외 흐름이 섞여 있어서 코드를 읽기 어렵다.
    사용 후에는 반드시 disconnect() 를 호출해서 연결을 해제해야 한다.

3.예외 처리 도입3 - 정상, 예외 흐름 분리

public class NetworkServiceV2_3 {
    public void sendMessage(String data){
        String address = "http://example.com";
        NetworkClientV2 client = new NetworkClientV2(address);
        client.initError(data);
        try {
            client.connect();
            client.send(data);
            client.disconnect(); //예외 발생시 무시
        } catch (NetworkClientExceptionV2 e) {
            System.out.println("[오류] 코드: " + e.getErrorCode() + ", 메시지: " +
                    e.getMessage());
        }
    }
}
  • 하나의 try 안에 정상 흐름을 모두 담는다.
  • 그리고 예외 부분은 catch 블럭에서 해결한다.
  • 이렇게 하면 정상 흐름은 try 블럭에 들어가고, 예외 흐름은 catch 블럭으로 명확하게 분리할 수 있다.

해결된 문제

  • 자바의 예외 처리 메커니즘과 try , catch 구조 덕분에 정상 흐름은 try 블럭에 모아서 처리하고, 예외 흐름은 catch 블럭에 별도로 모아서 처리할 수 있었다.
  • 덕분에 정상 흐름과 예외 흐름을 명확하게 분리해서 코드를 더 쉽게 읽을 수 있게 되었다.

남은 문제

  • 사용 후에는 반드시 disconnect() 를 호출해서 연결을 해제해야 한다.

4.예외 처리 도입4 - 리소스 반환 문제

현재 구조에서 disconnect()를 항상 호출하려면 다음과 같이 생각할 수 있다.

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() 가 항상 호출될 것 같다.

하지만 지금과 같은 방식에는 큰 문제가 있다.
바로 catch 에서 잡을 수 없는 예외가 발생할 때이다.
-> 결국 새로운 대안이 필요하다.

5.예외 처리 도입5 - finally

자바는 어떤 경우라도 반드시 호출되는 finally기능을 제공한다.

try {
//정상 흐름
} catch {
//예외 흐름
} finally {
//반드시 호출해야 하는 마무리 흐름
}
  • try ~ catch ~ finally 구조는 정상 흐름, 예외 흐름, 마무리 흐름을 제공한다.
  • 여기서 try 를 시작하기만 하면, finally 코드 블럭은 반드시 호출된다.
  • try , catch 안에서 잡을 수 없는 예외가 발생해도finally 는 반드시 호출된다.
  • finally 코드 블럭이 끝나고 나서 이후에 예외가 밖으로 던져짐
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();
        }
    }
}

처리할 수 없는 예외와 finally
try , catch 안에서 처리할 수 없는 예외가 발생해도 finally 는 반드시 호출된다.

정리
자바 예외 처리는 try ~ catch ~ finally 구조를 사용해서 처리할 수 있다. 덕분에 다음과 같은 이점이 있다.

  • 정상 흐름과 예외 흐름을 분리해서, 코드를 읽기 쉽게 만든다.
  • 사용한 자원을 항상 반환할 수 있도록 보장해준다.

6.예외 계층1 - 시작


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

  • NetworkClientExceptionV3 : NetworkClient 에서 발생하는 모든 예외는 이 예외의 자식이다.
  • ConnectExceptionV3 : 연결 실패시 발생하는 예외이다. 내부에 연결을 시도한 address 를 보관한다.
  • SendExceptionV3 : 전송 실패시 발생하는 예외이다. 내부에 전송을 시도한 데이터인 sendData 를 보관한다.

이렇게 예외를 계층화하면 다음과 같은 장점이 있다.

  • 자바에서 예외는 객체이다. 따라서 부모 예외를 잡거나 던지면, 자식 예외도 함께 잡거나 던질 수 있다. 예를 들어서 NetworkClientExceptionV3 예외를 잡으면 그 하위인 ConnectExceptionV3 ,SendExceptionV3 예외도 함께 잡을 수 있다.
  • 특정 예외를 잡아서 처리하고 싶으면 ConnectExceptionV3 , SendExceptionV3 와 같은 하위 예외를 잡아서 처리하면 된다.
public class NetworkClientExceptionV3 extends Exception{
    public NetworkClientExceptionV3(String message) {
        super(message);
    }
}
  • NetworkClient 에서 발생하는 모든 예외는 이 예외를 부모로 하도록 설계한다.
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;
    }
}
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;
    }
}
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;
        }
    }
}
  • ConnectExceptionV3 : 연결 실패시 발생하는 예외 따로 만들었다.
  • SendExceptionV3 : 전송 실패시 발생하는 예외 따로 만들었다.
  • 연결 관련 오류 발생하면 ConnectExceptionV3 를 던지고, 전송 관련 오류가 발생하면SendExceptionV3 를 던진다.
public class NetworkServiceV3_1 {
    public void sendMessage(String data) {
        String address = "https://example.com";
        NetworkClientV3 client = new NetworkClientV3(address);
        client.initError(data);
        try {
            client.connect();
            client.send(data);
        } catch (ConnectExceptionV3 e) {
            System.out.println("[연결 오류] 주소: " + e.getAddress() + ", 메시지: " +
                    e.getMessage());
        } catch (SendExceptionV3 e) {
            System.out.println("[전송 오류] 전송 데이터: " + e.getSendData() + ", 메시지: " + e.getMessage());
        } finally {
            client.disconnect();
        }
    }
}
  • 예외 클래스를 각각의 예외 상황에 맞추어 만들면, 각 필요에 맞는 예외를 잡아서 처리할 수 있다.
  • 예를 들면 e.getAddress() , e.getSendData() 와 같이 각각의 예외 클래스가 가지는 고유의 기능을 활용할 수 있다.
  • catch (ConnectExceptionV3 e) : 연결 예외를 잡고, 해당 예외가 제공하는 기능을 사용해서 정보를 출력한다.
  • catch (SendExceptionV3 e) : 전송 예외를 잡고, 해당 예외가 제공하는 기능을 사용해서 정보를 출력한다.

Main 클래스

public class MainV3 {
    public static void main(String[] args) {
        NetworkServiceV3_1 networkService = new NetworkServiceV3_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("프로그램을 정상 종료합니다.");
    }
}
  • 실행 결과를 보면 ConnectExceptionV3 , SendExceptionV3 이 발생한 각각의 경우에 출력된 오류 메시지가 다른 것을 확인할 수 있다.

7.예외 계층2-활용

  • 이번에는 예외를 잡아서 처리할 때 예외 계층을 활용해보자.
  • NetworkClientV3 에서 수 많은 예외를 발생한다고 가정해보자. 이런 경우 모든 예외를 하나하나 다 잡아서 처리하는 것은 상당히 번거로울 것이다. 그래서 다음과 같이 예외를 처리하도록 구성해보자.
public class NetworkServiceV3_2 {
    public void sendMessage(String data) {
        String address = "https://example.com";
        NetworkClientV3 client = new NetworkClientV3(address);
        client.initError(data);
        try {
            client.connect();
            client.send(data);
        } catch (ConnectExceptionV3 e) {
            System.out.println("[연결 오류] 주소: " + e.getAddress() + ", 메시지: " +
                    e.getMessage());
        } catch (NetworkClientExceptionV3 e) {
            System.out.println("[네트워크 오류] 메시지: " + e.getMessage());
        } catch (Exception e) {
            System.out.println("[알 수 없는 오류] 메시지: " + e.getMessage());
        } finally {
            client.disconnect();
        }
    }
}

8.실무 예외 처리 방안1

예외 공통 처리
처리할 수 없는 예외들은 중간에 여러곳에서 나누어 처리하기 보다는 예외를 공통으로 처리할 수 있는 곳을 만들어서 한 곳에서 해결하면 된다. 어차피 해결할 수 없는 예외들이기 때문에 이런 경우 고객에게는 현재 시스템에 문제가 있습니다라고 오류 메시지를 보여주고, 만약 웹이라면 오류 페이지를 보여주면 된다. 그리고 내부 개발자가 지금의 문제 상황을 빠르게 인지할 수 있도록, 오류에 대한 로그를 남겨두면 된다. 이런 부분은 공통 처리가 가능하다.

9.실무 예외 처리 방안2 - 구현

실습한 내용을 언체크 예외로 만들고,해결할 수 없는 예외들을 공통으로 처리해보자.

  • NetworkClientExceptionV4 는 언체크 예외인 RuntimeException 을 상속 받는다.
  • 이제 NetworkClientExceptionV4 와 자식은 모두 언체크(런타임) 예외가 된다.
public class NetworkClientExceptionV4 extends RuntimeException{
    public NetworkClientExceptionV4(String message){
        super(message);
    }
}
public class SendExceptionV4 extends NetworkClientExceptionV4{
    private final String sendData;
    public SendExceptionV4(String sendData, String message) {
        super(message);
        this.sendData = sendData;
    }
    public String getSendData() {
        return sendData;
    }
}
public class ConnectExceptionV4 extends NetworkClientExceptionV4{
    private final String address;
    public ConnectExceptionV4(String address, String message) {
        super(message);
        this.address = address;
    }
    public String getAddress() {
        return address;
    }
}
public class NetworkClientV4 {
    private final String address;
    public boolean connectError;
    public boolean sendError;
    public NetworkClientV4(String address) {
        this.address = address;
    }
    public void connect() {
        if (connectError) {
            throw new ConnectExceptionV4(address, address + " 서버 연결 실패");
        }
        System.out.println(address + " 서버 연결 성공");
    }
    public void send(String data) {
        if (sendError) {
            throw new SendExceptionV4(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;
        }
    }
}
public class NetworkServiceV4 {
    public void sendMessage(String data) {
        String address = "https://example.com";
        NetworkClientV4 client = new NetworkClientV4(address);
        client.initError(data);
        try {
            client.connect();
            client.send(data);
        } finally {
            client.disconnect();
        }
    }
}
  • NetworkServiceV4 는 발생하는 예외인 ConnectExceptionV4 , SendExceptionV4 를 잡아도 해당 오류들을 복구할 수 없다. 따라서 예외를 밖으로 던진다.
  • 언체크 예외이므로 throws 를 사용하지 않는다.
  • 사실 NetworkServiceV4 개발자 입장에서는 해당 예외들을 복구할 수 없다. 따라서 해당 예외들을 생각하지 않는 것이 더 나은 선택일 수 있다. 해결할 수 없는 예외들은 다른 곳에서 공통으로 처리된다.
  • 이런 방식 덕분에 NetworkServiceV4 는 해결할 수 없는 예외 보다는 본인 스스로의 코드에 더 집중할 수 있다. 따라서 코드가 깔끔해진다.
public class MainV4 {
    public static void main(String[] args) {
        NetworkServiceV4 networkService = new NetworkServiceV4();
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("전송할 문자: ");
            String input = scanner.nextLine();
            if (input.equals("exit")) {
                break;
            }
            try {
                networkService.sendMessage(input);
            } catch (Exception e) { // 모든 예외를 잡아서 처리
                exceptionHandler(e);
            }
            System.out.println();
        }
        System.out.println("프로그램을 정상 종료합니다.");
    }
    //공통 예외 처리
    private static void exceptionHandler(Exception e) {
        //공통 처리
        System.out.println("사용자 메시지: 죄송합니다. 알 수 없는 문제가 발생했습니다.");
        System.out.println("==개발자용 디버깅 메시지==");
        e.printStackTrace(System.out); // 스택 트레이스 출력
        //e.printStackTrace(); // System.err에 스택 트레이스 출력
        //필요하면 예외 별로 별도의 추가 처리 가능
        if (e instanceof SendExceptionV4 sendEx) {
            System.out.println("[전송 오류] 전송 데이터: " + sendEx.getSendData());
        }
    }
}

공통 예외 처리
여기에 예외를 공통으로 처리하는 부분이 존재한다.

  • Exception 을 잡아서 지금까지 해결하지 못한 모든 예외를 여기서 공통으로 처리한다. Exception 을 잡으면필요한 모든 예외를 잡을 수 있다.
  • 예외도 객체이므로 공통 처리 메서드인 exceptionHandler(e) 에 예외 객체를 전달한다.

exceptionHandler()

  • 해결할 수 없는 예외가 발생하면 사용자에게는 시스템 내에 알 수 없는 문제가 발생했다고 알리는 것이 좋다.
    • 사용자가 디테일한 오류 코드나 오류 상황까지 모두 이해할 필요는 없다. 예를 들어서 사용자는 데이터베이스 연결이 안되서 오류가 발생한 것인지, 네트워크에 문제가 있어서 오류가 발생한 것인지 알 필요는 없다.
  • 개발자는 빨리 문제를 찾고 디버깅 할 수 있도록 오류 메시지를 남겨두어야 한다.
  • 예외도 객체이므로 필요하면 instanceof 와 같이 예외 객체의 타입을 확인해서 별도의 추가 처리를 할 수 있다.

e.printStackTrace()

  • 예외 메시지와 스택 트레이스를 출력할 수 있다.
  • 이 기능을 사용하면 예외가 발생한 지점을 역으로 추적할 수 있다.
  • 참고로 예제에서는 e.printStackTrace(System.out) 을 사용해서 표준 출력으로 보냈다.
  • e.printStackTrace() 를 사용하면 System.err 이라는 표준 오류에 결과를 출력한다.
    • IDE에서는 System.err 로 출력하면 출력 결과를 빨간색으로 보여준다.
    • 일반적으로 이 방법을 사용한다.

[추가] try-with-resources

애플리케이션에서 외부 자원을 사용하는 경우 반드시 외부 자원을 해제해야 한다. try 에서 외부 자원을 사용하고, try 가 끝나면 외부 자원을 반납하는 패턴이 반복되면서 자바에서는 Try with resources라는 편의 기능을 자바 7에서 도입했다. 이름 그대로 try 에서 자원을 함께 사용한다는 뜻이다. 여기서 자원은 try 가 끝나면 반드시 종료해서 반납해야 하는 외부 자원을 뜻한다.

이 기능을 사용하려면 먼저 AutoCloseable 인터페이스를 구현해야 한다.

public interface AutoCloseable {
    void close() throws Exception;
}

이 인터페이스를 구현하면 Try with resources를 사용할 때 try 가 끝나는 시점에 close() 가 자동으로 호출된다.
그리고 다음과 같이 Try with resources 구문을 사용하면 된다.

try (Resource resource = new Resource()) {
// 리소스를 사용하는 코드
}

이제 구현 코드를 만들어보자.
기존 코드를 Try with resources 구문을 사용하도록 변경해보자.

public class NetworkClientV5 implements AutoCloseable {
    private final String address;
    public boolean connectError;
    public boolean sendError;
    public NetworkClientV5(String address) {
        this.address = address;
    }
    public void connect() {
        if (connectError) {
            throw new ConnectExceptionV4(address, address + " 서버 연결 실패");
        }
        System.out.println(address + " 서버 연결 성공");
    }
    public void send(String data) {
        if (sendError) {
            throw new SendExceptionV4(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;
        }
    }
    @Override
    public void close() {
        System.out.println("NetworkClientV5.close");
        disconnect();
    }
}
  • implements AutoCloseable 을 통해 AutoCloseable 을 구현한다.
  • close(): AutoCloseable 인터페이스가 제공하는 이 메서드는 try 가 끝나면 자동으로 호출된다. 종료 시점에 자원을 반납하는 방법을 여기에 정의하면 된다. 참고로 이 메서드에서 예외를 던지지는 않으므로 인터페이스의
    메서드에 있는 throws Exception 은 제거했다.
public class NetworkServiceV5 {
    public void sendMessage(String data) {
        String address = "https://example.com";
        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 resources 구문은 try 괄호 안에 사용할 자원을 명시한다.
  • 이 자원은 try 블럭이 끝나면 자동으로 AutoCloseable.close() 를 호출해서 자원을 해제한다.
  • 참고로 여기서 catch 블럭 없이 try 블럭만 있어도 close() 는 호출된다.
  • 여기서 catch 블럭은 단순히 발생한 예외를 잡아서 예외 메시지를 출력하고, 잡은 예외를 throw 를 사용해서 다시 밖으로 던진다.

`

profile
안녕하세요. wony입니다.

0개의 댓글