Tag Archives: 프로그래밍

C# 6.0 완벽 가이드/ 직렬화

직렬화 개념

  • 직렬화(serialization)는 메모리 안의 객체 하나 또는 객체 그래프(서로 참조하는 객체들의 집합)를 저장 또는 전송에 적합한 바이트 스트림 또는 XML 노드들로 평탄화(flattening)하는 연산을 뜻한다.
    • 그 반대는 역직렬화(deserialization), 즉 자료 스트림을 해석해서 메모리 안의 객체 또는 객체 그래프를 재추축하는 연산이다.
  • 직렬화와 역직렬화의 주된 용도는 다음 두 가지이다.
    • 객체를 네트워크나 응용 프로그램 경계 너머로 전송한다.
    • 객체를 파일이나 데이터베이스에 저장한다.
  • 그보다 덜 자주 쓰이는 용도는 객체들을 깊게 복제하는 것이다. 또한 직렬화 및 역직렬화 기능을 제공하는 자료 계약(data contract) 직렬화 엔진과 XML 직렬화 엔진을 XML 파일(구조가 알려진)의 저장과 적재를 위한 범용 도구로 사용할수도 있다.
  • .NET Framework는 직렬화 및 역직렬화를, 객체들의 직렬화/역직렬화를 원하는 클라이언트의 관점에서는 물론 자신의 직렬화 방식을 어느 정도 제어하고자 하는 형식의 관점에서도 지원한다.

직렬화 엔진

  • .NET Framework는 다음과 같은 네 가지 직렬화 메커니즘을 제공한다.
    • 자료 계약 직렬화기
    • 이진 직렬화기(데스크톱 응용 프로그램에서)
    • (특성 기반) XML 직렬화기(XmlSerializer)
    • IXmlSerializable 인터페이스
  • 이 중 처음 셋은 직렬화 작업의 대부분 또는 전부를 실제로 수행하는 직렬화 ‘엔진’이다. 마지막은 XmlReader와 XmlWriter를 이용해서 직렬화를 독자가 직접 구현하는데 필요한 틀을 제공할 뿐이다.
    • IXmlSerializable은 자료 계약 직렬화기(data contract serializer)와 함께 사용할 수도 있고, XmlSerializer와 함께 사용할 수도 있다 (좀 더 복잡한 XML 직렬화 과제를 처리하려는 경우)
  • 아래 표는 네 메커니즘을 비교한 것이다 별표가 많을수록 점수가 높다.
자료 계약 직렬화기 이진 직렬화기 XmlSerializer IXmlSerializable
자동화 수준 *** ***** **** *
형식 결합도 선택 강함 느슨함 느슨함
버전 내구성 ***** *** ***** *****
객체 참조 유지 선택 아니요 선택
비공용 필드 직렬화 가능 아니요
상호운용적 메시지 적합성 ***** ** **** ****
XML 파일 읽기/쓰기의 유연성 ** **** *****
축약된 출력 ** **** ** **
성능 *** **** * ~ *** ***

 

  • IXmlSerializable의 점수들은 독자가 XmlReader와 XmlWriter를 이용해서 코드를 최적으로 작성했다고 가정한 것이다. XML 직렬화 엔진에서 좋은 성능을 내려면 같은 XmlSerializer 객체를 재사용해야 한다.

Continue reading

C# 6.0 완벽 가이드/ 네트워킹

  • .NET Framework System.Net.* 이름공간들에는 HTTP나 TCP/IP, FTP 같은 표준 네트워크 프로토콜을 이용해서 통신을 수행하는데 사용할 수 있는 다양한 클래스가 있다. 핵심 구성요소들을 요약하면 다음과 같다.
    • HTTP나 FTP를 통한 간단한 다운로드/업로드 연산을 위한 퍼사드 클래스 WebClient
    • 클라이언트 쪽 HTTP나 FTP 연산들을 저수준에서 제어할 수 있는 WebRequest 클래스와 WebResponse 클래스
    • HTTP 웹 API와 RESTful 서비스의 소비를 위한 HttpClient 클래스
    • HTTP 서버 작성을 위한 HttpListener 클래스
    • SMTP를 통한 메일 메시지 작성 및 전송을 위한 SmtpClient 클래스
    • 도메인 이름과 주소의 변환을 위한 Dns 클래스
    • 전송 계층과 네트워크 계층에 직접 접근하는데 쓰이는 TcpClient, UdpClient, TcpListener Socket 클래스
  • Windows 스토어 앱은 이 형식 중 일부에만, 구체적으로 말하면 WebRequest와 WebResponse, HttpClient 에만 접근할 수 있다. 그러나 Windows 스토어 앱은 Windows.Networking.Sockets에 있는 TCP 및 UDP 통신용 WinRT 형식들도 사용할 수 있다.

