Java공부(코딩)

코딩초보의 자바(Java)공부 22일차 { 예외처리 실습 }

동곤일상 2025. 1. 4. 15:39
반응형

2025.01.03 - [Java공부(코딩)] - 코딩초보의 자바(Java)공부 21일차 { 예외처리 }

 

코딩초보의 자바(Java)공부 21일차 { 예외처리 }

오늘은 자바의 예외처리에대해서다뤄보겠슴다!!예외 계층자바의 예외 처리는 다음 키워드를 사용한다.`try` , `catch` , `finally` , `throw` , `throws` 예외를 다루기 위한예외처리용 객체를 제공`Object` :

ddkk1120.tistory.com

 

위에 글을 먼저 보시고 학습해봅시다 !!! 이어지는 내용이에요

참고 : 김영한의 자바

https://inf.run/2dUwc


예외처리 도입 - 시작

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

}

오류 코드

이전에는 오류 코드(`errorCode` ) 반환 값으로 리턴해서, 어떤 오류가 발생했는지 구분했다.

여기서는 어떤 종류의 오류가 발생했는지 구분하기 위해 예외 안에 오류 코드를 보관한다.

 

오류 메시지

오류 메시지(`message` )에는 어떤 오류가 발생했는지 개발자가 보고 이해할 있는 설명을 담아둔다.

오류 메시지는 상위 클래스인 `Throwable` 에서 기본으로 제공하는 기능을 사용한다.

package exception.ex;

public class NetworkClientV1 {
	private final String address;
	private boolean connectError;
	private boolean sendError;
	
	public NetworkClientV1(String address) {
		this.address = address;
	}
	public void connect() throws NetworkExceptionV1{
		if(connectError) {
			throw new NetworkExceptionV1("connectError : "+address, "서버에 연결 실패 : ");
		}System.out.println("연결 성공");
	}
	public void send(String data)throws NetworkExceptionV1 {
		if(sendError) {
			throw new NetworkExceptionV1("sendError : "+address, "데이터 전송 실패 : "+data);
		}
		System.out.println(address+"데이터 전송 성공 : "+data);
	}
	public void disconnect() {
		System.out.println(address+" 서버연결해제");
	}
	public void initError(String error) {
		if(error.contains("error1")) {
			connectError = true;
		}
		else if(error.contains("error2")) { //error2를 포함하는가?
			sendError = true;
		}
	}
}

기존의 코드와 대부분 같지만, 오류가 발생했을 오류 코드를 반환하는 것이 아니라 예외를 던진다.

따라서 반환 값을 사용하지 않아도 된다. 여기서는 반환 값을 `void` 처리한다.

 

이전에는 반환 값으로 성공, 실패 여부를 확인해야 했지만, 예외 처리 덕분에 메서드가 정상 종료되면 성공이고,

예외가 던져지면 예외를 통해 실패를 확인할 있다.

 

오류가 발생하면, 예외 객체를 만들고 거기에 오류 코드와 오류 메시지를 담아둔다. 그리고 만든 예외 객체를

`throw` 통해 던진다.

package exception.ex;

public class NetworkServiceV1 {
	public void sendMessage(String data) throws NetworkExceptionV1{
		String address = "http://.donggon.com";
		
		NetworkClientV1  client = new NetworkClientV1(address);
		client.initError(data); //오류검사
		
		client.connect();
		client.send(data);
		client.disconnect();
	}
}

여기서는 예외를 별도로 처리하지 않고, `throws` 통해 밖으로 던진다.

 

public class NetworkMainV1 {

public static void main(String[] args)throws Exception{

NetworkServiceV1 networkServiceV1 = new NetworkServiceV1();

Scanner scan = new Scanner(System.in);

 

while(true) {

System.out.println("전송한 문자 : ");

String input = scan.nextLine();

if(input.equals("exit")) {

break;

}

networkServiceV1.sendMessage(input);

System.out.println();

}

System.out.println("종료e");

}}

전송한 문자 :

hahahahljjfj

연결 성공

http://.donggon.com데이터 전송 성공 : hahahahljjfj

