C# LINQ

김민구·2025년 5월 27일
0

C#

목록 보기
25/31

LINQ란 무엇일까요?

LINQ는 컬렉션을 편리하게 다루기 위한 목적으로 만들어진 기능이며, C# 3.0 버전부터 탑재되었습니다. LINQ 덕분에 C# 프로그래머는 데이터를 찾고, 병합하고, 정렬하는 코드를 작성하는 부담을 상당히 줄일 수 있게 되었습니다. 마치 데이터베이스의 쿼리 언어(SQL)처럼 C# 코드 안에서 데이터를 질의(Query)하고 조작할 수 있게 해주는 것입니다.

LINQ의 기본 구조 (쿼리 구문)

LINQ는 데이터를 쿼리하기 위해 여러 가지 절(clause)을 사용합니다. 기본적인 쿼리 구문은 다음과 같은 형태로 이루어집니다.

fromwhereorderbyselect

각 절의 역할은 다음과 같습니다.

  1. from 절: 쿼리할 데이터 원본과 범위 변수(range variable)를 지정합니다. 범위 변수는 데이터 원본의 각 요소를 대표하며, 이어지는 절들에서 이 변수를 사용하여 요소를 참조합니다.
    예: from profile in arrProfile (arrProfile이라는 데이터 원본에서 각 요소를 profile이라는 이름으로 참조)

  2. where 절: 데이터 원본에서 특정 조건을 만족하는 요소만 걸러내는 필터링(Filtering) 역할을 합니다. where 절 뒤에는 불리언(bool) 값을 반환하는 조건식이 옵니다.
    예: where profile.Height < 175 (키가 175 미만인 profile만 선택)

  3. orderby 절: 필터링된 데이터를 특정 기준에 따라 정렬(Sorting)합니다. 오름차순(ascending) 또는 내림차순(descending)으로 정렬할 수 있으며, 기본값은 오름차순입니다.
    예: orderby profile.Height (Height를 기준으로 오름차순 정렬)
    예: orderby profile.Height descending (Height를 기준으로 내림차순 정렬)

  4. select 절: 쿼리 결과를 프로젝션(Projection)합니다. 즉, 쿼리 결과로 어떤 데이터를 반환할지 정의합니다. 데이터 원본의 요소를 그대로 반환하거나, 요소의 일부만 선택하거나, 여러 요소를 조합하여 새로운 형태의 익명 형식(Anonymous Type)으로 반환할 수 있습니다.
    예: select profile (조건에 맞는 profile 객체 자체를 반환)
    예: select profile.Name (조건에 맞는 profile의 Name 속성만 반환)
    예: select new { Name = profile.Name, InchHeight = profile.Height * 0.393 } (Name과 계산된 InchHeight를 포함하는 새로운 익명 형식 객체 반환)

다음은 기본적인 LINQ 쿼리 구문의 예시 코드입니다.

// Profile 클래스 정의 (이름과 키)
class Profile
{
    public string Name { get; set; }
    public int Height { get; set; }
}

// 데이터 원본 (Profile 배열)
Profile[] arrProfile =
{
    new Profile(){Name="정우성", Height=186},
    new Profile(){Name="김태희", Height=158},
    new Profile(){Name="고현정", Height=172},
    new Profile(){Name="이문세", Height=178},
    new Profile(){Name="하하", Height=171}
};

// LINQ 쿼리
var profiles = from profile in arrProfile // 데이터 원본과 범위 변수
               where profile.Height < 175 // 키가 175 미만인 요소 필터링
               orderby profile.Height // 키를 기준으로 오름차순 정렬
               select profile; // 필터링되고 정렬된 profile 객체 선택

// 쿼리 결과 출력 (예시)
foreach (var profile in profiles)
{
    Console.WriteLine($"이름:{profile.Name}, 키:{profile.Height}");
}

위 예시에서는 키가 175 미만인 Profile 객체들을 키 순서대로 정렬하여 가져옵니다. select new { ... } 구문을 사용하면 원하는 속성만 선택하거나 계산된 속성을 추가하여 새로운 형태의 결과를 만들 수 있습니다.

메서드 구문 (표준 쿼리 연산자)

LINQ 쿼리는 쿼리 구문 외에 메서드 구문으로도 작성할 수 있습니다. 이는 Where(), OrderBy(), Select() 등과 같은 확장 메서드를 체인처럼 연결하여 사용합니다. 쿼리 구문과 메서드 구문은 서로 변환 가능하며 동일한 결과를 생성합니다. 많은 경우 컴파일러가 쿼리 구문을 메서드 구문으로 변환합니다.

