0Pricing
C# Academy · 강의

즉시, 지연, 명시적 로딩

Include, ThenInclude, 지연 로딩 프록시, 명시적 Load 호출을 사용해 관련 데이터가 로드되는 시점을 제어합니다.

즉시, 지연, 명시적 로딩은(는) CoddyKit의 무료 C# Academy 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 C# Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. C# Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

세 가지 로딩 전략

EF Core는 관련 데이터를 로드하는 세 가지 전략을 제공합니다. 즉시 로딩(쿼리와 함께 로드), 지연 로딩(접근할 때 로드), 명시적 로딩(필요할 때 로드)입니다. 적절한 전략을 선택하면 N+1 문제와 불필요한 데이터베이스 왕복을 피할 수 있습니다.

Include를 사용한 즉시 로딩

즉시 로딩은 Include()를 사용하여 같은 데이터베이스 조회에서 관련 데이터를 JOIN으로 함께 로드합니다. 가장 예측하기 쉬운 전략이며 N+1 문제를 방지합니다.

// Single query with JOIN
var blogs = await _db.Blogs
    .Include(b => b.Posts)
    .Where(b => b.IsActive)
    .ToListAsync();

// SQL: SELECT * FROM Blogs LEFT JOIN Posts ON ...

중첩 관계를 위한 ThenInclude

ThenInclude는 탐색 그래프를 더 깊은 수준까지 연결합니다. 한 번의 쿼리로 여러 수준의 데이터를 로드할 수 있습니다.

var orders = await _db.Orders
    .Include(o => o.Customer)
    .Include(o => o.Lines)
        .ThenInclude(l => l.Product)
            .ThenInclude(p => p.Category)
    .Where(o => o.Status == OrderStatus.Pending)
    .ToListAsync();

필터링된 Include

EF Core 5 이상은 Include 내부에서 관련 데이터를 필터링하는 기능을 지원합니다. 별도의 쿼리 없이 자식 엔터티의 일부만 로드할 수 있습니다.

// Load only published posts for each blog
var blogs = await _db.Blogs
    .Include(b => b.Posts.Where(p => p.IsPublished)
                         .OrderByDescending(p => p.PublishedAt)
                         .Take(5))
    .ToListAsync();

지연 로딩 설정하기

지연 로딩은 탐색 속성에 처음 접근할 때 해당 속성을 자동으로 로드합니다. Microsoft.EntityFrameworkCore.Proxies 패키지를 설치하고 UseLazyLoadingProxies()를 호출하여 활성화합니다.

// 1. Install package: dotnet add package Microsoft.EntityFrameworkCore.Proxies
// 2. Enable in DbContext setup:
builder.Services.AddDbContext<AppDbContext>(opt =>
    opt.UseSqlite(connStr)
       .UseLazyLoadingProxies());

// 3. Mark navigation properties as virtual:
public class Blog
{
    public int Id { get; set; }
    public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
}

지연 로딩 실제 사용(그리고 N+1 함정)

지연 로딩은 편리하지만 반복문에서는 위험합니다. 각 접근마다 새로운 데이터베이스 조회가 실행되어 N+1 문제가 발생하기 때문입니다. 목록을 처리하는 상황에서는 항상 즉시 로딩을 우선 사용하십시오.

var blogs = await _db.Blogs.ToListAsync(); // 1 query

foreach (var blog in blogs)
{
    // BAD: each access fires a new query!
    var count = blog.Posts.Count; // N queries
    Console.WriteLine(count);
}

// FIX: Use eager loading
var blogs2 = await _db.Blogs.Include(b => b.Posts).ToListAsync();

명시적 로딩

명시적 로딩을 사용하면 Entry().Collection().LoadAsync() 또는 Entry().Reference().LoadAsync()로 필요할 때 탐색 속성을 로드할 수 있습니다. 관련 데이터가 조건부로 필요할 때 유용합니다.

var blog = await _db.Blogs.FindAsync(1); // no posts loaded

// Load posts only when needed
if (needsPosts)
{
    await _db.Entry(blog!)
             .Collection(b => b.Posts)
             .LoadAsync();
}

// Load single reference
var post = await _db.Posts.FindAsync(99);
await _db.Entry(post!)
         .Reference(p => p.Blog)
         .LoadAsync();

