이 글은 인스타그램(Instagram)의 엔지니어링 블로그에 작성된 글을 번역한 것이다. 원문은 여기.

최근 유사한 문제로 고민하다가 발견한, 많은 도움을 받을 수 있었던 글.

또 다른 번역글도 있으니 참고하시길.





Sharding & IDs at Instagram


인스타그램에는 매 초당 25개의 사진과 90개의 Like 정보가 등록된다. 우리는 이런 중요한 정보들이 메모리에 적합하게 적재되고 사용자들이 빠르게 사용할 수 있도록 만들기 위해 데이터를 샤딩(Sharding)하고 있다. 다시 말하면 데이터들을 여러 개의 작은 버킷으로 나누고 각각의 버킷은 데이터의 일부를 저장하는 형태이다.

우리의 애플리케이션 서버는 DJango로 구현되어 있으며 백엔드 데이터베이스 서버로 PostgreSQL을 사용하고 있다. 데이터를 샤딩하기로 결정한 이후 우리가 가진 첫 번째 고민은 주 데이터 저장소로서 PostgreSQL을 그대로 활용할 것인지 아니면 다른 것으로 변경할 것인지 여부를 결정하는 것이었다. 몇 가지 NoSQL 솔루션을 검토해 봤지만 현재 우리에게 가장 적합한 솔루션은 여러 대의 PostgreSQL 서버에 데이터를 샤딩하는 것이라는 결론에 도달했다.

그러나 데이터를 이들 서버에 나누어 기록하기에 앞서 우리는 데이터베이스 내에 분산될 각각의 데이터(예를 들면 우리의 시스템으로 등록되는 각 사진들)를 위한 유일한 식별자를 할당해야 하는 이슈를 해결해야만 했다. 동시에 여러 데이터베이스에 데이터가 추가되는 환경에서는 단일 데이터베이스에서 동작하는 해결책 - 예를 들면 데이터베이스의 자동 증가 기본 키 기능을 사용하는 등 - 은 올바른 해결책이 될 수 없었다. 이 블로그의 내용은 이와 같은 이슈를 우리가 어떻게 해결했는지를 설명한다.

시작하기에 앞서 우리는 시스템의 기본적인 요구 사항을 다음과 같이 정리했다.

  1. 생성된 ID는 반드시 시간 순으로 정렬이 가능해야 한다 (예를 들어 모든 사진 ID는 사진에 대한 추가 정보를 조회하지 않고도 정렬이 가능해야 한다).
  2. ID는 64비트여야 한다(인덱스의 크기를 줄이고 시스템에 적용된 Redis와 같은 더 나은 저장소를 지원할 수 있다).
  3. 우리 시스템은 새로운 시스템 컴포넌트의 도입을 최소화해야 한다. 겨우 몇 명의 엔지니어로 인스타그램의 확장성을 확보하기 위해서는 우리가 신뢰하는 간결하며 이해하기 솔루션으로 구성되어야 한다.

기존의 솔루션

이미 ID 생성 문제에 대한 해결책은 여러 가지가 존재한다. 이들 중 우리가 고려해 본 방법은 다음과 같다.

웹 애플리케이션에서 ID를 생성하는 방법

이 방법은 ID 생성을 데이터베이스가 아닌 애플리케이션에 완전히 위임하는 방식이다. 예를 들어 몽고DB의 ObjectId는 12바이트 길이의 인코딩된 타임스탬프를 첫 번째 컴포넌트로 사용한다. 또 다른 방법은 UUID를 사용하는 방법이다.

장점:
  1. 각각의 애플리케이션 스레드가 ID를 독자적으로 생성하기 때문에 ID 생성의 실패와 병목 현상을 최소화할 수 있다.
  2. ID의 첫 번째 컴포넌트로 타임스탬프를 사용하면 ID를 시간 순으로 정렬할 수 있다.
단점:
  1. 유일성을 보장하기 위해서는 통상 더 많은 저장 공간(96비트 혹은 그 이상)이 필요하다.
  2. 일부 UUID 타입은 완전히 랜덤하게 만들어져 본질적으로 정렬이 불가능하다.

독립된 서버에서 ID를 생성하는 방법

아파치 주키퍼(ZooKeeper)를 사용하는 트위터의 Snowflake와 같은 Thrift 서비스를 이용하여 64비트의 유일한 ID를 생성한다.

장점:
  1.  Snowflake의 ID는 64비트이며 UUID의 절반에 지나지 않는다.
  2. 정렬을 위해 시간을 ID의 첫 번째 컴포넌트로 사용할 수 있다.
  3. 일부 노드가 다운되더라도 사용 가능한 분산 시스템이다.
단점:
  1. 복잡도가 증가하며 시스템 아키텍처 내의 구성 요소가 증가하게 된다 (ZooKeeper, Snowflake 서버 등)

DB 티켓 서버를 활용하는 방법

이 방법은 데이터베이스의 자동 증가 기능을 이용하여 유일성을 확보하는 방법이다. 플리커(Flickr)가 이 방식을 사용하지만 두 개의 티켓 DB(하나는 홀수, 하나는 짝수를 담당)를 통해 SPOF(Single Point of Failure, 시스템 컴포넌트 중 하나가 다운됨으로서 전체 시스템이 마비되는 현상)을 해결하고 있다.

장점:
  1.  DB는 이미 친숙하며 확장에 대한 예측이 가능하다.
단점:
  1. 결과적으로 쓰기 병목이 발생할 수 있다 (플리커에서 이런 현상을 경험했지만 대규모 서비스에서는 큰 이슈가 아닐 수 있다).
  2. 관리해야 할 머신(혹은 EC2 인스턴스)이 증가한다
  3. 단일 DB를 사용하면 SPOF의 근원이 될 수 있다. 여러 DB를 사용하면 시간으로 정렬 가능한 ID의 생성을 보장할 수 없다.
이상의 방법들을 검토해 본 결과 트위터의 Snowflake가 우리의 요구사항에 가장 적합했지만 ID 서비스를 위해 시스템의 복잡도가 증가하는 것이 마음에 걸렸다. 결국 우리는 PostgreSQL을 통해 이와 유사한 컨셉의 방법을 직접 구현하기로 결정했다.

우리의 해결책

우리의 샤딩 시스템은 수천 개의 '논리적' 샤드로 구성되며 각각의 논리적 샤드는 보다 적은 수로 구성된 물리적 샤드에 매핑된다. 이렇게 함으로써 몇 대의 데이터베이스 서버만으로 샤드를 구성할 수 있었으며 향후 데이터를 새로운 버켓으로 이동할 필요없이 단순히 논리적 샤드를  다른 데이터베이스 서버로 이동하는 것만으로 샤드를 확장할 수 있게 되었다. 또한 PostreSQL의 스키마 기능을 이용하여 손쉽게 스크립팅과 관리가 가능하도록 구성하였다.

스키마(개별 테이블에 대한 SQL 스키마와 혼동하지 않기를 바란다)는 PostgreSQL이 제공하는 논리적 그룹화 기능이다. 각각의 PostreSQL DB는 여러 개의 스키마를 가질 수 있으며 각각의 스키마는 하나 이상의 테이블을 가질 수 있다. 테이블 이름은 DB가 아닌 스키마 별로 유일해야 하며 기본적으로 PostreSQL은 이 모든 것을 'public'이라는 이름의 스키마를 통해 관리한다.

앞서 이야기한 각각의 '논리적' 샤드란 우리의 시스템 내에서는 Postgres 스키마이며 샤드된 각 테이블(예를 들면 사진 데이터들)은 각 스키마에 위치하게 된다.

ID의 생성은 Postgres가 제공하는 자동 증가 기능과 내장 프로그래밍 언어인 PL/PSGSQL을 이용하여 각 샤드 내의 각 테이블에서 담당한다.

각 ID는 다음과 같이 구성된다.
  • 밀리 초 단위의 시간을 위한 41비트(41년을 사용할 수 있는 ID를 생성할 수 있다).
  • 논리적 샤드 ID를 표현하기 위한 13비트
  • 자동 증가 값을 1024로 나눈 나머지 값을 표현하기 위한 10비트. 이는 샤드 당 및 초당 1024개의 ID를 생성할 수 있음을 의미한다.
예를 들어 설명해 보자. 현재 시간이 2011년 9월 9일 오후 5시 정각이며 시스템이 2011년 1월 1일부터 가동되기 시작했다고 가정해보자. 그렇다면 현재는 시스템 가동 이후 1387263000 밀리 초가 지난 시간이며 따라서 ID의 처음 41비트는 다음과 같은 값이 채워진다.

id = 1387263000 << (64 - 41)


다음으로 추가할 데이터의 샤드 ID를 결정해야 한다. 데이터를 사용자 ID를 기준으로 샤딩하며 2000개의 논리적 샤드가 존재하고 사용자 ID가 31341이라고 가정하면 샤드 ID는 31341 % 2000 = 1341이 된다. 따라서 ID의 다음 13비트는 이 값으로 채워진다.
id |= 1341 << (64 - 41 - 13)

마지막으로 자동 증가 값(이 값은 각 스키마 내의 각 테이블마다 유일하다)을 얻어 나머지 비트를 채워야 한다. 이미 테이블에  5,000개의 ID가 생성되어 있다고 가정하면 다음 값은 5001이며 이 값을 1024로 나누어(그러면 10비트로 채우기에 적당하다) 나머지 비트를 다음과 같이 채우면 된다.
id |= (5001 % 1024)

이제 ID가 완성되었으므로 이 값을 RETURNING 키워드를 통해 애플리케이션 서버로 리턴하면 된다.

아래의 코드는 완성된 PL/PGSQL 코드이다(예제 스키마는 insta5이다).
CREATE OR REPLACE FUNCTION insta5.next_id(OUT result bigint) AS $$
DECLARE
    our_epoch bigint := 1314220021721;
    seq_id bigint;
    now_millis bigint;
    shard_id int := 5;
BEGIN
    SELECT nextval('insta5.table_id_seq') %% 1024 INTO seq_id;

    SELECT FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000) INTO now_millis;
    result := (now_millis - our_epoch) << 23;
    result := result | (shard_id << 10);
    result := result | (seq_id);
END;
$$ LANGUAGE PLPGSQL;

그리고 테이블은 다음과 같이 생성한다.
CREATE TABLE insta5.our_table (
    "id" bigint NOT NULL DEFAULT insta5.next_id(),
    ...테이블의 나머지 스키마...
)

이로서 끝이다. 기본 키는 애플리케이션 내에서 완전히 유일하다(게다가 샤드 ID를 포함하고 있어 매핑이 더욱 쉬워졌다). 이 방법을 실제 서비스에 적용해 본 결과는 매우 만족스러웠다. 이같은 확장성에 대한 해결책을 마련하는데 흥미가 있다면 우리 회사에 지원해 보기 바란다.

Mike Krieger, co-founder


















Posted by 웹지니 트랙백 1 : 댓글 14

(by 웹지니, aspnetmvp@gmail.com)