http://.donggon.com 서버연결해제

 

전송한 문자 :

error1kkkk

Exception in thread "main" exception.ex.NetworkExceptionV1: 서버에 연결 실패 :

at lang/exception.ex.NetworkClientV1.connect(NetworkClientV1.java:13)

at lang/exception.ex.NetworkServiceV1.sendMessage(NetworkServiceV1.java:10)

at lang/exception.ex.NetworkMainV1.main(NetworkMainV1.java:16)

`error1` 이면 연결 실패가 발생한다.

모든 곳에서 발생한 예외를 잡지 않았기 때문에 결과적으로 `main()` 밖으로 예외가 던져진다.

`main()` 밖으로 예외가 던져지면 예외 메시지와 예외를 추적할 있는 스택 트레이스를 출력하고 프로그램을

종료한다.

전송한 문자 :

error2kkgha

연결 성공

Exception in thread "main" exception.ex.NetworkExceptionV1: 데이터 전송 실패 : error2kkgha

at lang/exception.ex.NetworkClientV1.send(NetworkClientV1.java:18)

at lang/exception.ex.NetworkServiceV1.sendMessage(NetworkServiceV1.java:11)

at lang/exception.ex.NetworkMainV1.main(NetworkMainV1.java:16)

error2가 포함된 문자열이 입력되면

모든 곳에서 발생한 예외를 잡지 않았기 때문에 결과적으로 `main()` 밖으로 예외가 던져진다.

`main()` 밖으로 예외가 던져지면 예외 메시지와 예외를 추적할 있는 스택 트레이스를 출력하고 프로그램을

종료한다.

 

**남은 문제**

예외 처리를 도입했지만, 아직 예외가 복구되지 않는다. 따라서 예외가 발생하면 발생하면 프로그램이 종료된다.

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


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

public class NetworkServiceV1_2 {
	public void sendMessage(String data) throws NetworkExceptionV1{
		String address = "http://.donggon.com";
		
		NetworkClientV1  client = new NetworkClientV1(address);
		client.initError(data); //오류검사
		
		try {
			client.connect();
			
		} catch (NetworkExceptionV1 e) {
			System.out.println("[오류]코드 : "+e.getErrorCode()+" 오류메시지 : "+e.getMessage());
			return;
		}
		try {
			client.send(data);
			
		} catch (NetworkExceptionV1 e) {
			System.out.println("[오류]코드 : "+e.getErrorCode()+" 오류메시지 : "+e.getMessage());
			return;
		}
		client.disconnect();
	}
}
전송한 문자 : 
error1
[오류]코드 : connectError   오류메시지 : http://.donggon.com서버에 연결 실패  

전송한 문자 : 
error2
http://.donggon.com 연결 성공
[오류]코드 : sendError   오류메시지 : http://.donggon.com 데이터 전송 실패 : error2

전송한 문자 : 
han
http://.donggon.com 연결 성공
http://.donggon.com데이터 전송 성공 : han
http://.donggon.com 서버연결해제

전송한 문자 : 
exit
종료e

**해결된 문제**

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

**남은 문제**

예외 처리를 했지만 정상 흐름과 예외 흐름이 섞여 있어서 코드를 읽기 어렵다.

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


정상-예외 흐름 분리

위에 코드는 정상과 예외가 섞여있어

코드읽기가 불편했었다

한번에 합쳐버리자!!

public class NetworkServiceV1_3 {
	public void sendMessage(String data) throws NetworkExceptionV1{
		String address = "http://.donggon.com";
		
		NetworkClientV1  client = new NetworkClientV1(address);
		client.initError(data); //오류검사
		try {
			client.connect();
			client.send(data);
			client.disconnect();
			
		} catch (NetworkExceptionV1 e) {
			System.out.println("[오류]코드 : "+e.getErrorCode()+" 오류메시지 : "+e.getMessage());
		
		}
	}
}

**해결된 문제**

자바의 예외 처리 메커니즘과 `try` , `catch` 구조 덕분에 정상 흐름은 `try` 블럭에 모아서 처리하고, 예외 흐름

