말하는 컴공감자의 텃밭

Java - Optional<> 본문

백엔드/Java

Java - Optional<>

현콩 2024. 7. 4. 14:08
728x90

1. Optional이란 무엇인가?

Optional은 Java 8에서 추가된 클래스다.

이 클래스는 null이 들어올 수 있는 값을 감싸는 컨테이너 역할을 한다.

Optional을 사용하면 null 값을 직접 다루지 않고도 null 처리를 할 수 있다.

 

예를 들어, 다음과 같은 JPA 메서드에서 Optional을 사용할 수 있다.

 Optional<StudyMember> findByMemberAndStudy(Member member, Study study);

 

현재 개발중인 프로젝트에서 Jpa 메서드를 작성할때도 사용했었다.

 

2. 왜 Optional을 사용하는가?

2.1 NullPointerException 방지 일명 "NPE"

Optional을 사용하면 null 값을 직접 다루지 않기 때문에 NullPointerException을 방지할 수 있다.

이는 코드의 안정성을 높여주고, 예외 처리 코드를 줄여준다.

Optional<StudyMember> studyMemberOptional = findByMemberAndStudy(member, study);
if (studyMemberOptional.isPresent()) {
    StudyMember studyMember = studyMemberOptional.get();
    // 값 처리
} else {
   System.out.println("없는 멤버에요!: " + activeMember.getName());
}

 

 

2.2 코드 가독성 향상

null 체크를 위한 if 문을 줄이고, 의도를 명확하게 드러내는 코드를 작성할 수 있다.

Optional을 사용하면 값이 존재하는지 여부를 명시적으로 표현할 수 있다.

activeStudyMembers.forEach(activeMember -> {
    System.out.println("활동중인 멤버: " + activeMember.getName());
});

 

2.3 함수형 프로그래밍 스타일 지원

Optional은 함수형 프로그래밍 스타일을 지원한다. map, flatMap, filter 등 다양한 메서드를 제공하여, 값이 존재하는 경우에만 해당 연산을 수행할 수 있게 해준다.

Optional<String> memberName = studyMemberOptional.map(StudyMember::getName);
memberName.ifPresent(System.out::println);

Optional<StudyMember> activeStudyMember = studyMemberOptional
    .filter(studyMember -> studyMember.getStatus() == Status.ACTIVE);
activeStudyMember.ifPresent(activeMember -> {
    System.out.println("현재 활동중인 스터디 멤버: " + activeMember.getName());
});

 

3. Optional의 문제점

3.1 남용 문제

Optional을 남용하면 오히려 코드가 복잡해질 수 있다.

모든 nullable 값을 Optional로 처리하려고 하면, 불필요한 객체 생성과 메서드 호출이 늘어나 성능에 악영향을 줄 수 있다.

 

3.2 직렬화 문제

Optional은 Serializable 인터페이스를 구현하지 않는다.

따라서, 직렬화가 필요한 상황에서는 사용할 수 없다.

 

3.3 컬렉션과의 조합 문제

Optional을 컬렉션 내부에 사용하는 것은 권장되지 않는다.

대신, 빈 컬렉션을 반환하거나 특별한 값 객체를 사용하는 것이 더 바람직하다.

4. 고려해야 할 사항

4.1 Optional의 반환

메서드의 반환 타입으로 Optional을 사용하는 것은 좋지만, 필드 타입으로 Optional을 사용하는 것은 피해야 한다.

Optional은 메서드 시그니처에서 의도를 명확히 하기 위해 사용되는 것이지, 객체의 상태를 표현하는 데 사용되는 것은 아니다.

 

4.2 기본 값 제공

Optional의 orElse나 orElseGet 메서드를 사용하여 기본 값을 제공할 수 있다.

이를 통해 null 대신 기본 값을 사용하는 코드를 간단하게 작성할 수 있다.

StudyTitle studyTitle = studyTitleOptional.orElse(new studyTitle());
System.out.println("기존의 제목: " + studyTitle.getName());

 

studyTitleOptional이 비어 있을 때 new StudyTitle() 객체를 반환하여 사용한다.

이 방법은 Optional이 비어 있지 않더라도 new StudyTitle() 객체가 항상 생성된다.

StudyTitle defaultStudyTitle = new StudyTitle();
defaultStudyTitle.setName("스터디 제목을 입력하세요");

StudyTitle studyTitle = studyTitleOptional.orElse(defaultstudyTitle);
System.out.println("스터디 제목: " + studyTitle.getName());

 

기본값으로 사용할 객체를 미리 생성하고, orElse를 통해 studyTitleOptional이 비어 있을 때 이 기본 객체를 반환한다.

이 방법은 미리 생성된 객체를 사용하므로, Optional이 비어 있지 않더라도 객체 생성이 추가로 발생하지 않는다.

 

Optional<StudyTitle> studyTitleOptional = findStudyTitleBySomeCriteria();

StudyTitle studyTitle = studyTitleOptional.orElseGet(() -> new StudyTitle());
System.out.println("기존의 제목: " + studyTitle.getName());

 

4.3 스트림과 함께 사용
스트림과 함께 사용하면 Optional을 더욱 효율적으로 활용할 수 있다.

예를 들어, Optional을 스트림으로 변환하여 처리할 수 있다.

List<StudyMember> activeStudyMembers = studyMemberOptional
    .stream()
    .filter(studyMember -> studyMember.getStatus() == Status.ACTIVE)
    .collect(Collectors.toList());

activeStudyMembers.forEach(activeMember -> {
    System.out.println("활동중인 스터디 멤버: " + activeMember.getName());
});


4.4 예외 처리
Optional의 orElseThrow 메서드를 사용하여 예외를 던질 수 있다. 

이를 통해 값이 존재하지 않을 때 명확하게 예외를 처리할 수 있다.

StudyMember studyMember = studyMemberOptional.orElseThrow(() -> 
    new NoSuchElementException("존재하지 않는 멤버입니다"));



Optional은 null 처리를 더욱 안전하고 명확하게 해주는 유용한 도구지만, 남용하지 않도록 주의해야 한다.

적절한 상황에서 Optional을 사용하여 코드의 가독성과 안정성을 높여보자.

728x90
Comments