Notice
Recent Posts
Recent Comments
Link
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

뭐라도 쓰겠지

25.03.28 / LINQ(Language Integrated Query) 본문

프로그래밍

25.03.28 / LINQ(Language Integrated Query)

김데피 2025. 3. 28. 15:27

LINQ는 컬렉션을 편리하게 다루기 위한 목적으로 만들어진 질의(Query) 언어이다. C# 3.0 버전에서부터 탑재한 LINQ 덕분에 C# 프로그래머는 데이터를 찾고 병합하고 정렬하는 코드를 작성하는 짐을 상당 부분 내려놓을 수 있게 됐다.

 

데이터를 미디어에서 읽고, 거르고, 정렬하는 작업은 프로그램에서 부지기수로 일어난다. 이러한 데이터 작업은 당연히 프로그래머가 프로그래밍해 넣어야 한다. 프로그래머는 데이터를 가공하는 논리적인 작업에 더 신경을 스고 싶어하지만, 안타깝게도 데이터를 가공하기 전에 필요한 데이터를 찾아내는 일이 도저히 프로그래머를 놔주지 않는다. 게다가 이 지루한 작업은 양도 적지 않다.

 

LINQ는 우리를 이 지루한 데이터 작업에서 해방시켜준다. LINQ는 Language INtegrated Query의 약어로, C#에 통합된 데이터 질의 기능을 말한다.

 

질의(Query)란 원래 무언가에 대해 물어본다는 뜻인데, 데이터 질의라고 하면 데이터에 대해 물어본다는 말이 된다. 그렇다면 그 질문을 받은 누군가는 데이터에 대한 답변을 줄 것이다. 기본적으로 질문은 다음 내용을 포함한다.

  • From: 어떤 데이터 집합에서 찾을 것인가?
  • Where: 어떤 값의 데이터를 찾을 것인가?
  • Select: 어떤 항목을 추출할 것인가?

이제 LINQ의 특징에 대해 알아보자.

  • LINQ의 주요 특징
    • 1. 통합된 쿼리 언어
      • C#에서 직접 쿼리를 작성할 수 있는 문법(from, where, select 등)을 제공한다.
      • SQL 쿼리와 유사하여 직관적이며, 코드와 쿼리가 한 몸처럼 결합된 형태로 작성된다.
    • 2. 강력한 형식 검사(Static Typing)
      • 컴파일 타임에 타입 검사가 이루어지므로, 잘못된 타입 변환이나 프로퍼티 이름 등을 조기에 잡을 수 있다.
      • SQL 쿼리 문자열에서 발생하기 쉬운 런타임 오류 가능성을 줄여준다.
    • 3. 일관된 프로그래밍 모델
      • 메모리 내 개체(IEnumerable<T>), 데이터베이스(Entity Framework 등을 통해), XML, 기타 LINQ 지원 프로바이더 등에 동일한 쿼리 문법으로 접근할 수 있다.
      • 여러 데이터 소스를 동일한 관점으로 조회하거나 조작할 수 있다.
    • 4. 지연 실행(Deferred Execution)
      • 일반적으로 IEnumerable<T> 기반의 LINQ 쿼리는 실제 데이터를 조회하는 시점까지 실행되지 않는 특성을 가진다.
      • 필요한 시점 전까지는 쿼리가 실제로 수행되지 않으므로, 불필요한 리소스 사용을 줄일 수 있다.
    • 5. 표준 연산자(Standard Query Operators) 제공
      • Where, Select, OrderBy, GroupBy, Distinct, Join 등 풍부한 확장 메소드가 제공되어, 복잡한 데이터 처리 로직을 간결하게 표현할 수 있다.
  • LINQ의 두 가지 문법
    • C#에서 LINQ를 작성하는 방식은 크게 쿼리 식(Query Expression) 문법과 메소드 체이닝(Method Chain) 문법 두 가지가 있다. 실제로 대부분의 쿼리 식은 컴파일 타임에 메소드 체인 호출로 변환된다.
// 예시: 정수 리스트에서 짝수만 골라 내림차순으로 정렬 후 선택
int[] numbers = { 1, 2, 3, 4, 5, 6 };

var query =
    from num in numbers      // 데이터 소스
    where num % 2 == 0       // 필터링
    orderby num descending   // 정렬
    select num;              // 선택

foreach (var n in query)
{
    Console.WriteLine(n);  // 6, 4, 2
}

SQL과 유사한 키워드를 사용하여 쿼리를 작성하는 방식인 쿼리 식의 예시이다.

int[] numbers = { 1, 2, 3, 4, 5, 6 };

