OOP: revisited #1

프로그래밍이란 무엇일까? 이 질문에 답하는 것은 당황스러운 일이지만, 답은 매우 간단하다.

소프트웨어를 작성하는 것.

그렇다면, 소프트웨어를 작성하는 것은 무엇을 의미하는 것일까? 이 질문에 답하는 것은 어려운 일이지만, 내가 내린 답은 다음과 같다.

실세계에 존재하는 시스템을 모방하여 이를 컴퓨터 시스템위에서 구동시키는 일련의 과정

다시 한번! 그렇다면, 소프트웨어란 무엇일까?

소프트웨어는 당연히 하드웨어의 작동방식일테지만, 소프트웨어를 어떤 식으로 해부하느냐 혹은 바라보느냐(즉, 어떻게 모방하느냐)에 따라 그 패러다임은 심하게 달라질 수 있다. 그리고, 그 패러다임은 소프트웨어의 설계 및 구현은 설계에서 유지보수까지 소프트웨어 생명주기에 막대한 영향을 미치곤 한다.

패러다임을 이해하기 위해 그림 명화 두장을 준비했다. 왠 뜬금없는 미술이야기냐 라고 불평하실 분도 계시겠지만, 모방mimesis을 가장 극명하게 표현해내는 예술이 미술이므로 가져온 것이다. 일단 그림을 보면서 이야기해보자.

Las Meninas by Velazquez
Las Meninas by Velazquez

왼쪽의 그림은 디에고 벨라스케스Diego Rodríguez의 “시녀들”이라는 작품이다. 이 그림에 대한 자세한 내용은 위키백과를 통해서 확인하실수 있다.

1656년에 완성된 작품으로, 이 시기의 미술화풍을 잘 반영이라도 하듯 섬세한 표현이 두드러진다. 이 작품 내부에는 해석할만한 텍스트가 넘쳐나는 재미있는 작품이지만, 우리가 지금 이 작품에서 읽어야 할 것은 “텍스트”가 아니라 화풍이므로 자세한 것은 잊고 넘어가자.

Las Meninas - Picasso
Las Meninas - Picasso

오른쪽의 작품은 파블로 피카소 Pablo Picasso의 “시녀들”이다. 1957년에 그린 연작중 하나를 가져왔다. 흥미로운 점은 위의 “시녀들”을 파블로 피카소가 재해석해서 그린 그림이라는 점이다. 벨라스케스의 그림과는 엄청난 차이를 보인다. 이 차이는 어디에서 오는 것일까?

비록 벨라스케스의 시녀들을 보고 그린 그림이지만, 같은 이미지를 두고 두 사람의 해석이 다르다는 점을 알 수 있다. 사실, 같은 물건을 보고 그린 두 화가의 그림(사조도 다른!)을 비교하는 것이 가장 좋겠지만, 이 두 그림을 가지고도 충분히 모방에 있어서 사람의 생각 혹은 패러다임이 얼마나 작용하는지를 보여주는 사례라고 볼 수 있다.

그리고, 미술에서 나타나는 이 차이는 소프트웨어의 세계에도 그대로 적용된다.

미술의 사조가 화가가 받아들인 이미지를 어떤 식으로 그려내는가에 중점이 있다면, 프로그래밍의 패러다임은 프로그래머가 소프트웨어를 구성하는 기본 단위와 그 구성방식을 무엇으로 생각하느냐에 중점이 있다고 할 수 있다. 미술에 인상파, 고전주의, 낭만주의, 사실주의, 입체파, 추상파, 다다이즘등의 수많은 사조가 있다면, 프로그래밍에는 순차적 프로그래밍Procedual Programming, 객체지향 프로그래밍Object-Oriented Programming, 함수 프로그래밍Functional Programming, 관점지향 프로그래밍Aspect-Oriented Programming등이 존재한다.

