작성
·
1.1K
1
안녕하세요. 강사님.
항상 친절한 답변 감사합니다^^
다대다 관계에서 효율적인 조회 쿼리 작성에 대해 질문이 있습니다.
현재 저는 다음과 같은 엔티티 클래스를 개발했습니다.^^
다대다 관계를 가지는 세개의 엔티티를 지니고있고 TagProjectOutput Entity는 다대다 연결을 엔티티로 두 엔티티를 필드를 가지고있습니다. 또한, Tag, ProjectOutput 엔티티와 다대일 단방향 매핑 연결을 했습니다.
ProjectOuutput 목록을 조회할 때 가지고 있는 Tag 정보도 동시에 조회하는 메소드를 개발하는 중에 고민되는 부분이 있어서요^^
ProjectOutput과 ProjectOutput이 지닌 Tag 목록을 보여주는 Dto를 만들어 List로 뽑아내야 하는 상황인데요.
현재는 다대다 연결 엔티티인 TagProjectOutput List를 뽑아와 Key는 ProjectOutput, Value는 List<Tag>인 Map으로 가공 후 Service Layer에서 Dto로 변환하는 코드를 사용하고 있습니다.
/**
* 다대다 연결 엔티티인 TagProjectOutput List를 뽑아와 Key는 ProjectOutput, Value는 List<Tag>인 Map으로 가공 후 Service Layer에서 Dto로 변환
*/
@Override
public LinkedHashMap<ProjectOutput, List<Tag>> findMapByTagNameList(TagProjectOutputSearchCondition condition) {
// 다대다 연결 엔티티 목록 조회
List<TagProjectOutput> fetched = queryFactory.selectFrom(tagProjectOutput)
.where(tagProjectOutput.dataStatusCode.eq(DataStatusCode.USE), tagNamesEq(condition.getTagNames()))
.join(tagProjectOutput.projectOutput, projectOutput).fetchJoin().join(tagProjectOutput.tag, tag)
.fetchJoin().fetch();
// Key : ProjectOutput, Value : Tag List 형식의 LinkedHashMap 자료구조로 가공
LinkedHashMap<ProjectOutput, List<Tag>> map = new LinkedHashMap<>();
for (TagProjectOutput tagProjectOutput : fetched) {
ProjectOutput projectOutput = tagProjectOutput.getProjectOutput();
if (map.containsKey(projectOutput)) {
List<Tag> tags = map.get(projectOutput);
tags.add(tagProjectOutput.getTag());
map.replace(projectOutput, tags);
} else {
List<Tag> tags = new ArrayList<>();
tags.add(tagProjectOutput.getTag());
map.put(projectOutput, tags);
}
}
return map;
}
/**
* Tag명 리스트 조건으로 TagProjectOutput를 조회하기 위한 동적 쿼리 메소드
*/
private BooleanExpression tagNamesEq(List<String> tagNames) {
return isEmpty(tagNames) ? null : tagProjectOutput.tag.tagName.in(tagNames);
}
위와 같은 메소드를 Querydsl로 작성해 사용 중인데, 지금은 데이터가 얼마 되지않아 큰 문제는 없지만, 나중에 List를 가져와 다시 한 번, LinkedHashMap 자료 구조로 변환하는 과정이 성능에 문제가 될까 염려가 되네요.
TagProjectOutput과 ProjectOutput 간 양방향 매핑은 ProjectOutput에 다른 컬렉션 필드가 추가될 수 있어서 왠만하면 단방향 매핑인 상태로 개발해야할 것 같은데, 추가적으로 고려해야하는 부분이 있을까요?
답변 4
1
1
wxxsox lee님 테스트까지 해보셨군요^^
2.838568246초 / 1000000 = 0.0000028이네요. 결국 1ms도 안되는 매우 작은 숫자입니다.
이번에 테스트를 해보셔서 아시겠지만, 성능에서는 암달의 법칙이라는 것이 중요합니다.
예를 들어서 API 호출에서 List를 LinkedHashMap로 바꾸는데 0.0000028초가 걸리고 + 쿼리 조회 결과가 0.1초(100ms)가 걸린다고 했을 때 전체 시간은 0.1000028초가 걸리는 것입니다. 그렇다면 애플리케이션 메모리 호출은 아무리 최적화를 해도 도움이 안되겠지요^^
애플리케이션은 대부분 외부 네트워크 통신이 이슈가 되지 이렇게 메모리에서 몇백건 돌린다고 이슈가 되는 일은 거의 없습니다^^
보통 내부 메서드 호출 한번 하는 것 보다 외부 네트워트 한번 통신하는 것이 5~10만배 정도 더 오래 걸리는 작업니다.
도움이 되셨길 바래요.
0
항상 친절하신 답변에 이렇게 경험해볼 기회까지 주신다니 감사합니다^^
질문 주신대로 100개의 List를 데이터를 LinkedHashMap으로 변환하는 for 루프를 1,000,000번 돌리는 테스트 코드를 작성했습니다.
@Test
@DisplayName("100개의 List 데이터를 LinkedHashMap으로 변환하는 시간을 측정")
void ListToLinkedHashMapTest() {
// given
// 100개의 TagProjectOutput 데이터를 가져오기
List<TagProjectOutput> tagProjectOutputs = tagProjectOutputRepository.findAll();
assertThat(tagProjectOutputs.size()).isEqualTo(100);
// when
// 100개의 List를 LinkedHashMap으로 1,000,000번 변환하는 수행 시간 측정
StopWatch stopWatch = new StopWatch();
stopWatch.start();
for (int i = 0; i < 1000000; i++) {
LinkedHashMap<ProjectOutput, List<Tag>> map = new LinkedHashMap<>();
for (TagProjectOutput tagProjectOutput : tagProjectOutputs) {
ProjectOutput projectOutput = tagProjectOutput.getProjectOutput();
if (map.containsKey(projectOutput)) {
List<Tag> tags = map.get(projectOutput);
tags.add(tagProjectOutput.getTag());
map.replace(projectOutput, tags);
} else {
List<Tag> tags = new ArrayList<>();
tags.add(tagProjectOutput.getTag());
map.put(projectOutput, tags);
}
}
}
stopWatch.stop();
// then
System.out.println("수행 시간 = " + stopWatch.getTotalTimeSeconds());
System.out.println(stopWatch.prettyPrint());
}
StopWatch를 이용해서 시간을 측정했고 그 결과 제 로컬 환경에서 1,000,000번의 루프를 수행하는데
2.838568246초 정도 측정되네요. 2.838568246를 1000000로 나누면 매우 적은 시간이 들 것으로 예상이 되는데..
이정도 시간은 문제가 없다고 생각해도 괜찮을까요?^^ 관련 경험이 없어 판단하기가 어렵네요.
감사합니다.
0
안녕하세요. wxxsox lee님 좋은 질문입니다.
먼저 List를 가져와 다시 한 번, LinkedHashMap 자료 구조로 변환하는 것에 대한 성능 문제 걱정이 있으시군요^^
제가 답을 바로 드릴 수 있지만! 그러면 wxxsox lee님의 실력이 늘지 않으니 제가 반대로 질문을 드릴께요.
성능 문제에 대한 걱정은 사실 성능 테스트를 해보아야 합니다^^! 너무 고민하기 보다는 코드로 테스트를 딱! 해보고 테스트 결과를 얻어봐야 합니다. 그래야 본인 실력이 쌓이거든요.
예를 들어서 본인 애플리케이션 요구사항 중에 이 API는 초당 100번 정도 호출되어도 문제가 없어야 한다 라는 성능 요구사항이 있어야 겠지요. 그러면 데이터를 한번에 100개씩 있다고 가정하고 1초에 100번 해당 메서드를 호출해도 이 부분에 문제가 없는지 검증해보면 되겠지요?
지금 검증하고 싶은 부분은 단순히 List를 LinkedHashMap으로 변환하는 것이니 100개의 List 데이터를 LinkedHashMap으로 변환 하는데, 시간이 얼마정도 소요되는지 측정해보시면 좋습니다^^! 단순히 for 루프로 1000000번 정도 돌리고, 결과에 시간/1000000을 하시면 한번 돌리는데 걸리는 시간을 대략 알 수 있습니다. 그리고 그 결과를 여기 답글에 남겨주세요.
두번째로 연관관계 매핑은 우선 최대한 단뱡향으로 해보고, 꼭 양뱡향이 필요하면 그때 추가해도 됩니다^^
그럼 답글 기다릴께요.