N+1 관련해서 질문있습니다.
2
작성한 질문수 1
안녕하세요. 우선 좋은 강의 제작해주신 토비님께 항상 감사하고 있어요. 이제 배운지 1년된 왕초보입니당..
혼자 배워보면서 개인 프로젝트를 만들고 있는데 JPA를 사용하고 있어요.
제가 궁금한 것이... N+1 관련한 문제입니다. 아 일단 프로젝트 주제는 복식부기 가계부에요.
@Entity
...
public class Journal extends BaseEntity {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ledger_id", nullable = false, updatable = false)
@OnDelete(action = OnDeleteAction.CASCADE)
private Ledger ledger;
...
@OneToMany(mappedBy = "journal", fetch = FetchType.LAZY, cascade = CascadeType.ALL,
orphanRemoval = true)
private List<EntryLine> entries = new ArrayList<>(2);
...
public EntryLine getEntryLine(EntrySide side) {
switch (side) {
case CREDIT : this.entries.stream().filter(line -> line.isCredit()).findFirst()
.orElseThrow(...);
case DEBIT : this.entries.stream().filter(line -> line.isDebit()).findFirst()
.orElseThrow(...);
default : throw new ...
}
}
...
// Service에서 저장되기 전에 호출
public void validateSavable() {
...
validateJournalSave();
}
private void validateJournalSave() {
AccountType debit = getEntryLine(EntrySide.DEBIT).getAccountType();
AccountType credit = getEntryLine(EntrySide.CREDIT).getAccountType();
if(!this.transactionType.isValidPlacement(debit, credit)) {
throw new ...
}
}
}Journal Class에서 EntryLine List에 접근하고 있어요.
그리고 EntryLine Class는 이렇게 생겼어요.
@Entity
...
public class EntryLine extends BaseEntity {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "journal_id", nullable = false, updatable = false)
private Journal journal;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "account_id", nullable = false)
private Account account;
...
// private package 접근제어자 사용
// Account는 Category를 참조중이에요.
AccountType getAccountType() {
return this.account.getCategory().getAccountType();
}
}
거래가 저장되기 전에 Journal : validateJournalSave() 에서 this.transactionType에 따라 차변과 대변에 올바르게 위치하고 있는지 검사한 후 저장하고 있는데 이것을 생성과 수정할 때 두 곳에서 사용하고 있어요.
Ledger에 5개 Category가 있고, Account는 그 Category를 참조하고 Category에서만 AccountType이 있어요.
Journal이 각 EntryLine의 AccountType을 얻기 위해
Journal -> EntryLine -> Account -> Category -> getAccountType() 이렇게 흘러가네요.
이렇게 접근해도 설계상 괜찮은걸까요?
Journal을 저장할때는 @Query 사용해서 Fetch Join으로 필요한 Account를 가져오고 있는 상황이에요.
Journal이라는 엔티티가 비즈니스 로직 수행을 위해서 다른 엔티티의 필드까지 깊게 참조?? 가져오도록 설계하는게 옳은건지 모르겠어요.
답변 1
0
안녕하세요, 인프런 AI 인턴입니다. Jubuseong님!
프로젝트에서 N+1 문제와 관련된 질문을 주셨고, 이를 해결하기 위한 접근 방식에 대한 조언을 드리겠습니다.
N+1 문제에 대한 이해
N+1 문제는 주로 JPA에서 발생하며, 하나의 쿼리를 통해 기본 엔티티를 로드한 후 관련된 여러 엔티티를 각각의 쿼리로 로드하는 시나리오입니다. 이는 많게는 N+1개의 쿼리를 실행하게 되어 비효율적일 수 있습니다.
현재 설계에서의 문제점
Journal 엔티티가 EntryLine 리스트를 통해 AccountType 정보를 가져오기 위해 여러 엔티티를 탐색하는 부분은 깊은 객체 탐색을 유발할 수 있습니다. 특히, @ManyToOne(fetch = FetchType.LAZY) 설정으로 인해 각 EntryLine 객체별로 Account와 Category에 접근할 때 추가적인 쿼리가 발생하여 N+1 문제를 야기할 수 있습니다.
해결책 제안
Fetch Join 사용: 이미 사용 중이신
@Query및 Fetch Join을 통해Journal과 관련된EntryLine및 그 이상의 계층(Ledger, Account, Category)을 한번의 쿼리로 가져올 수 있습니다. 이를 통해 N+1 문제를 해결할 수 있으며, 관련 객체가 모두 초기화된 상태로 작업을 수행할 수 있습니다.DTO 사용: 깊은 탐색이 필요하지 않다면, 관련 데이터만 포함한 DTO를 사용하는 것도 좋은 방법입니다. DTO는 필요한 데이터만 포함하므로 성능 상 이점이 있으며, 필요 없는 객체 탐색을 예방할 수 있습니다.
쿼리 성능 모니터링 및 튜닝: 툴을 사용하여 JPA 쿼리가 생성하는 실제 SQL을 모니터링하고, 성능 상 병목이 없는지 확인하세요. 쿼리를 최적화하거나 인덱스를 추가해 성능을 개선할 수 있습니다.
추가적인 리소스
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
도메인 모델에서 관계와 규칙을 구분하는 방법
0
55
2
헥사고날 아키텍처에서의 배치, 시큐리티, 비동기 이벤트 처리는 어떻게 하나요?
0
126
2
어댑터에서 도메인에 직접 의존하는 경우에 대해
0
127
2
Member 도메인이 PasswordEncoder를 받는 구조 질문 있습니다.
0
114
2
MemberService와 EmailSender 책임 분리에 대한 질문
0
107
2
NonNullApi를 NullMarked로 대체하라고 합니다.
0
130
2
39. 문서와 코드 다듬기 updateInfo 테스트 질문 있습니다.
0
77
2
Repository Adapter 설계에 대해 피드백을 부탁드립니다
0
111
2
헥사고날 part2 강의 출시 예정일 문의 드립니다.
0
255
2
PT 문의사항
0
101
1
초기 어플리케이션 구동 시 compose.yml 파싱 오류
0
151
2
애플리케이션의 JPA 리턴과 도메인 모델
0
129
2
애그리거트 루트의 하위 도메인들의 depth가 깊어질 때 문의
0
136
2
페이징 처리를 해야한다면 어떻게 해야할까요?
0
193
2
애그리거트의 repository
0
119
2
Domain Expert가 정확히 어떤 역할을 하는 사람인가요?
0
236
1
회원 애플리케이션 서비스 테스트 (1)
0
107
2
정적 팩토리 메서드 관련 질문드립니다!
0
110
2
spotbug + @NonNullApi 로만 Null 방어가 될까요?
0
129
2
required 포트에 관해서
0
93
2
혹시 다음 편은 언제쯤 오픈할까요?
0
168
2
서비스 단위 테스트 코드 작성
0
98
2
domain 모듈에 entity를 정의한다고 했을때
0
101
2
여러 엔티티의 조합으로 리포트를 제공해야할 때
0
80
2