`catch` 블럭에 별도로 모아서 처리할 있었다.

덕분에 정상 흐름과 예외 흐름을 명확하게 분리해서 코드를 쉽게 읽을 있게 되었다.

 

**남은 문제**

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

try {

client.connect();

client.send(data);

 

 

} catch (NetworkExceptionV1 e) {

System.out.println("[오류]코드 : "+e.getErrorCode()+" 오류메시지 : "+e.getMessage());

 

}finally {

client.disconnect();

}

 

이와같이 finally 키워드를 추가하여

disconnect를 넣어준다면

전송한 문자 :

ee

http://.donggon.com 연결 성공

http://.donggon.com데이터 전송 성공 : ee

http://.donggon.com 서버연결해제

 

전송한 문자 :

error1

[오류]코드 : connectError 오류메시지 : http://.donggon.com서버에 연결 실패

http://.donggon.com 서버연결해제

 

문자를 전송 할때마다

항상 서버연결이 해제된다.!!

(중간에 다른 예외 RunTimeException이 터져도!!)

 

 

한번 중간에 다른 예외를 넣어보자

public void send(String data)throws NetworkExceptionV1 {

if(sendError) {

// throw new NetworkExceptionV1("sendError ",address+ " 서버에 데이터 전송 실패 : "+data);

throw new RuntimeException();

}

System.out.println(address+"데이터 전송 성공 : "+data);

}

전송한 문자 :

error1

[오류]코드 : connectError 오류메시지 : http://.donggon.com서버에 연결 실패

http://.donggon.com 서버연결해제

 

전송한 문자 :

error2

http://.donggon.com 연결 성공

http://.donggon.com 서버연결해제

Exception in thread "main" java.lang.RuntimeException

at lang/exception.ex.NetworkClientV1.send(NetworkClientV1.java:21)

at lang/exception.ex.NetworkServiceV1_3.sendMessage(NetworkServiceV1_3.java:11)

at lang/exception.ex.NetworkMainV1_3.main(NetworkMainV1_3.java:16)

 

catch` 에서 잡을 없는 예외가 발생해서,

예외를 밖으로 던지는 경우에도 `finally` 먼저 호출하고 나서 예외를 밖으로 던진다.

 


예외계층

 

`NetworkClientExceptionV3` : `NetworkClient` 에서 발생하는 모든 예외는 예외의 자식이다.

 

`ConnectExceptionV3` : 연결 실패시 발생하는 예외이다. 내부에 연결을 시도한 `address` 보관한다

.

`SendExceptionV3` : 전송 실패시 발생하는 예외이다. 내부에 전송을 시도한 데이터인 `sendData` 보관한

.


public class NetworkExceptionV3 extends Exception{
	public NetworkExceptionV3(String message) {
		super(message);
//NetworkClient` 에서 발생하는 모든 예외는 이 예외를 부모로 하도록 설계한다.
	}

}
public class ConnectException  extends NetworkExceptionV3{
	//연결(connect) 실패시 발생하는 예외
	private String address;

	public ConnectException(String message,String address) {
		super(message);
		this.address = address;
	}
	public String getAddress() {
		return address;
	}
}
public class SendExceptionV3 extends NetworkExceptionV3{
	//전송 실패시 발생하는 예외
	
	private String date;

	public SendExceptionV3(String message,String data) {
		super(message);
		this.date = data;
	}
	
	public String getDate() {
		return date;
	}

}

 

public class NetworkClientV3 {
	private final String address;
	private boolean connectError;
	private boolean sendError;

	public NetworkClientV3(String address) {
		this.address = address;
	}
	public void connect() throws ConnectExceptionV3{
		if(connectError) {
			throw new ConnectExceptionV3("connectError  ",address+ "서버에 연결 실패  ");
		}System.out.println(address+" 연결 성공");
	}


	public void send(String data)throws SendExceptionV3 {
		if(sendError) {
			throw new SendExceptionV3("sendError  ",address+ " 서버에 데이터 전송 실패 : "+data);
			//			throw new RuntimeException();
		}
		System.out.println(address+"데이터 전송 성공 : "+data);
	}


