10-6. Optional

박은서's avatar
Feb 06, 2026
10-6. Optional

1. Optional<T>

1️⃣ Optional<T> 이란?

1) 기본 개념

  • 값이 있을 수도 있고 없을 수도 있는 상황을 명시적으로 표현하기 위한 컨테이너 타입
Optional<T>
  • 내부에 단 하나의 값 또는 값 없음(empty) 상태를 가짐
  • null을 직접 담지 않음
  • Java 8부터 도입

2) 핵심 목적

  • null로 인한 오류(특히 NullPointerException)를 줄이고, API의 의도를 분명히 하는 것

2️⃣ Optional이 필요한 이유

1) 기존 방식의 문제

String name = user.getName(); if (name != null) { System.out.println(name.toUpperCase()); }
  • null 체크를 깜빡하기 쉽다
  • 메서드 시그니처만 봐서는 null이 올 수 있는지 알기 어렵다
  • NullPointerException의 주요 원인

2) Optional 사용 시

Optional<String> name = user.getName(); name.ifPresent(n -> System.out.println(n.toUpperCase()));
  • “이 값은 없을 수도 있다”는 사실이 타입으로 드러남
  • 호출자가 반드시 처리하도록 유도

3️⃣ Optional 생성 방법

1) 값이 확실히 있을 때

Optional<String> opt = Optional.of("hello");
⚠️ null 전달 시 즉시 NullPointerException

2) 값이 있을 수도, 없을 수도 있을 때

Optional<String> opt = Optional.ofNullable(value);
  • value == nullOptional.empty()
  • 가장 흔히 사용됨

3) 빈 Optional

Optional<String> opt = Optional.empty();

4️⃣ 값 확인 및 추출

1) 존재 여부 확인

if (opt.isPresent()) { System.out.println(opt.get()); }
  • 가능은 하지만 권장되지 않음
  • 결국 null 체크와 다를 바 없음

2) ifPresent (권장)

opt.ifPresent(value -> System.out.println(value));

3) 기본값 제공

String result = opt.orElse("default");
⚠️ orElse값이 있어도 인자가 항상 평가됨
String result = opt.orElseGet(() -> expensiveOperation());
  • 필요할 때만 실행 → 성능상 더 안전

4) 값이 없으면 예외

String result = opt.orElseThrow();
String result = opt.orElseThrow(() -> new IllegalStateException("값 없음"));

5️⃣ map / flatMap / filter (핵심)

1) map – 값 변환

Optional<String> opt = Optional.of("java"); Optional<Integer> length = opt.map(String::length);
  • 값이 없으면 자동으로 empty 유지

2) flatMap – Optional 중첩 방지

Optional<Optional<String>> ❌ Optional<String> ✔
Optional<User> user; Optional<String> email = user.flatMap(User::getEmail);
getEmail()Optional<String>을 반환할 때 사용

3) filter – 조건 만족 시만 유지

Optional<String> opt = Optional.of("java"); opt.filter(s -> s.length() > 3) .ifPresent(System.out::println);

6️⃣ Optional 사용 시 권장 규칙 (중요)

✅ 사용해야 할 곳

  • 메서드 반환 타입
Optional<User> findUserById(long id)
  • 스트림 파이프라인
  • 값이 없음을 정상적인 흐름으로 표현할 때

❌ 피해야 할 곳

① 필드
class User { Optional<String> name; // ❌ }
② 메서드 파라미터
void print(Optional<String> name); // ❌
③ 컬렉션 대신
Optional<List<User>> // ❌
List<User> (빈 리스트 사용)

7️⃣ Optional vs null (요약 비교)

항목
null
Optional
의도 표현
❌ 불명확
✅ 명확
타입 안정성
NPE 위험
높음
낮음
함수형 처리
불가
가능

8️⃣ 흔한 안티패턴

❌ get() 남용

opt.get(); // 값 없으면 예외

❌ isPresent + get

if (opt.isPresent()) { return opt.get(); }

❌ Optional을 Serializable DTO 필드로 사용

9️⃣ 실습

1) Optional.of()

