지긋지긋한 if (obj != null) 코드, 이제 그만 쓰고 싶으시죠? 자바 8에서 도입된 Optional은 Null을 우아하게 처리하기 위해 등장했지만, 잘못 쓰면 코드가 더 지저분해지고 성능까지 깎아먹습니다. 오늘은 'Optional답게' 코드를 짜는 법과, 시니어 개발자들이 질색하는 안티 패턴들을 정리해 드립니다.
1. Optional을 사용하는 진짜 이유
많은 분이 "NPE를 방지하기 위해서"라고 답하지만, 더 정확한 목적은 "메서드의 반환 값이 '없을 수도 있음'을 명시적으로 표현하기 위함"입니다.
즉, 반환 타입이 Optional<User>라면, 이 메서드를 사용하는 개발자는 "아, 이 결과는 null일 수 있으니 반드시 대처해야겠구나"라는 것을 코드를 보자마자 알 수 있습니다.
2. 제발 이렇게 쓰지 마세요: 최악의 안티 패턴 3가지
❌ 안티 패턴 1: 무분별한 .get() 사용
Optional을 사용하면서 내부 값을 꺼내기 위해 바로 .get()을 호출하는 것은 가장 흔한 실수입니다.
// 최악의 예시
Optional<User> user = userRepository.findById(id);
User result = user.get(); // 만약 값이 없다면 NoSuchElementException 발생!
이 방식은 기존의 null 체크를 하지 않는 것과 다를 바가 없습니다. Optional을 쓰는 의미가 완전히 사라집니다.
❌ 안티 패턴 2: ifPresent()를 기존 if문처럼 사용하기
// 기존 방식
if (user != null) {
System.out.println(user.getName());
}
// Optional을 잘못 활용한 예시
userOptional.ifPresent(u -> {
System.out.println(u.getName());
});
단순히 if문을 ifPresent로 바꾸는 것은 코드만 길어질 뿐 큰 이점이 없습니다.
❌ 안티 패턴 3: Optional을 필드(Field)나 파라미터로 사용
Optional은 직렬화(Serialization)를 지원하지 않습니다. 따라서 엔티티의 필드나 메서드의 인자로 사용하는 것은 설계상 매우 위험합니다.
- 권장: 반환 타입(Return Type)으로만 사용하세요.
3. 실무에서 바로 쓰는 Optional 정석 패턴
이제 실무에서 "코드 좀 짤 줄 아네?"라는 소리를 듣는 깔끔한 패턴들을 살펴보겠습니다.
✅ 패턴 1: orElseThrow (값이 없으면 에러 던지기)
데이터가 없을 때 예외를 발생시키는 가장 표준적인 방법입니다.
User user = userRepository.findById(id)
.orElseThrow(() -> new CustomException("해당 유저를 찾을 수 없습니다."));
✅ 패턴 2: orElse vs orElseGet (성능 차이 주의!)
이 부분은 면접에서도 자주 나옵니다.
- orElse(new User()): 값이 있든 없든 new User()를 항상 실행합니다.
- orElseGet(() -> new User()): 값이 없을 때만 실행합니다.
// 값이 있어도 새로운 객체를 생성하는 낭비가 발생할 수 있음
User user = userRepository.findById(id).orElse(createNewUser());
// 값이 없을 때만 메서드가 호출됨 (성능 최적화)
User user = userRepository.findById(id).orElseGet(() -> createNewUser());
✅ 패턴 3: map과 filter 활용하기
Optional의 진가는 여기서 나옵니다. if문 없이 체이닝으로 처리하세요.
String grade = userRepository.findById(id)
.filter(u -> u.isActive()) // 활성화된 유저만 필터링
.map(u -> u.getGrade()) // 유저 객체에서 등급 정보만 추출
.orElse("GUEST"); // 위 과정에서 하나라도 없으면 "GUEST" 반환
4. Optional 사용 시 반드시 기억할 5계명
- 단순히 null 체크가 목적이라면 Optional을 쓰지 마세요. (성능 오버헤드가 발생합니다.)
- 반환 타입으로만 사용하세요. (필드, 파라미터, 생성자 인자로 금지)
- Optional.ofNullable()을 활용하세요. (null일 수도 있는 값을 감쌀 때 안전합니다.)
- 컬렉션(List, Map 등)은 Optional로 감싸지 마세요. (빈 리스트 Collections.emptyList()를 반환하는 게 정석입니다.)
- 원시 타입용 Optional을 따로 쓰세요. (Optional<Integer> 대신 OptionalInt, OptionalLong을 써야 박싱/언박싱 비용을 아낍니다.)
🏁 마치며: "Optional은 약속입니다"
Optional은 단순히 도구가 아니라, 동료 개발자와의 약속입니다. "이 메서드는 비어있을 수 있으니 안전하게 처리해줘"라는 메시지를 전달하는 것이죠. 오늘 배운 안티 패턴만 피해도 여러분의 코드는 훨씬 견고하고 우아해질 것입니다.
다음 포스팅에서는 자바의 또 다른 스테디셀러 주제, "String vs StringBuilder vs StringBuffer"의 내부 메모리 구조를 파헤쳐 보겠습니다!
https://run-code-rich.tistory.com/entry/java-optional-guide-anti-patterns
[Java] Optional 완벽 가이드: 더 이상 NullPointerException은 없다
[Java] Optional 완벽 가이드: NPE(Null)와의 전쟁을 끝내는 법자바 개발자라면 누구나 한 번쯤은 java.lang.NullPointerException 때문에 골머리를 앓아본 적이 있을 것입니다. 자바의 아버지 중 한 명인 토니
run-code-rich.tistory.com
'TECH > 웹개발' 카테고리의 다른 글
| [Java] 자바 메모리 구조 완벽 가이드: Stack, Heap, Method Area의 상호작용 (0) | 2026.03.10 |
|---|---|
| [Java] String vs StringBuilder vs StringBuffer: 아직도 헷갈린다면 이 글 하나로 끝내세요 (0) | 2026.03.09 |
| [Java 21] 자바 서버 성능의 혁명: 가상 스레드(Virtual Threads) 구조와 실무 활용 가이드 (1) | 2026.03.03 |
| [Java] Record와 Text Blocks: 자바 17에서 코드를 우아하게 만드는 두 가지 무기 (0) | 2026.02.28 |
| [Java] LTS 완벽 가이드: Java 11, 17, 21 핵심 기능과 실무 적용 코드 예시 (0) | 2026.02.25 |