네트워크 구조

  • 아래 그림은 .NET 프레임워크의 네트워킹 형식들을 해당 통신 계층별로 배치한 그림이다. 대부분의 형식은 전송 계층(transport layer)과 응용 계층(application layer)에 있다.
    • 전송 계층은 바이트 송, 수신을 위한 기본 프로토콜들(TCP와 UDP)을 정의한다.
    • 응용 계층은 구체적인 응용을 위한 고수준 프로토콜들을 정의한다. 이를테면 웹 페이지 조회(HTTP), 파일 전송(FTP), 메일 전송(SMTP), 도메인 이름과 IP 주소 사이의 변환(DNS) 등이 이 계층에 속한다.

Continue reading

C# 6.0 완벽 가이드/ 스트림과 입출력

스트림 구조

  • .NET 스트림 구조는 배경 저장소(backing store), 장식자(decorator), 적응자(adapter)라는 세 가지 개념으로 구성된다. 아래 그림에 이들이 나와 있다.

  • 배경 저장소는 입출력 연산이 실제로 효과를 발휘하는 종점(endpoint)이다. 이를테면 파일이나 네트워크 연결이 배경 저장소에 해당한다. 좀 더 정확히 말하면 배경 저장소는 다음 둘 중 하나 또는 둘 다이다.
    • 바이트들을 차례로(순차적) 읽을 수 있는 원본(source)
    • 바이트들을 차례로 기록할 수 있는 대상(destination)
  • 그런데 배경 저장소가 쓸모가 있으려면 프로그래머가 접근할 수 있어야 한다. 그런 용도로 쓰이는 표준 .NET 클래스가 바로 Stream이다.
    • 이 클래스에는 읽기, 쓰기, 위치 지정을 위한 일단의 메서드를 제공한다. 모든 지원 자료가 메모리에 들어 있는 배열과는 달리, 스트림은 자료를 직렬로(serially) 다룬다. 스트림에서는 한 번에 1바이트씩 또는 관리 가능한 크기의 블록 하나씩만 읽거나 쓸 수 있다.
    • 따라서 배경 저장소가 아무리 커도 스트림을 사용하는데는 아주 적은 양의 메모리만 필요하다.
  • 스트림은 크게 두 종류로 나뉜다.
    • 배경 저장소 스트림
      • 배경 저장소의 구체적인 종류에 맞게 특화된 스트림이다. 이를테면 FileStream이나 NetworkStream이 이 종류에 해당한다.
    • 장식자 스트림
      • 장식자 스트림은 다른 스트림의 자료를 적절히 변환하는 기능을 제공한다. 이를테면 DeflateStream나 CryptoStream이 장식자 스트림이다.
  • 장식자 스트림에는 다음과 같은 구조적인 장점들이 있다.
    • 압축이나 암호화 같은 기능을 배경 저장소 스트림마다 따로 구현할 필요가 없다.
    • 스트림의 자료를 변환(‘장식’)하기 위해 스트림의 인터페이스를 변경할 필요가 없다.
    • 장식자를 실행시점에서 스트림에 연결할 수 있다.
    • 여러 장식자를 사슬처럼 이을 수 있다(이를테면 압축한 후 암호화하는 등)
  • 배경 저장소 스트림과 장식자 스트림은 바이트만 다룬다. 이것이 유연하고 효율적인 방식이긴 하지만, 응용 프로그램은 텍스트나 XML 같은 좀 더 높은 수준의 자료를 다루는 경우가 많다. 이런 간극을 메우는 것이 적응자(adapter)이다.
    • 적응자는 특정 서식에 대응되는 형식으로 특화된 메서드들ㅇ르 가진 클래스로 스트림을 감싼다. 예컨대 텍스트 읽기 적응자는 ReadLine이라는 메서드를 제공하고 XML 쓰기 적응자는 WriteAttributes라는 메서드를 제공한다.
  • 장식자처럼 적응자도 스트림을 감싸는 래퍼(wrapper) 클래스이다. 그러나 장식자와는 달리, 적응자 자체는 스트림이 아니다. 일반적으로 적응자는 바이트 지향적 메서드들을 완전히 감춘다.
  • 정리하자면 배경 저장소 스트림은 미가공 자료(raw data)를 제공한다.
    • 장식자 스트림은 암호화 같은 변환을 수행하되, 역시 바이트 수준에서 작동한다.
    • 적응자는 문자열이나 XML 같은 고수준 형식을 다루기 위한 형식있는 메서드들을 제공한다.
    • 이들을 하나의 사슬로 이으려면 그냥 한 객체를 다른 객체의 생성자에 넣으면 된다.