불과 수 년 전까지만 해도 웹 애플리케이션을 개발하면서 웹 애플리케이션의 Globalization을 고려하는 개발자나 그에 대한 수요는 그다지 많지 않았다. 그도 그럴 것이 당시 대한민국 인터넷 사용자를 대상으로 한 시장이 지금처럼 포화상태도 아니었을 뿐더러 대한민국 IT 업계가 아직은 대한민국 인터넷 사용자만으로도 먹고 살만 했기 때문이다. 물론 서비스 자체가 여전히 성장의 여지가 남아있었음도 한 몫 했을테고.

그러나 시간이 점차 흐르면서 이제 국내 시장을 타겟으로 개발되는 서비스들도 글로벌 시장을 고려하지 않을 수 없게 되었다. 기껏해야 5천만, 그 중 인터넷 인구 2천만이 조금 넘는 대한민국 인터넷 시장에서 서비스와 서비스를 제공하는 회사의 성장이 한계에 부딪히기까지는 그다지 많은 시간이 필요하지 않을 것이라는 것쯤은 입아프게 말하지 않아도 조금만 생각해 보면 누구나 알 수 있는 자명한 사실.

그러나 최근 들어 게임 및 포털들의 해외 진출이 시작되고 중소 기업들도 좁은 국내 시장에서 탈피하여 해외로 진출하지 않는 이상 더 이상의 성장이 어렵기 때문에 이제 웹 개발자들에게 Globalization이란 애플리케이션의 설계 단계에서부터 반드시 고려되어야 하는 필수적인 요소가 되어가고 있다. 태생이 꼬부랑 글씨 쓰는 애들은 자기네 나라 말로만 만들어도 지구 상에 왠만한 나라 사람들은 무리 없이 사용할 수 있지만 우리네 한글은 그 우수성에도 불구하고 대한민국에서만 사용되다보니 한글로 아무리 좋은 서비스를 만들어봤자 우리만의 잔치가 될 뿐이며 전 세계 사용자를 대상으로 하는 글로벌 서비스가 될 수 없다.

필자는 현재까지 약 6년여에 걸쳐 여러 글로벌 서비스를 구축, 운영해 온 경험을 가지고 있으며 그런 필자의 경험이 독자 여러분에게 조금이나마 도움이 되기를 바라는 마음으로 글로벌 웹 서비스 구축에 필요한 기본적인 몇 가지를 여러분과 공유하고자 하는 의미에서 이번 포스트 시리즈를 기획하게 되었다.  

글로벌 웹 애플리케이션 구축에서 첫 번째로 소개하고자 하는 내용은 MaxMind사의 IP 데이터베이스를 활용하는 방법이다. 웹 애플리케이션 경험이 조금이라도 있는 개발자라면 MaxMind 사의 홈페이지를 통해 필요한 정보를 어렵지 않게 수집할 수 있겠지만 이번 포스트는 간략한 예제를 통해 웹 애플리케이션에서 IP 데이터베이스를 활용하는 방법을 빠르게 소개하는 것이 목적이다.

글로벌 서비스에서의 IP Database와 위치 기반 콘텐츠의 중요성

IP 데이터베이스는 말 그대로 전 세계에 널려있는 IP 주소에 대한 데이터베이스이다. 특정 IP가 어느 나라, 어느 지역에 할당되어 있으며 어떤 ISP에서 사용하고 있는지, 혹은 어떤 사용자에게 할당되어 있는지 등 유용한 정보가 담겨있다. 애플의 아이폰 덕분에 위치 정보가 어디에 어떻게 활용되는지는 이제 누구나 익숙하겠지만 IP 주소에 기반한 위치 정보는 글로벌 시장을 타겟으로 하는 웹 서비스 입장에서는 매우 중요하다. 왜냐하면 내가 만든 서비스를 어느 나라에서 많이 사용하는지, 또는 어떤 지역에서 사용하는지, 어느 지역 사용자가 어떤 서비스의 활용도가 높은지 등 서비스를 분석하고 향상시키기 위해 필요한 모든 분석 지표에 지역 및 위치 정보가 활용되기 때문이다.

뿐만 아니라 서비스가 제공하는 콘텐츠도 이 위치 정보에 의해 좌우되기도 한다. 예를 들어 얼마 전 구글이 야심차게 오픈했다는 구글 뮤직 페이지를 방문해 보면 아래와 같은 화면을 볼 수 있을 것이다.

그림 1: 구글 뮤직 페이지 - 한국에는 언제 열어주려나...

현재 사용자의 접속 지역을 토대로 페이지의 콘텐츠를 제어한 좋은 예이다. 만일 내가 만드는 서비스가 디지털 아이템, 특히 CP(Content Provider, 콘텐츠 공급자)에 의해 제공되는 아이템을 온라인에서 판매하는 서비스라면 사용자의 접속 지역을 파악하는 것은 더욱 중요해진다. 왜냐하면 이들 콘텐츠의 판매에 대한 라이선스 계약에 따라 특정 지역에서는 해당 아이템을 사용자가 구매하지 못하도록 제어할 필요가 있기 때문이다. 특히 각 국가나 지역별로 판매하는 디지털 아이템의 가격이 달라질 수 있다면 현재 접속 중인 사용자의 위치는 더욱 중요해진다. 사용자의 접속 지역을 잘못 판단하면 향후 매출 집계 및 CP에 대한 정산에 있어 오차가 발생할 수 있으며 이는 곧 회사의 손실로 이어지기 때문이다.

따라서 IP 데이터베이스의 구축 및 사용자의 위치 정보 수집 및 활용 등은 웹 애플리케이션을 위한 프레임워크 레벨에서 구현되어 전체 조직 내의 개발자들이 공통의 코드를 사용하도록 해야 한다. 특히 IP 데이터베이스가 문제인데 구글이나 마이크로소프트 등 대형 인터넷 서비스/소프트웨어 업체 수준의 정밀한 IP 데이터베이스가 우리 회사에도 있다면 얼마나 좋겠냐마는 현실적으로 어려운 이상 대안을 찾을 수 밖에 없다. 구글 신에게 도움을 청해보면 알겠지만 그래도 현재 가장 쓸만한 IP 데이터베이스를 제공하는 곳이 바로 MaxMind.com이다.

MaxMind GeoIP 솔루션 소개

MaxMind사는 전 세계의 IP 데이터베이스를 서비스로 제공하는 회사로 IP를 이용하여 해당 IP가 사용되는 국가 및 지역에 대한 정보 및 해당 IP의 위치(위도 및 경도) 정보를 제공하는 데이터베이스와 API를 제공한다. 크게 무료 버전과 유료 버전으로 구분되며 무료 버전(GeoLiteCity라는 이름으로 제공된다)의 경우 IP에 대한 국가 수준의 정보가 제공되는 반면 유료 버전(GeoIPCity라는 이름으로 제공된다)을 구매하면 도시 수준의 보다 상세한 위치 정보를 얻을 수 있게 된다. 아래 그림은 MaxMind GeoLiteCity 데이터베이스에 대한 소개 및 다운로드 페이지의 모습이다.

그림 2: MaxMind사의 GeoLiteCity 데이터베이스 소개 페이지

그림 2에서 붉게 표시한 링크를 클릭하면 GeoLiteCity.dat.gz 파일을 다운로드할 수 있다. 이 파일은 GeoLiteCity.dat 파일을 압축한 파일이며 이 파일이 바로 전 세계 IP 주소의 위치 정보를 담고 있는 데이터베이스 파일이다. 물론 페이지 아래 쪽에서 CSV 형식의 파일을 다운로드 후 여러분이 사용하는 RDBMS로 가져올 수도 있다. 이 포스트에서는 GeoLiteCity.dat 파일을 사용하기로 한다. GeoLiteCity.dat 파일은 단순한 바이너리 파일이며 따라서 다운로드한 파일을 그대로 웹 서버에 배포하여 사용할 수 있다. 이 파일로부터 특정 IP 주소의 위치 정보를 가져오기 위해서는 별도의 API가 필요하며 그림 2의 웹 페이지 좌측 메뉴엣 [GeoIP APIs] 링크를 클릭하여 독자 여러분의 개발 언어에 따라 적당한 API를 다운로드하면 된다. 이 포스트에서는 C#으로 작성된 .NET용 API를 사용한다. 다운로드 한 C# API 압축 파일의 압축을 해제해보면 아래 그림과 같은 파일들이 존재하는 것을 확인할 수 있다.

그림 3: MaxMind C# API를 구성하는 소스 파일들

전체 소스가 모두 필요한 것은 아니며 ~Example.cs 파일을 제외한 나머지 파일들만 프로젝트에서 사용하면 된다.

Working with MaxMind API

그러면 간단한 예제를 통해 MaxMind API의 사용법을 확인해 보자. 먼저 Visual Studio를 실행하고 아래 그림과 같이 필요한 파일들을 프로젝트에 추가하자.

그림 4: ASP.NET MVC2 프로젝트를 구성한 모습

그림 4에서 보듯이 GeoLiteCity.dat 파일은 웹 애플리케이션 프로젝트의 루트에 추가했으며 MaxMind  API를 구성하는 소스 파일들은 GeoIP라는 폴더에 추가하였다. MaxMind API의 핵심은 LookupService 클래스로 이 클래스에 IP 주소를 전달하면 Location 클래스의 인스턴스를 돌려준다. Location 클래스는 지정된 IP 주소를 사용하는 지역의 국가 코드와 지역 코드, 위도 및 경도, 우편 번호 등의 데이터를 가지고 있다. 그러면 ASP.NET MVC 프로젝트에 기본으로 생성된 HomeController.cs 파일을 열고 Index 액션 메서드를 아래와 같이 수정해보자.

public ActionResult Index()
{
	var lookup = new LookupService(Server.MapPath("GeoLiteCity.dat"));
	var location = lookup.getLocation("168.126.63.1");

	ViewData["country"] = String.Format("Country: {0} ({1})",
		location.countryName, location.countryCode);
	ViewData["location"] = String.Format("Location: Latitude: {0}, Longitude: {1}",
		location.latitude, location.longitude);

	return View();
}

이제 /Views/Home/Index.aspx 파일을 열고 코드를 아래와 같이 수정하자.


    

<%= Html.Encode(ViewData["country"]) %>

<%= Html.Encode(ViewData["location"]) %>


이 페이지를 실행한 결과는 아래 그림과 같다.

그림 5: 예제 코드의 실행 결과

그림에서 보듯이 지정한 IP 주소의 소유 국가와 위치 정보가 잘 나타나는 것을 볼 수 있다. 이처럼 API가 제공하는 정보들을 활용하면 서비스의 콘텐츠를 지역별로 얼마든지 제어할 수 있게 된다.

MaxMind API 사용 시 주의할 점

