0Pricing
Java Academy · 강의

Optional 모범 사례

orElse, orElseGet, orElseThrow를 적용하고 Optional의 일반적인 안티 패턴을 피합니다.

Optional 모범 사례은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

Optional 모범 사례

Optional은 올바르게 사용하면 강력한 도구입니다. 이 레슨에서는 실제 코드베이스의 구체적인 예를 통해 지켜야 할 사항과 피해야 할 사항을 다룹니다.

권장: 반환 형식으로 Optional 사용하기

Optional의 주된 사용 사례는 값을 찾지 못하거나 생성하지 못할 수 있는 메서드의 반환 형식으로 사용하는 것입니다.

import java.util.Optional;

// Good: signals to caller that result might be absent
public Optional<User> findByUsername(String username) { ... }
public Optional<Config> getConfig(String key) { ... }
public Optional<String> getHeader(String name) { ... }

// These are all cases where absence is a normal outcome,
// not an exceptional condition

금지: 필드로 Optional 사용하기

Optional 필드는 객체 오버헤드를 추가하고 직렬화를 복잡하게 만듭니다. 클래스 내부의 선택적 필드에는 null 또는 센티널 값을 사용하십시오.

// BAD
class User {
    private Optional<String> middleName; // wrong
}

// GOOD: use null for absent optional field
class User {
    private final String middleName; // null = absent
    public Optional<String> middleName() {
        return Optional.ofNullable(middleName); // expose as Optional in getter
    }
}
User u = new User(null); // null field
System.out.println(u.middleName().isPresent()); // false

금지: 메서드 매개변수로 Optional 사용하기

Optional 매개변수를 사용하면 호출하는 쪽에서 불필요하게 값을 Optional로 감싸야 합니다. 대신 오버로드 또는 @Nullable을 사용하십시오.

// BAD
void sendNotification(String message, Optional<String> subject) { ... }
// caller must write: sendNotification("Hello", Optional.of("News"))

// GOOD: overloading
void sendNotification(String message) { sendNotification(message, null); }
void sendNotification(String message, String subject) {
    // subject may be null
}
// caller writes: sendNotification("Hello", "News") — clean

금지: 컬렉션에서 Optional 사용하기

List, 집합 또는 맵 값 안에 Optional을 넣지 마십시오. 컬렉션에 비어 있는 Optional을 넣는 것은 의미가 없으므로 해당 항목을 생략하십시오.

// BAD: List of Optionals
List<Optional<User>> results = ...; // meaningless empty Optionals

// GOOD: List of present values only
List<User> presentUsers = maybeUsers.stream()
    .flatMap(Optional::stream)
    .collect(Collectors.toList());

// BAD: Map with Optional values
Map<String, Optional<String>> config = ...;

// GOOD: just use null or omit the key for absent values
Map<String, String> config2 = ...;
String value = config2.getOrDefault("key", "default");

권장: 비용이 큰 기본값에는 orElseGet 사용하기

기본값을 계산해야 하는 경우(데이터베이스 쿼리, 네트워크 호출)에는 항상 orElseGet을 사용하십시오. supplier는 필요할 때만 호출됩니다.

import java.util.Optional;

// BAD: buildDefault() always called, even when value is present!
String config = getConfig("timeout").orElse(buildDefault());

// GOOD: supplier called lazily only when empty
String config2 = getConfig("timeout").orElseGet(() -> buildDefault());

// Or with method reference:
String config3 = getConfig("timeout").orElseGet(this::buildDefault);

// For cheap literals, orElse is fine
String name = getConfig("name").orElse("default");

권장: 필수 값에는 orElseThrow 사용하기

값이 없는 것이 프로그래밍 오류 또는 불변 조건 위반이라면 설명적인 예외와 함께 orElseThrow를 사용하십시오.

import java.util.Optional;

// Good: throw when absence is an error
User user = userRepository.findById(userId)
    .orElseThrow(() -> new UserNotFoundException("User not found: " + userId));

