M365Con 2027 – 무료 온라인 컨퍼런스 · 11–22 Jan 2027 · 120 sessions→ m365con.net

블로그 · Mirko Peters

N+1 쿼리 문제, 이번에 확실히 해결하기

By ·

다시 찾아주셔서 감사합니다, 개발자 여러분! Entity Framework Core로 애플리케이션을 만들어 본 적이 있다면, 원인을 알 수 없어 머리를 긁적이게 만든 성능 병목을 한 번쯤 겪어 보셨을 것입니다. 개발 환경에서는 바로 실행되던 쿼리가 운영 환경에서는 왜 느려지는 걸까요? 대개 원인은 바로 눈앞에 숨어 있습니다. 바로 악명 높은 N+1 쿼리 문제입니다. 이번 글에서는 이 조용한 성능 저해 요소가 무엇을 일으키는지, 사용자가 눈치채기 전에 어떻게 찾아낼 수 있는지, 그리고 즉시 로딩, 프로젝션, 그리고 똑똑한 최적화 기법으로 어떻게 효과적으로 해결하는지 깊이 살펴보겠습니다.

Automatically translated with AI from the original post. Read the English original

▶ BLOGConquering the N+1 QueryProblem Once and For AllM365.FM
Titelbild: Conquering the N+1 Query Problem Once and For All

다시 찾아주셔서 감사합니다, 개발자 여러분! Entity Framework Core로 애플리케이션을 만들어 본 적이 있다면, 원인을 알 수 없어 머리를 긁적이게 만든 성능 병목을 한 번쯤 겪어 보셨을 것입니다. 개발 환경에서는 바로 실행되던 쿼리가 운영 환경에서는 왜 느려지는 걸까요? 대개 원인은 바로 눈앞에 숨어 있습니다. 바로 악명 높은 N+1 쿼리 문제입니다. 이번 글에서는 이 조용한 성능 저해 요소가 무엇을 일으키는지, 사용자가 눈치채기 전에 어떻게 찾아낼 수 있는지, 그리고 즉시 로딩, 프로젝션, 그리고 똑똑한 최적화 기법으로 어떻게 효과적으로 해결하는지 깊이 살펴보겠습니다.

본격적인 기술 내용에 들어가기 전에, 관련 팟캐스트 에피소드 Improve Entity Framework Core Query Performance도 꼭 확인해 보시기 바랍니다. 이 에피소드에서는 흔히 빠지는 Entity Framework 함정 몇 가지를 짚어 보고, 애플리케이션을 매우 빠르게 유지하기 위한 실용적인 조언을 나눕니다.

N+1 쿼리 문제 이해하기

N+1이란 무엇인가?

N+1 문제는 관련 데이터를 불러올 때 추가 쿼리가 많이 발생하는 방식으로 로드할 때 생깁니다. Entity Framework Core에서는 이 문제가 반복문 안에서 관련 데이터에 접근할 때 자주 나타납니다. 예를 들어 주문 100건을 가져온다고 해 보겠습니다. 그다음 각 주문에서 customer 속성에 접근합니다. 그러면 전체 주문을 가져오는 쿼리 1개와, 고객마다 실행되는 쿼리 100개가 추가로 발생합니다. 결과적으로 쿼리 한 개면 충분했을 작업이 101개의 쿼리가 됩니다. 이로 인해 애플리케이션이 느려지며, 데이터가 많을수록 그 영향은 더 커집니다. 데이터가 적을 때는 이 문제를 알아채기 어렵지만, 운영 환경에서는 큰 문제가 됩니다.

운영 환경에서 N+1 쿼리 감지하기

애플리케이션이 데이터베이스로 보내는 쿼리 수를 살펴보면 N+1 문제를 발견할 수 있습니다. 감지하는 방법은 다음과 같습니다.

  • 실행되는 모든 쿼리를 확인할 수 있도록 SQL 로깅을 활성화합니다.
  • 프로파일링 도구를 사용해 쿼리 성능을 점검합니다.
  • 쿼리 수를 모니터링합니다. 결과가 적은데도 쿼리가 많이 보인다면 N+1 문제가 있을 가능성이 높습니다.
  • 운영 환경에서 사용하는 것과 비슷한 대용량 데이터 세트로 애플리케이션을 테스트합니다.

팁: 새 쿼리를 추가하거나 관련 데이터를 로드하는 방식을 변경한 뒤에는 항상 로그와 프로파일링 도구를 확인하세요.

즉시 로딩(Eager Loading)으로 N+1 해결하기

Entity Framework Core에서는 즉시 로딩(eager loading)을 사용해 N+1 문제를 해결할 수 있습니다. 즉시 로딩을 사용하면 필요한 데이터를 한 번의 쿼리로 모두 가져올 수 있습니다. 이를 위해 Include 메서드를 사용합니다. 그러면 EF가 관련 테이블을 조인해 모든 데이터를 한 번에 가져옵니다. 예를 들어 작성자와 그들의 도서를 가져오고 싶다면 다음과 같이 작성할 수 있습니다.

var authorsWithBooks = context.Authors.Include(a => a.Books).ToList();

