|
|
1 Linux More Popular as Deployment Platform
많은 개발자들이 자신의 실력을 다른 개발자들에게 자랑하기 위해서 혹은 그들과 의견을 주고 받기 위한 목적으로 오픈소스 프로젝트를 수행한다. 많은 경우 프로젝트 리더들은 프로젝트를 함께 개발하는 개발자의 소프트웨어 개발 환경에 대한 정보를 알기를 원한다.
이클립스 프로젝트는 4월 일반적인 개발자들이 어떠한 환경에서 개발을 하는지에 대한 광범위한 조사를 실시 했다. 주목할만한 내용은 개발플렛폼으로 리눅스가 상당한 진보를 이루어냈으며, 특히 배포플랫폼으로써는 윈도우즈를 추월했다는 점이 될 것이다.
이클립스 제단의 마케팅 디렉터인 Ian skerrit 이클립스를 사용하는 개발자의 64%가 윈도우즈를 사용하고 27%가 리눅스를
사용하고 있다는 결과를 발표하였다. 참고로 2007년 조사결과에 따르면 윈도우즈 사용자는 74% 리눅스 사용자는 20% 였다.
상당한 개발자가 리눅스를 개발환경으로 이동되었음을 알 수 있다.
deployment 플랫폼으로 보자면 리눅스가 43%로 41%의 윈도우를 앞지르고 있다.
개발자 커뮤니티는 데스크탑 개발환경과 deployment 개발환경을 지속적으로 리눅스로 바꾸어나가기 때문인 것으로 보고 있다. Skerrit는 그의 블로그에서 우분투와 레드햇으로의 이동은 분명한 이득을 주지만 윈도우즈는 그러하지 못하기 때문이라고 그 이유를 설명한다.
이러한 결과가 발생한 또 다른 이유는 리눅스 친화적인 Mysql과 Oracle과 같은 데이터베이스의 점유율 때문이다. 이들 두 데이터베이스의 점유율은 55%가 넘고 있다.
또한 이번 조사 결과는 기업들이 단지 오픈소스를 사용하기만 하는데에 머무르지 않고, 오픈소스에 공헌을 하는 정도로 오픈소스에 대한 인식이 성숙되었음을 보여주고 있다.
2007년에 46%의 기업들이 OSS의 사용을 허락했지만 OSS에 대한 공헌은 이루어지지 않았다. 즉 내부적으로 OSS를
사용하기만 했다. 지금은 이 비율이 27%로 줄었다. 이제 48.2% 의 응답자들이 그들의 기업이 OSS 커뮤니티로의 지원을
허락하고 있다고 응답을 하고 있다. 2007년의 37%에 비하면 10%나 증가한 수치다. - 국내의 경우에도 대략 2007년과
2008년을 기점으로 기업들의 오픈소스에 대한 직접적인 참여와 지원이 늘어나고 있다. -. OSS를 통한 비지니스 모델에 기대를
걸고 있다는 응답도 2007년 10%인 것에서 2008년 15.6%로 대폭 증가했다.
2 The Open Source Developer Report 2009
이 문서는 2009년 Eclipse Community 의 보고서인 The Open Source Developer Report를 보고 정리했다.
이 보고서는 다음과 같은 핵심내용을 포함하고 있다.
- 리눅스는 데스크탑과 서버 환경모드를 공유하고 있다. 개발자들이 개발을 위한 데스크탑운영체제로 Microsoft 윈도우즈에서 리눅스와 Mac OX로의 이동하는 모습이 분명히 감지되고 있다. 특히 depolyment 운영체제환경으로 리눅스는 가장 일반적인 플렛폼이 되었다.
- Mysql 과 Oracle 데이터베이스는 55%이상의 지배적인 점유율을 보여주고 있다.
- 형상관리도구로는 SVN(Subversion)이 57% 점유율을 보여주고 있다.
- 기업은 성숙한 OSS 모델을 받아들이고 있다.
- 더 많은 기업들이 오픈소스 소프트웨어의 사용과 기여에 참여하고 있다. 보고서에 따르면 2008년 48.2%였다. 2007년에는 37%였다.
- 15.6 %의 회사가 OSS에 연계한 비지니스 모델을 만들고 있다.
3 보고서 참여자 통계 자료
응답자의 반이상이 프로그래머였으며 기타 시스템 아키텍쳐, 매니저, 소프트웨어 품질관리 및 테스터, 마케팅 메니저 등이 참여하였다.
4 개발자 데스크탑 개발 환경
개발자가 사용하는 데스크탑 운영체제 통계정보를 보여주고 있다. 윈도우즈에서 Linux와 Mac OSX로 이동하는 것을 확인할 수 있다. 26.9%의 개발자가 주요 개발 데스크탑 환경으로 리눅스를 사용하고 있다고 응답했는데, 2007년에 비하면 7%가 증가한 수치다.
2007년에 비하면 10%가량 줄어들긴 했지만, 여전히 윈도우즈는 64%의 사용율을 가지는 지배적인 운영체제다. 가장 많이 사용되는 리눅스 배포판은 Unbuntu인 것으로 조사되었다. Mac OSX도 2007년 3.5%에서 6.9%로 증가했다.
- Windows Server or Client : 64.3%
- Linux : 26.9%
- Ubuntu 14.5%
- Fedora 3.4%
- SUSE 3.2%
- Debian 2.6%
- RHEL 1.3%
- 기타 배포판 1.8%
- Mac OSX : 6.9%
- Sun Solaris or OpenSolaris : 0.9%
- Other : 0.8%
4.1 IDE : 통합개발 환경
Eclipse IDE가 가장 인기있는 개발환경으로 지목되었다.
- Eclipse JDT : 60%
- Eclipse PHP 개발 툴 : 12.6%
- Eclipse C/C++ 개발 툴 - CDT - : 6.3%
4.2 형상관리 툴
SCM(Source code management), CMS(changes management) 그리고 BM(Build management)에 대한 사용율을 조사한 자료다.
SCM : 가장 널리 사용되는 소스코드 관리 시스템은 Subversion으로 57.7%의 사용율을 보였다. 20%의 CVS가
그 뒤를 따랐다. SCM으로는 오픈소스가 절대적인 위치를 차지하고 있음이 확인되고 있다. 단 5,000 명이상의 고용인을 가진
대기업의 경우에는 Subversion의 점유율이 41.3 %로 상대적으로 적었다는 점이 특이할만 하다. 최근 몇 년사이에
Subversion의 사용이 증가했는데, 일반적으로 대기업은 이러한 변화에 느리게 반응하는게 이유인 것 같다.
CMS : JIRA와 Bugzilla가 각각 17% 점유율을 기록했다. CMS는 아예 사용하지 않는다는 응답도 22.7%에 달했다. 예상과 달리 TRAC은 7.2%의 낮은 점유율을 보여주었다. Custom 즉 직접만들어서 사용한다는 응답이 8.4%에 달했는데, 이는 회사의 다른 정보들과 연동되어서 사용되어야 하는 CMS의 특성때문으로 생각된다.
BE : 33.4%의 Ant가 가장 널리 사용되는 것으로 확인되었다. CMS와 마찬가지로 21.7%의 비교적 많은 응답자가 BE를 사용하지 않는다고 응답을 했다.
5 언어 및 응용프로그램
자바는 커뮤니티에 의해서 주도적으로 사용되는 언어였으며, 76.6%가 그들의 첫번째 언어로 자바를 사용한다고 응답했다. 개발용도는 Server-Centric 30.2%, 웹이 24.7%, 데스크탑 애플리케이션이 23.4%였다.
이 보고서는 주요 3가지 개발용도에 대해서 각각 어떠한 툴 혹은 언어를 사용하는지에 대해서 조사했다.
5.1 Server-Centric 애플리케이션
server-centric 애플리케이션의 개발을 위해서 가장 널리 사용되는 되는 것은 servlet이였다. 그다음으로는 spring 프레임워크였으며, EJB도 38.3%로 널리 사용되는 것으로 조사되었다. 대표적인 server-centric 애플리케이션은 J2EE 애플리케이션, 데이터베이스 애플리케이션, ERP, CRM 등이 있다.
5.2 Web, Rich 인터넷 애플리케이션
웹과 rich Internet 애플리케이션 (RIAs)의 경우에는 주도적인 프레임워크나 툴이 없는 것으로 조사되었다. 오픈소스 Ajax 프레임워크, Flash/Flex 등이 많이 사용되었으며, 아예 사용하지 않는 다는 응답도 꽤 되었다. GWT가 많이 사용되고 있다는 점이 눈에 띈다.
5.3 Rich Desktop Applications
Eclipse RCP (47.6%)와 Swing (43.8%)로 양분되어 있는 모습을 보여주고 있다.
6 Deployment 환경
개발자들이 그들이 개발한 애플리케이션이 설치될 타겟 환경으로 어떠한 것을 이용하는지에 대한 조사내용을 포함하고 있다. 운영체제, 데이터베이스 그리고 애플리케이션 서버 환경에 대해서 조사를 했다.
리눅스는 주목할만한 Depolyment 타겟 운영체제로 성장을 했으며, 지금은 오히려 윈도우를 앞서고 있는 것으로 확인되었다.
2008년 기준 42.7%의 점유율을 보여주고 있는데 2007년의 37%에 비하면 5%가까이 증가한 수치다.
Ubuntu(12%), Red Hat Enterprise Linux (10.2%) 배포판이 널리 사용되고 있었다. 결과를 보면
윈도우환경의 유저가 리눅스, Mac OSX 등으로 이동한 것으로 보인다.
6.1 주요 데이터 베이스
예상대로 Mysql 과 Oracle이
각각 27.7%와 27.3%로 주요하게 사용되고 있었다. Mysql과 Oracle은 선호대상이 갈리는 것이 확인 되었다.
기업규모로 보자면, 5000 명 이상을 고용하고 있는 거대 기업의 경우에는 Oracle이 33.5%, IBM DB2가
10.7%, Mysql 이 11.6% 였다.
Oracle은 다음과 같은 선호대상층을 가진다.
- Java를 주요 개발언어로 사용하는 경우
- server-centric 애플리케이션을 개발하는 경우
- Application 서버를 이용하는 경우
Mysql은 다음과 같은 선호대상층을 가진다.
- PHP 언어를 주요언어로 사용하는 개발자
- RIA/Web 애플리케이션
6.2 주요 Application 서버
Apache Tomcat이 가장 널리 사용되고 있다. 그다음으로 JBoss, IBM WebSphere가 사용되고 있다. 아무
것도 사용하지 않는다는 응답도 25.3%나 되었다. 5000명이상 고용인을 가진 대기업의 경우에도 여전히 Apache
Tomcat이 가장 널리 사용되었으나 IBM WebSphere가 6.9%에서 12.0%로 크게 오르고 Apache Tomcat
는 34.8%에서 26%로 떨어진걸 확인할 수 있었다.
7 평가
몇년전만 하더라도 오픈소스는 기업에서는 받아들이기 힘든 모델이라고 평가되기도 했었다. 혹은 많은 기업들이 단지 오픈소스의 단물만 빨아먹으려고 할뿐 결코 오픈소스 모델에 참여하지는 않을 것이라고 생각되던 때도 있었다.
그러나 이 보고서에 따르면, 오픈소스는 메이저한 개발/배포 환경은 물론이고 비지니스 환경까지 그 영역을 지속적으로 확장시켜가고
있음을 확인할 수 있다. 더불어 분명하게 더 많은 기업들이 적극적으로 오픈소스 모델에 직접적으로 참여하고 있는 것도 확인할 수
있다.
대세는 OSS
 :::

