서비스를 운영하다 보면 늘 예상치 못한 문제가 튀어나오곤 합니다. 이번엔 문서 및 카테고리 이동 기능이 발목을 잡았습니다.
실제로 고객들이 자주 사용하는 기능이여서, 해당 기능으로 고객들은 작성해놓은 문서를 새로운 카테고리나, 폴더등으로 옮기곤 합니다. 그런데 고객들의 사용 빈도만큼이나 "너무 느려요", "이동기능 로딩만 5초 넘게 걸려요" 같은 불만도 많았습니다.
그래서 실제로 테스트를 해보니 응답 시간이 평균 6초에 달했습니다. 사용자는 6초 동안 화면에서 로딩 메시지를 봐야 하고, 작업 도중 페이지를 닫아버리는 경우도 있었습니다.
따라서 해당 기능에 대해 어떤 부분에서 성능 문제가 발생했는지, 파악하고 개선하자는 목표로 작업을 진행했습니다.
6초의 로딩 시간, 문제는 반복되는 save()호출이었다
문제의 원인을 파악하기 위해 해당 코드 부분을 살펴보니 아래와 같은 로직이 작성되어 있었습니다.
개선 전 코드
for document in documents:
document.position = new_position
document.save()
이 코드는 각 문서를 하나씩 업데이트하고, 매번 save() 메서드를 호출합니다.
save() 메서드는 내부적으로 데이터베이스와 통신하기 때문에 호출할 때마다 데이터베이스 쿼리가 발생합니다.
즉, 문서가 많아질수록 요청 속도가 기하급수적으로 느려지는 구조였던 거죠.
예를 들어 문서가 95개라면, 데이터베이스에 95번의 쿼리가 발생합니다.
이건 단순히 서버 리소스를 낭비하는 것뿐 아니라, 응답 시간을 길게 만드는 주범이었습니다.
해결 방법: bulk_update로 쿼리 최적화
문서를 하나씩 저장하는 대신, Django에서 제공하는 bulk_update()
메서드를 사용해 데이터를 한 번에 업데이트하도록 코드를 변경했습니다.
bulk_update는 한 번의 데이터베이스 쿼리로 여러 개의 데이터를 업데이트할 수 있어 다수의 데이터를 업데이트 해야할 때 성능 향상에 유용 합니다.
개선 후 코드
from django.db import transaction
# 모든 문서의 position 값을 한꺼번에 변경
for document in documents:
document.position = new_position
# bulk_update로 한 번에 저장
with transaction.atomic():
Document.objects.bulk_update(documents, ['position'])
이 코드는 문서를 반복하며 필요한 값만 변경한 뒤, 한 번의 쿼리로 저장합니다.
문서가 95개든 1,000개든 한 번의 쿼리로 처리되기 때문에 성능이 크게 개선됩니다.
결과는? 속도가 30배 빨라졌습니다.
코드를 수정한 후 테스트를 진행해보았습니다.
결과는 정말 놀라웠습니다.
테스트 환경 : 스테이징(실제 서비스 환경과 동일한, 인프라를 구축한 환경)
개선 전: 평균 응답 시간 6초
개선 후: 평균 응답 시간 0.2초
성능 향상: 약 30배
이제 고객들이 긴 로딩 메시지를 볼 필요 없이 빠르게 작업을 이어갈 수 있게 되었고, 서버 자원의 낭비도 크게 줄어들었습니다.
정리하며: 배운 점
이 경험을 통해 한 가지 확실히 배운 점은, “데이터베이스와의 통신은 꼭 필요한 만큼만 하자” 라는 기본 원칙입니다.
단순히 반복문 내에서 저장 메서드를 호출하는 코드가 성능에 얼마나 큰 영향을 미칠 수 있는지 실무에서 직접 체감한 사례였어요.
특히, Django ORM을 사용한다면 bulk_update, bulk_create 같은 쿼리 최적화 기능을 적극적으로 활용하는 것이 중요하다는 것을 깨달았습니다.
'프로그래밍' 카테고리의 다른 글
번역 API 개선기: 번역 가능 언어 추가 요청을 받으며 배운 점 (0) | 2024.12.03 |
---|---|
시스템 로그 페이지 조회 성능 최적화 (0) | 2024.04.04 |
F-Lab 자바 백엔드 코스 멘토링 후기 (1) | 2023.12.03 |
MSW? (3) | 2023.11.23 |
REST API란? (0) | 2023.09.06 |