LINQ는 C# 코드에서 데이터 소스(SQL 데이터베이스, 컬렉션, XML 등)를 일관성 있게 쿼리할 수 있도록 해주는 도구입니다. 이 글에서는 LINQ의 소개와 필요성, LINQ를 사용한 데이터 질의, IEnumerable과 IQueryable 인터페이스를 다룹니다.
1. LINQ의 소개와 필요성
LINQ란 무엇인가?
LINQ는 Language Integrated Query의 약자로, C#에서 데이터 소스를 쿼리하는 기능을 제공합니다. LINQ는 다양한 데이터 소스에 대해 일관된 문법으로 데이터를 검색, 필터링, 변환할 수 있습니다. 대표적으로 다음과 같은 데이터 소스에서 사용됩니다:
- 컬렉션 (예: List, Array)
- 데이터베이스 (LINQ to SQL, Entity Framework)
- XML 문서 (LINQ to XML)
- 기타 데이터 소스 (예: 파일, Web API)
LINQ가 필요한 이유
- 일관된 쿼리 문법: LINQ는 SQL, 컬렉션, XML 등 서로 다른 데이터 소스에 대해 동일한 쿼리 문법을 제공합니다.
- 코드 가독성 향상: 데이터 처리 코드가 간결해지고 읽기 쉬워집니다.
- 타입 안전성: 컴파일 시점에 쿼리 오류를 잡아주기 때문에 런타임 오류를 줄일 수 있습니다.
- 유연한 데이터 변환: 데이터를 필터링하고 변환하는 작업을 간단히 수행할 수 있습니다.
2. LINQ를 사용한 데이터 질의
LINQ는 **쿼리 문법(Query Syntax)**과 메서드 문법(Method Syntax) 두 가지 스타일을 제공합니다. 둘 다 동일한 결과를 반환하며, 상황에 따라 편리한 방식을 사용할 수 있습니다.
데이터 준비
먼저, LINQ를 사용할 데이터 소스를 준비합니다.
using System;
using System.Collections.Generic;
using System.Linq; // LINQ를 사용하려면 필수
class Program
{
static void Main()
{
// 예제 데이터: 학생 리스트
List<Student> students = new List<Student>
{
new Student { Id = 1, Name = "홍길동", Score = 85 },
new Student { Id = 2, Name = "김철수", Score = 92 },
new Student { Id = 3, Name = "이영희", Score = 78 },
new Student { Id = 4, Name = "박민수", Score = 95 },
new Student { Id = 5, Name = "최지혜", Score = 88 }
};
// LINQ 쿼리를 사용해 데이터를 처리하는 방법을 아래에서 배웁니다.
}
}
// 학생 클래스를 정의
class Student
{
public int Id { get; set; } // 학생 ID
public string Name { get; set; } // 이름
public int Score { get; set; } // 점수
}
(1) Query Syntax (쿼리 문법)
SQL과 유사한 문법으로 데이터를 처리합니다.
// 80점 이상인 학생 필터링 (Query Syntax 사용)
var highScorers = from student in students
where student.Score >= 80
select student;
Console.WriteLine("80점 이상인 학생:");
foreach (var student in highScorers)
{
Console.WriteLine($"{student.Name} (점수: {student.Score})");
}
(2) Method Syntax (메서드 문법)
C# 메서드 체인을 사용하여 데이터를 처리합니다.
// 80점 이상인 학생 필터링 (Method Syntax 사용)
var highScorers = students.Where(student => student.Score >= 80);
Console.WriteLine("80점 이상인 학생:");
foreach (var student in highScorers)
{
Console.WriteLine($"{student.Name} (점수: {student.Score})");
}
추가 LINQ 연산자 예제
정렬: 점수 순으로 학생 정렬
var sortedStudents = students.OrderBy(student => student.Score);
Console.WriteLine("점수 순으로 정렬된 학생:");
foreach (var student in sortedStudents)
{
Console.WriteLine($"{student.Name} (점수: {student.Score})");
}
데이터 변환: 이름만 추출
var names = students.Select(student => student.Name);
Console.WriteLine("학생 이름 목록:");
foreach (var name in names)
{
Console.WriteLine(name);
}
그룹화: 점수 기준 그룹화
var groupedStudents = students.GroupBy(student => student.Score / 10);
Console.WriteLine("점수대별 학생 그룹:");
foreach (var group in groupedStudents)
{
Console.WriteLine($"점수대: {group.Key * 10}점대");
foreach (var student in group)
{
Console.WriteLine($" - {student.Name} (점수: {student.Score})");
}
}
💡 LINQ는 데이터를 필터링, 정렬, 변환, 그룹화 등 다양한 방식으로 처리할 수 있는 강력한 연산자를 제공합니다.
3. IEnumerable과 IQueryable 인터페이스
LINQ는 두 가지 주요 인터페이스인 **IEnumerable**과 **IQueryable**을 통해 데이터를 처리합니다. 두 인터페이스의 차이를 이해하면 LINQ를 더 효과적으로 사용할 수 있습니다.
IEnumerable
- 데이터 소스: 컬렉션과 같은 메모리 내 데이터.
- 즉시 실행: 쿼리를 호출할 때 데이터가 즉시 실행됩니다.
- 적용: 메모리 내의 데이터 소스 (예: List, Array)에서 주로 사용됩니다.
var enumerableQuery = students.Where(student => student.Score >= 80);
Console.WriteLine("IEnumerable 사용:");
foreach (var student in enumerableQuery)
{
Console.WriteLine($"{student.Name} (점수: {student.Score})");
}
IQueryable
- 데이터 소스: 데이터베이스와 같은 외부 데이터 소스.
- 지연 실행: 쿼리가 실행될 때까지 데이터 처리가 지연됩니다.
- 적용: 데이터베이스 연동 기술 (예: Entity Framework)에서 주로 사용됩니다.
// 예: Entity Framework를 사용하는 경우
using (var context = new SchoolContext())
{
IQueryable<Student> queryableQuery = context.Students.Where(student => student.Score >= 80);
Console.WriteLine("IQueryable 사용:");
foreach (var student in queryableQuery)
{
Console.WriteLine($"{student.Name} (점수: {student.Score})");
}
}
주요 차이점 요약
특징 | IEnumerable | IQueryable |
데이터 소스 | 메모리 내 데이터 | 데이터베이스, 웹 서비스 등 외부 소스 |
실행 방식 | 즉시 실행 | 지연 실행 |
사용 사례 | 컬렉션, 배열 | LINQ to SQL, Entity Framework |
💡 메모리 내 데이터에는 IEnumerable, 데이터베이스와 같은 외부 데이터에는 IQueryable을 사용하는 것이 일반적입니다.
LINQ의 성능과 최적화
- 지연 실행 (Deferred Execution)
LINQ 쿼리는 필요할 때까지 실행되지 않습니다. ToList()나 ToArray()를 호출하면 즉시 실행됩니다.var query = students.Where(student => student.Score >= 80); // 지연 실행 var result = query.ToList(); // 즉시 실행
- 효율적인 필터링
데이터가 많을수록 필터 조건을 처음에 사용하는 것이 성능에 유리합니다.
var optimizedQuery = students.Where(s => s.Score >= 80).OrderBy(s => s.Name);
- 캐싱 사용
동일한 쿼리를 여러 번 실행할 경우, 결과를 캐싱하여 성능을 개선할 수 있습니다.