JAVA의 가장 기본이 되는 내용

try-with-resources

cwchoiit 2024. 4. 23. 09:56
728x90
반응형
SMALL

참고자료:

 

김영한의 실전 자바 - 중급 1편 | 김영한 - 인프런

김영한 | 실무에 필요한 자바의 다양한 중급 기능을 예제 코드로 깊이있게 학습합니다., 국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바 문법을

www.inflearn.com

 

try-with-resources 구문을 사용해서 사용한 자원을 반납하는데 효율적으로 반납해보자.

애플리케이션에서 외부 자원을 사용하는 경우 반드시 외부 자원을 해제해야 한다. (예: Database connection)

그래서 항상 try - finally 구문을 사용했다. 

try {
   //정상 흐름
} catch() {
   //예외 흐름
} finally {
   //반드시 호출해야 하는 마무리 흐름
}

이런 반복적인 구문을 사용하면서 편의 기능을 자바 7에서 도입했는데 그게 try-with-resources다.

이 구문을 사용하면 try가 끝나는 순간 자원을 알아서 반납해준다.

 

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

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

 

이 인터페이스를 구현한 클래스의 인스턴스를 사용하면 try가 끝나는 시점에 close() 메서드가 자동으로 호출된다.

 

그리고 다음과 같이 try-with-resources 구문을 사용하면 된다.

try (Resource resource = new Resource()) {
    resource.xxx
    ...
}

 

직접 이 AutoClosable 인터페이스를 구현해서 try-with-resources를 사용해보자.

package exception.ex4;

import exception.ex4.exception.ConnectExceptionV4;
import exception.ex4.exception.SendExceptionV4;

public class NetworkClientV5 implements AutoCloseable {

    private final String address;
    private boolean connectError;
    private boolean sendError;

    public NetworkClientV5(String address) {
        this.address = address;
    }

    public void connect() {
        if (connectError) {
            throw new ConnectExceptionV4(address, "서버 연결 실패");
        }

        System.out.println(address + " 서버 연결 성공");
    }

    public void send(String data) {
        if (sendError) {
            // throw new RuntimeException("ex");
            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();
    }
}

어떤 클래스던 AutoClosable 인터페이스를 구현하면, 반드시 구현해야 하는 메서드 close()가 있다. 이 close() 메서드에서 원하는 작업(리소스 정리)을 해주면 된다. 

 

NetworkClientV5 클래스를 사용하는 코드는 이렇게 생겼다.

package exception.ex4;

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());
        }
    }
}

이렇게 try-with-resources 구문으로 인스턴스를 생성하고 try 구문 안에서 원하는 작업을 다 수행하면 try가 끝나는 시점에 바로 구현한 close() 메서드가 실행된다.

 

Main

package exception.ex4;

import exception.ex4.exception.SendExceptionV4;

import java.util.Scanner;

public class MainV4 {
    public static void main(String[] args) {
        NetworkServiceV4 networkService = new NetworkServiceV4();

        Scanner scanner = new Scanner(System.in);

        while (true) {
            System.out.println("전송할 문자: ");
            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();

        //필요하면 예외 별로 별도의 추가 처리 가능
        if (e instanceof SendExceptionV4 sendExceptionV4) {
            System.out.println("[전송 오류] 전송 데이터: " + sendExceptionV4.getData());
        }
    }
}

실행결과:

전송할 문자: 
hello
https://example.com 서버 연결 성공
https://example.com 서버에 데이터 전송: hello
https://example.com 서버 연결 해제

전송할 문자: 
error1
https://example.com 서버 연결 해제
사용자 메시지: 죄송합니다. 알 수 없는 문제가 발생했습니다.
==개발자용 디버깅 메시지==
exception.ex4.exception.ConnectExceptionV4: 서버 연결 실패
	at exception.ex4.NetworkClientV4.connect(NetworkClientV4.java:18)
	at exception.ex4.NetworkServiceV4.sendMessage(NetworkServiceV4.java:11)
	at exception.ex4.MainV4.main(MainV4.java:21)

 

정상 흐름이나, 예외 흐름이나 모두 서버 연결 해제가 잘 호출된다. 그리고 예외 흐름일 때 호출 시점이 중요한데 보면 catch에 걸렸을 때 catch로 가기전 close()메서드가 호출됐음을 확인할 수 있다. 이렇듯 정말 try가 끝나는 즉시 close()메서드가 호출된다. 

 

try-with-resources 장점

  • 리소스 누수 방지: 모든 리소스가 제대로 닫히도록 보장한다. 실수로 finally 블록을 적지 않거나 finally 블럭 안에서 자원 해제 코드를 누락하는 문제들을 예방한다.
  • 코드 간결성 및 가독성 향상: 명시적인 close() 메서드 호출이 필요 없어 코드가 더 간결하고 읽기 쉬워진다.
  • 스코프 범위 한정: 예를 들어 리소스로 사용되는 client 변수의 스코프가 try 블록 안으로 한정된다. 따라서 코드 유지보수가 더 쉬워진다. 
  • 조금 더 빠른 자원 해제: 기존에는 try - catch - finally 순으로 catch 이후에 자원을 반납하는데 try-with-resources 구문은 try 블록이 끝나면 바로 반납한다.
728x90
반응형
LIST