package ex05; import java.util.Optional; public class OptEx01 { public static void main(String[] args) { // 1. Optional of Method String name = "ssar"; Optional<String> opt = Optional.of(name); // 선물박스 안에 값이 들어감 (of에는 null 들어갈 수 없음) System.out.println(opt.get()); // get() - 값이 무조건 있으니까 꺼내! 실제 코드에서는 사용 X System.out.println(opt.get().length()); } }
notion image
실제 코드에서는 사용할 일 없음. 무조건 값이 들어갈거면 그냥 String으로 받으면 됨

2-1) Optional.ofNullable() - isEmpty()

package ex05; import java.util.Optional; public class OptEx01 { public static void main(String[] args) { // 2. Optional nullable String name2 = null; Optional<String> opt2 = Optional.ofNullable(name2); if (opt2.isPresent()) { // 값이 있으면 System.out.println(opt2.get()); } if (opt2.isEmpty()) { // null이면 throw new RuntimeException("name2에 값이 없어요"); } } }
notion image

2-2) Optional.ofNullable() - isPresent()

package ex05; import java.util.Optional; public class OptEx01 { public static void main(String[] args) { // 2. Optional nullable String name2 = "cos"; Optional<String> opt2 = Optional.ofNullable(name2); if (opt2.isPresent()) { // 값이 있으면 System.out.println(opt2.get()); } if (opt2.isEmpty()) { // null이면 throw new RuntimeException("name2에 값이 없어요"); } } }
notion image

3-1) orElseThrow() - ①홀수

package ex05; import java.util.Optional; public class OptEx01 { static String download(int i){ if(i % 2 == 0){ return null; }else{ return i+""; } } public static void main(String[] args) { // 3. orElse String name3 = download(1); // 짝수 = null, 홀수 = 값 Optional<String> opt3 = Optional.ofNullable(name3); // 3-1. orElseThrow String value1 = opt3.orElseThrow(() -> new RuntimeException("opt3에 값이 없어요")); System.out.println("value1 : " + value1); // 3-2. orElse } }
notion image

3-1) orElseThrow() - ②짝수

package ex05; import java.util.Optional; public class OptEx01 { static String download(int i){ if(i % 2 == 0){ return null; }else{ return i+""; } } public static void main(String[] args) { // 3. orElse String name3 = download(2); // 짝수 = null, 홀수 = 값 Optional<String> opt3 = Optional.ofNullable(name3); // 3-1. orElseThrow String value1 = opt3.orElseThrow(() -> new RuntimeException("opt3에 값이 없어요")); System.out.println("value1 : " + value1); // 3-2. orElse } }
notion image

3-2) orElse() - ①홀수

package ex05; import java.util.Optional; public class OptEx01 { static String download(int i){ if(i % 2 == 0){ return null; }else{ return i+""; } } public static void main(String[] args) { // 3. orElse String name3 = download(1); // 짝수 = null, 홀수 = 값 Optional<String> opt3 = Optional.ofNullable(name3); // 3-1. orElseThrow String value1 = opt3.orElseThrow(() -> new RuntimeException("opt3에 값이 없어요")); // System.out.println("value1 : " + value1); // 3-2. orElse String value2 = opt3.orElse("0"); System.out.println("value2 : " + value2); } }
notion image

3-2) orElse() - ②짝수

package ex05; import java.util.Optional; public class OptEx01 { static String download(int i){ if(i % 2 == 0){ return null; }else{ return i+""; } } public static void main(String[] args) { // 3. orElse String name3 = download(2); // 짝수 = null, 홀수 = 값 Optional<String> opt3 = Optional.ofNullable(name3); // 3-2. orElse String value2 = opt3.orElse("0"); System.out.println("value2 : " + value2); // 3-1. orElseThrow // String value1 = opt3.orElseThrow(() -> new RuntimeException("opt3에 값이 없어요")); // System.out.println("value1 : " + value1); } }
notion image
Share article