var result = numbers
    .Where(num => num % 2 == 0)
    .OrderByDescending(num => num)
    .Select(num => num);

foreach (var n in result)
{
    Console.WriteLine(n);  // 6, 4, 2
}

LINQ 표준 연산자에 해당하는 확장 메소드를 직접 호출하여 연결하는 방식의 메소드 체이닝의 예시이다.

 

  • 쿼리 식과 메소드 체이닝 비교
    • 쿼리 식: SQL 쿼리와 비슷하게 작성하므로, 데이터 소스를 조회하고 필터링, 정렬, 그룹화 등의 작업을 직관적으로 표현할 수 있다.
    • 메소드 체이닝: 람다 식을 사용하여 조건을 처리하므로, 함수형 프로그래밍 스타일을 선호하거나 더 복잡한 매개변수를 전달해야 하는 경우 유리하다.
  • 다양한 LINQ 제공 범위
    • 1. LINQ to Objects
      • 가장 기본적인 형태로, 메모리에 있는 IEnumerable<T>(배열, 리스트 등)에 쿼리를 수행한다.
    • 2. LINQ to XML
      • XML 문서에 대해 LINQ 쿼리를 수행할 수 있게 해주는 기능이다. XML DOM 대신, 직관적인 쿼리 구문을 통해 노드를 찾고 필터링할 수 있다.
    • 3. LINQ to SQL / Entity Framework (LINQ to Entities)
      • 데이터베이스 테이블과 레코드를 객체와 연결(ORM)하여, LINQ 쿼리를 사용해 DB 쿼리를 수행할 수 있게 해준다.
    • 4. 기타
      • 제 3자 라이브러리나 자체적으로 만든 LINQ 프로바이더를 통해, 다양한 형태의 데이터 소스에 대해 쿼리를 수행할 수도 있다.

핵심 표준 연산자 예시

  • Where - 조건에 맞는 요소만 필터링
var evenNumbers = numbers.Where(x => x % 2 == 0);

 

  • Select - 요소를 특정 형태로 변환
var squares = numbers.Select(x => x * x);

 

  • OrderBy / OrderByDescending - 오름차순/내림차순 정렬
var sorted = numbers.OrderBy(x => x);

 

  • GroupBy - 특정 기준에 따라 그룹화
var grouped = numbers.GroupBy(x => x % 2 == 0 ? "Even" : "Odd");

 

  • Join - 두 컬렉션(혹은 테이블)을 연결하여 하나의 시퀀스로 만듬
var query = from a in collectionA
            join b in collectionB on a.Id equals b.AId
            select new { A = a, B = b };

 

  • Distinct - 중복 요소 제거
var distinctValues = numbers.Distinct();

 

  • Sum, Max, Min, Count, Average - 집계 함수
var total = numbers.Sum();
var max = numbers.Max();
var average = numbers.Average();

 

LINQ를 사용할 때 주의할 점도 있다.

  • 1. 지연 실행(Deferred Execution)에 대한 이해
    • LINQ 쿼리가 언제 실제로 실행되는지, 어떤 시점에 DB 접근이 일어나는지를 잘 파악해야 성능이나 부작용 측면에서 안전하게 사용할 수 있다.
  • 2. ToList(), ToArray() 등의 즉시 실행
    • 필요 시점에 ToList(), ToArray() 등을 호출하여 컬렉션을 확정(실제 결과를 한 번에 가져옴)할 수 있다.
  • 3. 복잡한 쿼리의 경우 성능 고려
    • LINQ를 통해 간결하고 가독성 좋게 작성 가능하지만, 때로는 SQL 최적화가 더 적절할 수 있다. 특히 대용량 데이터베이스 작업이라면 실행 계획을 확인하면서 쿼리를 작성해야 한다.
  • 4. LINQ to Entities에서의 제한
    • Entity Framework나 기타 ORM으로 LINQ를 사용할 때, 메모리 내 함수(예: 커스텀 C# 메소드)는 DB 쿼리로 직접 변환되지 않을 수 있다. 이런 부분은 주의 깊게 작성해야 한다.

정리하자면 LINQ는 C#에서 데이터를 다루는 방식을 크게 단순화하고 통일해주는 강력한 기능이다. 익숙해지면, 다양한 데이터 소스에 대해 짧고 간결한 코드로 필터링, 정렬, 그룹화 등의 작업을 일관되게 처리할 수 있다. SQL과 비슷한 "쿼리 식"과 람다 기반 "메소드 체이닝" 문법을 적절히 활용하면, 가독성과 생산성을 크게 향상시킬 수 있다.