정보기술 ·
Django 쿼리셋 중복 객체 문제, Exists 서브쿼리로 해결...성능 최적화 방법 제시
장고 웹 프레임워크에서 관계형 데이터 필터링 시 발생하는 중복 객체 문제 해결법이 공개됐다.
[한국정보기술신문] 장고(Django) 개발자 Johnny Metz가 1월 3일 자신의 블로그를 통해 장고 쿼리셋에서 중복 객체를 효과적으로 제거하는 방법을 공개했다. 장고 ORM을 사용하는 개발자들이 관계형 데이터베이스를 다룰 때 자주 겪는 중복 객체 문제에 대한 실용적인 해결책을 제시한 것이다.
장고는 웹 개발에 널리 사용되는 파이썬 기반 프레임워크로, ORM(Object-Relational Mapping) 기능을 통해 데이터베이스 쿼리를 추상화한다. 그러나 일대다 또는 다대다 관계를 통해 데이터를 필터링할 때 의도치 않게 중복된 객체가 반환되는 문제가 발생한다.
중복 발생 원인은 SQL JOIN
문제의 핵심은 장고가 관계를 탐색할 때 SQL JOIN을 수행한다는 점이다. 예를 들어 저자(Author)와 책(Book) 모델이 일대다 관계로 연결된 상황에서, 특정 조건을 만족하는 책을 쓴 저자를 찾으면 한 저자가 여러 권의 책을 쓴 경우 그 저자가 쿼리 결과에 중복으로 나타난다.
구체적으로 Alice라는 저자가 Book B와 Book C 두 권의 책을 썼고, 제목이 Book으로 시작하는 책을 쓴 저자를 찾는다면 Alice가 결과에 두 번 포함된다. JOIN 연산 과정에서 Alice의 정보가 각 책마다 한 번씩 포함되기 때문이다.
기존 해결법의 한계
가장 직관적인 해결 방법은 distinct() 메서드를 사용하는 것이다. 이 메서드는 중복을 제거하지만 모든 필드를 비교하기 때문에 JSONField나 TextField 같은 대용량 필드가 있을 경우 성능 저하가 심각하다.
PostgreSQL 데이터베이스를 사용하는 경우 distinct()에 특정 필드를 지정할 수 있다. 이는 id 같은 고유 필드만 비교하므로 성능이 개선되지만 정렬 제약이 있다. PostgreSQL은 DISTINCT ON 표현식과 초기 ORDER BY 표현식이 일치해야 하므로 다른 필드로 정렬하면 오류가 발생한다.
이를 우회하기 위해 서브쿼리를 중첩해서 사용할 수 있지만 코드가 복잡해지고 가독성이 떨어진다.
Exists 서브쿼리가 최선의 해결책
Metz는 Exists 서브쿼리를 사용하는 방법을 최선의 해결책으로 제시했다. 장고의 Exists와 OuterRef를 활용하면 깔끔하고 효율적으로 중복을 방지할 수 있다.
이 방식은 데이터베이스가 조건을 만족하는 첫 번째 행을 찾으면 즉시 평가를 중단하기 때문에 매우 효율적이다. 장고 공식 문서에 따르면 Exists는 많은 경우 서브쿼리보다 성능이 우수하다.
또한 distinct()와 달리 정렬 제약이 없어 어떤 필드로든 자유롭게 정렬할 수 있다. 코드의 의도도 명확하게 드러난다. 특정 조건을 만족하는 관련 객체를 가진 부모 객체를 찾는다는 의미가 직관적으로 표현된다.
무엇보다 PostgreSQL뿐 아니라 모든 데이터베이스에서 작동한다는 장점이 있다. distinct()의 특정 필드 지정 기능은 PostgreSQL 전용이지만 Exists 서브쿼리는 표준 SQL 기능이므로 데이터베이스 제약이 없다.
개발자들의 실무 활용 기대
장고는 전 세계적으로 수많은 웹 서비스에 사용되고 있으며, 인스타그램, 스포티파이, 유튜브 등 대형 플랫폼도 장고를 활용한다. ORM의 편리함 덕분에 SQL 작성 없이도 복잡한 쿼리를 수행할 수 있지만, 이러한 추상화 레이어가 때로는 예상치 못한 성능 문제를 야기한다.
Metz가 제시한 Exists 서브쿼리 패턴은 이런 문제를 해결하는 실용적인 방법으로, 특히 대용량 데이터를 다루는 서비스에서 성능 개선 효과가 클 것으로 예상된다. 관계형 데이터 필터링은 웹 개발에서 매우 흔한 작업이므로 이 기법은 장고 개발자들의 실무에 즉시 적용 가능하다.
한국정보기술신문 정보기술분과 유상헌 기자 news@kitpa.org