Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[♻️ refactor] N+1 문제 해결 및 HomeServiceImpl 성능 개선 #178

Merged
merged 3 commits into from
Dec 11, 2024

Conversation

junggyo1020
Copy link
Contributor

@junggyo1020 junggyo1020 commented Dec 7, 2024

📄 Work Description

✨ Problem

  • 현재 코드는 scrapRepository를 각 InternshipAnnouncement마다 호출함으로써 데이터베이스 접근이 비효율적으로 이루어지고 있어 해당 부분을 리펙토링하였습니다. (N+1 문제 발생)

🔧  Code Refactoring

  • QueryDSL Tuple을 활용해 [홈 - 나에게 딱 맞는 공고 API]의 공고 조회 로직 성능을 크게 개선했습니다.
    • Scrap 정보를 한 번에 조회 하도록 변경했습니다.
    • Enum 타입으로 호출하여 컴파일 시 타입체크가 가능하도록 하였고, Color Enum의 내부 메서드를 통해 색상값을 불러오도록 수정했습니다.
    • 불필요한 메서드를 제거하고, isScrapped 로직을 좀 더 단순하게 수정했습니다.
image

🧪 Performance Test & 📄 Report

  • 변경 전/후의 query 로직에 대한 비교를 위해 JMeter 도구를 활용해 성능 테스트를 진행했습니다.
  • 대표적인 도구인 k6, nGrinder, JMeter 중 HTML 형식의 보고서가 잘 정리되어 있고, 레퍼런스의 규모와 사용성 측면에서 상당히 이점이 존재하는 JMeter를 사용하여 성능 테스트를 진행했습니다.

Test Scenario

  • Number Of Threads (users) : 100 -> 100명의 사용자가 동시에 요청을 보냄
  • Ramp-up period (seconds) : 1 -> 부하테스트를 진행할 것이기 때문에 쓰레드 수들을 나눠서 테스트 하지 않음
  • Loop Count : Infinite (단, request가 총 50000회 응답할때까지 반복)

Setting

image
  • Protocol[http] -> http
  • Server Name or IP -> localhost 혹은 배포된 IP 입력 (본인은 로컬에서 테스트했기 때문에 localhost로 설정함)
  • Port Number(포트 번호) : 8080 (본인은 로컬에서 테스트했기 때문에 8080으로 설정함)
  • HTTP Request -> CREATE/GET/PUT/PATCH/DELETE 등 선택
  • Path -> /api/v1/home?sortBy=deadlineSoon&startYear=2024&startMonth=12 ([홈 - 나에게 딱 맞는 공고 API]를 기준으로 설정함)
image
  • HTTP Header의 Name 에는 Authorization, Value 에는 Bearer 토큰 입력

Result (개선 전)

image
  • number of Samples(샘플 수) : 50831
  • Average(평균 응답 속도) : 75ms
  • Error %(오류율) : 0.20%
  • Throughput(처리량) : 1292.8/sec
  • Reseived KB/sec(클라이언트(JMeter)가 서버로부터 초당 수신하는 데이터의 양) : 6814.23KB/sec
  • Sent KB/sec(클라이언트(JMeter)가 서버로 초당 전송하는 데이터의 양) : 461.17KB/sec

Result (개선 후)

image
  • number of Samples(샘플 수) : 50600
  • Average(평균 응답 속도) : 29ms
  • Error %(오류율) : 0.20% (동일)
  • Throughput(처리량) : 3272.5/sec
  • Reseived KB/sec(클라이언트(JMeter)가 서버로부터 초당 수신하는 데이터의 양) : 17249.02KB/sec
  • Sent KB/sec(클라이언트(JMeter)가 서버로 초당 전송하는 데이터의 양) : 1167.37KB/sec

최종 Result

Average(평균 응답 속도)

이전: 75ms
개선: 29ms
개선율 = ((75ms - 29ms) / 75ms) * 100 ≈ 61.3% 단축
→ 평균 응답 시간이 약 61.3% 개선(낮을수록 좋으므로 단축률로 표시)