순차적 프로그래밍은 소프트웨어를 일종의 시방서로 보는 패러다임으로 인간에 대한 배려보다는 컴퓨터라는 하드웨어를 어떻게 작동시킬 것인가에 초점을 맞추고 있다. (일반적인) C언어가 대표적인 사례라고 할 수 있으며, 대부분의 OS커널에서 제공하는 시스템 콜들이 이 패러다임을 따르고 있다. OS커널에서 제공하는 시스템 콜들을 기준으로 생각해본다면, 시스템 전체를 어찌 구성할 것인가 보다는, 하드웨어/커널이 가진 기능의 리스트에 가깝다. 또한, 하드웨어가 할 수 있는 일들을 목록으로 작성하고, 그 목록에 있는 일들을 어떤 순서로 실행할 것인가에 초점을 맞추고 있다.

함수 프로그래밍은 (수학적인) 함수들의 연결관계로 소프트웨어를 작성하는 방법론이다. 굉장히 유연하고, 직관적인 프로그래밍 방식이지만 인간이 기본적으로 세상을 인식하는 방식과는 차이가 있으며, 특수한 분야에서만 주로 사용되는 결과를 낳았다. 개인적으로는 상당히 관심이 가는 분야이며, 함수 프로그래밍분야에서 등장한 functor(함수자)의 개념은 C++에 도입되어 tr1::function과 같은 템플릿을 낳기도 하였으며, LISP나 scheme등의 언어적 기능들은 다른 객체지향 언어에 지대한 영향을 주었다.

마지막으로 오늘의 주제인 객체지향 프로그래밍이 있다. 객체지향 프로그래밍은 소프트웨어를 상태와 행동으로 정의되는 객체들의 상호연동을 이용해 소프트웨어를 표현한다. 객체지향 프로그래밍이 널리 사용되는 이유는 다름이 아니라, 객체지향 프로그래밍 패러다임이 제공하는 생각의 방식이 인간의 기본적인 사고방식과 유사하기 때문이다. 그리고, 그 유사성의 핵심은 추상화abstraction에 있다.

철학의 주제 중 하나인 인식론에서 그 유래를 찾아볼 수 있는 추상화는 다음과 같이 정리할 수 있다.

  1. 세상의 모든 사물은 다르다. 서로 같은 것은 존재하지 않는다.
  2. 그럼에도 인간은 사물을 구분짓는다. (Classification)
  3. 즉, 인간은 각 사물이 존재하는 공통된 성분을 추출하여 이를 토대로 세상을 인식한다.

이렇게 정리해놓으면 알쏭달쏭하지만, 쉽게보면 다음과 같다.

네모 두개

위의 그림을 놓고 “저 둘은 뭐죠?” 라는 질문을 던진다면, 보통 “네모 두개” 혹은 “직사각형 두개”라는 답변이 나올 것이다. 크기도 다르고 색깔도 다르지만, 자연스럽게 두 도형의 공통된 특징을 추출하여 개념화하는 것이다. 이 과정이 인식론적 추상화의 시작점이다. 인식론 혹은 일반철학에서 이런 추상화의 결과로 나온 것을 흔히 개념concept이라고 부른다. 실제 사물이 인식되어 인간의 사고속에 담겨지는 그 “무언가”를 개념이라고 호칭한다. 물론, 이 개념은 실제 사물과 연결되는 것도 있고, 아닌 것도 있지만 이 글에서 논하는 것은 범주를 넘어서므로 딱히 논하지는 않겠다.

객체지향 프로그래밍은 이 추상화의 과정을 거쳐 생성된 개념들을 코드로 옮긴다. 이때 작성된 코드가 객체의 설계도라고 볼 수 있는 클래스class인지, 아니면 객체의 첫 상태라고 볼 수 있는 프로토타입prototype인지에 따라 세부적인 형태는 달라질 수 있겠지만, 어쨌든 개념을 코드로 옮긴다는 점에서는 동일하다고 볼 수 있다.