// Good: in validation code
String apiKey = Optional.ofNullable(System.getenv("API_KEY"))
    .orElseThrow(() -> new IllegalStateException(
        "API_KEY environment variable is required"));

System.out.println("Key found: " + apiKey.length() + " chars");

안티패턴: isPresent + get()

isPresent()를 확인한 다음 get()을 호출하는 안티패턴을 피하십시오. 이는 null 확인보다 나을 것이 없으며 Optional의 목적을 무력화합니다.

import java.util.Optional;

Optional<String> opt = findValue();

// BAD: isPresent() + get() — no improvement over null check
if (opt.isPresent()) {
    String val = opt.get();
    process(val);
}

// GOOD: use the functional API
opt.ifPresent(this::process);

// GOOD: transform and return
return opt.map(this::process).orElse(null);

// GOOD: provide a default
String result = opt.orElse("default");

연결에 관한 모범 사례

깔끔한 연결을 구성하십시오: find → filter → 변환 → fallback. 각 단계의 의미가 자체적으로 드러납니다.

import java.util.Optional;

record User(long id, String plan, boolean active) {}

static Optional<User> findUser(long id) { return Optional.empty(); }

String result = findUser(42L)
    .filter(User::active)           // keep only active users
    .filter(u -> "PRO".equals(u.plan())) // keep only PRO users
    .map(u -> "Welcome, Pro user #" + u.id()) // transform
    .orElse("Upgrade to Pro!");     // fallback

System.out.println(result); // Upgrade to Pro!

서비스 계층에서 Optional 사용하기

모범 사례를 따르는 실제와 유사한 서비스 계층 예제입니다.

import java.util.Optional;

class OrderService {
    public Optional<OrderSummary> getOrderSummary(long orderId) {
        return orderRepo.findById(orderId)
            .filter(o -> !o.isCancelled())
            .map(this::toSummary);
    }

    public OrderSummary requireOrderSummary(long orderId) {
        return getOrderSummary(orderId)
            .orElseThrow(() -> new OrderNotFoundException(orderId));
    }

    private OrderSummary toSummary(Order o) { return new OrderSummary(o); }
}
// API callers choose: Optional (handle absence) or require (exception)

Optional과 null 안전성 도구

Optional은 완전한 null 안전성 적용을 위해 @Nullable/@NonNull 주석 및 SpotBugs/NullAway와 같은 도구를 보완합니다.

// Annotation-based: declares intent for in-class nulls
import org.jetbrains.annotations.*;

class ProductService {
    @Nullable  // may return null — for framework/bean use
    public String getCachedName(String id) { return cache.get(id); }

    // Optional — for service API that handles absence
    public Optional<Product> findProduct(String id) {
        return repo.findById(id);
    }
}
// Use @Nullable for fields/parameters; Optional for return types

빠른 확인

다음 코드의 문제는 무엇입니까?

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

복습: Optional 모범 사례

핵심 내용:

  • Optional은 반환 형식으로만 사용하고, 필드나 메서드 매개변수로는 사용하지 마십시오
  • 기본값을 계산하는 비용이 크다면 orElse보다 orElseGet을 선호하십시오
  • 값의 부재가 프로그래밍 오류를 의미하면 orElseThrow를 사용하십시오
  • isPresent() + get()을 피하고, 대신 ifPresent, map 또는 orElse를 사용하십시오
  • 컬렉션 안에 Optional을 넣지 말고, 대신 스트림을 필터링하십시오
  • 명확한 데이터 흐름을 위해 읽기 쉬운 연결을 구성하십시오: filter → map → orElse

자주 묻는 질문

“Optional 모범 사례” 강의는 무료인가요?

네 — “Optional 모범 사례” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“Optional 모범 사례”에서 뭘 배우나요?

orElse, orElseGet, orElseThrow를 적용하고 Optional의 일반적인 안티 패턴을 피합니다. 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Java Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“Optional 모범 사례” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. Optional이 존재하는 이유
  2. Optional 생성과 검사
  3. Optional 변환: map과 flatMap
  4. Optional 모범 사례
← Java Academy(으)로 돌아가기