	public void disconnect() {
		System.out.println(address+" 서버연결해제");
	}
	public void initError(String error) {
		if(error.contains("error1")) {
			connectError = true;
		}
		else if(error.contains("error2")) { //error2를 포함하는가?
			sendError = true;
		}
	}


}

연결 관련 오류 발생하면 `ConnectExceptionV3` 던지고, 전송 관련 오류가 발생하면

`SendExceptionV3` 던진다.

 

public class NetworkServiceV3 {

public void sendMessage(String data) throws NetworkExceptionV3{

String address = "http://.donggon.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 a) {

System.out.println("전송[오류] 전송데이터 : "+a.getDate()+"오류 메시지 : "+a.getMessage());

}finally {

client.disconnect();

}

}

}

 

예외 클래스를 각각의 예외 상황에 맞추어 만들면, 필요에 맞는 예외를 잡아서 처리할 있다.

예를 들면 `e.getAddress()` , `e.getSendData()` 같이 각각의 예외 클래스가 가지는 고유의 기능을 활용

있다.

public class NetworkMainV1_3 {

public static void main(String[] args)throws Exception{

NetworkServiceV3 networkServiceV3 = new NetworkServiceV3();

Scanner scan = new Scanner(System.in);

 

while(true) {

System.out.println("전송한 문자 : ");

String input = scan.nextLine();

if(input.equals("exit")) {

break;

}

networkServiceV3.sendMessage(input);

System.out.println();

}

System.out.println("종료e");

}}

 

출력

전송한 문자 :

err

http://.donggon.com 연결 성공

http://.donggon.com데이터 전송 성공 : err

http://.donggon.com 서버연결해제

 

전송한 문자 :

error1

연결[오류] 주소 : http://.donggon.com서버에 연결 실패 오류 메시지 : connectError

http://.donggon.com 서버연결해제

 

전송한 문자 :

error2

http://.donggon.com 연결 성공

전송[오류] 전송데이터 : http://.donggon.com 서버에 데이터 전송 실패 : error2오류 메시지 : sendError

http://.donggon.com 서버연결해제

 


예외계층 -활용-

연결 오류는 중요하다.

`ConnectExceptionV3` 발생하면 다음과 같이 메시지를 명확하게 남기도록 하자.

) `[연결 오류] 주소: ...`

 

`NetworkClientV3` 사용하면서 발생하는 나머지 예외(`NetworkClientExceptionV3` 자식) 단순

하게 다음과 같이 출력하자

) `[네트워크 오류] 메시지:`

 

외에 예외가 발생하면 다음과 같이 출력하자.

) `[ 없는 오류] 메시지:`

 

try {

client.connect();

client.send(data);

 

 

} catch (ConnectExceptionV3 e) {

System.out.println("연결[오류] 주소 : "+e.getAddress()+" 오류 메시지 : "+e.getMessage());

}

catch (NetworkExceptionV3 a) {

System.out.println("네트워크[오류] : "+a.getMessage());

}catch(Exception a) {

System.out.println("알수없는[오류]"+a.getMessage());

}

finally {

client.disconnect();

}

 

이와같이 try catch문을 바꿈

 

ConnectExceptionV3 발생**
```java
try {
// 1. ConnectExceptionV3 발생
} catch (ConnectExceptionV3 e) { // 2. 잡아서 처리
} catch (NetworkClientExceptionV3 e) {
} catch (Exception e) {
}

 

SendExceptionV3 발생**
```java
try {
// 1. SendExceptionV3 발생
} catch (ConnectExceptionV3 e) { // 2. 대상이 다름
} catch (NetworkClientExceptionV3 e) { // 3.NetworkClientExceptionV3은 부모이므로 여
기서 잡음
} catch (Exception e) {
}
```
`NetworkClientExceptionV3` 은 `SendExceptionV3` 의 부모이다. 부모 타입은 자식을 담을 수 있다. 따라
서 `NetworkClientExceptionV3` 을 잡으면 `SendExceptionV3`
도 잡을 수 있다.

 

**RuntimeException 발생**
```java
try {
// 1. RuntimeException 발생
} catch (ConnectExceptionV3 e) { // 2. 대상이 다름
} catch (NetworkClientExceptionV3 e) { // 3.대상이 다름
} catch (Exception e) { // 4.Exception은 RuntimeException의 부모이므로 여기서 잡음
}