지금까지 MaxMind API를 이용하여 IP 주소를 기준으로 사용자의 위치 정보를 얻어내는 방법을 소개하였다. 포스트를 마무리하면서 한 가지 주의할 점은 IP 주소를 이용한 위치 정보는 (당연하겠지만) 공인 IP를 대상으로 하기 때문에 사설 IP를 사용하는 사무실 이나 가정에서 개발자 PC에 할당된 주소를 API에 전달하면 null이 리턴된다. 또한 MaxMind IP 데이터베이스의 커버리지는 100%가 아닌 99.5%이므로 0.5%는 잘못된 정보가 리턴되거나 혹은 null이 리턴될 수 있다. 따라서 Location 객체 등을 사용하기 전에는 반드시 null 여부를 검사하는 것이 좋다.

또한 MaxMind가 제공하는 무료 버전의 IP 데이터베이스를 사용할 계획이라면 국가 및 위/경도 값 외에 지역이나 우편 번호 등 나머지 정보들은 커버리지가 낮은 편이므로 이 정보에 너무 의존하지 않는 것이 좋다. 만일 이 정보들이 반드시 필요하다면 유료 버전을 구매할 것을 권한다.




Posted by 웹지니 트랙백 0 : 댓글 4
안녕하세요 웹지니입니다.

실로 오랜만에 블로그 포스팅인데 광고질 + 자랑질이네요 ^^
수 개월에 걸쳐 준비한 Test-Drive ASP.NET MVC의 번역서가 곧 출간될 예정입니다.
표지는 아래와 같이 결정되었어요. 


이번 번역서도 앞서 출간된 Professional Enterprise .NET과 마찬가지로 제이펍에서 출간됩니다.

예상 출간일은 6월 초이며 출간되면 이번엔 좀 새로운 형식(?)의 이벤트를 통해 한 분께 증정할까 해요.

어줍잖은 번역의 책이 여러분께 작은 도움이 되기를 소망합니다 :)

 
Posted by 웹지니 트랙백 0 : 댓글 15
빼꼼...

안녕하세요 웹지니입니다! 흐헤헤 -ㅅ-;;;
어제 있었던 북한의 도발 때문에 많이들 불안하셨을 것으로 생각합니다.
저로서는 조금 더 부지런히, 열심히 살아야겠다고 다시 한 번 추스르게 된 계기가 되었어요.
뭐... 이번 포스트를 꼭 그래서 올리는 건 아니구요...-ㅅ-;;

예전에 블로그를 통해 Custom HtmlHelper 메서드를 구현하는 것과 관련된 글을 올린 적이 있었어요. 벌써 1년도 넘게 지난 시점이군요. 당시 HtmlHelper 클래스의 확장 메서드를 구현하면서 최종적으로 태그를 생성하는 과정에서 ASP.NET MVC가 제공하는 TagBuilder 클래스를 사용했었는데요.

오늘 포스트는 요즘 유행(?)하는 Fluent Interface 패턴 형태로 TagBuilder 클래스를 손쉽게 활용할 수 있도록 확장하는 내용을 담아봅니다. Fluent Interface 패턴을 활용하면 매우 읽기 쉬운 코드를 작성할 수 있다는 장점이 있지요. 해서 한 번 만들어 봤습니다. 이름하야 FluentTagBuilder!!



Implementing FluentTagBuilder

아시는 분은 아시겠지만 Fluent Interface의 핵심은 메서드가 자기 자신 혹은 관련된 다른 객체의 인스턴스를 리턴하는 것입니다. 즉, 메서드 호출 시 스스로를 계속 리턴하여 연속적으로 메서드를 호출할 수 있도록 구현하는 것이지요. 실질적으로 HTML 태그를 생성하는 기능은 TagBuilder가 이미 훌륭하게 구현하고 있으니 그냥 맡겨버리고 우리는 이 TagBuilder 클래스의 인스턴스를 계속해서 리턴하는 Wrapper 메서드만을 구현해주면 됩니다. 일단 코드를 좀 살펴볼까요?

public class FluentTagBuilder : TagBuilder
{
	public FluentTagBuilder(string tagName)
		: base(tagName)
	{ }

	public FluentTagBuilder Append(FluentTagBuilder innerTag)
	{
		base.InnerHtml += innerTag.ToString();
		return this;
	}

	public FluentTagBuilder AppendHtml(string html)
	{
		base.InnerHtml += html;
		return this;
	}

	public FluentTagBuilder AppendText(string html) {
		  base.InnerHtml += HttpUtility.HtmlEncode(html);
		  return this;
	}

	public FluentTagBuilder With(FluentTagBuilder tagBuilder)
	{
		base.InnerHtml = tagBuilder.ToString();
		return this;
	}

	public FluentTagBuilder With(string attribute, string attributeValue)
	{
		base.Attributes.Add(attribute, attributeValue);
		return this;
	}

	public FluentTagBuilder With(IDictionary attributes)
	{
		base.MergeAttributes(attributes);
		return this;
	}

	public FluentTagBuilder And(string attribute, string attributeValue)
	{
		base.Attributes.Add(attribute, attributeValue);
		return this;
	}

	public FluentTagBuilder AndIf(bool condition, string attribute, string attributeValue)
	{
		if (condition)
		{
			base.Attributes.Add(attribute, attributeValue);
		}
		return this;
	}
}
예제 코드에서 크게 어렵거나 이해가 안 가는 부분은 없으실거라 생각해요. 그러면 실제로 어떻게 사용하는지를 알아볼까요? 예전 포스트에서 구현했던 이미지를 포함하는 링크를 생성하는 ImageLink 메서드를 FluentTagBuilder 클래스를 이용하면 아래와 같이 구현할 수 있습니다.

public static MvcHtmlString ImageLink(
	this HtmlHelper helper, 
	string imageUrl, 
	string actionName, 
	string controllerName, 
	RouteValueDictionary routeValues, 
	IDictionary imgAttributes, 
	IDictionary htmlAttributes)
{
	UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
	string resolvedImageUrl = urlHelper.Content(imageUrl);

	string url = UrlHelper.GenerateUrl(
		null, actionName, controllerName, routeValues, helper.RouteCollection,
		helper.ViewContext.RequestContext, true
	);
	
	FluentTagBuilder imageTagBuilder = new FluentTagBuilder("img")
		.With(imgAttributes)
		.With("src", resolvedImageUrl);

	return MvcHtmlString.Create(new FluentTagBuilder("a")
		.Append(imageTagBuilder)
		.With("href", url)
		.With(htmlAttributes)
		.ToString()
	);
}
18번 라인부터 27번 라인까지의 코드가 바로 FluentTagBuilder 클래스를 이용하여 태그를 생성하는 부분입니다. 예전 포스트와 비교할 때 코드가 확연히 줄어들었을 뿐 아니라 코드를 이해하기도 더 쉬워진 것 같죠?

아직 어딘가에 살아있음을 보여주기 위한 짧은 포스트는 여기서 마치고 이만 물러갑니다.
멋진 하루 보내시기 바랍니다!
Posted by 웹지니 트랙백 0 : 댓글 6
안녕하세요? 웹지니입니다.

지난 수 개월간 공들여 번역했던 Professional Enterprise .NET 서적의 표지 디자인을 오늘 확인할 수 있었습니다. 지난 번 Professional ASP.NET MVC 번역서와 마찬가지로 제이펍에서 출간될 예정이에요. 다음 주말 경이면 예판을 시작할 수 있을 것 같습니다.


항상 새로운 책을 내놓을 때는 만감이 교차하는 것 같아요. 뭔가 뿌듯하면서도 한편으로는 '이 책이 척박한 IT 환경 속에서 힘들게 일하는 우리나라 닷넷 개발자들에게 도움이 될까'라는 걱정이 앞서기도 합니다. 특히 이번 책은 저에게도 생소한 내용이라 더욱 걱정이 크네요. 하지만 많은 분들께 도움이 될 수 있는 좋은 내용들로 채워진 책이기에 두려움 보다는 기쁜 마음으로 이번 책을 받아보려 합니다.

이쯤에서 또 한 번 이벤트를 질러볼까요? 지난 번 ASP.NET MVC 번역서 출간 때 폭발적인(응?) 호응을 불러 일으켰던 이벤트!

이 포스트에 첫 번째로 댓글을 작성하시는 분께 Professional Enterprise .NET 번역서를 한 권 보내드리도록 하겠습니다. 댓글 작성 시 비밀 댓글로 수령지 주소와 메일 주소, 연락처 등을 알려주시면 책이 출간된 후에 역자 증정본 중 한 권을 선물로 보내드릴게요.

결과를 바로 알 수 있으니 많은 참여를 부탁하는 건 무리겠죠? -ㅅ-;;;;;
즐거운 하루 되세요!



Posted by 웹지니 트랙백 0 : 댓글 10

Using NHibernate: Introduction

2010.07.23 14:44 from ASP.NET
안녕하세요? 웹지니입니다.

최근 저는 회사에서 진행 중인 프로젝트에 유명한 ORM도구인 NHIbernate를 적용하고 있습니다. 사실 시작한지 얼마 되지 않아 많은 것을 알지는 못하지만 그래도 제가 경험했던 내용들을 토대로 간단하게 NHibernate를 소개하기 위해 짧은 연재를 진행하려고 합니다. 물론 초급 수준의 포스트가 되겠지만 그래도 많은 분들께 조그마한 도움이 될 수 있기를 바라면서 시작해 보겠습니다.

NHibernate란?

NHibernate는 이미 자바 진영에서는 오래전부터 활용되고 있는 Hibernate 프레임워크를 .NET 환경으로 포팅한 프레임워크입니다. NHibernate는 Linq To SQL이나 Entity Framework와 같은 ORM(Object-Relational Mapping) 프레임워크로 쉽게 말하자면 DB 스키마를 C# 객체로 매핑하여 개발자가 직접 SQL 쿼리를 작성하지 않고 객체 중심의 프로그래밍이 가능하도록 해주는 프레임워크입니다.

최근의 프로그래밍 개발 패턴을 살펴보면 종전처럼 DB 설계부터 시작하여 쿼리를 중심으로 애플리케이션을 구현해 나가는 Bottom-up 방식의 Data-First 패턴에서 모델 중심 혹은 데이터 중심으로 애플리케이션을 설계하고 이에 따라 애플리케이션과 DB를 구현해 나가는 Top-Down 방식의 Model-First 패턴이 주를 이루고 있습니다.

특히 DDD(Domain Driven Development) 방법론이 확산되면서 도메인 모델 패턴이 널리 알려지고 이와 같은 패턴을 통해 구현되는 애플리케이션의 수가 점차 늘어나면서 ORM 도구의 사용 역시 매우 활발하게 이루어지고 있습니다. 그렇다면 .NET 환경에서 선택할 수 있는 ORM 도구는 NHibernate가 유일했을까요? 물론 그렇지 않습니다. .NET 환경에서 ORM은 .NET 3.0의 Linq 기술과 함께 릴리즈된 Linq to SQL에 의해 알려지게 되었습니다.

또한 비교적 소규모의 단조로운 데이터베이스를 위한 Linq to SQL을 보완하기 위해 마이크로소프트는 Entity Framework(이하 EF)라는 또 다른 형태의 ORM 도구를 준비하여 릴리즈 하기도 했습니다. 그리고 EF는 .NET 4에 이르러 PI(Persistence Ignorance) 개념 등 진정한 의미의 도메인 모델 패턴을 지원하기 위해 큰 폭으로 발전하고 있습니다. 그럼에도 불구하고 제가 NHibernate를 선택한 까닭을 요약해 보면 다음과 같습니다.

