오늘은 사고에 대처하는 프로의 자세 "예외 처리(Exception Handling)"를 배웠다.
// iveMembers.size()는 6
String member = iveMembers.get(10);
아이돌 멤버는 6명인데 10번째 정보를 달라고 하면
IndexOutOfBoundsException이 발생하며 프로그램이 즉시 종료됨
뒤에 중요한 코드가 있어도 실행되지 않음
이때 필요한 것이 바로 try-catch 블록
시도해보고(try), 문제 생기면 잡아라(catch)
ry {
// 1. 위험한 행동 시도
String member = iveMembers.get(10);
} catch (IndexOutOfBoundsException e) {
// 2. 예외가 발생하면 실행되는 구조 로직
System.out.println("🚨 그런 멤버는 없습니다!");
}
String leader = null;
System.out.println(leader.length()); // 펑! 💥
존재하지 않는(Null) 객체에게 일을 시키면 NullPointerException이 발생함. 이것 또한 try-catch로 잡아서 대처할 수 있음
try {
System.out.println(leader.length());
} catch (NullPointerException e) {
System.out.println("🚨 리더가 없네요. 임시 리더를 뽑습니다.");
leader = "안유진"; // 복구(Recovery)
}
요약
- Unchecked Exception: IndexOutOfBounds, NullPointer처럼 실행 중에 뜬금없이 터지는 에러들
- Handling: 에러를 잡아서 로그를 남기거나, 다른 값으로 대체하는 등 수습을 하는 과정
// ❌ 컴파일 에러 발생!
Thread.sleep(1000);
Unhandled exception: java.lang.InterruptedException"
(해석: 야, 자다가 누가 깨우면 어떡할 거야? 대책 세워놨어?)
자바는 예측 가능한 위협에 대해서는 반드시 안전장치(try-catch) 를 마련하라고 강요
try {
for (int i = 3; i > 0; i--) {
System.out.println(i + "초...");
Thread.sleep(1000); // 1초간 잠듬
}
System.out.println("🎉 컴백!");
} catch (InterruptedException e) {
// 혹시라도 자다가 누가 깨웠을 때(Interrupt) 실행될 코드
System.out.println("😴 으악! 누가 깨웠어!");
}
이처럼 Exception 클래스를 직접 상속받는 예외들은 모두 Checked Exception
요약
- Unchecked Exception: 실수하면 터지니까 조심해.(자율)
- Checked Exception: 위험하니까 무조건 처리해.(강제)
프로그램이 치명적인 상황(파일 입출력 오류, 네트워크 끊김 등)에서도 살아남을 수 있게 해주는 최소한의 안전장치
직접 에러를 만들어 던지는(throw)방법
public boolean buyTicket(int money) {
if (money < 10000) {
return false; // "실패했어 (소곤소곤)"
}
// ... 예매 로직 ...
return true;
}
public void buyTicket(int money) {
if (money < 10000) {
throw new RuntimeException("돈 없어! 나가!"); // "실패!!! (와장창)"
}
// ... 예매 로직 ...
}
에러를 던진다는 것은 비상벨을 울리는 것과 같음
호출하는 쪽에서 귀를 막고 싶어도 막을 수가 없음. 프로그램이 멈춰버리기 때문에, 개발자는 강제적으로 이 상황을 처리(catch) 해야만 함
결론
throw는 단순히 에러를 내는 게 아니라, 이대로 진행하면 더 큰 사단이 나니까 여기서 멈춰! 라고 외치는 방어 수단
1. Return: 실패했어. 보고 들을지는 너 맘이야(수동적)
2. Throw: 실패했어! 당장 처리해!(능동적/강제적)
// extends RuntimeException: 실행 중에 발생하는 상태 문제
public class NotEnoughFanPowerException extends RuntimeException { ... }
// extends Exception: 꼭 처리해줘야 하는 중요한 문제
public class IdolNotFoundException extends Exception { ... }
try {
buyTicket("BlackPink", 50000);
} catch (IdolNotFoundException e) {
// 검색 오류 -> 다시 검색 유도
System.out.println("아이돌 이름을 다시 확인해주세요!");
} catch (NotEnoughFanPowerException e) {
// 결제 오류 -> 충전 유도
System.out.println("팬심을 충전하고 다시 오세요!");
} catch (Exception e) {
// 그 외 알 수 없는 오류 -> 고객센터 연결
System.out.println("고객센터에 문의하세요.");
}
만약 모든 에러를 Exception e 하나로 퉁쳤다면?
고객에게 뭔가 잘못됐어요 라는 말밖에 해주지 못함.
// "아이돌을 찾아 줄 건데, 없을 수도 있어(Optional)!"
public Optional<Idol> findByNameOptional(String name) {
return Optional.ofNullable(db.get(name));
}
이렇게 Optional 이라는 상자에 포장해서 주면
사용하는 쪽에서는 강제적으로 상자를 열어봐야함
그냥 idol.getName()을 할 수 없게 됨 -> 아주 안전
// 상자에 값이 있으면 꺼내서 인사 시키기
optionalIdol.ifPresent(idol -> System.out.println(idol.getGreeting()));
// 상자가 비어있으면 '연습생' 객체를 대신 씁니다.
Idol idol = optionalIdol.orElse(new Idol("연습생", "무소속"));
// 반드시 있어야 하는 값이라면, 없으면 에러를 던지게 할 수도 있습니다.
Idol idol = optionalIdol.orElseThrow(
() -> new RuntimeException("누구세요?")
);
// 아이돌 객체 -> 팀 이름(String)으로 변환 (없으면 "팀 없음")
String teamName = optionalIdol
.map(Idol::getTeam)
.orElse("팀 없음");
상자에서 굳이 꺼내지(get) 않고도 내용을 바꿀 수 있음
Optional<List<T>> 하지 말고, 그냥 빈 리스트(Collections.emptyList())를 주는 게 훨씬 깔끔메서드에서 Null을 리턴하는 습관을 버리기!
대신 비어있을 수 있음을 뜻하는 Optional 리턴하기
4일동안 객체지향, 람다, 스트림, 예외처리에 대해서 배웠다.
튜터님의 설명이 귀에 쏙쏙 박혀서 정말 유익한 시간이었다.