이 블로그는 방문자 통계를 위해 티스토리 기본 기능과 Woopra를 사용합니다. 원하지 않으신다면 사용하시는 웹 브라우저에 내장된 DNT 헤더를 켜고, JavaScript를 끄셔도 무방합니다.
이 블로그 방문자의 약 60%는 네이버 검색을 사용하십니다. 을 이용하시면 더 유용한 정보를 쉽게 얻게 되실 수도 있습니다. [mediatoday]
« 2018/02 »
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28      
블로그 이미지
제가 주제인 블로그... 그냥 주제 없는 블로그입니다. 전공 분야나 예전 관심 분야 등등에 관한 글이 우선입니다만, 두어 문단을 넘길 만한 글이라면 대강 정리해 기록합니다. 학부생입니다. 트위터에서 볼 수 있습니다. http://aurynj.net/ 어­리


 
 

장고는 반쪽 MVC이다

Sablog Models/인터넷·웹 | 2013.12.01 02:13 | Posted by 어­리

요 며칠 간 프로젝트를 진행하면서 장고(Django)를 쓰기 시작했다. 사실 장고 자체를 접해 본 건 다소 오래 된 일이었던 것으로 기억한다. 아마 서버를 겸해 공부한답시고 설치한 리눅스가 너무 어려워서 날리면서 한동안은 리눅스와 거리를 두고 살았고, 파이썬도 잊었을 것이다. 그 이후 호스팅 업체를 알아보면서 윈도우 기반으로 PHP+MySQL이 돌아가는 APM을 쓴 적도 있다. 지금은? 만약 지금의 내가 과거의 내게 충고할 수 있다면, PHP를 가능한 대안으로 생각할 여지를 만들도록 놔 두지 않을 것이다. 요즘은 멀티부팅을 밥 먹듯 한다.

장고는 MVC 웹 프레임워크이다. 구체적으로는 DBMS에 대한 파이썬 클래스 ORM 래핑이 M이고, HTTP Request-Response를 수행하는 파이썬 객체 래핑이 V이며, URL 패턴에 따라 V를 적용(dispatch)하는 파이썬 코드와 그 밖의 데코레이터 기능이 C에 들어간다. 잘 보면 C의 의미가 MVC에 비해 다소 다른데, 이 떄문에 장고는 MVC의 변종인 MTV라고도 한다. 그러나 장고처럼 백엔드가 M과 V를 구성하기 위한 유틸리티가 되는 상황에서, 다 만들어진 M과 V에 대해 C가 또 한 번의 추상화를 하는 건 MVC 모두의 코드를 늘리는 퍽 불필요한 짓이다. 이런 건 대강 MVC가 맞다고 해도 되지 않나 싶다.

아무튼 올해에야 장고를 잡은 건 PHP를 버린 이후로 내가 서버 프로그래밍을 게을리 했다는 걸 방증하긴 하는데... 그 외에도 내가 장고를 잡기 꺼리고 레일즈나 스프링 MVC를 건드린 이유가 몇 가지 있었을 것이다. 파이썬 패키지 관리가 단연 첫째 문제였는데 (그래서 이번 서버는 우분투로 하고 pip만을 썼다) 다른 프레임워크에 비하면 파이썬은 몇백 배 나은 배포사용(deployment) 환경을 가지고 있었다... 그 다음 문제가 뭐였는지 기억도 잘 안 나고, 그냥 장고를 쓰기로 했다. 적절한 결정 덕분에 지금까지 별 난관 없이 장고로 그럭저럭 서버 하나를 완성해 나가고 있다. 유일한 문제라면 장고가 슬슬 마음에 안 든다는 것이다.

장고의 문제는 MVC 모두에 존재한다. 우선 M에서 가장 큰 문제는 장고 ORM이 정책적으로 DBMS에 구현된 기능에 대한 래퍼라는 것이다. 아무리 Enum의 구현이 RDBMS마다 다르기로서니 장고 규모의 프레임워크에서 models.EnumField가 없는 게 말이 되는가. 무조건 serial로 PK를 만드는 문제는 또 어떤가! 이런 정책은 ORM과 RM 모두에 해악이다. 한편 V에서 가장 큰 문제를 꼽자면, M과는 역설적으로 장고의 규모가 너무 크다는 점일 것이다. 나는 왜 @csrf_exempt가 데코레이터(decorator)여야 하는지 이해할 수 없다. 분명 장고의 액션 기반 HTTP 뷰 모델은 성공한 모델이고, 여타의 웹 프레임워크와 함께 놓고 보아도 꽤 성숙한 축에 속한다. 그러나 장고의 V를 이용해 좀 더 복잡한 일을 하려고 하면 반드시 문제가 생긴다. 거기 당신, 장고를 이용해 코드 중복 없이 XML과 JSON으로 된 API를 모두 제공하는 서버를 객체지향적으로 짤 수 있는가?

그러나 이와는 비교도 안 되는 문제가 바로 C의 문제이다. 운을 떼자면, 나는 SOAP/REST 양시론자이다. MVC냐 아니냐의 문제는 차치하더라도 여기서 문제가 발생한다. 장고의 URL dispatcher는 REST에만 대응한다. 그러나 그마저 반쪽 REST이다! 그 이유는 간단한데, URL만 신경쓸 뿐 Method에 따른 dispatching이 전혀 없기 때문이다. URL에 따라서만 뷰가 달라지는 프레임워크는 다시 말해 URL 패턴에 따라 뷰가 달라지는 환경에서만 사용 가능하다. 이 방식은 같은 자원에 대해 메서드와 헤더에 민감하게 반응하기 어렵고 따라서 OPTIONS나 Accept를 놓치기 쉽기 때문에 REST 의미론과 배치되는 경향이 있다. 그렇다고 장고에서 무리해서 메서드를 먼저 걸러내자니 뷰를 자원 객체처럼 만들게 되고 여러 URL 패턴을 하나의 뷰에 대응시키면서 지저분한 코드만 남는다.[각주:1] 아마 이것이 앞으로 장고에서 고쳐지는 일은 없을 것이다. 참고로 URL과 Method dispatching을 모두 해 주는 프레임워크도 있다. 스프링 MVC가 대표적이다.