1. Linq to SQL의 경우 1:1 관계만을 지원하여 복잡한 비즈니스 애플리케이션의 구현에 적합하지 않다.
2. Linq to SQL은 마이크로소프트 SQL Server만을 지원한다.
3. EF의 경우 1:N 관계를 지원하지만 마이크로소프트 .NET 프레임워크에서만 사용이 가능하다. (현재 Mono 프레임워크는 EF를 완벽하게 구현하고 있지 못합니다).
4. EF 역시 마이크로소프트 SQL Server만을 지원한다.  이 부분은 제가 잘못 알고있었기에 정정합니다. EF는 SQL Server 외의 다른 RDBMS벤더도 지원되고 있습니다. 제보(?)해주신 권효중님께 감사드립니다. ㅎㅎ
5. NHibernate는 이미 수 년 전부터 자바 진영에서 활용되어 온 이미 검증된 프레임워크이다.
6. NHibernate는 Mono 프레임워크를 지원한다.
7. NHibernate는 현존하는 거의 모든 RDBMS를 지원한다.
8. 아직 개발 중이긴 하지만 NHibernate의 3.0 버전에서는 HQL(Hibernate Query Language)를 기반으로 한 Linq 구문의 지원이 가능하다 (현재 릴리즈된 최신 버전은 2.1.2의 경우 Linq to NHibernate를 통해 부분적인 Linq 구문의 지원이 가능합니다. 그러나 Linq to NHibernate의 경우 HQL 기반이 아닌 NHibernate의 Criteria API를 기반으로 하고 있어 JOIN 등 여러 가지 구문을 지원하지 못합니다. 3.0 버전은 현재 알파 버전이지만 NHibernate의 소스 저장소에서 다운로드하여 빌드 후 사용이 가능합니다).

그렇습니다. 아시는 분은 아시겠지만 저는 Windows 서버 환경 뿐 아니라 리눅스 서버 환경도 고려한 제품을 개발 중이며 이에 따라 Mono 프레임워크의 지원 여부가 선택에 가장 큰 영향을 미쳤습니다. 그러나 실상 NHibernate 프레임워크는 Mono 프레임워크를 지원한다는 것 외에도 많은 장점을 가진 프레임워크입니다.

왜 ORM 도구를 사용해야 하는가?

.NET 개발자들에게 있어 ORM이라는 도구는 아직은 다소 생소한 도구라고 할 수 있습니다. 해외 개발자들은 그렇지 않겠지만 국내 개발자 중 대부분은 Linq to SQL을 통해 ORM이라는 개념을 처음 접하게 된 경우가 생각보다 많았습니다. 더군다나 Linq to SQL이 처음 .NET 개발자들에게 공개되었을 때 ORM 도구를 반대하던 - 보다 정확히 말하자면 Linq to SQL을 반대하던 - 개발자들은 크게 다음과 같은 점들을 ORM 도구의 단점으로 지적했습니다.

1. ORM 도구가 생성하는 SQL 구문의 변경이나 최적화가 불가능하다.
2. ORM 도구를 사용하게 됨으로써 DataSet과 같은 기존의 범용 클래스를 사용하는 대신 테이블의 각 컬럼과 매핑되는 속성을 가진 별도의 클래스를 구현해야 한다. 애플리케이션의 볼륨이 커질수록 이러한 클래스들의 수 역시 늘어날 것이며 이는 결과적으로 애플리케이션의 실행 성능에 영향을 미칠 수 있다.

여기에서 모델 객체를 추가로 구현해야 하는 부담에 대한 지적이 없었던 이유는 대부분의 ORM 도구들은 데이터베이스 테이블을 기반으로 모델 객체를 자동으로 생성하는 코드 자동 생성 기능을 제공하고 있기 때문입니다. 특히 Linq to SQL과 Entity Framework는 자동 객체 생성 기능을 훌륭하게 지원하고 있습니다. 그에 반해 NHibernate 프레임워크는 개발자가 직접 모델을 구현하고 모델과 테이블 사이의 매핑을 별도의 XML 파일로 구성해야 한다는 단점이 존재합니다. 사실 이런 부분은 생산성의 저하를 가져오게 마련입니다. 그러나 다행히 이런 문제는 Visual NHibernateFluent NHibernate와 같은 도구를 함께 사용하면 얼마든지 극복이 가능합니다.

다시 본론으로 돌아가 앞서 언급했던 단점들이 존재함에도 불구하고 왜 ORM 도구를 사용해야 하는지에 대해 다시 생각해 보겠습니다. 확실히 많은 수의 모델 객체와 이에 대한 매핑 정보를 유지해야 하는 것은 애플리케이션의 실행 성능에 부담을 줄 수 있습니다. 물론 쿼리 자체를 개발자가 임의로 최적화할 수 없다는 점도 단점이 될 수 있습니다. 그럼에도 불구하고 ORM 도구를 사용해야 하는 이유를 저는 다음과 같이 생각합니다.

1. 데이터베이스에 대한 추상화: 대부분의 ORM 도구는 여러 가지 RDBMS를 지원하며 각각의 RDBMS 벤더에 적합한 쿼리를 자동으로 생성합니다. 따라서 개발자는 여러 DBMS를 지원하는 소프트웨어를 개발하기 위해 DBMS에 특화된 쿼리를 매번 작성할 필요없이 객체 기반의 코드를 통해 어떤 DBMS든 사용이 가능하게 됩니다. 또한 데이터베이스에 대한 액세스가 추상화됨으로 인해 TDD(Test Driven Development) 방법론에 따른 테스트 용이성(Testability)을 확보하기가 쉽습니다.

2. 강력하게 형식화된 데이터 액세스: 예전에 DataSet 객체를 사용하는 경우 새로운 데이터를 추가하거나 기존 데이터를 수정하기 위해서는 반드시 해당 컬럼의 데이터 형식에 대해 알고 있어야 했습니다. 그러나 ORM 도구를 사용하게 될 경우 해당 객체의 속성을 통해 강력하게 형식화된 데이터 액세스 API를 구현할 수 있게 됩니다. 또한 이를 통해 비즈니스 규칙에 대한 유효성 검사를 보다 효율적으로 실행할 수 있습니다.

3. 개발 생산성의 향상: 상기 두 가지 항목만 보더라도 이미 개발 생산성이 몰라보게 향상될 수 있음을 깨달을 수 있습니다. 뿐만 아니라 개발자가 잘못된 타입의 데이터를 사용하거나 Null 값이나 객체에 액세스하는 등의 자잘한 실수를 보완할 수 있어 예기치 않은 버그의 발생 및 디버깅에 대한 노력을 최소화할 수 있습니다.

4. 유지보수성의 향상: ORM 도구를 사용하게 된다면 비즈니스 로직이 명확해 집니다. 기존의 방식대로라면 비즈니스 로직 중간에 SQL 쿼리나 저장 프로시저 호출이 포함되고 따라서 이러한 부분에 대한 추가 분석이 따라줘야 전체 로직을 분석할 수 있게 되는 반면 ORM 도구를 사용할 경우에는 DB 관련 로직이 비즈니스 로직에 개발 언어 형태로 포함되기 때문에 코드 분석이 보다 용이해 집니다. 특히 Linq 구문을 활용하게 된다면 더욱 명확해 지겠지요. 이렇게 비즈니스 로직이 명확해지면 코드를 유지보수하기도 매우 쉽고 편리합니다.

물론 저는 이 글을 통해 모든 애플리케이션에 ORM을 반드시 탑재해야 한다고 주장하는 것은 아닙니다. 어떤 도구든지 마찬가지겠지만 필요한 곳에 적절한 활용이 무엇보다 중요합니다. 특히 ORM을 통해 해결할 수 없는 방대한 양의 쿼리나 프로세스가 많은 국내 개발 현실을 비추어볼 때 ORM 도구를 활용하는 것이 적합한지에 대해서는 의구심을 품을 수도 있습니다. Linq to SQL이나 EF가 저장 프로시저를 객체의 메서드 형태로 사용할 수 있도록 하는 것이나 NHibernate가 HQL(Hibernate Query Language)이나 Native SQL을 직접 호출할 수 있도록 구현되어 있는 것도 바로 이런 문제를 보완하기 위해서가 아닐까요. 결론적으로는 이런 도구를 활용하는 여러분의 선택이 무엇보다 중요하다고 할 수 있겠습니다.

NHibernate 다운로드하기

그러면 본격적으로 NHibernate 프레임워크를 사용하기 위한 준비를 시작해 보겠습니다. 이 글을 작성하는 현재 NHibernate는 2.1.2 버전이 가장 최신 버전이며 NHibernate 프레임워크의 공식 웹사이트에서 다운로드가 가능합니다.