이 코드는 작성자와 도서를 조인하는 단일 쿼리를 실행합니다. 애플리케이션은 필요한 데이터를 한 번에 모두 받습니다. 즉시 로딩은 쿼리 수를 줄이고 애플리케이션을 더 빠르게 만듭니다. 관련 데이터가 바로 필요하다는 것을 알고 있을 때 즉시 로딩을 사용하는 것이 좋습니다. 이렇게 하면 속도 저하를 피하고 애플리케이션을 원활하게 유지할 수 있습니다.

성능을 위한 추적(Tracking)과 비추적(No-Tracking) 비교

Entity Framework Core는 로드하는 모든 엔터티를 기본적으로 추적합니다. 추적은 EF가 무엇이 변경되었는지 알 수 있도록 도와줍니다. 하지만 추적은 애플리케이션을 느리게 만들 수 있습니다. 데이터 세트가 클수록 더 느려집니다. 메모리 사용량도 늘어날 수 있습니다. 추적이 켜져 있으면 쿼리 실행 시간도 길어질 수 있습니다. AsNoTracking을 사용하면 쿼리가 빨라지고 메모리도 덜 사용합니다. 그 결과 애플리케이션이 더 빠르게 응답하고 동시에 더 많은 사용자를 처리할 수 있습니다.

팁: 추적을 끄면 쿼리가 더 빨리 실행됩니다. 데이터를 읽기만 하면 되는 경우에 가장 적합합니다.

데이터를 업데이트하거나 삭제하지 않으려면 asnotracking을 사용하십시오. 대부분의 웹 API는 데이터를 읽기만 합니다. 엔드포인트의 약 70~80%는 정보만 표시합니다. 이런 경우에는 asnotracking을 사용할 때 더 좋은 성능을 얻을 수 있습니다. 다음은 이를 사용하기 좋은 몇 가지 상황입니다:

  • 데이터만 표시하는 보고서와 대시보드
  • 표시할 목록이나 상세 정보를 반환하는 API 응답
  • 데이터베이스에서 읽기만 하는 검색 또는 필터 엔드포인트

페이지 나누기와 대용량 데이터 집합

EF를 대량의 데이터와 함께 사용하면 심각한 문제가 생길 수 있습니다. 한 번에 너무 많은 데이터를 불러오면 애플리케이션이 느려지거나 심지어 충돌할 수도 있습니다. 다음과 같은 상황이 발생할 수 있습니다:

  • 애플리케이션이 메모리를 과도하게 사용하여 메모리가 부족해질 수 있습니다.
  • 데이터가 너무 많아 결과를 얻는 데 오랜 시간이 걸릴 수 있습니다.
  • 한 번에 너무 많은 데이터를 불러오려고 하면 데이터베이스가 작동을 멈출 수 있습니다.

페이지 나누기를 사용하면 이러한 문제를 해결할 수 있습니다. 페이지 나누기는 데이터를 더 작은 단위로 나눕니다. 이렇게 하면 애플리케이션이 더 빨라지고 사용하기도 편해집니다. EF에서는 이를 위해 Skip() 및 Take()를 사용할 수 있습니다. Skip은 일부 레코드를 건너뛰고, Take는 원하는 개수만큼만 가져옵니다. 이를 통해 대용량 데이터를 효율적으로 처리하고 애플리케이션을 안정적으로 실행할 수 있습니다.

대량 작업과 일괄 처리

대량 작업을 사용하면 데이터베이스의 작업 속도를 높일 수 있습니다. 이 방법들을 사용하면 많은 레코드를 한 번에 변경할 수 있습니다. 작은 작업에는 표준 EF 메서드로도 충분합니다. 하지만 데이터가 많아지면 속도가 느려집니다. 대량 작업을 사용하면 수천 개의 행을 빠르게 삽입, 업데이트 또는 삭제할 수 있습니다. CSV 파일에서 데이터를 가져올 때 사용할 수 있습니다. 레코드를 동기화하거나 야간 업데이트를 수행할 때도 도움이 됩니다.

일괄 처리를 사용하면 작업을 그룹화하여 애플리케이션을 더 빠르게 실행할 수 있습니다. 일괄 업데이트에서는 UpdateRange()를 사용하여 많은 항목을 한 번에 변경할 수 있습니다. SaveChanges()는 너무 자주 호출하지 않도록 하십시오. 호출할 때마다 데이터가 데이터베이스로 전송됩니다. EF Core 7에는 대량 변경을 위한 ExecuteUpdate 및 ExecuteDelete와 같은 새로운 메서드가 추가되었습니다.

결론

Entity Framework Core 최적화는 추측에 의존할 필요가 없습니다. N+1 쿼리 문제가 어떻게 발생하는지 이해하고, SQL 생성을 모니터링하는 도구를 활용하며, 즉시 로딩과 no-tracking 쿼리를 사용하고, 페이지 나누기와 대량 작업을 적용하면 느린 데이터 계층을 고성능 엔진으로 바꿀 수 있습니다. 항상 성능 기준선을 측정하고, 실제에 가까운 조건에서 쿼리를 철저히 테스트하십시오.

이 전략들을 더 깊이 알아보려면 Improve Entity Framework Core Query Performance 관련 에피소드를 꼭 들어 보십시오. 이러한 모범 사례를 적용하면 애플리케이션에 숨은 병목 현상을 피하고 사용자에게 원활한 경험을 제공할 수 있습니다!

Entities in this article

Entity Framework Core

RSS