서론
이번 글에서는 findById()
, getById()
메서드의 차이점과 제가 생각하는 느낀 점에 대해 말해보고자 합니다.
따라서 개인적인 생각이 담긴 글이니 어느정도 비판적인 시각으로 봐주시면 감사하겠습니다.
사전 설명
다음과 같은 Entity 연관관계를 가지고 있을 때 저희는 일반적으로 게시판에 해당하는 댓글을 추가하기 위해서클라이언트 요청으로부터 게시판의 Id, 댓글내용, 작성자
등 필요한 정보를 받게됩니다.
그러면 저희는 Serivce Layer에서 해당하는 게시판이 있는지 확인 후 Reply 객체를 생성해서 연관관계를 맺은 후 저장하게 됩니다.
간단히 이러한 로직이 만들어지게 됩니다.
public void save(RequestReply requestReply) {
// 해당하는 게시판이 존재하는가 ?
Board board = boardRepository.findById(requestReply.getBoardId())
.orElseThrow(EntityNotFoundException::new);
// 댓글 생성
Reply reply = Reply.builder()
.content(requestReply.getContent())
.board(board)
.build();
replyRepository.save(reply);
}
해당 로직 테스트 코드는 다음과 같습니다.
@Test
@DisplayName("댓글등록")
void replySave() {
//given
StudyBoard studyBoard = StudyBoard.builder()
.title("토비의 스프링 스터디원 구해요")
.studyName("초보를 위한 Spring Study")
.description("이러한 이유로 모집합니다.")
.place("서울")
.recruitmentDeadline(LocalDateTime.now().plusDays(7))
.recruiter(4)
.createBy("KJJ")
.build();
StudyBoard study = boardRepository.save(studyBoard);
RequestReply requestReply = new RequestReply(study.getId(), "저요저요!");
System.out.println("==================================================================");
//when save 호출시 쿼리 발생 갯수 확인
replyService.save(requestReply);
}
findById()
앞서 살펴본 예제는 findById()를 통해 DB에 쿼리가 발생하여 객체를 찾게 됩니다.
따라 다음과 같이 총 2번의 쿼리가 발생하게 됩니다.
여기서 드는 생각은 왜?
- Board 객체를 찾아야 하지?
- Id 값을 알고 있으니 Insert Query 하나만 발생해도 충분한 거 아닌가?
이러한 생각이 들 수 있습니다.
하지만 ORM 입장에서는 해당 ID 값 만으로는 어떤 타입의 객체인지 알 수 없습니다.
따라서 앞서 살펴본 것처럼 한번 찾고 Reply 객체를 생성하여 Board를 설정하는 과정이 발생하게 됩니다.
이처럼 두 번 Query 가 발생하는 것이 무조건 나쁜 과정일까요?
해당 질문은 아래에서 좀 더 다루겠습니다.
getById()
getById()
메서드가 생소하신 분들도 있을 것 같은데요 Spring Data JPA 2.5.3 이후부터 getOne() 메서드가 Deprecated 되고추가된 메서드입니다 기존 getOne() 메서드와 하는 일은 동일합니다.
그렇다면 앞선 예제에서 findById()
로 Board를 찾는 대신 getById()
를 통해 Board를 찾는다면 어떻게 될까요?
다음과 같이 하나의 Query 만 발생하게 됩니다.
따라서 앞서 findById() 메서드를 사용했을 때 가진 생각을 해결할 수 있는데요!
우선 getById()
가 어떻게 작동하는지부터 알아보죠!
getById()
는 지연 로딩으로 작동하게 되는데요! ID 값을 제외한 나머지 필드에 접근했을 때 Query가 발생하게 됩니다.
public void save(RequestReply requestReply) {
Board board = boardRepository.getById(requestReply.getBoardId());
System.out.println("ID 값 :"+ board.getId()); // 이때는 Query 가 발생하지않음
System.out.println("=============================================");
System.out.println("Title 값: "+ board.getTitle()); //이때는 정보가 필요하기때문에 Query 가 발생함
// 댓글 생성
Reply reply = Reply.builder()
.content(requestReply.getContent())
.board(board)
.build();
replyRepository.save(reply);
}
예제를 본다면 더욱 이해하기 쉬울 것 같습니다
이처럼 어떠한 객체의 ID 값이 DB에 반드시 존재하고 ID를 제외한 다른 필드에 접근하지 않을 때 사용하게 되면 좋을 것 같습니다.
나의 생각💡
findById()
, getById()
의 차이점은 즉시 로딩, 지연 로딩으로 가져오는가? 의 차이점이 있습니다.
여기서 또 메서드 이름으로 유추해볼 수 있는 주제가 있는데요.
왜 getOne() 메서드가 Deprecated 되고 getById()로 이름만 변경되었을까요?
여기서 알 수 있는 것은 메서드가 조금 더 명확하게 변경되었다는 것입니다.
get
은 무엇을 받다, 얻다 라는 의미가 있습니다.
즉 어떠한 객체를 무조건 받는다는 가정이 있습니다. (null을 return 하면 안 된다.)
find
은 찾다 라는 의미를 가지고 있습니다.
즉 객체를 찾을 수 있고 없을 수 도 있다는 가정이 있습니다. (Optional로 return 하여 선택권을 제공)
이렇게 명확한 구분은 저희가 메서드 이용하는 입장에서 기준을 가지고 사용할 수 있게 됩니다.
따라서 이처럼 두 번 Query 가 발생하는 것이 무조건 나쁜 과정일까요?
에 대한 질문은 메서드의 의미를 생각해보면 답이 나올 것 같습니다.
비즈니스상 클라이언트로부터 들어온 ID 값이(게시글이) 어떠한 경우에도 항상 존재하는가?
- Yes 인 경우에는 getById()로 메서드로 사용해도 문제가 발생하지 않습니다. (없다면 예외가 발생합니다.)
- NO 인 경우에는 findById()를 사용해서 존재 여부를 확인해야 하겠죠?
결론
프레임워크가 제공하는 메서드명을 읽다 보면 의도를 어느 정도 파악할 수 있습니다.
이러한 확실한 메서드명은 개발자로 하여금 어떤 문제를 발생할 수 있는지 내부 코드를 보지 않아도 어느 정도 유추할 수 있는데요!
이러한 코드가 정말로 좋은 코드이지 않을까요?
'Spring > JPA' 카테고리의 다른 글
[JPA] 비관적 락 , 낙관적 락 (4) | 2021.05.24 |
---|---|
[JPA] 순환참조 해결하기(JackSon 동작원리) (0) | 2020.12.30 |