해결된 질문
작성
·
1.3K
1
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;
}
...
}
이런식으로 엔티티가 설계되는 것이 옳은 방식인지 궁금합니다.
찾아보니 baeldung에서 21년 5월 경 작성한 아티클 내용 중 관련 내용이 있어 확인해보니
지원을 제대로 할 때 까지는 기본 방식을 이용하자는 내용 인 것 같습니다.
3.3 JPA 파트를 확인해주세요. 에러 예시들을 참고해보시면 좋을 것 같습니다.
제 생각엔 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체크를 하셨지만 이후 값을 꺼내는 행동이 없습니다.
옵셔널 처리에 대한 가이드는 영한님이 추천하신 정리글과
서포터즈 코즈위버님이 추천한 정리글을 참고하시면 도움 되리라 생각합니다.
Optional 제대로 활용하기 - Increment (latera.kr)
위 정리글을 안본 상태에서 단순 요청하신대로 동작 시켜야 한다면, .get()으로 Optional 값을 꺼내 Member객체를 가져와 사용할 수 있을 것 같아요.
감사합니다.
생성자 매개변수에 Optional을 사용을 제거해주세요.
설명은 아래 정리글을 참고해주세요
https://escapefromcoding.tistory.com/247