그래서 어떻게 할 거냐고? 물론 다음에 웹 서버를 만든다고 해도 여전히 파이썬을 쓸 생각이다. 하지만 새로운 프레임워크를 배울 시간이 모자라지만 않다면 다른 라이브러리로 건너갈 생각이 강하다. Flask같은 마이크로프레임워크는 영 취향이 아니고, 데이터 모델로는 SQLAlchemy를, 웹 서비스 모델에는 Werkzeug를 고려할 것이다. 아무래도 장고의 한계를 느꼈기 때문이다. 물론 장고는 좋은 웹 프레임워크이다. 그러나 연장 탓을 많이 하는 프로그래머에게는 남이 만든 프레임워크가 맞지 않는지도 모르겠다.


추가(2013-12-1 16:07). Class-based view를 어떻게 생각하느냐는 의견을 전달받았다. 일단 "괜찮지 않나, 잘 모르겠다" 정도로 대답했는데, 아무래도 장고를 더 써 봐야 알 것 같다. 클래스 기반 뷰 자체는 REST와 잘 상통하는 면이 있지만, 사실 이상적인 웹 서비스에서 REST의 자원 객체는 데이터베이스 ORM 객체와 크게 다를 게 없기 때문에 개념적으로 생각하자면 같은 모델을 두 번 정의하는 게 된다. 굳이 중복을 없애자면 데이터 모델을 만들고 DB나 HTTP API를 위한 데코레이터를 작성하는 방식이 나은데 이건 장고 프레임워크의 장점을 완전히 깎아 먹자는 말이므로 패스. 그리고 사족이지만 WebDAV같은 프로토콜은 django.views.generic.View를 평범하게 써서는 구현할 수 없다...

  1. 구체적인 예를 들자면, 회원 가입에 쓰이는 POST /account와 회원 열람에 쓰이는 GET /account/(id)가 있다고 하자. 이들은 사실상 같은 자원에 대한 다른 접근이기 때문에 URL에 따라 뷰를 바꾸기보다는 메서드로 먼저 걸러내고 (id)를 뜯어내는 쪽이 훨씬 적절하다. GET /account에서 어떤 오류를 돌려줘야 할지를 생각해 보면 명확하다. 그렇다고 r'^account$'와 r'^account/(?P<_id>d+)$'로 나눌 수 있는 뷰를 r'^account(?:/(?P<_id>d+))?$'로 하나의 뷰에 넣어야 하는가. [본문으로]

어떤 웹 브라우저를 써야 합니까

Sablog Models/sabless | 2013.10.11 18:37 | Posted by 어­리

어떤 웹 브라우저를 써야 합니까. 내가 상당히 많이 받았던 질문인데, 이는 다름아니라 내가 2007년 무렵부터 Internet Explorer 6를 쓰지 말아야 한다고 내 주변 사람들에게 몸소 광고를 하고 다녔기 때문이다. IE6를 쓰지 말라는 건 하나의 주장에 불과하기 때문에 나는 이 주장에 대한 쉽고 합리적인 근거를 찾느라 꽤나 애를 먹었다. 게다가 이미 내 이야기를 듣는 사람들은 IE6를 사용하고 있었고 그걸 인터넷이라는 이름으로 알고 있었다. 내 호소에 면면이 공감하더라도 그 사람들은 그 대체재에 대한 견해를 그 호소의 당사자인 나로부터 구할 수밖에 없었다. 나는 그것을 감수했다. 물론 항상 일이 잘 진행된 것은 아니다.

이 문제는 시간이 흐르고 흘러 IE의 다음 버전이 끊임없이 나오고 Windows 7이 나온 지금까지도 이어져 오고 있다. 왜 관공서에서는 아직도 Windows XP와 Internet Explorer 6를 쓰고 있는가? 이는 사실 "알 수 없는 출처의 애플리케이션 설치를 허용하세요"만큼이나 심각한 컴퓨터 보안 불감증의 문제이기도 하지만, 문제는 무조건 익숙한 것이 우선일 때 작업 능률이 증가한다는 것이다. 하나의 전산망이 똑똑한 시스템과 약간 멍청한 시스템에 동시에 대응하기를 바라는 것은, 학교에서 바른 생활 사나이이고 수능 모의고사도 만점을 받는 학생이 밖에 나가면 날라리인 생활을 수십 년 지속하기를 바라는 것만큼이나 미친 짓이다. 결론적으로, 행정 전산망과 웹서비스는 IE6 기반이다. 이는 근 몇 년 동안 이 작업 환경에 완벽히 적응한 새로운 공무원들을 양산하는 데 일조하기까지 한다.