메서드 구문 예시:

var profiles = arrProfile.Where(profile => profile.Height < 175) // Where 확장 메서드 사용
                         .OrderBy(profile => profile.Height) // OrderBy 확장 메서드 사용
                         .Select(profile => new { Name = profile.Name, InchHeight = profile.Height * 0.393 }); // Select 확장 메서드 사용

메서드 구문에서는 람다식(Lambda Expression)을 활용하여 조건을 지정하거나 데이터를 선택합니다.

다양한 LINQ 활용

LINQ는 기본적인 필터링, 정렬, 프로젝션 외에도 다양한 작업을 수행할 수 있습니다.

  • 여러 데이터 원본 질의하기 (Nested from): from 절을 중첩하여 사용하여 여러 컬렉션의 데이터를 조합하여 쿼리할 수 있습니다.
    예: 각 반(Class)의 학생(Score) 점수 중 60점 미만인 점수만 가져오기

    var classes = from c in arrClass // 첫 번째 데이터 원본 (반)
                  from s in c.Score // 두 번째 데이터 원본 (점수 배열)
                  where s < 60 // 점수가 60 미만인 경우 필터링
                  select new { c.Name, Lowest = s }; // 반 이름과 점수 선택
  • 데이터 분류하기 (group by): 데이터를 특정 기준에 따라 그룹핑(Grouping)할 수 있습니다. group by 절의 결과는 IGrouping<TKey, TElement> 형식으로 반환되며, 각 그룹은 키와 해당 그룹에 속하는 요소들의 컬렉션을 가집니다. into 키워드를 사용하여 그룹 결과를 새로운 범위 변수로 가져와 추가 작업을 수행할 수 있습니다.
    예: Profile 데이터를 Height < 175 기준에 따라 그룹핑하기

    var listProfile = from profile in arrProfile // 데이터 원본
                      group profile by profile.Height < 175 into g // 키가 175 미만 여부로 그룹핑하고 결과를 g로
                      select new { GroupKey = g.Key, Profiles = g }; // 그룹 키와 해당 그룹의 Profile들 선택
  • 데이터 원본 연결하기 (join): 여러 데이터 원본을 특정 조건에 따라 연결하여 데이터를 가져올 수 있습니다. 이는 데이터베이스의 조인 연산과 유사합니다. join 절 뒤에 연결할 데이터 원본, 내부 컬렉션의 범위 변수, 조인 조건 (equals 사용)이 옵니다. 외부 조인도 지원하며, intoDefaultIfEmpty()를 함께 사용하여 구현합니다.
    예: arrProfile과 arrProduct 데이터를 Name과 Star 속성을 기준으로 연결하기

    var listProfile = from profile in arrProfile // 외부 데이터 원본
                      join product in arrProduct on profile.Name equals product.Star into ps // 내부 데이터 원본과 조인 조건, 결과를 ps로
                      from product in ps.DefaultIfEmpty(new Product(){Title="없음"}) // 외부 조인을 위한 부분
                      select new // 결과 형태 정의
                      {
                          Name = profile.Name,
                          Work = product.Title,
                          Height = profile.Height
                      };

    LINQ는 내부 조인(Inner Join), 왼쪽 외부 조인(Left Outer Join), 오른쪽 외부 조인(Right Outer Join), 완전 외부 조인(Full Outer Join)을 지원하지만, 외부 조인 방식은 SQL보다 조금 아쉬운 부분이 있을 수 있습니다.

표준 쿼리 연산자 (Standard Query Operators)

LINQ는 위에서 살펴본 Where, OrderBy, Select, GroupBy, Join과 같은 것들을 포함하여 다양한 작업을 수행하는 표준 쿼리 연산자들을 제공합니다. 이들은 대부분 System.Linq 네임스페이스에 정의된 확장 메서드 형태로 제공됩니다.

또한, 데이터 집계(Aggregation)를 위한 연산자들도 있습니다.

  • Count(): 요소의 개수를 계산합니다.
  • Average(): 숫자의 평균을 계산합니다.
  • Min(): 최소값을 찾습니다.
  • Max(): 최대값을 찾습니다.
    이러한 연산자들은 람다식을 인수로 받아 특정 속성에 대해 집계를 수행할 수 있습니다.
profile
C#, Unity

0개의 댓글