<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0' version='2.0'><channel><atom:id>tag:blogger.com,1999:blog-931078517202405968</atom:id><lastBuildDate>Fri, 18 May 2012 09:09:56 +0000</lastBuildDate><category>번역</category><category>그래픽스</category><category>konstruktor</category><category>음악</category><category>영화</category><category>안드로이드</category><category>게임</category><category>개발</category><title>electroscape.ko</title><description>rationality on top of blazing enthusiasm.</description><link>http://ko.blog.influx.kr/</link><managingEditor>noreply@blogger.com (segfault)</managingEditor><generator>Blogger</generator><openSearch:totalResults>15</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-931078517202405968.post-3400227410255431435</guid><pubDate>Fri, 13 Apr 2012 17:18:00 +0000</pubDate><atom:updated>2012-04-18T17:55:40.444+09:00</atom:updated><title>PHP: 잘못된 디자인의 프랙탈</title><description>&lt;p&gt;&lt;a href="http://me.veekun.com/blog/2012/04/09/php-a-fractal-of-bad-design/"&gt;PHP: a fractal of bad design&lt;/a&gt; (원문)&lt;/p&gt;&lt;p&gt;수년간 PHP는 가장 널리 쓰이는 웹 프로그래밍 언어 중 하나로 각광받아 왔습니다. 하지만 여러 프로그래밍 언어가 발달하고 진보적인 웹 개발 방법론들이 제안되고 PHP의 설계와 구현에 대한 비판이 나오기 시작합니다. 그리고 PHP에 대한 비판은 프로그래밍 언어 커뮤니티에서 가장 흔한 떡밥(flamebait) 중 하나가 되어 있습니다.&lt;/p&gt;&lt;p&gt;그런 상황에서 나온 글이 이 긴 글입니다. PHP는 거시적으로 봐도 문제가 보이지만 그 문제들을 확대해 보면 거기에 얽힌 문제가 무수하게 계속 발견되기 때문에 제목에서 PHP를 잘못된 디자인의 &lt;a href="http://ko.wikipedia.org/wiki/%ED%94%84%EB%9E%99%ED%83%88"&gt;프랙탈&lt;/a&gt;이라고 표현합니다. 작성자는 "PHP는 이렇다" 하며 비유를 드는 것으로 시작해서 PHP의 단점이라고 여겨지는 것들을 철저히 해체하기 시작합니다. "오늘 PHP를 쓰러뜨리지 않고선 집에 돌아가지 않겠다"는, 어떤 독기가 느껴지는 글입니다. 다분히 파괴적이긴 하나 어떤 관점에서 PHP가 비판받아 왔는지를 잘 보여주는 글이라고 생각합니다.&lt;/p&gt;&lt;p&gt;물론 지금까지 PHP를 별 문제 없이 써 오신 분들도 계실 겁니다. 하지만 지금까지 PHP로 프로그래밍을 하면서 생긴 습관이 나쁜 것이었다면? 계몽적인 태도라는 점은 인정하지만 PHP 외에 다른 언어를 써 본 적이 없는 분들을 위해 PHP의 어떤 부분이 바보같은지 알려드리고 어떤 관점에서 프로그래밍 언어를 보면 좋을지 시각을 넓혀드리기 위해 이 글을 번역해 보았습니다.&lt;/p&gt;&lt;p&gt;사족을 달자면 PHP를 쓸 수 밖에 없거나 유지보수가 필요 없는 작은 규모의 프로젝트를 할 것이 아니라면 PHP는 가급적 피하라고 권하고 싶습니다. PHP는 문제점 투성이고 세상에는 PHP보다 훨씬 좋은 언어가 많습니다.&lt;/p&gt;&lt;p&gt;이사다 뭐다 해서 개인적으로 바쁜 상황에서 급하게 번역한 거라 잘못 번역된 내용이 있을 겁니다. 만약 그런 부분이 보인다면 지적해 주세요. 기쁘게 받아들이겠습니다.&lt;/p&gt;&lt;h2&gt;서문&lt;/h2&gt;&lt;p&gt;저는 까칠한 사람입니다. 주위의 많은 것에 불평합니다. 세상에는 제가 좋아하지 않는 기술들이 많이 있는데, 그것은 당연한 것입니다. 프로그래밍은 우스울 정도로 역사가 짧은 학문이고 우리 중 누구도 우리가 지금 무엇을 하고 있는지조차 갈피를 잡지 못합니다. &lt;a href="http://en.wikipedia.org/wiki/Sturgeon%27s_Law"&gt;스터전의 법칙&lt;/a&gt;을 감안하면 주변에 불평할 것들이 평생에 걸쳐서 널려 있습니다.&lt;/p&gt;&lt;p&gt;모든 것이 같지는 않습니다. PHP는 그저 쓰기 어색하다거나 제가 원하는 부분과 맞지 않다거나 차선책이라거나 신념에 맞지 않는 것이 아닙니다. 저는 일반적으로 좋은 방식이라고 여겨지는 것 중에 제가 싫어하는 것들과 나쁜 방식이라고 여겨지는 것 중에 좋아하는 것들을 말씀드릴 수 있습니다. 자, 질문하세요! 이런 주제로 흥미로운 대화를 나눠 볼수도 있을 겁니다.&lt;/p&gt;&lt;p&gt;PHP는 유일한 예외입니다. 실질적으로 PHP의 모든 부분은 어딘가 잘못되어 있습니다. 언어, 프레임워크, 생태계 모두 &lt;strong&gt;그냥 개판입니다&lt;/strong&gt;. 단 하나만 콕 집어서 얘기할 수도 없는 것이 아닙니다. 이 모든 폐해는 시스템 전반에 걸쳐 있기 때문입니다. 매번 PHP의 불만을 정리할 때마다 한번 쭉 훑은 상태에서도 자꾸만 트리비아가 질릴 정도로 발견되어서 이내 막혀버리고 맙니다.  (그래서 프랙탈)&lt;/p&gt;&lt;p&gt;PHP는 나의 기술을 망치는 골칫거리입니다. PHP는 완전 개판이지만 아직 다른 것들을 배워보지도 않은 (권력이 있는) 아마추어들이 칭찬을 해대는 통에 미치겠습니다. 결점을 상쇄시킬만한 장점은 쥐꼬리만하고 저는 이 언어의 존재 자체를 잊어버리라고 권하고 싶습니다.&lt;/p&gt;&lt;p&gt;하지만 일단 모든 것을 정리해야 할 것 같군요. 자 갑니다, 이게 마지막이예요.&lt;/p&gt;&lt;h2&gt;비유&lt;/h2&gt;&lt;p&gt;저는 막 Mel에게 저의 좌절감을 내뱉었고 그녀는 여기에 (그것에 대해) 써 보길 권했습니다.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;심지어 저는 PHP의 뭐가 잘못되었는지조차 말할 수가 없어요. 왜냐 하면... 음.... 공구상자가 있다고 해 봐요. 각종 공구가 들어있는. 뭐 그런대로 괜찮아 보이겠죠. 표준적인 것들이 들어 있을거구요.&lt;/p&gt;&lt;p&gt;그런데 거기서 드라이버를 꺼냈는데 끝이 이상한 삼각형 모양인 거예요. 뭐 좋아, 나한테 유용하진 않겠지만 언젠간 쓸만할 때도 있겠지.&lt;/p&gt;&lt;p&gt;이제 망치를 꺼냈는데 경악스럽게도 양쪽 끝에 못뽑이가 달려 있네요. 뭐 어쨌든 쓸 수야 있겠죠. 옆으로 잡고 망치의 가운데 부분으로 못을 박는다거나 하는 식으로.&lt;/p&gt;&lt;p&gt;이제 펜치를 꺼냈는데 톱니 무늬가 없어요. 평평하고 매끈하네요. 유용하진 않지만 어쨌든 볼트를 돌릴 수야 있을테니 뭐 어때요.&lt;/p&gt;&lt;p&gt;그리고 계속 봅시다. 공구상자 안의 모든 것들이 괴상하지만 완전히 쓸모없진 않을 거예요. 그리고 전체적으로 보면 문제가 아니예요. 왜냐하면 일단 공구가 있긴 하니까.&lt;/p&gt;&lt;p&gt;이제 그 공구상자를 쓰면서 "이봐 대체 이 공구상자에 무슨 문제가 있다고? 난 지금까지 이것들을 써 왔고 이것들은 쓸만하다고!"라고 하는 말하는 수백만명의 목수들을 보게 됩니다. 그리고 목수들이 자기가 지은 집을 보여주는데, 방들은 죄다 오각형 모양이고 지붕은 거꾸로 뒤집혀 있군요. 그리고 정문을 두드리면 집이 폭삭 무너져 내리고 안에서 그 사람들이 왜 문을 부수냐고 소리를 지릅니다.&lt;/p&gt;&lt;p&gt;이게 PHP의 문제점이예요.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;입장&lt;/h2&gt;&lt;p&gt;저는 아래의 요소들이 프로그래밍 언어를 생산적이고 가치있게 만드는 데 중요한 요소라고 주장하지만 PHP는 이 모든 것들을 심각하게 위반합니다. 만약 이것들이 중요하다는 것에 동의하지 않는다면 대체 어떤 것에 동의하실지 짐작할 수 없군요.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;언어는 &lt;strong&gt;예측 가능&lt;/strong&gt;해야 합니다. 언어는 사람의 아이디어를 표현하고 컴퓨터로 하여금 이를 실행하도록 하는 매체 역할을 합니다. 따라서 인간이 프로그램을 이해하고 있는 것이 실제로 들어맞는지는 중요합니다.&lt;/li&gt;&lt;li&gt;언어는 &lt;strong&gt;일관성&lt;/strong&gt;이 있어야 합니다. 비슷한 것들은 비슷하게 생겨야 하고 다른 것들은 다르게 생겨야 합니다. 언어의 일부분을 통해 나머지를 배우는데 어려움이 없어야 합니다.&lt;/li&gt;&lt;li&gt;언어는 &lt;strong&gt;간결&lt;/strong&gt;해야 합니다. 새로운 언어들은 낡은 언어들에 내재된 보일러플레이트*를 줄이기 위해 존재합니다. (우리는 기계어로 코드를 짤 수도 있겠죠) 언어는 그 자신이 새로운 보일러플레이트를 만들어내는 것을 지양해야 합니다.&lt;/li&gt;&lt;li&gt;언어는 &lt;strong&gt;신뢰성&lt;/strong&gt;이 있어야 합니다. 언어는 문제를 해결하기 위한 도구입니다. 언어 자신이 문제를 만들어내지 말아야 합니다. "버그 발견했다!"는 상황이 오는 것은 큰 집중력의 손실입니다.&lt;/li&gt;&lt;li&gt;언어는 &lt;strong&gt;디버깅이 가능&lt;/strong&gt;해야 합니다. 만약 뭔가가 잘못된다면 프로그래머는 그것을 고쳐야 하며 그 과정에서 최대한의 도움을 받아야 합니다.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;제 입장은 이렇습니다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;PHP는 놀라움으로 가득합니다: &lt;code&gt;mysql_real_escape_string&lt;/code&gt;, &lt;code&gt;E_ACTUALLY_ALL&lt;/code&gt;&lt;/li&gt;&lt;li&gt;PHP는 일관적이지 못합니다: &lt;code&gt;strpos&lt;/code&gt;, &lt;code&gt;str_rot13&lt;/code&gt;&lt;/li&gt;&lt;li&gt;PHP는 보일러플레이트가 필요합니다: C API 호출시 오류 체크, &lt;code&gt;===&lt;/code&gt;&lt;/li&gt;&lt;li&gt;PHP는 신뢰할 수 없습니다: &lt;code&gt;==&lt;/code&gt;, &lt;code&gt;for ($foo as &amp;$bar)&lt;/code&gt;&lt;/li&gt;&lt;li&gt;PHP는 불투명합니다: 스택 추적 불가, 복잡한 에러 보고&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;저는 매 문제 하나하나마다 왜 이것이 이 범주에 들어가는지 따로 부연설명을 하지 않을 겁니다. 한다면 아마 끝이 없겠지요. 아마 독자 여러분들이 스스로 생각하실 수 있을 거라고 믿습니다.&lt;/p&gt;&lt;h2&gt;노 코멘트&lt;/h2&gt;&lt;p&gt;저는 PHP 논쟁을 많이 벌였습니다. 그럴 때마다 논의를 중단시키려는 목적 말고 없는 뻔한 반론들을 엄청나게 봐 왔습니다. 여기서 제발 저한테 이런 소리는 들이밀지 말아주세요. :(&lt;/p&gt;&lt;ul&gt;&lt;li&gt;"좋은 목수는 연장 탓을 하지 않고 나쁜 목수는 어쩌구저쩌구…" 같은 소리는 하지 마세요. 아무런 의미 없는 말입니다. 좋은 목수라면 망치로든 바위로든 못을 박을 수야 있겠지요. 하지만 얼마나 많은 목수들이 바위로 못을 박을 수 있겠습니까? 좋은 개발자를 결정짓는 요소 중 하나가 자기에게 가장 좋은 도구를 선택하는 능력입니다.&lt;/li&gt;&lt;li&gt;수없이 많은 이상한 예외나 이해할 수 없는 동작을 기억하는 것은 개발자의 책임이라고 말하지 마세요. 네, 이런 거야 어떤 시스템에서든 필연적이긴 합니다. 컴퓨터는 기본적으로 똥이거든요. 하지만 그렇다고 그게 시스템의 바보같음을 포용할 수 있는데 제한이 없다는 것을 의미하지는 않습니다. PHP는 예외 말고는 아무것도 없습니다. 그리고 실제 프로그램을 작성하는 것보다 언어와 씨름하는데 드는 노력이 더 크다면 그건 전혀 좋은 것이 아닙니다. 내가 쓰는 도구는 내 일거리를 더 늘리지 말아야 합니다.&lt;/li&gt;&lt;li&gt;"그게 C API에서 동작하는 방식"이라고 말하지 마세요. 고작 C API의 래퍼나 헬퍼 함수 정도 제공하는게 전부라면 대체 고수준 언어를 쓰는 이유가 뭡니까? 그럴거면 그냥 C로 짜세요! 그럴 때를 위한 &lt;a href="http://www.boutell.com/cgic"&gt;CGI 라이브러리&lt;/a&gt;도 있습니다.&lt;/li&gt;&lt;li&gt;"그게 니가 이상한 짓을 하기 때문에 생기는 것"이라고 말하지 마세요. 만약 두 개의 기능이 있다면 누군가가 언젠가 그것들을 함께 사용할 이유를 찾아내겠지요. 그리고 다시 한번, 이것은 C 언어가 아닙니다. 스펙도 없고 "정의되지 않은 동작"이 존재할 이유도 없습니다.&lt;/li&gt;&lt;li&gt;"페이스북이나 위키백과가 PHP로 만들어졌다"고 하지 마세요. 이미 알고 있다고요! 아마 Brainfuck으로도 그런 걸 만들 수 있겠죠. 하지만 그런 걸 만들어낼 수 있을 정도로 똑똑한 사람들이라면 그런 사람들은 플랫폼의 문제도 극복할 수 있습니다. 아시겠지만 어떤 다른 언어로 만들어진다면 개발 기간은 절반이 될 수도 있고 두배가 될 수도 있겠죠. 그러니까 그런 사실만으로는 어떤 의미도 내포하지 않습니다.&lt;/li&gt;&lt;li&gt;이상적으로는 그냥 저한테 아무 말도 하지 마세요. 만약 이 목록에서 어떤 것도 당신의 PHP에 대한 견해를 상처입힐 수 없다면 아마 다른 어떤 것도 없을 겁니다. 그러니까 인터넷의 누군가랑 키배 뜰 생각 하지 마시고 멋진 웹사이트를 만들어서 제가 틀렸다는 것을 입증해 주세요.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;여기서 사족, 저는 Python을 무척 좋아합니다. 원하신다면 저야 기쁘게 불평불만을 쏟아낼 수 있습니다. 저는 이게 완벽한 언어라고 말하지 않았습니다. 다만 저는 장점과 단점을 저울질해서 이것이 제가 쓰기에 가장 적합한 언어라는 결론을 내렸을 뿐입니다.&lt;/p&gt;&lt;p&gt;그리고 저는 PHP로 똑같은 걸 할 수 있는 개발자를 만나본 적이 없습니다. 하지만 이내 패배를 인정하고 사과하는 개발자들은 많이 봤습니다. 이런 사고방식은 대단합니다.&lt;/p&gt;&lt;h2&gt;PHP&lt;/h2&gt;&lt;h3&gt;핵심 언어&lt;/h3&gt;&lt;p&gt;CPAN은 "Perl의 표준 라이브러리"로 불려 왔습니다. 그게 Perl의 표준 라이브러리에 대해서 많은 것을 이야기하는 건 아니지만 탄탄한 기반에서는 많은 대단한 것들을 만들어낼 수 있다는 점을 내포하고 있습니다.&lt;/p&gt;&lt;h4&gt;철학&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;PHP는 원래 대놓고 프로그래머가 아닌 사람들 (그리고 행간을 읽으면 프로그램이 아닌 것들) 을 위해 설계되었습니다. 하지만 그 근본으로부터 잘 벗어나진 못했습니다. &lt;a href="http://www.php.net/manual/phpfi2.php#overload"&gt;PHP 2.0 문서&lt;/a&gt;에서 &lt;code&gt;+&lt;/code&gt; 같은 연산자들의 형변환에 대해 다루는 부분을 인용합니다.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;각각 자료형에 대해서 연산자를 따로 두면 언어는 더 복잡해질 것입니다. 예를 들어 문자열형에 '==' 연산자를 쓰지 못하기 때문에 'eq' 같은 걸 쓴다고 칩시다. 그렇게 하는 의미를 잘 모르겠습니다. 특히나 PHP처럼 대부분의 스크립트가 단순하고 학습 곡선이 가파르지 않은 기본적인 문법으로 이루어진 언어를 원하는, 프로그래머가 아닌 사람들에 의해 만들어진 언어라면요.&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;&lt;li&gt;PHP는 무슨 수를 써서라도 계속 돌아가도록 만들어졌습니다. 오류를 내면서 중단되거나 뭔가 알 수 없는 동작을 하는 상황이 오면 PHP는 뭔가 알 수 없는 동작을 하는 것을 택합니다. 아무것도 없는 것보다 아무거라도 있는 것이 낫다는 겁니다.&lt;/li&gt;&lt;li&gt;명확한 디자인 철학이 없습니다. 초창기 PHP는 Perl의 영향을 받았습니다. "out" 파라미터가 있는 거대한 stdlib은 C에서 가져온 것이고 객체지향 부분은 C++와 Java에서 가져왔습니다.&lt;/li&gt;&lt;li&gt;PHP는 많은 부분을 다른 언어에서 차용합니다. 하지만 그 다른 언어를 알고 있는 사람들에게는 자꾸만 이해할 수 없는 모습을 보입니다. &lt;code&gt;(int)&lt;/code&gt;는 C처럼 보이지만 &lt;code&gt;int&lt;/code&gt;는 존재하지 않습니다. 이름공간은 &lt;code&gt;\&lt;/code&gt;를 사용합니다. 새 배열 문법은 &lt;code&gt;[key =&gt; value]&lt;/code&gt;와 같이 해시 리터럴을 가진 언어들과는 다른 모양을 지니고 있습니다.&lt;/li&gt;&lt;li&gt;약타입 (역주: 예를 들면 문자열, 숫자 등등 간의 암시적 형변환)이 너무 복잡해서 어떤 사소한 프로그래머의 노력도 결코 가치 없는 것으로 만들어 버립니다.&lt;/li&gt;&lt;li&gt;새로운 기능들 중 일부만이 새로운 문법으로 구현됩니다. 대부분은 함수나 함수처럼 보이는 형태로 구현됩니다. 예외적으로 클래스가 있는데, 클래스는 여러 가지의 새로운 연산자나 키워드가 들어갈 필요가 있었죠.&lt;/li&gt;&lt;li&gt;여기서 나열한 문제 중 일부는 퍼스트 파티를 통한 해결책이 있습니다. Zend가 자신들의 오픈 소스 언어를 고치도록 비용을 지불할 의사가 있다면요.&lt;/li&gt;&lt;li&gt;&lt;p&gt;멀리서 보면 수많은 경우가 있습니다. PHP 문서의 어딘가에서 가져온 이 코드를 보시죠.&lt;/o&gt;&lt;pre&gt;&lt;code&gt;@fopen('http://example.com/not-existing-file', 'r');&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이 코드는 어떻게 동작할까요?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;PHP가 &lt;code&gt;--disable-url-fopen-wrapper&lt;/code&gt;로 컴파일되었다면 이 코드는 동작하지 않을 것입니다. (문서에서는 '동작하지 않는다'라는 것이 뭘 의미하는지 말하지 않습니다. 널을 반환하나? 예외를 발생시키나?) PHP 5.2.5에서 이 플래그가 사라졌다는 것을 유념하세요.&lt;/li&gt;&lt;li&gt;php.ini에서 &lt;code&gt;allow_url_fopen&lt;/code&gt;가 꺼져 있다면 역시 동작하지 않습니다. (어떻게? 몰라요.)&lt;/li&gt;&lt;li&gt;&lt;code&gt;@&lt;/code&gt; 때문에 존재하지 않는 파일이라는 경고 메시지는 출력되지 않을 것입니다.&lt;/li&gt;&lt;li&gt;하지만 php.ini에서 &lt;code&gt;scream.enabled&lt;/code&gt;가 켜져 있다면 출력됩니다.&lt;/li&gt;&lt;li&gt;아니면 &lt;code&gt;ini_set&lt;/code&gt;로 &lt;code&gt;scream.enabled&lt;/code&gt;를 수동으로 설정했다면요.&lt;/li&gt;&lt;li&gt;하지만 맞는 &lt;code&gt;error_reporting&lt;/code&gt; 레벨이 설정되지 않았다면 출력되지 않을 겁니다.&lt;/li&gt;&lt;li&gt;하지만 그게 출력된다면, php_ini나 &lt;code&gt;ini_set&lt;/code&gt;를 통해 설정된 &lt;code&gt;display_errors&lt;/code&gt;에 달려 있을 겁니다.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;저는 이 별다른 해를 끼치지 않는 함수 호출이 PHP 컴파일 옵션과 서버 전역 설정, 그리고 프로그램 내부에서의 설정을 모르고서는 어떻게 동작할지 전혀 말씀드릴 수가 없습니다. 그리고 이 모든 것들은 전부 &lt;em&gt;기본적으로 내장된&lt;/em&gt; 동작입니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;PHP는 전역적이고 암시적인 상태로 가득 차 있습니다. &lt;code&gt;mbstring&lt;/code&gt;는 전역 문자셋을 사용합니다. &lt;code&gt;func_get_arg&lt;/code&gt;와 그 친구들은 전형적인 함수처럼 보이지만 현재 실행중인 함수 위에서 동작합니다. 오류와 예외 처리에는 전역 기본값을 사용합니다. &lt;code&gt;register_tick_function&lt;/code&gt; 함수는 매 틱(tick)마다 실행될 전역 함수를 설정합니다. 잠깐, 뭐라구요?!&lt;/li&gt;&lt;li&gt;스레딩이나 기타등등에 대한 지원이 전혀 없습니다. (이미 위에 나열한 것들을 봐서 놀랍진 않군요.) &lt;code&gt;fork&lt;/code&gt; 가 없는 문제와 더불어 (나중에 언급합니다) 이것은 PHP를 통해 병렬 프로그램을 작성하는 것을 극히 어렵게 만듭니다.&lt;/li&gt;&lt;li&gt;PHP 코드 중 일부는 &lt;em&gt;버그를 발생시키도록 설계&lt;/em&gt;되었습니다.&lt;ul&gt;&lt;li&gt;&lt;code&gt;json_decode&lt;/code&gt; 함수는 잘못된 입력이 들어오면 null을 반환합니다. 하지만 문제가 전혀 없는 JSON 객체일지라도 null을 반환할 수 있습니다. 이 함수는 매번 사용할 때마다 &lt;code&gt;json_last_error&lt;/code&gt;를 같이 호출하지 않는 한 &lt;em&gt;전혀 신뢰할 수 없습니다&lt;/em&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;array_search&lt;/code&gt;, &lt;code&gt;strpos&lt;/code&gt; 함수는 문자열의 처음 위치에서 찾으면 0을 반환합니다. 하지만 찾지 못한다면 false를 반환합니다.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;두번째 경우를 조금 더 설명드리도록 하겠습니다.&lt;/p&gt;&lt;p&gt;C 언어에서는 &lt;code&gt;strpos&lt;/code&gt; 같은 함수는 서브스트링을 찾지 못하면 -1을 반환합니다. 만약 그런 경우를 체크하지 않는다면 당신은 엉뚱한 메모리 영역을 가리키게 될 것이고 프로그램은 터질 것입니다. (아니, 아마도요. 이건 C 언어잖아요. 씨발 대체 누가 알겠냐고. 하지만 최소한 이런 경우를 위한 도구는 있죠.)&lt;/p&gt;&lt;p&gt;이를테면 파이썬에서 비슷한 역할을 하는 &lt;code&gt;.index&lt;/code&gt; 메소드는 찾지 못했을 경우 예외를 발생시킵니다. 역시 그런 경우를 체크하지 않으면 프로그램은 터지게 되겠죠.&lt;/p&gt;&lt;p&gt;PHP에서 이러한 함수들은 false를 반환합니다. 만약 &lt;code&gt;FALSE&lt;/code&gt;를 배열 인덱스값으로 사용하거나 &lt;code&gt;===&lt;/code&gt;로 비교하지 않고 이런 저런 짓을 할 경우에 PHP는 false 값을 0으로 알아서 변환해 버립니다. 그럼 당신의 프로그램은 터지지 않겠죠. 대신 당신의 프로그램은 strpos 함수를 사용할 때마다 결과값을 일일히 체크하는 보일러플레이트 코드를 넣지 않는 한 &lt;em&gt;일말의 경고도 없이 엉뚱한 짓을 하게 됩니다&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;이건 정말 나쁜 겁니다! 프로그래밍 언어는 나와 같이 일을 하기 위해서 만들어진 도구잖아요. 여기, PHP는 개발자가 빠지라고 스스로 찾기 힘든 곳에다가 함정을 파 버립니다. 그리고 저는 문자열 처리나 결과값이 같은지 비교하는 등의 따분한 작업을 하면서도 신경을 곤두세워야 합니다. PHP는 그야말로 &lt;em&gt;지뢰밭&lt;/em&gt;입니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;저는 PHP 인터프리터나 그것의 개발자들에 대해 수많은 멋진 이야기를 들었습니다. 그런 이야기들은 PHP 코어나 디버깅된 PHP 코어, 코어 개발자와 소통해본 사람들에게서 왔습니다. 하지만 그 중 칭찬의 말은 단 하나도 없었습니다.&lt;/p&gt;&lt;p&gt;저는 그래서 여기서 결론을 내립니다. 왜냐 하면 자꾸 반복되는 얘기라서요. PHP는 아마추어들의 커뮤니티입니다. PHP를 디자인하고 작업하고 코드를 짜는 사람들 중에 자기가 뭘 하는지 제대로 아는 것 같은 사람들은 극히 드뭅니다. (아 이것 참, 독자 여러분들은 물론 드문 예외죠!) 그리고 문제의 실마리를 잡을 수 있는 사람들은 다른 플랫폼으로 가 버리게 됨으로서 결국 전체에 있어서 평균적인 능력은 계속 줄어들게 됩니다. 바로 이것이 PHP가 가지고 있는 가장 큰 문제점입니다. 그야말로 장님이 장님의 무리를 이끌고 있는 상황.&lt;/o&gt;&lt;p&gt;좋아요, 일단 팩트로 돌아갑시다.&lt;/p&gt;&lt;h4&gt;연산자&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;==&lt;/code&gt;는 &lt;a href="http://habnab.it/php-table.html"&gt;쓸모 없습니다&lt;/a&gt;.&lt;/li&gt;&lt;ul&gt;&lt;li&gt;이것은 추이적 관계(transitive)가 아닙니다. &lt;code&gt;"foo" == TRUE&lt;/code&gt;, &lt;code&gt;"foo" == 0&lt;/code&gt;… 하지만 물론 &lt;code&gt;TRUE != 0&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;==&lt;/code&gt; 연산자는 가능하다면 숫자, 그러니까 부동 소숫점 자료형으로 변환합니다. 그러므로 긴 16진수 문자열을 비교할 때 (이를테면 비밀번호 해쉬 값이라거나) &lt;a href="http://phpsadness.com/sad/47"&gt;그렇지 않은 경우에도 참으로 비교해 버립니다&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;같은 이유로, &lt;code&gt;"6" == " 6"&lt;/code&gt;, &lt;code&gt;"4.2" == "4.20"&lt;/code&gt;, &lt;code&gt;"133" == "0133"&lt;/code&gt;입니다. 하지만 &lt;code&gt;133 != 0133&lt;/code&gt;이라는 건 명심하세요. 왜냐 하면 &lt;code&gt;0133&lt;/code&gt;은 8진수 표기죠.&lt;/li&gt;&lt;li&gt;&lt;code&gt;===&lt;/code&gt; 연산자는 값과 자료형을 같이 비교합니다… 객체만 제외하고요. &lt;code&gt;===&lt;/code&gt;는 실제로 같은 객체일 경우에만 참이 됩니다. 객체에 대해서만 &lt;code&gt;==&lt;/code&gt; 연산자는 &lt;code&gt;===&lt;/code&gt; 연산자가 다른 자료형에 하는 것처럼 값(과 그 안의 모든 어트리뷰트)과 자료형을 같이 비교합니다. &lt;a href="http://developers.slashdot.org/comments.pl?sid=204433&amp;cid=16703529"&gt;뭐?&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;비교 연산자는 두 가지 다른 방법으로 배열을 정렬하려고 시도합니다: 첫번째는 길이를 기준으로, 두번째는 원소를 기준으로. 만약 서로 다른 키값으로 구성된 같은 개수의 원소들로 이루어져 있다면 비교가 불가능합니다.&lt;/li&gt;&lt;li&gt;타입 안전한 &lt;code&gt;==&lt;/code&gt;은 &lt;code&gt;===&lt;/code&gt;가 있습니다. 타입 안전한 &lt;code&gt;&amp;lt;&lt;/code&gt;는… 없네요. 당신이 무엇을 하든간에 무조건 &lt;code&gt;"123" &amp;lt; "0124"&lt;/code&gt;입니다.&lt;/li&gt;&lt;li&gt;이상의 미친 동작들과 Perl처럼 문자열과 숫자 쌍 연산자들을 쓸 수 없는 점에도 불구하고 PHP는 &lt;code&gt;+&lt;/code&gt; 연산자를 오버로딩하지 않습니다. &lt;code&gt;+&lt;/code&gt;는 무조건 더하기 연산이고 문자열 결합은 무조건 &lt;code&gt;.&lt;/code&gt;입니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;[]&lt;/code&gt; 배열 첨자 연산자는 &lt;code&gt;{}&lt;/code&gt;로 쓸 수도 있습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;[]&lt;/code&gt;는 문자열과 배열 뿐만 아니라 어떤 변수에도 쓸 수 있습니다. 널을 반환하고 어떤 경고도 발생하지 않습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;[]&lt;/code&gt;로 배열을 자를 수 없습니다. 오직 하나의 원소만 가져올 수 있습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;foo()[0]&lt;/code&gt;는 문법 오류입니다. (PHP 5.4에서 고쳐짐)&lt;/li&gt;&lt;li&gt;어떤 다른 비슷한 문법을 가진 언어들과 달리, &lt;code&gt;?:&lt;/code&gt;는 left-associative합니다. 예를 들어&lt;pre&gt;&lt;code&gt;  $arg = 'T';&lt;br /&gt;  $vehicle = ( ( $arg == 'B' ) ? 'bus' :&lt;br /&gt;               ( $arg == 'A' ) ? 'airplane' :&lt;br /&gt;               ( $arg == 'T' ) ? 'train' :&lt;br /&gt;               ( $arg == 'C' ) ? 'car' :&lt;br /&gt;               ( $arg == 'H' ) ? 'horse' : 'feet' );&lt;br /&gt;  echo $vehicle;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;는 horse를 출력합니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;변수&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;변수를 선언하는 것은 불가능합니다. 존재하지 않는 변수가 처음 참조되면 자등으로 널값으로 초기화됩니다.&lt;/li&gt;&lt;li&gt;전역 변수는 사용되기 전에 &lt;code&gt;global&lt;/code&gt; 선언이 필요합니다. 이건 위의 원인에 따른 자연스러운 결과이기 때문에 타당할 수도 있습니다. 다만 명시적인 선언 없이는 전역 변수를 읽을 수조차 없다는 점을 제외하면요. PHP는 대신 같은 이름의 지역 변수를 조용히 만듭니다. 제가 아는 것 중에 비슷한 스코핑 문제를 가지고 있는 다른 언어는 없습니다.&lt;/li&gt;&lt;li&gt;참조가 없습니다. PHP에서 참조라고 불리는 것은 그냥 별명(alias)일 뿐입니다. Perl의 참조자 같이 한 걸음 물러나 생각해야 할 부분이 없습니다. Python처럼 pass-by-object도 없습니다.&lt;/li&gt;&lt;li&gt;"참조성"은 언어의 어떤 다른 부분과 달리 변수를 감염시킵니다. PHP는 동적 언어이기 때문에 변수에는 자료형이 없습니다... 함수 정의나 변수 문법, 대입을 꾸미는 참조를 제외하면요. 한번 변수의 참조를 만들면 (이것은 어디에서나 일어날 수 있습니다) 그것은 참조로 굳어져 버립니다. 이것을 감지할 수 있는 방법은 없고 참조를 해제하고 싶으면 변수 자체를 날려버리는 방법 외에는 없습니다.&lt;/li&gt;&lt;li&gt;사실 거짓말을 했어요. &lt;a href="http://www.php.net/manual/en/book.spl-types.php"&gt;SPL 자료형&lt;/a&gt;이란 것이 있는데 이것 역시 변수를 감염시킵니다: &lt;code&gt;$x = new SplBool(true); $x = "foo";&lt;/code&gt;는 동작하지 않을 것입니다. 이건 보시다시피 정적 타이핑과 비슷한 것입니다.&lt;/li&gt;&lt;li&gt;존재하지 않는 변수(나중에 배열이 될)의 키값의 참조를 만들 수 있습니다. 존재하지 않는 배열을 사용할 때에는 알림(notice)이 발생하지만 이 경우에는 그렇지 않습니다.&lt;/li&gt;&lt;li&gt;상수는 문자열을 받는 함수 호출에 의해서 정의됩니다. 그 이전에 상수는 존재하지 않습니다. (이것은 아마 Perl의 &lt;code&gt;use constant&lt;/code&gt; 동작을 흉내낸 것일 수도 있겠습니다.)&lt;/li&gt;&lt;li&gt;변수 이름은 대소문자를 구분합니다. 함수와 클래스 이름은 구분하지 않습니다. 메소드 이름도 해당되는데, 이는 camelCase 네이밍을 이상한 선택으로 만들어 버립니다.&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;언어 구조&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;array()&lt;/code&gt;와 기타 유사한 것들은 함수가 아닙니다. &lt;code&gt;array&lt;/code&gt; 그 자체는 아무 역할도 하지 않습니다. &lt;code&gt;$func = "array"; $func();&lt;/code&gt;는 동작하지 않습니다.&lt;/li&gt;&lt;li&gt;배열 unpacking은 &lt;code&gt;list($a, $b) = ...&lt;/code&gt;로 할 수 있습니다. &lt;code&gt;list()&lt;/code&gt; 역시 &lt;code&gt;array&lt;/code&gt;처럼 함수 비스무리한 문법입니다. 왜 이것이 별개의 문법으로 구현되지 않고 혼란스럽게 되어 있는지는 모릅니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;(int)&lt;/code&gt;는 분명히 C 언어처럼 보이기 위해 만들어졌습니다. 하지만 저것 자체가 하나의 토큰입니다. PHP에는 &lt;code&gt;int&lt;/code&gt;라는 것이 없습니다. 한번 직접 해 보세요. &lt;code&gt;var_dump(int)&lt;/code&gt;는 동작하지 않을 뿐만 아니라 &lt;code&gt;(int)&lt;/code&gt; 자체를 캐스팅 연산자로 해석해서 파싱 오류를 뱉어냅니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;(integer)&lt;/code&gt;는 &lt;code&gt;(int)&lt;/code&gt;와 같습니다. &lt;code&gt;(bool)&lt;/code&gt;/&lt;code&gt;(boolean)&lt;/code&gt;나 &lt;code&gt;(float)&lt;/code&gt;/&lt;code&gt;(double)&lt;/code&gt;/&lt;code&gt;(real)&lt;/code&gt; 같은 것도 있습니다.&lt;/li&gt;&lt;li&gt;배열로 강제 형변환을 하기 위한 &lt;code&gt;(array)&lt;/code&gt;와 객체를 위한 &lt;code&gt;(object)&lt;/code&gt; 연산자도 있습니다. 말도 안 되는 것처럼 보이겠지만 쓸 만한 구석이 있긴 있습니다. 만약 함수의 인자로 한 개의 아이템 또는 리스트를 넘기고 싶고 둘 다 똑같이 취급하고 싶을 때 쓸 수 있습니다. 하지만 그다지 신뢰성이 없는 것이, 만약 한개의 &lt;em&gt;객체&lt;/em&gt;를 넘긴다면 그걸 배열로 캐스팅하면 배열 하나가 만들어져서 그 객체의 어트리뷰트를 담게 됩니다. (만약 객체로 캐스팅하면 그 반대의 과정이 일어납니다.)&lt;/li&gt;&lt;li&gt;&lt;code&gt;include()&lt;/code&gt;와 그 친구들은 기본적으로 C의 &lt;code&gt;#include&lt;/code&gt;와 같습니다. 다른 소스 파일을 읽어서 그 안에 그대로 때려박습니다. PHP에는 모듈 시스템이 없습니다. 심지어 PHP 코드에도요.&lt;/li&gt;&lt;li&gt;함수나 클래스는 지역 범위(local scope) 안에 네스팅될 수 없습니다. 단지 전역으로 정의됩니다. 만약 파일을 인클루드하면 변수는 현재 함수 안에 들어갑니다. 하지만 함수나 클래스는 전역으로 정의됩니다.&lt;/li&gt;&lt;li&gt;배열에 내용을 추가시키려면 &lt;code&gt;$foo[] = bar;&lt;/code&gt;처럼 합니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;echo&lt;/code&gt;는 함수가 아니라 선언문 같은 겁니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;empty($var)&lt;/code&gt;는 함수같이 생겼지만 함수가 아닌 것의 극단적인 예일 것입니다. 예를 들어 &lt;code&gt;empty($var || $var2)&lt;/code&gt;는 파싱 오류를 냅니다. 대체 세상에 왜 파서가 &lt;code&gt;empty&lt;/code&gt;라는 것의 존재에 대해서 알고 있어야 하는 겁니까?&lt;/li&gt;&lt;li&gt;블록을 정의하는 쓸데없는 문법이 있습니다. &lt;code&gt;if (...): ... endof;&lt;/code&gt; 등등...&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;오류 처리&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;PHP의 독특한 연산자 중 하나가 에러 출력을 &lt;em&gt;씹는 데 사용되는&lt;/em&gt; &lt;code&gt;@&lt;/code&gt;입니다. (실제로는 DOS에서 가져옴)&lt;/li&gt;&lt;li&gt;PHP 에러는 스택 추적을 제공하지 않습니다. 스택 추적을 하려면 핸들러를 설치해야 합니다. (하지만 그것도 치명적인 오류(fatal error)에는 해당되지 않습니다. 자세한 건 아래를 보세요)&lt;/li&gt;&lt;li&gt;PHP 파싱 오류는 단지 파싱 상태만을 뱉을 뿐 더 이상 아무 것도 보여주지 않습니다. 따라서 코딩 중 뭔가를 빼먹었을 때 &lt;a href="http://phpsadness.com/sad/44"&gt;디버깅이 끔찍하게 어려워집니다&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;예를 들어 PHP 파서는 내부적으로 &lt;code&gt;::&lt;/code&gt; 연산자를 &lt;code&gt;T_PAAMAYIM_NEKUDOTAYIM&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; 연산자를 &lt;code&gt;T_SL&lt;/code&gt;로 표현합니다. 제가 "내부적"이라고 적었지만 만약에 저것들을 엉뚱한 곳에 집어넣었다가는 에러 메시지에서 저 문구를 보게 될 겁니다.&lt;/li&gt;&lt;li&gt;대부분의 에러 핸들링은 아무도 볼 일 없는 서버 로그에 에러 메시지를 출력하는 형태로 이루어집니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;E_STRICT&lt;/code&gt;가 있지만 실제로 도움이 되는 것처럼 보이지는 않습니다. 게다가 이것이 실제로 무슨 역할을 하는지에 대한 문서도 없습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;E_ALL&lt;/code&gt;는 모든 에러 범주를 포함합니다. &lt;code&gt;E_STRICT&lt;/code&gt;만 제외하고요.&lt;/li&gt;&lt;li&gt;&lt;p&gt;무엇이 허용되고 무엇이 허용되지 않는지에 대한 일관성이 전혀 없습니다. &lt;code&gt;E_STRICT&lt;/code&gt;가 여기서 어떻게 작용하는지는 잘 모르겠지만 이것들은 허용됩니다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;존재하지 않는 객체의 프로퍼티에 접근. 예를 들어 &lt;code&gt;$foo-&amp;gt;x&lt;/code&gt;. (경고)&lt;/li&gt;&lt;li&gt;함수나 변수나 클래스의 이름에 변수를 사용 (출력되지 않음)&lt;/li&gt;&lt;li&gt;정의되지 않은 상수를 참조 (알림)&lt;/li&gt;&lt;li&gt;객체가 아닌 무언가의 프로퍼티에 접근하려고 할 때 (알림)&lt;/li&gt;&lt;li&gt;존재하지 않는 변수를 참조하려고 할 때 (알림)&lt;/li&gt;&lt;li&gt;&lt;code&gt;2 &amp;lt; "foo"&lt;/code&gt; (출력되지 않음)&lt;/li&gt;&lt;li&gt;&lt;code&gt;foreach (2 as $foo);&lt;/code&gt; (경고)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;그리고 이것들은 허용되지 않습니다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;존재하지 않는 클래스 상수에 접근하려고 할 때. 예를 들어 &lt;code&gt;$foo::x&lt;/code&gt; (치명적 오류)&lt;/li&gt;&lt;li&gt;함수나 변수, 클래스의 이름에 상수를 사용 (파싱 오류)&lt;/li&gt;&lt;li&gt;존재하지 않는 함수를 호출하려고 할 때 (치명적 오류)&lt;/li&gt;&lt;li&gt;블록이나 파일의 맨 마지막에 세미콜론을 남길 때 (파싱 오류)&lt;/li&gt;&lt;li&gt;&lt;code&gt;list&lt;/code&gt;나 기타 등등 내장스러운 것들을 메소드 이름으로 사용할 때 (파싱 오류)&lt;/li&gt;&lt;li&gt;함수의 반환값의 원소값을 참조할 때. 예를 들어 &lt;code&gt;foo()[0]&lt;/code&gt; (파싱 오류. 위에서 언급했다시피 PHP 5.4에서 해결되었습니다)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;그리고 여기서 언급한 것이 아니더라도 기타 이상한 파싱 에러가 있습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;__toString&lt;/code&gt; 메소드는 예외를 발생시키지 않습니다. 만약 발생시키려고 하면 PHP는 음... 어... 예외를 발생시키긴 합니다. (실제로는 치명적 오류입니다. 그런대로 괜찮긴 합니다만....)&lt;/li&gt;&lt;li&gt;PHP 에러와  PHP 오류는 완전히 다릅니다. 이 둘은 &lt;em&gt;전혀&lt;/em&gt; 상호작용하지 않습니다.&lt;ul&gt;&lt;li&gt;PHP 에러(내부적으로 발생하는, 그리고 &lt;code&gt;trigger_error&lt;/code&gt;를 호출하는)는 &lt;code&gt;try/catch&lt;/code&gt;로 잡을 수 없습니다.&lt;/li&gt;&lt;li&gt;비슷하게 예외는 &lt;code&gt;set_error_handler&lt;/code&gt;로 설치된 오류 핸들러를 타지 않습니다.&lt;/li&gt;&lt;li&gt;대신 처리하지 못한 예외를 위해 &lt;code&gt;set_exception_handler&lt;/code&gt;가 따로 있습니다. 왜냐하면 프로그램의 진입점부터 &lt;code&gt;try&lt;/code&gt; 블록으로 감싸는 것은 &lt;code&gt;mod_php&lt;/code&gt; 모델에서는 불가능하기 때문입니다.&lt;/li&gt;&lt;li&gt;치명적 오류 (예를 들어 &lt;code&gt;new ClassDoesntExist()&lt;/code&gt;)는 어떤 것에도 잡히지 않습니다. &lt;em&gt;수많은&lt;/em&gt; 비교적 무해한 문제들도 치명적인 오류를 발생시키며 의문스러운 이유로 프로그램을 종료시켜 버립니다. 종료 함수는 계속 돌지만 스택 추적을 할 수가 없으며 (최상단에서 돌기 때문) 프로그램이 제대로 종료되었는지 오류로 인해 종료된 건지 쉽게 알 수가 없습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;finally&lt;/code&gt; 구문이 없습니다. 따라서 래퍼 코드(핸들러 설정 - 코드 실행 - 핸들러 해제나 몽키패칭 - 테스트 수행 - 몽키패치 해제)를 작성하기가 귀찮고 어렵습니다. 객체지향이나 예외처리의 많은 부분을 Java에서 가져왔음에도 불구하고 이것은 &lt;a href="https://bugs.php.net/bug.php?id=32100"&gt;의도적&lt;/a&gt;입니다. 왜냐 하면 "PHP의 문맥에서는 별로 말이 안 된다."라나요. 엉?&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;함수&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;함수 호출은 &lt;a href="http://www.phpwtf.org/php-function-calls-have-quite-some-overhead"&gt;비용이 많이 듭니다&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;몇몇 내장된 함수들은 참조를 반환하는 함수와 &lt;a href="http://www.phpwtf.org/php-function-calls-returning-references"&gt;이상하게 동작&lt;/a&gt;합니다.&lt;/li&gt;&lt;li&gt;앞서 언급했던 수많은 '함수가 아니지만 함수처럼 보이는 구문'들은 함수와 상호작용하는 것들과는 어울리지 못합니다.&lt;/li&gt;&lt;li&gt;함수 인자들은 실제로는 정적 타이핑과 다름없는 "형 힌트"라는 것을 가지고 있습니다. 모든 내장 함수들이 이런 방식의 타이핑을 사용하는데도 형 힌트로 &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;object&lt;/code&gt;나 기타 "핵심" 자료형을 줄 수가 없습니다. 아마도 PHP에 &lt;code&gt;int&lt;/code&gt; 같은 것이 것이 없기 때문일 겁니다. (앞에서 이야기한 &lt;code&gt;(int)&lt;/code&gt;를 보세요.) 내장 함수에서 엄청나게 쓰이는 &lt;code&gt;mixed&lt;/code&gt;, &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;callback&lt;/code&gt; 같은 &lt;a href="http://www.php.net/manual/en/language.pseudo-types.php#language.types.mixed"&gt;유사 형&lt;/a&gt;들도 마찬가지입니다.&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;그 결과,&lt;/p&gt;&lt;pre&gt;&lt;code&gt;function foo(string $s) {}&lt;br /&gt;&lt;br /&gt;foo("hello world");&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;는 이런 에러를 발생합니다.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;PHP Catchable fatal error: Argument 1 passed to foo() must be an instance of string, string given, called in...&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;아마 "형 힌트"라는 것이 애시당초 존재할 필요가 없다는 걸 알아차리실 지도 모르겠습니다. &lt;code&gt;string&lt;/code&gt; 같은 클래스는 없죠. 만약 &lt;code&gt;ReflectionParameter::getClass()&lt;/code&gt;로 형 힌트를 동적으로 검사하려고 하면 그런 클래스는 존재하지 않는다고 할 겁니다. 따라서 실제 클래스 명을 받아오는 것은 불가능하게 되죠.&lt;/li&gt;&lt;li&gt;함수의 반환값은 힌팅할 수 없습니다.&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;현재 함수의 인자를 다른 함수로 넘기는(디스패치, 그렇게 보기 드문 것은 아닙니다) 것은 &lt;code&gt;call_user_func_array('other_function', func_get_args())&lt;/code&gt;를 통해 할 수 있습니다. 하지만 &lt;code&gt;func_get_args&lt;/code&gt; 함수는 실행 중에 이것이 함수 파라미터가 될 수 없다면서 치명적인 오류를 뱉습니다. 이게 대체 무슨 에러랍니까? (PHP 5.3에서 고쳐짐)&lt;/li&gt;&lt;li&gt;클로져는 모든 변수가 에워싸지도록(close over) 요구합니다. 왜 인터프리터가 알아서 할 수 없는 걸까요? 아마 모든 기능들의 방해물이 아닐까 합니다. (네, 그건 다른 곳에서 명시적으로 이야기하지 않는 한 변수를 쓰는 것은 만드는 것이기 때문입니다.)&lt;/li&gt;&lt;li&gt;에워싸진 변수들은 다른 함수 인자들과 같은 형식으로 "넘겨집니다". 배열, 문자열, 기타 등등 역시 "by value"로 넘겨집니다. &lt;code&gt;&amp;&lt;/code&gt;를 쓰지 않으면요.&lt;/li&gt;&lt;li&gt;에워싸진 변수들은 실질적으로 자동으로 넘겨진 인자들이고 네스팅된 범위라는 것이 없기 때문에 클로져는 private 메소드에 접근할 수 없습니다. 심지어 클래스 내에 정의되었을지라도요. (PHP 5.4에서 고쳐졌을까요? 모르겠습니다.)&lt;/li&gt;&lt;li&gt;함수 인자에 변수명을 명시적으로 줄 수 없습니다. 제안된 적은 있으나 "코드가 지저분해진다"는 이유로 &lt;a href="http://www.php.net/~derick/meeting-notes.html#named-parameters"&gt;거부당했습니다&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;기본값을 가진 함수 인자들은 기본값이 없는 함수 인자 앞에 올 수 있습니다. 심지어 문서에서조차 이것이 이상하고 쓸데없다는 점을 인정하고 있습니다. (그럼 대체 왜 허용하는 건데?)&lt;/li&gt;&lt;li&gt;함수로 전달된 잉여 인자들은 무시됩니다. (내장 함수들은 제외하고요. 내장 함수들은 에러를 냅니다.) 없는 인자는 널로 취급됩니다.&lt;/li&gt;&lt;li&gt;가변 인자 함수를 작성하게 위해서는 &lt;code&gt;func_num_args&lt;/code&gt;, &lt;code&gt;func_get_arg&lt;/code&gt;, &lt;code&gt;func_get_args&lt;/code&gt; 같은 함수를 쓰면서 법석을 떨어야 합니다. 그런 것을 위한 문법은 존재하지 않습니다.&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;객체지향&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;PHP의 함수와 관련된 부분은 C와 비슷하게 설계되었고 객체(ㅋㅋㅋ)와 관련된 부분은 자바와 비슷하게 설계되었습니다. 저는 이것이 얼마나 거슬리는지 이루 말할 수 없습니다. 전역 함수 중에서 대문자가 들어간 것은 찾지 못했습니다만 &lt;a href="http://www.php.net/manual/en/class.reflectionfunction.php"&gt;중요한 내장 클래스&lt;/a&gt;는 camelCase 메소드명을 쓰고 있으며 Java 스타일의 &lt;code&gt;getFoo&lt;/code&gt; 같은 접근자를 사용합니다. 이거 동적 언어 맞죠? Perl, Python, Ruby 모두 코드를 통한 "프로퍼티" 접근 같은 개념을 가지고 있지만 PHP 혼자 &lt;code&gt;__get&lt;/code&gt; 같은 것들을 가지고 있습니다. 클래스 체계는 좀 더 저수준의 Java 언어를 본따서 설계되었고 동시대의 다른 언어들에 비해서 자연스럽고 의도적으로 &lt;em&gt;더욱 제한되어&lt;/em&gt; 있습니다. 저는 당황했습니다.&lt;/li&gt;&lt;li&gt;클래스는 객체가 아닙니다. 메타프로그래밍을 위해선 마치 함수처럼 문자열로 된 이름을 통해서 참조해야 합니다.&lt;/li&gt;&lt;li&gt;내장형 자료형은 객체가 아니며 (Perl과는 다르게) 객체처럼 보이게 할 수 있는 방법도 없습니다.&lt;/li&gt;&lt;li&gt;클래스가 나중에 추가된 기능이고 언어의 대부분은 함수 또는 함수스러운 문법으로 구현되어 있음에도 불구하고 &lt;code&gt;instanceof&lt;/code&gt;는 연산자입니다. 자바의 영향일까요? 클래스가 일급객체가 아니다? (저는 잘 모르겠군요.)&lt;ul&gt;&lt;li&gt;하지만 &lt;code&gt;is_a&lt;/code&gt; 함수는 있습니다. 그 객체가 주어진 클래스 이름 문자열과 같은지 확인하기 위한 추가적인 인자와 함께요.&lt;/li&gt;&lt;li&gt;&lt;code&gt;is_subclass_of&lt;/code&gt;와 마찬가지로 &lt;code&gt;get_class&lt;/code&gt;는 함수입니다. &lt;code&gt;typeof&lt;/code&gt;는 없습니다.&lt;/li&gt;&lt;li&gt;그리고 이것은 내장된 자료형과는 동작하지 않습니다. (다시 한번, &lt;code&gt;int&lt;/code&gt; 같은 건 애시당초 없는 겁니다) 이런 경우를 위해, &lt;code&gt;is_int&lt;/code&gt; 같은 것들이 필요해집니다.&lt;/li&gt;&lt;li&gt;그리고 오른쪽 항은 변수나 문자열 리터럴이 되어야 합니다. 표현식은 올 수 없습니다. 만약 그렇게 하면... 파싱 오류가 발생합니다.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;clone&lt;/code&gt;이 연산자라고?!&lt;/li&gt;&lt;li&gt;OO 설계는 Java와 Perl의 이상한 짬뽕입니다.&lt;/li&gt;&lt;li&gt;객체 어트리뷰트는 &lt;code&gt;$obj-&amp;gt;$foo&lt;/code&gt;이지만 클래스 어트리뷰터는 &lt;code&gt;$obj::foo&lt;/code&gt;. 이런 짓을 하는 다른 언어에 대해서는 들어본 적이 있고 이게 얼마나 유용할지도 모르겠습니다.&lt;/li&gt;&lt;li&gt;또한 인스턴스의 메소드는 여전히 정적(&lt;code&gt;Class::method()&lt;/code&gt;)으로 호출할 수 있습니다. 만약 다른 메소드에서 호출된다면 현재 &lt;code&gt;$this&lt;/code&gt;를 가진 일반적인 메소드 호출이 되어야 한다고 생각합니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;new&lt;/code&gt;, &lt;code&gt;private&lt;/code&gt;, &lt;code&gt;public&lt;/code&gt;, &lt;code&gt;protected&lt;/code&gt;, &lt;code&gt;static&lt;/code&gt; 등등등... Java 개발자들을 이기기 위한 겁니까? 개인적인 취향이라는 건 알고 있지만 왜 이런 것들이 동적 언어에서 필요한 건지 잘 모르겠습니다. C++에서 이런 것들은 대부분 컴파일이나 컴파일 타임에서 이름을 찾는 것에 관련되어 있잖아요.&lt;/li&gt;&lt;li&gt;하위 클래스는 private 메소드를 오버라이딩할 수 없습니다. 하위 클래스에서 오버라이딩된 public 메소드는 볼 수조차 없으며 혼자서 호출될 수도 없으며 상위 클래스의 private 메소드를 호출할 수도 없습니다.&lt;/li&gt;&lt;li&gt;메소드는 예를 들어 "list"라는 이름을 가질 수 없습니다. 왜냐 하면 &lt;code&gt;list()&lt;/code&gt;는 특수한 문법이라 (함수가 아니라) 파서가 혼란스러워 하기 때문입니다. 이게 모호할 이유가 전혀 없는데, 클래스에 몽키패칭은 잘 됩니다. (&lt;code&gt;$foo-&amp;gt;list()&lt;/code&gt;는 구문 오류가 아닙니다.)&lt;/li&gt;&lt;li&gt;생성자의 인자를 평가하는 과정에서 예외가 발생하면 (예를 들어 &lt;code&gt;new Foo(bar())&lt;/code&gt;에서 &lt;code&gt;bar()&lt;/code&gt;가 예외를 생성할 경우) 생성자가 호출되지 않고 소멸자가 호출됩니다. (PHP 5.3에서 고쳐짐)&lt;/li&gt;&lt;li&gt;&lt;code&gt;__autoload&lt;/code&gt;나 소멸자 안에서 발생한 예외는 치명적 오류를 발생시킵니다.&lt;/li&gt;&lt;li&gt;사실 생성자와 소멸자는 없습니다. &lt;code&gt;__construct&lt;/code&gt;는 Python의 &lt;code&gt;__init__&lt;/code&gt;처럼 초기화 메소드(initializer)입니다. 클래스에 호출하여 메모리를 할당하고 객체를 생성하는 방법은 없습니다.&lt;/li&gt;&lt;li&gt;기본 초기화 메소드는 없습니다. 만약 상위 클래스가 &lt;code&gt;__construct&lt;/code&gt;를 정의하고 있지 않은 경우에 &lt;code&gt;parent::__construct()&lt;/code&gt;를 호출하면 치명적 오류가 발생합니다.&lt;/li&gt;&lt;li&gt;객체지향은 보통 언어의 일부분 (예를 들어 &lt;code&gt;for...as&lt;/code&gt;)이 알아들을 수 있는 형태의 반복자 인터페이스를 제공하는데 어떤 내장 형식 (예를 들면 배열)도 이를 구현하고 있지 않습니다. 만약 배열의 반복자를 원한다면 &lt;code&gt;ArrayIterator&lt;/code&gt;로 감싸야 합니다. 잇거나 나누거나 반복자를 일급객체로 사용하는 기본적 방법은 없습니다.&lt;/li&gt;&lt;li&gt;클래스를 문자열로 변환하거나 변환 시 어떻게 행동할지를 재정의할 수 있으나 숫자나 기타 기본 형식에 대한 변환은 지원하지 않습니다.&lt;/li&gt;&lt;li&gt;문자열, 숫자, 배열은 모두 문자열 변환 기능이 있습니다. 언어 전체가 이것에 크게 의존하고 있습니다. 함수와 클래스는 문자열입니다. 만약 &lt;code&gt;__toString&lt;/code&gt;가 정의되어 있지 않다면 내장된 혹은 사용자 정의된 객체(심지어 클로져까지)를 문자열로 변환할 시 오류가 발생하게 됩니다. 심지어 &lt;code&gt;echo&lt;/code&gt;까지 잠재적으로 오류를 낼 가능성을 가지고 있습니다.&lt;/li&gt;&lt;li&gt;비교 연산자의 오버로딩을 지원하지 않습니다.&lt;/li&gt;&lt;li&gt;인스턴스 메소드 안에 정의된 정적 변수는 전역입니다. 모든 인스턴스가 한 변수를 공유합니다.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;표준 라이브러리&lt;/h3&gt;&lt;p&gt;예를 들어 Perl은 "약간의 조립 과정이 필요"입니다. Python은 "배터리 포함"입니다. PHP는 캐나다산 키친 싱크대입니다. &lt;a href="http://mcguirehimself.com/?p=4146"&gt;게다가 양쪽 수도꼭지에 전부 C라고 적혀 있습니다&lt;/a&gt;.&lt;/p&gt;&lt;h4&gt;일반&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;모듈 시스템이 없습니다. PHP 확장을 컴파일할 수 있지만 php.ini에 등록해야만 불러올 수 있습니다. 그리고 당신이 할 수 있는건 확장을 켜고 (내용을 전역 네임스페이스에 주입하고) 끄는 것 뿐입니다.&lt;/li&gt;&lt;li&gt;네임스페이스가 최근에 추가된 기능이긴 하지만 표준 라이브러리는 전혀 분류되지 않았습니다. 전역 이름공간에 수천개의 함수들이 있습니다.&lt;/li&gt;&lt;li&gt;라이브러리 덩어리들은 전혀 일관성을 갖추고 있지 않습니다.&lt;ul&gt;&lt;li&gt;밑줄의 여부: &lt;code&gt;strpos&lt;/code&gt; / &lt;code&gt;str_rot13&lt;/code&gt;, &lt;code&gt;php_uname&lt;/code&gt; / &lt;code&gt;phpversion&lt;/code&gt;, &lt;code&gt;base64_encode&lt;/code&gt; / &lt;code&gt;urlencode&lt;/code&gt;, &lt;code&gt;gettype&lt;/code&gt; / &lt;code&gt;get_class&lt;/code&gt;&lt;/li&gt;&lt;li&gt;"to" vs 2: &lt;code&gt;ascii2ebcdic&lt;/code&gt;, &lt;code&gt;bin2hex&lt;/code&gt;, &lt;code&gt;deg2rad&lt;/code&gt;, &lt;code&gt;strtolower&lt;/code&gt;, &lt;code&gt;strtotime&lt;/code&gt;&lt;/li&gt;&lt;li&gt;목적어+동사 vs 동사+목적어: &lt;code&gt;base64_encode&lt;/code&gt;, &lt;code&gt;str_shuffle&lt;/code&gt;, &lt;code&gt;var_dump&lt;/code&gt; vs &lt;code&gt;create_function&lt;/code&gt;, &lt;code&gt;recode_string&lt;/code&gt;&lt;/li&gt;&lt;li&gt;인자 순서: &lt;code&gt;array_filter($input, $callback)&lt;/code&gt; vs &lt;code&gt;array_map($callbasck, $input)&lt;/code&gt;, &lt;code&gt;strpos($haystack, $needle)&lt;/code&gt; vs &lt;code&gt;array_search($needle, $haystack)&lt;/code&gt;&lt;/li&gt;&lt;li&gt;접두사 혼란: &lt;code&gt;usleep&lt;/code&gt; vs &lt;code&gt;microtime&lt;/code&gt;&lt;/li&gt;&lt;li&gt;대소문자를 구분하지 않는 (문자열 관련) 함수들은 이름에 &lt;code&gt;i&lt;/code&gt;가 붙냐 안 붙냐에 따라 달라집니다.&lt;/li&gt;&lt;li&gt;배열 관련 함수의 절반 가까이가 &lt;code&gt;array_&lt;/code&gt;로 시작하지만 나머지는 그렇지 않습니다.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;키친 싱크대. 라이브러리는 이런 것들로 구성되어 있습니다.&lt;ul&gt;&lt;li&gt;ImageMagick 바인딩, GraphicsMagick(ImageMagick의 fork) 바인딩, 그리고 EXIF 데이터를 조회하기 위한 여러가지 함수들 (이미 ImageMagick에서 제공하고 있는 기능인데도)&lt;/li&gt;&lt;li&gt;bbcode (특정 게시판 시스템에서 사용되는 종류의 마크업) 를 파싱하는 함수들.&lt;/li&gt;&lt;li&gt;지나치게 많은 XML 패키지. &lt;code&gt;DOM&lt;/code&gt; (OO), &lt;code&gt;DOM XML&lt;/code&gt; (아님), &lt;code&gt;libxml&lt;/code&gt;, &lt;code&gt;SimpleXML&lt;/code&gt;, "XML Parser", &lt;code&gt;XMLReader&lt;/code&gt;/&lt;code&gt;XMLWriter&lt;/code&gt;, 그리고 기억할 수 없는 수많은 약어들. 분명히 저것들 사이에 차이점은 있을거고 어떤게 어떤건지 알아보는 것은 자유겠죠.&lt;/li&gt;&lt;li&gt;두개의 특정 신용 카드 처리기를 위한 바인딩. SPPLUS와 MCVE. 뭐라고?&lt;/li&gt;&lt;li&gt;MySQL 데이터베이스에 접속하기 위한 세 가지 방법: &lt;code&gt;mysql&lt;/code&gt;, &lt;code&gt;mysqli&lt;/code&gt; 그리고 &lt;code&gt;PDO&lt;/code&gt; 추상화 뭐시기.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;C의 영향&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;함수 인자로 넘기는 "반환값" 파라미터. PHP도 임의의 해시를 반환하거나 여러 개의 인자를 한꺼번에 반환할 수가 있잖아요.&lt;/li&gt;&lt;li&gt;특정 서브시스템을 위한, 가장 최근에 발생한 에러를 가져오기 위한 십여 개의 함수들. (아래를 보세요) 예외는 8년 전에도 PHP에 있었습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;mysql_real_escape_string&lt;/code&gt; 같은 함수들. 심지어 고장난 &lt;code&gt;mysql_escape_string&lt;/code&gt;와도 같은 인자를 갖는데, 이는 단지 MySQL C API의 일부이기 떄문입니다.&lt;/li&gt;&lt;li&gt;i&gt;부분적인 기능을 위한 전역적 동작. (마치 MySQL처럼) 여러 개의 MySQL에 접속하여 동시에 쓰기 위해서는 매 함수 호출시마다 MySQL 핸들을 함수로 전달해야 합니다.&lt;/li&gt;&lt;li&gt;그리고 래퍼는 정말, 정말로 얇습니다. &lt;code&gt;dba_firstkey&lt;/code&gt; 없이 &lt;code&gt;dba_nextkey&lt;/code&gt;를 호출하면 segfault로 죽습니다.&lt;/li&gt;&lt;li&gt;C의 비슷한 문자 처리 함수들과 거의 일치하는 &lt;code&gt;ctype_*&lt;/code&gt; 함수들(예를 들면 &lt;code&gt;ctype_alnum&lt;/code&gt;)이 있습니다. &lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;제너릭&lt;/h4&gt;&lt;p&gt;그런거 없습니다. 만약 두가지의 미묘하게 다른 기능이 필요하다면 PHP에는 두 개의 함수가 있습니다.&lt;/p&gt;&lt;p&gt;배열을 어떻게 반대 방향으로 정렬하나요? Perl이라면 &lt;code&gt;sort { $b &lt;=&gt; $a }&lt;/code&gt;로 하면 되겠죠. Python은? &lt;code&gt;.sort(reverse=True)&lt;/code&gt;로. PHP에서는 &lt;code&gt;rsort()&lt;/code&gt;라는 별개의 함수가 있습니다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;C 함수의 오류를 확인하기 위한 함수들: &lt;code&gt;curl_error&lt;/code&gt;, &lt;code&gt;json_last_error&lt;/code&gt;, &lt;code&gt;openssl_error_string&lt;/code&gt;,  &lt;code&gt;imap_errors&lt;/code&gt;, &lt;code&gt;mysql_error&lt;/code&gt;, &lt;code&gt;xml_get_error_code&lt;/code&gt;, &lt;code&gt;bzerror&lt;/code&gt;, &lt;code&gt;date_get_last_errors&lt;/code&gt;. 그 외엔?&lt;/li&gt;&lt;li&gt;정렬 함수들: &lt;code&gt;array_multisort&lt;/code&gt;, &lt;code&gt;arsort&lt;/code&gt;, &lt;code&gt;asort&lt;/code&gt;, &lt;code&gt;ksort&lt;/code&gt;, &lt;code&gt;krsort&lt;/code&gt;, &lt;code&gt;natsort&lt;/code&gt;, &lt;code&gt;natcasesort&lt;/code&gt;, &lt;code&gt;sort&lt;/code&gt;, &lt;code&gt;uasort&lt;/code&gt;, &lt;code&gt;uksort&lt;/code&gt;, &lt;code&gt;usort&lt;/code&gt;&lt;/li&gt;&lt;li&gt;문자열 검색 함수들: &lt;code&gt;ereg&lt;/code&gt;, &lt;code&gt;eregi&lt;/code&gt;, &lt;code&gt;mb_ereg&lt;/code&gt;, &lt;code&gt;mb_eregi&lt;/code&gt;, &lt;code&gt;preg_match&lt;/code&gt;, &lt;code&gt;strstr&lt;/code&gt;, &lt;code&gt;strchr&lt;/code&gt;, &lt;code&gt;strichr&lt;/code&gt;, &lt;code&gt;strrchr&lt;/code&gt;, &lt;code&gt;strpos&lt;/code&gt;, &lt;code&gt;stripos&lt;/code&gt;, &lt;code&gt;strrpos&lt;/code&gt;, &lt;code&gt;strripos&lt;/code&gt;, &lt;code&gt;mb_strpos&lt;/code&gt;, &lt;code&gt;mb_strrpos&lt;/code&gt; + 기타 등등 바리에이션&lt;/li&gt;&lt;li&gt;별 도움은 안 되는 이름만 다른 함수들도 많습니다. &lt;code&gt;strstr&lt;/code&gt; / &lt;code&gt;strchr&lt;/code&gt;, &lt;code&gt;is_int&lt;/code&gt; / &lt;code&gt;is_integer&lt;/code&gt; / &lt;code&gt;is_long&lt;/code&gt;, &lt;code&gt;is_float&lt;/code&gt; / &lt;code&gt;is_double&lt;/code&gt;, &lt;code&gt;pos&lt;/code&gt; / &lt;code&gt;current&lt;/code&gt;, &lt;code&gt;sizeof&lt;/code&gt; / &lt;code&gt;count&lt;/code&gt;, &lt;code&gt;chop&lt;/code&gt; / &lt;code&gt;rtrim&lt;/code&gt;, &lt;code&gt;implode&lt;/code&gt; / &lt;code&gt;join&lt;/code&gt;, &lt;code&gt;die&lt;/code&gt; / &lt;code&gt;exit&lt;/code&gt;, &lt;code&gt;trigger_error&lt;/code&gt; / &lt;code&gt;user_error&lt;/code&gt; …&lt;/li&gt;&lt;li&gt;&lt;code&gt;scandir&lt;/code&gt;는 주어진 디렉토리의 파일 목록을 반환합니다. 디렉토리의 순서대로 파일 목록을 반환하는 것이 아니라 파일을 알파벳 순서로 정렬해서 반환합니다. 그리고 역순으로 정렬하기 위해 추가적인 인자를 받습니다. 아마 정렬 함수로는 충분하지 않았나 봅니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;str_split&lt;/code&gt; 함수는 문자열을 일정한 길이의 덩어리들로 나눕니다. &lt;code&gt;chunk_split&lt;/code&gt; 함수는 문자열을 일정한 길이의 덩어리로 나누고 구분자로 합칩니다.&lt;/li&gt;&lt;li&gt;압축 파일에서 읽기 위해서는 포맷에 따라서 서로 다른 함수를 써야 합니다. bzip2, LZF, phar, rar, zip, gzip/zlib를 위한 총 6개의 서로 다른 API들이 있습니다.&lt;/li&gt;&lt;li&gt;배열을 인자로 해서 함수를 호출하는 방법이 너무 이상하기 때문에 (&lt;code&gt;call_user_func_array&lt;/code&gt;), &lt;code&gt;printf&lt;/code&gt; / &lt;code&gt;vprintf&lt;/code&gt;나 &lt;code&gt;sprint&lt;/code&gt; / &lt;code&gt;vsprintf&lt;/code&gt; 같이 나누는 경우가 있습니다. 전자는 인자들을 받고 후자는 인자들로 이루어진 배열을 받습니다.&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;텍스트&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;preg_replace&lt;/code&gt;에 &lt;code&gt;/e&lt;/code&gt; (eval) 플래그를 걸면 문자열을 정규식 규칙에 맞게 변환하고, &lt;em&gt;그것을 평가합니다&lt;/em&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;strtok&lt;/code&gt;는 해당하는 C 함수와 똑같이 만들어졌습니다. 이것은 여러 의미에서 나쁜 아이디어인데, PHP로는 배열을 쉽게 반환할 수 있다는 점을 명심하세요. (C에서는 그게 어렵죠) 그리고 &lt;code&gt;strtok(3)&lt;/code&gt;이 쓰는 핵(문자열 내용을 그 자리에서 고치기)은 여기서 사용되지 않습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;parse_str&lt;/code&gt;는 &lt;em&gt;쿼리&lt;/em&gt; 문자열을 파싱합니다. 이름만 가지고는 그걸 전혀 알 수 없죠. 그리고 이것은 따로 배열 인자를 주지 않으면 &lt;code&gt;register_globals&lt;/code&gt;처럼 동작하여 쿼리 내용을 전역 범위에 변수로 넣어버립니다. (물론 아무것도 반환하지 않습니다.)&lt;/li&gt;&lt;li&gt;&lt;code&gt;explode&lt;/code&gt; 함수에 빈 구분자를 줄 수 없습니다. 다른 대부분의 문자열 나누기 함수는 구분자를 주지 않으면 문자 단위로 나눈다는 것으로 간주합니다. 대신 PHP에선 완전히 다른 함수가 이 역할을 하는데, 이름도 혼란스럽게 &lt;code&gt;str_split&lt;/code&gt;입니다. 그리고 "문자열을 배열로 변환하는데 사용된다"고 설명되어 있습니다.&lt;/li&gt;&lt;li&gt;날짜를 포매팅할 떄 C API와 똑같으며 시스템 날짜 설정을 따르는 &lt;code&gt;strftime&lt;/code&gt;라는 함수가 있습니다. 완전히 다른 사용법의 &lt;code&gt;date&lt;/code&gt;라는 함수도 있는데 영어로만 동작합니다.&lt;/li&gt;&lt;li&gt;“&lt;code&gt;&lt;a href="http://php.net/manual/en/function.gzgetss.php"&gt;gzgetss&lt;/a&gt;&lt;/code&gt; — gz 파일 포인터에서 한 줄을 읽어서 HTML 태그를 벗겨냅니다.” 저는 이런 함수가 존재하는 의미가 뭔지 궁금해 미칠 것 같습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;mbstring&lt;/code&gt;&lt;ul&gt;&lt;li&gt;캐릭터 셋이 문제가 되는 경우에 사용하는 "멀티바이트"에 관한 함수들입니다.&lt;/li&gt;&lt;li&gt; 일반 문자열처럼 동작하지만 하나의 전역적인 "기본" 문자 셋 설정이 있습니다. 몇몇 함수들은 문자셋을 직접 줄 수 있지만 모든 인자와 반환값에 적용이 됩니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;ereg_*&lt;/code&gt; 함수를 제공하지만 더 이상 지원하지 않습니다(deprecated). 전용 플래그를 걸어줘야 UTF-8을 인식할 수 있긴 하지만 &lt;code&gt;preg_*&lt;/code&gt;는 운이 없는 편이죠.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;시스템과 반영(reflection)&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;전반적으로 텍스트와 변수의 경계를 모호하게 만드는 엄청나게 많은 함수들이 있습니다. &lt;code&gt;compact&lt;/code&gt;와 &lt;code&gt;extract&lt;/code&gt;는 빙산의 일각일 뿐이죠.&lt;/li&gt;&lt;li&gt;실제로 PHP에서 '동적'이라는 개념을 위한 몇 가지 방식이 있습니다. 하지만 얼핏 보면 중요한 차이점이나 상대적 이득은 없는 것 같습니다. &lt;code&gt;classkit&lt;/code&gt;로 사용자가 정의한 클래스를 바꿀 수 있습니다. &lt;code&gt;runkit&lt;/code&gt;로 그것을 대체하고 사용자가 정의한 모든 것을 바꿀 수 있습니다. &lt;code&gt;Reflection*&lt;/code&gt; 클래스로 언어의 거의 모든 부분을 반영할 수 있습니다. 하지만 이미 함수나 클래스의 프로퍼티에 접근하기 위한 수많은 개별 함수들이 있습니다. 이것들은 서브시스템에 독립적일까요, 관계가 있을까요, 아니면 단지 잉여일 뿐일까요?&lt;/li&gt;&lt;li&gt;&lt;code&gt;get_class($obj)&lt;/code&gt;는 객체의 클래스명을 반환합니다. &lt;code&gt;get_class()&lt;/code&gt;는 현재 호출되는 함수의 클래스명을 반환합니다. 하나의 함수가 두개의 완전히 다른 역할을 한다는 점은 차치하구요, 그럼 &lt;code&gt;get_class(null)&lt;/code&gt;은? 후자의 역할을 합니다. 그러니 임의의 값을 믿으면 안 됩니다. 놀라셨죠?&lt;/li&gt;&lt;li&gt;&lt;code&gt;stream_*&lt;/code&gt; 클래스로 파일스러운 내장 객체들과 비슷한 스트림 객체를 구현할 수 있습니다. 하지만 "tell"은 &lt;a href="https://bugs.php.net/bug.php?id=30157"&gt;내부적인 문제 때문에 구현할 수 없습니다&lt;/a&gt;. (게다가 이 시스템에 연계된 함수들은 &lt;a href="http://www.php.net/manual/en/book.stream.php"&gt;엄청나게 많습니다&lt;/a&gt;.)&lt;/li&gt;&lt;li&gt;&lt;code&gt;register_tick_function&lt;/code&gt;는 클로져를 받아들일 수 있습니다. &lt;code&gt;unregister_tick_function&lt;/code&gt;는 받아들이지 못하는데, 대신 클로져 객체를 문자열로 변환할 수 없다는 에러를 발생할 겁니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;php_uname&lt;/code&gt; 함수는 현재 OS에 대해 출력합니다. PHP가 현재 작동중인 OS를 알 수 없으면 PHP를 &lt;em&gt;컴파일&lt;/em&gt;한 시스템의 OS 정보를 알려 줍니다. 그리고 그 두가지 상황을 구분할 수 있는 방법은 없습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;fork&lt;/code&gt;와 &lt;code&gt;exec&lt;/code&gt;는 내장 함수가 아닙니다. 이것들은 pcntl 확장에 들어 있으며 기본적으로 포함되어 있지 않습니다. &lt;code&gt;popen&lt;/code&gt;는 pid를 제공하지 않습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;session_decode&lt;/code&gt;는 임의의 PHP 세션 문자열을 읽을 때 사용합니다. 하지만 이미 활동 중인 세션이 있을 때만 동작합니다. 그리고 결과값을 반환하는 것이 아니라 &lt;code&gt;$_SESSION&lt;/code&gt;에다가 때려박습니다.&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;기타&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;curl_multi_exec&lt;/code&gt;는 실행 중 오류가 발생하면 &lt;code&gt;curl_errno&lt;/code&gt;를 바꾸지 않습니다. 하지만 &lt;code&gt;curl_error&lt;/code&gt;는 건드립니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;mktime&lt;/code&gt;의 인자 순서는 이렇습니다: 시간, 분, 초, 월, 일, 년.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;데이터 조작&lt;/h3&gt;&lt;p&gt;프로그램은 데이터를 먹고 더 많은 데이터를 뱉어내는 것 이상도 아닙니다. awk부터 시작해서 Prolog, C까지 좋은 언어들은 조작할 데이터의 유형에 따라 설계됩니다. 만약 언어가 데이터를 만질 수 없다면 아무것도 할 수 없다는 겁니다.&lt;/p&gt;&lt;h4&gt;숫자&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;32비트 시스템에서 정수는 32비트의 부호 있는 정수입니다. PHP의 동시대 언어들과 달리 자동 bigint 변환이 없습니다. 그래서 CPU 아키텍쳐에 따라 수학이 조금씩 다를 가능성도 있습니다. 큰 정수를 다룰 경우에는 GMP나 BC 래퍼 확장을 이용하는 수밖엔 없습니다. (개발자들은 &lt;a href="http://www.php.net/~derick/meeting-notes.html#add-a-64bit-integer"&gt;완전히 별개의 64비트 전용 자료형을 제안했습니다&lt;/a&gt;. 이건 미쳤어요!)&lt;/li&gt;&lt;li&gt;PHP는 &lt;code&gt;0&lt;/code&gt;로 시작하는 8진수 문법을 지원합니다. 그러므로 예를 들어 &lt;code&gt;012&lt;/code&gt;는 숫자 10이 되겠지요. 하지만 &lt;code&gt;08&lt;/code&gt;는 숫자 0이 됩니다. &lt;code&gt;8&lt;/code&gt;이나 &lt;code&gt;9&lt;/code&gt;, 그리고 그 뒤에 오는 숫자는 전부 사라집니다. &lt;code&gt;01c&lt;/code&gt;는 문법 오류입니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;pi&lt;/code&gt;는 함수입니다. &lt;code&gt;M_PI&lt;/code&gt;는 상수입니다.&lt;/li&gt;&lt;li&gt;&lt;a href="https://bugs.php.net/bug.php?id=13756"&gt;지수승 연산자&lt;/a&gt;가 없습니다. &lt;code&gt;pow&lt;/code&gt; 함수만 있습니다.&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;텍스트&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;유니코드 지원이 없습니다. ASCII만 신뢰성있게 동작합니다. 정말로요. 위에서 언급한 대로 &lt;code&gt;mbstring&lt;/code&gt; 확장이 있는데 엉망입니다.&lt;/li&gt;&lt;li&gt;즉 기본 문자열 처리 함수에 UTF-8 문자열을 집어넣으면 깨질 수도 있다는 각오를 해야 합니다.&lt;/li&gt;&lt;li&gt;비슷하게 예를 들어 ASCII 밖의 문자의 대소문자 비교에 대한 고려도 없습니다. 대소문자를 구분하지 않는 함수들이 넘쳐나는데도 불구하고 &lt;code&gt;é&lt;/code&gt;와 &lt;code&gt;É&lt;/code&gt;를 비교하지 못합니다.&lt;/li&gt;&lt;li&gt;변수 보간(variable interpolation)에서 키를 따옴표로 감쌀 수 없습니다. 예를 들어 &lt;code&gt;"$foo['key']"&lt;/code&gt;는 문법 오류를 냅니다. 따옴표를 없애거나 (그럴 경우 경고문이 뜹니다!) &lt;code&gt;${...}&lt;/code&gt;/&lt;code&gt;{$...}&lt;/code&gt;를 쓸 수밖에 없습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;"${foo[0]}"&lt;/code&gt;는 괜찮아요. &lt;code&gt;"${foo[0][0]}"&lt;/code&gt;는 문법 오류를 냅니다. &lt;code&gt;$&lt;/code&gt;를 안쪽에 넣으면 둘 다 문제가 없습니다. Perl 문법의 열화 카피인 걸까요? (거기다가 의미는 완전히 다른)&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;배열&lt;/h4&gt;&lt;p&gt;아놔....&lt;/p&gt;&lt;ul&gt;&lt;li&gt;PHP의 배열은 리스트, 순서 있는 해쉬, 순서 있는 집합, 희소 리스트, 그리고 때때로 이것들을 이상하게 섞어놓은 것을 하나로 만든 것입니다. 이게 어떻게 동작할까요? 이걸 쓰면 메모리를 얼마나 먹을까요? 그걸 대체 누가 알까요? 어쨌든간에 다른 선택지는 없습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;=&amp;gt;&lt;/code&gt;는 연산자가 아닙니다. &lt;code&gt;array(...)&lt;/code&gt;나 &lt;code&gt;foreach&lt;/code&gt; 안에서만 존재하는 특별한 문법입니다.&lt;/li&gt;&lt;li&gt;원소값에 음수를 줄 수 없습니다. &lt;code&gt;-1&lt;/code&gt;는 &lt;code&gt;0&lt;/code&gt;처럼 유효한 키값입니다.&lt;/li&gt;&lt;li&gt;이것이 PHP의 유일한 자료 구조임에도 불구하고 단축 표현이 없습니다. 실은 &lt;code&gt;array(...)&lt;/code&gt;가 단축 표현이죠. (PHP 5.4에서 "리터럴"이 생겼습니다. &lt;code&gt;[...]&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;&lt;code&gt;=&amp;gt;&lt;/code&gt; 문법은 Perl에서 따 온 것입니다. Perl에서는 따옴표 없이 &lt;code&gt;foo =&amp;gt; 1&lt;/code&gt;처럼 쓸 수 있게 해 줍니다. (사실 말인데 왜 이게 Perl에 있는 걸까요. 그렇지 않으면 그건 그냥 콤마잖아요) PHP에서는 경고를 발생시키지 않고 이렇게 하는 것이 불가능합니다. 문자열 키에 따옴표를 쓰지 않고 해시를 만드는 걸 이런 식으로 할 수 있는 언어는 없습니다.&lt;/li&gt;&lt;li&gt;&lt;p&gt;배열 함수들은 배열과 해시, 이 둘이 섞인 경우 혼란스럽고 일관성 없는 동작을 합니다. 예를 들어 "배열의 차이를 계산하는" &lt;code&gt;array_diff&lt;/code&gt; 함수를 보시죠.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$first  = array("foo" =&gt; 123, "bar" =&gt; 456);&lt;br /&gt;$second = array("foo" =&gt; 456, "bar" =&gt; 123);&lt;br /&gt;echo var_dump(array_diff($first, $second));&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이 코드는 어떻게 동작할까요? 만약 &lt;code&gt;array_diff&lt;/code&gt; 함수가 인자를 해시로 인식한다면 분명히 이것들은 같은 키에 다른 값을 가지고 있으니 다르겠지요. 만약 리스트로 인식한다면 다를 겁니다. 배열의 순서가 다르거든요.&lt;/p&gt;&lt;p&gt;사실 &lt;code&gt;array_diff&lt;/code&gt; 함수는 이 둘을 같은 것으로 봅니다. 왜냐 하면 이것을 집합으로 인식하거든요. 값만 비교하고 순서는 무시해 버립니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;비슷한 맥락에서 &lt;code&gt;array_rand&lt;/code&gt;는 배열의 랜덤한 키값을 선택하는 이상한 동작을 합니다. 목록에서 뭔가를 선택할 일이 있을 때의 대부분의 공통적인 케이스에서 도움이 안 되죠.&lt;/li&gt;&lt;li&gt;&lt;p&gt;PHP 코드가 얼마나 키값의 순서를 보존하는데 의존하는지에도 불구하고&lt;/p&gt;&lt;pre&gt;&lt;code&gt;array("foo", "bar") != array("bar", "foo")&lt;br /&gt;array("foo" =&gt; 1, "bar" =&gt; 2) == array("bar" =&gt; 2, "foo" =&gt; 1)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;만약 배열이 섞여 있으면 어떤 일이 벌어지는지 파악하는 것은 독자들에게 맡깁니다. (전 몰라요.)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;array_fill&lt;/code&gt; 함수는 길이가 0인 배열을 만들 수 없습니다. 대신 경고를 발생시키고 false를 반환합니다.&lt;/li&gt;&lt;li&gt;(많은) 정렬 함수 모두 정렬을 안에서 하고 아무것도 반환하지 않습니다. 정렬된 새 배열을 만드는 것은 불가능합니다. 배열을 직접 복사하고 거기에 다시 정렬을 해서 써야 합니다.&lt;/li&gt;&lt;li&gt;하지만 &lt;code&gt;array_reverse&lt;/code&gt; 함수는 새 배열을 반환합니다.&lt;/li&gt;&lt;li&gt;순서가 있는 것들의 목록과 키와 값의 매핑이라는 개념은 함수 인자를 다루는데 적합하게 보이지만 쓰이지 않습니다.&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;배열이 아닌 것&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;표준 라이브러리에는 "Quickhash"라는 것이 있는데 해쉬를 구현하기 위한 "특정한 강타입 클래스"의 객체지향 구현이라고 합니다. 그리고 서로 다른 키와 값 자료형의 조합으로 이루어진 4개의 클래스가 있습니다. 상대적으로 성능이 얼마나 나오는지, 왜 기본 배열 구현이 이렇게 정말 흔한 케이스에 최적화되지 않았는지 모르겠습니다.&lt;/li&gt;&lt;li&gt;배열을 감싸서 객체처럼 보이게 해주는 (&lt;em&gt;5개&lt;/em&gt;의 서로 다른 인터페이스로 이루어진) &lt;code&gt;ArrayObject&lt;/code&gt; 클래스가 있습니다. 사용자 클래스는 같은 인터페이스를 구현할 수 있습니다. 하지만 몇 개의 메소드만 있을 뿐이고 절반 가까이는 내장 배열 함수처럼 보이지 않으며 배열을 받는 내장 함수는 &lt;code&gt;ArrayObject&lt;/code&gt;나 다른 배열과 비슷한 클래스를 어떻게 다룰 지 모릅니다.&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;함수&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;함수는 데이터가 아닙니다. 클로져는 실제로 객체지만 일반 함수는 아닙니다. 심지어 그 함수의 이름만 가지고 참조할 수도 없습니다. &lt;code&gt;var_dump(strstr)&lt;/code&gt;는 리터럴 문자열 &lt;code&gt;"strstr"&lt;/code&gt;로 예상하고 경고를 출력합니다. 임의의 문자열과 함수 "참조"를 알아차릴 방법은 없습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;create_function&lt;/code&gt;은 기본적으로 &lt;code&gt;eval&lt;/code&gt;의 래퍼입니다. 이것은 정규 이름을 가진 함수를 만들어서 전역에 설치합니다. (그래서 가비지 컬렉팅이 되지 않습니다. 루프 안에서 쓰지 마세요!) 이 함수는 현재 범위에 대해서 전혀 모르기 때문에 클로져가 아닙니다. 이름에는 NUL 바이트가 들어가 있기 때문에 일반 함수와 충돌하지 않습니다. (왜냐하면 파일의 어딘가에 NUL이 들어가 있으면 PHP 파서가 실패해버리기 때문입니다)&lt;/li&gt;&lt;li&gt; &lt;code&gt;__lambda_func&lt;/code&gt;라는 함수를 선언하면 &lt;code&gt;create_function&lt;/code&gt;가 동작하지 않습니다. 실제 구현은 &lt;code&gt;__lambda_func&lt;/code&gt;라는 함수를 만들어서 내부적으로 깨진 이름을 바꾸는 &lt;code&gt;eval&lt;/code&gt;인데, &lt;code&gt;__lambda_func&lt;/code&gt;라는 이름이 이미 있으면 처음 단계에서 치명적 오류를 뱉게 되는 것입니다.&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;기타&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;NULL&lt;/code&gt;을 증가(&lt;code&gt;++&lt;/code&gt;)시키면 1이 됩니다. 감소(&lt;code&gt;--&lt;/code&gt;)시키면 &lt;code&gt;NULL&lt;/code&gt;이 됩니다. 비슷하게 문자열을 감소시키면 아무 것도 변하지 않습니다.&lt;/li&gt;&lt;li&gt;제네레이터(generator)가 없습니다.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;웹 프레임워크&lt;/h3&gt;&lt;h4&gt;실행&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;php.ini&lt;/code&gt; 파일 하나가 PHP의 &lt;em&gt;엄청나게 많은 기능들&lt;/em&gt;을 조종하고 무엇을 언제 오버라이드할지를 결정하는 복잡한 규칙을 도입합니다. 보통 임의의 장치에 설치되는 PHP 소프트웨어는 환경에 맞추도록 설정을 오버라이드해야 하기 때문에 &lt;code&gt;php.ini&lt;/code&gt; 같은 메커니즘을 사용하는 것은 이해할 수 없습니다.&lt;/li&gt;&lt;li&gt;&lt;p&gt;PHP는 기본적으로 CGI 형식으로 동작합니다. 매번 페이지를 읽을 때마다 PHP는 실행하기 전에 모든 코드를 재컴파일합니다. 심지어 Python의 장난감 프레임워크의 개발 서버도 이런 식으로 돌지는 않습니다.&lt;/p&gt;&lt;p&gt;이로 인해 한번 컴파일하고 PHP 코드를 어떤 다른 언어만큼 빠르게 가속시킨다는 "PHP 가속기" 시장이 생겨났습니다. PHP의 배후에 있는 회사인 Zend는 이것을 자신들의 &lt;a href="http://www.zend.com/products/server/"&gt;수익 모델&lt;/a&gt;로 포함시켰습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;한동안 PHP 오류는 기본적으로 클라이언트쪽으로 뿌려졌습니다. 아마 개발 중에 도움이 되라고 그렇게 했을 겁니다. 지금은 더이상 그렇지 않은 것 같지만 여전히 가끔씩 페이지의 상단에 mysql 오류를 볼 때가 있습니다.&lt;/li&gt;&lt;li&gt;PHP는 &lt;a href="http://phpsadness.com/sad/11"&gt;특정 쿼리 인자를 넘기면 PHP 로고를 출력&lt;/a&gt;하는 등의 이상한 "이스터 에그"로 가득합니다. 이것은 &lt;em&gt;당신의&lt;/em&gt; 어플리케이션을 개발하는데 완전히 관계없는 것이긴 하지만 PHP를 사용하고 있는지 확인하는 수단이 될 수 있습니다. (거기에 대략적인 버젼까지) &lt;code&gt;mod_rewrite&lt;/code&gt;를 쓰던 FastCGI를 쓰던 리버스 프록싱을 하던 &lt;code&gt;Server:&lt;/code&gt; 설정을 바꾸던 관계 없습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;&amp;lt;?php … ?&amp;gt;&lt;/code&gt; 태그 바깥의 공백 (심지어 라이브러리까지)은 리터럴 텍스트로 간주되어 응답에 섞여집니다. (그리고 "헤더가 이미 보내짐" 오류를 일으키기도 합니다) 유명한 해결 방법으로는 &lt;code&gt;?&amp;gt;&lt;/code&gt; 토큰을 버리는 겁니다. PHP는 불평하지 않을 것이고 뒤에 줄넘김이 붙지도 않을 것입니다.&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;배치 (deployment)&lt;/h4&gt;&lt;p&gt;배치는 주로 PHP의 가장 큰 장점 중 하나로 꼽혀 왔습니다. 그냥 파일만 몇개 놓으면 끝이라는 거죠. 정말로 Python이나 Ruby, Perl보다 모든 과정이 쉽긴 하지만 미비한 점이 많습니다.&lt;/p&gt;&lt;p&gt;전반에 걸쳐서 저는 웹 어플리케이션을 앱 서버로 돌리고 리버스 프록싱을 하는 것을 선호합니다. 설정하는데 그렇게 노력이 들지도 않고 장점은 충분합니다. 웹 서버와 앱을 따로 관리할 수 있으며 웹 서버를 더 설치할 필요 없이 여러 개의 앱 프로세스를 동시에 돌릴 수 있으며 다른 사용자로 앱을 돌리는 것도 쉬우며 웹 서버를 교체할 수도 있으며 웹 서버를 건드리지 않고 앱을 내릴 수도 있으며 FIFO 시점만 조정하는 것으로 빈틈없이 배치할 수 있습니다. 웹 어플리케이션을 웹 서버와 합치는 것은 불합리하며 그렇게 해서 얻을 수 있는 이점은 없습니다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;PHP는 기본적으로 Apache에 묶여 있습니다. 따로 실행시키거나 다른 웹 서버에서 돌리는 것은 다른 언어로 배치하는 것과 비슷한 (혹은 더 많은) 수준의 삽질을 요합니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;php.ini&lt;/code&gt;는 어디에서나 동작하는 모든 PHP 어플리케이션에 적용됩니다. 단지 하나의 &lt;code&gt;php.ini&lt;/code&gt; 파일이 있을 뿐이고 전역적으로 적용됩니다. 만약 공유된 서버에서 설정을 고쳐야 하거나 두개의 어플리케이션을 돌리는데 둘이 서로 다른 설정이 필요하다면? 당신은 운이 없는 겁니다. 만약 할 수 있다면 모든 필요한 설정들의 합집합을 만들고 앱 내부에서 &lt;code&gt;ini_set&lt;/code&gt;를 사용하거나 &lt;code&gt;.htaccess&lt;/code&gt; 파일을 체크하는 식으로 줄여가야 합니다. 그리고 와, 설정이 어떻게 값을 읽어오는지 확인하기 위해 체크해야 할 엄청나게 많은 곳들이 있네요.&lt;/li&gt;&lt;li&gt;비슷하게 PHP 앱과 거기에 의존된 것들을 쉽게 "감쌀" 수 있는 방법은 없습니다. 두 개의 어플리케이션이 서로 다른 라이브러리나 PHP 버젼을 요구한다면? Apache를 새로 빌드하는 것부터 시작해야겠군요.&lt;/li&gt;&lt;li&gt;이 "파일 묶음" 방식은 게다가 라우팅을 엄청나게 고통스럽게 만듭니다. URL 계층 구조가 전체 코드 트리이기 때문에 어떤 것들을 보이게 만들고 어떤 것들을 보이지 않게 만들어야 하는지를 결정할 화이트리스트와 블랙리스트를 신중하게 만들어야 합니다. 설정 파일이나 다른 "일부분"들은 직접 불러오는 것을 막기 위해 C 스타일의 가드를 넣어야 합니다. 버젼 컨트롤 찌꺼기 (예를 들어 &lt;code&gt;.svn&lt;/code&gt;) 역시 보호가 필요합니다. &lt;code&gt;mod_php&lt;/code&gt;에서는 파일시스템의 모든 것들이 잠재적인 진입점이 됩니다. 앱 서버에서는 단 하나의 진입점과 어떤 URL이 보여질 것인지에 대한 컨트롤만 있으면 됩니다.&lt;/li&gt;&lt;li&gt;CGI 스타일의 파일 묶음을 빈틈없이 업그레이드할 수 있는 방법은 없습니다. 만약 업데이트의 중간에 사이트에 접속하면 사이트가 깨지거나 이상한 동작을 하게 되겠죠.&lt;/li&gt;&lt;li&gt;&lt;p&gt;Apache에서 PHP를 돌리도록 설정하는 것이 "간단"함에도 불구하고, 거기에조차 몇 가지의 보이지 않는 함정이 숨어 있습니다. PHP 문서에서 &lt;code&gt;.php&lt;/code&gt; 파일을 PHP로 돌리기 위해서 &lt;code&gt;SetHandler&lt;/code&gt;를 권장하는데, &lt;code&gt;AddHandler&lt;/code&gt;는 잘 동작하는 것처럼 보이지만 여기에 문제가 있습니다.&lt;/p&gt;&lt;p&gt;&lt;code&gt;AddHandler&lt;/code&gt;를 사용할 때 당신은 Apache에게 "이것을 PHP로 실행하라"는 것이 &lt;code&gt;.php&lt;/code&gt; 파일을 핸들링하기 위한 유일한 방법이라고 전달합니다. &lt;strong&gt;하지만!&lt;/strong&gt; Apache는 파일 확장자에 관해서 지구상의 어떤 인간들과도 다른 아이디어를 가지고 있습니다. Apache는 가령 &lt;code&gt;index.html.en&lt;/code&gt; 같은 파일은 HTML과 영어로 인식하도록 설계되었습니다. Apache에게 파일은 여러 개의 확장자를 동시에 가질 수 있는 것입니다.&lt;/p&gt;&lt;p&gt;만약 파일 업로드 폼이 있고 그것을 통해 어떤 public 디렉토리에 파일을 저장할 수 있다고 상상해 봅시다. 사람들이 PHP 파일을 올리지 못하게 하기 위하여 &lt;code&gt;.php&lt;/code&gt; 확장자를 검사하는 기능을 넣을 것입니다. 만약 공격자가 &lt;code&gt;foo.php.txt&lt;/code&gt;라는 파일을 업로드하면 문제없이 업로드가 될 것입니다. 하지만 Apache는 그것을 PHP로 인식하고 실행하게 될 것입니다.&lt;/p&gt;&lt;p&gt;문제는 "원래 파일 이름을 사용하지 않는다", "꼼꼼히 검사하지 않는다" 같은 것이 아닙니다. 문제는 웹 서버가 어디에나 널려 있는 코드를 실행시킬 수 있다는 점입니다. 이것은 PHP가 "배치하기 쉽다"는 것과 일치하는 속성이기도 합니다. CGI는 &lt;code&gt;+x&lt;/code&gt;가 필요합니다. 하지만 PHP에서는 그런 것을 할 필요가 없습니다. 그리고 이것은 단지 이론적인 문제가 아닙니다. 이런 문제를 겪은 여러 사이트들을 봤습니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;부재하는 기능들&lt;/h4&gt;&lt;p&gt;아래의 기능들 모두 웹 어플리케이션을 개발할 때 여러가지 면에서 중요하다고 생각합니다. PHP가 "웹 프로그래밍 언어"로 팔리고 있기 때문에 이런 기능들의 일부라도 구현되어 있으면 합리적일 것입니다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;템플릿 시스템의 부재. PHP 그 자체가 템플릿 시스템이지만 프로그램이 아닌 하나의 거대한 인터폴레이터 형식은 아니어야 합니다.&lt;/li&gt;&lt;li&gt;XSS 필터의 부재. "&lt;code&gt;htmlspecialchars&lt;/code&gt; 쓰는 걸 잊지 마세요" 같은게 아니라 &lt;a href="http://pypi.python.org/pypi/MarkupSafe"&gt;이런 거&lt;/a&gt;요.&lt;/li&gt;&lt;li&gt;CSRF 보호 기능의 부재. 직접 만들어 써야 합니다.&lt;/li&gt;&lt;li&gt;일반화된 표준 데이터베이스 API의 부재. PDO 같은 것들은 모든 개별 데이터베이스 API를 추상화해서 차이를 없애야 합니다.&lt;/li&gt;&lt;li&gt;라우팅의 부재. 당신의 웹사이트는 파일시스템과 일치할 것입니다. 많은 개발자들은 &lt;code&gt;mod_rewrite&lt;/code&gt; (와 일반적으로 &lt;code&gt;.htaccess&lt;/code&gt;)를 사용하는 것이 괜찮은 대안이라고 속아 왔습니다.&lt;/li&gt;&lt;li&gt;인증 허가 기능의 부재&lt;/li&gt;&lt;li&gt;개발 서버 기능의 부재&lt;/li&gt;&lt;li&gt;실시간 디버깅 기능의 부재&lt;/li&gt;&lt;li&gt;일관성 있는 배치 기능의 부재. 단지 "파일들을 전부 서버로 복사하세요" 뿐.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;보안&lt;/h3&gt;&lt;h4&gt;언어의 경계&lt;/h4&gt;&lt;p&gt;PHP의 보안에 대한 나쁜 평판의 대부분이 임의의 언어로 된 데이터를 받아서 다른 언어로 그대로 내놓는다는 점에서 옵니다. &lt;code&gt;"&amp;lt;script&amp;gt;&lt;/code&gt;는 SQL에서는 아무런 의미가 없지만 물론 HTML에선 있습니다.&lt;/o&gt;&lt;p&gt;이걸 더욱 악화시키는 것은 "입력을 정화(sanitize)하십시오" 라는 공통적인 외침입니다. 이건 &lt;em&gt;완전히 잘못된 것입니다&lt;/em&gt;. 한번 휘두르면 데이터 더미를 본질적으로 "깨끗하게" 만드는 마법의 지팡이 같은 것은 없습니다. 단지 필요한 것은 언어를 쓰는 것입니다. SQL에 플레이스홀더(placeholder)를 단다거나 프로세스를 호출할 때 인자 목록을 쓴다거나 등등....&lt;/p&gt;&lt;ul&gt;&lt;li&gt;PHP는 명시적으로 데이터를 "정화"하는 것을 &lt;em&gt;권장&lt;/em&gt;합니다. 그리고 그것을 위한 &lt;a href="http://www.php.net/manual/en/book.filter.php"&gt;데이터 필터 확장&lt;/a&gt;까지 있습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;addslashes&lt;/code&gt;, &lt;code&gt;stripslashes&lt;/code&gt; 같은 슬래쉬 관련 함수들은 완전히 말도 안되고 도움도 안되는 빨간 청어(역주: 중요한 것에서 집중을 분산시키는 것이라는 의미)입니다.&lt;/li&gt;&lt;li&gt;제가 말씀드릴 수 있는 것은 안전하게 외부 프로세스를 호출할 수 있는 방법은 없다는 겁니다. 단지 쉘로 명령을 내려서 실행시킬 수 있을 뿐입니다. 미친 듯이 이스케이핑을 하고 기본쉘이 제대로 이스케이핑 해제를 하는 것을 기대하거나 &lt;code&gt;pcntl_fork&lt;/code&gt;와 &lt;code&gt;pcntl_exec&lt;/code&gt;를 쓰는 방법 외에는 없습니다.&lt;/li&gt;&lt;li&gt;&lt;code&gt;escapeshellcmd&lt;/code&gt;와 &lt;code&gt;escapeshellarg&lt;/code&gt; 두개가 거의 비슷한 설명과 함께 있습니다. 하지만 &lt;code&gt;escapeshellarg&lt;/code&gt;는 윈도에서 돌지 않고 (Bourne 쉘의 형식을 가정하기 때문에) &lt;code&gt;escapeshellcmd&lt;/code&gt;는 단지 마침표를 공백으로 치환할 뿐입니다. (뭔가를 하려고 하면 그냥 조용하게 죽어버릴 수도 있습니다)&lt;/li&gt;&lt;li&gt;여전히 널리 쓰이는, 기본 내장된 MySQL 바인딩에서는 준비된 선언(prepared statement)을 만드는 방법이 없습니다.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;지금까지, &lt;a href="http://www.php.net/manual/en/security.database.sql-injection.php"&gt;SQL 인젝션에 대한 PHP 문서&lt;/a&gt;에서는 타입 검사, &lt;code&gt;sprintf&lt;/code&gt;나 &lt;code&gt;is_numeric&lt;/code&gt; 사용, &lt;code&gt;mysql_real_escape_string&lt;/code&gt;을 어디서든지 직접 사용, &lt;code&gt;addslashes&lt;/code&gt;를 직접 사용하는 등의 제 정신이 아닌 것 같은 대응 방법을 추천하고 있습니다. 유저 코멘트란을 제외하고 PDO나 파라미터화(parameterization)의 언급은 하나도 없습니다. 2년 전에 PHP 개발자들에게 이 점을 매우 구체적으로 이야기했고 개발자들은 들었습니다. 하지만 이 페이지는 아직까지 바뀌지 않았습니다.&lt;/p&gt;&lt;h4&gt;기본 상태가 취약함&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;register_globals&lt;/code&gt;. 한동안 기본적으로 꺼져 있었고 5.4에서는 없어졌습니다. 뭐 상관 없습니다. 이건 &lt;em&gt;골칫거리&lt;/em&gt;입니다.&lt;?li&gt;&lt;li&gt;&lt;code&gt;include&lt;/code&gt; 또한 HTTP URL을 받아들일 수 있습니다.&lt;/li&gt;&lt;li&gt;Magic quote. 안전한 상태에 가깝긴 하지만 여전히 개념을 제대로 이해하고 있는 것과는 거리가 멀어 보입니다.&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;핵심&lt;/h4&gt;&lt;p&gt;PHP 인터프리터 자체에 &lt;em&gt;정말 끝내주는&lt;/em&gt; 보안 문제가 몇 차례 있었습니다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;2007년 인터프리터에 정수 오버플로우 취약점이 발견되었습니다. 수정은 &lt;code&gt;if (size &amp;gt; INT_MAX) return NULL;&lt;/code&gt;로 시작되어서 &lt;a href="http://use.perl.org/use.perl.org/_Aristotle/journal/33448.html"&gt;그대로 막장으로 갔습니다&lt;/a&gt;. (C에 익숙하지 않은 분들을 위해서: INT_MAX는 하나의 변수에 담을 수 있는 가장 높은 숫자입니다. 이제 나머지를 이해하실 수 있을 거라고 믿습니다.)&lt;/li&gt;&lt;li&gt;더 최근에, PHP 5.3.7에서 &lt;code&gt;crypt()&lt;/code&gt; 함수로 인해 &lt;a href="https://bugs.php.net/bug.php?id=55439"&gt;누구나 패스워드 없이 로그인을 할 수 있는 버그&lt;/a&gt;가 발견되었습니다.&lt;/li&gt;&lt;li&gt;PHP 5.4는 서비스 거부 공격(DoS)에 취약한데, &lt;code&gt;Content-Length&lt;/code&gt; 헤더(임의로 설정가능한)를 받아서 그만큼의 메모리를 할당하려고 시도하기 때문입니다. &lt;a href="http://www.exploit-db.com/exploits/18665/"&gt;이건 매우 나쁜 아이디어입니다&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;찾아보면 더 나오겠지만 X개의 보안 취약점이 있다는 것은 요지가 아닙니다. 소프트웨어는 버그가 있고 뭐든 일어날 수가 있기 때문이죠. 하지만 이것들의 본질이 끔찍하다는 겁니다. 그리고 이것들은 제가 찾은 것이 아닙니다. 최근 몇 달동안 갑자기 저희 집 문 앞에 나타났을 뿐입니다.&lt;h3&gt;결론&lt;/h3&gt;&lt;p&gt;몇몇 코멘트에서 저한테 결론이 없다는 것을 잘 지적했습니다. 네, 뭐 저는 결론이 없습니다. 여기까지 읽어보셨다면 시작하기도 전에 저에게 동의했을 거라고 추정했죠.&lt;/p&gt;&lt;p&gt;PHP만 알고 있는 상태고 다른 것을 배울 의향이 있다면 &lt;a href="http://docs.python.org/tutorial/"&gt;Python 튜토리얼&lt;/a&gt;을 읽어보시고 웹 개발에서는 &lt;a href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt;를 써 보세요. (저는 템플릿 언어의 팬은 아닙니다만 그래도 괜찮긴 합니다.) 그것은 여러분의 앱을 나누지만 모두 다 같은 조각들이고 충분히 친숙하게 보일 겁니다. 여기에 대해서는 나중에 여기에 속하지 않는 언어와 웹 스택 전반을 소개하는 블로그 포스팅에서 따로 다루겠습니다.&lt;/p&gt;&lt;p&gt;그리고 이후에 더 큰 프로젝트를 하실 거라면 중간 레벨에 있는 &lt;a href="http://www.pylonsproject.org/"&gt;Pyramid&lt;/a&gt;가 좋습니다. &lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt;도 있습니다. Django 사이트와 비슷한 것을 만들기에 적합한 엄청나게 거대한 괴물이죠.&lt;/p&gt;&lt;p&gt;만약 개발자가 아닌데 어떤 이유로 이 글을 읽으셨다구요? 지구상의 모든 사람들이 &lt;a href="http://learnpythonthehardway.org/"&gt;Learn Python The Hard Way&lt;/a&gt;를 정독하기 전까지 전 행복해지지 않을 겁니다. 그러니 읽으세요.&lt;/p&gt;&lt;p&gt;써본 적은 없지만 Ruby와 Rails나 경쟁자들이 있고 Perl 역시 Catalyst와 함께 여전히 잘 살아 있습니다. 끊임없이 읽고 배우고 만들고 열중하세요.&lt;/p&gt;&lt;h3&gt;감사의 말&lt;/h3&gt;&lt;p&gt;이하에 감사드립니다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://alokmenghrajani.github.com/wtf/php.html"&gt;PHP turtles&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://phpsadness.com/"&gt;PHP Sadness&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.phpwtf.org/"&gt;PHP WTF&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://wiki.theory.org/YourLanguageSucks#PHP_sucks_because%3A"&gt;YourLanguageSucks&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://tnx.nl/php.html"&gt;Perl과 비교한 PHP&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://two-pi-r.livejournal.com/622760.html"&gt;Pi의 빽빽하고 화나고 영감을 주는 불평&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://tracks.ranea.org/post/13908062333/php-is-not-an-acceptable-cobol"&gt;PHP는 용인 가능한 COBOL이 아니다&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.php.net/manual/en/index.php"&gt;PHP 문서&lt;/a&gt;&lt;/li&gt;&lt;li&gt;수많은 PHP 팬과 안티들&lt;/li&gt;&lt;li&gt;그리고 물론, Rasmus Lerdorf(PHP의 창시자)와 그의 Perl에 대한 완전한 몰이해&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;더 부연할 것이 있거나 틀린 부분이 있으면 알려주세요.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/931078517202405968-3400227410255431435?l=ko.blog.influx.kr' alt='' /&gt;&lt;/div&gt;</description><link>http://ko.blog.influx.kr/2012/04/php.html</link><author>noreply@blogger.com (segfault)</author><thr:total>7</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-931078517202405968.post-3038942683857820573</guid><pubDate>Thu, 07 Apr 2011 14:42:00 +0000</pubDate><atom:updated>2011-04-08T00:47:39.042+09:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>konstruktor</category><category domain='http://www.blogger.com/atom/ns#'>개발</category><title>메쉬와 씨름하기</title><description>&lt;p&gt;요즘 Konstruktor 렌더링 엔진의 퀄리티를 개선하기 위한 방법을 생각하고 있습니다. 현재 퀄리티에서 가장 문제가 되는 부분은 쉐이딩입니다.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-j2lrSKzqqUU/TZ3SxKr9JeI/AAAAAAAAAKA/ATu3P7T-LdM/s1600/render.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 284px;" src="http://1.bp.blogspot.com/-j2lrSKzqqUU/TZ3SxKr9JeI/AAAAAAAAAKA/ATu3P7T-LdM/s320/render.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5592858054388164066" /&gt;&lt;/a&gt;&lt;p&gt;플랫 쉐이딩으로 인해 물체 표면이 각져 보입니다. 현재 LDraw의 스펙에서는 단순히 삼각형, 사각형을 그리기 위한 vertex들의 집합(mesh)만 정의되어 있고, surface normal을 정의하지 않습니다. 그래서 단순히 그 정점의 데이터만 가지고 surface normal을 뽑아내면 저런 식으로 물체가 각져 보이게 되는데, smooth shading을 하기 위해서는 인접한 삼각형과 사각형을 이루는 vertex가 가지는 surface normal 벡터들을 보간해야 합니다. 하지만 가장 큰 문제는 LDraw 파일의 스펙이 strictly declarative하지 않다는 것입니다. 그러다 보니 렌더러에서 정해진 파일 스펙 이상을 표현하기가 어렵게 되다 보니 현재 mesh를 인접한 것들끼리 서로 분류하기 위해서는 전체 파일 트리를 뒤지면서 메쉬를 재구성하는 절차가 필요해지게 되는데, 그 시점에서 고려해야 할 부분이 많은 꽤나 골치아픈 작업이 될 것 같습니다.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-UpewxeJVx1o/TZ3b_bAb9XI/AAAAAAAAAKI/R061mo9SJHA/s1600/render2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 163px;" src="http://4.bp.blogspot.com/-UpewxeJVx1o/TZ3b_bAb9XI/AAAAAAAAAKI/R061mo9SJHA/s320/render2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5592868194891855218" /&gt;&lt;/a&gt;&lt;p&gt;그리고 또 다른 문제가 있는데, 이 스크린샷에서는 일부 삼각형의 쉐이딩이 깨집니다. 파트 제작자의 실수인지는 모르겠지만 정점 순서가 잘못되어 surface normal이 인접 삼각형과 반대 방향으로 반전되어 생기는 문제입니다. 근본적으로는 렌더러의 문제가 아니라 파트 자체의 문제이나 메쉬 재구성 과정을 통해서 능동적으로 고칠 여지는 있습니다.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;일단 고려해야 할 부분은:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;메쉬 전체를 그루핑하면 안 됩니다. 원래부터 각진 표면이 있기 때문에 모든 정점에 대해 normal smoothing을 적용하면 안 됩니다. 일단 인접 삼각형, 사각형과의 각도를 재어서 threshold를 적당하게 주면 될 겁니다.&lt;/li&gt;&lt;li&gt;메모리 사용량을 최소화해야 합니다. 지금 LDraw 구조를 나이브하게 활용하면 중복되는 정점 데이터가 너무 많이 생깁니다. 메쉬를 재구성하는 김에 triangle, quad strip 등으로 재조립해서 중복되는 정점 데이터를 최소화해야 합니다.&lt;/li&gt;&lt;li&gt;그리고 이 모든 과정은 휴리스틱해야 합니다. triangle/quad strip을 만드는 문제도 그렇고 두번째 스크린샷의 normal 반전 오류를 고치는 등의 문제를 해결하기 위해서는 적절한 조건이 있어야 할텐데, 이건 조금 더 생각해봐야 할 문제인 것 같군요.&lt;/li&gt;&lt;li&gt;빨라야 합니다. part 데이터는 수시로 메모리에 적재되기 때문에 이 모든 메쉬 재구성 작업에 들어가는 시간은 최대한 적은 것이 좋을 겁니다.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;사실 이런 것은 시중에 나와있는 대부분의 3D 소프트웨어가 기본으로 하는 일일텐데 공부하는 셈 치고 독자적으로 구현해 볼 생각입니다. 이 모든 작업을 위해서는 일단 자료구조부터 잘 설계해야 하는데, 조금 더 생각해보고 실제 구현해 본 다음에 이어서 포스팅하겠습니다. 아마 비슷한 걸 다루는 논문이라도 있는지 찾아봐야겠군요.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/931078517202405968-3038942683857820573?l=ko.blog.influx.kr' alt='' /&gt;&lt;/div&gt;</description><link>http://ko.blog.influx.kr/2011/04/blog-post.html</link><author>noreply@blogger.com (segfault)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-j2lrSKzqqUU/TZ3SxKr9JeI/AAAAAAAAAKA/ATu3P7T-LdM/s72-c/render.png' height='72' width='72'/><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-931078517202405968.post-8989943091828304994</guid><pubDate>Wed, 16 Feb 2011 14:11:00 +0000</pubDate><atom:updated>2011-02-16T23:39:09.048+09:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>개발</category><title>rss2metaweblog</title><description>&lt;p&gt;관리하고 있는 4개의 블로그의 포스트를 중앙에 모을 수 있는 방법이 없을까 하고 생각하고 있었습니다. 네이버 블로그를 그렇게 좋아하진 않지만 네이버 블로그의 규모와 유저 베이스를 결코 앝볼 수 없기에 글을 모아놓는다면 거기가 가장 적당할 거라고 생각했습니다.&lt;/p&gt;&lt;p&gt;블로그에서 다른 블로그로 글을 실시간으로 보낼 수 있는 방법을 찾아보았지만 찾을 수 없었습니다. 글의 배급은 RSS나 Atom을 통해, 블로그에 글을 게시하는 통일된 인터페이스는 MetaWeblog API를 통해 제공되지만 이 둘을 합쳐서 실시간으로 피드를 받아와서 블로그에 글을 게시해 주는 서비스가 없는 것 같더군요.&lt;/p&gt;&lt;p&gt;그래서 임의의 RSS/Atom 피드를 크롤링하면서 MetaWeblog API를 통해 해당 피드들을 임의의 블로그에 게시해 주는 간단한 스크립트를 만들어 보았습니다. XMLRPC나 MetaWeblog API, JSON, RSS 피드 리더 전부 파이선에서 그냥 바로 가져다 쓸 수 있는 형태로 만들어져 있어서 어렵지 않게 작업이 가능했습니다. 테스트까지 합쳐서 1시간 반 정도 걸렸습니다.&lt;/p&gt;&lt;p&gt;혹시 필요하실 분이 계실까봐 공개합니다. &lt;a href="https://github.com/segfault87/rss2metaweblog"&gt;https://github.com/segfault87/rss2metaweblog&lt;/a&gt; MIT 라이센스를 따르며 파이선 2.x, Universal Feed Parser 5.0가 필요합니다. 네이버 블로그에서 쓰려고 만들었지만 아마 MetaWeblog API를 지원하는 블로그 서비스라면 어디서든지 사용이 가능할 겁니다. 설정 파일의 형식은 JSON이며 대략적인 설정 방법은 제 설정 파일을 참고해 주세요.&lt;/p&gt;&lt;pre&gt;{&lt;br /&gt;    "preferences": {&lt;br /&gt;            "update_interval": "5m"&lt;br /&gt;        },&lt;br /&gt;    "feeds": [&lt;br /&gt;        {&lt;br /&gt;            "id": "electroscape.en", &lt;br /&gt;            "url": "http://en.blog.influx.kr/feeds/posts/default"&lt;br /&gt;        }, &lt;br /&gt;        {&lt;br /&gt;            "id": "electroscape.ko", &lt;br /&gt;            "url": "http://ko.blog.influx.kr/feeds/posts/default"&lt;br /&gt;        }, &lt;br /&gt;        {&lt;br /&gt;            "id": "scrapranch", &lt;br /&gt;            "url": "http://scrapranch.influx.kr/rss"&lt;br /&gt;        }, &lt;br /&gt;        {&lt;br /&gt;            "id": "tangibility", &lt;br /&gt;            "url": "http://tangibility.influx.kr/rss"&lt;br /&gt;        } &lt;br /&gt;    ], &lt;br /&gt;    "target": [&lt;br /&gt;        {&lt;br /&gt;            "id": "naver", &lt;br /&gt;            "url": "https://api.blog.naver.com/xmlrpc", &lt;br /&gt;            "blogid": "segfault87", &lt;br /&gt;            "userid": "segfault87",&lt;br /&gt;            "password": "API 키" &lt;br /&gt;        }&lt;br /&gt;    ],&lt;br /&gt;    "category_map": {&lt;br /&gt;        "naver": {&lt;br /&gt;            "electroscape.en": "electroscape.en", &lt;br /&gt;            "electroscape.ko": "electroscape.ko", &lt;br /&gt;            "scrapranch": "scrap ranch", &lt;br /&gt;            "tangibility": "tangibility"&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/931078517202405968-8989943091828304994?l=ko.blog.influx.kr' alt='' /&gt;&lt;/div&gt;</description><link>http://ko.blog.influx.kr/2011/02/rss2metaweblog.html</link><author>noreply@blogger.com (segfault)</author><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-931078517202405968.post-768373057416029757</guid><pubDate>Thu, 27 Jan 2011 13:56:00 +0000</pubDate><atom:updated>2011-01-27T23:02:15.774+09:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>konstruktor</category><category domain='http://www.blogger.com/atom/ns#'>개발</category><title>최종(?) 스크린샷.</title><description>&lt;p&gt;Konstruktor의 첫번째 베타 버전(0.9.0-beta1)의 스크린샷입니다.&lt;/p&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/segfault87/5393062462/sizes/o/" title="Flickr에서 segfault87님의 konstruktor1"&gt;&lt;img src="http://farm6.static.flickr.com/5016/5393062462_55fd7e582a.jpg" width="500" height="311" alt="konstruktor1" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/segfault87/5392463961/sizes/o/" title="Flickr에서 segfault87님의 konstruktor2"&gt;&lt;img src="http://farm6.static.flickr.com/5134/5392463961_baa377de2a.jpg" width="500" height="311" alt="konstruktor2" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/segfault87/5393062556/sizes/o/" title="Flickr에서 segfault87님의 konstruktor3"&gt;&lt;img src="http://farm6.static.flickr.com/5294/5393062556_f1e434550e.jpg" width="500" height="303" alt="konstruktor3" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/931078517202405968-768373057416029757?l=ko.blog.influx.kr' alt='' /&gt;&lt;/div&gt;</description><link>http://ko.blog.influx.kr/2011/01/blog-post_27.html</link><author>noreply@blogger.com (segfault)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm6.static.flickr.com/5016/5393062462_55fd7e582a_t.jpg' height='72' width='72'/><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-931078517202405968.post-2978660524289891885</guid><pubDate>Thu, 27 Jan 2011 06:11:00 +0000</pubDate><atom:updated>2011-01-27T15:18:23.790+09:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>konstruktor</category><category domain='http://www.blogger.com/atom/ns#'>개발</category><title>체크리스트</title><description>&lt;ol&gt;&lt;li&gt;자르기, 복사, 붙여넣기 기능 구현&lt;/li&gt;&lt;li&gt;부품 검색 기능 구현&lt;/li&gt;&lt;li&gt;설정 GUI 구현&lt;/li&gt;&lt;li&gt;multipart thumbnail 갱신 버그 수정&lt;/li&gt;&lt;li&gt;웹사이트 제작&lt;/il&gt;&lt;/ol&gt;&lt;p&gt;자잘한 문제만 남았습니다. 이제 정말 릴리즈가 코 앞이라는 것이 실감이 나기 시작하는군요. 이번에는 두번째 베타의 마일스톤을 정리해 봅니다.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;윈도 버전 릴리즈 준비&lt;/li&gt;&lt;li&gt;Statistics 기능 구현&lt;/li&gt;&lt;li&gt;Part Explorer 사용성 개선&lt;/li&gt;&lt;li&gt;POV-Ray 렌더러 설정 UI 추가&lt;/li&gt;&lt;li&gt;KDE 의존성 걷어내는 작업 준비&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/931078517202405968-2978660524289891885?l=ko.blog.influx.kr' alt='' /&gt;&lt;/div&gt;</description><link>http://ko.blog.influx.kr/2011/01/blog-post.html</link><author>noreply@blogger.com (segfault)</author><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-931078517202405968.post-3539817460987398026</guid><pubDate>Thu, 20 Jan 2011 08:25:00 +0000</pubDate><atom:updated>2011-01-20T17:36:17.320+09:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>konstruktor</category><title>VirtualBox</title><description>&lt;p&gt;&lt;a href="http://farm6.static.flickr.com/5047/5369762998_f6399a6874_o.png"&gt;&lt;img src="http://farm6.static.flickr.com/5047/5369762998_438de6b86e.jpg" alt="WTF?" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://www.virtualbox.org"&gt;VirtualBox&lt;/a&gt;에 개발 환경을 구성해서 테스트해 보고 있습니다. VirtualBox에서 죽는다는 보고가 들어와서 살펴 보니 OpenGL 드라이버에 pbuffer 지원이 빠져 있더군요. 사실상 GLX에서 offscreen에 렌더링할 유일한 방법이라 이 확장이 꼭 필요한데, 요즘 대부분의 GLX 구현에 기본적으로 포함된 확장이 구현이 안 되었다는 점이 매우 수상합니다. &lt;a href="http://chromium.sourceforge.net"&gt;Chromium&lt;/a&gt;을 베이스로 한 것 같은데 일종의 wrapper 같은 거라 구현이 특별히 어렵진 않을 거 같습니다. 시간 나면 패치나 만들어서 보내볼까 합니다.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/931078517202405968-3539817460987398026?l=ko.blog.influx.kr' alt='' /&gt;&lt;/div&gt;</description><link>http://ko.blog.influx.kr/2011/01/virtualbox.html</link><author>noreply@blogger.com (segfault)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm6.static.flickr.com/5047/5369762998_438de6b86e_t.jpg' height='72' width='72'/><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-931078517202405968.post-7272232416342263873</guid><pubDate>Sun, 16 Jan 2011 15:33:00 +0000</pubDate><atom:updated>2011-01-17T19:32:05.565+09:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>konstruktor</category><title>Konstruktor 작업 현황.</title><description>&lt;ol&gt;&lt;br /&gt;&lt;li&gt;알파 릴리즈를 목전에 두고 있습니다. 핵심적인 기능은 대부분 구현이 되었고 지루한 UI 작업이 부분적으로 남아 있습니다. 집중력 잃지 않고 이번 달까지 릴리즈할 수 있도록 노력하겠습니다.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;몇몇 시스템에서 OpenGL rendering context 생성이 제대로 되지 않는다는 버그 보고가 들어왔습니다. &lt;a href="http://nouveau.freedesktop.org/wiki/"&gt;nouveau&lt;/a&gt; 드라이버를 쓰는 시스템인 것 같은데, 여기서 테스트하기도 곤란한 상황입니다. 특별히 OpenGL을 변태적으로 활용하지는 않은 것 같은데 왜 이런 데서 문제가 발생하는지 모르겠습니다. 일단 버그 보고는 계속 받겠습니다.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;POV-Ray 3.7 RC 버전을 붙여 봤는데 일단 문제 없이 동작합니다. 문제는 SMP 지원이 추가되면서 렌더링 중간 과정이 바로 스트리밍 되지 않고 완료되면 짠 하고 뜬다는 건데 이건 어쩔 수가 없네요.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;버텍스 쉐이더 구현은 뒤로 미루기로 했습니다. 버텍스 쉐이더를 구현하려는 이유가 색깔별로 buffer를 따로 두지 않기 위함이었는데 색 버퍼를 따로 둬도 그렇게까지 performance hit이 일어나지 않을 거라는 판단이 들었습니다. 장기적으로 보면 메모리 사용량 절약을 위해서라도 버텍스 쉐이더를 쓰는게 옳다고는 생각되지만 지금은 릴리즈가 급선무라.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;오브젝트 선택 구현을 거의 새로 했습니다. 이제 오브젝트의 선택을 훨씬 더 직관적으로 할 수 있는데다가 boolean operation(add, subtract, intersect)도 지원합니다. 뿌듯하군요.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;슬슬 KDE 의존성을 빼는 계획을 세우고 있습니다. 지금 KDE의 구성요소를 다음과 같은 부분에서 활용하고 있는데,&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;XMLGUI&lt;/li&gt;&lt;br /&gt;&lt;li&gt;KSettings&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Oxygen 아이콘&lt;/li&gt;&lt;br /&gt;&lt;li&gt;몇몇 helper class들&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;도움 클래스나 환경설정이야 그냥 대체해 버리면 상관이 없지만 XMLGUI 프레임워크 덕을 많이 보고 있어서 당장 갈아치우기도 곤란한 상황입니다. 아이콘도 마찬가지고요. 게다가 KDE와 UX를 맞추고 싶다는 개인적인 욕심도 있고. 아마 가능하다면 KDE dependency는 optional하게 빼는 쪽으로 가게 될 것 같습니다.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;윈도와 맥 버전은 이번 알파 릴리즈에 포함되지 않을 겁니다. KDE 의존성 빼는 작업이 끝나면 양 플랫폼으로도 릴리즈할 계획입니다. KDE 런타임을 X11 밖에서 쓰기에는 너무 덩치가 큰지라…. 윈도 버전이 실은 매우 중요한데, 처음에는 이 프로젝트가 리눅스용 &lt;a href="http://www.ldraw.org"&gt;LDraw&lt;/a&gt; 캐드를 만들어 보겠답시고 시작한 프로젝트였지만 멀티플랫폼이 되니 어떤 플랫폼에서 가장 많이 쓰일 지는 어차피 안 봐도 뻔하기 때문이지요.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;릴리즈가 다가오니 홈페이지를 만들어야 할텐데 그저 귀찮습니다. github 페이지로 때울까…. 아니 홈페이지 만들어 주실 분 안 계시려나. (…)&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;여담이지만 작업 경과는 항상 git 저장소에 반영하고 있습니다. 소스 코드나 커밋 로그는 여기서 보실 수 있습니다: &lt;a href="http://github.com/segfault87/Konstruktor"&gt;http://github.com/segfault87/Konstruktor&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/931078517202405968-7272232416342263873?l=ko.blog.influx.kr' alt='' /&gt;&lt;/div&gt;</description><link>http://ko.blog.influx.kr/2011/01/konstruktor.html</link><author>noreply@blogger.com (segfault)</author><thr:total>2</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-931078517202405968.post-245403938643024020</guid><pubDate>Thu, 16 Dec 2010 12:33:00 +0000</pubDate><atom:updated>2010-12-17T08:34:03.202+09:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>그래픽스</category><title>ACM SIGGRAPH Asia 2010 (day two)</title><description>&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Modeling Player Performance in Rhythm Games&lt;/b&gt; : 음악 게임 난이도 측정에 probablistic model이라.... 중간에 나와서 자세한 건 기억이 안 나는군요.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Introduction to OpenCL by Example&lt;/b&gt; : OpenCL에 대한 개략적인 설명과 사용법 설명. 전반적으로 대충 알고 있었던 건데 오후에 OpenCL을 주제로 좀 더 심도있는 강의가 있다는 사실을 미리 알았으면 다른 걸 들었어야 하는 건데 말입니다.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Physical and Graphical Effects in OpenCL by Example&lt;/b&gt; : Xcode의 OpenCL 예제 코드를 기준으로 설명하더군요. 나중에 cloth simulation을 GPGPU로 구현하기 위한 전략을 설명하던데 대단히 흥미롭게 들었습니다. Bullet Physics Engine에 OpenCL로 구현된 코드가 있다고 하니 시간 나면 들여다 봐야겠습니다. GPGPU 프로그래밍 경험이 없어서 여기서 중점적으로 다룬 performance-wise 문제는 뜬구름 잡는 것 처럼 느껴지긴 해도 대충 이건 이렇게 하고 저건 저렇게 하는구나 하는 개념 정도는 알게 되었으니 만족.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Geometry Simulation for Feature Films&lt;/b&gt; : ILM에서 아티스트 한 분이 오셔서 아바타, 인디아나 존스 4, 터미네이터 4 등의 영화의 몇몇 샷에서의 지오메트리 시뮬레이션을 어떤 식으로 구현했는지를 자세히 설명하셨습니다. 아무리 시뮬레이션을 돌려도 원하는 결과를 얻기 위해서는 아티스트의 눈이 필연적으로 갈 수 밖에 없고 엄청난 노가다가 동원됩니다. 1초 남짓한 샷 하나를 얻는 데도 4개월 가까이 걸렸다는 얘기를 듣고 VFX 업계에 대한 경외감이.... 엔지니어와 아티스트가 어떤 식으로 커뮤니케이션을 하며 문제를 해결해 가는지에  대해서도 알 수 있었고 ILM의 workflow에 대해서도 간단하게나마 알 수 있었던 유익한 시간이었습니다.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Animation Shorts&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Sintel : 블렌더 제단에서 자유 소프트웨어만으로 만든 작품입니다. 예전에 한국어 자막을 만든 적이 있는데 설마 내가 만든 자막이 나오는게 아닐까 하고 두근두근하면서 봤는데 아예 자막이 안 나오더군요. 하긴 뭐 자막 없이 봐도 전혀 지장 없는 작품이긴 하다....&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Nirvana : 스미에 스타일의 짧은 모션 그래픽 작품.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The Gruffalo : 동화책을 원작으로 했더군요. 따스한 색감이 매력적이었습니다. 러닝 타임은 30분.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A Lost and Found Box of Human Sensation : 중간에 들어와서 내용 파악이 잘 안 되더군요. 기억에 남는 건 이언 맥켈런 옹의 나레이션.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The Light : 현란하고 추상적인 3분짜리 모션 그래픽 애니메이션. 영상 끝나고 성경 구절 인용이 나오는 걸 보고 좀 벙쪘습니다. 제목이 The Light인 것도 그렇고 아무래도 종교적인 함의가 있는 것 같은데 아마도 제가 본 가장 난해한 종교적인 영상일 겁니다.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Star Wars The Clone Wars: The Zilo Beast Strikes Back : 스타워즈 TV시리즈 클론 워즈의 파일럿 에피소드인 것 같습니다. 코루스칸트 행성에서 Zilo Beast라는 괴물이 난동을 부리고 제다이들이 힘을 합쳐 막아낸다는 이야기.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The Light House Keeper : 매우 짧은 흑백 단편입니다. CGI가 쓰이긴 했지만 거의 hand-drawn 애니메이션.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The Lost Thing : 은근히 디스토피아적인 배경인데도 파스텔톤의 색감과 몽환적인 분위기가 어우러져서 묘한 느낌의 애니메이션입니다. 크리쳐 디자인도 재밌더군요.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Luciernaga : 깜빡 졸았는데 이미 끝나 있었음. 어 뭐야....&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The Birthday Gift : 중국 애니메이션. 무슨 전래동화 읽는 기분이었습니다. 영어 대사를 중국풍으로 혀 꼬아서 읽으니 어찌나 웃기던지.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The Mouse Who is Soared : 하늘을 나는 쥐의 이야기. 쥐가 웃기게 생겼다는거 말고는 딱히 기억에 남지는 않네요.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Lost and Found : The Lost Thing이랑 소재가 비슷합니다. 한 소년과 펭귄의 우정을 그린 작품. 이것도 동화책 원작이더군요. 어른의 눈에서 보면 뻔하지만 그래도 마냥 훈훈한 이야기.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/931078517202405968-245403938643024020?l=ko.blog.influx.kr' alt='' /&gt;&lt;/div&gt;</description><link>http://ko.blog.influx.kr/2010/12/acm-siggraph-asia-2010-day-two.html</link><author>noreply@blogger.com (segfault)</author><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-931078517202405968.post-3892683445868602691</guid><pubDate>Wed, 15 Dec 2010 16:23:00 +0000</pubDate><atom:updated>2010-12-16T01:24:02.760+09:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>그래픽스</category><title>ACM SIGGRAPH Asia 2010 (day one)</title><description>&lt;p&gt;제가 본 것들에 대한 짤막한 감상입니다.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;b&gt;Scattered Data Interpolation for Computer Graphics&lt;/b&gt; : 여러 가지 data interpolation 기법을 소개하고 실제 구현사례를 소개합니다. skinning, cel shading, path morphing 등 specific한 예를 들어 설명하긴 했는데 보간법이라는 것이 키프레임 기반의 애니메이션에서 핵심적인 역할을 하다 보니 CG 애니메이션 전반적으로 중요한 기법이라 흥미롭게 보았습니다. Voronoi diagram을 이용한 tessellation에 대해서도 소개하긴 하던데 그냥 간단히 훑고 넘어가는 것 같아서 아쉬웠습니다. 중간에 나와서 잘은 모르지만 뒤에 더 설명했으려나….&lt;/li&gt;&lt;li&gt;&lt;b&gt;Toy Story 3 Double Feature: Characters and Lighting&lt;/b&gt; : 토이 스토리 3의 character development와 주요 장면의 lighting setup에 대해서 다뤘습니다. 픽사가 언제나 그랬듯이 이런 류의 프리젠테이션에서 기술에 대해서 in-depth하게 다루기보다는 주로 감성적인 부분을 중심으로 소개하는 편인데, 사실 타깃 오디언스가 엔지니어보다는 아티스트이긴 하지만 워낙에 픽사 영화를 좋아하는지라 흥미롭게 보았습니다.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Lightfield Photography: Theory and Methods&lt;/b&gt; : 기대하면서 프리젠테이션 룸에 앉아 있는데 발표자가 회장에 아직 도착하지 못했다고 하면서 세션이 취소되었습니다. 대체 이게 뭔….&lt;/li&gt;&lt;li&gt;&lt;b&gt;Introduction to Computer Graphics&lt;/b&gt; : 말 그대로 학부 컴퓨터 그래픽스 개론 수준의 강의. drawing primitives, projection, lighting, shading 등등 이쪽 일을 하고 있다면 숨쉬는 공기처럼 기본적인 것들을 다루더군요. 앞의 세션이 파토나버린 관계로 잠깐 시간 때우는 용도로 봤습니다. 그런데 방이 지나치게 작어서 사람들이 꽉 찬 나머지 바깥까지 대기줄이 서서 방 안쪽에 있는 사람이 한명 나가면 대기줄의 한명이 들어가는 식으로 하더군요. 앞 세션 취소된 것도 그렇고 행사 진행이 은근 매끄럽지 않은 점이 걸립니다. 이런 식으로 보고 싶은 세션 못 보는 일이 생긴다면 나중에는 환불 요구하는 사람도 생길 거 같다는 생각이 듭니다. 보고 싶은 세션 보러 왔는데 이런 식으로 방이 꽉 차서 들어가지도 못한다면…. 여기 참가하려고 단돈 만원을 낸 것도 아닐 거고.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Fast Forward Session&lt;/b&gt; : 정신 없었습니다. 그냥 앞으로의 토크에 대한 맛보기 수준.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;여담이지만 FC라고 적힌 네임택 달고 다니는 사람이 어찌나 부럽던지….&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/931078517202405968-3892683445868602691?l=ko.blog.influx.kr' alt='' /&gt;&lt;/div&gt;</description><link>http://ko.blog.influx.kr/2010/12/acm-siggraph-asia-2010-day-one.html</link><author>noreply@blogger.com (segfault)</author><thr:total>3</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-931078517202405968.post-5330304894704184778</guid><pubDate>Tue, 02 Nov 2010 11:57:00 +0000</pubDate><atom:updated>2010-11-03T00:23:05.251+09:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>게임</category><category domain='http://www.blogger.com/atom/ns#'>번역</category><title>존 카맥이 iPhone용 RAGE를 말하다</title><description>&lt;p&gt;지난 8월 12일 존 카맥은 QuakeCon 2010의 기조 연설에서 iOS용 RAGE를 발표하여 많은 사람들을 놀라게 했습니다. 그리고 발매일을 약 한 달 앞둔 시점에서 카맥은 &lt;a href="http://www.bethblog.com/"&gt;베데스다 블로그&lt;/a&gt;를 통해서 iOS용 RAGE의 개발 비화와 기술적인 사항을 정리한 포스팅을 남겼습니다. RAGE에 대한 언급은 물론, 그래픽스와 사운드, 리소스 관리 등 게임의 전반적인 기술적 사항에 대해서 언급하고 있으므로 RAGE 게임에 관심이 있는 분은 물론 모바일 게임 개발에 관심 있는 분들도 한번 쯤 보시는 게 좋겠습니다. 회사에서 눈치 보면서 대충대충 의역한거라 내용이 부정확할 수 있습니다. 만약 시간 남으시는 분들은 원문과 대조해서 오역 발견하시면 알려 주시면 감사하겠습니다. 원문은 &lt;a href="http://bethblog.com/index.php/2010/10/29/john-carmack-discusses-rage-on-iphoneipadipod-touch/"&gt;여기&lt;/a&gt;에서 볼 수 있습니다. 그리고 몇 가지 부분에서 번역에 조언해 주신 &lt;a href="http://mearie.org/"&gt;lifthrasiir&lt;/a&gt;님께 감사드립니다.&lt;/p&gt;&lt;h2&gt;iPhone 용 RAGE&lt;/h2&gt;&lt;p&gt;작년에 id에서의 모바일 게임 개발 작업에는 우여곡절이 있었습니다. 계획은 언제나 다음에 RAGE 같은 것을 iPhone/iPad/iPod 터치용으로 만드는 것이었지만 id에서 진행중인 큰 프로젝트의 뒤에 가려져서 모바일 게임 개발은 그다지 우선순위가 높지 않았습니다. 물밑에서 진행중인 작업이 있긴 했지만, 7월 말이 되어서야 프로젝트를 본격적으로 이끌어 나갈 코어 엔진 코드에 손을 댈 수 있었습니다.&lt;/p&gt;&lt;p&gt;저는 일이 잘 풀리자 흥분했습니다. 그리고 그 때는 QuakeCon이 막 시작할 무렵이었죠. 그래서 저는 전통을 깨고 제 기조 연설에서 기술 데모를 시연했습니다. 나중에 생각해 보면 저는 그 때 소개를 나쁘게 했던 것 같습니다. 아마 제가 이렇게 말했던 것 같군요. "이건 RAGE구요, iPhone 용이구요, 60 fps로 돌아가요." 그래서 몇몇 사람들은 PC/콘솔판 RAGE 게임 전체를 iPhone에서 플레이할 수 있다는 의미로 해석한 것 같습니다. 하지만 그런 건 절대로 아닙니다.&lt;/p&gt;&lt;p&gt;제가 시연했던 것은 처음부터 새로 작성된 기술 데모였습니다. 하지만 RAGE의 컨텐츠 제작 파이프라인과 데이터를 활용해서 만들어졌죠. 우리는 iOS에 RAGE 본편 전체를 옮기지 않았습니다, 아니 엄두조차 내지 않고 있습니다. 만약 (놀랍게도!) PC/콘솔 버젼의 RAGE 게임을 약간의 노력만으로도 iPhone 4에다가 컴파일할 수 있게 된다고 하더라도 그것은 절망적으로 나쁜 아이디어일 것입니다. 가장 최근에 나온 최고의 모바일 장비조차도 Xbox 360과 PS3 성능의 극히 일부밖에 되지 않습니다. 그러므로 이 플랫폼에 성능의 트레이드오프를 만드는 것 자체부터 적절하지가 않습니다. 조작의 차이야 말할 것도 없고요.&lt;/p&gt;&lt;p&gt;우리가 만들고 있는 것은 iOS 플랫폼에서 이전에 본 적이 없는 것입니다. 눈부시게 아름다우며 무척 재미있습니다. 지난 QuakeCon 이후로 개발 작업은 격렬히 진행 중이고 11월이 끝나기 전까지는 앱을 출시할 수 있기를 바랍니다.&lt;/p&gt;&lt;p&gt;이 게임의 제작에 메가텍스쳐 컨텐츠 제작 파이프라인을 사용하게 된 것은 이 게임의 범위 때문입니다. 이 게임에 들어가는 데이터는 큽니다. 그것도 아주 크죠. 하지만 700메가가 넘는 iPhone 용 Myst&lt;sup&gt;1&lt;/sup&gt;를 보면서 사람들이 아무리 커다란 앱이라도 다운받을 것이라는 자신감을 가지게 되었습니다. 그리고 그 용량이 RAGE의 SD 버젼의 용량이 되었고 iPad / iPhone 4용 HD 버젼은 그 용량의 2배 가까이 될 것입니다. 이건 앱이라기보단 차라리 영화를 내려받는 것과 비슷할 겁니다. 그러니 오랜 다운로드에 대비해 두세요. 다른 관점에서 보면 RAGE 게임 본편은 JPEG-XR로 압축된 20 기가의 데이터로 채워져 있으니 무압축 0.7 기가의 데이터는 분명 거기서 아주 작은 일부일 뿐이겠지요.&lt;/p&gt;&lt;p&gt;그렇게 광활한 레벨을 게임에 넣지 않을 계획이었기 때문에 게임이 얼마나 좋게 보이든간에 가격을 높이 책정하면 많은 사람들이 실망할 것이라는 것을 알고 있었습니다. iPhone 타이틀의 가격대를 너무 낮은 가격은 피하되 다양하게 책정하면서 실험해 보았습니다. 그리고 우리는 RAGE의 SD 버전은 $0.99, HD 버전은 $1.99로 책정하는 것이 적당하다는 결론을 내렸습니다. 우리는 이 프로젝트를 최대한 우리의 통제하에 놓을 수 있도록 집중해야 했습니다. 하지만 사람들은 이 가격에 만족할 거라고 생각합니다.&lt;/p&gt;&lt;p&gt;우리는 RAGE 게임의 일부분 중에서 "Mutant Bash TV"를 가지고 iPhone 게임을 만들기로 결정하였습니다. Mutant Bash TV는 RAGE의 황무지 세계관에 있는 포스트-아포칼립틱 전투 게임 TV 쇼인데, 이것은 적을 조준하고, 쏘고, 재장전을 하고, 적의 공격을 피하고, 지난 번보다 더 나은 점수를 얻으면서 레벨의 끝까지 가는 등 진정한 일인칭 슈팅 게임으로서는 최적의 설정이지요. 기본적인 서바이벌 외에도  주울 수 있는 아이템이나 헤드 샷, 힛 스트릭 배수&lt;sup&gt;2&lt;/sup&gt; 등 게임성에 깊이를 더하는 부분도 있으며 그냥 쏘기만 하면 끝나는 것부터 거의 불가능에 가까운 것까지 광범위한 스킬 레벨도 있습니다.&lt;/p&gt;&lt;p&gt;이 프로젝트의 최대 목표는 여러번 다시 플레이할 수 있는 게임을 만드는 것입니다. 이런 게임플레이의 핵심은 스토리 진행이나 캐릭터의 발전이나 기타 등등이 아닌 보상의 측면에 있습니다. 예를 들어서 둠 레저렉션에서 게임의 많은 요소가 처음 플레이할 때는 좋았지만 나중에는 리플레이 가치를 떨어트렸죠. RAGE iOS판은 언제나 액션 뿐입니다. 저는 수십 번 이 게임을 플레이해 보았고, 테스트하는 작업은 여전히 일이라기보다는 즐거움입니다.&lt;/p&gt;&lt;h2&gt;기술적 상세&lt;/h2&gt;&lt;p&gt;id Tech 5 엔진은 기본적으로 게임상의 모든 것에 균일하게 페이징된(uniform paged) 가상 텍스쳐 시스템을 사용합니다. 알고리즘 자체는 3GS나 그 이후 장치들에서 구현할 수 있지만 프래그먼트당 처리 코스트가 상당한 데다가 PVRTC&lt;sup&gt;3&lt;/sup&gt; 형식의 텍스쳐에다가 개별 페이지를 업데이트하는 것은 불가능합니다. 그래서 모바일용 RAGE에서 사용한 방법은 월드 전반에 가변적인 사이즈를 가지는 인접한 "텍스쳐 섬"들을 만들어서 텍스쳐 스트리밍을 적용하는 것이었습니다. 이 방법은 훨씬 빠르지만 큰 표면이 있으면 강제로 분할(geometric subdivision)해야 하며 피드백에 의한 반응이 아닌 철저히 예측적인 방식으로 스트리밍을 해야 합니다. 캐릭터, 아이템, UI는 전통적인 방식으로 텍스쳐링됩니다.&lt;/p&gt;&lt;p&gt;우리는 PC에서 레벨을 만들어서 RAGE에서 돌려보았습니다. 그리고 프로파일링 / 추출 도구를 사용하여 iOS용 맵 데이터를 생성했습니다. 이 도구는 지도 상의 경로를 찾아 다니면서 어느 텍스쳐 섬이 언제, 무슨 해상도와 무슨 방향으로 보일 지를 결정합니다. 텍스쳐 섬의 픽셀 정보는 원래 RAGE의 큰 페이지 파일에서 추출되어 필요한 만큼 여러 해상도로 이방성 필터링&lt;sup&gt;4&lt;/sup&gt;이 적용된 후 장치에 맞는 여러 개의 1024x1024 PVRTC 포맷으로 압축되어 들어가게 됩니다.&lt;/p&gt;&lt;p&gt;텍스쳐 형태로 압축하는 것은 텍스쳐 데이터를 전부 구겨 넣어서 앱 용량을 최소함과 동시에 한 뷰를 수백 개의 텍스쳐 섬으로 쪼개고 싶지는 않다는 서로 상충하는 목표가 있습니다. 다른 NP-완전&lt;sup&gt;5&lt;/sup&gt;한 문제들과 마찬가지로 그리디&lt;sup&gt;6&lt;/sup&gt;한 측정 방식의 최적화 배치 전략을 사용했습니다.&lt;/p&gt;&lt;p&gt;1 기가바이트가 넘는 미디어를 관리하는 데에 플래시 메모리 IO와 프로세스 메모리 관리는 매우 중요하며, 이것들을 연구하기 위해서 상당히 많은 성능 측정 작업을 거쳤습니다.&lt;/p&gt;&lt;p&gt;결정적으로 모든 데이터는 정적이며 쉽게 버릴 수 있습니다. iOS는 스왑 파일&lt;sup&gt;7&lt;/sup&gt;이 없기 때문에 만약 당신이 너무 많은 동적 메모리를 사용하면 OS는 한두번 경고하다가 이내 프로세스를 죽여 버립니다. iOS 개발자들의 골칫거리가 그 "너무 많은" 메모리가 정확히 얼마인지 정의되어 있지 않다는 건데, 사실 이것은 다른 앱들이 (사파리, 메일, iPod 등등) 메모리에 얼마나 상주해 있느냐에 따라서 달라지는 문제입니다. 만약 당신이 모든 게임 데이터를 메모리에 상주시킨다면 OS는 아무 것도 할 수 없습니다. 그리고 당신은 위험에 빠지게 되는 것이죠. 하지만 만약 당신의 데이터가 읽기 전용의 메모리 맵 파일에 들어있다면 OS는 필요할 시 그것들을 해제할 수 있습니다. 이렇게 하면 만약 다음 (메모리 영역)이 필요할 시 게임이 뚝뚝 끊기는 문제가 있겠지만 최소한 갑자기 프로그램이 종료되는 문제는 없습니다. 다만 메모리 부족 경고는 여전히 프레임레이트를 몇 초 동안 지옥에 보내버리는데, 심지어 그 시점에서 게임 자체에서 하는 일이 별로 없다고 하더라도 백그라운드의 다른 모든 앱에서 메모리를 버리기 때문입니다.&lt;/p&gt;&lt;p&gt;흥미있게도 32비트 운영체제임에도 불구하고 단지 700 메가바이트의 가상 메모리 공간에 매핑을 할 수 있습니다. 3 기가 정도는 안 되더라도 저는 최소한 그 두 배는 기대했거든요.&lt;/p&gt;&lt;p&gt;페이지 폴트&lt;sup&gt;8&lt;/sup&gt; 시 메모리 맵 파일에서 32kb의 데이터를 가져오는데 iPhone 4에서는 1.8 ms, iPod touch 2세대에서는 2.2 ms가 소요됩니다. 만약 파일의 맨 처음에서 폴트가 일어나면 32kb가 아닌 128kb를 가져 오는데, 파일의 처음에는 헤더가 있다고 가정한 일종의 최적화로 보입니다.&lt;/p&gt;&lt;p&gt;Apple의 운영체제에서 기존 UNIX 플래그가 제대로 동작하는지 항상 불안했기 때문에 iOS서 fcntl(fd, F_NOCACHE) 함수가 기대했던 대로 정확히 동작한 것이 다행이라고 말씀드리고 싶습니다. 이걸 사용하면 파일 캐싱을 건너뛰면서 페이지로 정렬된 타겟 메모리의 페이지 폴트 대역폭이 30mb/s (구형 아이팟 터치는 22mb/s)로 매우 빠른 성능을 보여 줍니다. 그리고 일반적인 읽기와는 달리 메모리의 복사가 전혀 일어나지 않기 때문에 부분적으로 더 빨라질 수 있습니다. 하지만 가장 중요한 점은 캐싱을 끔으로서 더 중요한 국소성&lt;sup&gt;9&lt;/sup&gt;을 지니는 다른 버퍼 데이터를 침범하지 않는다는 점입니다. 월드 상의 모든 메가텍스쳐 데이터는 제가 언제 먼저 미리 읽어 놓을지를 알고 있기 때문에 캐싱되지 않은 방식으로 읽혀집니다. 그리고 데이터를 버려야 할 시점도 명확합니다. 만약 주어진 영역을 지나가면 그 영역의 텍스쳐 데이터는 앞으로 다시는 필요하지 않을 것입니다. 반면 적들의 애니메이션과 사운드 데이터 같은 데이터는 나중에 다시 나타날 수도 있습니다.&lt;/p&gt;&lt;p&gt;저는 텍스쳐 읽는 작업이 끝난 뒤에 캐싱되지 않은 읽기 스레드에서 관계된 월드의 지오메트리 정보를 미리 읽도록 해 보았습니다. 하지만 생각해 보면 지오메트리 정보를 텍스쳐에 같이 끼워넣고 캐싱되지 않은 읽기를 했어야 했습니다.&lt;/p&gt;&lt;p&gt;OpenAL&lt;sup&gt;10&lt;/sup&gt;은 1024개까지 사운드 버퍼를 만들 수 있는 제약이 있는 것 같습니다. 우리는 큰 문제를 일으키지 않고 정적 버퍼 매핑을 동적으로 생성하고 삭제하고 싶었습니다. 그래도 1024개는 항상 그 아래로 유지할 수 있는 합리적인 갯수입니다.&lt;/p&gt;&lt;p&gt;OpenAL에서 (디스어셈블을 통해) 발견하고 놀란 동작 중 하나는 Play() 명령을 실행하면 버퍼의 4킬로바이트를 항상 건드린다는 것입니다. 말이 되는 동작인데, 사운드 믹싱을 깨트리지 않으려면 일단 버퍼를 램에다가 강제로 페이징해서 올려야 하기 때문입니다. 하지만 호출시 스레드가 예측할 수 없이 잠깐씩 멈추더군요. 그게 적당한 크기의 믹스 어헤드 버퍼를 가진 믹싱 스레드에서 발생한 페이지 폴트로 말미암은 것이었으면 하고 바랐습니다만, 아마도 실제로는 GPU가 이미 모든 버퍼 대역폭을 차지해 버려서 십여 개의 사운드 버퍼가 폴트를 내 버리는 극단적인 상황이라 생각합니다. 그래서 모든 OpenAL 명령을 별개의 스레드에서 처리하도록 만들까 생각중입니다. 그렇게 하면 만약 채워야 할 페이지가 있을 경우 프레임레이트가 불안정해지는 대신에 사운드에 약간의 지연이 생기겠지요.&lt;/p&gt;&lt;p&gt;플래시 메모리 읽기 대기열에 우선순위를 부여할 수 있었다면 좋겠다는 생각을 했습니다. 게임 스레드에서 CPU 폴트에 가장 높은 우선순위를, 사운드 샘플 읽기는 중간, 텍스쳐 읽기는 가장 낮게. 큰 텍스쳐를 여러 개의 덩어리로 나누는 것이 CPU가 멈추는 최악의 케이스를 방지할 수 있다는 것을 발견했습니다.&lt;/p&gt;&lt;p&gt;이 프로젝트에서 고심을 거듭하여 결정한 두 가지 기술적인 점이 있습니다.&lt;/p&gt;&lt;p&gt;기본적인 렌더링 기술은 모두 고정 파이프라인 기능만을 가지고 표현할 수 있는 것을 알고 있었기에 게임을 OpenGL ES 1.1을 사용하여 만들었습니다. 덕분에 낡은 MBX&lt;sup&gt;11&lt;/sup&gt; GPU 플랫폼에서도 구동이 가능합니다. 구형 플랫폼을 지원하는 것이 좋긴 하나 그것들은 이제 시장에서 무시해도 좋을 정도라는 증거는 충분합니다. 그리고 저는 이 결정을 위해 몇 가지 기능 구현과 최적화를 할 기회를 포기해야 했습니다.&lt;/p&gt;&lt;p&gt;구닥다리 고정 기능을 가지고 삽질하는 것은 재미있었습니다. 예를 들어 싱글 패스로 DOT3 법선 매핑(normal mapping) 위에 단색 동적 광원(dynamic lighting) 효과를 얻기 위해서 조명 수치를 텍스쳐의 환경 색(environment color)의 알파 채널에 붙이는 방법을 사용했습니다. 이렇게 하면 색이 GL_SRC_ALPHA, GL_ZERO 블렌딩 모드를 거쳐서 혼합되어 불투명한 캐릭터에 영향을 주게 됩니다. 이런 식의 고정 파이프라인 트릭을 쓰다 보면 여전히 흐뭇한 기분이 들지만 사실 이런 건 요즘같은 픽셀 쉐이더의 시대에 필요한 스킬은 아니지요.&lt;/p&gt;&lt;p&gt;다른 큰 문제는 코드베이스의 혈통입니다.&lt;/p&gt;&lt;p&gt;제가 개인적으로 작성한 iPhone의 코드에는 울펜슈타인 RPG의 렌더러, 울펜슈타인 클래식과 둠 클래식의 모든 iPhone용 코드, 그리고 몇 가지 테스트 어플리케이션의 코드가 포함되어 있습니다. 그 시점에서 저는 플랫폼에서 '해야 할 옳은 것들'이 뭔지는 잘 알고 있었지만 어떻게 하면 하나의 온전한 게임에 적용시킬 수 있을 지에 대한 원숙한 표현법은 잘 몰랐습니다. 둠 클래식에는 괜찮은 코드가 있었지만 모두 C 언어로 작성된 것들이었고 저는 새로운 게임은 (절제된) C++ 언어로 개발하는 것을 선호할 것입니다.&lt;/p&gt;&lt;p&gt;우리가 선택할 수 있었던 것은 Escalation Studios에서 개발한 (저에게 그닥 접점은 없었던) 둠 레저렉션이었습니다. 게임의 플레이 스타일도 어느 정도 비슷했기 때문에 (RAGE에서 돌아볼 거리가 많긴 하지만요) 이걸 사용하는 것은 합리적인 선택인 것 같았습니다. 이것은 "&lt;a href="http://www.joelonsoftware.com/articles/fog0000000069.html"&gt;코드를 절대로 버리지 말라&lt;/a&gt;"는 신조와도 일치하는 것이기도 합니다. 저는 제가 성공을 거듭하는 동안 코드를 싸그리 뭉쳐서 갖다 버린 적이 많은지라 저 글의 여러 가지 부분에서 동의하지 않습니다만 저 글에는 여전히 지혜가 녹아들어 있습니다.&lt;/p&gt;&lt;p&gt;만약 처음부터 새로(from scratch) 만든다고 했을때 코드베이스가 어떻게 될 것인가에 대해서는 긍정적이었습니다. 100k 미만의 가변적인 CPU 데이터를 가질 것이며 게임의 리소스와 관련된 어떤 문자열 데이터도 보이지 않을 것이며&lt;sup&gt;12&lt;/sup&gt; 새로운 플랫폼에서는 60 fps, 구형 플랫폼에서는 30 fps로 돌아갈 것이라고 생각했습니다. 그리고 저는 이걸 대략 4개월 내에 할 수 있다고 확신했습니다. (하지만 그건 아마 오산이었겠죠) 하지만 불행히도 아이폰 프로젝트에 4개월을 할애할 수 없는 상황이어서 2개월으로 밀어붙이게 되었습니다. (지금은 거의 완성된 상태이며 다시 돌아갈 연구 주제를 찾고 있습니다.)&lt;/p&gt;&lt;p&gt;그래서 우리는 개발 편의와 코드 효율성을 타협하는 조건으로 레저렉션의 코드베이스 위에서 만들기 시작했습니다. 이것은 저에게는 흥미로운 경험이었는데, 원래 제가 다루는 모든 코드는 저의 "코딩 DNA"가 있는 것이어야 했기 때문입니다. 왜냐 하면 id 소프트웨어의 코딩 표준은 기본적으로 "존 카맥이 하는 식으로 코딩하라"이기 때문이거든요. 반면 Escalation의 프로그래머들은 완전히 다른 배경을 가지고 있었기에 원래의 코드베이스는 "여기에는 STL&lt;sup&gt;13&lt;/sup&gt;이 있고 저기에는 boost&lt;sup&gt;14&lt;/sup&gt;가 있고 프로퍼티 리스트&lt;sup&gt;15&lt;/sup&gt;를 채우고 이벤트를 디스패치하고 저걸 위임(delegate)하라" 같은 식이었죠.&lt;/p&gt;&lt;p&gt;저는 다른 큰 게임들의 코드베이스가 그에 고통받고 있음에도 불구하고 우리의 거대한 코드베이스에서 다양한 "현대적" C++ 디자인 패턴을 활용함으로서 이득을 볼 수 있을 거라는 생각을 하고 있었습니다. 하지만 저는 이 생각을 철회했습니다.&lt;/p&gt;&lt;p&gt;저는 여기에 대해서 (주로 트위터에) 자주 불평하고 있으며 가끔 다른 모바일 개발자들에게 다양한 실례들을 지적하고는 합니다. 지금까지 이 방법은 통하고 있으며 아마 결국 이것은 옳은 결정이었을 겁니다.&lt;/p&gt;&lt;p&gt;존 카맥&lt;br /&gt;&lt;br /&gt;10-26-2010&lt;/p&gt;&lt;h2&gt;잡담&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;설마 했는데 정말 OpenGL ES 1.1만으로 게임을 만들었군요. 요즘 스마트폰이나 모바일 장치는 기본이 OpenGL ES 2.0이라 그런지 구형 스펙을 가지고 이 정도 퀄리티의 게임을 만들어 냈다는 것이 믿기지가 않습니다. 생각해보면 텍스쳐 스트리밍 자체가 고정 파이프라인만 가지고도 구현할 수 있는 것이긴 하지만 모바일에서도 프로그래머블 파이프라인으로 쉐이더 써서 게임 만드는 시대에 고정 파이프라인으로 이 정도 게임을 만들었다는 것이 놀랍습니다.&lt;/li&gt;&lt;li&gt;최근에 존 카맥이 트위터로 공개한 사실이지만 안드로이드용 RAGE도 준비중이라고 합니다. 포스팅의 내용에 따르면 iOS용은 PVRTC를 사용했다는 거 같은데 이것은 아이폰의 하드웨어가 PowerVR GPU 위에서만 돌아가고 있기 때문에 가능한 것인 반면 하드웨어에 중립적인 안드로이드 장치에서 PowerVR 전용 확장을 쓰긴 어렵겠죠. Qualcomm의 Adreno, ARM의 Mali, NVIDIA의 Tegra 등 다른 GPU를 쓰는 안드로이드 장치도 많을 텐데 안드로이드 포트에서는 이 문제를 어떻게 해결할 지 사뭇 궁금해집니다. iOS용은 PVRTC 텍스쳐 스트림을 그대로 데이터에 때려박았다는 듯한 뉘앙스인데, 안드로이드 포트는 아예 렌더링 패스를 다르게 구현하려나?&lt;/li&gt;&lt;li&gt;저 바닥에서 짬밥 좀 많이 먹은 프로그래머라면 대체적으로 그렇지만 이 양반도 디자인 패턴이나 개발 방법론에 대해서는 보수적인 면이 있는 것 같습니다. 하긴 뭐 예전부터 그랬으니 별로 새로울 건 없군요. GPL로 공개한 소스를 까 보면 구조 자체는 별다른 언어적 기교 없이 무척 심플하게 되어 있었죠. 여담이지만 롤러코스터 타이쿤 시리즈의 제작자 크리스 소여는 Z80 시절부터 어셈블리로 프로그래밍을 한 영향인지 롤러코스터 타이쿤 2까지 코드 대부분을 x86 어셈블리로 짰다는 전설은 유명하죠.&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;각주&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;Myst: 1993년 Mac OS로 나온 그래픽 어드벤쳐 게임입니다. 나중에 iPhone으로 포팅이 되었는데 용량이 720MB에 달했습니다. 다른 평균적인 iPhone 어플리케이션에 비해서 굉장히 큰 편이죠.&lt;/li&gt;&lt;li&gt;FPS 게임에서 죽지 않고 적을 연속으로 계속 죽이면 보너스가 주어지는 게임플레이 요소.&lt;/li&gt;&lt;li&gt;PVRTC: iPod/iPod touch/iPad에서 사용하는 PowerVR 그래픽스 프로세서에서 사용되는 블럭 단위의 텍스쳐 압축 알고리즘입니다. S3TC나 DXTC와는 다르게 저주파 이미지 두개를 Bilinear Interpolation으로 업스케일해서 변조하는 식으로 구현합니다. 알고리즘 상세는 여기서 확인하실 수 있습니다: &lt;a href="http://web.onetel.net.uk/~simonnihal/assorted3d/fenney03texcomp.pdf"&gt;http://web.onetel.net.uk/~simonnihal/assorted3d/fenney03texcomp.pdf&lt;/a&gt;&lt;/li&gt;&lt;li&gt;이방성 필터링(Anisotropic filtering): 텍스쳐를 여러 장 다운샘플링하여 보이는 각도에 따라 다른 해상도의 텍스쳐를 부분적으로 적용하는 테크닉입니다. 밉 맵(MIP Map)과 비슷하지만 밉 맵은 렌더링 속도를 향상시키기 위한 테크닉이고, 이방성 필터링은 비스듬한 물체의 표면에서 렌더링 퀄리티를 향상시키기 위한 목적입니다. 밉 맵은 등방성(isotropic)인 반면 이방성 필터링은 가로 세로 다른 비율로 이미지를 다운샘플링합니다.&lt;/li&gt;&lt;li&gt;NP-완전: &lt;a href="http://en.wikipedia.org/wiki/NP-complete"&gt;http://en.wikipedia.org/wiki/NP-complete&lt;/a&gt;&lt;/li&gt;&lt;li&gt;탐욕(그리디) 알고리즘: 순간순간마다 최적이라고 생각되는 것을 골라 나가는 알고리즘입니다.&lt;/li&gt;&lt;li&gt;스왑 파일: 페이징 개념에서 말하는 가상 메모리 파일.&lt;/li&gt;&lt;li&gt;페이지 폴트: 가상 메모리에 저장되어 있지만 물리적인 기억 장치에 올라오지 않은 메모리 영역이 있을 경우 이를 물리적 메모리에 올리기 위해서 소프트웨어에서 발생시키는 트랩(trap).&lt;/li&gt;&lt;li&gt;일시적 참조의 국소성(Temporal locality of reference): 메모리 영역에서 순간적으로 접근이 많이 이루어지는 부분을 말합니다. 이를테면 캐싱으로 인해서 빈번하게 접근되는 메모리 영역이 엉킬 수가 있겠죠.&lt;/li&gt;&lt;li&gt;OpenAL: Creative Technology에서 개발한 다채널 오디오 API입니다. 이름에서 알 수 있듯이 OpenGL의 construct를 상당 부분 모방하고 있으며, iPhone 개발 환경에서 오디오 API의 일환으로 제공하고 있습니다.&lt;/li&gt;&lt;li&gt;PowerVR MBX: 구형 iPhone(1세대, 3G)와 iPod touch(1세대, 2세대)의 AP에 탑재되어 있는 GPU입니다. OpenGL ES 1.1 스펙에 대응하고 있습니다.&lt;/li&gt;&lt;li&gt;아마 리소스 문자열을 코드베이스 내에 하드코딩하는 것을 의미하는 것 같습니다.&lt;/li&gt;&lt;li&gt;STL: C++의 표준 템플릿 라이브러리입니다. container, iterator, functor, algorithm 등 다양한 템플릿 기반의 편의 기능을 제공합니다.&lt;/li&gt;&lt;li&gt;Boost: C++의 연장선상에 있는 라이브러리의 집합입니다. 이 라이브러리의 몇 가지 기능들은 차기 C++0x 표준의 일부로 편입되기도 했습니다.&lt;/li&gt;&lt;li&gt;프로퍼티 리스트(plist): NeXTSTEP과 여기서 파생된 애플의 API에서 제공하는 직렬화(serializable) 가능한 데이터를 저장하기 위한 파일과 이에 접근하기 위한 API를 말합니다.&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/931078517202405968-5330304894704184778?l=ko.blog.influx.kr' alt='' /&gt;&lt;/div&gt;</description><link>http://ko.blog.influx.kr/2010/11/iphone-rage.html</link><author>noreply@blogger.com (segfault)</author><thr:total>2</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-931078517202405968.post-8824738816050425736</guid><pubDate>Sat, 02 Oct 2010 17:14:00 +0000</pubDate><atom:updated>2010-10-07T02:06:57.661+09:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>영화</category><title>&lt;불청객&gt; 감상 소감.</title><description>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_wsaBTslQUzs/TKdoXRUFUYI/AAAAAAAAAFY/i17iTBVlBH4/s1600/uninvited.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://3.bp.blogspot.com/_wsaBTslQUzs/TKdoXRUFUYI/AAAAAAAAAFY/i17iTBVlBH4/s320/uninvited.jpg" width="225" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;최근에 디시인사이드 &lt;a href="http://gall.dcinside.com/list.php?id=hit&amp;no=9880&amp;page=1&amp;bbs="&gt;힛갤에 올라간 그 영화&lt;/a&gt;이지요. 어쩌다 보니 기회가 되어 지인과 같이 &lt;a href="http://www.filmforum.co.kr/"&gt;필름포럼&lt;/a&gt;에 가서 보고 왔습니다.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;백수들이 모여 사는 자취방이 갑자기 통째로 은하계로 날아가 버린다는 설정은 좋습니다. 이 설정 하나만으로도 힛갤감.&lt;/li&gt;&lt;li&gt;디시인사이드에 심취한 감독이 만들어서 그런지 특유의 루저 정서가 작품 전체를 관통합니다. 출연 배우들도 실제로 비슷한 생활을 했다고도 하고 전체적으로 빈티나는 분위기만큼은 제대로 표현이 되었던 것 같습니다. 작품의 프로덕션 밸류를 봐도 그런 게 바로 느껴지지 않습니까?&lt;/li&gt;&lt;li&gt;하지만 대사는 심심하고 개연성이 부족해서 극적인 재미는 떨어집니다.&lt;/li&gt;&lt;li&gt;연기 수준은 딱 기대했던 수준만큼이었습니다. 애초에 "자취방 같이 사는 형들"이랑 찍은 영환데 뭘 더 기대하랴….&lt;/li&gt;&lt;li&gt;감독이 서울대 출신의 엘리트라고 화제가 되었길래 청년실업에 대한 뭔가 특별한 문제의식을 기대했는데 특별한 건 없더군요. 고작해야 포인트맨의 "ㅉㅉ 한심한 백수들" 정도가 전부.&lt;/li&gt;&lt;li&gt;CG가 매우 많이 들어간 영화인데 당연하지만 특수효과 수준은 조악합니다. 그나마 무중력 장면이라든지 몇몇 장면은 괜찮았던 것 같습니다.&lt;/li&gt;&lt;li&gt;기본 설정은 &amp;lt;고무인간의 최후&amp;gt;를 연상시키고 중간중간에 &amp;lt;2001: 스페이스 오딧세이&amp;gt; 같은 영화의 오마주가 나옵니다. 이런 걸 찾아보는 재미가 있습니다.&lt;/li&gt;&lt;li&gt;감독 본인도 이 영화가 극장에 걸릴거라고는 기대하지 않았다고 합니다. 원래 필름스쿨 들어가려고 포트폴리오로 만들던 작품이라고 하는데 어쩌다보니 부천 영화제에 출품되었고 소규모지만 극장개봉도 하게 되었다고 하는군요.&lt;/li&gt;&lt;li&gt;단점이 많지만 그렇다고 이 영화가 나오지 말았어야 할 영화인가? 최소한의 자원으로 대규모(?) 스케일의 영화를 만들었다는 점은 높이 사야 할 점입니다. 결과물은 조악하지만 이 말도 안 되는 기획을 실천에 옮기기 위해서 발산했던 큰 에너지가 느껴집니다. 이런 영화에 상업영화의 잣대를 들이대는 것은 무의미한 일일 겁니다.&lt;/li&gt;&lt;li&gt;디시인사이드가 언급됩니다. 시작할 때 "이 영화를 디씨인사이드에 바칩니다", 그리고 스탭롤에서 한번 더….&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;a href="http://www.flickr.com/photos/segfault87/5043786509/" title="Flickr에서 segfault87님의 이응일 &amp;lt;불청객&amp;gt; 감독과"&gt;&lt;img alt="이응일 &amp;lt;불청객&amp;gt; 감독과" height="375" src="http://farm5.static.flickr.com/4144/5043786509_b47faba04f.jpg" width="500" /&gt;&lt;/a&gt;&lt;br /&gt;감독님과 한 컷.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.flickr.com/photos/segfault87/5043787077/" title="Flickr에서 segfault87님의 노회찬 진보신당 대표와"&gt;&lt;img alt="노회찬 진보신당 대표와" height="375" src="http://farm5.static.flickr.com/4091/5043787077_e87da544e1.jpg" width="500" /&gt;&lt;/a&gt;&lt;br /&gt;진보신당 노회찬 대표도 이 영화를 보러 오셨더군요. 바로 옆 자리에 어디서 본 인물이 있어서 자세히 보니…. 이웃(?) 정당 당원이라고 소개하면서 인사 드리니 흔쾌히 사진을 같이 찍어 주셨습니다.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/931078517202405968-8824738816050425736?l=ko.blog.influx.kr' alt='' /&gt;&lt;/div&gt;</description><link>http://ko.blog.influx.kr/2010/10/blog-post.html</link><author>noreply@blogger.com (segfault)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_wsaBTslQUzs/TKdoXRUFUYI/AAAAAAAAAFY/i17iTBVlBH4/s72-c/uninvited.jpg' height='72' width='72'/><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-931078517202405968.post-493509886489047847</guid><pubDate>Tue, 28 Sep 2010 12:02:00 +0000</pubDate><atom:updated>2010-09-28T21:02:45.692+09:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>konstruktor</category><category domain='http://www.blogger.com/atom/ns#'>개발</category><title>디버깅 시작</title><description>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_wsaBTslQUzs/TKHViDJq5hI/AAAAAAAAAFU/1hFpA6KrA-Q/s1600/konstructor.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://4.bp.blogspot.com/_wsaBTslQUzs/TKHViDJq5hI/AAAAAAAAAFU/1hFpA6KrA-Q/s320/konstructor.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;결국 프로그램의 이름을 또 한번 바꾸었습니다. 새 이름은 &lt;b&gt;Konstruktor&lt;/b&gt;. 별 아이디어도 없는 상황에서 해외 유저가 제안해 주셔서 바꾸게 됐습니다. 이것도 사실 이상적인 이름은 아니지만 KLDraw, Klotz 따위보단 나은 것 같아요.&lt;/p&gt;&lt;p&gt;&lt;a href="http://planetmono.blogspot.com/2010/09/klotz.html"&gt;앞의 포스트&lt;/a&gt;에서 언급했던 normal 역전 버그를 보고 있습니다. 작업 좀 더 편하게 하자고 30분 가량 삽질해서 우선 디버그용 프로그램부터 만들고….&lt;/p&gt;&lt;p&gt;이런 프로그램을 만드는데는 GLUT가 확실히 적격인 것 같습니다. 쓰기 쉽고 간단하기 때문이거든요. 어차피 LDraw 렌더링 코드는 UI 툴킷과 따로 분리가 되어 있기 때문에 추가적인 작업도 불필요했고요. 다만 스트링 출력 함수가 무척 짜증나게 되어 있네요. 원래 GLUT는 스트링 출력 함수가 없고 문자 출력 함수가 있습니다. 다시 말하자면 함수 하나가 한개의 글자밖에 찍을 수 없다는 건데, 따로 wrapper를 만들기 귀찮아서 OpenGLUT의 비표준(?) glutBitmapString() 함수를 써야 했습니다.&lt;/p&gt;&lt;p&gt;일단 10월 내로는 릴리스하겠다고 나 자신과 약속했는데 글쎼요. 과연 지킬 수 있을까….&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/931078517202405968-493509886489047847?l=ko.blog.influx.kr' alt='' /&gt;&lt;/div&gt;</description><link>http://ko.blog.influx.kr/2010/09/blog-post.html</link><author>noreply@blogger.com (segfault)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_wsaBTslQUzs/TKHViDJq5hI/AAAAAAAAAFU/1hFpA6KrA-Q/s72-c/konstructor.png' height='72' width='72'/><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-931078517202405968.post-2553547023977615254</guid><pubDate>Fri, 17 Sep 2010 17:05:00 +0000</pubDate><atom:updated>2010-09-18T02:09:16.478+09:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>음악</category><title>글로벌 개더링 2010</title><description>&lt;a href="http://www.flickr.com/photos/segfault87/4998321001/" title="Flickr에서 segfault87님의 IMAG0102.jpg"&gt;&lt;img src="http://farm5.static.flickr.com/4129/4998321001_262eca80dc.jpg" width="500" height="299" alt="IMAG0102.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://vuent.co.kr/"&gt;글로벌 개더링 2010 홈페이지&lt;/a&gt;&lt;/p&gt;&lt;p&gt;작년에 비해서 라인업이 부실해 보이긴 하지만 지금까지 발표된 해외 라인업 전부 좋아하는 뮤지션이라 별 불만은 없습니다. 특히 팻보이 슬림은 공연 영상 보면서 바로 비행기 티켓 끊어서 브라이튼 해변으로 달려가고 싶은 생각이 들었을 정도니까요. 친히 한국까지 찾아 주신다니 저야 뭐 헤헤 넙죽넙죽.&lt;/p&gt;&lt;p&gt;작년에 가장 아쉬웠던 것이 글로벌 개더링 2009에 못 갔던 것이라 여전히 아쉬움은 남습니다. 프로디지, 언더월드, 로익솝, 펜듈럼, MSTRKRFT 모두 좋아하는 뮤지션이라서요. 특히 요즘 자주 듣고 있는 Röyksopp은 너무 아쉽습니다. 차라리 작년에 안 오고 올해에 왔더라면…. 그러고 보니 얼마 전에 Senior 앨범이 발매된 모양인데 유튜브에서 찾아서 몇 곡 들어보고 사야겠습니다.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/931078517202405968-2553547023977615254?l=ko.blog.influx.kr' alt='' /&gt;&lt;/div&gt;</description><link>http://ko.blog.influx.kr/2010/09/2010.html</link><author>noreply@blogger.com (segfault)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm5.static.flickr.com/4129/4998321001_262eca80dc_t.jpg' height='72' width='72'/><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-931078517202405968.post-871442939119978691</guid><pubDate>Tue, 07 Sep 2010 17:56:00 +0000</pubDate><atom:updated>2010-09-08T02:56:10.256+09:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>안드로이드</category><category domain='http://www.blogger.com/atom/ns#'>개발</category><title>디시인사이드 업로더 사용자 1000명 돌파.</title><description>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_wsaBTslQUzs/TIZ2estbieI/AAAAAAAAAEQ/YKqUcZfWlFI/s1600/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7+2010-09-07+%EC%98%A4%EC%A0%84+12.24.09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="296" src="http://2.bp.blogspot.com/_wsaBTslQUzs/TIZ2estbieI/AAAAAAAAAEQ/YKqUcZfWlFI/s400/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7+2010-09-07+%EC%98%A4%EC%A0%84+12.24.09.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;제가 처음으로 만들어 본 안드로이드용 어플리케이션인 &lt;a href="http://palladium.planetmono.org/dcuploader"&gt;디시인사이드 업로더&lt;/a&gt;의 유저 수가 얼마 전에 1000명을 넘어섰습니다. 1000명이 그리 많은 숫자는 아니지만 제 나름대로의 기준으로 정해두려고 합니다. 이런 저런 버그도 많고 부족한 점도 많은 어플리케이션이지만 각계(?)에서 너무 긍정적으로 평가해 주시고 잘 쓰고 계시니 몸 둘 바를 모르겠습니다.&lt;/p&gt;&lt;p&gt;제가 디시의 충성스러운 이용자도 아닌데 왜 하필 디시업로더를 만들게 됐는지를 설명드리면, 디시인사이드가 말도 많고 탈도 많지만 일단 규모면에서 가장 큰 곳 중 하나거든요. 처음에 모바일 어플리케이션을 만들 생각을 하면서 기존의 대형 커뮤니티를 어떻게 모바일과 연계시켜서 일상적인 상황에서 부담없이 쓸 수 있을지를 고민했습니다. 특히나 디시는 명목상으로는 디카 커뮤니티이기도 하고요. 스마트폰을 가지고 다니면서 바로 꺼내서 사진을 찍고 즉시 디시인사이드에 올리는 것을 생각하고 만든 것이 디시업로더입니다. 게다가 단순히 사진을 올리는 것 뿐만 아니라 &lt;a href="http://m.boxweb.net/"&gt;박스웹&lt;/a&gt;과 &lt;a href="http://m.oolzo.com/"&gt;물조&lt;/a&gt;의 도움을 받아 모바일 페이지와 연계하여 나름의 모바일 SNS로 발전했다고 생각합니다.&lt;/p&gt;&lt;p&gt;앞으로의 계획은 일단 신기능은 비로그인(유동닉) 글쓰기까지만 구현할 거고요, 그 이후에 특별한 일 없으면 아마 버그픽스 정도로 유지할 생각입니다. 혹시 새로운 기능 제안하실 게 있다면 댓글 달아 주세요.&lt;/p&gt;&lt;p&gt;고작 천 명밖에 안 쓰는데 무슨 자뻑질이냐... 하시면 할 말이 없지만 제가 지금까지 만든 프로그램 중에서 이정도로 퍼진 프로그램이 없기 때문에 감회가 새롭군요. 여튼 부족한 프로그램 써 주셔서 감사하고요, 앞으로도 잘 부탁드립니다.&lt;/p&gt;&lt;p&gt;PS: 혹시나 해서 밝히는 거지만 디시업로더는 MIT 라이센스로 배포되는 자유 소프트웨어입니다. 소스 코드는 &lt;a href="http://code.google.com/p/segfault-snippets/"&gt;http://code.google.com/p/segfault-snippets/&lt;/a&gt;에서 받으세요.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/931078517202405968-871442939119978691?l=ko.blog.influx.kr' alt='' /&gt;&lt;/div&gt;</description><link>http://ko.blog.influx.kr/2010/09/1000.html</link><author>noreply@blogger.com (segfault)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_wsaBTslQUzs/TIZ2estbieI/AAAAAAAAAEQ/YKqUcZfWlFI/s72-c/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7+2010-09-07+%EC%98%A4%EC%A0%84+12.24.09.png' height='72' width='72'/><thr:total>3</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-931078517202405968.post-4840805611316897625</guid><pubDate>Mon, 06 Sep 2010 12:04:00 +0000</pubDate><atom:updated>2010-09-06T21:30:40.288+09:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>개발</category><title>Klotz 릴리즈까지 남은 것들</title><description>병특 시작한 이래 작업을 거의 못 하고 있었습니다. 계속 이렇게 방치해둘 수는 없다는 생각에 첫번째 베타 릴리즈까지 해야 할 점들을 정리해 둡니다.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;렌더링 엔진 개선&lt;/h2&gt;&lt;br /&gt;&lt;h3&gt;1. Back Face Culling (BFC)&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;단순히 뒷면을 거르는 것만의 문제가 아닌, 제대로 된 surface normal vector를 구하기 위해서 꼭 구현해야 하는 거라서 릴리즈 전까지는 작업을 해야 하는 부분입니다.&lt;/p&gt;&lt;p&gt;보통 3D 하면 BFC는 기본이지만, LDraw 스펙에서 BFC는 필수가 아니며 일종의 확장으로 구현이 되어 있습니다. 물론 대부분의 primitive는 BFC-compilant하지만 만에 하나 그렇지 않은 경우를 위해서 예외 처리를 해 두어야 합니다.&lt;/p&gt;&lt;p&gt;지금 BFC 자체는 구현이 거의 되어 있지만 렌더링 코드에서 문제가 계속 발생하고 있습니다. 분명히 스펙대로 짰는데 subfile inclusion시 가끔 normal이 역전되는 문제가 발견되고 있습니다. 지금 여기서 작업이 막혀 있는데 그래서 요즘 능력의 부족함을 통감하고 있습니다. 고등학교때 수학 수업 좀 더 제대로 들어 놓을 걸 그랬네요.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;2. Vertex Buffer Object (VBO)&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;지금 렌더링 코드는 전부 glBegin - glVertex - glEnd로 구현이 되어 있습니다. 가장 기본적이며 간단한 방법임과 동시에 가장 비효율적이기도 합니다. 정점 하나 찍거나 색깔 하나 찍는데 함수 호출 하나라니 함수 호출 오버헤드만 해도 상당한 수준이겠지요. 결국 이 방식은 OpenGL ES와 최신 OpenGL 4 스펙에서 (고정기능 파이프라인 함수들과 함께) deprecate된 semantics라 portability를 위하여 VBO 기반으로 코드를 전부 뜯어고칠 필요가 있습니다.&lt;/p&gt;&lt;p&gt;문제는 LDraw 포맷의 계층적 구조가 그닥 VBO와 친하지 않다는 것입니다.&lt;/p&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_wsaBTslQUzs/TITLcb69rbI/AAAAAAAAAD0/cIT_j0_zvV4/s1600/vbo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://1.bp.blogspot.com/_wsaBTslQUzs/TITLcb69rbI/AAAAAAAAAD0/cIT_j0_zvV4/s400/vbo.png" width="400" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;보시다시피 LDraw 모델은 최상단에 모델 파일이 있고 거기서 다른 서브파일들을 참조하는 방식으로 구성이 되어 있습니다. 그리고 가장 밑에 있는 part 혹은 subpart, primitive에서 실제  선, 삼각형, 사각형과 같은 정보를 담고 있습니다.&lt;/p&gt;&lt;p&gt;그럼 여기서 어디까지를 VBO로 만들 것인지의 문제가 있습니다.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;전체 모델 : 버퍼 하나를 만들어서 거기에 모든 데이터를 넣습니다. 가장 단순한 방식이나 모델에 수정이 가해지만 버퍼 전체의 내용을 새로 만들어야 합니다.&lt;/li&gt;&lt;li&gt;Part : 모델 레벨에서 가장 기본적인 단위까지 VBO를 만듭니다. 그러니 part 하나 하나가 object가 되는 방식이지요. 언뜻 합리적으로 보이나 밑에 얽혀있는 수많은 primitive 데이터가 각 part마다 중복되어 들어가므로 메모리가 낭비되는 문제가 있습니다.&lt;/li&gt;&lt;li&gt;Primitive : primitive 도형들에 대해서 VBO를 만듭니다. 다만 primitive의 색상은 caller의 색상을 따르므로, 그릴 때마다 버퍼의 색상 정보를 일일히 업데이트합니다. 색상 정보를 바꾸는 데 드는 오버헤드가 어느 정도인지는 아직 모릅니다.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;1번은 그닥 고려할만한 가치가 없을 것 같고 2번 혹은 3번을 선택해야 할 것 같은데, 일단 두 가지 경우를 모두 테스트해 보고 최선이 무엇인지 가늠해 본 다음에 결정해야 하겠습니다.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;3. Line type 5&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;LDraw 스펙 문서에 보면 Line type 5가 있습니다. 간단히 설명하면 두 쌍의 벡터가 있는데, 각 벡터를 현재 projection 행렬에 곱합니다. 그럼 그 두개의 벡터가 2차원 평면에 사상되는데, 이 벡터가 서로 평행(identical)이라면 그립니다. 한 마디로 말하면 둥근 오브젝트의 경계선을 그리기 위한 엘리먼트라고 보시면 됩니다.&lt;/p&gt;&lt;p&gt;현재 카메라 시점에 따라 그리는 결과물이 달라지므로 미리 계산할 수는 없고 매번 렌더링할 때마다 검사하여 그리는 엘리먼트이기 때문에 아직 렌더러를 프로파일링해보지 않아 모르긴 몰라도 아마 부하가 상당할 겁니다.&lt;/p&gt;&lt;/p&gt;그러므로 이걸 최적화하는 것도 과제 중 하나인데, vertex shader를 이용할 생각입니다. 몇십 줄의 ARB program으로도 구현이 가능할 것 같네요.&lt;/p&gt;&lt;p&gt;단, 요즘 대부분 그래픽 카드가 프로그래머블 파이프라인이라 걱정할 필요는 없지만 리눅스 환경에서 몇몇 오픈 소스 3D 드라이버는 아직 쉐이더 구현이 미비하거나 아예 없으므로 CPU fallback을 마련해야 할 것 같습니다.&lt;/o&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;레이트레이서(POV-Ray) 연동&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;POV-Ray 3.6까지는 정상 작동하나 POV-Ray 3.7 베타로 테스트해보니 안 되더군요. 그래서 문제를 분석하다가 POV-Ray 소스 코드를 뒤져보니 stdout으로 출력물을 보내는 기능 전체가 주석 처리되어 있었습니다. 지금 구현은 POV-Ray에서 targa 포맷의 이미지를 stdout으로 스트리밍하고 Klotz에서 이걸 받아서 실시간으로 보여주게 되어 있는데 POV-Ray 3.7이 베타라서 이 기능을 임시로 빼 놓은 모양이더군요. 하긴 3.7에 SMP 기능이 들어가서 렌더링 결과가 깍두기로 나오더니 제대로 되려나 싶었습니다.&lt;/p&gt;&lt;p&gt;이걸 발견한 게 작년의 일이니 지금은 고쳐졌을지 모르겠군요. 시간 되면 한번 알아봐야겠습니다.&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;UI 및 편집기 개선사항&lt;/h2&gt;&lt;br /&gt;&lt;h3&gt;1. de-KDE-ify&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Klotz에서 KDE 라이브러리를 쓴 건 다음 이유에서였습니다.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;XMLGUI&lt;/li&gt;&lt;li&gt;설정 파일 통합&lt;/li&gt;&lt;li&gt;KDE와의 UX integration&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;리눅스의 KDE 환경에서 KDE와의 통합은 적절한 선택이었다고 생각하지만 다른 플랫폼(Mac OS X, 윈도)으로 가면 상황이 달라집니다. 고작 캐드 프로그램 하나 쓰겠답시고 그 크고 무거운 KDE 환경 전체를 끌고 와야 할 필요는 없지요.&lt;/p&gt;&lt;p&gt;그래서 윈도와 맥의 경우 (그리고 리눅스에서도 선택적으로) KDE 라이브러리를 제외하는 작업이 필요합니다. KDE에 의존적인 코드가 많지 않아서 그나마 다행이지만 더 급한 문제가 많으니 지금 당장은 어렵고 장기적으로 해야 하겠습니다.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;2. Cut, Copy and Paste&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;이거 안되면 캐드 맞나요? (…)&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;3. 설정 GUI&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;기본 설정은 너무 빈약하고, 레이트레이서 설정은 아예 없습니다. 이런 단순한 UI 작업은 단지 노가다일 뿐이라 선뜻 손이 가지 않네요.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;4. Data model 개선&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;가끔씩 편집하다 보면 DataModel이 깨지더군요. Model은 솔직히 귀찮아서 야매로 구현했는데 (Qt의 MVC 구현이 너무 복잡해서…) 조금 시간이 걸릴 것 같긴 하지만 시간 들여서 조금씩 고쳐 나가야 하겠습니다.&lt;/p&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_wsaBTslQUzs/TITYk7hyMMI/AAAAAAAAAD8/abn_ViuQV5s/s1600/scr1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_wsaBTslQUzs/TITYk7hyMMI/AAAAAAAAAD8/abn_ViuQV5s/s320/scr1.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;인증샷. 앞서 말한 BFC 버그 때문에 쉐이딩이 엉망이군요.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/931078517202405968-4840805611316897625?l=ko.blog.influx.kr' alt='' /&gt;&lt;/div&gt;</description><link>http://ko.blog.influx.kr/2010/09/klotz.html</link><author>noreply@blogger.com (segfault)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_wsaBTslQUzs/TITLcb69rbI/AAAAAAAAAD0/cIT_j0_zvV4/s72-c/vbo.png' height='72' width='72'/><thr:total>0</thr:total></item></channel></rss>
