CodeGym /행동 /C# SELF /Where로 데이터 필터링하기

Where로 데이터 필터링하기

C# SELF
레벨 31, 레슨 2
사용 가능

1. 소개

필터링이란 컬렉션에서 어떤 조건에 맞는 요소만 뽑아내는 거야. 상상해봐: 네 데이터베이스에 10 000개의 상품이 있는데, 매니저가 "딱" 12개만, 새로운 까다로운 기준에 맞는 것만 필요하대. 매번 직접 반복문 돌리고 조건 체크하는 건 귀찮고, 코드도 점점 더러워지지. LINQ가 이런 귀찮은 일 대신 해줘.

실제 앱에서 필터링은 데이터 다룰 때 제일 많이 쓰는 작업이야: 유저한테 필요한 데이터만 보여주고, 컬렉션에서 필요 없는 요소는 빼고, 리포트나 검색, 이메일 보낼 때도 뽑아서 쓰지. 면접에서도 LINQ 필터링 질문은 진짜 자주 나와. 이거 제대로 이해하면 .NET 개발자 입문할 때 완전 든든해.

2. Where 메서드 알아보기

의미와 동작 원리

Where 메서드는 LINQ의 확장 메서드로, 필터링 조건을 정하는 함수(혹은 람다식)를 받아. 이 메서드는 새로운 시퀀스를 반환하는데(원본은 안 건드림!), 그 안에는 조건이 true인 요소만 들어가.

기본 시그니처:

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate)

generics나 어려운 단어에 겁먹지 마. 지금 중요한 건 전체적인 아이디어야: Where는 원본 컬렉션이랑 네가 정한 조건을 받아. 실제로 써보면 진짜 간단해:

  • source — 필터링할 컬렉션 (예: List<Product>)
  • predicate — 함수/조건 (예: p => p.Price > 100)

간단한 예제

예를 들어, 상품 리스트가 있는데 가격이 100보다 큰 것만 고르고 싶어. 옛날식이면 foreach로 돌면서 직접 새 리스트에 추가했겠지. LINQ면 한 줄이면 끝이야.

3. 필터링

우리 Product 클래스랑 시작 컬렉션

지난 모듈에서 이런 식으로 상품 클래스를 만들었었지:


class Product
{
    public string Name { get; set; }
    public double Price { get; set; }

    // 화면에 예쁘게 출력하려고:
    public override string ToString()
    {
        return $"{Name} (가격: {Price})";
    }
}

상품 리스트가 있다고 해보자:


var products = new List<Product>
{
    new Product { Name = "빵", Price = 30 },
    new Product { Name = "우유", Price = 87 },
    new Product { Name = "치즈", Price = 250 },
    new Product { Name = "초콜릿", Price = 130 }
};

Where로 100보다 비싼 상품 필터링


var expensiveProducts = products.Where(p => p.Price > 100);

foreach (var product in expensiveProducts)
{
    Console.WriteLine(product);
}

내부적으로 일어나는 일:

  • Where가 products 리스트의 모든 요소를 하나씩 보고,
  • 각 요소마다 함수(여기선 p => p.Price > 100)를 실행하고,
  • 함수가 true를 반환하면 그 상품이 새 컬렉션에 들어가.

화면에 출력되는 결과:

치즈 (가격: 250)
초콜릿 (가격: 130)

중요: 원본 리스트 products는 그대로야! LINQ는 네 데이터를 망가뜨리지 않아.

4. 지연 필터링 (Deferred Execution)

재밌고 중요한 포인트: Where의 결과는 실제로 요소에 접근할 때만 계산돼. 예를 들어 foreach 루프가 시작될 때 LINQ가 원본 컬렉션을 돌면서 실시간으로 필터링하지. 결과를 안 쓰면 아무 일도 안 일어나. 이걸 지연 실행(deferred execution)이라고 해.

이게 주는 장점이 많아:

  • 메서드를 여러 개 체이닝해서(예: 필터링하고 정렬까지) 첫 번째로 결과를 쓸 때만 실제로 실행돼.
  • 메모리 절약: 중간에 불필요한 컬렉션 변환이 없어.
  • 필요하면 실행을 취소하거나, 원하는 개수만큼 찾고 중단할 수도 있어.

비주얼 다이어그램


원본 컬렉션       —►   Where (조건)    —►   필요한 요소만 순회
    
[ x ] [ x ] [ + ] [ + ]  —►   p.Price > 100      —►   [ + ] [ + ]

(x — 조건에 안 맞는 요소, + — 조건에 맞는 요소)

5. 문법 옵션

클래식 확장 메서드 방식 (Method Syntax)