다시 살짝 인식론으로 돌아가서 본다면, 인간이 하는 일이 사물을 통해 개념을 만들어내는 추상화의 작업만 있는 것은 아니다. 알고 있는 개념을 이용해 그에 부합하는 사물을 만들어내거나 모사하는 구체화의 작업도 존재한다. 객체지향 프로그래밍에서는 이 과정이 코드 실행단계라고 이야기할 수 있다. 코드로 옮겨진 개념들이 컴퓨터라는 기계를 통해 실행되면서, 실제로 객체가 생성되고 상호작용을 시작하는 것이다. 그리고, 이 상호작용을 통해 프로그래머의 의도를 표현하게 된다.

물론, 인간이 추상화를 어떤 방식으로 수행하는지와 그 결과물인 개념이 어떤 구성요소로 이루어져 있는지에 관해서는 이견이 많을 수 있겠지만, 객체지향 프로그래밍에서는 명확하게 정의되어 있다. 객체는 상태와 행위로 구성된다. 따라서, 그 객체를 개념화한 클래스 혹은 프로토타입 역시 상태와 행위로 구성된다. 이론적으로 본다면, 객체지향 프로그래밍 패러다임은 인간의 자연스러운 추상화에 의해 생성된 개념을 코드라는 형태로 써내려가기만 하면 손쉽고 자연스러운 소프트웨어 작성을 도모할 수 있다.

이론은 어디까지나 이론이다. 인간이 생각해낸 개념을 상태와 행위로 구성되는 개념만으로 표현이 힘든 것도 있거니와, A라는 프로그래머가 만든 개념이 불필요하게 복잡하고 이해하기 어려운 형태일 수도 있고, 또는 지나치게 단순하거나 한 개념에 너무 많은 것을 뭉뚱 그려놓은 (흔히 말하는 -스파게티-) 형태일 수도 있다. 이런 이론과 현실의 갭이 프로그래머의 능력을 나타내는 지표가 될 수 도 있겠지만, 객체지향 프로그래밍의 단점으로 바라볼 수 도 있다.

이 갭을 줄일 수 있는 가장 좋은 방법은 아마도 논리적-철학적 사고방식의 습관화가 아닐까 생각해본다. 어쩌면, 소프트웨어 개발자/프로그래머에게 가장 필요한 지식은 수학이라기 보다는 철학일 수 도 있을 것이다.

.. 이후는 다음 기회에 (우왕 길다)

ps. 꼴랑 이거 쓰는데 한달 걸렸어요. 쳇.
ps2. 레이아웃 깨지는 문제로 다시 엽니다. 🙂

Zero-configuration: automatic lookup

Zero Configuration: Simple is best는 어렵다.에서 언급한 바와 같이, 현재 제작중인 소프트웨어의 모듈/시스템수는 엄청나게 증가해버렸다. 서로 연동해야하는 모듈이 많으니 이리저리 입력해야하는 네트워크 정보(각 노드의 IP와 Port)가 매우 많다. (제어UI에는 8개정도지만, 세밀하게 조정에 들어가면 셀수 없이 많다.) 이런 상황에서 각 노드들을 효율적으로 관리하려면 어찌해야 할까?

각 노드마다 설정을 전부 해야한다고 생각하면, 그 복잡도는 노드가 추가될 때마다 선형적으로 증가하며, 각 노드에서 필요한 기능-설정이 추가될 때 마다 기하급수적으로 증가한다. 언제까지 이런 설정을 반복하고 있을 수는 없다. 쉽게 생각할 수 있는 해결책은 중앙집중관리노드를 도입해서 실제로 기능을 수행하는 각 노드들이 중앙 노드를 바라보고 있는 Star형 토폴로지를 구현하는 것이다. 이러면, 문제가 해결될 것 같지만 크게 2가지 관점에서 결정적인 단점을 가지고 있다.

  1. 전체 시스템의 복잡도는 전혀 줄어들지 않으며, 다만 중앙 노드로 압축될 뿐이다.
  2. 중앙 노드에 장애가 발생할 경우, 전체 시스템이 동작하지 않는다.

이런 단점을 최소화하는 모델이라면, 전화번호부 모델이 있다. Java Jini나 Web Service에서 활용하는 Naming 서비스 혹은 Directory서비스가 대표적인 사례인데, 필요한 노드(서비스)의 위치를 관리하는 Naming 서비스를 도입하여, 각 노드간 통신을 위해 필요한 위치정보를 URL 혹은 그와 유사한 형태로 저장하여 서로 Lookup을 하게 하는 것이다. Respository나 Directory 서비스의 복잡도는 상대적으로 낮은 편이다. 권한이나 인증같은 부분을 제외하고 생각하면, 등록과 조회만 있으면 된다. TCP/IP네트워크에 익숙하다면, DNS를 생각하면 된다.

실제 데이터를 주고 받기 위한 통신 채널은 각 노드사이에서 형성되므로 Naming 서비스는 단지 ‘전화번호부’의 역할만을 수행한다. 각 노드들 역시 Naming 서비스의 위치만 설정해주면 되므로, 설정은 더더욱 간편해진다.

중앙 노드로 압축되는 복잡도의 문제는 어느정도 해소가 가능해졌지만, 중앙 노드의 장애에 대한 문제점은 아직 남아있다. Naming 서비스의 이중화로 해결할 수 있는 문제일 수도 있겠지만, Naming 서비스의 조회결과를 로컬에 캐쉬하는 방법도 있다. 노드들이 급격하게 위치를 변경하는 시스템이라면 독이 될 수 있지만, 위치가 그렇게 급격하게 변하지 않는 다소 정적인 시스템이라면 충분히 위력을 발휘할 것으로 생각된다.

멋져 보이지만, 마지막 결정타가 하나 남아있다. 이건 ‘Zero-Configuration’ 이 아니다. 여전히 사용자는 Naming 서비스의 위치를 ‘설정’해주어야 한다. TCP/IP에는 Broadcasting이라는 멋진 방법이 존재한다. 각 노드들에 Naming 서비스를 설정하지 않아도 Broadcasting으로 Naming 서비스를 찾아낼 수 있다. 물론, 수동 설정도 가능해야 하겠지만 말이다. 🙂

XML, XPath 그리고 소프트웨어 테스팅

보통 TDD(Test-driven development)는 잘 짜여진 테스팅 케이스를 이용해 소프트웨어의 수행 결과를 점검하는 형태로 이루어진다. 문제는, 이 방식의 접근은 처음부터 TDD를 하지 않으면 적용하기 힘들다는 거다!

게다가 최근 TDD가 없이는 개발이 불가능한 상황에 봉착했다. 입력 데이터의 종류는 수없이 많고, 이 데이터의 조합에 따라 서로 Side-effect를 발생시키는 골머리아픈 상황이다. (소프트웨어 설계가 잘못된건 아니다. 애시당초 풀어야할 문제가 이렇게 꼬여있다.) 설상가상으로 데이터의 종류가 무엇인지 알지 못하는 상황에서 종류를 guessing하고 조합해서 문제를 풀어야 할 정도니 말 다했다. 그리고, 출력되는 결과물도 굉장히 다양하다. (그 입력만큼이나 복잡하다)

프로게이머도 아닌데 엄청난 키보드+마우스 컨트롤을 자랑하며 테스팅을 할 수는 없는 노릇이고 해서, 간단한 테스팅 프로그램을 도입해서 문제를 해결하기로 생각하였다. 입력데이터를 잘 준비해두면 생성된 결과물을 잘 검사하기만 하면 되는 일 아니겠는가?