사실 어떤 분야의 소프트웨어는 하드웨어의 발전으로부터 상당히 동떨어져 있는 것 같지만, 그런 분야는 없다. 이는 업무용 소프트웨어와 정부, 사내 프레임워크에도 마찬가지라서, 하드웨어가 그렇듯 소프트웨어도 몇 년 지나고 나면 몇 푼의 유지보수만으로는 버틸 수가 없게 된다. 갈아엎지 않으면 완전히 구시대의 유물이 되는 것이다. 윈도우 폰이, 대표적으로 2010년도의 옴니아 2가 결국 어떻게 되었는가를 생각해 보면 편하다. 하지만 쓰던 소프트웨어를 쓰고 또 쓰고 다시 쓰려는 관성은 유독 한국인에게 강한 건지, 그냥 소프트웨어 엔지니어의 입김이 약한 건지 알 수 없는 노릇이다. (행정망과 달리 기업의 사내망은 IE6 기준으로 남은 곳이 없다고 보면 된다. 행정직의 입김이 세다고 결론내려지는 순간이다.) 사실 지금 웹 브라우저가 뭔지를 모르는 사람은 거의 없다. 모바일에서 IE6를 계속 쓰는 사람은 옴니아 2를 쓰는 사람만큼이나 많다(적다). 그럼에도 불구하고 데스크탑과 모바일에서 같은 웹브라우저를 쓰는 장점을 못 누리는 사람은, 옛날에 웹 브라우저가 뭔지 모르던 사람만큼이나 많다. 물론 이는 프로모션의 문제이고.

아무튼 옛날의 나는 그렇게 좋은 방식으로 웹브라우저 전도를 한 것은 아니었다. 나는 웹 브라우저라는 생소한 지식을 가진 사람 앞에 파이어폭스와 크롬이라는 두 가지 선택지를 내밀었다. 언젠가부터는 크롬을 추천했지만, 파이어폭스를 추천한 적도 있다. 다른 웹 브라우저의 장점은 무엇인가? 빠르고, 바이러스가 끼어들 여지가 적으며, 동기화와 온갖 확장 기능이 있다! 심지어 윈도우와 독립적으로 자동 업데이트가 되기 때문에 이 모든 장점이 끊임없이 발전한다! 이런 접근은 소프트웨어 엔지니어에게는 좋은 홍보 방법이지만, 일반인 앞에서는 윈도우가 나쁘고 리눅스가 좋다는 소리만큼 한숨 나오는 접근에 불과하다. 중요한 것은 일단 빠르다는 것이고, 인터넷 익스플로러에서 쓰던 북마크를 그대로 가져 올 수 있다는 것이다. 그리고 웬만한 시스템은 점점 많아지는 바이러스에 허덕이고 있기 때문에, 웹브라우저를 바꾸는 건 일단 시스템을 대대적으로 청소한 이후에 행하도록 한다. (포맷을 하면 더 좋다. 어차피 IE6를 오래 쓰면 포맷은 종종 하게 되어 있다.) 그래야만 보안성이 좋은 웹브라우저라는 게 무슨 뜻인지 체득할 수 있기 때문이다.[각주:1]

물론 한때 IE6는 범세계적 실용 표준(de facto standard)이었다. MS의 다소 무책임한 마케팅으로 월드 와이드 웹이라는 공공재는 IE6에 잠식되었고, 이는 구글 크롬에 의해 반복되는 역사가 되고 있다. 오늘날 IE6 호환성 이야기는 어느 정도 IE6의 문제로 결론지어진 것 같지만, 우리 삶에서 이 문제는 아직 끝나지 않았다. 최종 사용자 입장에서는 문제가 굉장히 복잡해진 것이다. 다소 웃기는 것이 있다면, 실용 표준이 법적 표준(de jure standard)을 앞서는 현상은 일반인에게서 더 자주 발견되더라는 것이다. 내가 만난 누군가는 다른 웹 브라우저를 쥐어 줬을 때 누가 뭐래도 IE6를 기술 표준으로 판단하고 있었다. 문제 많은 액티브엑스도, 사람 정신을 혼미하게 하는 툴바도 필수 기능이 된다. 잘 보이던 웹 사이트가 크롬에서 깨진다고? 크롬의 잘못이지! [각주:2] 이런 사람들은 환경적인 영향으로 강제로 웹브라우저를 바꾸게 하지 않으면 답이 없더라. 그런데 우리나라 대표 포털 웹사이트인 네이버가 당시 새 웹표준에 굉장히 게으르게 대응했기 때문에 IE6를 쓰는 사람들만 굉장히 편했다. 게다가 우리 나라에서는 실용 표준뿐만 아니라 법적 표준도 IE6이다! 소프트웨어 엔지니어들이 IE6 얘기만 나오면 거품을 무는 건 이 때문이다... 혹시 모르지, 어느 날 네이버에서 지금 운용하는 웹 페이지들을 모두 IE8 이하에서 깨지게 하면 어르신들이 불만을 가져 지금이라도 법적 표준이 바뀔지도. 물론 현실성이 없는 얘기라는 건 안다. 아니 그보다도 왜 이 장유유서의 나라는 어르신이 불편해야 비로소 바뀌는 거야...

  1. 이런 접근에 관해서 참고가 될 만한 글을 붙인다. 강성훈, <a class="tx-link" target="_blank" href="http://j.mearie.org/post/27713540987/what-is-wrong-with-sebulsik-evangelism">두벌식과 세벌식</a>. [본문으로]
  2. 두벌식을 쓰다 세벌식으로 간신히 건너온 사람이 뜬금없이 "예전에는 웃길 때 '으앜ㅋㅋㅋ'이라고 썼는데 세벌식은 이게 안 되니까 짜증난다"라고 한다고 해 보자. 도깨비불 현상을 싫어하는 세벌식 유저는 얼마나 황당한가... [본문으로]

'Sablog Models > sabless' 카테고리의 다른 글

어떤 웹 브라우저를 써야 합니까  (0) 2013.10.11

페도라 19로 늦은 업그레이드

Sablog Models/시스템 | 2013.10.11 17:39 | Posted by 어­리

뭐 특별한 내용은 아니고 업그레이드 후기 및 잡설. rpmfusion 관련 문제도 꼬이고, 리눅스에서 개발할 일도 없어서 꽤나 오래 페도라 업그레이드를 안 하고 지냈다. 거의 반 년. 그러다 이제 슬슬 방학도 끝났고, 새 학기 접어들어 뭔가 시작하려다 보니 네이티브를 안 쓸 수가 없었다...-_-; 물론 새 학기라서 뭔가 삽을 뜰 수 있으리라고 생각한 건 오산이었다.

