| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
- 이스케이프 문자
- coding test
- list 컬렉션
- ㅅ
- JSON
- datastructure
- 혼자 공부하는 C언어
- 윤성우의 열혈 자료구조
- R
- 메모리구조
- insertion sort
- C 언어 코딩 도장
- Selection Sorting
- 알기쉬운 알고리즘
- 윤성우 열혈자료구조
- Graph
- 이것이 자바다
- buffer
- C programming
- stream
- Serialization
- s
- Algorithm
- Stack
- Today
- Total
Engineering Note
[Java] Optional()에서 반환타입에서 .get()이 안좋은 이유와 예외처리를 하는 이유 본문
[Java] Optional()에서 반환타입에서 .get()이 안좋은 이유와 예외처리를 하는 이유
Software Engineer Kim 2026. 1. 9. 18:17
프로그램이 완벽하게 동작하면 좋다. 개발자가 사용자의 행동을 예측해서 정상동작하는 구조로 프로그램을 만들면 예외처리를 할 필요는 없지만, 현재의 프로그램은 현재의 상황에서 완벽하게 동작하는 프로그램일 가능성이 높다.
예외처리는 그래서 필요하다.
몇 년동안 개발자를 하면서 예외처리를 하는 이유를 기계적으로 암기하고 있었다. 오늘 A사 기업 과제를 하면서 리팩토링과 코드리뷰를 통해 예외처리가 중요한 이유를 배웠다.
코드 리팩토링 한 과정
단위 테스트를 작성하면서 Optional의 데이터의 .get()을 많이 활용했다. 우선 값이 잘 저장되었는지 확인하기 위해서 사용했는데 별로 좋은 코드가 아니었다.
Optional은 null을 반환할 수 있는 환경의 문제를 해결하기 위해 Java 8부터 생긴 특수한 타입이다. 그런데 여기서 .get()은 반드시 값이 있다고 가정하고, 행동하는 코드이다. 현재의 상황에서는 값이 반드시 있다는 걸 알고 있기 때문에 .get()을 사용할 수 있지만, 소프트웨어의 생명주기 동안 항상 같은 맥락을 유지하는 SW는 흔치 않다. 아니 거의 없다.
여러 객체가 협렵하는 구조의 소프트웨어에서 내가 작성한 코드가 인수인계라는 이벤트가 생길 수도 있고, 사용자의 의해서 DB의 값이 변경되어 현재의 코드가 null을 반환하는 코드로 변경될 수도 있다. 이러한 상황을 방지하기 위해 Optinal에서는 다양한 예외처리 메서드를 제공하는데 이를 적극적으로 활용해야 한다.
// 수정 전
Item item = itemRepository.findByName("아이폰").get(0);
// 수정 후 (추천)
Item item = itemRepository.findByName("아이폰")
.stream()
.findFirst()
.orElseThrow(() -> new NoSuchElementException("테스트 데이터 생성 실패: '아이폰' 상품을 찾을 수 없습니다."));
수정후 코드는 예외상황이 발생하더라도 이를 적절히 처리하는 코드를 추가함으로써 수정 전 코드가 가지는 문제를 보완했고, 이렇게 예외처리를 추가하면서 에러메세지를 작성해두면 실제 예외가 발생했을 때 디버깅이 용이해지고, 에러발생의 원인을 맥락적으로 이해할 수 있게 된다.
Optional()에서 반환타입에서 .get()이 안좋은 이유와 예외처리를 하는 이유
1. .get()이 안 좋은 이유: "폭탄 돌리기"
Optional은 "값이 없을 수도 있다"라는 것을 명시하기 위해 만들어진 바구니입니다. 그런데 여기서 .get()을 바로 쓰는 것은 바구니 안에 내용물이 있는지 확인도 안 하고 손을 집어넣는 것과 같습니다.
- 무책임한 에러: 값이 없으면 NoSuchElementException이 발생합니다. 문제는 이 에러가 "어디서, 왜" 발생했는지에 대한 설명이 전혀 없습니다.
- 컴파일러가 못 잡는 버그: .get()을 쓰는 순간, Optional이 제공하는 "안전 장치"를 스스로 해제하는 꼴이 됩니다. 결국 NullPointerException을 피하려고 만든 Optional을 쓰면서, 이름만 다른 런타임 에러를 마주하게 됩니다.
2. .orElseThrow()가 좋은 이유: "확실한 의도 표현"
반면 .orElseThrow()는 **"값이 없는 상황을 내가 예상하고 있고, 그 경우 어떻게 처리할지 결정했다"**는 것을 코드로 보여줍니다.
① 친절한 가이드 (디버깅의 신)
단순히 에러가 나는 게 아니라, 내가 직접 적은 에러 메시지가 로그에 찍힙니다.
// 로그만 봐도 "아, 22번 장바구니가 없네!"라고 바로 알 수 있음
cartRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException("해당 장바구니를 찾을 수 없습니다. ID: " + id));
② 비즈니스 로직과의 연결
단순히 프로그램이 죽는 게 아니라, 우리가 설계한 **CustomException(ErrorCode)**과 연결할 수 있습니다. 이를 통해 클라이언트에게 "상품이 품절되었습니다" 같은 예쁜 에러 메시지를 보낼 수 있게 되죠.
③ 코드 가독성 (선언적 프로그래밍)
"값 가져와, 만약 없으면 뒤져봐, 뒤졌는데도 없으면 에러 던져"라는 긴 if-else문을 한 줄의 흐름으로 읽히게 만듭니다.
Optinal 반환 객체를 활용하는 메서드 3가지
1. orElse(T other)
값이 비어있다면 other를 반환합니다.
- 특징: Optional 안의 값이 존재하더라도 other 인자에 들어가는 표현식이나 메서드는 일단 실행됩니다.
- 주의사항: 인자로 상수를 넘길 때는 괜찮지만, 새로운 객체를 생성하거나 DB 기능을 호출하는 메서드를 넣으면 성능 낭비가 발생할 수 있습니다.
// 값이 있든 없든 "Default" 문자열 객체 생성 로직은 일단 실행됨
String result = optionalName.orElse("Default");
2. orElseGet(Supplier<? extends T> other)
값이 비어있다면 Supplier를 통해 생성된 값을 반환합니다.
- 특징: Optional이 비어있을 때만 인자로 전달된 함수(람다)를 실행합니다. (Lazy Evaluation)
- 권장 상황: 객체 생성 비용이 크거나, 내부 로직이 복잡한 경우 사용합니다.
// 값이 비어있을 때만 generateDefaultName() 메서드가 호출됨
String result = optionalName.orElseGet(() -> generateDefaultName());
3. orElseThrow(Supplier<? extends X> exceptionSupplier)
값이 비어있다면 예외를 발생시킵니다.
- 특징: 값이 없으면 로직을 더 이상 진행할 수 없는 상황(예: 데이터 필수 조회 실패)에서 사용합니다.
- 변화: 자바 10부터는 인자 없이 orElseThrow()만 사용하면 NoSuchElementException을 던지도록 간소화되었습니다.
// 값이 없으면 사용자 정의 예외 발생
Member member = optionalMember.orElseThrow(() -> new MemberNotFoundException("사용자를 찾을 수 없습니다."));
// 자바 10 이상 (NoSuchElementException 발생)
Member member = optionalMember.orElseThrow();
'Programming Language > Java' 카테고리의 다른 글
| [Java] 문자열은 얕은 복사(참조 변수 할당)로 참조해도 괜찮은 이유 (0) | 2026.01.15 |
|---|---|
| [Java] 깊은복사와 얕은복사 그리고 방어적복사 (0) | 2026.01.13 |
| [Java] default 접근제어자와 테스트코드 패키지 (0) | 2026.01.08 |
| [Java] 동일성(Identity)과 동등성(Equality) (0) | 2025.12.23 |
| [Java] ArrayList 초기화 (0) | 2025.12.15 |