이 정도만 되면, 문제를 해결하기는 쉬울 것이지만 검사해야 할 항목들이 자주 변한다는 사실을 깨달았다. 입력데이터별로 차이도 존재하고, 데이터 종류에 따라, 또 그 조합에 따라 항목은 계속 바뀌어야 한다. 이쯤되면, 간단한 스크립트 언어를 만들어서 검사항목을 쉽게 변경할 수 있게 하면 되리라는 생각이 든다. 어머나. 배보다 배꼽이 커져버렸다. 🙂

그래서, XML과 XPath를 도입하기로 했다. 수행결과를 XML로 남기고, 남겨진 XML파일이 가져야할 조건, 즉 정상적인 생성물의 조건을 XPath 표현식으로 작성하는 것이다. TDD의 용어로 말하면, XPath 표현식은 Test case가 된다.

시험적으로 전체 시스템의 일부에만 적용해 보았는데 아주 마음에 든다. 물론, 서로 의존관계에 있는 XPath 표현식을 계층적으로 처리해주는 작업을 해야하긴 하지만, 그건 천천히 해도 될 것 같다. 🙂

ps. 관련된 코드와 예제를 공개하고 싶지만, 작업하다보니 대외비 코드가 너무 많이 들어갔네요. T_T

Zero-configuration: Simple is Best는 어렵다.

Zero-configuration이란 말을 처음 접했던 건, 한국에서 널리 사용되는 자막 파일인 SMI에 대한 지원을 Mac OS X용 통합 코덱(?)인 Perian에 넣을 수 없다는 애플포럼의 한 쓰레드 때문이었다. 요지는, SMI를 지원하기 위해서 자막의 인코딩 셋업같은 별도의 세팅이 필요하기 마련인데, 이게 Zero-configuration에 위배되기 때문에 SMI에 대한 지원을 넣을 수 없다는 내용이었다. (물론 이유가 이것만 있는 것은 아니었다.)

Zero-configuration은 말 그대로 설치 이후 설정이 필요없는 소프트웨어의 특성을 의미한다. 더 보기 “Zero-configuration: Simple is Best는 어렵다.”

Boost 1.35: 주목할만한 새로운 라이브러리!

  1. asio: Portable networking, including sockets, timers, hostname resolution and socket iostreams, from Chris Kohlhoff.
  2. Function Types: Boost.FunctionTypes provides functionality to classify, decompose and synthesize function, function pointer, function reference and pointer to member types. From Tobias Schwinger.
  3. Fusion: Library for working with tuples, including various containers, algorithms, etc. From Joel de Guzman, Dan Marsden and Tobias Schwinger.
  4. Interprocess: Shared memory, memory mapped files, process-shared mutexes, condition variables, containers and allocators, from Ion Gaztañaga.
  5. Intrusive: Intrusive containers and algorithms, from Ion Gaztañaga.
  6. MPI: Message Passing Interface library, for use in distributed-memory parallel application programming, from Douglas Gregor and Matthias Troyer.
  7. System: Operating system support, including the diagnostics support that will be part of the C++0x standard library, from Beman Dawes.

하나씩 뜯어보고 리뷰를 해야겠군요. 아싸. (포스팅거리가 생겼다)

Flowcut!: Orthogonal Code Injection for C++

C++ Native AOP의 가능성에서는 Aspected Oriented Programming이란 용어를 사용하였고, 크게 문제될게 없다고 생각했지만, AOP에 대해 고찰을 해보니 AOP라고 (좁은 의미에서) 부르기에는 약한 것이 사실이었다.

그래서 생각해낸 말이 직교적(수직적?) 코드 삽입orthogonal code injection이다. 뭐 기존에 존재하는 말일수도 있지만… 말 그대로 “기본 뼈대가 되는 기존 로직에 수직적인 코드삽입을 해야할 때 쓰면 좋지 않을까?” 라는 생각으로 만든 Flowcut!이란 이야기 되겠습니다.

더 보기 “Flowcut!: Orthogonal Code Injection for C++”