무튼 우선 rpmfusion 문제를 해결했다. 그냥 --nogpgcheck을 돌렸는데, 지난번에 fedup으로 업그레이드하면서 남은 rpmnew 파일이 gpgcheck=1로 되어 있어서 yum이 이러지도 저러지도 못하는 상황이었다. /etc/yum.repos.d에서 rpmnew 파일을 직접 수정해야 했다.(지금 fedup은 gpgcheck=0을 유지하고 업그레이드해 주기 때문에 이런 문제는 없다.) 그리고 나서야 마음놓고 모든 패키지를 fedora 18 최신으로 올렸다. gnome-shell이 버전 3.6.3으로, ibus이 1.5.3으로 올라갔다. 당장 나를 매우 골치아프고 불편하게 만들던 이 둘의 속도와 잔버그(예: 레이아웃 전환 중 포커스 잃음)들은 많이 개선되었다. 물론 속도는 여전히 불편하더라. 그래도 fedup 돌리기 귀찮아서 몇 주를 더 썼다.

그리고 지난 7월에 나온 fedora 19. 어제 드디어 fedup --network 19 --disablerepo fedora-yumex를 돌렸다... timlau/yumex가 fedup을 지원하지 않는 건가? 어찌됐든 1Mbps도 안 나오는 속도만 아니었다면 스트레스는 좀 덜 받았을 것 같다. 오늘 새벽이 다 되어서야 리부트를 시키고 자고 일어나니 업그레이드가 끝나 있었다. 예의 gnome-shell은 3.8.4, ibus는 1.5.4인데, 모든 버그와 레이턴시가 날아갔다. 로그인 속도도 빨라지고 심비어 gnome-tweak-tool에서 Alt_R을 붙여도 마냥 빠르다! 그 밖에도 업그레이드 릴리즈 이후 석 달 지난 배포판답게 몇 가지 바뀐 패키지가 있다. 당장 찾은 건 glew 1.9.0인데 많은 걸 비교분석할 처지는 아니고... git이 아직 1.8.3인 이유는 잘 모르겠지만 조만간 업데이트되겠지. 네트워크 제어판이 핫스팟 키프레이즈를 처리 못 하고 다운되던 버그도 수정.

결론부터 말하자면 페도라는 여전히 좋은 배포판이고, 더욱 좋아지고 있다. 나는 아무래도 요즘 페도라의 업데이트 정책이 잘 유지될 수 있을까 걱정하던 중이었다. 내가 우분투를 쓰지 않는 이유는 우분투의 기원인 데비안 때문인지, LTS와 LTS가 아닌 판의 안정성 및 업데이트 차이가 너무 심한 것이 이유이다. 물론 오픈소스 소프트웨어들이 점점 신나게 발전하고 있고 여기에 안정적으로 대응하기 어렵다는 사실은 부정할 수 없다. 그 때문인지 페도라 17, 18에서는 배포판 업그레이드와 빠른 업데이트의 중간적 대응이 흔들리는 느낌이었다. 하지만 일단 올라와서 보니 그건 내가 페도라 18을 오래 쓰느라 가진 기우였던 것 같다. 페도라 19를 초기부터 쓰고 지금은 20 알파를 쓰는 지인에 의하면 지금 페도라는 충분히 안정되어 있다고.

자바(Java)로 객체지향을 처음 접하는 사람이 꽤 흔하다. 이들이 가장 많이 하는 실수이면서 잘 풀리지 않는 의문 중 하나가 stackoverflow에 차고 넘치는 'abstract static'이다. abstract static으로 규격을 만들어 놓으면 인스턴스화할 필요는 없으면서 상속될 수는 있으므로, 프로그램 내에서 문맥의 영향을 받지 않는 메서드를 모아 쓰기 좋은 구조일 것이다. 이런 패턴은 상당히 많은 프로그램에 적용될 수 있을 것이다. 그런데 왜 그런 좋은 게 금지되는 걸까?

답은 먼 곳에 있지 않다. 자바는 객체지향 언어라는 것. 그리고 그 중에서도 클래스-인스턴스 관계를 매우 엄밀하게 따지는 언어라는 것이다. 일견 자바의 static은 C++의 static과도, Python의 classmethod와도 닮아 보이지만, Python을 배운 사람이라면 staticmethod와 classmethod가 어떻게 다른지 찾을 수 있을 것이다. 자바에서도 클래스 그 자체가 객체로 취급되기는 한다. 하지만 그것이 우리가 알고 있는 역할을 하는 객체, 즉 인스턴스처럼 능동적으로 움직이는 것은 아니다. 그렇기 때문에 static 메서드가 상속되어서는 안 되는 것이다. 비록 하위 클래스가 상위 클래스의 static 내부 메서드의 이름을 가져올 수는 있지만, 이것은 오버라이딩이 아니다. 클래스는 인스턴스를 위한 생성구조(construct)일 뿐이며, 클래스가 인스턴스의 다형성을 지원한다. 이것이 전통적인 클래스-인스턴스 관계이다.

다 끝난 얘기지만, 좀 더 자바 자체를 두고 살펴 보자. 자바에는 자기 반영성(reflective)의 기반이 되는 java.reflect 패키지가 있다. 이 패키지에는 클래스를 나타내는 java.lang.Class에서 꺼내지는 java.reflect.Field와 java.reflect.method가 있고, 이들은 임의의 인스턴스 객체를 가리키는 java.lang.Object를 자동으로 상속하는 클래스이지만, java.lang.Class는 임의의 클래스를 그 이름만으로 받지 않는다. 이를테면 이런 식이다.