이 스타일이 우리가 지금 쓰는 거야:

var result = products.Where(p => p.Price > 100);

대안: Query Syntax

LINQ는 SQL 쿼리랑 비슷한 문법도 지원해:


var result = from p in products
             where p.Price > 100
             select p;

결과는 똑같아!
어떤 문법을 쓸지는 취향 차이지만, method syntax(점 찍는 방식)가 실제 프로젝트나 긴 체이닝에서는 더 자주 보여.

6. 복잡한 필터 조건

여러 조건

논리 연산자(&&, ||, !)도 쓸 수 있어:


// "초콜릿" 빼고 비싼 상품 찾기
var filtered = products.Where(
    p => p.Price > 100 && p.Name != "초콜릿"
);

문자열 필터링 (부분 문자열, 대소문자, Contains)


// 이름에 "ㅣ"가 들어간 상품
var lProducts = products.Where(p => p.Name.Contains("ㅣ"));

참고: Contains는 대소문자 구분해! 대소문자 무시하고 싶으면 이렇게:


var lProducts = products.Where(p => p.Name
    .ToLower().Contains("ㅣ")); // 이제 "우유"랑 "초콜릿" 둘 다 결과에 들어감

여러 컬렉션으로 필터링

예를 들어, 상품 리스트랑 결제 리스트가 있다고 해보자. 결제 리스트에 있는 상품만 필터링하고 싶으면 Any나 Join 같은 메서드를 쓰는 게 좋아 — 이건 나중에 더 다룰 거야. 어쨌든 LINQ로 훨씬 복잡한 필터도 할 수 있다는 것만 알아둬!

7. 유용한 팁

중첩 필터링과 메서드 체이닝

Where를 여러 번 이어서 쓸 수도 있어:


var filtered = products
    .Where(p => p.Price > 100)
    .Where(p => p.Name.StartsWith("초"));

근데 조건을 논리 연산자로 한 번에 합치는 게 더 효율적이고 코드도 깔끔해.

내장 비교자와 커스텀 함수

기본 연산자로 부족할 때도 있어. 예를 들어, 문화권까지 고려해서 대소문자 무시하고 문자열 필터링하고 싶을 때. 이럴 땐 비교 메서드를 쓰면 편해:


var rusProducts = products.Where(
    p => p.Name.StartsWith("초", StringComparison.OrdinalIgnoreCase)
);

8. 필터링과 사용자 입력

실제로 간단한 커맨드 필터링 만들어보자! 예를 들어, 사용자한테 최소 가격을 물어보고, 그에 맞는 상품만 보여주는 거지.


Console.Write("상품의 최소 가격은? ");
var minPriceStr = Console.ReadLine();
if (double.TryParse(minPriceStr, out double minPrice))
{
    var filtered = products.Where(p => p.Price >= minPrice);
    foreach (var product in filtered)
    {
        Console.WriteLine(product);
    }
}
else
{
    Console.WriteLine("오류: 숫자가 아님.");
}

이제 우리 "미니 앱"이 거의 마트처럼 보이지?

9. Where 쓸 때 흔한 실수와 함정

프로그래밍에서도 인생처럼 함정이 있어. 이런 건 조심하자.

ToList()나 ToArray() 호출 안 함

Where의 결과는 리스트도 아니고 배열도 아니야! IEnumerable<Product> 객체야. foreach에서는 잘 돌아가지만, 인덱스로 접근하거나 컬렉션이 꼭 필요하면 .ToList()나 .ToArray()를 꼭 써:


var filteredList = products.Where(p => p.Price > 100).ToList();

이걸 무시하면 "Collection was modified during enumeration" 같은 에러가 쉽게 나 — 특히 필터링 도중에 원본 컬렉션을 바꾸면.

원본 컬렉션 변경

LINQ 필터링은 지연 실행이라, 순회 중에 원본 리스트에서 요소를 삭제하면 예외가 날 수 있어. 특히 멀티스레드 상황이나, 필터링 중에 컬렉션이 바뀌는 경우 조심해야 해.

Null 값과 조건 함수

컬렉션에 null이 들어있는데, 조건 함수에서 객체 필드에 바로 접근하면 NullReferenceException이 나. 예를 들어:


var filtered = products.Where(p => p.Name.StartsWith("아"));

만약 products에 어떤 요소가 null이면 코드가 터져.

팁: null이 있을 수 있으면 항상 null 체크를 추가하자:


var filtered = products.Where(p => p != null && p.Name.StartsWith("아"));
2
과제
C# SELF, 레벨 31, 레슨 2
잠금
여러 기준으로 객체 필터링하기
여러 기준으로 객체 필터링하기
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION