느린 직렬화(Serialization) 과정 최적화하기
직렬화 과정의 병목을 찾아내고 해결하는 최적화 기법들로, 데이터베이스 쿼리는 빠르지만 JSON 변환 과정에서 발생하는 성능 저하를 해결
N+1 쿼리 해결
문제: 관련 객체 접근 시 매번 추가 쿼리 발생
Post목록에서 각각의author.username접근 시 N+1 문제 발생
해결: select_related와 prefetch_related 활용
select_related: ForeignKey, OneToOneField 관계 (JOIN 사용)prefetch_related: 역방향 ForeignKey, ManyToManyField 관계
# 개선 전
queryset = Post.objects.all() # N+1 발생
# 개선 후
queryset = Post.objects.select_related('author').all() # 2번 쿼리로 해결SerializerMethodField 최적화
문제: 커스텀 필드에서 매번 무거운 연산 실행
해결책 1: DB 레벨에서 미리 계산 (annotate)
# View에서 미리 계산
queryset = Post.objects.annotate(likes_count=Count('likes'))
# Serializer에서 일반 필드로 사용
likes_count = serializers.IntegerField(read_only=True)해결책 2: 인스턴스 레벨 캐싱
def get_complex_data(self, obj):
if hasattr(obj, '_complex_data'):
return obj._complex_data
result = self._calculate_complex_data(obj)
setattr(obj, '_complex_data', result)
return result목록/상세 Serializer 분리
목적: 목록 API에서 불필요한 데이터 제거로 응답 속도 향상
class PostListSerializer(serializers.ModelSerializer):
# 목록에 필요한 최소 필드만
class Meta:
fields = ['id', 'title', 'author_username']
class PostDetailSerializer(serializers.ModelSerializer):
# 상세 정보 포함
class Meta:
fields = ['id', 'title', 'content', 'author', 'tags', 'files']
# ViewSet에서 action별 Serializer 분리
def get_serializer_class(self):
if self.action == 'list':
return PostListSerializer
return PostDetailSerializervalues()를 이용한 초고속 직렬화
목적: 대용량 데이터의 읽기 전용 API에서 극한 성능 필요 시
장점: 모델 인스턴스화 오버헤드 제거로 월등한 속도 단점: ModelSerializer 편의기능 사용 불가, 유지보수 어려움
# 기본 Serializer 사용
class UltraFastPostSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField()
author__username = serializers.CharField()
# values()로 dict 형태 데이터 추출
queryset = Post.objects.select_related('author').values(
'id', 'title', 'author__username'
)최적화 우선순위
select_related/prefetch_related: 모든 직렬화의 기본, N+1 의심 시 최우선 적용
annotate: 집계/통계 데이터 필요 시
Serializer 분리: 목록과 상세의 필요 데이터가 다를 때
values() + Serializer: 대용량 읽기 전용 API에서 극한 성능 필요 시
Last updated
