Back to WordPress.com
호스팅 만료 한 달을 앞두고, 다시 wordpress.com 으로 전격 이사를 왔다.
국내의 정치적 핍박을 참지 못하고 사이버 망명길에 오르기엔, 여긴 그저 조회수 낮은 힘없는 일개 프로그래머의 해우소일 뿐이지만, 그래도 최근 MB족들의 하는 꼬라지를 보면 언제 호스팅이 날아가고 데이터가 악의 손아귀에 들어갈 지 모를 터다.
어쨌거나 설치형 워드프레스(이하 WP)에서 호스팅형으로 돌아갈 때 가장 걸리는 것은 바로 첨부 파일이다. 비록 WP가 XML 기반의 Export/Import 를 지원하긴 하지만 첨부 파일까지 자동으로 옮겨주지는 않기 때문이다. 그래서 처음 생각한 건 각종 이미지들을 구글의 피카사웹으로 올린 후, 그 URL을 찾아서 XML파일을 교체한다는 아이디어였다.
일단 첨부파일들을 모두 다운받아서 피카사 업로더를 이용해서 올린 후, 구글 피카사웹 API 샘플을 이용하니 손쉽게 URL을 알아낼 수 있었다. 주의할 점이라면 한글로된 앨범 이름을 위해서 유니코드를 써야 한다는 것 정도가 샘플과의 유일한 차이였다.
def get_picasa_urls(email,password,wp_album):
gd_client = gdata.photos.service.PhotosService()
gd_client.email = email
gd_client.password = password
gd_client.source = 'wordpress_attachment_exporter'
gd_client.ProgrammaticLogin()
print 'picasa web connected'
dic = {}
albums = gd_client.GetUserFeed(user='default')
print len(albums.entry), 'albums found'
for album in albums.entry:
if album.title.text != wp_album:
print 'skipping album', unicode(album.title.text,'utf-8')
break
print unicode(album.title.text), 'album found'
#print 'title: %s, number of photos: %s, id: %s' % (album.title.text,album.numphotos.text, album.gphoto_id.text)
photos = gd_client.GetFeed('/data/feed/api/user/default/albumid/%s?kind=photo' % (album.gphoto_id.text))
for photo in photos.entry:
#print ' Photo:', photo.title.text, ' id:', photo.gphoto_id.text, ' url:', photo.content.src
dic[unicode(photo.title.text,'utf-8')] = unicode(photo.content.src,'utf-8')
print len(dic), 'images in picasa'
return dic
그다음 한 일은 내보내기한 XML 파일의 기존 URL을 위에서 찾아낸 새 URL로 교체하는 것인데, 정규식을 활용해보기로 했다. 문제는 이렇게 했는데도 실제로 미리보기가 안된다는 것이었는데, 아마도 flickr나 피카사 모두 직접 링크를 지원하지 않는 모양이었다. 그래서 어쩔 수 없이 모든 첨부파일을 손으로 하나씩 WP.com 으로 업로드한 후 그 URL을 적용하기로 했다.
아래는 최종 버전.
import re
import codecs
# configuration
wp_export_xml = '/Volumes/data/Downloads/wordpress.2009-07-25.xml'
attachment_ext = 'jpg|JPG|gif|GIF|png|PNG|bmp|BMP|doc|docx'
attachment_url_pattern = 'http://reiot[^"\'<>]+?\.(%s)'%attachment_ext
attachment_filename_pattern = re.compile('[^/=]+?\.(%s)'%attachment_ext)
wpcom_prefix = 'http://boxcatstudio.files.wordpress.com/2009/07/'
attachments = {}
# faster than open().read().encode('utf-8')
lines = codecs.open(wp_export_xml,"r","utf-8").read()
itr = re.compile(attachment_url_pattern).finditer(lines)
for m in itr:
orig_url = m.group()
new_url = wpcom_prefix + attachment_filename_pattern.search(orig_url).group()
attachments[orig_url] = new_url
print len(attachments), 'attachments found'
i = 1
for orig_url in attachments.keys():
new_url = attachments[orig_url]
print '[%d/%d] %s => %s'%(i,len(attachments),orig_url,new_url)
lines = lines.replace(orig_url,new_url)
i = i + 1
out_xml = codecs.open(wp_export_xml+".converted.xml","w","utf-8")
out_xml.write(lines)
out_xml.close()
정규식은 항상 볼 때마다 새로운 느낌이 들어서, 꽤 시간이 많이 걸렸다. 또 큰 파일에서 유니코드로 빠르게 읽으려면 codecs 모듈이 좋댄다.
5 comments 2009/07/27
Idogame

동접 1천명 == 3백만원
매출 1조짜리 기업 NHN에서 출시한 아이두 게임과 게임 오븐에 대한 짧은 생각:
- 하루 최고 동접 1000명을 찍으면 한달에 300만원을 지급한다? 한게임 유입자수 풀을 키우려는 전략이겠지만, 너무 짜다. 돈은 현재의 2배 정도로 늘리되, in game ad를 무조건 탑재하도록 하면 윈윈이 되지 않을까.
- 정말 이걸로 돈을 벌려면, 접속 시간을 최대한 늘이는 게임 또는 특정 시간의 접속 비율을 높이는 방향으로 게임을 디자인해야 할 것이다. (자꾸만 동접 늘리는 트릭만 구상하는 레이옷..)
- 게임오븐 IDE은
.NET이 아니라Native C++으로 만들어졌고, 루아(luabind)로 서버 및 클라이언트를 개발한다. 놀라운 점은 IDE 자체적으로 스크립트 디버깅이 가능한데, 브레이크 포인트라든지 콜스택, watch를 모두 지원한다는 것. (dev.naver.com/opensource에다가 이것만 따로 무료로 공개해주면 좋으련만!!!) - 네트워킹 역시 루아로 이루어지는데, key-value로 된 메시지를 보내거나 SyncObject라고 해서 자동적으로 테이블을 상대편으로 복제할 수 있다. 얘들도 __newindex 로 루아 테이블 내부 값의 생성, 삭제 및 변동을 추적했을까나~
- 사실 게임 오븐으로 만든 서버를 어떻게 호스팅하는가? 가 가장 궁금하다. 그냥 서버 exe 를 적당한 서버로 배포해서 실행하지는 않을테니, 여기에 설마 클라우드 개념이 들어갔을까? 흐흠..
2 comments 2009/05/21
Fail Fast, Fail Often
최근 레이다에 자주 걸리던 MDA 프레임워크에 대한 게임 디자인 워크샵이 사내에서 피자와 함께 열려서 쉬귀군과 함께 참가했다.
첫날에는 Sissyfight 3000을 1분 내에 끝내라는 퍼블리셔의 요청 메일을 받았다. 원작 게임이 재미없었던 우리 팀은, 두 세번 게임을 뒤집어 엎어 포커 버전으로 만들다가, 종료 5분 전 공격 카드는 버리고 색깔 카드와 방어 카드 만으로 2분짜리 클베 버전을 출시할 수 있었다. 뭔가 개발이 잘 풀리지 않아 잘나가는 옆테이블로 이직을 하고 싶었는데, 막상 출시를 하고 보니 사용자들이 제법 만족해서 좀 당황하기도 했다.
둘째 날에는 타워 디펜스를 페이퍼 프로토타이핑으로 만들었다. 초반부터 에이전트005가 투입되어서 자꾸 지금 방향이 틀렸고 자기 방식이 맞다고 주장해서 개발 기간을 제법 까먹었다. 클베를 하고 나서는 테스터들의 의견을 적극 반영하다 보니 원래 추구하는 미학과는 다른 방향으로 바뀌기도 했다. 워낙 타워 디펜스의 종류가 다양하다보니 협동이냐 경쟁이냐들 두고 초반부터 설전을 벌였는데, 실제로 테스터들로부터도 비슷한 문제를 지적받았다. 막판 30초를 남기고 뭔가 재미있어 보이는 버전이 나오긴 했는데 테스트도 못해봐서 결국 클베 버전을 출시할 수 밖에 없었다. 흑.
어쨌든 뭔가 쓸만한 걸 하나 배웠답시고 무려 주말에 출근해서 MDA를 시험 적용해 보기로 했다. 현재 상태에서 재미있는 것과 없는 것들을 나열하고, 도저히 버릴 수 없는 제약 조건을 뺀 나머지 기능들은 모두 feature creep이라는 누명을 씌워서 버렸다. 다음에는 무엇을 핵심적인 재미요소로 구현할 건가를 이야기했는데, 의외로 내가 생각하는 재미를 단 한 명에게 공감시키기조차도 힘들었다. 그래서 내 의도와 비슷한 동영상을 구해서 보여준 후, Fail Fast 을 해보니 몇 시간만에 비교적 게임이라고 부를만한 상태로 바꾸는 데까지는 성공했다. (물론 난 옆에서 서핑만하고 쉬귀군이 열심히 코딩을 했지만..) 그래도 보여주기엔 남부끄러운 수준이고, 핵심적인 제약 사항은 완수하지도 못했기에 일단 퇴근했다. 결과적으로는 실패로 끝난 거구만. OTL
TDD가 소프트웨어의 최종적인 진화의 방향을 점진적으로 설계해주는 것처럼, Fail Fast이 게임 디자인에 있어서 비슷한 역할을 하는 것 같다. 즉, 어느 정도 팀내에 공감이 이루어 졌다면, 한 두명이 안되는 이유를 1부터 10까지 나열하며 떠들 시간에 닥치고 일단 만들고 보자는 건데, 그러고 보니 쉬귀군의 신묘한 개똥 철학과 공통점이 많은 것 같다. (물론 일반적인 개발자는 따라해서는 안된다. 흐흐) 다만 이를 위해서는 빠른 변경이 가능한 게임 엔진 및 툴의 개발이 선행되거나 동시에 진행되어야 한다는 게 우리의 숙제일 것이다.
덕분에 몸은 좀 피곤하지만, 미학 – 사용자들에게 주고자 하는 재미 – 에 대한 확고한 비전의 공유가 얼마나 중요한지를 깨달을 수 있었던 한 주였다. 프리 프러덕션 기간에 게임의 재미에 대해서 명확하게 확정을 짓지 않고 프러덕션 단계로 들어가면, 항상 나중에 참가한 멤버들이나 경영진, 테스터 또는 퍼블리셔로부터 너무 낙관적으로 개발을 해서 그런지 재미가 없다는 이야기를 듣고는, 자의반 타의반으로 feature creep의 유혹을 받게 된다는 경험적 법칙이랄까. 옛날 게임 기획할 때 누가 이런 걸 가르쳐줬으면 지금쯤 성공한 기획자가 되었을지도 모르겠다. ㅎㅎ
짧은 시간에 게임 개발을 압축해서 경험할 수 있기에, 기획자나 프로그래머는 물론이거니와 중간 관리자들도 한번쯤 들어볼 만한 워크샵이다. 이런 좋은 경험을 혼자 낼름하지 않고 널리 전파하시느라 수고하신 Kay 님과 epiphany님께 감사드린다. (후기 상품은 내꺼임!!)
see also:
5 comments 2009/05/17
Named Boost Tuple
프로토콜 객체로 std::pair 나 boost::tuple 을 채택할 때의 문제는 순서가 틀리더라도 타입만 변환이 된다면 컴파일 타임 에러가 나지 않는다는 점과, first/second 라든지 get() 이 한눈에 안들어온다는 점이다. 또 디버거에서도 내부 값을 찾아 보기가 힘들고 컴파일 시간도 길어지기 때문에, boost::preprocessor 나 boost::tuple 기반의 직렬화 기법이 편하고 관리하기도 좋지만, 그냥 구조체로 만들어서 적당히 memcpy로 보내는 걸 다들 애용하는 것이리라.
boost::tuple<int,short,char> t; int i = get<0>(t); int j = get<1>(t); int k = get<2>(t);
그런데, 이걸 코드 생성기에서 이렇게 만들면 어떨까?
class login : public boost::tuple<wstring,string,int>
{
public :
const wstring & userid() const { return get<0>(); }
void userid( const wstring & v) { get<0>() = v; }
const string & passwd() const { return get<1>(); }
void passwd( const string & v) { get<1>() = v; }
int key() const { return get<2>(); }
void key( int v ) { get<2>() = v; }
};
login msg;
msg.userid(L"reiot");
msg.passwd(".com");
msg.key(1975);
wstring userid = msg.userid();
string passwd = msg.passwd();
int key = msg.key();
이 정도면 읽기도 쉽고 직렬화하기도 편한 객체처럼 보일 것 같은데… (여전히 디버거에서는 보기가 귀찮지만.. autoexp.dat 를 잘 고치면 해결할 수 있지 않을까나~)
2 comments 2009/05/01
first chance exception : The tag is invalid
요즘은 디버깅할 때 Ctrl+Alt+E를 눌러서 first chance exception이 발생할 때에도 브레이크포인트가 걸리게 하는 편이다. 뭔가 내가 모르는 문제가 저 안에서 벌어지고 있다는 게 못마땅하기 때문인데, 언젠가부터 64비트 OS에서 32비트 서버 어플리케이션에서 소켓 연결을 할 때, First chance exception 0×000006C5 : The tag is invalid 라는 예외가 던져져서 디버깅할 때 짜증이 솟구치는 나날들이 계속되었다. 더군다나 현재 채택한 DB 쿼리 방식이 필요할 때마다 재연결하는 connection pooling 이라서 디버거를 좀 오래 잡고만 있어도 계속 예외가 던져져서 집중이 자꾸 깨지곤 했었다.
조사해보니 마이크로소프트도 포기한 예외였는데, 도무지 이걸 무시할 방법을 찾지 못하다가 (Win32 예외 목록에 없으니까!) 그저께 커스텀 예외도 추가할 수 있다는 것을 우연찮게 겨우 이제서야 알게 되었다.
지금은… 마음이 편하다.
Add comment 2009/05/01
Visual Studio 2005 Intellisense Hotfix
.gif)
비주얼 스튜디오 2005에서 어떤 솔루션만 열면 인텔리센스가 멈추지 않는 현상을 만나서 검색해니, SP1 이후로 한글판 핫픽스 하나와 영문판 핫픽스 하나가 나와 있었다.
둘 중에서 후자만 깔아도 된다고는 하는데, 영문판 핫픽스는 한글판에서는 설치가 안된다는 것이 문제였다. 다행히도 쉬귀군의 팀은 영문판을 쓰고 있었기에 강제로 원격 설치(-_-)를 해서 관련 dll 들을 받아서 그냥 덮어씌워보니 그럭저럭 잘 돌아가는 것 같았다. 조금 걱정이라면 디버그-릴리즈 전환할 때마다 인텔리센스가 돌아간다는 점이랄까.
정리하자면, 영문판 사용자에게 부탁해서 다음 dll 들을 덮어쓰면 된다!
- VC/packages/vcpkg.dll
- VC/packages/VCProjectEngine.dll
- VC/packages/VCProject.dll
참고로, VC/bin/amd64 또는 x86_amd64 아래에도 버전은 같지만 크기가 다른 VCProjectEngine.dll 파일이 있긴 한데 얘네들은 안덮어써도 돌긴 돌아갔다…
ps. 컴파일이 빨라진다는 핫픽스도 있구나..
2 comments 2009/04/23