private Class<? extends EntryActivity> mHostActivityClass = MainActivity.class;

여기서 MainActivity는 EntryActivity를 상속한다고 가정하자. 우리는 Class<? extends EntryActivity> 타입의 값이 MainActivity라는 이름이 아니라 MainActivity.class로 표현된다는 것에 주목할 필요가 있다. Class 타입은 그 값의 클래스적 특징만을 보여주며, 클래스가 할 수 있는 일들만을 제공한다. 따라서 mHostedActivityClass는 우리가 알고 있는 객체로서의 클래스와는 본질적으로 다르다. mHostedActivityClass는 오직 EntryActivity 클래스를 상속한 어떤 클래스 자체이며, 인스턴스를 만들고, 하위 클래스를 생성구조로 하는 인스턴스의 필드를 읽고 쓰거나 메서드를 발현시키는(invoke) 기능만을 갖고 있다. 이는 만약 EntryActivity에 어떤 static 메서드가 있더라도 마찬가지인데, java.reflect.Method에는 invoke(obj, ... args) 메서드가 있어서 static 메서드의 경우 인스턴스를 나타내는 첫 인자를 무시하는 방식이 된다. 이는 JVM 위에 올려졌을 때 완전히 동등한 바이트코드로 해석된다.

중요한 것은 Class<? extends EntryActivity> 타입의 값이 MainActivity가 아니라 MainActivity.class라는 점에서, mHostActivityClass 자체에서 어떤 static 클래스를 꺼내서 쓸 방법이 없다는 것이다. EntryActivity.DoStaticWork();이지, EntryActivity.class.DoStaticWork();가 아니지 않은가? 이는 mHostActivityClass가 하나의 오브젝트같지만, 실제로는 JVM이 객체지향을 구현하는 고전적인 방식에 완전히 의존적인 일종의 데이터 구조일 뿐이며, 이는 메서드를 가져다 쓸 때에도 같은 방식을 취해야 한다는 것을 의미한다. 따라서 static 메서드는 오버라이드될 수 없다. 오버라이드는 인스턴스가 자기 자신을 알고 있을 때 다형성을 만드는 방법이기 때문이다. MainActivity.class가 자기 자신을 알 리가 없지 않은가! 그렇다고 Class<? extends EntryActivity>같은 무언가가 MainActivity의 이름을 받게 언어를 구현하는 것이 가능할까? 적어도 자바에서는 그럴 리가 없다.

내가 말하고자 하는 것은 자바의 방식이 전적으로 맞다는 것이 아니다. 세상은 디자인 패턴으로 이루어져 있지 않다. 실제로 일을 나눌 때에는 static과 non-static을 구분할 때 자바의 고전적인 객체지향 논리를 적용해야 한다는 것이 정말로 방해가 된다. 그나마 그것을 헤쳐나가는 방식이 수많은 패턴으로 굳혀져 왔고, 자바는 지금 패턴을 바탕으로 업계에서 가장 선호되는 언어 중 하나의 입지를 놓치지 않고 있다. 그러나 패턴을 어떻게 썼느냐로 코드를 판단할 수 있는 만큼, 그 패턴들이 실제로 적용된 곳은 이래도 그만 저래도 그만인 억지가 있는 것도 사실이다. 중요한 것은 컴퓨터에게 일을 어떻게 시키느냐와, 사람이 그것을 언제 어떻게 다루느냐에 있다.

하지만 abstract와 static은 모두 객체지향의 키워드인 만큼 abstract static은 금지되는 것이 맞다. 자기 반영성이 훨씬 유연하고 견고한 언어인 C#에서도, 자바와 같은 원리가 작용해서 abstract static은 불가능한 것이 된다. 또한 자바에서 invoke()를 남용하면 큰 코 다칠 수 있다는 사실도 유념해야 할 것이다. 사실 이 문제는 객체지향에서만 발생한 것은 아니지만, 잘 보면 C에서는 대충 void *로 다루고 예의 상 규약을 지켜 줬기 때문에 어떻게든 설계가 가능했다. 어딜 가나 type system이 문제가 되는 것이다. 만약 함수형 언어가 지금처럼 상승세를 타면서, F#이나 하스켈(Haskell)같은 언어가 미래에 지금의 naive한 함수론을 버린다면, 언젠가는 프로그래밍 언어에서 고계(high-order) 함수와 고계 컴파일러 매크로를 포함하는 견고한 현대적 타입 시스템의 입지를 구축할 수 있지 않을까. 물론 먼 미래의 이야기다. abstract static으로 일을 나누는 것도 그만큼이나 먼 꿈이다.

안드로이드 SDK에서 l10n은 strings.xml을 이용해 이루어진다. 해외 법인이 따로 있는 회사에서 앱을 개발하는 경우에는 아예 소스 코드를 넘겨서 별도의 앱을 만들어 버리는 경우가 많지만, 규모가 그렇게 크지도 않고 문자열 외의 대대적인 로컬화가 필요가 없다면 res/values locale postfix modifier 사용은 필수적이다. 이럴 경우 트리 메인테이너에게는 언어별 strings.xml을 관리해야 한다는 부담이 작용한다. 개발이 항상 선형으로 이루어질 수는 없기에, 로컬화 담당자가 strings.xml을 들고 왔을 때 무엇이 누락되었을지 찾기 어렵기 때문이다. 주로 이런 문제는 담당 로케일의 QA가 운좋게 찾아내는 게 고작이다.