저작권의 출현
쓰여진 글에 대한 저작권이 언제 출현했을까? 거기에 대해서 왠지 알아보고 싶어졌다. 당연히 쓰여진 글의 저작권이니까. 글이
종이든지 어디에든지간에 씌여진 시점부터라고 생각할 수도 있겠지만, 실제 저작권은 쓰기가 발명되고 난 뒤에도 한참이나 지나서
만들어졌다. 정확히 말하자면 쓰기가 내면화 되는 시점 - 대게는 인쇄가 일반화된 시점부터 본격적인 내면화가 시작되었다고 보고
있다 - 부터 만들어졌다고 봐야 될 것이다.
쓰기가 내면화 되었는지가 왜 중요한지에 대해서 알아봐야 겠는데, 그럴려면 최초 등장한 쓰기와 쓰기가 내면화된 시점의 쓰기의 차이점에 대해서 먼저 이해를 해야 될것 같다.
쓰기가 내면화되지 않은 시절에 말은 기억을 용이하도록 하기 위해서 예전부터 내려온 틀에박힌 정형구과 운율등을 이용했다. 영웅의 일대기를 그린 호머의 서사시가 대표적인 예가 될 것이다.
아킬레스가 분전을 했다. 라고 말하는 것보다 빠른발의 아킬레스는 영웅의 방패와 창을 들고 적진으로 돌진했다라는 식으로 표현하는게 훨씬 기억하기가 쉽기 때문이다. 오디세우스를 지모가 뛰어난 오디세우스라고 한다든지, 신과 같은 아가멤논이여라고 하는 것도 비슷한 이유에서다. 이러한 정형구적인 표현은 중세 작품에도 자주 등장한다. 잠자는 동안의 달콤한 꿈을 믿을 수 있다면 내 꿈은 필시 좋은 소식이 온다는 증거일 것이다.
- 로미오와 줄리엣 중-. 정형구적인 표현은 사람의 감성을 강하게 자극하기 때문에 적절히 사용한다면 매우 숭고하게 보이기도
한다. 그래서 호머의 서사시가 그동안 그렇게 높은 평가를 받아왔는지도 모르는 일이다. 지금은 이러한 표현은 거의 사용하지 않지만
말이다. 환타지 소설에서나 간혹 볼 수 있을 뿐이고, 가능하면 정형구적인 표현을 사용하지 말도록 지도를 받는다.
아뭏든.. 이들 정형구와 운율은 개인의 것이 아닌 집단에 의해서 전승된 공중의 지혜와 같은 것이기 때문에, 이들로 이루어진 결과물 역시 개인의 것이 아닌 집단의 것이라는 인식이 강했다. 그러니 저작권을 주장한다는 것 자체가 있을 수 없는 일이었다.
쓰기가 출현한뒤에도 오랫동안 저작권이라는 개념이 모호했는데, 왜냐하면 초기 쓰기는 지금의 쓰기와는 달리 사람이 말로
한것을 옮겨적는 작업이였기 때문이다. 공동의 재산이라고 생각되는 말을 종이에 고정시키는 작업이였다. 이당시 말로 된 작품이란
것은 과거의 지혜를 기억하기 좋게 운율이 가미되어서 전승된 정형구의 집합이였으니, 이들 과거의 지혜와 정형구들 운율들에 소유권을
주장할 수 없는 것이었다. 그것은 공동의 재산이였기 때문이다. 이경우 중요한 것은 저작권이 아니라 그 지혜를 말로써 나타낼 수
있는 능력을 가진자 즉 시인 혹은 나이를 먹은 지혜로운 노인들이였다.
노인이 천대받는다고 걱정들이 이만저만이 아니다. 슬프다고 생각할지 모르겠지만 이것은 당연한 현상이다. 지금 지혜는 구전되는
말로써 전달되는게 아니다. 시간과 공간에 독립된 문자에 의해서 전달되고 공유되어진다. 현대사회에서 노인의 지혜는 거의 쓸모가
없어졌다. 물론 노인이 가지고 있는 삶의 지혜는 중요한 자산이 될 수 있겠으나 자본주의 사회에서 돈으로 환산되지 않는 삶의
지혜는 쓸모가 없어진다. 대게는 할일 없는 노인의 쓸데없는 잔소리 취급받는다. 그러니 노인이 버려질 수 밖에.. 이건 덤으로
드는 생각인데, 노인이 되어서 버려지지 않기 위해서는 자기 스스로의 생산수단을 가지는 수 밖에 없다. 농사든지 뭐든지간에
말이다. 그렇지 않다면, 당신이 왠만큼의 돈과 지식과 정보를 가지지 않는 한은 폐기처분 될 걸 각오해야 할 것이다.
본론으로 되돌아와서 인쇄술이 발달해서 본격적인 쓰기가 정착되면서, 동시에 프라이버시 의식 역시 성숙하게 된다. 기존의 말하기가
집단과의 교류하는 행위라면 쓰기와 읽기는 개인의 내면과의 대화이기 때문이다. 혼자 짱박혀서 무엇인가를 할 수 있는 개인만의 공간이
라는 것이 이때부터 생겨나고 여기에서 프라이버시라는게 자라난다. 물론 그전에도 몇몇 작가들이 그러한 개인적인 행위를 해오긴
했지만 극히 예외적인 경우였다. 또한 창작의 행위가 가능했는데, 글로 적혀지면 기억할 필요가 없기 때문에 고리타분한 정형구적
표현을 벗어던지고 자신만의 생각을 글로 옮길 수 있게 되었기 때문이다.
이렇게 해서 씌여진 문서나 책은 공중과 집단의 것이 아닌, 개인이 창작해서 만들어낸 소유할 수 있는 물건의 성격을 띄게 된다. 이러한 과정을 거쳐서 자신의 물건에 대해서 소유권을 주장하듯이 자신이 만들어낸 글에 대해서 소유권을 주장하는 저작권의 개념이 만들어지게 된다.
이후에 글과 문서에 대한 저작권의 개념이 확고해지고 최근까지 안정적으로 유지된다. 이것은 인터넷시대에까지 계속 유지되게 된다.
왜 아시아는 저작권이 잘 지켜지지 않는가 ?
인터넷 컨텐츠에 대한 저작권이 특히 아시아지역에서 잘 지켜지지 않는 것에 대해서, 그들이 교육을 못받아서, 미개해서, 심지어
양심이 버려서라는 평가를 하기도 한다. 하지만 조금만더 돌이켜 생각해보면, 이것은 문화적차이 때문인거지 교육을 받지 못했거나
미개하기 때문인 것은 아니라는 걸 알 수 있다.
서양은 이른 시간에 쓰기를 내면화 시켰고 이에 기반해서 개인주의를 발전시켰다. 반면 동양은 여전히 개인적이라기 보다는 좀더
집단친화적이고 공동체의 가치를 중요시 하는 경향이 있다. 이러한 공동체의 가치를 중요시 하는 문화 즉 구술적문화에서는 컨텐츠를
개인의 것이 아닌 집단의 것으로 생각하는 경향이 강하게 남아 있다. 과거 쓰기가 내면화 되기 전의 사람들이 그랬던 것 처럼
말이다. 그래서 이들은 여전히 컨텐츠가 순전히 개인의 물건이라는 개념을 쉽게 받아들이지 못하는 것이다.
동양인들의 인식이 저급하기 때문이 아니라는 것이다. 물론 많은 사람들이 진보라는 것을
개인주의에 기반한 자본주의가 얼마나 제대로 뿌리를 내렸는가로 판단하려고 하기 때문에, 동양의 그러한 공동체적 마인드 자체를
저급하게 생각하는 경향이 있긴 하다. 그러나 동양적인 마인드와 서양적인 마인드는 서로 다른것이지
등급을 메길 수 있는 그런건 아니다. 유럽을 중심으로 하는 공동체생활방식의 등장, 자본주의에 대한 위기론 혹은 회의론들,
동양철학에 대한 재평가, 뿔뿌리 공동체라고 할 수 있는 오픈소스운동과 자유소프트웨어 운동 등에서 나타나듯이 말이다.
자유소프트웨어와 저작권
자유소프트웨어 공동체와와 오픈소스 공동체는 자본주의와 개인주의에 기반한 생각과 지혜의 물건화와 사유화에 반대한다. 이들은 생각, 아이디어는 함께 공유해야할 공동의 자산이라고 본다. 고대의 서사시처럼 말이다.
어떻게 보면, 자유소프트웨어와 오픈소스는
서양보다는 동양에 어울리는 운동이라고 볼 수 있다. 이러한 운동이 오히려 서양에서 먼저 시작되고 서양에서 뿌리를 내리고 있는
것은 모순이라고 볼 수 있을 것이다. 자본주의가 먼저 뿌리를 내렸고, 공동체정신이 거의 남아있지 않았기 때문에 오히려 그에 대한
반성으로 먼저 서양사회에서 생겨난게 아닐까 ?
이러한 반저작권 혹은 반자본주의적 운동이 소프트웨어영역에 쉽게 뿌리내린 이유는 인터넷이란 매체의 특징이 한 몫 한것으로 생각된다. internet은
애초에 관리주체나 중심이 없는 상태로 설계되었는데, 이는 자원과 노동력을 집중시키는 자본주의와는 많은 차이를 보인다. 예컨데
인터넷은 독점적인 권한을 가지는 개인 혹은 집단이 관리하는 사유재산이 아닌 인류공동의 재산이라는 인식이 초기에 생성이 되었다.
이러한 인식하에서 자유소프트웨어 재단이 있게한 해커문화가 생겨나게 되었고, 인터넷의 자본화에
맞서서 강력한 풀뿌리 공동체를 지켜내는 원동력이 되었다. 그 어떤 산업분야에서도 볼 수 없는 (독점적인 저작권에 기반하지
않은)자유로운 정보공유, 서로 연대하며 서로를 존중해주는 공동체 문화를 간직하고 있다. :::

아오.. 일단 포스팅 하고 보자..
 문서는 완성된게 아니며, 틀린 내용이 있을 수 있습니다. 수정해야 할 부분이 있음 알려주세요. 확인 후 반영하도록 하겠습니다.
