LINQ = C# 코드 안에서 컬렉션(배열, List, DB데이터 등)을 SQL처럼 깔끔하게 필터/정렬/변환/집계하는 기능
원래는 이런 식으로 썼던 코드를,
int[] numbers = { 3, 5, 4, 2, 6, 7, 8 };
List<int> evens = new List<int>();
foreach (int n in numbers)
{
if (n % 2 == 0)
evens.Add(n);
}
evens.Sort(); // 오름차순 정렬
LINQ로는 이렇게 쓸 수 있다.
int[] numbers = { 3, 5, 4, 2, 6, 7, 8 };
var evens = numbers
.Where(n => n % 2 == 0) // 짝수만 필터
.OrderBy(n => n) // 오름차순 정렬
.ToList(); // List<int>로 변환
Where : 조건 필터링OrderBy : 정렬ToList : 리스트로 변환n => n % 2 == 0 : 조건을 나타내는 람다식 (Func<int,bool>)결과는 똑같지만, LINQ 버전이 훨씬 읽기 쉽다. “무슨 일을 하는지”가 코드에 그대로 드러나는 게 장점이다.
대표적으로 다음과 같은 곳에 쓸 수 있다.
int[], string[] 같은 배열List<T>, Dictionary<K,V> 같은 컬렉션IQueryable<T> (Entity Framework 같은 ORM)IEnumerable<T> 를 구현한 대부분의 타입즉, “여러 개의 데이터가 모여 있는 것”에 대해서 거의 다 쓸 수 있다고 보면 된다.
LINQ를 가장 많이 쓰는 방식은 메서드 체인 스타일이다.
컬렉션
.Where(조건)
.Select(변환)
.OrderBy(정렬기준)
.GroupBy(그룹기준)
.ToList();
int[] numbers = { 3, 5, 4, 2, 6, 7, 8 };
// 짝수만 가져오기
var evens = numbers
.Where(n => n % 2 == 0)
.ToList();
Where는 Func<T, bool> 타입의 함수를 받는다.n => n % 2 == 0은 “n이 짝수면 true”를 반환하는 람다식이다.데이터를 다른 형태로 바꾸고 싶을 때 사용한다.
int[] numbers = { 1, 2, 3, 4, 5 };
var squares = numbers
.Select(n => n * n) // n을 n의 제곱으로 변환
.ToList();
문자열로 바꾸는 것도 가능하다.
var texts = numbers
.Select(n => $"숫자: {n}")
.ToArray();
Select의 인수 타입: Func<TSource, TResult>int[] numbers = { 3, 5, 4, 2, 6, 7, 8 };
var sortedAsc = numbers
.OrderBy(n => n) // 오름차순
.ToList();
var sortedDesc = numbers
.OrderByDescending(n => n) // 내림차순
.ToList();
문자열도 똑같이 쓸 수 있다.
string[] names = { "Kim", "Park", "Lee", "Choi" };
var sortedNames = names
.OrderBy(n => n) // 이름 오름차순
.ToList();
실무에서 자주 쓰이는 “거래처별 매출집계”, “품목별 수량합계” 같은 작업에 사용한다.
class Order
{
public string CustomerCode { get; set; }
public decimal Amount { get; set; }
}
List<Order> orders = ...; // 주문 리스트라고 가정
var sumByCustomer = orders
.GroupBy(o => o.CustomerCode) // 거래처코드별로 그룹핑
.Select(g => new
{
Customer = g.Key, // 그룹 키 = CustomerCode
Total = g.Sum(o => o.Amount),
Count = g.Count()
})
.ToList();
정리하면:
GroupBy(o => o.CustomerCode) : CustomerCode가 같은 주문끼리 하나의 그룹이 된다.g.Key : 해당 그룹의 거래처 코드g.Sum(o => o.Amount) : 그 거래처의 주문금액 총합g.Count() : 그 거래처의 주문 건수LINQ는 다음처럼 SQL 비슷한 문법으로도 쓸 수 있다.
int[] numbers = { 3, 5, 4, 2, 6, 7, 8 };
var evens =
from n in numbers
where n % 2 == 0
orderby n
select n;
var list = evens.ToList();
위 코드는 내부적으로는 다음과 같은 메서드 체인으로 바뀐다.
var evens = numbers
.Where(n => n % 2 == 0)
.OrderBy(n => n);
둘 다 똑같이 동작하니, 편한 스타일을 선택해서 쓰면 된다.
보통은 메서드 문법(점 찍고 이어 쓰는 방식)을 더 많이 사용한다.
LINQ 메서드를 이해하려면, “이 메서드가 어떤 타입의 함수를 인수로 받는지”를 보면 된다.
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate
)
predicate : TSource 하나를 받아서 bool을 반환하는 함수n => n % 2 == 0 같은 람다식은 실제로 Func<int,bool> 타입이다.public T Find(Predicate<T> match)
Predicate<T>는 사실상 Func<T,bool>와 같은 형태다(입력 T, 출력 bool).
그래서 다음 둘은 역할이 거의 같다.
List<T>.Find(n => n % 2 == 0) → Predicate<T> 사용Where(n => n % 2 == 0) → Func<T,bool> 사용결국 LINQ는 “함수를 인수로 받아서 컬렉션을 처리하는 라이브러리”이고, 그 함수는 대부분 람다식으로 작성한다.
아래는 “주문(Order) 리스트”를 LINQ로 처리하는 실무형 예제다.
using System;
using System.Collections.Generic;
using System.Linq;
namespace LambdaExample
{
class Order
{
public int Id { get; set; } // 주문번호
public string CustomerCode { get; set; } // 거래처 코드
public string ItemCode { get; set; } // 품목 코드
public DateTime OrderDate { get; set; } // 주문일자
public decimal Amount { get; set; } // 주문금액
public string Status { get; set; } // 상태: "완료", "취소" 등
}
class Program
{
static void Main()
{
List<Order> orders = GetSampleOrders();
// 1) 완료 상태이면서 50만원 이상인 주문만 조회
var highValueOrders = orders
.Where(o => o.Status == "완료" && o.Amount >= 500_000m)
.OrderByDescending(o => o.Amount)
.ToList();
// 2) 거래처별 매출 합계 (취소 제외)
var sumByCustomer = orders
.Where(o => o.Status == "완료")
.GroupBy(o => o.CustomerCode)
.Select(g => new
{
CustomerCode = g.Key,
TotalAmount = g.Sum(o => o.Amount),
OrderCount = g.Count()
})
.OrderByDescending(x => x.TotalAmount)
.ToList();
// 3) 공통 합계 함수 + 조건을 Func<Order,bool>로 전달
decimal c001Total = GetTotalAmount(
orders,
o => o.CustomerCode == "C001" && o.Status == "완료"
);
decimal c002CancelTotal = GetTotalAmount(
orders,
o => o.CustomerCode == "C002" && o.Status == "취소"
);
}
static decimal GetTotalAmount(List<Order> orders, Func<Order, bool> predicate)
{
return orders
.Where(predicate) // 조건을 람다식으로 받아서 필터
.Sum(o => o.Amount); // 금액 합계
}
static List<Order> GetSampleOrders()
{
return new List<Order>
{
new Order { Id = 1, CustomerCode = "C001", ItemCode = "ITEM-01", OrderDate = new DateTime(2025, 1, 10), Amount = 300_000m, Status = "완료" },
new Order { Id = 2, CustomerCode = "C001", ItemCode = "ITEM-02", OrderDate = new DateTime(2025, 1, 15), Amount = 800_000m, Status = "완료" },
new Order { Id = 3, CustomerCode = "C002", ItemCode = "ITEM-03", OrderDate = new DateTime(2025, 2, 01), Amount = 150_000m, Status = "취소" },
new Order { Id = 4, CustomerCode = "C002", ItemCode = "ITEM-01", OrderDate = new DateTime(2025, 2, 20), Amount = 1_200_000m, Status = "완료" },
new Order { Id = 5, CustomerCode = "C003", ItemCode = "ITEM-02", OrderDate = new DateTime(2025, 3, 05), Amount = 500_000m, Status = "완료" },
new Order { Id = 6, CustomerCode = "C003", ItemCode = "ITEM-03", OrderDate = new DateTime(2025, 3, 10), Amount = 50_000m, Status = "완료" },
};
}
}
}
여기서 LINQ가 하는 일은 크게 세 가지다.