오픈소스의 경우 얼마나 많은 타깃 언어가 만들어질 것인지 아무도 모르기 때문에 분위기가 다소 자유롭지만, 강제적인 분위기일 뿐 문제는 더욱 귀찮다. 이를테면, 주 언어에서 제거된 항목은 다른 모든 언어에서도 제거되어야 한다. 주 언어에서 새로 만들어진 항목은 다른 언어에도 반영되어야 한다. K-9 Mail의 경우 전자는 perl을 사용해 메인테이너가 관리하고 그 다음에는 번역자가 협조하도록 되어 있으나, 후자에 대해서는 사실상 대책이 없다. 새로 항목을 만들면서 다른 언어마다 주석을 만들어 주는 것도 한계가 있는 것이다.

아래는 K-9 Mail에서 항목을 모두 삭제할 때 사용하는 스크립트이다.

find . -name strings.xml | xargs perl -pi -e's!^\s+<string name="string_name_here".+?</string>\s*\z!!'

다음은 내가 쓰는 스크립트이다. 언어마다 어떤 항목이 들어있는지 수동으로 점검하는 방법이다.

ls res | grep values | while read -d $'\n' fn; do echo $fn; echo `cat res/$fn/strings.xml | grep string_name_here`; done

그러나 이것도 결국 문제가 한두 개일 때 얘기이고, 결국 주 언어와 대상 언어 간에 직접 비교를 할 수밖에 없다. 그래서 찾아낸 방법이다.

  1. 자바스크립트 콘솔을 지원하는 웹브라우저로 두 XML 파일을 연다. 나는 크롬을 사용한다.
  2. var s = document.getElementsByTagName('string')
  3. var a = []; for (var i = 0; i < s.length; i++) a[i] = s[i].getAttribute('name');
  4. a = a.sort().join('\n');
  5. 결과를 텍스트 파일로 저장해서 diff를 돌린다.

결국 name의 정렬된 목록을 뽑아 내서 비교하는 것이다. diff만 가능하다면 자바스크립트만으로 해결하는 방법이 있을 텐데, 없어서 약간 번거로울 수 있다. node를 사용하면 완전 자동화가 될지도 모르겠다.

1. 고장의 기미가 보이면 그 하드웨어를 고치려 들지 말고, 무슨 수를 써서라도 사용 시간을 줄이고 중요한 것부터 백업부터 해야 한다. 노력해서 하드 디스크를 고쳐서 원만하게 쓴다고 돈이 아껴지는 게 아니다. 어떤 자동화된 도구를 쓰든 자칫하면 엄청난 시간 낭비로 이어져 전혀 절약이 되지 않을 뿐더러, 대개의 경우 더 심각한 고장 상황을 초래하고 더 많은 데이터를 잃게 되는 원인이 된다. 논리적 오류가 물리적 오류로 발전하고, 물리적 오류가 번져 나가고... 이런 상황은 막아야 하니까 말이다. RAM이든 HDD이든 저장소는 예나 지금이나 원시적인 방식으로 돌아가는 것을 알 수 있다. 데이터 밀도가 높아지면 결함에 대한 내성은 낮아지기 마련이고, 안정적인 데이터 관리에는 돈이 필수적이다. 슬픈 사실이다.


2. USB 외장 하드인 경우, 디스크의 케이스나 시스템의 USB 포트에 결속 불량이 없는지 점검한다. 모든 포트가 정상이면 좋겠지만, 어떤 포트는 소켓이 덜렁거려서 전원 공급이 불안정하고, 어떤 포트는 전원 공급은 잘 되는 주제에 데이터 통신만 먹통일 수 있다. 평소에 습관적으로 많은 기기를 고장내는 경우 (내가 그렇다) 이런 부분도 허술해져 있어서 2차적인 문제를 불러오는 경우도 한몫하기도 한다. 이 점검은 하드 디스크 수리와는 별개이지만, 평소에 점검하지 못했다면 점검해 보자. 한편 결속 불량과는 별개로, 흔들림이 없고 시원한 곳에서 작업하는 것이 필수적이다.


3. 드라이브 속성에 들어가 인덱싱을 끈다. 디스크가 현상 유지를 하는 데 상당한 도움이 된다. 리눅스에서 ntfs-3g를 쓰면 윈도우에서 꺼지지 않는 desktop.ini나 thumbs.db 생성은 막을 수 있지만, 윈도우에서 작업하는 것에 비해 하드 디스크에 전반적인 무리를 주면서 오류 정정 성능은 떨어지는 것으로 보인다. 그러므로 이 방법은 하드 디스크에 단 하나의 파일도 쓸 수 없고, 임의로 몇 개의 파일을 잃어버려도 어쩔 수 없는 때에만 적용한다. 예를 들면, MFT 근방에 에러가 크게 났고, 중요한 파일은 윈도우에서 미리 백업한 경우.


4. 디스크가 내는 소리에 귀를 기울인다. 디스크는 버저 소리도 내지만, 헤드를 움직이는 스테핑 모터가 작동하는 소리, 암이 흔들리는 소리, 디스크를 돌리는 스핀들 모터가 작동하는 소리도 중요한 정보이다. 어떤 물리적인 불량 섹터는 헤드가 지나가는 순간 헤드와 칩 전체에 쇼크를 주게 되는데, 이는 예기치 않은 통신 두절 혹은 그 이상, 이를테면 물리적 불량의 확산을 초래할 수 있다. 이 때 헤드는 디스크 위에 떨어지면서 칠판을 긁는 듯한 마찰음을 낸다. 이 마찰음은 파일을 정상적으로 읽고 있는 동안의 소리와 구분해 내기 약간 까다롭다. 문제를 일으키는 파일은 꼭 파악해서 다른 파일을 모두 백업한 이후 시간이 남아 돌지 않는 이상 다시 읽게 시키지 않는다.


5. 물리적 불량의 쇼크로 통제 불능이 된 경우, 디스크가 계속 돌아가면서 이 마찰음이 지속되고 심지어 타는 냄새까지 나기도 한다. 이 때는 디스크와 헤드가 못 쓸 만큼 정도로 상하지 않도록 당장 전원을 끊어야 한다. 백업 중에 이런 현상을 수십 번 겪게 될 것이고, 헤드는 충격으로 수명을 다하게 된다. 읽기 중에 전원을 끊는 것도 헤드와 디스크에 충격을 주고 물리적 불량을 초래하지만, 그래도 전원을 끊는 게 낫다. 이후에 전원을 공급했을 때 디스크가 회전하지 않는다면 헤드가 그 자리에 주저앉은 것인데, 살아나기는 글러먹었다고 보면 되겠다. 그래도 하드 디스크가 무덤에서 뛰쳐나오는 날이 가끔 있는데 당연히 기회이고 노하우가 필요하다. 예를 들자면, 스핀들을 신경쓰며 관성을 이용해 수평 방향으로 약한 충격을 주면 암이 빠져나오면서 디스크가 돌기 시작할 것이다. 모터를 정렬하든 헤드를 살리든 하드를 뜯는 것은 금기이다.


6. 같은 하드 디스크를 쓰더라도 외장 하드는 내장 하드보다 수명이 짧을 수밖에 없다. 외장 하드의 경우 디스크에 결함을 초래할 가능성들이 훨씬 많은 곳에 산재해 있다. 만약 서버에 2차 저장소를 추가한다면 내장 하드로 해야 한다. 맥 미니라거나 노트북이라거나 하는 이유로 내장 하드 도입이 불가능하다면 전원 위기 대비책을 갖춘 도킹스테이션을 고려하라. 외장 하드 케이스는 서버의 환경에서 하드 디스크에 악영향만 끼치는 존재이다. 하드 디스크가 ntfs로 포맷되어 있고 서버 운영체제가 리눅스인 경우는 거의 최악이다. 외장 하드라고 해도 고장이 나기 시작한 시점에서 백업을 시작할 경우 가능하다면 내장 하드로 바꿔서 작업을 하는 게 좋다. 돈이 있고 물리적 문제가 치명적이지 않다면 우선 이미지를 뜨는 게 최선이다. 물론 카피가 날아갈지도 모르지만.


7. 역시 평소에 백업을 잘 하는 것이 제일 중요하다! 프로그램에 따라 설정 파일이나 아카이브가 중요한 경우가 있는데, 이것들의 위치를 알아 놓는 것이 좋다. 평소에 백업을 한다면, 유저의 홈 폴더 전체를 주기적으로 백업하는 것이 현명하다. 사용자가 중요하게 여길 파일은 AppData나 Documents에 있는 경우가 다반사이므로 웬만하면 자동으로 백업이 되기 때문이다. 역으로 홈 폴더를 정리해서 백업할 만한 깔끔한 상태를 유지하는 것도 중요한 일이 된다. 동기화를 할 수 있는 여건인데 시간이 없다고, 디스크에 무리가 간다고 동기화를 하지 않는 것만큼 미련한 짓은 없다. 돈이 없다고 백업 디스크를 장만하지 않는 것은, 슬프지만 그것 또한 미련한 짓이다.


8. 제품에 결함이 없다면, 어쩔 수 없는 고장은 없다. 평소의 백업만큼 평소의 디스크 건강 관리와 시스템 건강 관리가 중요하다. 디스크 검사와 정리, 조각 모음도 주기적으로 수행한다. 노트북은 모든 면에서 데스크탑보다 하드에 무리를 주는데, 노트북을 켠 채로 움직인다거나, 심한 발열을 방치한다거나, 파일 시스템에 문제를 일으킨다고 알려진 바이러스나 시스템 유틸리티 버그를 회피하기 위해 노력해야 한다. 그리고 청결한 환경. 하드 디스크에 숨구멍이 있는 건 널리 알려진 사실이다. 일반적인 섬유 먼지는 물론, 자동차의 배기 가스, 책 먼지, 탈취제 스프레이의 비말 등 작은 입자 중 하드 디스크에 영향을 끼치지 않는 것은 없다. 담배 연기는 특히 치명적이다. 온도나 자기장에 비해 먼지에 대해서는 하드 디스크를 부족하게 배려하게 되기 쉽다.

LinkManager 클래스에 private LinkManager()와 private static LinkManager sInstance;가 있다고 가정하자.


예를 들면 public static LinkManager getInstance(...) 말미에서,

return sInstance == null ? sInstance = new LinkManager() : sInstance;

이런 코드는 가능하지만,

return sInstance != null ? sInstance : sInstance = new LinkManager();

이건 불가능하다. 이건 C/C++에서 업어온 삼항연산자의 정의 때문이다. C11의 정의는 다음과 같다.

(6.5.14) logical-OR-expression:
    logical-AND-expression
    logical-OR-expression || logical-AND-expression
(6.5.15) conditional-expression:
    logical-OR-expression
    logical-OR-expression ? expression : conditional-expression
(6.5.16) assignment-expression:
    conditional-expression
    unary-expression assignment-operator assignment-expression

Java 7에서도 마찬가지.

(15.24) ConditionalOrExpression:
    ConditionalAndExpression
    ConditionalOrExpression || ConditionalAndExpression
(15.25) ConditionalExpression:
    ConditionalOrExpression
    ConditionalOrExpression ? Expression : ConditionalExpression
(15.26) AssignmentExpression:
    ConditionalExpression
    Assignment
(15.26) Assignment:
    LeftHandSide AssignmentOperator AssignmentExpression

요즘 연산자를 어떻게 정의하는 게 좋은지 고민을 많이 하는데, 이 정의가 수많은 언어에 등장한다는 걸 생각하면 좋은 연산자 설계인지 아닌지 영 판단이 안 선다... 애초에 저런 구조가 하나만 가능하다는 건 컴파일러를 생각해야만 이해가 간다.

