2025.01.03 - [Java공부(코딩)] - 코딩초보의 자바(Java)공부 21일차 { 예외처리 }
코딩초보의 자바(Java)공부 21일차 { 예외처리 }
오늘은 자바의 예외처리에대해서다뤄보겠슴다!!예외 계층자바의 예외 처리는 다음 키워드를 사용한다.`try` , `catch` , `finally` , `throw` , `throws` 예외를 다루기 위한예외처리용 객체를 제공`Object` :
ddkk1120.tistory.com
위에 글을 먼저 보시고 학습해봅시다 !!! 이어지는 내용이에요
참고 : 김영한의 자바
예외처리 도입 - 시작
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()` 를 호출한다.
'Java공부(코딩)' 카테고리의 다른 글
코딩초보의 자바(Java)공부 24일차 { 제네릭 - Generic 2} (0) | 2025.01.08 |
---|---|
코딩초보의 자바(Java)공부 23일차 { 제네릭 - Generic1 } (1) | 2025.01.06 |
코딩초보의 자바(Java)공부 21일차 { 예외처리 } (1) | 2025.01.03 |
코딩초보의 자바(Java)공부 20일차 { 중첩클래스 , 내부클래스 2 } (0) | 2025.01.02 |
코딩초보의 자바(Java)공부 20일차 { 중첩 클래스 , 내부클래스 1} (2) | 2025.01.02 |