삽질의 현장/- .NET

#064_닷넷(.NET)_.Net Framework 기본 - LINQ의 역할 (기본형) 1

shovelman 2015. 11. 6. 17:05


안녕하세요 삽잡이입니다.


이번 시간에는 LINQ의 기본적인 문법들에 대해서 알아보려고합니다.

LINQ 중에서도 LINQ to Object에 대해서 알아보도록 하지요..

즉, 객체 대상으로 LINQ를 때릴 때를 살펴보자 이겁니다.



LINQ는 모든 데이터 타입을 다룰 수 있는 기능을 제공해줍니다.

쿼리를 날릴 대상(Target)만이 달라질 뿐이지 문법이 어디 달라지겠습니까?

즉, 누구를 타겟으로 질의를 던질 수 있느냐로 종류만 나뉠 뿐이라는 것입니다.

그 종류에는 Object, XML, DataSet, Entities, 병렬 LINQ로 나뉩니다.


DataBase를 대상으로도 LINQ를 던질 수 있는 종류도 있습니다.

DataSet과 Entities인데요... 이 들이 바로 데이터베이스와 관련된 내용입니다.

LINQ는 어마어마하군요...


아무튼... 자세한 것은 그때 알아보도록 하고 이제 본격적으로 달려보도록 하겠습니다.

겁먹지 마시길 바랍니다... 사실, SQL과 다를 것이 없습니다. 그럼 고고!



예를 들어 나이 제한 구역을 가본다고 해보겠습니다.

어딜 가든 자유롭게 상상해보시고...

이와 같은 쿼리문을 통해 배열에서 검색을 하는 쿼리 식을 만들 수 있습니다.


거의 LINQ 커리의 정석입니다. 

LINQ는 쿼리언어이기에 문법을 그대로 해석하는 것이 아니라 '읽어내는 것'이 좋습니다.



'AgeArray' 라는 컬렉션에서 n 에 원소 하나씩을 가져와서 (결국 모든 원소가 되겠지요)

where에 맞는 조건을 거치게 됩니다.

그리고 n에 대해서 정렬을 하고 

조건에 맞고 정렬된 원소 n을 결과들의 집합인 subset에 보관하게 됩니다.


결론적으로 int형 집합을 가공된 int형 집합으로 만들었습니다.

하지만 항상 같은 형식의 집합만을 만드는 것은 아닙니다.

자세한 것은 좀 뒤에서 하도록 하지요...


예제 코드에 있는 쿼리의 틀을 한번 가져와봤습니다.

이와 같은 틀로 쿼리 식을 만들게 됩니다.



from은 말 그대로 from 절입니다.


where은 '조건절'입니다.

true/false만 올 수 있지요. 즉, bool 형식만이 올 수 있다 이겁니다.


orderby는 정렬절입니다.

정렬 방식에는 오름차순(ascending)과 내림차순(descending)이 있지요.

기본적으로 오름 차순으로 정렬을 해줍니다.

즉, 내림 차순을 위해서는 명시를 해줘야한다 이겁니다.


DB의 저장되는 데이터는 순서대로 저장되지 않습니다.

이 순서대로라는 말은 들어온 순서가 아닌, 자동으로 크기 순으로 정렬해주지 않는다는 말입니다.


즉, 컬렉션이 아니다 이겁니다. Database는 데이터의 집합일 뿐입니다.

순서대로 불러오는 것은 맞지만, 

DB에는 순서라는 개념을 가지고 있지 않기 때문에

orderby를 통해 정렬을 해줍니다.

없으면 정렬이 안된다 즉, 오름 차순, 내림 차순이 보장되지 않는다는 것입니다.

컬렉션과 Database의 저장되는 개념을 혼동하시면 안됩니다.


아무튼.. 다음으로 select는 결과 집합입니다.

예를 들어 select n의 경우 n들의 집합을 만들어내라는 뜻을 가지게 됩니다.



여기서 중요한 사실이 있습니다.

결과 집합은 반드시 IEnumerable<T> 형식이어야합니다.