Continue reading

C# 6.0 완벽 가이드/ 동시성과 비동기성

소개

  • 동시성이 필요한 상황은 대부분 다음 네 범주 중 하나에 속한다.
    • 반응성 좋은 UI 작성
    • 다수의 요청을 동시에 처리
    • 병렬 프로그래밍
    • 예측 실행
  • 현재의 컴퓨터 구조에서 프로그램은 다중 스레드 적용(multithreading)이라고 부르는 일반적인 메커니즘을 이용해서 여러 작업을 동시에 실행한다. 이중 스레드 적용은 동시성의 근본 개념 중 하나이며 CLR과 운영체제 모두 다중 스레드 적용을 지원한다. 따라서 동시적인 프로그램을 만들려면 스레드 적용의 기초, 특히 스레드들이 공유 상태에 미치는 영향을 이해하는 것이 꼭 필요하다.

스레드 적용

  • 스레드는 독립적인 실행 흐름 또는 실행 경로이다. 한 스레드의 코드는 다른 모든 스레드와는 독립적으로 진행된다.
  • 각 스레드는 운영체제의 한 프로세스 안에서 실행된다. 프로세스는 프로그램이 실행되는 하나의 격리된 환경을 제공한다.
    • 단일 스레드 프로그램에서는 프로세스의 격리된 환경에서 스레드 하나만 실행되며, 따라서 그 스레드가 그 환경의 모든 것을 독차지한다.
    • 다중 스레드 프로그램에서는 한 프로세스 안에서 여러 스레드가 실행된다. 그 스레드들은 동일한 실행 환경(특히, 동일한 메모리)를 공유한다. 이는 다중 스레드 적용이 유용한 이유 중 하나이다.
    • 예컨대 한 스레드가 배경에서 자료를 만들어서 메모리에 저장하면 다른 한 스레드가 메모리의 자료를 표시할 수 있다. 그러한 자료를 공유되는 상태(shared state) 줄여서 공유 상태라고 부른다.

Continue reading

C# 6.0 완벽 가이드/ 진단과 코드 계약

  • 뭔가 잘못되었을 때문 문제를 진단하는데 도움이 되는 정보를 확인하는 것이 중요하다.
    • 이때 IDE나 디버거가 크게 도움이 되지만 그런 도구는 개발 도중에나 사용할 수 있다. 일단 응용 프로그램을 배포/설치하고 나면 응용 프로그램 자신이 진단(diagnostic) 정보를 수집해서 기록해야 한다.
    • 이를 지원하기 위해 .NET Framework는 진단 정보 기록, 응용 프로그램 행동방식 감시, 실행시점 오류 검출을 위한 일단의 수단을 제공하며, 가능한 경우 응응 프로그램과 디버깅 도구를 연동하는 수단들도 제공한다.
  • .NET Framework는 또한 코드 계약(code contracts)을 강조하는 수단도 제공한다. .NET Framework 4.0에서 도입된 코드 계약 기능을 이용하면 메서드가 일단의 상호 의무조항들을 점검해서 만일 의무조항 위반 사항이 있으면 실행을 일찍 실패하게 만들 수 있다.
  • 이 장에 나오는 형식들은 주로 System.Diagnostics 이름공간과 System.Diagnostics.Contracts 이름공간에 정의되어 있다.