모든 예외를 잡아서 처리하려면 마지막에 `Exception` 두면 된다.

 

주의할 점은 예외가 발생했을 때 `catch` 를 순서대로 실행하므로, 더 디테일한 자식을 먼저 잡아야 한다.

 

catch (ConnectExceptionV3| SendExceptionV3 e) {

System.out.println("연결 또는 전송[오류] 오류 메시지 : "+e.getMessage());

}

이와같이 여러 예외를 한번에 잡는 방법도 존재함!!

 


실무에서 체크예외 사용에 대한 문제점

 

1)처리할 없는 예외**: 예외를 잡아서 복구할 있는 예외보다 복구할 없는 예외가 많다.

2)체크 예외의 부담**: 처리할 없는 예외는 밖으로 던져야 한다. 체크 예외이므로 `throws` 던질 대상을 일일이

명시해야 한다.

 

사실 `Service` 개발하는 개발자 입장에서 많은 라이브러리에서 쏟아지는 모든 예외를 다루고 싶지는 않을

이다.

특히 본인이 해결할 없는 모든 예외를 다루고 싶지는 않을 것이다.

본인이 해결할 있는 예외만 잡아서처리하고, 본인이 해결할 없는 예외는 신경쓰지 않는 것이 나은 선택일 있다.


언체크예외  사용 시나리오

이번에는 `Service` 에서 호출하는 클래스들이 언체크(런타임) 예외를 전달한다고 가정해보자.

`NetworkException` , `DatabaseException` 잡아도 복구할 없다. 언체크 예외이므로 이런 경우 무시

하면 된다.

 

언체크 예외를 던지는 예시**

class Service {

void sendMessage(String data) {

...

}

}

```

언체크 예외이므로 `throws` 선언하지 않아도 된다.

사용하는 라이브러리가 늘어나서 언체크 예외가 늘어도 본인이 필요한 예외만 잡으면 되고, `throws` 늘리지

않아도 된다.


실무예외처리방안 - 구현-

`NetworkClientExceptionV4` 언체크 예외인 `RuntimeException` 상속 받는다.

이제 `NetworkClientExceptionV4` 자식은 모두 언체크(런타임) 예외가 된다.

 

구현을 코드로 보자!

public class NetworkExceptionV4 extends RuntimeException{
	public NetworkExceptionV4(String message) {
		super(message);
//NetworkClient` 에서 발생하는 모든 예외는 언체크 예외로 설정
	}

}
public class ConnectExceptionV4  extends NetworkExceptionV4{
	//연결(connect) 실패시 발생하는 예외
	private String address;

	public ConnectExceptionV4(String message,String address) {
		super(message);
		this.address = address;
	}
	public String getAddress() {
		return address;
	}
}
public class SendExceptionV4 extends NetworkExceptionV4{
	//전송 실패시 발생하는 예외
	
	private String date;

	public SendExceptionV4(String message,String data) {
		super(message);
		this.date = data;
	}
	
	public String getDate() {
		return date;
	}

}
public class NetworkClientV4 {
	private final String address;
	private boolean connectError;
	private boolean sendError;
	private boolean testError;

	public NetworkClientV4(String address) {
		this.address = address;
	}
	public void connect(){
		if(connectError) {
			throw new ConnectExceptionV4("connectError  ", address+ "서버에 연결 실패  ");
		}System.out.println(address+" 연결 성공");
	}


	public void send(String data) {
		if(sendError) {
			throw new SendExceptionV4("sendError  ",address+ " 서버에 데이터 전송 실패 : "+data);
			//			throw new RuntimeException();
		}
		
		System.out.println(address+"데이터 전송 성공 : "+data);
	}
	public void test() throws RuntimeException{
		if(testError) {
			throw new RuntimeException(address+" 서버 버그발생");
		}
		System.out.println("RunTime 테스트정상");
	}