조회 필터를 사용하는 명시적 로딩

LoadAsync()를 호출하기 전에 .Query()를 사용하여 명시적 로딩에 필터를 적용할 수 있습니다. 이렇게 하면 반환되는 데이터의 양을 줄일 수 있습니다.

var blog = await _db.Blogs.FindAsync(1);

// Only load posts published this year
await _db.Entry(blog!)
         .Collection(b => b.Posts)
         .Query()
         .Where(p => p.PublishedAt.Year == DateTime.UtcNow.Year)
         .LoadAsync();

Console.WriteLine(blog!.Posts.Count);

대규모 Include를 위한 분할 쿼리

하나의 쿼리에서 여러 컬렉션을 로드하면 카테시안 폭발(행이 곱셈처럼 늘어나는 현상)이 발생합니다. 컬렉션별로 별도의 쿼리를 실행하고 메모리에서 결합하려면 AsSplitQuery()를 사용하십시오.

var orders = await _db.Orders
    .Include(o => o.Lines)
    .Include(o => o.Shipments)
    .AsSplitQuery()  // 3 separate SELECTs instead of one giant JOIN
    .ToListAsync();

// Or configure globally:
opt.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);

과도한 데이터 조회를 피하는 프로젝션

모든 관련 데이터와 함께 전체 엔터티를 로드하는 대신 Select를 사용하여 DTO로 프로젝션하십시오. 이렇게 하면 최적화된 단일 쿼리에서 필요한 데이터만 정확히 가져올 수 있습니다.

var summaries = await _db.Blogs
    .Select(b => new BlogSummaryDto
    {
        Name      = b.Name,
        PostCount = b.Posts.Count(p => p.IsPublished),
        LatestPost = b.Posts
            .Where(p => p.IsPublished)
            .OrderByDescending(p => p.PublishedAt)
            .Select(p => p.Title)
            .FirstOrDefault()
    })
    .ToListAsync();
// No Include needed - translated to subqueries

실전: 올바른 전략 선택

실제 개발에서는 다음 결정 가이드를 사용하십시오:

  • 즉시 로딩 + Include: 관련 데이터가 항상 필요할 때, 목록 페이지
  • 프로젝션: 읽기 전용 뷰, API 응답 — 가장 효율적
  • 명시적 로딩: 앱 로직에 따라 조건부로 로드할 때
  • 지연 로딩: 웹 앱에서는 피하고, 데이터 집합이 작은 데스크톱 또는 스크립팅 환경에서만 사용

빠른 확인

지연 로딩에서 N+1 문제란 무엇입니까?

복습: 로딩 전략

핵심 요점:

  • 즉시 로딩(Include): 한 번의 쿼리로 데이터를 로드 — 액세스 패턴을 예측할 수 있을 때 적합
  • 필터링된 Include: 자식 데이터의 일부만 로드 — EF Core 5 이상
  • 지연 로딩: 편리하지만 위험함 — 반복문에서는 피해야 함(N+1 문제)
  • 명시적 로딩: 세밀하게 제어하면서 필요할 때 로드
  • Select를 사용한 프로젝션: 읽기 전용 시나리오에서 가장 효율적
  • 분할 쿼리: 여러 컬렉션을 Include할 때 카테시안 폭발 방지

자주 묻는 질문

“즉시, 지연, 명시적 로딩” 강의는 무료인가요?

네 — “즉시, 지연, 명시적 로딩” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 C# Academy 강의 전체를 잠금 해제할 수 있습니다. C# Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“즉시, 지연, 명시적 로딩”에서 뭘 배우나요?

Include, ThenInclude, 지연 로딩 프록시, 명시적 Load 호출을 사용해 관련 데이터가 로드되는 시점을 제어합니다. 브라우저에서 직접 실행하는 실습 코드로 C# Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

C# Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 C# Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.

“즉시, 지연, 명시적 로딩” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 C# Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 C# Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 즉시, 지연, 명시적 로딩
  2. 원시 SQL, 저장 프로시저와 보간
  3. 컴파일된 쿼리와 성능
  4. 전역 쿼리 필터와 소유 엔터티
← C# Academy(으)로 돌아가기