조건부 컴파일

  • 전처리기 지시자(preprocessor directive)들을 이용해서 C# 코드 안의 임의의 구역을 조건부로 컴파일할 수 있다. 전처리기 지시자는 # 기호로 시작하는 특별한 명령으로, 컴파일러에게 코드의 컴파일 방식에 관한 지시를 전달하는 역할을 한다.(그리고 C#의 코드 구성요소와는 달리 하나의 전처리기 지시자 문장은 반드시 코드 한 줄을 차지한다)
    • 논리적으로 전처리기 지시자로 시작하는 문장(줄여서 전처리기 지시문)은 실제 컴파일 작업이 일어나기 전에 실행된다. (실제로는 C# 컴파일러가 어휘 분석 단계에서 전처리기 지시자들을 처리한다).
    • 조건부 컴파일을 위한 전처리기 지시자들은 #if, #else, #endif, #elif이다.
  • #if 지시자는 컴파일러에게 지정된 전처리기 기호가 정의되어 있는 경우에만 그 다음 코드 구역을 컴파일하라고 지시한다.
    • 전처리기 기호는 코드 안에서 #define 지시자로 정의할 수도 있고 컴파일러 옵션으로 정의할 수도 있다. #define으로 정의된 기호는 해당 파일 안에서만 효력을 가지지만, 컴파일러 옵션으로 정의된 기호는 어셈블리 전체에 적용된다.
#define TESTMDOE  // #define 지시문은 파일의 처음 부분에 있어야 한다. 기호 이름은 대문자로만 구성하는 것이 관례이다.

using System;

class Program
{
  static void Main()
  {
    #if TESTMODE
      Console.WriteLine("시험 모드");
    #endif
  }
}

Continue reading

C# 6.0 완벽 가이드/ 객체 처분과 쓰레기 수거

  • 객체 중에는 열린 파일이나 자물쇠(lock), 운영체제 핸들, 비관리(unmanaged) 객체 같은 자원들을 해제하는 명시적인 해체(tear-down) 코드가 필요한 객체들이 있다. .NET의 어법에서 그런 작업을 처분(disposal)이라고 부른다.
    • .NET Framework는 객체 처분 기능을 지원하기 위해 IDisposable이라는 인터페이스를 제공한다.
  • 처분은 쓰레기 수거(garbage collection, GC)와는 다른 연산이다. 보통의 경우 처분은 프로그래머가 명시적으로 수행하지만, 쓰레기 수거는 런타임이 자동으로 수행해준다.
    • 다른 말로 하면 프로그래머는 파일 핸들이나 자물쇠, 운영체제 자원들의 해제를 신경 쓰고, CLR은 그런 자원들이 차지하던 메모리의 해제를 신경 쓴다.

IDisposable, Dispose, Close

  • .NET Framework는 해체 수단이 필요한 형식을 위해 다음과 같은 특별한 인터페이스를 제공한다.
public interface IDisposable
{
  void Dispose();
}

Continue reading

C# 6.0 완벽 가이드/ 그 밖의 XML 기술들

XmlReader 클래스

  • XmlReader는 XML 스트림을 저수준, 전진 전용 방식으로 읽어들이는 고성능 XML 판독기를 나타내는 클래스이다.
  • XML 판독기는 XmlReader.Create를 호출해서 생성하는데, 이 메서드는 Stream이나 TextReader 또는 파일 이름을 뜻하는 URI 문자열을 인수로 받는다.
using (XmlReader reader = XmlReader.Create("customer.xml"))
  • Stream과 URI에서 XML 자료를 가져오는 속도가 느릴 수도 있기 때문에, XmlReader의 메서드들에는 비차단(nonblocking) 코드를 작성하는데 적합한 비동기 버전들이 존재한다.
  • 다음은 문자열로부터 XML을 읽어 들이는 XmlReader 인스턴스를 생성하는 예이다.
XmlReader reader = XmlReader.Create(new System.IO.StringReader(myString));

Continue reading

C# 6.0 완벽 가이드/ LINQ to XML

  • .NET Famework는 XML 자료를 다루는 여러 API를 제공한다. .NET Framework 3.5부터 범용 XML 문서 처리의 주된 수단은 LINQ to XML 이다. LINQ to XML은 가볍고 LINQ 친화적인 DOM과 이를 보충하는 일단의 질의 연산자들로 구성되어 있다.
  • LINQ to XML의 모든 형식은 System.Xml.Linq 이름공간에 있다.

전체적인 구조

DOM이란 무엇인가?

  • 다음과 같은 XML 파일을 생각해 보자
<?xml version="1.0" encoding="utf-8"?>
<customer id="123" status="archived">
  <firstname>Joe</firstname>
  <lastname>Bloggs<lastname>
</customer>
  • 다른 모든 XML 파이렃럼 이 파일은 하나의 XML 선언(declaration)으로 시작한다.
    • 그 다음은 XML 문서 전체의 뿌리(루트)에 해당하는 요소(element)로 그 이름은 customer이다.
    • 이 customer 요소에는 2개의 특성(attribute)이 있다. 각 특성은 이름(id와 status)과 값(“123”, “archived”)으로 구성된다.
    • customer 요소 안에는 두 자식 요소 firstname과 lastname이 있다. 이 요소들은 각자 단순 텍스트 내용(“Joe”와 “Bloggs”)을 담고 있다.
  • 이러한 구성요소들(선언, 요소, 특성, 값, 텍스트 내용)을 각각 클래스로 나타낼 수 있다.
    • 그리고 그런 클래스에 자식 내용을 저장할 수 있는 컬렉션 속성들을 부여한다면, 문서 전체를 나타내는 객체들의 트리를 형성할 수 있다.
    • 그러한 트리가 바로 흔히 DOM이라고 줄여서 표기하는 문서 객체 모형(document object model)이다.

Continue reading

C# 6.0 완벽 가이드/ LINQ 질의 연산자

개요

  • 표준 질의 연산자들은 다음 세 범주로 나뉜다.
    • 순차열을 입력받고 순차열을 출력하는 연산자(순차열 -> 순차열)
    • 순차열을 입력받고 요소 하나 또는 스칼라값 하나늘 출력하는 연산자
    • 입력 없이 순차열을 출력하는 연산자(생성 메서드)

순차열->순차열

  • 이 범주의 질의 연산자는 하나 이상의 순차열을 입력받고 하나의 순차열을 산출한다. 대부분의 질의 연산자가 이 범주에 속한다. 아래 그림은 이 범주의 질의 연산자 중 순차열의 형태를 바꾸는 것들을 나타낸 것이다.

분류 형식 내용 연산자
필터링(선별) IEnumerable<TSource> -> IEnumerable<TSource> 원래 요소들의 부분집합을 출력한다. Where, Take, TakeWhile, Skip, SkipWhile, Distinct
투영 IEnumerable<TSource> -> IEnumerable<TResult> 주어진 람다 함수를 이용해서 각 요소를 변환한다. SelectMany는 중첩된 순차열을 평평한 순차열로 만든다(평탄화).

LINQ to SQL이나 EF에 대한 Select와 SelectMany는 내부 결합(inner join), 왼쪽 외부 결합(left outer join), 교차 결합(cross join), 비등가 결합(non-equi join)을 수행한다.

Select, SelectMany
결합 IEnumerable<TOuter>, IEnumerable<TInner> -> IEnumerable<TResult>
IEnumerable<TFirst>, IEnumerable<TSecond> -> IEnumerable<TResult>
두 순차열의 요소들을 합친다. Join과 GroupJoin 연산자는 지역 질의에 효율적으로 작동하도록 설계된 것으로, 내부 결합과 왼쪽 외부 결합을 지원한다.

Zip 연산자는 두 순차열을 동시에 열거하면서 각 요소 쌍에 함수를 적용한다. Zip 연산자에서는 두 형식 매개변수의 이름이 TOuter와 TInner가 아니라 TFirst와 TSecond이다.

Join, GroupJoin, Zip
정렬 IEnumerable<TSource> -> IOrderedEnumerable<TSource> 입력 순차열 요소들의 순서를 바꾼다. OrderBy, ThenBy, Reverse
그룹화 IEnumerable<TSource> -> IEnumerable<IGrouping<TKey, TElement>> 입력 순차열의 요소들을 적절히 묶어서 여러 개의 부분 순차열들을 출력한다. GroupBy
집합 연산 IEnumerable<TSource>, IEnumerable<TSource> -> IEnumerable<TSource> 같은 형식의 순차열 두 개를 입력 받아서 합집합, 교집합, 차집합을 출력한다. Concat, Union, Intersect
변환 메서드: 가져오기 IEnumerable -> IEnumerable<TResult> OfType, Cast
변환 메서드: 내보내기 IEnumerable<TSource> -> 배열, 목록, 사전, 조회 객체(lookup), 순차열 ToArray, ToList, ToDictionary, ToLookup, AsEnumerable, AsQueryable

Continue reading

C# 6.0 완벽 가이드/ LINQ 질의

  • LINQ (Language Intergrated Query; 언어에 통합된 질의)는 지역 객체 컬렉션과 원격 자료 저장소에 대한 형식에 안전한 구조적 질의를 작성하는데 사용하는 C# 언어 기능들과 .NET Framework 기능들을 통칭하는 용어이다. LINQ는 C# 3.0과 .NET Framework 3.5에 도입되었다.
  • LINQ를 이용하면 IEnumerable<T>를 구현하는 임의의 컬렉션(목록, 배열)과 XML DOM에 대해 질의를 수행할 수 있으며, SQL Server 데이터베이스의 테이블과 같은 원격 자료 저장소에 대한 질의도 수행할 수 있다. LINQ는 컴파일 시점 형식 점검의 장점과 동적인 질의 작성의 장점을 모두 제공한다.

첫걸음

  • LINQ의 기본적인 자료 단위는 순차열(sequence)과 요소(element)이다. 순차열은 IEnumerable<T>를 구현하는 임의의 객체이고 요소는 그 순차열에 들어있는 항목이다. 다음 예에서 names는 순차열이고, “Tom”, “Dick”, “Harray”는 요소들이다.
    • 메모리 안에 있는 객체들의 지역 컬렉션이라는 점에서 이런 순차열을 지역 순차열이라고 부른다.
string[] names = { "Tom", "Dick", "Harray" };
  • 질의 연산자(query operator)는 순차열에 어떠한 변환(transformation) 연산을 적용하는 메서드이다. 전형적인 질의 연산자는 입력 순차열 하나를 받아서 출력 순차열을 산출한다. System.Linq의 Enumerable 클래스에는 약 40개의 질의 연산자가 있는데, 이들은 모두 정적 확장 메서드로 구현되어 있다. 이들을 통틀어 표준 질의 연산자라고 부른다.
    • 지역 순차열에 대해 작용하는 질의를 지역 질의(local query) 또는 객체 대상 LINQ 질의라고 부른다.
    • LINQ는 또한 SQL Server 데이터베이스 같은 원격 자료 저장소에서 동적으로 자료를 공급받는 순차열도 지원한다. 그런 순차열은 IQueryable<T> 인터페이스를 추가로 구현하는데, 이 인터페이스에 대응되는 일단의 표준 질의 연산자들이 Queryable 클래스에 있다.
  • LINQ에서 말하는 질의는 순차열들과 질의 연산자들로 이루어진 하나의 표현식이다. 그 표현식을 평가하면 순차열들이 연산자들에 의해 변환된다.
    • 예컨대 Where 연산자를 이용하면 이름들을 담은 배열에서 길이가 4개 이상인 이름만 추출할 수 있다.
string[] names = { "Tom", "Dick", "Harray" };
IEnumerable<string> filteredNames = System.Linq.Enumerable.Where(names, n => n.Length >= 4)

foreach (int n in filteredNames)
  Console.Write(n);  // Dick Harry

Continue reading