인프런 커뮤니티 질문&답변

고산하님의 프로필 이미지

작성한 질문수

실전! 스프링 데이터 JPA

Optional 적용 문제

해결된 질문

작성

·

1.2K

1

학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.

1. 강의 내용과 관련된 질문을 남겨주세요.
2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.
(자주 하는 질문 링크: https://bit.ly/3fX6ygx)
3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.
(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)

질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.
=========================================
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예/아니오)
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)

[질문 내용]
여기에 질문 내용을 남겨주세요.
회원 이메일을 사용하여 MemberRepository에서 회원 정보를 조회하는 메서드를 구현해 보았습니다.
해당 이메일의 회원이 DB에 존재하지 않을경우를 고려하여 다음과 같이 Optional로 감싸주었습니다.
 
Transactional(readOnly = true)
public interface MemberRepository extends JpaRepository<Member, Long> {

    Optional<Member> findByEmail(String email);
...
}

이 경우 Service 단에서 해당 메서드를 사용할 경우 변수 타입을 Optional<Member>로 선언해 줘야 했습니다.

 

@Transactional
    public void followFriend(FollowFriendRequest request) {
        Member requester = getMember(request.getRequesterEmail());
        Optional<Member> receiver  = memberRepository.findByEmail(request.getReceiverEmail());
        log.info("requester: '{}', receiver: '{}'", requester, receiver);
        if (receiver == null) {
            throw new MemberEmailNotFound();
        }
        if (requester.hasFriend(receiver) || relationRepository.existsByOwnerEmailAndFriendEmail(request.getRequesterEmail(), request.getReceiverEmail())) {
            throw new EmailDuplicatedException(request.getReceiverEmail());
        }
        relationRepository.save(new Relation(requester, receiver));
    }

 

이러면 위 코드 마지막 줄의 Relation 생성자 또한 Optional을 포함한 생성자를 추가로 만들어 줘야 하고 이렇게 되면 Relation 엔티티의 receiver 필드값에도 Optional을 씌워줘야 하는 문제가 발생했습니다. 이 경우 Optional을 어떻게 사용하는게 옳은건지 궁금합니다.

 

 

답변 2

1

고산하님의 프로필 이미지
고산하
질문자

답변 감사드립니다!

 

Optional에서 값을 꺼내는 것과 별개로 서비스단의 followFriend 메서드에서 

Oprtional<Member> receiver = memberRepository.findByEmail(request.getReceiverEmail());

와 같이 해당 이메일에 해당하는 Member 객체를 가져왔고 메서드의 마지막에서 

        relationRepository.save(new Relation(requester, Optional<Member> receiver));

와 같이 Relation 객체를 생성해서 DB에 저장해 주는 코드를 작성하였습니다. 

Optional로 감싸서 조회하였기 때문에 생성자 매개변수 receiver 또한 Optional로 감싸주게 되었고 이렇게 되면 Relation 객체의 friend 필드에도 Optional을 선언해 줘야했습니다.

@Entity

@Getter
public class Relation extends BaseEntity {

@ManyToOne(fetch = LAZY)
@JoinColumn(name = "member_id")
private Member owner;

@ManyToOne(fetch = LAZY)
@JoinColumn(name = "friend_id")
private Optional<Member> friend;

protected Relation() {}

public Relation(Member owner, Optional<Member> friend) {
this.owner = owner;
this.friend = friend;
}
...

}

이런식으로 엔티티가 설계되는 것이 옳은 방식인지 궁금합니다.

생성자 매개변수에 Optional을 사용을 제거해주세요.

설명은 아래 정리글을 참고해주세요

https://escapefromcoding.tistory.com/247

엔티티 필드로 Optional 사용은 본적이 없어서 사용 여부까지는 모르겠습니다.

아시는 분은 답변 부탁드립니다.

찾아보니 baeldung에서 21년 5월 경 작성한 아티클 내용 중 관련 내용이 있어 확인해보니 

지원을 제대로 할 때 까지는 기본 방식을 이용하자는 내용 인 것 같습니다.

3.3 JPA 파트를 확인해주세요. 에러 예시들을 참고해보시면 좋을 것 같습니다.

https://www.baeldung.com/java-optional-return

제 생각엔 Optional을 생성자로 넘기는 것은 바람직하지 않은 것 같습니다.

질문자님이 먼저 작성하신 코드는 다음과 같습니다.

 Optional<Member> receiver  = memberRepository.findByEmail(request.getReceiverEmail());
if (receiver == null) {
    throw new MemberEmailNotFound();
}

 

아래와 같이 orElse() 를 이용하거나 혹은 orElseThrow() 로 바로 오류를 던지면 코드가 더 줄어들 수 있습니다. 

Optional<Member> optionalMember= memberRepository.findByEmail(request.getReceiverEmail());
Member foundMember = optionalMember.orElseThrow(()-> new Exception());
....<코드 생략>...
new Relation(receiver, foundMember);

 

그리고 orElseThrow()에 걸리면 그 라인에서 프로세스가 종료됩니다.그래서 그 밑에 코드가 실행되지 않습니다.

그러니 밑에서는 Optional로 감싼 값을 가져갈 필요 없이 foundMember를 바로 사용하면 됩니다.

 

고산하님의 프로필 이미지
고산하
질문자

답변 전부 감사합니다! 질문을 많이 모호하게 드렸는데도 제가 궁금했던 내용을 답해주셔서 큰 도움이 되었습니다

0

안녕하세요. 고산하님, 공식 서포터즈 OMG입니다.

질문 주신 내용에도 나와있지만

"해당 이메일의 회원이 DB에 존재하지 않을경우를 고려하여 다음과 같이 Optional로 감싸주었습니다."

Optional로 감싸기만 한채, 자바8에서 제공하는 Optional 관련 처리 메서드 미사용과 더불어 값을 null체크를 하셨지만 이후 값을 꺼내는 행동이 없습니다.

옵셔널 처리에 대한 가이드는 영한님이 추천하신 정리글과 

http://homoefficio.github.io/2019/10/03/Java-Optional-%EB%B0%94%EB%A5%B4%EA%B2%8C-%EC%93%B0%EA%B8%B0/

서포터즈 코즈위버님이 추천한 정리글을 참고하시면 도움 되리라 생각합니다.

Optional 제대로 활용하기 - Increment (latera.kr)

 

위 정리글을 안본 상태에서 단순 요청하신대로 동작 시켜야 한다면, .get()으로 Optional 값을 꺼내 Member객체를 가져와 사용할 수 있을 것 같아요.

 

감사합니다.