C#

C# 기초부터 고급까지 Chapter 3.4. 고급 LINQ 커스터마이징 & 쿼리 최적화

Juan_ 2025. 5. 6. 17:05
728x90

📘 Chapter 3.4: 고급 LINQ 커스터마이징 & 쿼리 최적화


✅ 이번 챕터 목표

  • LINQ의 내부 작동 방식 이해
  • 쿼리식 vs 메서드 체이닝 차이
  • IEnumerable vs IQueryable 차이
  • Select, Where 커스터마이징 방법
  • 실무 성능 개선 전략 (지연 실행, ToList, Projection 등)

1️⃣ LINQ의 기본 개념 다시 정리

LINQ는 2가지 방식으로 쓸 수 있다:

✅ 쿼리식 (Query Syntax)

var result = from x in list
             where x > 10
             select x * 2;

✅ 메서드 체이닝 (Method Syntax)

var result = list.Where(x => x > 10)
                 .Select(x => x * 2);

➡️ 내부적으로는 둘 다 메서드 체이닝으로 변환된다


2️⃣ IEnumerable vs IQueryable 차이

항목IEnumerableIQueryable
위치System.LinqSystem.Linq
대상메모리 내 컬렉션DB 등 외부 소스 (EF, OData)
실행 방식C# 코드에서 직접 처리SQL 등으로 변환됨
실행 시점즉시 or 지연지연
장점간단하고 직관적외부 쿼리 최적화 가능

✅ 실무 예시 (EF 기준)

IEnumerable<User> users = db.Users.ToList();
var list = users.Where(u => u.Age > 20); // LINQ to Object

IQueryable<User> usersQuery = db.Users;
var query = usersQuery.Where(u => u.Age > 20); // LINQ to SQL

➡️ ToList()를 먼저 쓰면 쿼리가 쪼개져서 성능 저하

→ 반드시 필터 → ToList() 순서로 작성해야 함!


3️⃣ Select, Where 직접 커스터마이징

LINQ는 내부적으로 yield return 기반의 지연 실행(Deferred Execution)을 사용함

✅ 커스텀 Where 구현 예시

public static class MyLinq
{
    public static IEnumerable<T> MyWhere<T>(
        this IEnumerable<T> source,
        Func<T, bool> predicate)
    {
        foreach (var item in source)
        {
            if (predicate(item))
                yield return item;
        }
    }
}
var data = new[] { 1, 2, 3, 4, 5 };
var result = data.MyWhere(x => x % 2 == 0);

✅ 지연 실행(Deferred Execution) 확인

var result = data.Where(x =>
{
    Console.WriteLine($"검사 중: {x}");
    return x % 2 == 0;
});

Console.WriteLine("------");
foreach (var r in result)
{
    Console.WriteLine($"결과: {r}");
}

출력:

------
검사 중: 1
검사 중: 2
결과: 2
검사 중: 3
검사 중: 4
결과: 4
검사 중: 5

➡️ foreach 돌리기 전까지는 아무것도 실행 안 됨!


4️⃣ Projection 최적화 (Select vs SelectMany)

  • Select는 요소 1:1 변환
  • SelectMany는 1:N 펼치기
var customers = new[]
{
    new { Name = "A", Orders = new[] { "item1", "item2" } },
    new { Name = "B", Orders = new[] { "item3" } },
};

var flat = customers.SelectMany(c => c.Orders);
// 결과: item1, item2, item3

➡️ 실무에서 List 안의 List 펼칠 때 핵심


5️⃣ 실무 성능 개선 전략

전략설명
ToList() 남용 금지필터 후 ToList 해야 DB 최적화
FirstOrDefault() 조심IEnumerable는 전부 순회, IQueryable은 SQL 최적화
Projection만 가져오기Select(x => new { x.Id })로 필요한 필드만
Where 대신 Any()list.Any(x => x.조건)은 더 빠르고 명확

✅ LINQ에서 N+1 쿼리 피하기 (EF 예시)

var users = db.Users.Include(u => u.Posts).ToList();

✅ .Include()로 필요한 데이터 한번에 가져오기


✅ 정리 요약

항목요약
LINQ 종류쿼리식, 메서드 체이닝 → 결국 메서드 방식
IEnumerable메모리 내 처리 (ToList() 이후)
IQueryableDB 처리 (지연 실행 & SQL 변환)
커스터마이징Select/Where 직접 구현 가능
실무 팁ToList 남용 금지, Projection 사용 권장

📢 다음 챕터 예고 🎓

Chapter주제
3.5코드 분석 및 성능 측정 도구 사용 (BenchmarkDotNet 등)
728x90