	public void disconnect() {
		System.out.println(address+" 서버연결해제");
	}
	public void initError(String error) {
		if(error.contains("error1")) {
			connectError = true;
		}
		else if(error.contains("error2")) { //error2를 포함하는가?
			sendError = true;
		}
		else if(error.contains("error3")) {
			testError = true;
		}
	}


}

언체크 예외이므로 `throws` 사용하지 않는다.

 

public class NetworkServiceV4 {
	public void sendMessage(String data){
		String address = "http://.donggon.com";

		NetworkClientV4 client = new NetworkClientV4(address);
		client.initError(data); //오류검사
		try {
			client.connect();
			client.send(data);
			client.test();
			}
		finally {
			client.disconnect();
		}

	}}
public class NetworkMainV4 {
	public static void main(String[] args){
		NetworkServiceV4 networkServiceV4 = new NetworkServiceV4();
		Scanner scan = new Scanner(System.in);
	

		while(true) {
			System.out.println("전송한 문자 : ");
			String input = scan.nextLine();
			if(input.equals("exit")) {
				System.out.println(" 종료 ");
				break;
			}
			try {
			networkServiceV4.sendMessage(input);}
			catch(Exception e) {
				exceptionHandler(e);
			}
			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.getDate());
	}
	}

}

출력

전송한 문자 :

error1

http://.donggon.com 서버연결해제

사용자 메시지: 죄송합니다. 알 수 없는 문제가 발생했습니다.

==개발자용 디버깅 메시지==

exception.ex4.ConnectExceptionV4: connectError

at lang/exception.ex4.NetworkClientV4.connect(NetworkClientV4.java:15)

at lang/exception.ex4.NetworkServiceV4.sendMessage(NetworkServiceV4.java:10)

at lang/exception.ex4.NetworkMainV4.main(NetworkMainV4.java:20)

 

공통예외 처리

try {
networkService.sendMessage(input);
} catch (Exception e) { // 모든 예외를 잡아서 처리
exceptionHandler(e);
}

`Exception` 잡아서 지금까지 해결하지 못한 모든 예외를 여기서 공통으로 처리한다. `Exception` 잡으면

필요한 모든 예외를 잡을 있다.

예외도 객체이므로 공통 처리 메서드인 `exceptionHandler(e)` 예외 객체를 전달한다

 

 

**e.printStackTrace()**

예외 메시지와 스택 트레이스를 출력할 있다.

기능을 사용하면 예외가 발생한 지점을 역으로 추적할 있다.

 


try-with-resources

try` 에서 외부 자원을 사용하고, `try` 끝나면 외부 자원을 반납하는 패턴이 반복되면서 자바에서는 Try with

resources라는 편의 기능을 자바 7에서 도입했다. 이름 그대로 `try` 에서 자원을 함께 사용한다는 뜻이다. 여기서 자원

`try` 끝나면 반드시 종료해서 반납해야 하는 외부 자원을 뜻한다

 

이 기능을 사용하려면

 `AutoCloseable` 인터페이스를 구현

 

package lang;

public interface AutoCloseable {
	void close() throws Exception;

}

인터페이스를 구현하면 Try with resources 사용할 `try` 끝나는 시점에 `close()` 자동으로 호출된다.

 

public class NetworkClientV5  implements AutoCloseable{
	private final String address;
	private boolean connectError;
	private boolean sendError;
	private boolean testError;

	public NetworkClientV5(String address) {
		this.address = address;
	}
	public void connect(){
		if(connectError) {
			throw new ConnectExceptionV4("connectError  ", address+ "서버에 연결 실패  ");
		}System.out.println(address+" 연결 성공");
	}


	public void send(String data) {
		if(sendError) {
			throw new SendExceptionV4("sendError  ","  데이터 전송 실패 : "+data);
		}
		
		System.out.println(address+"데이터 전송 성공 : "+data);
	}
	public void test() throws RuntimeException{
		if(testError) {
			throw new RuntimeException(address);
		}
		System.out.println("RunTime 테스트정상");
	}


	public void disconnect() {
		System.out.println(address+" 서버연결해제");
	}
	public void initError(String error) {
		if(error.contains("error1")) {
			connectError = true;
		}
		else if(error.contains("error2")) { //error2를 포함하는가?
			sendError = true;
		}
		else if(error.contains("error3")) {
			testError = true;
		}
	}
	@Override
	public void close() throws Exception {
		System.out.println("NetworkClientV5.close");
		disconnect();
		
	}


}

implements AutoCloseable` 통해 `AutoCloseable` 구현한다.