idea: Distributed wiki

요즘 학교에 다니는지라, 이런저런 문서를 작업할 일이 많다. 뭐 회사다닐때도 많긴 했지만, 학교의 무선랜 환경이 그다지 좋지 않기 때문에 위키나 스프링노트에 문서작업을 하는 요즘에는 애로사항이 좀 많다. (다행히도 왠만한 강의실에서 무선랜이 다 되기 때문에 큰 문제는 없지만… 그래도 불안한건 사실이다! 간간히 넷이 끊겨 주시는데 정서 불안..)

이래서 생각난 것이 Mercurial과 유사한 형태의 저장 메커니즘을 가진 위키 시스템이다. 쉽게 말하면, 로컬에 위키 솔루션을 설치하고 원격에 있는 위키의 내용을 당겨(pull)와서 로컬에서 편집 및 저장을 자유롭게 하고, 네트워크가 사용 가능한 시점에 원격에 있는 위키에 밀어(push)넣는 방식이다. 중앙집중식 저장 메커니즘과는 달리 remote가 특정 서버가 아닌 설치된 위키 솔루션이라면 그 어느 것이든 상관이 없다는게 다른 점이랄까?

여기서 더 나아가, 데이터 뿐만이 아니라 위키 솔루션의 구현에 필요한 코드/이미지/데이터 역시 동일한 방식으로 관리할 수 있다. 즉, 집에 있는 위키솔루션을 업그레이드 하고 나서, 변경된 내용을 홈페이지에 있는 위키솔루션에 push를 하면 홈페이지에 있는 위키 솔루션이 업데이트가 되는거다. (물론 변경된 위키데이터들도 업데이트가 된다.) 그리고, 랩탑에 있는 위키솔루션에서 홈페이지에 있는 위키솔루션을 pull해오면 랩탑도 업데이트되는 거다.

이 정도로 생각을 해보니, 지금 사용하고 있는 Dokuwiki(혹은 RDB를 사용하지 않는 그 어떤 위키라도)에서 충분히 가능한 내용인듯 하다. 일단 각 노드에 php를 구동할 수 있는 웹서버를 설치해두고 위키 솔루션 전체를 Mercurial로 관리하는 거다. 그리고, 이런 작업을 위해 약간의 유틸리티 작업만 해주면 충분히 가능할 것 같다. (해보다가 뭣하면 이런 용도로 하나 만들지 뭐.)

음. 내친김에 Dokuwiki+hg로 한번 해볼까.

XMLCC: XML생성을 위한 C++ 라이브러리

어쩌다 이름이 XMLCC가 되었는지는 모릅니다. 그냥 손가락에서 저렇게 나왔어요. 신의 뜻이라 생각하고 넘어갑니다. 씨익-

사건의 발단은 회사에서 XML을 생성해서 서버에 전송해야 하는 일이 생겨서 std::stringstream을 이용해 XML을 생성하다 보니 코드가 너무 지저분해지는 겁니다. 후.. 이런저런 고민을 한 끝에 좀 깔끔하게 짤 수 있는 프로그램을 작성해보기로 했지요. 🙂

일단 만들 XML부터 봅시다.


<author>Crow, Lim</author>

저런 문서를 하나 만들기 위해, std::stringstream을 사용하게 되면 다음과 유사한 코드가 등장할겁니다.

std::stringstream ss;

void make_xml(std::stringstream&amp; ss, std::string const&amp; title, std::string const&amp; author, ...(생략)...)
{
ss << "<author>";
ss << author;
ss << "</author>";
}

보기만 해도 끔찍하군요. 엄청난 escaping문자들.. 보기만해도 어지럽습니다. 만약 다음과 같이 작성할수 있다면 행복할텐데요.