Error %(오류율) :

이전: 0.20%
개선: 0.20%
개선율 = ((0.20% - 0.20%) / 0.20%) * 100 = 0%
→ 오류율 변화 없음

Throughput(처리량) :

이전: 1292.8/sec
개선: 3272.5/sec
개선율 = ((3272.5 - 1292.8) / 1292.8) * 100 ≈ 153% 증가
→ 처리량 약 153% 향상 (약 2.5배)

Received KB/sec:

이전: 6814.23KB/sec
개선: 17249.02KB/sec
개선율 = ((17249.02 - 6814.23) / 6814.23) * 100 ≈ 153% 증가
→ 수신 데이터량 약 153% 증가 (약 2.5배)

Sent KB/sec:

이전: 461.17KB/sec
개선: 1167.37KB/sec
개선율 = ((1167.37 - 461.17) / 461.17) * 100 ≈ 153% 증가
→ 전송 데이터량 약 153% 증가

⚙️ ISSUE

🔗 Reference

- 변경 전/후의 query 로직에 대한 부하테스트를 진행하기 위해 JMeter 활용했습니다.
- 대표적인 도구인 k6, nGrinder, JMeter 중 JMeter를 선택한 이유는 HTML 형식의 보고서가 잘 정리되어 있고, 레퍼런스의 규모와 사용성 측면을 고려했습니다.
- Scrap 정보를 한 번에 조회 하도록 변경 (QueryDSL Tuple 활용)
- Enum Color의 내부 메서드를 활용해서 색상값을 불러오도록 수정
- 불필요한 메서드를 제거하고, isScrapped 로직을 좀 더 단순하게 수정
@junggyo1020 junggyo1020 added ♻️ refactor 코드 리팩토링 ex) 형식변경 🐶정교🐶 labels Dec 7, 2024
@junggyo1020 junggyo1020 self-assigned this Dec 7, 2024
@junggyo1020 junggyo1020 linked an issue Dec 7, 2024 that may be closed by this pull request
4 tasks
@junggyo1020 junggyo1020 changed the base branch from main to develop December 7, 2024 10:52
@jsoonworld
Copy link
Member

안녕하세요! 😊

리팩토링에 대한 상세한 설명과 성능 테스트 결과를 포함한 PR 정말 잘 봤습니다. 몇 가지 확인 사항과 함께 긍정적인 피드백을 드리고 싶습니다.


👍 잘된 점

  1. N+1 문제 해결
    findFilteredInternshipsWithScrapInfo 메서드를 통해 Scrap 정보를 한 번에 조회하도록 변경하면서 성능 개선이 잘 이루어진 점이 돋보입니다. 불필요한 데이터베이스 호출을 줄이고, 필요한 데이터만 효율적으로 가져오게 된 부분이 정말 좋습니다.

  2. 성능 개선 명확화
    JMeter를 활용한 성능 테스트와 결과 비교를 통해 변경 사항의 효과를 명확히 보여주신 점이 훌륭합니다. 평균 응답 속도가 61.3% 단축되고, 처리량이 약 2.5배 증가한 점은 성능 개선의 큰 성과라고 생각합니다.

  3. 코드 단순화
    isScrapped 로직 단순화와 Enum 타입 호출을 통해 코드 가독성과 안정성을 높인 점이 인상적입니다.


💡 추가 제안

Error Handling 개선
scrapId != null 로 Scrap 여부를 확인하는 로직이 단순하고 좋지만, 향후 확장성을 고려해 별도의 메서드로 분리하면 유지보수에 유리할 수 있습니다.


전반적으로 효율적인 리팩토링과 명확한 성능 개선을 보여주신 점이 정말 인상적입니다. 고생 많으셨습니다!

@junggyo1020 junggyo1020 merged commit e413bf1 into develop Dec 11, 2024
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
♻️ refactor 코드 리팩토링 ex) 형식변경 🐶정교🐶
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[♻️ refactor] N+1 문제 해결 및 HomeServiceImpl 성능 개선
2 participants