그림 1: NHibernate 프레임워크의 공식 웹사이트 (http://nhforge.org)

그림에서 표시된 [Download Now NH 2.1.2] 링크를 클릭하면 NHibernate 2.1.2 버전을 다운로드할 수 있습니다. ZIP 형식으로 압축된 파일이 다운로드되면 별도의 설치 과정은 필요치 않으므로 프로젝트에 어셈블리만 참조하면 곧바로 사용할 수 있습니다. 다운로드된 압축 파일을 풀어보면 아래와 같은 폴더가 존재하는 것을 볼 수 있습니다.

그림 2: NHibernate 프레임워크를 다운로드한 후 압축을 해제한 폴더의 모습

  • Configuration_Templates: NHibernate 프레임워크가 지원하는 DBMS별로 데이터베이스 연결을 위한 설정 예제 파일들이 제공되는 폴더입니다. 이 폴더의 파일들로 알 수 있듯이 NHibernate 프레임워크는 FireBird, Microsoft SQL Server, MySql, Oracle, PostgreSQL, SQLite 등의 RDBMS를 지원합니다.

  • Required_Bins: NHibernate 프레임워크를 사용하기 위한 필수 어셈블리 파일들이 제공되는 폴더입니다. 특히 이 폴더에는 Visual Studio에서 NHibernate의 Configuration 파일들을 작성할 때 인텔리센스를 지원하기 위한 XSD 파일들도 포함되어 있습니다.

  • Required_For_LazyLoading: Lazy-loading을 구현하기 위해 필요한 추가 어셈블리들이 제공되는 파일이 제공되는 폴더입니다. NHibernate의 Lazy-loading은 Castle의 DynamicProxy, LinFu, Spring.NET 등 세 가지 프레임워크를 지원합니다. 이러한 어셈블리들은 세 가지 중 원하시는 것을 선택적으로 사용하실 수 있습니다.
NHibernate 프레임워크와 Linq

앞서 다운로드한 NHibernate 2.1.2에서는 Linq 구문이 지원되지 않습니다. 따라서 2.1.2를 사용한다면 NHibernate의 Criteria API를 이용한 형태의 코드를 작성하여 모델 객체를 조작하게 됩니다. 그러나 Linq to NHibernate를 사용하면 제한된 범위 내에서 Linq를 사용할 수 있습니다. 또한 현재 개발 중인 NHibernate 3.0의 경우에는 HQL 기반의 Linq 기능을 구현하고 있어 Linq to NHibernate에 비해 더 폭넓은 범위의 Linq 구문을 지원받을 수 있습니다.

언제나 그렇듯 개발과 관련된 포스트는 예제 위주의 포스트로 구성하는게 쵝오-ㅅ-b지요. 다음 포스트부터는 간단한 예제 애플리케이션의 구현을 통해 NHibernate 프레임워크의 사용법을 차근히 익혀보겠습니다.







Posted by 웹지니 트랙백 0 : 댓글 12
안녕하세요 웹지니입니다.

세상엔 참 착한 사람이 많은거 같아요. 며칠 전까지 우분투 리눅스에 Mono 한 번 설치해 보겠다고 갖은 삽질을 마다 않던 저에게 단비같은 사이트가 하나 나타났네요. Badgerports.org라고 하는 이 사이트는 Jo Shields라는 사람이 운영하는 사이트입니다. 이 사람은 현재 데비안/우분투 리눅스를 위한 Mono 패키지 메인 관리자라고 하네요.

그림 1: bagderports.org 홈페이지

이 사이트는 단순히 사이트에 대한 소개 및 사용법 설명이 전부입니다. Jo가 제공하는 패키지 서버를 우분투 리눅스에 등록하면 Jo의 패키지 서버를 통해 최신 버전의 Mono 프레임워크와 MonoDevelop IDE를 apt-get 명령으로 손쉽게 설치할 수 있어요. 이 글을 작성하는 현재 제공되는 버전은 아래와 같습니다.

  • Mono 2.6.3
  • MonoDevelop 2.4 + 전체 패키지

자, 그럼 우분투 서버에 Jo의 패키지 서버를 등록해 볼까요?

badgerports 패키지 서버 등록하기

우선 우분투 리눅스의 상단 위치한 시스템 메뉴에서 [System > Software Sources] 메뉴를 선택합니다. 그러면 아래 그림과 같은 대화 상자가 나타납니다. 여기서 [Other Software] 탭을 클릭하고 [Add] 버튼을 클릭한 후 그림과 같이 소스 서버의 주소를 입력해 줍니다.


입력을 완료했으면 [Add Source] 버튼을 클릭하고 [Authentication] 탭을 다시 클릭한 후 [Import Key File] 버튼을 클릭합니다. 그러면 파일 찾기 대화 상자가 나타날텐데요. 여기를 클릭해서 키 파일을 다운로드한 후 해당 파일을 찾아 선택하면 됩니다. 키 파일을 지정했으면 [Close] 버튼을 클릭해서 대화 상자를 닫고 [Reload] 버튼을 클릭하여 새로 추가한 정보를 업데이트해 줍니다. 여기까지 하면 일단 패키지 서버의 추가는 완료!

Mono와 MonoDevelop 설치하기

패키지 서버를 등록했으므로 이제 apt-get 명령을 이용하면 Mono와 MonoDevelop을 손쉽게 설치할 수 있습니다. Mono와 MonoDevelop을 설치하는 명령은 아래와 같습니다.
sudo apt-get install mono
sudo apt-get install monodevelop
크흑... 두 줄의 명령만으로 Mono와 MonoDevelop을 손쉽게 설치할 수 있게 되었습니다. 아래는 인증샷.


그림에서 보듯이 Mono 2.6.3과 MonoDevelop 2.4가 설치된 것을 확인할 수 있어요.
착한 Jo한테 메일이라도 한 통 보내야 할까봅니다. 최저 생활비 수급자인 관계로 Donate는 생략~ ㅋ.

그럼 모두 즐거운 하루 되세요~
Posted by 웹지니 트랙백 0 : 댓글 3
안녕하세요? 웹지니입니다.

주말 동안에 구슬이 횽아가 멋진 소식을 전했네요. 뭐 거두 절미하고 번역 들어갑니다. ㅋ 원문은 여기를 클릭하세요.
(제가 태우는 담배가 레종 - Raison - 인데 발음이 비슷해서 그런가 왠지 친근한 느낌이... 켈룩 -ㅅ-;;)

달랑 점심 시간 1시간을 투자해서 번역한 글이라 다소 투박하거나 원문과 일치하지 않을 수 있음을 양해해 주시기 바랍니다.

UPDATED: ASP.NET Web Pages라는 이름으로 Razor Syntax를 지원하는 VS 2010 애드온의 베타 1버전이 발표 되었어요. 아쉽게도 현재로서는 .NET 4만 지원하는 것 같습니다. 정식 릴리즈때는 3.5도 지원해주면 좋겠네요. 다운로드는 여기로!!

ASP.NET의 새로운 뷰 엔진 - Razor

우리 팀에서 진행하는 작업 중 하나는 ASP.NET을 위한 새로운 뷰 엔진 옵션을 구현하는 것이었습니다.

ASP.NET MVC는 "뷰 엔진"이라는 개념을 지원하는데 이 뷰 엔진이란 서로 다른 템플릿 문법을 구현하는 교체 가능한 모듈입니다. ASP.NET MVC가 사용하는 기본 뷰 엔진은 ASP.NET 웹 폼의 템플릿과 마찬가지로.aspx/.ascx/.master 파일들을 사용합니다. 그 외에도 ASP.NET MVC 뷰 엔진으로 SparkNHaml이 많이 사용되고 있습니다.

우리가 구현하는 새로운 뷰 엔진은 코드 중심의 템플릿 접근법을 사용한 HTML의 생성에 최적화되어 있습니다. 이 새로운 뷰 엔진의 코드명은 "Razor"로 조만간 첫 번째 공식 베타 버전을 릴리즈할 예정입니다.

디자인 목표

우리는 "Razor"를 구현하면서 다음과 같은 디자인 목표를 수립했습니다.
  • 간결하고 유기적이며 풍부한 표현: Razor는 파일을 작성하는데 필요한 문자와 입력 수를 최소화하여 빠르고 유기적으로 워크플로우를 코딩할 수 있습니다. 다른 템플릿 문법들과 달리 Razor는 HTML 코드 내에 서버 측 코드를 명시적으로 표시할 필요가 없습니다. Razor 파서는 여러분의 코드 내에서 서버 측 코드를 알아서 유추해 냅니다. 따라서 정말 간결하며 풍부한 표현이 가능한 문법을 제공할 수 있기 때문에 빠르고 재미있게 코드를 작성할 수 있습니다.
  • 쉬운 학습: Razor는 배우기 쉬우며 최소한의 개념으로 매우 생산적으로 코드를 작성할 수 있습니다. 여러분은 이미 알고 있는 언어와 HTML 기술을 그대로 활용할 수 있습니다.
  • 새로운 언어는 아닙니다: 우리는 의도적으로 Razor를 위한 새로운 언어를 구현하지 않았습니다. 대신 개발자들이 이미 친숙한 C#과 VB (혹은 다른 언어)를 Razor와 함께 사용할 수 있기를 원했으며 여러분이 사용하는 언어를 토대로 놀라운 HTML 생성 기능을 제공하는 템플릿 마크업 문법을 제공하고자 했습니다.
  • 모든 텍스트 편집기에서 동작: Razor는 특별한 도구를 필요로 하지 않으며 평범한 텍스트 편집기에서도 코드를 작성할 수 있습니다(심지에 메모장으로도 훌륭한 결과를 만들어 낼 수 있습니다).
  • 훌륭한 인텔리센스 지원: Razor가 별도의 도구나 코드 편집기를 필요로 하지는 않지만 Visual Studio에서는 완벽한 인텔리센스를 지원합니다. Visual Studio 2010과 Visual Web Developer 2010은 Razor를 위한 완벽한 기능의 코드 편집기를 제공합니다.
  • 단위 테스트: 새로운 뷰 엔진은 뷰에 대한 단위 테스트를 제공합니다(컨트롤러나 웹 서버 및 특별한 AppDomain 없이도 모든 종류의 단위 테스트 프로젝트를 호스팅할 수 있습니다).
우리는 지난 몇 개월 간 애플리케이션의 구현을 위해 Razor를 사용했으며 (.NET이 아닌 다른 분야의 웹 개발자들의 그룹을 포함한) 다양한 자원봉사자들과 여러 가지 사용성 연구를 하고 있습니다. 많이 사용해 보시고 다양한 피드백을 주시면 감사하겠습니다.

선택과 확장성

ASP.NET의 훌륭한 기능 중 하나는 대부분의 기능들이 교체 가능하다는 점입니다. 만일 여러분이 원하는대로 동작하지 않는 기능을 발견한다면 다른 모듈로 대체할 수 있습니다.

ASP.NET MVC의 다음 버전에서는 "추가 > 뷰" 대화 상자를 새로 구현하여 뷰 템플릿 파일을 생성할 때 여러분이 템플릿 문법을 선택할 수 있도록 개선될 예정입니다. 또한 시스템에 설치되어 사용 가능한 뷰 엔진 중 하나를 손쉽게 선택할 수 있는 기능을 제공하여 보다 자연스럽게 뷰를 선택할 수 있게 될 것입니다.


Razor는 ASP.NET MVC에 내장된 뷰 엔진 중 하나가 될 것입니다. 모든 도우미 메서드들과 프로그래밍 모델 기능들은 Razor와 .ASPX 뷰 엔진에서 동일하게 적용됩니다.

또한 하나의 애플리케이션이나 사이트에서 여러 개의 뷰 엔진을 사용하는 뷰 템플릿을 혼합해서 사용할 수 있습니다. 예를 들어 일부 뷰는 .aspx 파일을 사용하고 다른 일부는 .cshtml이나 vbhtml 파일(각각 C#과 VB.NET을 위한 Razor의 파일 확장자입니다)을 사용할 수 있으며 또 다른 일부는 Spark나 NHaml을 사용하여 구성할 수 있습니다. 또한 어떤 뷰 엔진을 사용하는 뷰 템플릿 내에서 다른 뷰 엔진을 위한 부분 뷰(Partial View)를 중첩할 수도 있습니다. 여러분은 완벽한 선택과 유연성의 혜택을 보게 될 것입니다.

Razor를 이용한 Hello World 예제

Razor를 이용하면 정적 HTML (혹은 다른 텍스트 콘텐츠)에 서버 코드를 추가하여 동적인 페이지를 구성할 수 있습니다. Razor의 핵심 디자인 목표 중 하나는 이러한 코딩 과정을 보다 유기적으로 만들어 최소한의 코딩으로 서버 코드를 HTML 마크업에 추가할 수 있도록 하는 것입니다.

그러면 아래와 같은 결과를 만들어 내는 간단한 "Hello, World" 예제를 만들어 보겠습니다.


.ASPX 코드로 구현된 뷰

이 "Hello World" 예제를 ASP.NET의 ASPX 마크업 문법으로 구현한다면 우리는 HTML 마크업 내의 코드를 표현하기 위해 아래와 같이 <%= %> 태그를 사용해야 합니다.


이 "Hello World" 예제를 잘 살펴보면 코드의 시작과 끝을 알리기 위해 5개의 문자(<%= %>)가 필요합니다. 이들 중 일부(특히 키보드의 가운데 상단에 있는 % 문자)는 입력하기가 쉽지 않습니다.

Razor를 이용한 뷰

Razor에서는 @ 문자를 이용하여 코드의 시작과 끝을 표시할 수 있습니다. <% %> 태그와 달리 Razor는 코드 블록을 명시적으로 닫을 필요가 없습니다.


Razor 파서는 코드 블록 내에서 사용된 C#/VB 코드를 의미적으로 파악하기 때문에 위에서와 같이 코드 블록을 명시적으로 닫을 필요가 없습니다. Razor는 이런 구문들을 자체 코드 블록으로 파악하고 우리를 대신해 묵시적으로 코드 블록을 닫아줍니다.

이렇게 간단한 "Hello World" 예제에서 우리는 벌써 12개의 키 입력을 아낄 수 있었습니다. @ 문자는 % 문자보다 더 쉽게 입력할 수 있기 때문에 전체적으로 빠르고 유기적으로 코드를 입력할 수 있습니다.

루프와 중첩된 HTML 예제

일련의 제품 목록(과 제품의 가격)을 나열하는 또 다른 예제를 살펴보도록 합시다.


.ASPX 코드로 구현된 뷰

ASP.NET의 .ASPX 마크업 문법을 사용한다면 각각의 제품 정보를 나열할 <ul> 목록과 <li> 항목을 동적으로 생성하기 위해 아래와 같이 코드를 작성해야 합니다.


Razor를 이용한 뷰

아래의 예제 코드는 위의 코드를 Razor로 구현한 코드입니다.


위의 예제에서 알 수 있듯이 foreach 구문은 @ 기호를 이용하며 코드 블록 내에서 HTML 콘텐츠를 생성합니다. Razor 파서는 코드 블록 내의 C# 코드의 의미를 알고 있으므로 <li> 콘텐츠는 foreach 구문 내에 존재해야 하며 반복되는 콘텐츠로 처리됩니다. 또한 마지막의 } 문자는 foreach 구문을 종료하기 위한 것이라는 점도 인식합니다.

Razor는 <li> 태그 내의 @p.Name과 @p.Price 구문이 서버 측 코드이며 루프 내에서 매번 실행해야 한다는 것을 인식할 만큼 충분히 똑똑합니다. 또한 HTML과 코드가 혼합된 상황에서 유추를 통해 @p.Name과 @p.Price 코드를 자동적으로 닫아야 한다는 점도 스스로 알아냅니다.

이처럼 코드 블록을 열거나 닫기 위한 기호 없이도 코드를 작성할 수 있다는 것은 전체적인 코딩 프로세스를 매우 유기적이며 빠르게 만들어 줍니다.

If 블록과 다중 구문

아래와 같은 몇 가지 예제 코드를 더 살펴보겠습니다.

if 구문

foreach 예제와 마찬가지로 if 구문(혹은 C#이나 VB외의 다른 언어 구조)에서도 명시적인 코드 블록의 시작과 끝을 지정하지 않고도 콘텐츠를 포함할 수 있습니다. 예제를 살펴보겠습니다.


다중 구문

또한 @{ code } 블록내에 여러 줄의 구문을 포함할 수도 있습니다.


위의 예제에서 알 수 있듯이 변수들은 여러 개의 서버 코드 블록으로 확장될 수 있습니다. 예를 들어 message 변수는  여러 구문을 포함하는 @{ } 블록에 선언되었지만 @message 코드 블록에서도 사용됩니다. 이는 개념적으로 .aspx 마크업 파일의 <% %>와 <%= %> 구문과 동일합니다.

다중 토큰 구문

@( ) 구문은 여러 개의 토큰을 가질 수 있습니다. 예를 들어 문자열을 조합하는 앞서 예제의 코드는 다음과 같이 @( ) 코드 블록으로 재작성할 수 있습니다.


콘텐츠와 코드의 통합

Razor 파서는 여러분이 명시적으로 해왔던 작업들을 간소화할 수 있는 여러 가지 내장 기능들을 제공합니다.

HTML에서 메일 주소에 사용된 @와 충돌은 없나요?

Razor의 언어 파서는 템플릿에 사용된 @ 문자가 코드인지 정적 콘텐츠인지를 구분할 수 있습니다. 예를 들어 아래의 @ 문자는 메일 주소의 일부로 사용되고 있습니다.


이 파일을 파싱할 때 Razor 파서는 @ 문자 오른편의 콘텐츠가 C# 코드인지(CSHTML 파일의 경우) 아니면 VB 코드인지(VBHTML 파일의 경우) 아니면 그냥 정적인 콘텐츠인지를 판단합니다. 위의 코드는 아래와 같은 HTML 코드를 만들어 냅니다(메일 주소는 정적인 콘텐츠로 표현되며 @DateTime.Now는 서버 코드로 표현되고 있습니다).


콘텐츠가 코드로도 사용될 수 있는 경우(그리고 여러분은 이 콘텐츠를 콘텐츠로 처리하고 싶은 경우)에는 @@을 입력하여 명시적으로 @문자로 취급하도록 할 수도 있습니다.

중첩된 코드의 식별

if/else 구문이나 foreach 등 코드 블록 구문을 이용하여 HTML 콘텐츠를 중첩할 때 콘텐츠 블록의 시작을 명시적으로 판별할 수 있도록 HTML이나 XML 요소로 내부의 콘텐츠를 감싸야 합니다.

예를 들어 아래의 코드는 (서버 코드를 포함한) 다중 콘텐츠 블록을 <SPAN> 요소로 감싸고 있습니다.


이 코드는 다음과 같이 <SPAN> 태그를 그대로 사용하는 HTML 콘텐츠를 만들어 냅니다.


중첩된 콘텐츠를 <text> 블록으로 감싸면 콘텐츠를 감싸는 태그를 생략하고 나머지 콘텐츠만 렌더링할 수 있습니다.


위의 코드는 아래와 같이 콘텐츠를 감싸는 태그를 생략한 결과를 만들어 냅니다.


HTML 인코딩

기본적으로 @ 블록에 의해 출력되는 콘텐츠는 XSS 공격을 차단하기 위해 자동적으로 HTML 인코딩된 값으로 출력됩니다.

레이아웃/마스터 페이지 시나리오 - 기초

웹 사이트나 웹 애플리케이션에서 일관된 UI를 제공하는 것은 매우 중요한 일입니다. ASP.NET 2.0은 "마스터 페이지" 개념을 도입하여 .aspx 기반 페이지와 템플릿에 일관된 UI를 구현할 수 있는 기능을 제공합니다. Razor 또한 "레이아웃 페이지"를 제공하기 때문에 일반적인 사이트의 템플릿을 정의하고 이를 상속하여 사이트 내의 뷰와 페이지가 일관된 UI를 구현할 수 있습니다.

간단한 레이아웃 예제

아래의 예제는 SyteLayout.cshtml이라는 파일에 작성한 간단한 레이아웃 페이지입니다. 이 페이지에는 정적인 HTML 콘텐츠는 물론 동적인 서버 코드도 작성할 수 있습니다. 또한 "RenderBody()" 도우미 메서드를 호출해서 요청된 URL에 대한 콘텐츠를 채워넣을 수 있습니다.


그런 후 요청된 페이지를 위한 페이지 본문을 구성하는 콘텐츠/코드를 가지고 있는 "Home.cshtml" 뷰 템플릿을 생성하고 아래와 같이 콘텐츠를 작성하면 됩니다.


위의 코드에서 보듯이 LayoutPage 속성에 Home.cshtml 파일을 지정하였습니다. 이렇게 하면 이 뷰에 대한 레이아웃 페이지로 SiteLayout.cshtml 파일이 사용됩니다. 혹은 ASP.NET MVC 컨트롤러가 Home.cshtml 파일을 뷰 템플릿으로 호출할 때 레이아웃 파일을 지정하거나 아니면 사이트를 위한 기본 레이아웃을 설정할 수도 있습니다(이 경우 프로젝트 내의 모든 뷰 템플릿이 자동적으로 지정된 하나의 레이아웃 페이지를 사용하게 됩니다).

Home.cshtml 파일을 뷰 템플릿으로 렌더링하면 레이아웃과 자식 페이지의 콘텐츠가 하나로 합쳐져 아래와 같은 결과를 만들어 냅니다.


간결하고 명확하며 풍부한 표현이 가능한 코드

위의 예제에서 알 수 있듯이 레이아웃을 정의하고 사용하면 뷰나 페이지를 간결한 코드로 명확하게 구현할 수 있습니다. SiteLayout.cshtml 파일과 Home.cshtml 파일의 코드는 모든 콘텐츠를 두 개의 .cshtml 파일로 구성합니다. 즉, 추가적인 설정이나 태그도 필요치 않으며 <%@Page %> 디렉티브도 필요하지 않으며 다른 어떤 태그나 값을 설정할 속성도 필요치 않습니다.

우리는 여러분이 간력하고 쉬우며 유기적인 코드를 작성할 수 있도록 노력하고 있습니다. 또한 텍스트 편집기 만으로 모든 작업이 손쉽게 가능하도록 구현하고자 합니다. 여기에는 어떠한 코드 생성이나 인텔리센스도 필요치 않습니다.

레이아웃/마스터 페이지 시나리오 - 섹션 덮어쓰기

레이아웃 페이지에는 또 다른 섹션을 정의하여 해당 레이아웃을 사용하는 뷰 템플릿이 사용자 정의 콘텐츠를 채워넣을 수 있는 기능을 제공합니다. 이 기능을 활용하면 단편화된 콘텐츠 영역을 레이아웃 페이지에 채워넣어 사이트의 레이아웃에 대한 유연성을 확보할 수 있습니다.

예를 들어 SiteLayout.cshtml 파일에 두 개의 섹션을 추가하여 사이트가 제공하는 뷰 템플릿을 어디에 채워넣을지를 결정할 수 있습니다. 새로 추가된 섹션들은 "menu"와 "footer"라고 명명하고 RenderSection() 메서드를 호출할 때 optional=true 매개 변수를 지정하여 해당 섹션이 선택적인(즉, 반드시 필요하지는 않은) 부분이라고 표시할 수 있습니다(이 기능은 제가 예전에 포스팅했던 C#의 새로운 선택적 매개 변수 문법을 통해 구현됩니다).


새로 추가된 두 섹션은 선택적인 섹션으로 표시하였기 때문에 이 두 섹션을 Home.cshtml 파일에 정의할 필요는 없습니다. 따라서 이 사이트는 두 개의 섹션에 채워질 뷰 템플릿이 존재하지 않더라도 잘 동작합니다.

그러면 Home.cshtml 파일로 돌아가 Menu와 Footer 섹션을 정의해 보겠습니다. 아래의 코드는 Home.cshtml 파일의 모든 콘텐츠를 구현한 코드입니다. 별도로 다른 파일에 구현할 필요는 없습니다. 한 가지 변경된 것은 LayoutPage 속성 설정을 전체 사이트를 위한 설정으로 변경했다는 점입니다.


Menu와 Footer 섹션은 파일 내의 @section {}  블록을 통해 재정의되었습니다. 주 콘텐츠와 본문 콘텐츠를 구별할 필요 없이 단순히 인라인 코드로 정의하기만 하면 됩니다 (이렇게 함으로써 키 입력을 줄일 수 있음은 물론 이미 존재하는 페이지의 코드를 변경하지 않고도 레이아웃 페이지에 새로운 섹션을 손쉽게 추가할 수 있습니다).

Home.cshtml 파일을 다시 렌더링해보면 레이아웃과 서브 페이지들의 콘텐츠가 결합되어 새로 정의한 두 개의 섹션이 나타나게 될 것입니다. 클라이언트에 전송되는 HTML 코드는 다음과 같습니다.


캡슐화와 HTML 도우미 클래스의 재사용

지금까지 레이아웃 페이지를 이용해 사이트 전반의 UI를 구성하는 방법에 대해 알아보았습니다. 이제는 HTML 도우미 클래스를 재사용하여 HTML 생성 기능을 전체 사이트 - 는 물론 서로 다른 여러 사이트에서도 - 에서 재사용하는 방법에 대해 알아보겠습니다.

코드 기반 HTML 도우미

ASP.NET MVC에서는 코드 블록 내에서 호출이 가능하며 HTML 코드를 생성하는 HTML 도우미 메서드라는 개념을 제공합니다. 이들은 순수하게 코드로 (특히 확장 메서드 형태로) 구현되어 있습니다. ASP.NET MVC에 내장된 모든 HTML 확장 메서드는(여러분이 직접 구현하든 다른 사람이 구현했든) Razor 뷰 엔진에서도 그대로 활용할 수 있습니다.


선언적 HTML 도우미

코드만으로 구성된 클래스를 이용하여 HTML 출력물을 생성하는 방법은 잘 동작하기는 하지만 이상적인 형태는 아닙니다.

우리가 Razor를 통해 제공하고자 했던 기능 중 하나는 재사용 가능한 HTML 도우미를 보다 선언적인 방법으로 사용할 수 있도록 하는 것이었습니다. 아래와 같이 @helper {} 문법을 이용하면 재사용 가능한 도우미를 정의할 수 있습니다.


여러분은 Views\Helpers\ 디렉토리에 세 개의 .cshtml 파일을 구현하고 이를 사이트 내의 다른 어떤 뷰나 페이지에서도 재사용할 수 있습니다(특별히 다른 단계를 밟을 필요가 없습니다).


위의 예제에서 보듯이 ProductListing() 도우미 메서드는 매개 변수와 인자를 정의할 수 있습니다. 따라서 여러분은 원하는 어떤 것이든 매개 변수로 전달할 수 있습니다(또한 선택적 매개 변수와 Nullable 타입, 제네릭 등 기존의 언어적 특성들을 그대로 활용할 수 있습니다). 또한 Visual Studio를 사용한다면 이 메서드들을 디버깅할 수도 있습니다.

참고: @helper 구문은 Razor의 첫 번째 베타에는 포함되지 않을 예정이지만 이후의 버전에는 포함될 것입니다. 코드 기반 도우미는 첫 번째 베타에서 정상적으로 동작할 것입니다.

인라인 템플릿을 매개 변수로 전달하기

Razor의 또 다른 유용한 (그리고 정말 강력한) 기능은 "인라인 템플릿"을 도우미 메서드의 매개 변수로 전달할 수 있다는 점입니다. 이러한 "인라인 템플릿"은 HTML과 코드를 모두 포함할 수 있으며 필요에 따라 도우미 메서드에서 호출할 수도 있습니다.

아래의 예제는 DataGrid를 렌더링하는 "Grid" HTML 도우미 메서드를 사용하는 방법을 보여줍니다.


위의 예제에서 호출하는 Grid.Render() 메서드는 C# 구문입니다. 우리는 C#의 새로운 명명된 매개 변수 문법을 이용하여 강력하게 형식화된 인수를 Grid.Render 메서드에 전달합니다. 따라서 위의 구문은 컴파일 시점에 완벽한 코드 완성 및 인텔리센스의 지원을 받을 수 있습니다.

우리가 컬럼을 정의할 때 전달한 "format" 매개 변수가 바로 "인라인 템플릿"이며 보시다시피 HTML과 코드를 모두 포함하여 출력될 데이터의 형식을 재정의할 수 있습니다. 이 기능의 강력한 점은 Grid 도우미가 인라인 템플릿을 마치 대리자 메서드처럼 호출할 수 있으며 필요에 따라 몇 번이고 호출할 수 있다는 점입니다. 예제에서는 Grid의 각 행을 렌더링할 때 매번 인라인 템플릿이 호출되며 데이터를 표시하는 역할을 담당하는 템플릿에서 참조할 수 있는 "item" 변수를 전달해 줍니다.

이 방법을 이용하면 보다 풍부한 HTML 도우미 메서드를 개발할 수 있습니다. 이러한 도우미 메서드는 코드 접근 방식(현재 확장 메서드를 이용하는 방식) 뿐만 아니라 @helper {} 구문을 이용하는 선언적 방식으로도 구현이 가능합니다.

Visual Studio 지원

앞서 설명했듯이 Razor의 구현 목표 중 하나는 최소한의 입력이며 기본적인 텍스트 편집기에서도 손쉽게 편집이 가능하도록 하는 것입니다. 이를 위해 우리는 문법 자체를 간결하며 명확하게 유지했습니다.

또한 우리는 Razor를 디자인함에 있어 Visual Studio의 풍부한 코드 편집 기능을 활용할 수 있도록 구현했습니다. Razor 기반 파일에서도 HTML과 자바스크립트, C#/VB 코드에 대한 인텔리센스 기능을 완벽하게 지원합니다.


위의 그림에서 보듯이 foreach 구문 내에 포함된 코드에서 @p를 통해 참조하는 Product 객체에 대해 인텔리센스 기능이 동작하는 것을 볼 수 있습니다. 또한 솔루션 탐색기를 보면 Views 폴더가 .aspx 뷰 템플릿과 .cshtml 뷰 템플릿을 모두 가지고 있음도 알 수 있습니다. 여러분은 하나의 애플리케이션에서 여러 가지 뷰 엔진을 사용할 수 있으며 이로 인해 여러분에게 적합한 뷰 엔진을 손쉽게 선택할 수 있습니다.

요약

우리는 "Razor"가 코드 중심의 템플릿을 위한 새로운 뷰 엔진이라고 생각합니다. Razor를 이용하면 빠르고 풍부하며 재미있는 코딩을 즐길 수 있습니다. 문법은 매우 간결하며 입력을 최소로 줄여주는 동시에 마크업과 코드의 가독성을 전체적으로 향상시켜줍니다. Razor는 ASP.NET MVC의 다음 버전에서는 내장 뷰 엔진 중 하나로 제공될 것입니다. 또한 개별적인 .cshtml/.vbhtml 파일을 애플리케이션에 추가하여 개별적인 페이지로 실행시킬 수도 있습니다. 이를 통해 ASP.NET 웹 애플리케이션에서도 Razor를 활용할 수 있게 됩니다.

우리가 지난 몇 달간 고생해서 만든 Razor를 많은 개발자들이 사용해보고 다양한 피드백을 제공해 주기를 기대합니다. 조만간 첫 번째 공개 베타를 공개할 예정이므로 많은 피드백을 제공해 주시기를 바랍니다.

이 글이 도움이 되었기를 바라며

Scott

P.S: 블로그 외에도 저는 트위터를 통해 업데이트 소식과 관련 링크를 전달하고 있습니다. twitter.com/scottgu를 통해 저를 팔로우 해주세요.


Posted by 웹지니 트랙백 0 : 댓글 8
안녕하세요? 웹지니입니다.

드디어 여름인가봅니다. 날씨가 무척이나 덥네요.
요즘도 전 우리 회사 제품들을 리눅스 운영체제에서 동작할 수 있도록 하기 위해 Mono/리눅스와 씨름을 하고 있습니다. 리눅스 초짜에겐 다소 버거운(ㅠㅠ) 작업이네요.

오늘은 우분투 리눅스 10.04 LTS 버전에서 Mono와 MonoDevelop의 설치 방법에 대해 기록해 둘까 해요.
역시나 잊어버리면 안되기에...ㅋ

Mono 설치하기

우선은 Mono부터 설치해 보겠습니다. 며칠간 삽질하면서 보니 Mono를 설치할 수 있는 방법은 꽤나 다양하더군요. 대략 정리해보면 아래와 같습니다.

1. Mono 프로젝트 홈페이지에서 공식 릴리즈된 Mono 설치 파일을 다운로드하여 설치합니다. 이 경우 현재 시점에서는 2.6.4 버전이 설치됩니다.
2. Mono 프로젝트 홈페이지에서 Subversion 기반의 소스 저장소로부터 trunk혹은 tag 버전을 직접 다운로드 및 빌드하여 설치합니다. 이 경우 tag 버전은 2.6.4가, trunk 버전은 2.7이 설치됩니다.
3. Mono 프로젝트 홈페이지에서 매일 배포하는 Daily Build를 다운로드하여 빌드 및 설치합니다. 이 경우는 버전을 명확하게 알 수 없으며 해당 날짜가 버전 대신 사용됩니다. (MonoDevelop이 현재 실행 중인 Mono 버전을 알 수 없다고 징징거리더라고요 -ㅅ-;;;)

오늘 소개할 방법은 이 3번 방법입니다. 우선 구글링을 하다보니 어느 착한(?) 분이 친절하게도 설치 과정을 스크립트로 작성해 둔 것이 있었어요. 원문은 여기서 보실 수 있고 설치 스크립트는 아래와 같습니다. 

#!/bin/bash

TOPDIR=$(pwd)
BUILDDIR=$TOPDIR/build
DLDDIR=$TOPDIR/downloads

export PATH=/usr/local/bin:$PATH

echo "installing prerequisites"
sudo apt-get install -y build-essential libc6-dev g++ gcc libglib2.0-dev pkg-config subversion apache2 bison gettext

mkdir -p $BUILDDIR

echo
echo "downloading mono packages"
echo

cd $BUILDDIR

svn co svn://anonsvn.mono-project.com/source/trunk/xsp
svn co svn://anonsvn.mono-project.com/source/trunk/mod_mono

wget http://mono.ximian.com/daily/mono-latest.tar.bz2
wget http://mono.ximian.com/daily/monocharge-latest.tar.gz

cd $BUILDDIR
bunzip2 -df mono-latest.tar.bz2
tar -xvf mono-latest.tar
tar -xzvf monocharge-latest.tar.gz

echo
echo "building and installing mono packages"
echo
cd $BUILDDIR
cd mono-*
./configure --prefix=/usr/local --with-glib=system
make
sudo make install

cd $BUILDDIR
cd monocharge*
sudo env prefix=/path/to/prefix ./recharge.sh

cd $BUILDDIR
cd xsp
./autogen.sh --prefix=/usr/local
make
sudo make install

cd $BUILDDIR
cd mod_mono
./autogen.sh --prefix=/usr/local
make
sudo make install
cd $BUILDDIR

echo
echo "done"
위의 스크립트를 실행하면 최신 Daily Build Binary Package를 이용하여 Mono 프레임워크를 설치할 수 있습니다. 위의 스크립트를 *.sh 파일로 저장하고 실행하면 걍 날로 먹는 Mono 설치는 이것으로 끝 -ㅅ-;;;

MonoDevelop의 설치

어느 틈엔가 MonoDevelop이 2.4 버전으로 업그레이드가 되었어요. Mono 프로젝트의 소스 저장소와 공식 릴리즈된 MonoDevelop의 Tarball 파일을 다운로드하여 MonoDevelop 2.4를 설치하는 스크립트는 다음과 같습니다.

#!/bin/bash

TOPDIR=$(pwd)
BUILDDIR=$TOPDIR/build

echo "installing prerequisites"

sudo apt-get install -y libpango1.0-dev libatk1.0-dev libgtk2.0-dev
sudo apt-get install -y libglib2.0-dev libglade2-dev
sudo apt-get install -y libpanel-applet2-dev libgtkhtml3.14-dev libgtksourceview-dev
sudo apt-get install -y libgnomeprint2.2-dev libgnomeprintui2.2-dev
sudo apt-get install -y autoconf libtool automake
sudo apt-get install -y libgnome-desktop-dev
sudo apt-get install -y libglade2.0-cil-dev
sudo apt-get install -y subversion apache2-threaded-dev

echo
echo "downloading monodevelop packages"
echo

cd $BUILDDIR
svn co svn://anonsvn.mono-project.com/source/trunk/mono-addins
svn co svn://anonsvn.mono-project.com/source/trunk/gnome-sharp
svn co svn://anonsvn.mono-project.com/source/trunk/gnome-desktop-sharp
sudo wget http://ftp.novell.com/pub/mono/sources/monodevelop/monodevelop-2.4.tar.bz2

echo
echo "installing monodevelop 2.4"
echo

cd gnome-sharp
./bootstrap-2.24 --prefix=/usr/local
make
sudo make install
cd $BUILDDIR

cd gnome-desktop-sharp
./autogen.sh --prefix=/usr/local
make
sudo make install
cd $BUILDDIR

cd mono-addins
./autogen.sh --prefix=/usr/local
make
sudo make install
cd $BUILDDIR

tar xvjf monodevelop-2.4.tar.bz2
cd monodevelop-2.4
./configure --prefix=/usr/local
make
sudo make install

echo "done"
여기까지 실행하면 MonoDevelop 2.4의 설치도 완료! 아래는 인증샷입니다.


지금까지 작성한 스크립트로 이제는 삽질 그만 할래요 ㅠㅠ. 참, 이 삽질을 왜 했느냐구요?

삽질 비화 대공개!!

저는 현재 우리 회사 제품을 리눅스 운영체제 상에서도 동작할 수 있도록 수정하고 있습니다. 기존에는 .NET 3.5기반에서 Windows 운영체제와 SQL Server 데이터베이스만을 지원했거든요. 리눅스를 지원하게 되면서 자연스럽게 오라클이나 MySql 등 다른 데이터베이스 서버의 지원도 필요하게 되었어요. 이 때 가장 문제가 되었던 것은 DBMS별로 조금씩 다른 SQL 쿼리였습니다.

이쯤에서는 당연히 동일한 코드로 여러 DB를 지원할 수 있는 ORM 도구의 사용을 고려하게 됩니다. 그러나 Linq to SQL의 경우는 단순한 1:1 매핑만을 지원하고 Entity Framework의 경우에는 아직 Mono에서 지원이 되지 않고 있었죠. 그래서 결국 선택하게 된 것이 NHibernate였습니다.

NHibernate는 훌륭한 기능들을 제공했지만 두 가지가 아쉬웠는데 그 중 하나는 XML 파일 기반의 객체 관계 매핑이었고 두 번째는 다소 생소한 형태의 Criteria API였습니다. Criteria API란 NHibernate를 이용하여 SQL 쿼리를 만들어내기 위한 API입니다.

XML 파일 기반의 객체 관계 매핑 문제는 Fluent Interface 패턴을 이용하여 C# 코드로 객체 관계 매핑을 생성할 수 있는 Fluent NHibernate로 간단히 해결할 수 있었습니다. 또한 Criteria API는 Linq to NHibernate라는 녀석을 이용해서 해결할 수 있었죠. 여기에 가 보시면 NHibernate Linq 2.1.2 버전을 다운로드하실 수 있습니다.

문제는 바로 이 Linq to NHibernate였는데요. 이 녀석은 System.Data.Services 어셈블리의 3.5 버전을 참조로 빌드되었습니다. 안타깝게도 Mono 2.6.4에서는 이 어셈블리의 2.0 버전만 제공되는데요. 알고보니 이것은 Mono 팀의 실수라고 하네요. 즉, 3.5 버전이 제공되어야 함에도 불구하고 실수로 2.0 버전이(히뱀...) 배포된 것인 듯 합니다.

결국 Linq to NHibernate를 사용하는 우리 제품의 코드가 Mono 플랫폼에서 빌드조차 되지 않는 황당한 상황이 되어 버린거죠. 해서 가장 최신 버전의 Mono 플랫폼을 설치해 보게 되었고 그 결과 System.Data.Services 어셈블리의 3.5버전과 4.0 버전이 제공되는 것을 확인할 수 있었습니다.

이로 인해 Mono와 MonoDevelop 설치, 제품 코드 빌드 등에 거의 일주일을 날려먹었네요. 초짜는 힘들어요. ㅠㅠ
혹시 Mono로 개발하실 일이 있으시다면 저처럼은 삽질하지 마시길...

이만 물러갑니다!


Posted by 웹지니 트랙백 0 : 댓글 2
안녕하세요 웹지니입니다.
저는 지난 1999년부터 웹 개발자로서의 생활을 시작해 왔어요. 벌써 14년째가 되었군요.

이 포스트는 그동안 제가 해왔던 일들을 기록하기 위한 포스트입니다.
사실 이전 블로그에서는 페이지 형태로 유지해 왔었는데 아쉽게도 티스토리는 페이지를 지원하지 않네요.

저서 및 번역서

그 동안 저는 여러 권의 책을 집필 또는 번역을 해왔습니다. 이제보니 꽤 많이 쌓였네요. ^^;;
하지만 개인적으로 만족스러운 책은 별로 없었던 것 같아요. 조만간 시간이 나면 독자 여러분들보다 제가 우선 만족할 수 있는 그런 책을 써보고 싶습니다.

14. 세르게이의 HTML5 & CSS3 퀵 레퍼런스
장현희 역 / 제이펍
220 페이지 / 2013년 01월





13. HTML5 게임 프로그래밍: HTML5, CSS3, WebGL로 재미있게 배우는
장현희 역 / 제이펍
584 페이지 / 2012년 08월





12. Test-Drive ASP.NET MVC
장현희 역 / 제이펍
316 페이지 / 2011년 06월





11. Professional Enterprise .NET
장현희 역 / 제이펍
608 페이지 / 2010년 09월



10. 예제로 쉽게 배우는 Professional ASP.NET MVC 1.0
장현희 역 / 제이펍
600 페이지 / 2009년 10월
제1장 PDF 무료 다운로드:  Professional ASP.NET MVC 1.0 Chapter 1


9. RIA 시대의 새로운 웹 전략 Silverlight 2
장현희 저 / 웰북
456 페이지 / 2009년 4월



8. Programming WPF: 사용자 경험(UX)을 바꾸는 기술
장현희 역 / 한빛미디어
1,012 페이지 / 2008년 7월


7. 웹지니의 ASP.NET AJAX Programming
장현희 저 / 혜지원
416 페이지 / 2007년 11월



6. HTML, JavaScript, CSS 제대로 배우기
장현희 저 / 웰북
427 페이지 / 2007년 2월




5. 눈 감고도 할 수 있는 Windows XP SP2
장현희 저 / 혜지원
430 페이지 / 2005년 11월



4. 개발자와 함께하는 ASP 프로그래밍 제작노트 훔쳐보기
장현희 저 / 혜지원
600 페이지 / 2005년 1월



3. ASP.NET으로 구현하는 블로그 프로그래밍
장현희 저 / 가남사
543 페이지 / 2004년 9월
예제소스 다운로드:

ASPNET_Blog_Sample.zip


무료 PDF 도서 다운로드:

ASPNET_BLOG.zip



2. 실무 개발자가 풀어 쓴 ASP.NET 프로그래밍
장현희 저 / 가남사
752 페이지 /2002년 7월



1. www.myhome.asp3
장현희 저 / 크라운 출판사
633 페이지 / 2000년 10월



강의 및 기고

2005년에 처음으로 MVP에 선발된 후 지난 5년간 여러 세미나에 세션을 진행했었어요. 세션 발표라는게 맘처럼, 또 말처럼 쉽지만은 않았던 것 같습니다. 지금 생각해보니 다들 아쉬운 기억들 뿐이네요.

숭의여자대학교 - 성공 취업 전략 특강 (2012. 06. 07)
마이크로소프트 웹 매트릭스 - 패널 디스커션 참여 (2011. 01)
마이크로소프트 ReMIX 10 - ASP.NET MVC 2 확장성 경험하기 (2010. 06)
제2회 .NET 개발자 컨퍼런스 - Enterprise Development with ASP.NET MVC 2 (2010. 05)
마이크로소프트 TechDays 2009 온라인 - ASP.NET MVC 2 (2009. 10)
마이크로소프트 ReMIX 09 - ASP.NET 세션 (2009. 09)
구글 OpenSocial Summit - Introducing MySpace Developer Platform (2008. 11)
마이크로소프트 DevDays 2008 온라인 - ASP.NET MVC 1.0 (2008. 09)
SK 커뮤니케이션즈 개발자 데이 - Introducing Silverlight (2008. 04)
마이크로소프트 Jump Start: (주)동희산업 - ASP.NET AJAX & Silverlight (2008. 01)
마이크로소프트 DevDays 2007 - ASP.NET & Silverlight (2007. 11)
마이크로소프트 DevDays 2006 - ASP.NET AJAX (2006. 10)
마이크로소프트 DevDays 2006 - Secured Web Application (2006. 04)

월간 W.E.B - Silverlight 3가 궁금해? (2009. 08 ~ 2009. 12)
월간 마이크로소프트웨어 - ASP.NET MVC와 춤을 (2008. 06 ~ 2008. 09)
월간 마이크로소프트웨어 - ASP.NET 3.5 Extensions (2008. 02 ~ 2008. 03)

기타

이매진컵 2009 이집트 - 소프트웨어 설계 부문 한국대표 선발전 본선 심사위원



Posted by 웹지니 트랙백 0 : 댓글 6