1 Thread에 대해서
프로그램을 병렬로 실행시키는 방법으로 fork()에 대해서 알아보았다. fork()는 매우 이해하기 쉬운 프로그래밍 방법이긴 하지만 자원효율성에서 몇가지 문제점을 가지고 있다. 프로세스는 기본적으로 code, data, stack, file I/O, 그리고 signal table의 5가지 요소로 구성이 된다. fork()
를 이용해서 새로운 프로세스를 생성하게 되면, 이러한 5가지 구성요소가 모두 복사가 된다. 그러하다 보니 프로세스를 생성하는데
많은 비용이 소비될 것이다. 대게의 경우에는 프로세스를 새로 생성시킬때 발생하는 성능저하가 문제가 되지는 않겠지만 웹서비스처럼
대량의 접근이 발생하는 영역에서는 문제가 될 수 있다.
fork의 이러한 방식은 상당히 효율이 떨어지는 측면이 있다. 어떤 프로그램을 병렬로 실행시킨다고 했을 때, 실제 우리가 병렬로 실행되기를 원하는 영역은 코드의 일부분이지 프로그램 전체는 아니기 때문이다.
// ... pid = fork(); if (pid > 0) { // 실제는 이 부분의 코드만 병렬로 실행되면 된다. // fork()는 다른 모든영역의 코드가 복사되어 버린다. }
게다가 전혀다른 프로세스를 생성시킴으로써, 프로세스간 통신이라는 상당히 복잡한 문제까지를 해결해야 한다. 병렬로 작동하는
프로그램은 특성상 데이터를 공유하거나 서로 통신을 해야 하는 경우가 많다. 그런데 프로세스는 서로 독립된 객체이므로 일반적인
방법으로는 데이터를 공유할 수가 없다. 이러한 프로세스간 데이터 통신을 위해서 리눅스는 IPC라는 설비를 제공하는데, IPC라는게 사용하기가 여간 까다롭지가 않다. IPC에 대해서는 별도의 장을 할애해서 다룰 계획이다.
Thread를 이용하면 fork()를 이용한 프로세스 기반의 병렬처리의 문제점의 많은 부분을 해결할 수 있다. Thread는
새로운 프로세스를 생성시키지 않고, 특정 문맥(코드)만을 병렬로 실행할 수 있도록 허용한다. 새로운 프로세스를 생성시키지 않기
때문에 그만큼 자원을 아낄 수 있으며, 더 효율적으로 빠르게 움직일 수 있다. 또한 같은 프로세스이기 때문에, 데이터를
공유하기가 쉽다는 장점도 가진다.
1.1 Thread vs Process
Thread는 프로세스와 다음과 같은 차이점을 가진다.
- 프로세스는 독립적이다. 쓰레드는 프로세스의 서브셋이다.
- 프로세스는 각각 독립적인 자원을 가진다. 쓰레드는 stat, memory 기타 다른 자원들을 공유한다.
- 프로세스는 자신만의 주소영역을 가진다. 쓰레드는 주소영역을 공유한다.
- 프로세스는 IPC를 이용해서만 통신이 가능하다.
- 일반적으로 쓰레드의 문맥교환(context switching)는 프로세스의 문맥교환보다 빠르다.
1.2 Multi Thread 프로그램의 단점
모든 도구가 그러하듯이 Multi Thread 프로그램이라고 해서 장점만 가진 것은 아니다. Multi Thread 프로그램은 Multi Process 프로그래밍 방식에 비해서 다음과 같은 단점을 가진다.
- 하나의 쓰레드에서 발생된 문제가 전체 프로세스에 영향을 미친다.
멀티 프로세스의 경우에는 프로세스하나가 문제가 생기더라도 단일 프로세스로 문제가 제한된다. 그러나 멀티쓰레드 프로그램의 경우
하나의 쓰레드에 생긴 문제가 다른 쓰레드에까지 영향을 줄 수 있다. 예를 들어 쓰레드 하나가 다른 프로세스의 메모리 영역을
침범할 경우 프로세스 자체가 죽어버림으로써, 프로세스에 생성된 다른 모든 쓰레드도 프로세스와 함께 죽어버리게 된다. - 이
문제는 해결 가능하지만 여기에서는 다루지 않도록 하겠다. 시그널을 잘 활용하면 된다. 관심있으면 한번 고민해 보기 바란다. -
- 디버깅이 어렵다. 문맥이 서로 교환되므로 추적하기가 까다롭다.
이러한 단점이 있음에도 불구하고 멀티쓰레딩 프로그래밍 기법을 선호하고 있다.
1.3 PThread
Thread는 운영체제에서 제공하는 병렬처리 메커니즘으로, 실제 이 메커니즘을 이용하기 위해서는 Thread의 구현체가 필요하다.
리눅스에서는 pthread라는 thread 구현 라이브러리가 사용되고 있다. pthread는 POSIX thread 의 줄임말로 POSIX 표준을 따르고 있다. pthread는 리눅스 뿐만 아니라 다른 거의 대부분의 유닉스에서도 사용할 수 있다. 이외에도 BSD 계열에서 사용하는 ' Light Weight Kernel Threads , Apple 에서 사용하는 Multiprocessing Services등의 구현체가 있다. 이 문서는 pthread구현만을 설명하도록 할 것이다.
pthread는 리눅스 운영체제에서 제공하는 thread 를 제어하기 위한 함수들을 모아 놓은 C 라이브러리로, 다음과 같은 기능의 함수군을 제공한다.
1.4 Multi Thread 프로그램
병렬로 작동하지 않는 하나의 문맥흐름만을 가지는 프로그램을 단일 쓰레드 프로그램이라고 한다. 반대로 아래와 같이 문맥이 나뉘어서, 동시에 두개 이상의 쓰레드가 실행되면, 이를 멀티 쓰레드 프로그램'''이라고 한다.
1.5 Process, Kernel Thread, User Thread
프로세스는 가장 무거운 커널의 스케쥴링 단위이다. 프로세스는 운영체제에게 할당받은 자원들 - 파일 핸들러,소켓,장치 핸들러 - 을 할당받게 된다. 프로세스는 독립된 단위로써 파일이나 주소영역 등을 공유하지 않는다.
kernel thread는 가장 가벼운 커널 스케쥴링 단위다. 하나의 프로세스는 적어도 하나의
커널 쓰레드를 가지게 된다. 만약에 프로세스가 하나이상의 쓰레드를 가지고 있다면, 이들 쓰레드는 같은 메모리와 파일자원등을
공유하게 된다. 만약 커널의 프로세스 스케쥴러가 선점형이라면 쓰레드의 스케쥴러도 선점형인 경우가 많다. 참고삼아서 선점형과
비선점형에 대해서 간략하게 설명하도록 하겠다.
- 비선점형 : 특정 프로세스가 CPU를 독점하는 것이 가능하다.
- 선점형 : 특정 프로세스가 CPU를 독점하는게 불가능하다.
특정 프로세스가 CPU를 독점하는게 불가능하게 하는 것은 프로세스가 인터럽트를 무시하기 못하게 하는 것으로 구현한다. 선점형은
어떤 프로세스가 시스템콜을 수행중이더라도 커널이 인터럽트를 보내면, 즉시 빠져 나와야 한다. 즉 운영체제가 CPU를 선점한다는
얘기가 된다. 시스템콜이 수행중이더라도 인터럽트를 걸고 다른 일을 수행하도록 할 수 있으므로 보다 빠른 반응성을 보여준다.
때때로 쓰레드가 유저영역 라이브러리로 구현되는 경우가 있는데, 이를 user Thread 라고 부른다.
1.6 쓰레드의 생성과 종료
멀티 쓰레드 프로그램이라고 하더라도, 처음 시작되었을 때는 main()에서 시작되는 단일 쓰레드 상태로 작동이 된다. 이 상태에서 pthread_create(3) 함수를 호출함으로써, 새로운 쓰레드를 생성할 수 있다. pthread_create를 이용해서 생성된 새로운 쓰레드를 worker 쓰레드라고 하자.
멀티 쓰레드 프로그램은 다음과 같은 흐름을 가진다.
생성된 worker thread는 언젠가 종료가 될 것이다. Master Thread (이하 부모 쓰레드)는 pthread_join()을 이용해서 worker thread들의 종료를 기다린다. pthread_join()는 종료된 worker thread의 자원을 정리하는 일을 한다. fork()를 이용한 멀티 프로세스 프로그램에서, 부모 프로세스가 wait()를 이용해서 자식 프로세스를 기다리는 것과 같은 이유라고 보면 된다.
1.6.1 pthread_create : 쓰레드 생성
pthread_create(3)함수를 이용하면 새로운 쓰레드를 생성할 수 있다. 이 함수는 다음과 같이 사용할 수 있다.
#include <pthread.h> int pthread_create(pthread_t * thread, pthread_attr_t *attr, void * (*start_routine)(void *), void * arg);
- thread : 쓰레드가 성공적으로 생성되었을 때, 넘겨주는 쓰레드 식별 번호.
- attr : 쓰레드의 특성을 설정하기 위해서 사용한다. NULL일 경우 기본 특성
- start_routine : 쓰레드가 수행할 함수로 함수포인터를 넘겨준다.
- arg : 쓰레드 함수 start_routine를 실행시킬 때, 넘겨줄 인자
이 함수는 성공적으로 수행되었다면, 0을 리턴한다. 그렇지 않을 경우 1을 리턴한다.
1.6.2 pthread_join : 쓰레드 정리
쓰레드가 실행시키는 것은 함수 이다. 그러므로 return이나 exit(0)등을 이용해서 쓰레드를 종료시킬 수 있게 된다. 그러나 쓰레드 함수가 종료되었다고 해서 곧바로 쓰레드의 모든자원이 종료되지 않는다. fork()기반의 멀티프로세스 프로그램에서 종료된 자식프로세스를 정리하기 위해서 wait()로 기다리듯이, 종료된 쓰레드를 기다려서 정리를 해주어아만 한다. 그렇지 않을 경우 쓰레드의 자원이 되돌려지지 않아서 메모리 누수현상이 발생하게 된다.
pthread_create()로 생성시킨 쓰레드는 pthread_join()을 통해서 기다리면 된다. pthread_join 함수는 다음과 같이 사용할 수 있다.
#include <pthread.h> int pthread_join(pthread_t th, void **thread_return);
- th : pthread_create에 의해서 생성된, 식별번호 th를 가진 쓰레드를 기다리겠다는 얘기다.
- thread_return : 식별번호 th인 쓰레드의 종료시 리턴값이다.
pthread_join이 하는 일은 명확하다. 다만 주의 할것은 pthread_join은 반드시
joinable 한 상태로 생성된 쓰레드만을 기다릴 수 있다는 점이다. pthread_create로 쓰레드를 생성시킬 때,
나중에 join되지 않을 것으로 생각하고 생성시킬 수 있는데, 이렇게 되면 이 쓰레드는 종료하자마자 모든 자원을 해제하며,
pthread_join으로 기다릴 수가 없다. 부모쓰레드와 떨어져서 완전히 독립적으로 작용한다고 하여, 이를 detach 한다고 한다. 쓰레드를 detach하는 방법은 아래에서 다룰 것이다.
pthread_create와 pthread_join을 알고 있다면, 이제 thread를 생성시킬 수 있다.
#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> // 쓰레드 함수 void *t_function(void *data) { int id; int i = 0; id = *((int *)data); while(1) { printf("%d : %d\n", id, i); i++; sleep(1); } } int main() { pthread_t p_thread[2]; int thr_id; int status; int a = 1; int b = 2; // 쓰레드 생성 아규먼트로 1 을 넘긴다. thr_id = pthread_create(&p_thread[0], NULL, t_function, (void *)&a); if (thr_id < 0) { perror("thread create error : "); exit(0); } // 쓰레드 생성 아규먼트로 2 를 넘긴다. thr_id = pthread_create(&p_thread[1], NULL, t_function, (void *)&b); if (thr_id < 0) { perror("thread create error : "); exit(0); } // 쓰레드 종료를 기다린다. pthread_join(p_thread[0], (void **)&status); pthread_join(p_thread[1], (void **)&status); return 0; }
아주 전형적인 프로그램이긴 하지만 pthread_join부분에 문제가 있다. pthread_join은 쓰레드가 종료될 때까지 블럭되기 때문이다. 이래서는 쓰레드를 두개이상 생성시키지 못할 것이다. 그렇다고 pthread_join을 이용하지 않는다면, 메모리 누수가 생기게 되니, 생략할 수도 없는 노릇이다.
1.6.4 자식쓰레드를 부모쓰레드로 부터 분리하기
pthread_join의 사용으로 발생할 수 있는 문제점을 해결하기 위한, 가장 좋은 방법중의 하나는 pthread_detach
를 이용해서, 자식 쓰레드를 부모쓰레드와 완전히 분리해 버리는 방법이다. 이 경우 자식 쓰레드가 종료되면, 모든 자원이 즉시
반환된다. 반면, 자식 쓰레드의 종료상태를 알 수 없다는 문제가 발생한다. 대게의 경우 자식 쓰레드의 종료상태가 중요한 문제가
되지는 않을 것이다.
만약 자식 쓰레드의 종료상태를 알아내는게 중요하다면, 종료상태를 저장할 전역변수를 두고, 여기에 종료상태를 기록하는 방식을
사용할 수 있을 것이다. 자식 쓰레드가 종료할때, 변수의 값을 바꾸고, 부모쓰레드에 시그널을 전송하는 방법이다. 이 방법은 이
문서의 뒤에서 따로 다루도록 하겠다.
#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> // 쓰레드 함수 // 1초를 기다린후 아규먼트^2 을 리턴한다. void *t_function(void *data) { char a[100000]; int num = *((int *)data); printf("Thread Start\n"); sleep(5); printf("Thread end\n"); } int main() { pthread_t p_thread; int thr_id; int status; int a = 100; printf("Before Thread\n"); thr_id = pthread_create(&p_thread, NULL, t_function, (void *)&a); if (thr_id < 0) { perror("thread create error : "); exit(0); } // 식별번호 p_thread 를 가지는 쓰레드를 detach // 시켜준다. pthread_detach(p_thread); pause(); return 0; }
1.7 쓰레드 동기화
이제 우리는 간단한 다중쓰레드 프로그램을 만들 수 있게 되었다. 그러나 이들 쓰레드 생성 함수만 가지고는 복잡한 쓰레드 프로그램을 만들 수가 없다. 쓰레드간 동기화라고 하는 문제가 놓여있기 때문이다. 아주 간단한 프로그램이 아닌한은 반드시 동기화문제를 고민해야만한다.
동기화란 여러가지 의미로 사용될 수 있는데, 이 경우에 있어서 동기화는 서로의 시간을 맞춘다를
의미한다. 멀티쓰레드 프로그램은 하나의 시간에 여러개의 프로세스가 돌아가는 형태를 취한다. 또한 멀티쓰레드 프로그램은 자원의
상당부분을 서로 공유하는 경우가 많다. 만약 단지 자원을 읽어들이는 거라면 상관없지만 읽고/쓰는 것이라면 동기화와 관련된 문제가
발생할 수 있다.
예컨데 다음과 같은 경우다.
- A와 B 두개의 프로세스가 있다. 이 프로세스는 int count=1 이라는 자원을 공유한다.
- A가 count를 읽어들이고 1을 더한다.
- B가 count를 읽어들인다. 아직 A가 count에 쓰지 않았기 때문에, B도 1을 읽어들인다.
- A가 count에 2를 쓴다.
- B도 count에 2를 쓴다.
- count에는 2가 저장되었다.
우리가 원하는 값은 2가 아닌 3이다. 그러나 쓰레드가 동기화 되지 않음으로써, 원치않은 잘못된 연산을 하게 되었다. 우리는 이 문제를 해결해야 한다.
동기화 문제는 현실세계에서도 자주 발생한다. 화장실을 생각하면 된다. 화장실은 공유자원이며, 여러명이 사용한다. 누군가 화장실을 사용하고 있다면, 다른 사람은 화장실을 사용하면 안된다. 이 문제를 우리는 접근을 제어하는 방식으로 해결한다. 문을 걸어 잠궈서 한번에 한사람만 화장실에 들어가도록 하는 방법이다. 매우 이해하기 쉬운 방식이다.
다중쓰레드 프로그램에서도 마찬가지로 접근제어를 이용해서 이 문제를 해결한다. 이를 위해서 pthread는 mutex라는 잠금 메커니즘을 제공한다.
동시에 여러개의 쓰레드가 하나의 자원에 접근하려고 할때 발생하는 문제를 pthread는 임계영역을 두는 것으로 해결하고 있다. 임계영역안에는 접근하고자 하는 자원이 놓여있고, 오직 하나의 쓰레드만 임계영역안으로 진입할 수 있도록 제한한다. pthread는 이를 위해서 mutex를 제공한다. mutex는 그 자체가 가지는 잠금의 특성 때문에 mutex 잠금이라고 불리워지기도 한다.
위 그림은 mutex가 작동하는 방식을 보여준다. thread 1이 자원에 접근하면 mutex 잠금을 얻게 된다. 이 잠금은
단지 하나만 존재하기 때문에 thread 2는 잠금을 얻지 못하고 임계영역 밖에서 대기하게 된다. thread 1이 자원을 모두
사용하고 임계영역을 벗어나면 thread 2는 잠금을 얻게 되고 임계영역에 진입해서 자원을 사용할 수 있게 된다.
mutex를 사용하기 위해서는 다음의 4가지 함수가 필요하다.
- mutex 잠금객체을 만드는 함수
- mutex 잠금을 얻는 함수
- mutex 잠금을 되돌려주는 함수
- mutex 잠금객체를 제거하는 함수
1.8.3 pthread_mutex_init
mutex를 사용하기 위해서는 먼저 pthread_mutex_init() 함수를 이용해서, mutex 잠금 객체를 만들어줘야 한다.
pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr *attr);
이 함수는 두개의 인자를 필요로 한다.
- mutex : mutex 잠금객체
- mutex_attr : mutex는 fast, 'recursive, error checking의 3종류가 있다. 이 값을 이용해서 mutex 타입을 결정할 수 있다. NULL 일경우 기본값이 fast가 설정된다.
- fast : 하나의 쓰레드가 하나의 잠금만을 얻을 수 있는 일반적인 형태
- recursive : 잠금을 얻은 쓰레드가 다시 잠금을 얻을 수 있다. 이 경우 잠금에 대한 카운드가 증가하게 된다.
mutex_attr을 위해서 다음의 상수값이 예약되어 있다.
- fast : PTHREAD_MUTEX_INITIALIZER
- recursive : PTHREAD_RECURSIVE_MUTEX_INITIALIZER
- error checking : PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
1.8.4 pthread_mutex_lock
mutex 잠금을 얻기 위한 함수다.
int pthread_mutex_lock(pthread_mutex_t *mutex);
mutex 잠금을 얻는다라는 표현보다는 mutex 잠금을 요청한다라는 표현이 더 정확할 것 같다. 만약 mutex 잠금을 선점한 쓰레드가 있다면, 선점한 쓰레드가 mutex 잠금을 되돌려주기 전까지 이 코드에서 대기하게 된다.
때때로 잠금을 얻을 수 있는지만 체크하고 대기(블럭)되지 않은 상태로 다음 코드로 넘어가야할 필요가 있을 수 있을 것이다. 이 경우에는 아래의 함수를 사용하면 된다.
int pthread_mutex_trylock(pthread_mutex_t *mutex);
1.8.5 pthread_mutex_unlock
mutex 잠금을 되돌려주는 함수다.
int pthread_mutex_unlock(pthread_mutex_t *mutex);
1.8.6 mutex 잠금 예제
count 프로그램을 예제로 할 것이다. 임계영역안에서 보호되어야할 자원은 count이고, 여러개의 쓰레드가 count에 접근해서 +1을 시도하려고 한다. 이때 제대로된 count를 위해서는 한번에 하나의 쓰레드만이 counting을 하도록 해야할 것이다. mutex를 이용해서 임계영역을 보호하도록 할 것이다.
임계영역을 보호하지 않을 경우 다음과 같은 문제가 발생할 수도 있을 것을 예상할 수 있다.
int a = 1; Thread A 에서 a를 읽어들인다. Thread B 에서 a를 읽어들인다. Thread A 에서 a = a+1를 한다. { a = a+1; 결과는 2; } Thread B 에서 a++를 한다. { a = a + 1; // 읽어들인 값이 1이기 때문에 역시 결과는 2가 된다. }
두번의 count가 발생했기 때문에 3이되어야 하겠지만 임계영역이 보호되지 않음으로써 2가 되어 버렸다.
mutex는 임계영역을 잠금으로서 이러한 문제를 해결한다. 이러한 문제를 해결하기 위해서는 임계영역에 단지 하나의 쓰레드만 접근하는걸 보장해줘야 할 것이다. mutex는 아래의 요소들을 보장함으로써 이를 보장한다.
- Atomicity - mutex 잠금은 최소단위 연적 - atomic operation - 을 보장한다. atomic operation에 대해서 간단히 설명하고 넘어간다. 자세한 내용은
Atomic Operation을 참고하기 바란다.
- aotomic operation은 일련의 연산 즉 mutex 잠금 연산이 끝날때 까지 다른
프로세스가 그 연산의 변화를 알 수 없는 상태가 되는 연산을 의미한다. (일반적으로 연산은 이전의 연산의 결과를 관찰한 후에서야
이루어질 수 있게다)
- 전체연산중 하나라도 실패할 경우 모든 연산이 실패하며 시스템은 전체 연산이 시작하기 전의 상태로 복구된다.
- Singularity : 한 쓰레드가 뮤택스 잠금을 얻었다면, 이 쓰레드가 뮤택스 잠금을 내어놓기 전까지는 다른 쓰레드가 뮤택스 잠금을 얻을 수 없도록 한다.
- None Busy Wait : 이것은 성능과 관련된 것이다. 바쁜대기상태에 놓이지 않는다는 뜻이다. 뮤택스 잠금을 얻을 수 있는지를 확인하기 위한 연산이 필요하지 않는 다는 의미로 받아들이면 될 것이다.
이상 mutex는 위의 3가지를 지원하는 것으로 공유되는 자원을 충돌없이 그리고 효율적으로 사용할 수 있도록 보장해준다.
다음은 mutex를 사용한 count 예제프로그램이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #include <stdio.h> #include <unistd.h> #include <pthread.h> int ncount; // 쓰레드간 공유되는 자원 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 쓰레드 초기화 // 쓰레드 함 수 1 void* do_loop(void *data) { int i; pthread_mutex_lock(&mutex); // 잠금을 생성한다. for (i = 0; i < 10; i++) { printf("loop1 : %d", ncount); ncount ++; sleep(1); } pthread_mutex_unlock(&mutex); // 잠금을 해제한다. } // 쓰레드 함수 2 void* do_loop2(void *data) { int i; // 잠금을 얻으려고 하지만 do_loop 에서 이미 잠금을 // 얻었음으로 잠금이 해제될때까지 기다린다. pthread_mutex_lock(&mutex); // 잠금을 생성한다. for (i = 0; i < 10; i++) { printf("loop2 : %d", ncount); ncount ++; sleep(1); } pthread_mutex_unlock(&mutex); // 잠금을 해제한다. } int main() { int thr_id; pthread_t p_thread[2]; int status; int a = 1; ncount = 0; thr_id = pthread_create(&p_thread[0], NULL, do_loop, (void *)&a); sleep(1); thr_id = pthread_create(&p_thread[1], NULL, do_loop2, (void *)&a); pthread_join(p_thread[0], (void *) &status); pthread_join(p_thread[1], (void *) &status); status = pthread_mutex_destroy(&mutex); printf("code = %d", status); printf("programing is end"); return 0; } |
1.9 앞으로 할 것
쓰레드는 매우 광범위한 주제로 여기에서는 쓰레드를 사용하기 위한 가장 기본적인 내용만 다루었다. 쓰레드에 대한 좀더 자세한 내용은 별도의 장을 할애해서 다룰 생각이다.
:::

많은 개발자들이 회사일과는 별도로 오픈소스 프로젝트에 참가하려고 한다. 그리고 가능한 이름있는 프로젝트로까지 성장시키기를
원한다. 왜 일까 ? 돈을 벌 기회를 얻을 수 있다라거나 혹은 이름을 알릴 수 있는 기회를 엿볼 수 있다라는 등등의 이유가 있을
수도 있을 것이다. 그러나 그렇게 단정짓기에는 왠지 뒷통수가 근질근질 하다. 단지 그 이유 때문일까 ? 그렇다면 다른 산업분야에
비해서 왜 유독 소프트웨어 분야에서 그런 현상이 발생하는 것일까 ? 이렇게 말하면 오픈소스 공동체 혹은 자유소프트웨어 공동체가 잘 유지되고 있어서라고 대답할 수 있을 것이다. 그럴듯하다. 그렇다면 이렇게 되물어볼 수 있을 것이다. 왜 소프트웨어쪽은 그러한 풀뿌리 공동체 공동체가 살아있는 것일까. 다시 말하자면 왜 유독 놀만한 멍석이 잘 깔려져 있을까 라는 물음이다.
소프트웨어 산업에는 장인정신과 예술인의 중간의 의미를 가지고 있는 해커문화라
는게 남아 있다. 자신의 이름을 걸고 물건을 만드는 장인처럼 자신의 이름이 새겨진 소프트웨어를 만들어내기를 원한다. 혹은 자신의
이름이 아로새겨진 예술작품이 전시되는 것처럼 자신의 이름이 아로새겨진 소프트웨어가 전시 되기를 원한다. 다른 어떠한
산업분야에서도 이러한 모습을 발견할 수 없다. 아마 예술분야에서나 겨우 발견할 수 있을 것이다.
이렇게 멍석을 만들어 놓은 사람이 리차드 스톨만이다. 시대의 반항아. 똘아이?.
소프트웨어 계의 아방가르드, 공산주의자, 사회주의자, 근본주의자, 원리주의자, 순종주의자, 천재 프로그래머, 마지막 해커,
고집쟁이, 피리부는 아저씨, 사이비 교주 요러한 수식어가 따라다니는 사람이다.
이 사람이 어떻게해서 우리가 몸담고 있는 소프트웨어 바닥에 예술계에나 있을 법한 이러한 잘다듬어진 공동체, 해커 마인드를 만들게 되었는지, 소프트웨어 개발은 예술의 그것과 비슷하다라는 생각을 가지도록 했는지 즉 일종의 자존심을 가질 수 있는 문화의 기반을 닦았는지 생각해볼 필요가 있다. 이러한 현상은 예술계에서나 볼 수 있는 것이기 때문에 특이하며 때문에 생각해볼 가치가 있다.
우선 순수예술이라는 것에 대해서 좀 생각을 해봐야 겠다. 비록 소프트웨어 영역에서 처럼 성공적이진 못했지만 소프트웨어처럼 자본과
시장에 대항해서 그들 자신의 공동체를 지키고자 하는 운동들이 있어왔으며, 소프트웨어 보다는 더 오랜 역사를 가지고 있기 때문이다.
기실 예술이라는 것이 지금처럼 대접을 받게 된것은 그리 오래되지 않았다. 근대이전의 예술가는 독립적인 예술가가 아닌 교회 와 궁중의 후원을 받는 고용인에
지나지 않았다. 그러므로 자기가 만들고 싶은 것을 만들어낸다라는 지금의 예술가의 이미지와는 전혀달랐다. 후원을 받는 궁중과
교회에 봉사하는 고용인으로써 그들의 의뢰를 받아서 작품을 만들 따름이었으니 예술가라고 하기에는 부족함이 있었다. 실제 당시의
많은 작품들에 작가가 명시되어있지 않았던데에는 이러한 이유가 있었다. 레오나르도 다빈치, 바흐, 베토벤 정도되는 유명인 정도가
되어야 그나마 이름을 남길 수 있었으며, 이들 당대의 유명인 조차도 종속된 상태에서 벗어날 수가 없었다.
예술가가 예술가로써의 독립적인 지위와 명성 자존심을 회복 한것은 개인의 주체의 중요성이 부각된 르네상스 이후에서 부터이며,
이때부터 일반 대중은 예술에 대한 동경을 가지게 되고 그들을 존경하고 그들과 같이 되고자 하였다. 누구에게도 얽매이지 않고
자신이 만들고 싶어하는 것을 만들며 명성과 부를 누릴 수 있으며, 개인이 인간으로써 가져야할 자존감을 지켜나갈 수 있다!!
얼마나 매력적인가. 물론 이때의 예술가는 태어나면서 부터 자질을 타고난 천재라는 이미지가 강하긴 했지만 말이다.
자본이 지배하는 지금에서도 마찬가지일 것이다. 사람은 자본이나 회사 사회의 굴레에서 자본이나 혹은 고객을 위해서 무엇인가를
만들어내는 것보다는 자신이 자기를 위해서 무언가를 만들어내기를 원한다. 왜 예술가에 대한 동경을 가지고 있을까. 왜 자기
스스로가 자본가가 되길 원하는 것일까. 자기 자신을 위해서 무엇인가를 할 수 있기 때문이다. 자기가 자신을 위해서 무엇인가를 할
수 있다는 것은 자신의 삶을 자신이 책임질 수 있는 상태임을 얘기하는 것이다.
많은 노동자, 기술자들이 장신 혹은 예술가 처럼 그렇게 살기를 원한다. 정확히 말하자면 실용적 예술가라고 할 수 있는 장인이 되길 원한다. 그러나 실패했다. 소프트웨어 분야를 제외하고는... 다른 거의 모든 분야가 산업화되고 자본에 완전 종속되어 버렸다. 소프트웨어분야 처럼 해커문화, 장인문화, 풀뿌리 공동체 문화가 명백하게 남아 있는 분야는 없다.
소프트웨어를 완전히 산업에 종속시키지 않도록 만든데에는 역시 리차드 스톨만과 자유소프트웨어재단 그들의 이상에 따라주었던 도전적이고 열정적인 개발자들의 힘이 무엇보다 컸다고 할 수 있을 것이다. 적당히 자본과 타협하면서 적당히 우리의 자존심을 유지시켜줄 공동체를 지켜나가자라
고 안이하게 타협했던 다른 산업의 노동자와는 다른, 어찌 보면 정줄을 반쯤 놓은 사이비교주처럼 모든 상업, 독점, 자본, 시장,
상품에 일말의 타협도 없이 자신의 철학을 관철시킬려고 하는 이들이 있었기 때문에 어느 산업에서도 볼 수 없는 강력한 공동체를 만들 수 있었을 것이다.
많은 젊은이들이 소프트웨어영역에 대한 막연한 동경을 가지고 있는데에는 자유스러우며 반사회적이며 공동체지향적인 해커문화가 큰 역할을 하고 있다.
자유소프트웨어 재단과 오픈소스 진영의 껄끄러운 관계
많은 사람들이 자유소프트웨어운동과 오픈소스운동이 같은 것이라고 혼동한다. 때때로 오픈소스진영의 대부가 리처드 스톨만인 것으로 소개하는 문서들도 있다. 하지만 자유소프트웨어 재단과 오픈소스진영은 그닥 좋은 관계는 아니다.
2006년 리처드 스톨만이 한국을 방문해서 강연을 하는 와중에 자유소프트웨어가 아닌 오픈소스에 대한 질문을 하자 바로 차단신공을
발휘했다는 에피소드에서 보듯이, 일체의 어떠한 타협을 거부하는 자유소프트웨어 재단과 완고함은 비교적 자본과 시장과 유연한 관계를
보여주는 오픈소스진영은 (언뜻 이해되지 않기도 하겠지만)꽤나 껄끄러운 관계를 유지하고 있다.
아방가르드라고 할 수 있는 리처드 스톨만의 눈에 자본과 시장과 적당히 타협하는 모습을 보이는 오픈소스진영은 적과 손잡은 위험한
불순분자 정도로 보일지도 모르는 일이다. 아마도 분명히 그렇게 생각하고 있으리라고 생각한다. 그의 생각이 지나치게 완고하든지
간에 그의 그러한 행동은 오픈소스진영이 자본과 사회적 공동체 가운데에서 균형을 지킬 수 있도록 긴장감을 주는 것은 분명한 것 같다. 자본과 시장이라는 거대한 세력에 맞서서 자신들의 공동체를 지키려면 이러한 사람도 반드시 필요한 것이라는 생각이 든다.
산업화와 자본화의 거대한 물결속에서도 예술계와 소프트웨어계가 나름 이러한 공동체문화를 지켜나갈 수 있었던건 역량있는 이단아들이 활약해주었기 때문 아닐까. :::

대량 웹로그 데이터분석의 어려움
많은 통계정보들은 대게 시간축이 기준이 되는 경우가 많다. 특히 웹서비스의 경우가 더욱 그렇다. 웹서버스의 경우 웹로그를 이용해서 통계정보를 내는 경우가 많은데, 이때 문제가 되는 것은 로그의 양이 될 것이다.
로그의 양이 그리 많지 않다면야 RDBMS에
그냥 insert 한다음에 주기적으로 select해서 통계 데이터를 만들면 되겠지만, 하루에 쌓이는 로그의 양이 기가바이트쯤
된다고 하면, 로그를 저장하고 꺼내는 것 자체가 문제가 된다. 분석은 더 말할 필요도 없을 것이다. 로그 분석을 위한 데이터
마이닝 기법들은 그 자체가 하나의 연구분야이기도 하다.
혹시라도 빠른 분석정보의 출력을 원한다고 하면, 해야할 일이 보통 많은게 아니다. 임의의 페이지에 대한 1년간의 방문추이정보,
임의의 유저의 1년동안의 활동정보, 검색어의 사용추이 등의 통계정보를 만들어야 한다고 생각해보라. 경험많은 개발자라면, 방법이야
생각해낼 수 있겠지만, 매우 복잡하고, 많은 시스템자원과 시간이 소비되는 일일 거라는 건 미루어 짐작할 수 있을 것이다.
RRD의 사용
RRD(Round Robin Database)는 다양한 범위의 시간을 축으로 가지는 (한정된 크기를 가진)환영큐로 이루어진 형식의 데이터 베이스다.
예를들어서 PV에 대한 통계정보를 만드는 RRD는 다음과 같이 구성될 것이다.
- 5분간의 PV를 가지는 한시간짜리 테이블 : 단지 12개의 저장공간만 가지고 있으면 된다.
- 만약 12개의 공간을 다채우게 되면, 12개의 평균을 낸다음.. 1시간짜리의 평균데이터를 가지는 RRD 테이블에 값을
채워 넣는다. 이제 다시, 테이블의 처음으로 가서 5분 데이터를 써넣기 시작한다. 즉 테이블의 크기는 12개로 제한된다.
말그대로 테이블 형 테이터베이스
- 한시간이 최소간격이 되는 이 테이블을 24의 크기를 가지게 하면, 하루의 평균데이터를 가지게 될 것이다.
대략 다음과 같은 형식이다.
최소한의 크기로 1시간 평균, 하루 평균, 한달의 평균의 통계데이터를 유지할 수 있음을 알 수 있다. 시간간격을 다르게 한다면, 하루, 일주, 월, 분기, 년 등 다양한 주기의 통계정보를 생성할 수 있다.
물론 RRD 방식의 테이블이 가지는 단점도 있다. Round Robin방식을 취하게 됨으로써, 원본데이터가 손실되며, 연산이된 통계정보만 쌓인다는 점이다.
하지만 이정도로도 거의 대부분의 경우에 충분한 수준의 정보를 제공해 준다.
BitTable의 사용
BigTable은 고려대상이 아니다. BigTable은 거대한 저장공간영역을 준비해서 모든 로그를 저장하고, 강력한 (분산 컴퓨터의)연산능력을 이용해서 데이터를 분석하겠다. 라는 취지에서 만들어진 데이터 저장 방식이다. 이 문서는 간단하고 필요한 정보를 얻을 수 있는 실용적로그 분석시스템의 제안을 목적으로 하고 있다. 이 목적에서 봤을 때, Bigtable은 오버 테크놀러지다.
RRD 툴의 이용
이제 위에서와 같이 테이블이 꽉차면, 자동으로 필요한 통계연산을 한다음에 다음 테이블에 적도록 하는 애플리케이션을 만들면 된다.
아주 복잡한 애플리케이션이라고 할 수는 없겠지만, 상당히 귀찮은 일임에 틀림 없다. 그렇지만 걱정할 것 없다. 이미 그러한
구현이 있다. 이름도 RRD 이다.
이 RRD툴을 이용하면 간단하게, RRD 테이블을 구성할 수 있다. 뿐만 아니라 그래프까지 그려준다.
RRD를 이용한 페이지별 통계정보
현실적인 웹로그 분석의 예를 들어보자. 만약 페이지뷰의 통계정보를 제품별로 만들어 내고 싶다면 어떻게 해야 할까. ? 가장
간단한 방법은 제품의 갯수만큼 RRD테이블을 만드는 것이다. 100만개면 100만개의 파일을 만들면 된다.
100만개라고 하면, 굉장히 많은 양 같은데, 어느 정도의 크기를 가질지 계산해보자.
- 하나당 5k의 크기를 가진다고 가정한다.
테이블구성에 따라 다르겠지만, 대략 5k라면, 일,주,월,년,5년 의 평균데이터를 저장할 수 있다. 최소 시간간격을 1시간으로 하는 등의 방법으로, 최적화 시킨다면 3k미만으로도 할 수 있다.
- 5k * 100만 하면 대략 4Gbyte 정도가 된다.
고작 5Gbyte로 100만개의 상품의 통계정보를 저장할 수 있다. 거기다가 이미 통계된 정보가 전부 들어가 있기 때문에, 매우 빠른시간 (0.수초내)에 원하는 정보를 얻어올 수 있다.
천만이라고 해도 40G밖에 안된다.
유저 & 페이지 통계 정보
단 유저별로 페이지 통계정보 혹은 검색어통계정보등을 만들려고 한다면, 문제가 약간 달라진다. 100만 유저이고 만들어질 수 있는
페이지가 10만개라고 하고, 모든 정보를 RRD로 남길려고 한다면, 280000 G의 공간이 필요하다.
이 경우에는 TOPN개의 정보만 RRD 테이블로 구성할 필요가 있다. 검색어를 예로 한다면 다음과 같다.
- 하루동안의 TOPN의 검색어를 남긴다. 이 테이블의 크기는 7이다.
- 테이블의 크기 7을 다채우면, TOPN에 대한 TOPN을 만들어서 일주일치 데이터를 만든다.
- 일주일치의 TOPN을 저장하는 테이블의 크기는 8개다. 즉 두달동안 각 주의 TOPN 검색어의 통계정보를 저장할 수 있다.
이렇게 할때 필요한 디스크 공간을 대략 계산해 봤다. 시간간격을 어떻게 둘것인지와 TOPN의 크기를 어느정도로 할 것인지에 따라 다를 것이다. 여기에서는 TOPN의 크기를 10으로 잡아서 계산을 했다. 검색어의 크기를 20byte로 하고, 하루, 일주일, 한달, 일년, 5년의 정보를 남긴다고 하면..
필드 하나의 크기 : 20 * 10 = 200 (7*200)+(4*200)+(12*200)+(5*200) = 5600
약 6K로 유저의 5년치의 TOP10 검색어 정보를 남길 수 있다. 100만유저가 남긴다고 하면, 5G, 1000만 유저라고 해도 50G 정도면 된다.
물론 데이터의 손실이 필연적으로 발생한다는 TOPN 통계방식의 문제점이 있기는 하다. 그러나 유저활동의 대부분이 롱테일구조, 즉 상위 10%가 전체활동의 90%를 차지한다고 볼 수 있으니 문제될게 없다고 본다. 데이터를 몽땅남길 수도 있겠지만, 내상각에 이건 ' 90%의 완성도에서 1%의 완성도를 높일려고, 90%를 만들기 위해서 들였던 비용의 수백배를 소비하는 그닥 가치없어 보이는 행위로 보이기 때문이다.
구글의 웹사용 서비스
구글 서비스 중에 웹사용이란 서비스가 있다. 개인의 검색어와 검새어를 통해서 방문한 페이지의 대한 통계정보를 보여주는 서비인데, RRD 스타일의 데이터베이스 환경을 구축한걸 알 수 있다. 관심있으면 한번 사용해 보기 바란다.
얻을 수 있는 장점
단점은 대략 알고 있을테니 넘어가고.
- 빠르다.
유저별로 100만개의 테이블을 만들었다고 가정해도, 0.1초내에 데이터,를 읽어 올 수 있다는 걸 보장할 수 있을 것이다.
- 작은 용량
위에서 계산했던 것처럼, 작은 용량으로 충분한 효과를 얻을 수 있다.
- 관리성
사이즈가 작으니 관리하기도 편하다. 간단하게 그룹화, 나누기, 백업 작업을 할 수 있다.
실제 구현 테스트는 다음번 문서에서 :::

Abstract Factory pattern
Abstract Factory pattern 은 Factory pattern를 추상화 시킨 패턴이다. 즉 구체적인 클래스의 정의를 하지 않고, 인터페이스만 제공하게 함으로써 실제 구현은 상속된 클래스를 통해서 이루어지게 한다.
추상화 시키는 이유는 concrete Create의 종류가 많아지게 될 경우 애플리케이션의 코드 수정이 불가피해 질 수 있다는 Factory pattern의 단점을 보완하기 위함이다.
구조
예제 1
#include <iostream> using namespace std; #define DEFAULT 0 #define GET 1 #define PUT 2 /* Interface */ class Handler { public: virtual void execute(char *instr)=0; virtual int getID()=0; }; /* Factory Class */ class HandlerFactory { public: Handler *instance(){} }; class GetHandler : public Handler { public : void execute(char *str) { cout << "Get Handler : " << str << endl; } int getID(){return GET;} }; class PutHandler : public Handler { public : void execute(char *str) { cout << "Put Handler : " << str << endl; } int getID(){return PUT;} }; class DefaultHandler : public Handler { public : void execute(char *str) { cout << "DefaultHandler : " << str << endl; } int getID(){return DEFAULT;} }; class GetDataHandler : public HandlerFactory { public : Handler * instance(int type) { if (type == GET) return new GetHandler; if (type == PUT) return new PutHandler; return new DefaultHandler; } }; int main() { GetDataHandler *dh = new GetDataHandler; Handler *mh = dh->instance(PUT); mh->execute((char *)"ok"); cout << "ID : " << mh->getID() << endl; }
예제 2
#include <iostream> using namespace std; class Lock { public: virtual void lock()=0; virtual void unlock()=0; }; class LockFactory { public: Lock *getInstance(int type); }; class FileLock : public Lock { public: void lock(){cout << "File Record LOCK " << endl;} void unlock(){cout << "File Record UN Lock " << endl;} }; class MutexLock : public Lock { public: void lock(){cout << "Mutex LOCK " << endl;} void unlock(){cout << "Mutex UN Lock " << endl;} }; class DefaultLock : public Lock { public: void lock(){cout << "LOCK " << endl;} void unlock(){cout << "UN Lock " << endl;} }; class GetLockHandler : public LockFactory { public: Lock *gethandler(int type) { if (type == 1) { return new MutexLock; } if (type == 2) { return new FileLock; } return new DefaultLock; } }; int main(int argc, char ** argv) { GetLockHandler *LockHandler = new GetLockHandler; Lock *myLock = LockHandler->gethandler(2); myLock->lock(); myLock->unlock(); return 0; }
:::

음 글쎄요. 아주 간단한 패턴이라서, pattern이라는 이름을 붙이기가 좀 뭐하긴 하지만 굳이 bus라는 이름을 붙이기로 했습니다. 이름하여 bus pattern.
누구나 알만한 구조에, 이름을 붙인 이유는 기억해내기 쉽고, 다른 사람과 아이디어를 공유하기가 수월할 것 같아서 때문입니다. 이름이 하는 역할이기도 하구요. 그리고 있어보이기도 하잖아요 ?
유래
이 패턴을 체계적으로 정리해보려고 생각하게 된건, moniwiki를 다루면서 부터입니다. moniwiki는 지금의 대다수의 wiki구현들이 그러하듯이, 모듈적재방식의 개발환경을 제공하고 있습니다.
moniwiki의 모듈을 개발하면서, 이러한 개발방식을 잘 체계화하면, 매우 훌륭한 웹서비스 개발환경을 만들 수 있을 거라고 생각했습니다. 거기에, 웹서비스쪽을 보아하니 MVC모델이라는게 있더군요. 있더군요 ? 그걸 이제야 알았냐고 하신다면, 사실 그렇습니다. MVC가
아마도 사용자 애플리케이션제작에서 모델과 뷰를 분리시키기 위한 개발방법론인걸로 알고 있는데, 사용자 애플리케이션을 만들어 본
경험이 전혀 없었거든요. 웹서비스에도 꽤 오래전부터 적용되어 왔었지만, 웹은 지금있는 joinc 사이트를 유지하는 정도로만
해왔으니, 역시 MVC를 이해할 필요도 없었구요.
해서, 대충 MVC 모델을 훑어보게 되어고, 이왕이면 (비록 완전하지는 못하다고는 하더라도) MVC모델을 따르도록 해봐야 겠다라고 생각하게 되었습니다.
bus 구조
bus 구조는 이미 잘 알려져 있습니다. 주로 computer 하드웨어에서, 하드웨어를 구성하는 각종 기기를 연결시키기 위한 구조로 사용됩니다. 각종 기기가 선로에 메달리는 구조가 ' bus손잡이 구조와 비슷하다고 해서, bus구조라고 이름짓게 되었습니다. 이 선로는 데이터 흐름의 경로가 될겁니다. 이제, 필요한 기기들을 bus구조에 메달리게 하는 정도로 하드웨어와 결합을 시킬 수가 있됩니다. 기기들의 컴포넌트화를 이루게 되는 거죠.
bus방식은 많은 기기들이 붙을 경우, 한정된 선로에 많은 데이터가 흐름으로써, 병목현상이 나타난
다는 문제를 가지게 됩니다. 하지만 그럼에도 불구하고, 기기의 컴포넌트화가 용이하기 때문에, 지금까지도 주요한 컴퓨터 하드웨어
구조로 사용되고 있습니다. 마땅한 다른대안이 있는지는 하드웨어 전문가가 아니라서 잘은 모르겠습니다.
웹서비스에서의 버스 구조
애초에 bus가 컴포넌트화 - 모듈화 -를 잘 지원하는 구조이다 보니, moniwiki와 같은 모듈기반의 웹서비스 개발환경을
만드는데에도 유용한 구조가 될 수 있을 거라고 생각했습니다. 그래서 다음과 같은 구조를 가지는 개발환경을 만들기로 했습니다.
bus구조를 만들어서 모듈이 달라붙게 만든거죠. 2개의 선로를 준비했는데, 하나는 전역 데이터가 흘러갈 선로이고 다른 하나는 각 모듈과 클라이언트가 생성한 데이터가 흘러갈 선로입니다.
이제 이들 선로를 제어할 수 있는 controller가 필요하게 되겠죠. 그래서 controller를 만들어서 붙였습니다.
거기에 MVC모델을 적용한다고 했던 기억이 나는군요. C는 이미 구현이 되어있습니다. controller가 있으니까요. 이 controller는 C가 하는 데이터의 입출력제어 역할을 하게 됩니다. 데이터를 module로 전달하고, model과 view의 리턴값을 받아서, 다른 model로 넘기거나 클라이언트에게 보내는게 주 역할입니다. 이렇게 하고 보니, MV까지 구현이 되는군요. 전체적으로 MVC 모델이 완성이 되었습니다. V를 완전하게 구현하기 위해서는 템플릿을 쓰네 XSLT를 쓰네 하는등의 이슈가 있지만, 모양새는 갖추게 되었습니다.
이론상으로 볼적에, 이 bus구조를 따르도록 환경을 만든다면, 완전한 컴포넌트기반의 웹서비스 개발 환경을 충분히 만들 수 있을 지도 모른다는 생각을 할 수도 있겠죠 ? 구조는 그럴듯 하니까요 !?
DMF 프로젝트
그래서, 이걸 현실화 하기 위해서 PHP기
반의 프레임워크를 하나 만들어 보기로 했습니다. 흠.. 이미 만들어진 다른 웹 개발 프레임워크를 경험해보지 않은 상태에서 왠지
허접스러운 결과물이 나오지 않을까 하는 걱정이 들긴하더군요. 그럼에도 불구하고, 잠시 재미를 주는 정도는 되겠지라는 생각과
실패도 도움이 될 것이다라는 생각, 만들다 보면 다른 프레임워크들도 보게 되겠지라는 안일한 생각하에 일단 진행해보기로 했습니다.
얼추 뼈대는 만든것 같고, 이제 ajax 라이브러리 주워다가 대충 붙이고, 전체적으로 다음으면 뭔가 나올것 같다는 안일한 생각이 듭니다.
프로젝트 홈페이지는 여기입니다. 괜찮할것 같은가요 ? 프로젝트 홈페이지는 물론 DMF기반하에서 만들어지고 있습니다. :::

1 웹개발을 위한 프레임워크
빠르고 쉬운개발, 코드의 재사용, 원할한 커뮤니케이션 을 보장하는 다양한 프레임워크가 있는걸로 알고 있다. 뒷 끝을 흐리는 이유는, 그렇다더라 라고 얘기만 들어봤지 실제 RoR이라든지, Django라든지 하는 프레임워크를 사용해 본적은 없기 때문이다.
회사에서 프로젝트 단위로 웹서비스를 제작해본적이 없으니, 굳이 프레임워크를 사용할 필요성을 느끼지 못했기 때문이다. 그냥 APM으로 만족하고 살아왔다.
여하튼 그러하다가. 팀단위로 웹서비스를 제작할려고 하니, 개발자와 개발자, 개발자와 디자이너 같의 협업방법, 유지보수, 좀더
쉬운 개발방법등에 대해서 생각을 할 수 밖에 없게 되었다. 그래서 생각한게 프레임워크의 도입. 프레임워크의 도입목적은 개발의
속도, 편의성이 아닌 협업, 커뮤니케이션, 유지보수에 촛점을 맞추기로 했다. 음.. 협업,커뮤니케이션, 유지보수성을 만족하면 개발의 속도와 편의성까지 보장되는건가 ??
그렇다고 해서, 기존에 만들어진 프레임워크 수준으로 만들지는 않을 것이다. 많은 기능을 가진 프레임워크라기 보다는 협업을 위한 공통인터페이스, 공통 작업환경을 제공하는 정도가 될 것이다.
2 환경
Apache + PHP 기반으로 만들 생각이다. PHP로 하는 이유는 (언제나 그렇지만)익숙하기 때문이고, moniwiki의 플러그인 스타일을 따라서 구현하기 위함이다. 그 moniwiki가 Apache, PHP로 구현되어 있기도 하고.
3 기본컨셉
module를 포함한 page단위로 컨텐츠가 만들어지는 wiki와 동일한 컨셉이다. 다른점은 모듈을 MV를 따르게 하고, 별도의 Controller페이지를 둔다는 정도. 다음과 같은 모델및 아키텍처를 도입할 계획이다.
- 컴포넌트 Model
Module 단위로 개발된다. Page는 Module들의 집합이 된다.
- Message driven Architecture
Controll, Module는 Message를 이용해서, 데이터를 전달 받는다.
- 전술패턴
알고리즘(모듈)의 풀을 유지하고, 이름을 이용해서 모듈을 실행시킨다.
- 가볍게 : 가능한 개발자에게 많은 권한을 준다.
4 나름 대충 설계
몇개 공개된 웹프레임워크를 간단히 살펴봤는데, 다들 MVC라는 패턴을 사용하고 있다. 대략 model, view, controller을 분리하도록 하는 pattern이라고 한다. 이해는 가는데, 자세히는 모르겠고 그냥 비슷하게 대략 구현해볼까 한다. 결국 잘 분리하자라는 말이니까 !? 대략 구현으로 방향을 잡은 이유는 다음과 같다.
- 범용성을 강조한 나머지, 너무 무거운 경우가 많다. 대부분의 프레임워크에 대한 평가도 그렇다.
- 개발자에게 더 많은 권한을 주는 방향이 나을 것 같다. 대부분의 프레임워크가 개발자의 권한을 지나치게 제한한다는 느낌을
준다. 인터페이스정도만 따르도록 하고, 실제 개발은 각 개발자의 개성이 가능한 많이 반영 될 것이다. 개발자에게 많은 권한을
주는게 무슨 프레임워크냐고 할 수도 있겠다. 그냥 철학의 차이로 봐줬음 한다.
- Client의 모든 요청은 Controller를 통해서 이루어진다. 호출될 서비스는 URL String 형태로 Controller 페이지에 전달된다.
- Controller는 요청된 서비스페이지를 읽어들이고 해석한다. 만약 [[module]]를 만나면, module의 인스턴스를 만들고, model메서드와 view메서드를 실행한다.
- Module를 실행할때, DB Connection, Session 정보, 기타 유저정보등을 넘겨주게 된다.
moniwiki의 플러그인 사용 구조를 따라가려고 한다. 전형적인 전술패턴을 가지게 될 것 같다. 즉
- 모든 모듈은 서로 완전히 독립된다.
- 각 페이지들은 이들 모듈의 집합이다.
- Module는 3개의 인자를 가지며, MV의 구현을 가진다.
| formatter | 기본 설정값 |
| options | POST,PUT 등 유저입력 데이터, 필요에 따라 설정된 값들 |
| Value | 함수 인자 |
- 모듈은 하나의 파일에 대응되며, 다음의 MV 작업을 수행할 수 있어야 한다.
| Model | 데이터 프로세싱 |
| View | 프로세싱된 데이터를 보여준다. |
- Controller는 controller page에서 작동하며, page를 해석해서 모듈을 실행시키고, 결과를 client에 전달한다.
| Controller | 입력을 받아들여서 Model을 호출하고 결과를 리턴한다. |
예를들어서 bbs 모듈과 bbs 페이지를 네비게이션하기 위한 navi 모듈을 만들었다고 하면, 이 모듈들을 포함한 페이지는 다음과 같이 구성될 것이다.
<table><tr><td> [[bbs]] </td><td> [[navi]] </td></tr></table>
여기에서 navi모
듈은 bbs모듈의 model로 부터 total page, current page와 같은 정보를 받아와야 된다. navi에서 직접
DB에 연결해서 문제를 해결할 수도 있겠지만, 효율적이지 않을 것이다. 이 문제는 bbs.model의 return값을
controller가 받아서 navi 클래스에 options인자로 넘기는 것으로 해결한다.
추가될 기능들이다. 지금은 말그대로 프레임만 있는 프레임워크인데, 기능들 추가하면 프레임워크로써의 모습을 가져가게 되지 않을까 싶다는 ?
- session 관리 : 이건 기본으로 들어가야 겠지 ?
- DB Interface : 복잡하고 커다랗게 만들고 싶지 않다. 일단 facade 클래스를 만들어서 한단계만 추상화 하는 정도로 만족하려고 한다.
- Editor : page를 수정하거나 추가할 수 있도록 한다. 추가나 수정뒤에 바로 테스트 해볼 수 있다.
이러한 모든 기능들은 controller의 수정이 아닌 module를 추가하는 방식으로 이루어지게 할 것이다.
4.1 model class
협업을 위해서는 interface를 두는게 권장되겠으나 그냥 module class로 부터 상속받아서 구현하는 걸로 할 생각이다. 어디까지나 뼈대코드일 뿐이란걸 염두에 두고 읽어주길 바란다.
class Module { var $arg; var $modelarg; var $formatter; var $options; var $rtv; var $className = 'myTest'; var $isrtv=0; var $values; function Module($formatter, $options, $values="") { $this->formatter = $formatter; $this->options = $options; $this->values = $values; echo "Create class !!<br>"; } function className() { return $this->className; } // model 구현 // view로 넘어갈 데이터는 viewData를 이용해서 넘긴다. function model() { echo "This is Model method<br>"; } function view() { echo "This is View method<br>"; } }
각각의 module 는 위의 Module 클래스를 상속받아서 구현이 된다.
- formatter과 options을 이용해서 메시지를 교환하게 된다.
- formatter과 options는 성능을 위해서, 복사대신 레퍼런스를 사용할 수 있다.
4.2 Controller
controller는 다음과 같은 정보들을 관리한다.
- User Query를 분석해서, 각 모듈에 넘겨준다.
- cookie, DB connection을 관리한다.
- 설정값을 모듈에 넘겨준다.
- 모듈의 리턴값을 다른 모듈로 넘겨준다.
controller는 다음의 흐름을 가진다.
- preLoader
페이지를 읽기전에 실행된다. session, db connection, user 환경설정 값등을 처리한다. 처리된 값은 message 형태로 controller로 넘겨진다.
- moduleLoader
페이지를 읽기 시작하고 module의 model과 view를 실행한다.
- postLoader
페이지를 읽고 난후의 작업들이다. logging, 에러처리와 같은 작업을 수행할 것이다.
preLoader, postLoader 모두 MV를 따르는 모듈형식으로 작성이 된다. controller에서 preLoader
클래스의 인스턴스를 생성하면, model을 실행시키고, view를 실행시키는 방식이다. view는 주로 header 정보를 출력하게 될 것이다.
Loader를 3개로 분리함으로써, 모듈개발자는 controller의 수정에 신경을 쓸 필요가 없게 된다. 일반적으로
controller의 수정은 페이지혹은 서비스 전역적인 데이터의 처리와 전달이 필요할 때 발생하게 되는데, preLoader과
postLoader에 model과 view를 수정하는 것으로 controller를 수정할 필요가 없기 때문이다. 예를 들어
session 관리 기능이 필요하다고 하면, controller 수정이 아닌 preLoader에 model을 추가하면 된다.
5 문제들
5.1 데이터 교환
문제는 Model에서 처리된 데이터를 view로 전달하는 방식이다. 디자이너와 협력해야 하는 부분이니 이래저래 신경을 써야 할 부분이 꽤 된다.
- XSLT : 쓸데없이 무거워지지 않을까라는 생각 때문에 망설여진다. 디자이너도 이것저것 알아야 하는 것 같고.
5.2 모듈사이의 데이터 교환
model의 리턴값은 controller이 관리를 한다. 각 model은 리턴값을 넘길 때, 이름을 명시해야 한다. 즉 다음과 같을 것이다.
function model() { $rtv = array(); // ..... $rtv['username'] = $username; $rtv['opner'] = $opner; return $rtv; }
controller는 다음과 같이 리턴값을 관리한다.
class controller { var $options; // .... $this->options['RTV']=Module->model(); }
controller는 Module 클래스를 생성시킬때, $options를 인자로 넘겨주므로, 각 Module은 다음과 같이 원하는 다른 모듈의 리턴값을 읽어올 수 있다.
class ModuleB extents Module { //... function model() { $username = $this->options['RTV']['username']; $opner = $this->options['RTV']['opner']; } };
이론적으로는 DB connection, file handler 는 물론이고 클래스도 문제없이 전달할 수 있다.
모듈에 setTitle 메서드를 두는 것을 생각해 볼 수 있을 것이다. 이경우 하나의 모듈을 여러 페이지에서 재활용 할때 문제가
발생할 수 있다. 2개 이상의 모듈에서 getTitle를 호출할 수 있기 때문이다. 이 문제는 controller에서 페이지를
전처리 하는 것으로 해결하기로 했다.
#!title modulename 코드들... ... [[modulename]] [[modulename2]] [[modulename3]]
즉 page가 위와 같다면, controller는 modulename->getTitle를 호출한다. title설정은 javascript를 이용한다.
5.4 개인화 전략
개인화 전략이라 함은 결국 session 관리가 될 것인데, session관리등은 preLoader에서 처리하게 된다.
preLoader에서 session 관련 정보를 리턴하며, controller에서 리턴값을 받아서 formatter에 저장하는
방식이다. 예컨데 다음과 같다.
class preLoader { function model() { $usersession = new session(); // 이런 저런 유저 세션 작업 // 유저 theme, css, 홈디렉토리, 문자셋 기타등등의 정보들 $rtv['formatter']['user'] = $usersession; // DB Connection을 넘겨야 할 필요가 있다면. $rtv['formatter']['db'] = new DBI(); // 필요할 경우 header 정보를 생성한다. $this->viewData = $header; return $rtv; } function view() { return $this->viewData; } } controller: $rtv = preLoader->model(); $rtv의 key 이름이 formatter 임을 알 수 있으므로 아래와 같이 formatter를 설정한다. $this->formatter['user'] = $rtv['formatter']; module: 이제 다른 모듈들에서는 다음과 같이 user session 정보를 사용할 수 있다. $user = $this->formatter['user']; $user->isLogin(); // 로그인 체크 $user->getUserID(); // 사용자 ID .... // 기타등등등의 활용. // db 관련작업을 해야 한다면.. $dbi = $this->formatter['db']; $dbi->query($sql); $dbi->fetch(); .... // 기타등등등
5.5 서로 다른 view가 필요할 경우
model은 하나이지만 view는 페이지에 따라 달라질 수 있다. 이 문제는 view 메서드의 이름을 달리하는 걸로 해결하려고
한다. view_pagename 으로 해서 여러개의 메서드를 만들어서, 페이지에 맞는 view를 선택하도록 한다.
예를들어 page 이름이 userInfo일 경우에 다른 view를 보여주고 싶다면
$cp = new $className($formatter, $this->options); $viewPage = 'view_userInfo'; $cp->$viewPage(); 가 호출이 된다.
혹은 view 내부에서 pagename을 가지고 분기하도록 해도 될 것이다. view메서드의 이름을 달리하는 것보다는 pagename을 가지고 분기하도록 하는게 더 나을것 같기도 하다.
6 디렉토리 구성
--- controll.php --+-- data -+-- text | | | +-- cache | +-- config -+-- config.ini | +-- theme -+-- default -+-- _user.css | | | +-- blog | +-- plugin
7 테스트 페이지
- http://wowclub.joinc.co.kr/controll.php 에서 테스트결과와 모듈의 소스코드를 확인할 수 있다.
- controller는 POST 액션에 대해서 do method를 실행시킬 수 있어야 한다.
이것은 Module에 do 메서드를 추가함으로써 가능할 것이다. 그렇다면 POST액션이 일어날 경우에는
Module->model 대신에 Module->do를 실행시키게 된다. 만약 do 메서드가 존재하지 않는다면,
model을 그대로 실행하도록 하면 된다. 로그인 같은 모듈이라면 do 메서드가 존재할 것이고, 게시판 같은 경우라면 do
메서드가 필요 없을 것이다.
7.1 Ajax 가 도입될 경우에는 ?
Ajax 프레임워크를 만들고, model에서 호출하는 것으로 해결할 수 있다.
7.2 이 프레임워크?의 장점
wiki의 모듈방식을 사용했을 경우 얻을 수 있는 장점이다.
- Page단위로 개발됨으로, 개발/테스트/배포가 용이하다.
- 재활용이 쉬운 Module을 유지할 수 있다.
- Module간의 결합도가 매우 낮다. 즉 유지/보수가 쉬워진다.
- 디자인과 모델을 높은 수준에서 분리시킬 수 있다.
- 디자인 템플릿 유지가 용이하다.
- 디자이너는 템플릿 pgae를 만들어서 관리한다.
- 개발이 충분히 끝났다면, 개발자는 템플릿 page를 복사해서 (모듈을 포함한)서비스 페이지를 만들면 된다.
- 버전관리의 용이성
- wiki처럼 page단위로 버전관리가 용이하다.
:::

Adapter pattern
인터페이스가 일치하지 않는 클래스를 함께 운용해야 하는 경우에 사용할 수 있다. 비교적 간단한 패턴으로 각각의 class를 수정하지 않고 함께 운용할 수 있도록 해준다.
이 패턴에는 다음의 객체들이 존재한다.
- Adaptee : 원하는 기능을 가지고 있는 객체
- Client : Adaptee의 기능을 사용할려는 사용자 객체
- Adapter : Client의 인터페이스를 가지고 Adaptee의 원하는 기능을 구현한 객체
Adapter pattern은 Object Pattern과 Class pattern 두가지 방법으로 구현가능하다.
Object Pattern 버전
Client는 Adaptee의 methodB를 사용하고 싶다. 그런데, Client와 Adapter모두 구현이 끝난 상태 - 혹은 라이브러리만 가지고 있거나 -로 클래스를 수정할 수 없다. 대신 중간에 Client와 호환되는 인터페이스를 가진 Adapter를 둔다. 이 호환되는 메서드를 client가 호출하면, Adapter는 Adaptee의 methodB를 호출하게 된다. 이렇게 해서 기존 클래스의 수정없이 Client에서 Adaptee의 기능을 이용할 수 있게 된다.
집합관계를 이용한 새로운 객체를 두는 방식으로, Object Pattern 이라고 한다.
Client Adapter Adaptee +-----+ +------+ +-----+ | |---> > |---) ) | | |---> > |---) ) | +-----+ +------+ +-----+ Adapter는 Client와 Adaptee에 호환되는 Interface를 모두 가지고 -집합- 있다.
디자인 패턴을 생성하는 방법중 객체 합성을 이용한 예라고 보면 될것 같다. 다른 한가지 방법은 클래스 합성인데, 아래에 설명한다.
Class Pattern 버전
C++의 다중상속을 이용하는 방법이다. 여기에서 Adapter는 Adaptee를 상속받아서 구현할 수 있다.
예제
와우를 좋아하니, 와우의 직업군으로 예제를 만들어 봤다. 와우에서 가장 중요한 직업이라고 하면 역시 파티의 리더이자 몸빵인 Fighter와 Fighter와 파티의 피를 책임져 주는 healer일 것이다. - 참고로 나는 천민 냥꾼. 그래도 부사령관이라는. 말퓨리온서버 드라고너를 찾아주세요.-
Fighter도 피가 없을때에는 붕대질로 피를 채울 수 있기는 한데, 힐러의 healing 마법에 비해서는 그 효율성이 대단히 떨어질 수 밖에 없다.
그래서 Fighter에게 힐러의 1 level 힐링 능력을 선사하기로 했다. 와우저의 입장에서는 Fighter에게 마법이 생소할
수 있겠지만, D&D룰 같은 경우에만 봐도 Fighter Class가 마법을 배우는 것을 금지하고 있지는 않다. 좀
효율이 떨어져서 그렇지..
이제 Fighter에게 힐러의 cure 능력을 줘야 하는데.. 음 이게 인터페이스가 다른게 좀 문제다. Adapter Pattern을 이용하면, 이 문제를 풀 수 있을 것 같다.
Object pattern 버전
이건 Object pattern 버전의 코드다.
#include <iostream> using namespace std; /* 응급상자 클래스 여기에는 붕대와 붕대감기를 위한 Operation이 포함되어 있을 것이다. */ class AidKit { }; class Fighter { private: public: // 원래 전사는 붕대를 이용해서 치료할 수 밖에 없었지만.. virtual void cure(AidKit *) = 0; virtual void attack() = 0; virtual void defense() = 0; void myClass(){cout << "I'm fighter" << endl;} }; class Healer { public: void healing(int Level) { cout << "Level : " << Level << "Healing 시전" << endl; } void magic_attack(); void magic_defense(); }; class AdvFighter : public Fighter { private : Healer *heal; int healLevel; public: AdvFighter(Healer *h) { heal = h; healLevel = 1; } void attack(){cout << "Attack !!!" << endl;}; void defense(){cout << "Defense !!!" << endl;}; void setHealLevel(int lev) { } // 레벨업 되면서, 힐러의 healing 마법을 쓸 수 있게 되었다. // 1 level 이지만 이것만 해도 감사 감사. // 기존의 붕대질 스킬은 그대로 둔채, 새로운 스킬을 추가해도 된다. void cure(AidKit *) { heal->healing(healLevel); } void healing() { heal->healing(healLevel); } int getHealLevel() { return healLevel; } }; int main(int argc, char **argv) { Healer *myHealer; AidKit *myKit; AdvFighter *healFighter = new AdvFighter(myHealer); healFighter->myClass(); healFighter->cure(myKit); healFighter->healing(); healFighter->attack(); }
Class Pattern 버전
C++의 다중상속을 이용해서 구현했다.
// ...전략 class Fighter { private: public: virtual void cure(AidKit *) = 0; virtual void attack() = 0; virtual void defense() = 0; void myClass(){cout << "I'm Fighter" << endl;} }; class Healer { public: void healing(int Level) { cout << "Level : " << Level << "Healing 시전" << endl; } void magic_attack(); void magic_defense(); }; class AdvFighter : public Fighter, private Healer { private : int healLevel; public: AdvFighter() { healLevel = 1; } void cure(AidKit *) { Healer::healing(healLevel); } void attack(){}; void defense(){}; }; int main() { AdvFighter *myFighter = new AdvFighter; AidKit *myKit; myFighter->myClass(); myFighter->cure(myKit); return 0; }
generic Adapter pattern
템플릿을 이용한 generic adapter pattern이건 따로 문서를 만들어서 정리해볼 생각임. :::

음.. 난 주식,펀드,부동산 뭐 이런 재태크 같은거엔 신경끄고 살고 있다. 도박판 제로섬게임에서 개미가 설레발 쳐봤자
호구신세면하지 못할 거라는게 뻔하다는게 마인드였으니까.도박판논리로 봐도 그렇고, 정보공학논리로 봐도 그렇고 조금만 생각해보면
답이 뻔히 보이는 판이기도 하고.
도박판에서 승리를 할려면, 우선 밑천이 많아야 할 것이고 그 다음 많은 정보를 가지고 있어야 할거다. 근데, 개미들에게 밑천을
바라는 건 무리고 그렇다면 남은건 정보인데.. 개미들이 서점에 나온 주식관련서적에서 얻은 정보를 가지고 설레발쳐본댄듯 어차피
알려진 정보는 정보의 가치가 그만큼 떨어지는 법, 그러한 정보를 가지고 피와 살이 튀기는 정글주식판에 뛰어들어서 살아남을 수
있을 거라는 생각자체가 합리적이지 못하다고 보았기 때문이다. 어차피 도박판이라는게 합리적인 세계가 아니긴 하지만 말이다.
근데 무엇보다. 리눅스라서 주식거래 프로그램을 띄우지 못한다는 이유 때문에 주식이든 펀드든간에 멀리한 이유가 상당히 컸다.
거기에 귀차니즘으로 vmware니 하는 것 깔기도 귀찮았고. 뭐 손십게 hts?인가 하는 프로그램을 운용할 수 있었다면, 아마
재미삼아서라도 주식이라든지 기타등등에 뛰어들었겠지만 이건 당최 리눅스에서는 되는게 없으니.
리눅스를 사용하면 또다른 좋은 점은 지름신을 멀리할 수 있다는 건데. 홈쇼핑이 안되거든. 책살때도 발품팔고 게임살때도 발품을 판다. 그러다보면 물리적 한계 때문에 지름신이 강림하지 못하신다.
공황이 염려되는 어려운 상황에 리눅스를 선택하시라. 풋이니 콜이니 하는 것에 스트레스 받을 일도, 달달이 날라오는 카드결제대금에 가슴졸일 필요도 없다. :::

|