[Java] 김영한의 실전 자바 - 중급편 섹션9 예외처리1 - 이론
○ 예외 처리가 필요한 이유1 - 시작
간단히 사용자입력을 외부 서버에 전송하는 프로그램을 작성해본다.
public class NetworkClientV0 {
private final String address;
public NetworkClientV0(String address) {
this.address = address;
}
public String connect() {
//연결 성공
System.out.println(address + " 서버 연결 성공");
return "success";
}
public String send(String data){
//전송 성공
System.out.println(address + " 서버에 데이터 전송: "+data);
return "success";
}
public void disconnect() {
System.out.println(address+ " 서버 연결 해제");
}
}
public class NetworkServiceV0 {
public void sendMessage(String data) {
String address = "http://example.com";
NetworkClientV0 client = new NetworkClientV0(address);
client.connect();
client.send(data);
client.disconnect();
}
}
public class MainV0 {
public static void main(String[] args) {
NetworkServiceV0 networkService = new NetworkServiceV0();
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("프로그램을 정상 종료합니다.");
}
}
○ 예외 처리가 필요한 이유2 - 오류 상황 만들기
이어서, 오류 상황을 시뮬레이션 할 수 있는 코드를 만들어보자.
- 연결 실패: 사용자가 입력하는 문자에 "error1" 단어가 있으면 연결에 실패한다. 오류 코드는 "connectError"
- 전송 실패: 사용자가 입력하는 문자에 "error2" 단어가 있으면 데이터 전송에 실패한다. 오류 코드는 "sendError"
package exception.ex1;
public class NetworkClientV1 {
private final String address;
public boolean connectError;
public boolean sendError;
public NetworkClientV1(String address) {
this.address = address;
}
public String connect() {
if (connectError){
System.out.println(address + " 서버 연결 실패");
return "connectError";
}
//연결 성공
System.out.println(address + " 서버 연결 성공");
return "success";
}
public String send(String data){
if (sendError) {
System.out.println(address + " 서버에 데이터 전송 실패: " + data);
return "sendError";
}
//전송 성공
System.out.println(address + " 서버에 데이터 전송: "+data);
return "success";
}
public void disconnect() {
System.out.println(address+ " 서버 연결 해제");
}
// 사용자 입력 값을 기반으로 오류를 활성화
public void initError(String data){
if(data.contains("error1")){
connectError = true;
}
if(data.contains("error2")){
sendError = true;
}
}
}
○ 예외 처리가 필요한 이유3 - 반환 값으로 예외 처리
네트워크 서비스단에서는 연결이 실패해도 여전히 데이터를 전송한다. 연결이 실패하면 데이터를 전송하지 않도록 해보자.
그리고 연결이 실패해도 반드시 disconnect()를 호출해서 연결을 해제하도록 해보자.
public class NetworkServiceV1 {
public void sendMessage(String data) {
NetworkClientV1 client = new NetworkClientV1("http://example.com");
client.initError(data);
String connectResult = client.connect();
if(isError(connectResult)){
System.out.println("[네트워크 오류 발생] 오류 코드: " + connectResult);
return; // 오류가 발생한 경우 프로그램이 진행되지않도록 return으로 중지
}
String sendResult = client.send(data);
if (isError(sendResult)) {
System.out.println("[네트워크 오류 발생] 오류 코드: " + sendResult);
return;
}
client.disconnect(); // 연결에 실패해도 disconnect() 호출
}
private static boolean isError(String resultCode){
return !resultCode.equals("success");
}
}
public static void main(String[] args) { // 메인 코드 네트워크 서비스 버전 변경
NetworkServiceV1 networkService = new NetworkServiceV1();
...
}
🧐 정상 흐름과 예외흐름
아래 정상흐름은 단순하고 직관적이지만, 위의 네트워크 서비스는 정상흐름과 예외 흐름이 섞여있어 복잡하다.
client.connect(); // 정상 흐름 코드
client.send(data);
client.disconnect();
자바 예외처리를 통해 정상과 예외 흐름을 명확히 분리 할 수 있다.
○ 자바 예외처리1 - 예외 계층
🟢자바의 예외 계층
- Object : 자바에서 기본형을 제외한 모든 것은 객체다. 예외도 객체이다. 모든 객체의 최상위 부모는 Object 이므로 예외의 최상위 부모도 Object 이다.
- Throwable : 최상위 예외이다. 하위에 Exception 과 Error 가 있다.
- Error : 메모리 부족이나 심각한 시스템 오류와 같이 애플리케이션에서 복구가 불가능한 시스템 예외이다. 애플 리케이션 개발자는 이 예외를 잡으려고 해서는 안된다.
- Exception : 체크 예외
- 애플리케이션 로직에서 사용할 수 있는 실질적인 최상위 예외이다.
- Exception 과 그 하위 예외는 모두 컴파일러가 체크하는 체크 예외이다. 단 RuntimeException 은 예외로 한다.
- RuntimeException : 언체크 예외, 런타임 예외
- 컴파일러가 체크 하지 않는 언체크 예외이다.
- RuntimeException 과 그 자식 예외는 모두 언체크 예외이다.
- RuntimeException 의 이름을 따라서 RuntimeException 과 그 하위 언체크 예외를 런타임 예외라고 많이 부른다.
🟢체크 예외 vs 언체크 예외(런타임 예외)
- 체크 예외는 발생한 예외를 개발자가 명시적으로 처리해야 한다. 그렇지 않으면 컴파일 오류가 발생한다.
- 언체크 예외는 개발자가 발생한 예외를 명시적으로 처리하지 않아도 된다.
○ 자바 예외처리2 - 예외 기본 규칙
○ 자바 예외처리3 - 체크 예외
체크 예외는 잡아서 처리하거나, 또는 밖으로 던지도록 선언해야한다. 그렇지 않으면 컴파일 오류가 발생한다.
먼저 예외 코드를 작성한다.
/*
* Exception을 상속받으면 체크 예외가 된다.
* */
public class MyCheckedException extends Exception{
public MyCheckedException(String message) {
super(message);
}
}
예외도 객체이기 때문에 new 로 생성 후 예외를 발생시켜야한다.
public class Client {
// throws; 발생시킨 예외를 메서드 밖으로 던질때 사용
public void call() throws MyCheckedException{
throw new MyCheckedException("ex"); //throw; 예외 발생
}
}
/*
* Checked 예외는
* 예외를 잡아서 처리하거나,던지거나 둘중 하나를 필수로 선택해야 한다.
* */
public class Service {
Client client = new Client();
/*
* 예외를 잡아서 처리
* */
public void callCatch() {
try { //try 코드블럭에서 발생하는 예외를 잡아서 catch에 넘긴다.
client.call();
}catch (MyCheckedException e){
System.out.println("예외 처리, message= " + e.getMessage());
}
System.out.println("정상 흐름");
}
/*
* 체크 예외를 밖으로 던지는 코드
* 체크 예외는 예외를 잡지 않고 밖으로 던지려면 throws 예외를 메서드에 필수로 선언해야 한다.
* */
public void callThrow() throws MyCheckedException {
client.call();
}
}
🔶예외를 잡아서 처리 (callCatch)
public class CheckedCatchMain {
public static void main(String[] args) {
Service service = new Service();
service.callCatch();
System.out.println("정상 종료");
}
}
예외 처리, message=ex
정상 흐름
정상 종료
🔶예외를 처리 하지 않고 던지기
public class CheckedThrowMain {
public static void main(String[] args) throws MyCheckedException{
Service service = new Service();
service.callThrow();
System.out.println("정상 종료");
}
}
🟢체크 예외 장단점
- 체크 예외는 예외를 잡아서 처리X할땐, 예외를 밖으로 던지는 throws 예외 를 필수로 선언해야 한다. 그렇지
않으면 컴파일 오류가 발생한다. 이것 때문에 장점과 단점이 동시에 존재한다.- 장점: 컴파일러를 통해 문제를 잡으므로 개발자는 어떤 체크 예외가 발생하는지 쉽게 파악할 수 있다.
- 단점: 하지만 실제로는 개발자가 모든 체크 예외를 반드시 잡거나 던지도록 처리해야 하기 때문에, 너무 번거로운
일이 된다.
🟢체크 vs 언체크 예외
- 체크 예외: 예외를 잡아서 처리하지 않으면 항상 throws 키워드를 사용해서 던지는 예외를 선언해야 한다.
- 언체크 예외: 예외를 잡아서 처리하지 않아도 throws 키워드를 생략할 수 있다.
○ 자바 예외처리4 - 언체크 예외
언체크 예외는 컴파일러가 예외를 체크하지않는다. 또한 예외를 던지는 Throws를 선언하지않고 생략할수 있다.
package exception.basic.unchecked;
public class MyUncheckedException extends RuntimeException{
public MyUncheckedException(String message) {
super(message);
}
}
public class Client {
public void call() {
throw new MyUncheckedException("ex");
}
}
/**
* Unchecked 예외는
* 예외를 잡거나, 던지지 않아도 된다.
* 예외를 잡지 않으면 자동으로 밖으로 던진다.
*/
public class Service {
Client client = new Client();
/**
* 필요한 경우 예외를 잡아서 처리하면 된다.
*/
public void callCatch() {
try{
client.call();
}catch (MyUncheckedException e){
// 예외 처리 로직
System.out.println("예외 처리, message = "+e.getMessage());
}
System.out.println("정상 로직");
}
/*
* 예외를 잡지 않아도 된다. 자연스럽게 상위로 넘어간다.
* 체크 예외와 다르게 trhows 예외 선언을 하지 않아도 된다.
* */
public void callThrow() {
client.call();
}
}
위는 체크 예외와 실행결과는 동일하다. 언체크 예외도 필요한 경우 예외를 잡아서 처리할 수 있다.
하지만 언체크 예외는 체크 예외와 다르게 throws 예외를 선언하지 않아도 된다.
🟢체크 예외 장단점
- 언체크 예외는 예외를 잡아서 처리할 수 없을 때, 예외를 밖으로 던지는 throws 예외 를 생략할 수 있다.
- 장점: 신경쓰고 싶지 않은 언체크 예외를 무시할 수 있다. 체크 예외의 경우 처리할 수 없는 예외를 밖으로 던지려면 항상 throws 예외 를 선언해야 하지만, 언체크 예외는 이 부분을 생략할 수 있다.
- 단점: 언체크 예외는 개발자가 실수로 예외를 누락할 수 있다. 반면에 체크 예외는 컴파일러를 통해 예외 누락을 잡아준다.
'Java' 카테고리의 다른 글
[Java] 김영한의 실전 자바 - 중급편2 섹션1 제네릭 - Generic1,Generic2 (0) | 2024.06.07 |
---|---|
[Java] 김영한의 실전 자바 - 중급편 섹션10 예외처리2 - 실습 (0) | 2024.05.23 |
[Java] 김영한의 실전 자바 - 중급편 섹션8 중첩 클래스,내부 클래스2 (0) | 2024.05.20 |
[Java] 김영한의 실전 자바 - 중급편 섹션5 열거형 - ENUM (0) | 2024.05.09 |
[Java] 김영한의 실전 자바 - 중급편 섹션3,4 String ,래퍼,Class 클래스 (0) | 2024.05.09 |