void make_xml(std::stringstream& ss, std::string const& title, std::string const& author, ...(생략)...)
{
// 들여쓰기는 보기 편하라고 한 것 입니다.
element_start(ss, "book");
    attribute(ss, "title", title);

element_start(ss, "author");
    text(ss, author);
element_end(ss, "author");

element_start(ss, "date");
    attribute(ss, "year", year);
    attribute(ss, "month", month);
    attribute(ss, "day", day);
element_end(ss, "date");

element_end(ss, "book");
}

아쉽게도 위와 같은 코드는 작동하지 못합니다. XML의 특성상 attribute는 element 시작태그 내에서 정의가 되어야 하기 때문이지요. 물론, 이를 해결하기 위해 element_start의 인수로 attribute의 list를 넘겨주는 방법도 있습니다만, 이 방법은 해당하는 리스트를 생성해야한다는 단점이 있습니다. 의미상 코드에서 정적으로 정의가 가능한 XML을 굳이 리스트를 생성하면서 만들 필요는 없지요.

이를 위해 element_start()의 리턴을 객체로 주는 방법을 사용했습니다. 🙂 그 객체에서 시작태그를 생성하고 attribute도 처리하는 거지요.

void make_xml(std::stringstream& ss, std::string const& title, std::string const& author, ...(생략)...)
{
    element_start(ss, "book")
        .attribute("title", title);

    element_start(ss, "author");
    text(ss, author);
    element_end(ss, "author");

    element_start(ss, "date")
        .attribute("year", year)
        .attribute("month", month)
        .attribute("day", day);
    element_end(ss, "date");

    element_end(ss, "book");
}

element_start()의 리턴객체에서 attribute를 호출합니다. 자세히 보시면 뒤에 세미콜론(;)이 없는 라인들이 있는데 사실 한 expression이란 겁니다. 일종의 호출 체인이랄까요?

xmlcc.h에 있는 주요 부분을 보면 다음과 같습니다.

	struct element_start_tag
	{
		std::ostream& out_;

		element_start_tag(std::ostream& out, std::string const& name)
			: out_(out)
		{
			out_ << "<" << name;
		};

		element_start_tag(element_start_tag const& tag)
			: out_(tag.out_)
		{
		};

		element_start_tag& attribute(bool use, std::string const& name, std::string const& value)
		{
			if(use == true)
			{
				return attribute(name, value);
			}

			return *this;
		}

		element_start_tag& attribute(std::string const& name, std::string const& value)
		{
			out_ << " ";
			text(out_, name); 
			out_ << "=\"";
			text(out_, value); 
			out_ << "\"";
			return *this;
		}

		template<class ValueT, class FilterFuncT>
			element_start_tag& attribute(std::string const& name, ValueT const& value, FilterFuncT const& f)
		{
			out_ << " ";
			text(out_, name); 
			out_ << "=\"";
			text(out_, f(value)); 
			out_ << "\"";

			return *this;
		}

		template<class Func>
			element_start_tag& attribute_with_generator(std::string const& name, Func const& f)
		{
			out_ << " " << name << "=" << "\"";
			f(out_);
			out_ << "\"";
			return *this;
		}

		element_start_tag& operator()(std::string const& name, std::string const& value)
		{
			return attribute(name, value);
		}

		element_start_tag& operator()(bool use, std::string const& name, std::string const& value)
		{
			return attribute(use, name, value);
		}

		void close_now()
		{
			out_ << "/";
		}

		~element_start_tag()
		{
			out_ << ">";
		};
	};


	inline element_start_tag element_start(std::ostream& out, std::string const& name)
	{
		return element_start_tag(out, name);
	}

보시면, 생성자에서는 시작태그의 요소이름까지만 출력을 하고, 소멸자에서 태그를 닫습니다. attribute를 사용하면 stream에 attribute를 출력해주지요. 또한, close_now()를 호출하면 “/”를 출력시켜줌으로써 스스로 닫는 element를 생성해줍니다. 그리고, element_start()는 이 element_start_tag를 생성해주는 도우미 역할을 하지요.