즉, LINQ의 결과치는 IEnumerable<T> 형식이라는 것입니다.


물론 내부적으로 형식을 만들어주지만,

결과치를 보관하려면 객체가 있어야합니다.

그런데 우리는 그 객체가 뭔지 모릅니다. 컴파일러만 알고있지요.


우리는 인터페이스를 반환한다는 사실밖에 모릅니다.

그 인터페이스가 바로 IEnumerable<T> 형식이고,

제가 보여드린 예제에서는 int형식이 되는 것입니다.


따라서 결과치를 저장하는 위의 코드에서 subset은 

IEnumerable<int> 형식이었던 것입니다.


이와 같이 간단하게(?) 구현한 쿼리식과 달리

해당 쿼리식을 C#의 문법으로 구현할 때에는 상당히 복잡하고 난해해질 수 있습니다.

그러니.. 결국은 이와 같이 다수의 보관되어있는 데이터의 경우 

LINQ 언어를 사용하는 것이 훨씬 효율적이고 능률적이라는 사실을 알려드립니다.


왜 우리가 결과 집합에서 알 수 있는 사실은 IEnumerable<T> 형식이라고 했을까요?

해당 형식으로 만든 subset의 타입을 출력해보겠습니다.



이름이.. 참... 어렵게 만들어져있군요... 

우리가 지을 수 있는 이름의 경우를 배제하기 위해 컴파일러에서 어렵게 만들어줍니다.

아무튼... 우리는 그래서 IEnumerable<T> 형태로 만들어졌다는 사실은 확실히 알 수 있게됬습니다.


다시 말씀드리지만,

LINQ의 결과 집합은 IEnumerable<T> 형식입니다.

IEnumerable<T>로 구현한 녀석을은 모두 LINQ 쿼리를 때릴 수 있다는 사실도 알 수 있습니다.


그런데, 타입이 뭐가 올지 알고

예시를 든 코드의 경우 int형식만 받는 컬렉션을 사용했지만,

지저분하고 복잡한 컬렉션을 사용할 경우 어떻게 할까요...

즉, 결과 타입이 무엇인지 명확하게 알 수 없을 경우에 어떠한 타입으로 담을 것이냐 이겁니다.



따라서, var 키워드를 제공해주는 것입니다.

암시적으로 타입 변환이 깔끔하게 이루어지는 것을 확인할 수 있습니다.


우리는 IEnumerable<T> 형식을 통해 쿼리 결과를 반환하는 타입의 집합을 만들었습니다.

그런데, IEnumerable<T> 형식은 사실 IEnmerable 인터페이스를 상속했습니다.

따라서 결과집합으로 부모 형식인 IEnumerable도 가능합니다.


그런데, LINQ는 IEumerable을 타겟으로 동작하지 않습니다.

기본적으로 IEnumerable<T> 형을 타겟으로 동작하는 것입니다.



결과 집합으로 받을 때에는 IEnumerable 형식으로 받을 수는 있지만,

동작 시키는 녀석 즉, 쿼리를 던질 컬렉션은 IEumerable 형식이면 안됩니다.

반드시 IEnumerable<T> 형식이어야합니다.


정리하자면,

기본적으로 LINQ는 제네릭 형식인 IEumerable<T> 형식에 대해서 동작합니다.

IEnumerable 형식으로 쿼리를 던지기 위해서는 꼼수(?)를 써야하는데... 

이는 나중에 확인해보도록 하지요.


기본은 IEnumerable<T> 형식으로 쓰고, IEnumerable<T> 형식으로 받는다는 사실,

그리고 부모이기에 IEnumerable 형식으로 받을 수도 있다는 사실,

그런데 일반적으로 var 타입을 쓴다는 것을 기억하시길 바랍니다.


참고로  subset은 var형식이라면 집합이라는 것은 알지만, 

모든 원소가 어떤 형식으로 만들어지는지를 모르기 때문에

원소를 참조할 때에는 var 키워드를 사용해서 foreach 문을 통해서 순회할 수 잇습니다.



이번 시간은 여기까지 알아보도록 하겠습니다.


이상 삽잡이였습니다!