**close()**: `AutoCloseable` 인터페이스가 제공하는 메서드는 `try` 끝나면 자동으로 호출된다. 종료 시점

자원을 반납하는 방법을 여기에 정의하면 된다. 참고로 메서드에서 예외를 던지지는 않으므로 인터페이스의

메서드에 있는 `throws Exception` 제거했다.

 

public class NetworkServiceV5 {

public void sendMessage(String data) throws Exception{

String address = "http://.donggon.com";

 

try(NetworkClientV5 client = new NetworkClientV5(address)) {

client.initError(data); //오류검사

client.connect();

client.send(data);

client.test();

}

catch(Exception e) {

System.out.println("예외확인 : "+e.getMessage());

throw e;

}}}

Try with resources 구문은 `try` 괄호 안에 사용할 자원을 명시한다.

자원은 `try` 블럭이 끝나면 자동으로 `AutoCloseable.close()` 호출해서 자원을 해제한다.

참고로 여기서 `catch` 블럭 없이 `try` 블럭만 있어도 `close()` 호출된다.

여기서 `catch` 블럭은 단순히 발생한 예외를 잡아서 예외 메시지를 출력하고, 잡은 예외를 `throw` 사용해서

다시 밖으로 던진다.

 

public class NetworkMainV5 {

public static void main(String[] args){

NetworkServiceV5 networkServiceV5 = new NetworkServiceV5();

Scanner scan = new Scanner(System.in);

 

 

while(true) {

System.out.println("전송한 문자 : ");

String input = scan.nextLine();

if(input.equals("exit")) {

System.out.println(" 종료 ");

break;

}

try {

networkServiceV5.sendMessage(input);}

catch(Exception e) {

exceptionHandler(e);

}

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.getDate());

}

}

 

}

 

 

출력

전송한 문자 :

ㄷㄱ객

http://.donggon.com 연결 성공

http://.donggon.com데이터 전송 성공 : ㄷㄱ객

RunTime 테스트정상

NetworkClientV5.close

http://.donggon.com 서버연결해제

 

전송한 문자 :

error1

NetworkClientV5.close

http://.donggon.com 서버연결해제

예외확인 : connectError

사용자 메시지: 죄송합니다. 알 수 없는 문제가 발생했습니다.

==개발자용 디버깅 메시지==

exception.ex4.ConnectExceptionV4: connectError

at lang/exception.ex4.NetworkClientV5.connect(NetworkClientV5.java:15)

at lang/exception.ex4.NetworkServiceV5.sendMessage(NetworkServiceV5.java:9)

at lang/exception.ex4.NetworkMainV5.main(NetworkMainV5.java:20)

 

전송한 문자 :

 

**Try with resources 장점**

**리소스 누수 방지**: 모든 리소스가 제대로 닫히도록 보장한다. 실수로 `finally` 블록을 적지 않거나, `finally`

블럭 안에서 자원 해제 코드를 누락하는 문제들을 예방할 있다.

 

코드 간결성 가독성 향상**: 명시적인 `close()` 호출이 필요 없어 코드가 간결하고 읽기 쉬워진다.

 

 

스코프 범위 한정**: 예를 들어 리소스로 사용되는 `client` 변수의 스코프가 `try` 블럭 안으로 한정된다. 따라서

코드 유지보수가 쉬워진다.

 

**조금 빠른 자원 해제**: 기존에는 try ->catch ->finally로 catch 이후에 자원을 반납했다. 

Try with resources구분은 `try` 블럭이 끝나면 즉시 `close()` 호출한다.