attribute메소드는 operator()를 이용해도 사용할 수 있습니다. 이 경우 코드가 훨씬 간단해지지요.

void make_xml(std::stringstream& ss, std::string const& title, std::string const& author, ...(생략)...)
{
element_start(ss, "book")
    ("title", title);

    element_start(ss, "author");
        text(ss, author);
    element_end(ss, "author");

    element_start(ss, "date")
        ("year", year)
        ("month", month)
        ("day", day);
    element_end(ss, "date");

element_end(ss, "book");
}

사실 XML생성에 있어서 다른 XML의 구성요소들은 크게 문제가 되지 않는데, 가장 머리 아픈 부분은 element의 시작태그와 그 안에 내포된 attribute인지라 이런 잔머리를 굴려봤습니다. 🙂 덕분에 소스코드는 escape문자 없이 깔끔해졌군요. 🙂 잇힝-

좀 더 삽질해서 DSL(Domain Specific Langauge)형태로 만들어 보려고 했는데, 시간도 없고 이정도면 쓸만한거 같아서 여기서 멈췄습니다. 🙂

xmlcc.h 다운로드

C++ Web Server: Concept #1

1차분 Concept

  1. J2EE에서 등장했던 Filter개념으로 모든 플러그인들을 해결한다.
  2. Filter들의 Chain이 기본이다. Chain의 끝에는 Static 파일 Service가 들어간다.
  3. 각각의 Filter들은 Request와 결합하여 하나의 Task를 생성한다
  4. 특정 Request에 연관된 Task들은 Task Chain이라고 부른다.
  5. Task Chain은 내포한 Task들이 종료될때마다 다른 쓰레드로 이동이 가능해야 한다.
  6. Network IO는 전담 Multiplexer쓰레드에서 처리한다
  7. 스크립트 확장은 특정 스크립트 파일을 Filter로 만들어주는 ScriptingEngine에서 처리하도록 한다.
  8. Request/Response의 Body부분은 std::iostream의 shared_ptr로 관리한다

2차분

  1. Filter는 Request Filtering과 Response Creation을 담당하는 process_request operation과 Response Filtering을 담당하는 filt_response operation 으로 이루어진다.
  2. response::shared_type process_request(request::shared_type)
  3. void filt_response(response::shared_type)
  4. process_request에서 response를 생성하고 나면, 그때부터 filt_response를 process_request를 실행한 역순으로 실행한다. 즉, Stack이란 이야기
  5. 웹서버 쪽에서는 Mime parsing(폼변수 분석)에 대한 책임을 지지 않는다. HTTP Header까지만 책임을 지고 그 뒤는 각 모듈에게 맡긴다. (이래야 대용량 전송시 커스터마이징이 쉬워진다.)
  6. Mime Parsing에 대한 책임을 지지 않는 대신에 Mime Parsing을 위한 라이브러리를 별도로 제공한다. ScriptingEngine에서 이를 활용할 것인지는 별도로 고려한다.

C++ Web Server: Concept#0

C++로 웹서버를 만들 생각을 왜했는지는 이미 기억이 가물가물. @_@

  1. J2EE에서 등장했던 Filter개념으로 모든 플러그인들을 해결한다.
  2. Filter들의 Chain이 기본이다. Chain의 끝에는 Static 파일 Service가 들어간다.
  3. 각각의 Filter들은 Request와 결합하여 하나의 Task를 생성한다
  4. 특정 Request에 연관된 Task들은 Task Chain이라고 부른다.
  5. Task Chain은 내포한 Task들이 종료될때마다 다른 쓰레드로 이동이 가능해야 한다.
  6. Network IO는 전담 Multiplexer쓰레드에서 처리한다
  7. 스크립트 확장은 특정 스크립트 파일을 Filter로 만들어주는 ScriptingEngine에서 처리하도록 한다.
  8. Request/Response의 Body부분은 std::iostream의 shared_ptr로 관리한다