비슷하게 이상한 코딩을 다른 경우에도 할 수 있다. 멀쩡하게 절차적으로 짤 수 있는 struct allocator를 GNU C 확장을 이용해 Lisp 구조로 보이게 쓰는 것이다.

#include <stdlib.h>

struct link;
struct link *link_alloc(void *target);
void (*link_release)(struct link *) = free;

struct link {
    void *target;
};

struct link *link_alloc(void *target) {
    return target ? ({
            struct link *new_link = malloc(sizeof(struct link));
            new_link->target = target;
            new_link;
        }) : NULL;
}

아이고.

노트북에 설치를 잘못했다. 근데 시스템 날리기가 너무 귀찮다.


처음에는 anaconda로 해결을 볼까 했는데, anaconda는 루트 디렉터리를 꼭 날려야겠단다.

직접 커널 컴파일하는 건 그것대로 귀찮을 수가 없다.


yum을 쓰는 방법을 고려해 봤다.

http://linux.die.net/man/5/yum.conf


1. $arch 변경

export arch=x86_64 && yum makecache

yum은 uname -m으로 커널에서 값을 받아 arch를 결정한다. 소용이 없다.


2. repo 수정

/etc/yum/repos.d/의 $basearch 수정.

yum은 현재 arch와 비호환되는 repo를 무시한다. i386을 x86_64로 오버라이드하는 옵션이 없으니 소용이 없다.


귀찮았다. yum을 쓸 일도 아니었다.


3. rpm

rpm에는 --ignorearch가 있다. kernel-?.x86_64.rpm을 받아서 rpm -i로 설치했더니 특효. 왜 진작에 이 방법을 안 썼는지...


grub2-mkconfig로 /boot/grub2/grub.cfg를 수정했고, 부팅은 쉬웠다. 문제는 또 yum이었다; yum은 다시 32비트 커널로 부트했을 때에만 돌려야 하는 게 귀찮았다. 응용 프로그램이 32비트 어드레싱을 쓰는 건 변함이 없지만 램이 늘었으니 프로그램 수가 늘고 프로그램이 갖는 여유 램도 약간 늘었다. 그뿐이다.


윈도우 파티션 조각 모음을 오래 안 한 탓에 메인 하드디스크가 돌아가셔서 교체. OS 새로 설치. 쓸모없다.

Fedora 18에 와서 ibus의 고질적인 문제는 크게 고쳐지지 않았다. 한글 합성이 커서를 따라다니는 건 여전하다. 물론 일부 프로그램에 즉시 한글 입력이 안 되던 현상은 개선되었는데, 이는 GNOME 3.6이 integrated internal ibus를 갖고 있고 외부 ibus를 허용하지 않는 방식으로 해결된 것 같다.


문제는 노트북, 흔히 그렇듯 Alt_R과 Ctrl_R을 한/영 키와 한자 키로 쓰고 있었다. 한자 키야 여전히 ibus-hangul 세팅에서 변경 가능하지만 (이건 왜 통합 안 했나 모르겠다) 입력 방식 변경은 gnome 쇼트컷 설정에 통합되었다. 오 마이 갓. Alt_R은 등록 불가능.


Fedora 18 GNOME 3.6에서 입력 방식 변경 키를 단독 특수 키로 바꾸는 방법은 그리 어렵지 않다. Alt_R을 한/영 키로 쓰는 방법을 설명한다.


1. 입력 소스 전환 단축키와 구성 키를 제거한다. 이곳의 구성 키로 지정할 수 있는 Alt_R은 쓸모없다. 한/영 키가 있는 외부 키보드를 쓸 경우 이 과정을 생략한다.



2. 그놈 트윅 툴(gnome-tweak-tool)을 열고 입력 메뉴를 선택, 마지막의 입력 소스 전환 특수 키를 Alt_R로 바꾼다.



3. 실행 창(기본 Alt+F2)에서 r을 실행해 그놈 세션을 재시작한다.


영->한 전환 후 첫 입력이 무조건 영문으로 인식되는 에러가 있긴 하지만 이클립스에서도 쓸 만한 Alt_R이 되었다. 예전에 end 키 누르는 습관이 붙었는데 다시 살려야 하나 싶다.ㅇ>-<

64비트 MinGW/MSYS 팁

Sablog Models/시스템 | 2012.10.28 18:39 | Posted by 어­리

몇 달 묵은 글인데 영원이 묵히는 건 아니다 싶어 마무리함.


윈도우 8도 나왔고

이전부터 MS에서 정책적으로 버리던 Win32 static MSVCRT 네이티브 컴파일러 인기도 떨어지지 싶은데

Cygwin에 비해 단점도 많지만 MinGW는 아직 써야겠다.


하지만 배포판의 현실은


MinGW/MSYS configure 옵션은 *-*-mingw32 계열. 앞은 호스트, 뒤는 타겟.

i686-pc-mingw32

i686-w64-mingw32

x86_64-pc-mingw32

x86_64-w64-mingw32

참고: http://gcc.gnu.org/install/specific.html


배포되는 GCC 바이너리는 32비트 호스트 32비트 타겟이다. 64비트 시스템에서 보람차게 컴파일해 봤자..;

우선 64비트 타겟의 크로스 컴파일러를 만들고 다시 64비트 컴파일러를 만드는 방법이 상책이다.

방법은 검색하면 나옴

근데 GMP MPFR MPC ISL 필요한 건 어쩔 거야..가 아니라 결국 두 번 컴파일 해야 함

참고: http://gcc.gnu.org/install/prerequisites.html


중간에 커널 갈아치울 필요가 없다는 점에서 윈도우 NT커널에 감사해야지