RSS, Atom 등 피드를 읽어와서 사이드바 혹은 치환자 위치에 보여주는 플러그인입니다. 치환자 방식으로 사용할 때 치환자명은 [##_SimplePie_RSS_##] 입니다. 구버전은 언제나처럼 버전 관리글에 있습니다.

버전 1.1.2

최신 변경사항
- 피드 그룹을 사용할 때 발생하는 오류를 바로잡았습니다.
- 설정창을 정리하고 치환자 설명을 보강하고 수정하였습니다.

호환성과 버그 보고

이 플러그인은 SimplePie 파서에 의존하므로 심플파이 사용 환경이 되지 않으면 사용할 수 없습니다. 왠만한 현대적인 호스팅은 다 됩니다만, 확인하려면 플러그인 경로/sp.php로 들어가면 됩니다. 예를 들어 mydomain.com의 blog 폴더에 태터를 설치하셨다면

http://mydomain.com/blog/plugins/Loki_SimplePieRSS/sp.php

가 호환성 확인 페이지 주소입니다.

플러그인이 작동하지 않으면 먼저 위의 호환성 테스트를 해보시고, 호환이 된다면 디버그 모드로 들어가서 에러 메시지를 확인하시고 이상 증상과 함께 에러 메시지를 알려주시기 바랍니다.

사용 예시


사용 예시를 두 가지 제시하면 다음과 같습니다.

첫 번째는 아주 단순하게 1번 그룹에 모든 피드를 넣고 시간 역순대로 보여주는 형태입니다. 아이템 제목 길이는 9자로 축약하고, 제목 위에 마우스를 가져다 대면 원래 길이의 제목이 보이게 했습니다. 스킨은 설레는 마음 (핑크)입니다.

1번 예시

예시 1


<div class="feed">
<h3>피드</h3>

<ul>
<item>
<li> <a href="[##_item_permalink_##]" title="[##_item_title_full_##]">[##_item_title_##]<br />
<span class="name">[##_feed_title_##]</span>
<span class="date">[##_item_date_or_time_##]</span></a>
</li>
</item>
</ul>
</div>

설레는 마음 스킨은 메뉴 첫머리에 이미지를 사용하므로 "Recent Feed" 이미지 (RecentFeed.gif)를 만들어서 skin/customize/1/images 폴더에 넣고, 스킨 편집으로 들어가서 스타일 시트에 다음 줄을 추가했습니다.

.sideinfo .feed h3 { background:url(images/RecentFeed.gif);}

두 번째 예시는 피드 그룹을 블로그 피드와 트위터 피드 2개 설정해서 함께 보여주는 방식입니다. 사용 스킨은 O-range-O입니다.

사용자 삽입 이미지
<!-- recent feeds -->
        <group>
                <div id="[##_group_id_##]" class="listbox">
                    <h3>[##_group_name_##]</h3>
                    <ul><item>
                        <li>
                            <a href="[##_item_permalink_##]" title="[##_item_title_full_##]">[##_item_title_##]</a><br />
                            <span class="date">[##_item_date_or_time_##]</span> <span class="name">[##_feed_title_##]</span>
                        </li>
                    </item></ul>
                </div>
      </group>
피드 그룹에 따른 반복영역을 <group></group>으로 설정한 후 그룹명 치환자를 넣었습니다. 그리고 group_id 치환자를 (group1, group2 등) 아이디로 넣어서 아이디에 따라 헤더 이미지가 들어가도록 시트를 잡아주었습니다.
#group1 h3 {
    background: transparent
    url(http://lokasenna.pe.kr/blog/skin/orangeo_orange/css/image/orange_sidebar_feeds.gif) top left no-repeat !important;
    margin-bottom:0 !important;
}

#group2 h3 {
    background: transparent
    url(http://lokasenna.pe.kr/blog/skin/orangeo_orange/css/image/orange_sidebar_tweets.gif) top left no-repeat !important;
    margin-bottom:0 !important;
}
그리고 설정창에서는 그룹 이름을 지정해서 위의 group_name 치환자 자리에 들어갈 이름을 정해주었습니다.

사용자 삽입 이미지

즐겁게 사용하시고, 문제나 질문, 칭찬(?) 등이 있으면 댓글 달아주세요~
2009/11/27 16:15 2009/11/27 16:15
Posted by 로키
한동안 안하다가 조금씩이라도 해서 완성하자는 생각으로 코딩 프로젝트를 다시 잡았다. 그 과정에서 생긴 문제를 하나 해결해서 적어본다.

이 IRC 클라이언트를 만들면서 (가제 PRC) 처음부터 의도한 것은 기존 IRC 클라이언트보다 더 사용하기 쉽게 하자는 생각이었는데, 그 일환으로 초대 (INVITE) 이벤트도 텍스트 중심이 아닌 그래픽 인터페이스 중심으로 처리했다. 누군가에게 초대가 들어오면 그 사실만 알리는 게 아니라 초대받은 채널에 바로 입장할 수 있는 선택창을 띄우는 것.

초대받은 스크린샷

PRC에서 초대를 받았을 때 뜨는 메시지


그런데 여기서 문제가 생겼다. 어느쪽 선택지를 고르든 무조건 연결이 끊어지는 결과가 발생하는 것. 왜 그럴까 하고 소스를 뒤지다가 원인을 발견했다. 프로그램 창을 닫으면 서버 연결을 끊는 이벤트 바인딩을 넣어놓았는데, 그 바인딩을 메시지박스에서 계승하고 있던 게 문제였다. 다음은 관련 코드.

class App:

    def __init__(self, master):
        """초기 위젯 렌더링"""

        self.master = master
        #전체 창을 self.master 변수로 설정해서 같은 클래스 내 다른 함수에서도 부를 수 있게 한다

        self.master.bind("<Destroy>",self.closeWindow)
        #요 부분이 문제였다. closeWindow 함수는 연결을 끊는 함수인데 메시지박스에서 Yes를 선택하든 No를 선택하든 마찬가지로 창을 닫는 이벤트이니까 (<Destroy>) 같은 closeWindow 함수를 발동시킨다.

    #중략

    def closeWindow(self,event=None):
        """창이 닫히면 연결 끊기"""

        if self.connected:
            self.closeConnect()
            #연결이 되어 있다면 끊으라는 명령

    #후략


unbind로 바인딩을 끊으려고 해도 tkMessageBox 모듈로는 그게 좀 애매했다. tkMessageBox 모듈은 이런 식으로 불러오게 되어 있다.

        import tkMessagebox
        if tkMessageBox.askyesno(receivedInvite,askWhetherJoin):
            self.joinChannel(channel)

위젯이면 unbind를 할 수 있겠지만 위젯이 아니라 모듈로 바로 작업하는 거라서 그렇게도 할 수 없었다. 그렇다고 창을 닫을 때 동시에 서버 연결을 끊지 않으면 핑 타임아웃이 될 때까지는 같은 닉으로 재접속할 수 없으므로 사용하기 쉬운 IRC 프로그램을 만든다는 취지에도 어울리지 않았다.

그래서 검색하다가 발견한 것이 윈도우 매니져 프로토콜이었다. 창 닫는 버튼을 누른다고 바로 창을 닫는 대신 다른 이벤트를 먼저 발생시키는 이벤트라고 한다. 즉, 창을 닫으면 지정 함수를 발동시키는 게 아니라 사용자가 창을 닫는다는 명령을 내리면 함수를 발동시킨다는 미묘하면서도 중요한 차이다.

예시를 보고 바인딩 대신 프로토콜 핸들러로 바꿔본 결과는 성공. 서버 접속을 끊는 함수 바인딩이 메시지박스에 계승되지 않아서 원하는 효과를 낼 수 있었다.

class App:

   def __init__(self, master):
        """초기 위젯 렌더링"""

       self.master = master

        self.master.protocol("WM_DELETE_WINDOW",self.closeWindow)
        #바인딩 대신 프로토콜 핸들러로 바꾸었다

    #중략

   def closeWindow(self,event=None):
        """창 닫으라는 명령을 받으면 연결 끊고 창 닫기"""

       if self.connected:
           self.closeConnect()
            #연결이 되어 있다면 끊으라는 명령

        self.master.event_generate("<Destroy>")
        #그리고 창을 닫는다

    #후략


해피엔딩~ 초대받은 채널에 입장을 선택하면 자동으로 입장하고, 거절하면 입장하지 않는다. 어느 쪽이든 연결은 끊어지지 않는다. 이벤트 바인딩은 메시지 박스에 계승이 되고 프로토콜 핸들러는 안 되는 이유는 알 듯 말 듯 하면서 잘 모르겠지만(..) 어쨌든 좋은 게 좋은 거 아닌가.

자동입장한 스크린샷

접속 해제 없는 자동 입장 성공!


이런 식으로 천천히 작업하다 보면 언젠가는 완성을 볼 수 있겠지. 그러다 보면 코딩도 좀 늘어 있을까. 몇 달만에 본 소스인데도 막상 작업을 시작하니 대충 뭘 했는지 생각나는 거 보면 역시 배우는 방법 중에서는 해보는 게 으뜸인 듯 싶다.
2009/01/22 20:38 2009/01/22 20:38
Posted by 로키
IRC를 통해 주고받는 문자열 내에서 색깔 코드를 찾아내는 방법을 생각하다가 0부터 14까지의 숫자만 찾아내는 정규식 패턴을 짜봤다. (사실 이 색깔 코드도 mIRC랑 CTCP가 서로 달라서 어느 장단에 춤을 추어야 하나 고민 중. 특히 국내에서는 가장 흔한 클라이언트인 mIRC와의 호환은 꽤 유혹이 되지만, CTCP가 IRC 표준이라고 하면 그쪽에 맞추는 게 맞지 않을까.) 문제가 되는 건 이게 한 자리 수일 수도 있고 두 자리 수일 수도 있다는 것인데, 그래서 몇 번의 시행착오 끝에 나온 것은...

1[0-4]|(?<![0-9])[0-9]

정규식에서 | 는 '이거 아니면 저거'라는 뜻이니까 먼저 1 뒤에 0부터 4까지의 숫자가 있는 걸 찾아보고 (즉 10~14), 그게 아니면 앞에 숫자가 없는 0부터 9까지의 한자리수 숫자를 찾아보게 된다. 숫자 자체만 찾아야지 앞뒤 글자는 찾으려는 부분이 아니니까 [^0-9][0-9]는 아니고. 이렇게 하면 색깔 코드 바로 다음에 숫자가 나와도 색깔 코드하고 섞이는 일을 최대한 피할 수 있다. 예를 들어

chr(3) + '26번째 서랍에 있어요'

같은 문자열은 원래 chr(3) + '2'라는 색깔 코드 뒤에 '6번째 서랍에 있어요'라는 문장이 있는 것인데, 그냥 숫자를 찾았다가는 26이라는 존재하지 않는 색깔 코드를 붙이려고 해서 색도 표시가 안 되고 문장에서 숫자를 끊어먹어서 '번째 서랍에 있어요'가 된다. 반면 위의 정규식으로 하면 26이 아닌 2만 찾으니까 제대로 표시할 수 있다.

물론 이런 건 좀 어쩔 수가 없다.

chr(3) + '14번 타자입니다'

chr(3) + 14는 유효한 색깔 코드니까 이건 chr(3) + 1 색으로 '4번 타자입니다'라고 하려고 했는지 아니면 chr(3) + 14 색으로 '번 타자입니다'라고 하려고 했는지 컴퓨터는 알 수 없다. (사람이야 알지만..) 이런 문제 때문에 CTCP에 따르면 칸을 떼어서 색깔 코드와 실제 쓰려는 문장을 구분해야 하는데, 그렇게 칸을 떼면 mIRC에서 알아서 칸을 없애줄지 잘 모르겠어서. 음. 이 컴에서는 mIRC 구동이 안 되는데 어떻게 테스트하나. 그래서 mIRC 스크립팅 대안으로 파이썬을 배우기 시작한 건데 이런 식으로 다시 발목을 잡는 mIRC..ㅠㅠ
2008/08/18 09:33 2008/08/18 09:33
Posted by 로키

유니코드 유감

2008/07/10 05:34
어째서! 어째서 '원' 자가 들어간다고 멀티바이트로 설정한 문자열이 유니코드가 되어버리는 거냐..ㅡㅡ++++ '인'도 멀티바이트고 '제한'도 멀티바이트인데 '인원 제한'이나 '원'은 무조건 유니코드라니, 말이 돼? (크아악)
2008/07/10 05:34 2008/07/10 05:34
Posted by 로키

꾸역꾸역 진행 중

2008/07/04 21:59
어제는 아침 9시 반부터 오후 4시 반까지 모의고사 보고 들어와서 그냥 작정하고 놀았다. 그간 벼르던 코딩도 좀 하면서 이전에 적었던 모종의 프로젝트 진도를 조금 더 나갔다. 생각만큼 진척이 빠르지는 않았지만...

스크린샷

약간은 모양을 갖추기 시작


무엇보다 인코딩 문제가 골치아프다. 유니코드를 위젯에 직접 쓸 수는 없는데 사용자가 위젯에 입력한 문자열은 유니코드라든지 하는 상황에 @_@ 상태. 또 어떤 위젯에는 유니코드 직접 입력이 되고... 어찌어찌 한글 표시는 되게 해놨지만, 소스가 꼬인 상태여서 인코딩을 통합적으로 처리하는 기능을 만들어야 할 것 같다.

인코딩 다음에는 한동안 자료 구조를 짜봐야 할 것 같다. 예를 들어 지금은 이런 식으로 가고 있는데...

self.channels = [
{'name':'#포도원의제다이',
'users':['@Eldir','Loki'],
'lines':['Loki님이 입장하셨습니다.',]},
]

이 구조에는 추가할 것도 많지만, 변경할 것도 있다. 예를 들어 나중에 타자 중 알림까지 구현할 것을 생각하면 'users' 키는 단어 리스트 대신 딕셔너리 리스트로 하는 게 나을 수도 있다.

'users': [
{'name':'@Eldir', 'typing': False},
{'name': 'Loki', 'typing': True}
]

대신 사용자 리스트 정렬 같은 게 귀찮아지긴 한다. 사용자 정보는 타자중 표시만 있어도 된다면 이름을 딕셔너리 키로 사용하는 것도 한 방법일 듯.

'users': {
'@Eldir': True,
'Loki': False,
}

하는 식으로. 정렬은 keys() 목록 정렬로 해결할 수 있을 테고.

'lines' 부분 역시 태그 정보까지 표시할 수 있게 확장해야 한다. 지금 생각하는 건 사용자가 직접 입력할 리가 없는 특수문자로 문자열을 갈라서 정보를 표시하는 정도. 대충 이런 식? (아마 몇 번 돌려보고 디버깅하기까지는 애로사항이 꽃피겠지만...)

line = '안녕, 여러분!¿red,0,2¿bold,1,4¿strike,4,7'

self.channels[self.currChan]['lines'].append(line)

totalLines = str(len(lines))

splitline = line.split('¿')

self.displayText.insert(END,splitline[0].strip()+'\n','default')

try:
    taginfo = splitline[1:]
    for tag in taginfo:
       tagIndex = tag.split(',')
       self.displayText.tag_add(tagIndex[0],\
        "%s.%s" % (totalLines,tagIndex[1]), \
        "%s.%s" % (totalLines,tagIndex[2]))

except IndexError:
    pass

여기에 태그에 따라 글씨 색, 크기 등을 바꾸는 부분을 처리하면 ', 여러분!' 같은 같잖은 짓을 할 수 있다. 역시 인코딩이 괴롭히기는 해서, 한글로는 글자 수 처리가 제대로 안 되고 유니코드로 처리해야 할 것 같다. 유니코드를 제대로 처리할 체계만 갖추면 비교적 풍부한 포매팅 선택지를 제공할 수 있을 듯. 제공하는 게 과연 좋을지는 생각해볼 문제이긴 하다.

사용자 삽입 이미지
사용자 삽입 이미지
참, 원래는 이름을 DiRC로 하려고 했는데 이미 있어서 어려울 것 같다. DieRC나 DiceRC는 어감이 썩 좋지 않고 IRC 클라이언트라는 점도 덜 사는 것 같고. 무난해서 결국은 그렇게 갈지도 모르지만, pertho, 혹은 peorth (<-- 저 룬문자이지, 요 아가씨가 아님-->) 룬을 어떻게 활용할 수 있을까도 궁리하고 있다.

페어토 혹은 페이오스는 제비뽑는 상자 혹은 주사위 컵을 상징한다고 한다. 친구끼리 하는 즐거운 놀이를 뜻할 수도 있고 운명과 예언을 상징할 수도 있는, 어찌 보면 다소 이율배반적인 룬. 기본적으로는 여성적 신비와 자궁의 창조적 에너지를 가리킨다.

생각해보면 무엇이 나올지 모르는 주사위 컵이나 제비상자와 수많은 미래의 가능성을 품는 자궁, 그리고 예측불허인 미래나 운명은 일맥상통하는 데가 있다. 우연의 변덕, 무한한 가능성과 즐거운 놀이를 나타낸다는 점에서 페이오스 룬은 RPG 클라이언트에 꽤 어울리는 것 같다. 여성의 신비나 여성적 창조성이라는 뜻은 RPG계의 남초 현상을 살짝 찌르기도 하고..ㅋㅋ

문제는 어떻게 활용하느냐인데... 아이콘은 페이오스 룬의 입구에 주사위 하나 있는 정도로 처리하면 될 것 같고, 문제는 이름. Pertho/Peorth와 Python을 합성한 Pyrtho나 Pyorth도 생각해볼 수 있겠지만, 이름에서 의미가 확 살지 않는 건 좀 아쉽다. PeoRC도 그렇고...

사실 정말로 하고 싶은 이름은 따로 있긴 하다. 페이오스 룬의 가장 마음에 드는 번역, Dicebox. 깔끔하고 의미 전달 잘 되고 정말 딱인데 아쉽게도 내가 생각해낸 게 아니라서 쓸 수가 없다. SF 웹코믹 제목이거든. Dicecup도 생각할 수 있지만 역시 딱은 아니고, 이래저래 생각해 보고 있다.

어쨌든 답답하면 답답한 대로, 막히면 막히는 대로 꾸역꾸역 진행하고 있는 프로젝트다. 이리저리 생각해보고 머리쓰는 게 나름 재밌다. 이렇게 찌끄덕찌끄덕거리다 언젠가 완성할 수 있을까.
2008/07/04 21:59 2008/07/04 21:59
Posted by 로키
시간이 없어서 많이는 못하고 있지만 계속 생각은 하고 있는 코딩 프로젝트 때문에 이미지를 다루는 파이썬 라이브러리인 PIL (Python Imaging Library)을 사용해 보았다. 여기저기 예시를 참조한 끝에 빈 주사위 그림에 숫자를 그려넣는 스크립트를 만들었다. 아래는 그 스크립트.

#PIL에서 필요한 모듈 가져오기
from PIL import Image, ImageFont, ImageDraw

#이미지 경로와 폰트 파일 (트루타입) 설정
imgdir = 'images/'
fontfile = 'images/andlso.ttf'

#숫자 파일 만드는 기능
#아래 pasteImg에서 이쪽을 참조하므로 순서상 앞이어야 한다.
#프로젝트에 실제 사용할 때는 클래스라 순서는 상관없겠지만..
def makenumber(string,filename,color,size):
    """
    @string: string
    @filename: string
    @color: hex string
    @size: int
    """
    #글자체를 트루타입으로 불러온다
    font = ImageFont.truetype(fontfile,size)

    #글씨의 가로세로를 튜플로 구한다 가로 50, 세로 100이라면 (50, 100) 하는 식으로 나온다
    imgsize = font.getsize(string)

    #새로운 투명 이미지를 만든다  
    pic = Image.new("RGBA", imgsize, (0,0,0,0))
    draw = ImageDraw.Draw(pic)

    #투명 이미지에 지정한 문자열을 지정한 글자체와 색으로 쓴다
    draw.text((0,0),string,font=font,fill=color)

    #이미지를 지정한 파일명으로 저장하고 파일명 출력
    pic.save("%s%s.png" % (imgdir,filename),'png')

    return filename

#그림 위에 다른 투명 그림을 덧붙이는 기능
def pasteImg(bgfile,string,filename,color,size,ext,overwrite=False):
    """
    @bgfile: string
    @string: string
    @filename: string
    @color: hex
    @size: 2-tuple
    @ext: string
    @overwrite: boolean
    """
    #덮어쓰기가 0이면 지정한 숫자 파일이 있나 확인하고 없으면 만듦
    if not overwrite:

        try:
            #숫자파일이 있나 확인하느라 열어보고...
            fg = Image.open("%s%s.%s"%(imgdir,filename,ext))
        except IOError:
            #없어서 에러나면 makenumber를 불러서 숫자 파일 제작
            makenumber(string,filename,color,size)
            fg = Image.open("%s%s.%s"%(imgdir,filename,ext))

    #덮어쓰기가 1이면 무조건 숫자 파일을 만듦
    else:
        makenumber(string,filename,color,size)
        fg = Image.open("%s%s.%s"%(imgdir,filename,ext))

    #배경이 될 그림을 열고...
    bg = Image.open("%s%s.%s"%(imgdir,bgfile,ext))

    #배경 그림과 숫자 그림의 가로세로를 튜플로 낸 후..
    W, H = bg.size
    w, h = fg.size

    #배경과 붙여넣을 그림의 가로세로 차이의 반값을 구해서...
    x, y = (W-w)/2, (H-h)/2

    #위에서 낸 x, y를 왼쪽 위 구석으로 해서 숫자 파일을 붙여넣음
    #뒤에 숫자 파일 핸들러를 다시 지정해주는 건 투명 파일로 붙여넣는 데 필수라고 한다
    #이유는 모른다. 그냥 어디 게시판에서 찾은 해결책
    bg.paste(fg, (x,y), fg)

    #숫자를 붙여넣은 그림을 저장한다
    bg.save("%s%s/%s.%s"% (imgdir,bgfile,filename,ext), ext)

#그리고 기능을 사용
#빈 d20 (20면체 주사위) 그림에 1~20까지 숫자를 그려넣은 후
#(글자 크기는 30, 숫자는 검정색)
#images/d20 폴더에 파일명 1png에서 20.png으로 저장

i=1

while i<=20:
    pasteImg('d20',str(i),str(i),'#000',30,'png',True)
    i = i + 1

이렇게 해놓으면 주사위 그림과 폰트 파일만 있으면 어떤 주사위든 쉽게 만들 수 있다. 이런 거라든지...

사용자 삽입 이미지
뭐 별로 예쁘진 않다. 내게 너무 많은 걸 바라지 말아주길..(..) 하지만 중요한 건 주사위 그림 일일히 만들지 않아도 좋은 주사위 그림만 있으면 쉽사리 숫자를 그려서 만들 수 있다는 점. 다만 그림이 투시도 느낌이 아니면 숫자를 그릴 때 좀 더 정밀한 계산을 해야겠지.

한 한 달 전에 만들어놓은 스크립트인데, 이거 보니 다시 코딩하고 싶다..;_; 시험공부 지겨워, 엉엉.
2008/06/21 03:54 2008/06/21 03:54
Posted by 로키

대장정의 첫걸음

2008/05/30 02:45
파이썬 (Python)과 Tkinter로 IRC 클라이언트를 만드는 작업을 어제 시작했다. 어제오늘 시행착오 엄청나게 겪으면서 배운 게 정말 많아서 갈 길이 멀다는 실감이 새삼 들었다. 처음부터 생각하는 기능을 다 넣으려고 했다가는 시작도 제대로 못하고 포기할 것 같아서 일단 뼈대부터 시작해 하나하나 덧붙이면서 늘려가고 있다. Grid 레이아웃 관리자는 위젯을 하나씩 덧붙이기가 쉽기도 하고.

지금까지 갖춘 모습은 이 정도.

스크린샷

뼈만 앙상~


모습으로만 보면 아직 별 거 없지만, 처음부터 걸린 문제였던 서버 입력 처리를 해결한 후라 나름 뿌듯하다. 이전에 콘솔용 IRC 봇을 만들어 봤는데도 GUI로 넘어오니까 잘 안 돼서 결국 관련 예시를 참조해서 쓰레딩 (threading) 처리를 하니까 됐다. 쓰레딩 구문 부분을 주로 참고하고 큐는 목록으로 대체한 후 .pop(0)을 이용했고, GUI 생성을 단일 기능으로 처리하는 등 예시 코드하고는 좀 달라졌지만.

IRC에서 오는 메시지를 프로그램에 전부 표시하지는 않고 (개발하는 동안에는 디버깅용으로 IDLE 콘솔에는 표시하지만), 상태표시줄을 만들어서 메시지 종류에 따라 인증 중, 연결 실패, 연결 성공 등 상태만 요약해서 표시했다. 저 상태표시줄의 또 다른 주요 용도는 나중에 누구누구는 타자 중이라고 표시하는 것. 어느 세월에 거기까지 만드나...

여기서부터 할 일은 일단 서버와 채널 목록을 저장해 놓게 딕셔너리를 만들어서 피클 (pickle)로 쟁여놓고(..) 편집하는 인터페이스를 만드는 것. 설정 사항은 만들면서 잔뜩 늘어날 테니 지금부터 딕셔너리로 관리하는 게 좋겠지. 자동 조인 채널 목록은 딕셔너리에서 채널 창으로 옮겨놓고, 일단 채널에 모두 들어가지면 들어간 채널마다 탭 인터페이스를 만들어서 '들어갈 채널' 문구를 '현재 채널'로 바꾸고 '나가기' 버튼을 만들... 아아 복잡해진다..ㅠㅠ

어쨌든 일단은 설정 딕셔너리 만들고 저 텍스트 창에 뭔가 표시하는 게 급선무겠지. 그 다음에는 채널에 있는 사람 표시하는 목록, 그 다음은 정보 표시창. 입력창은 비교적 쉽게 만들 수 있을 듯. 여러 채널이나 탭 인터페이스는 그 다음에 생각하도록 하자. 메뉴바도 만들고... 그것까지 완성하면 이제 공포의 주사위 패널이 기다리고 있구나..(..) 프로그램 공부한 적 없는 인간에게는 산 넘어 산이라고밖에는...

재미로 하는 것인 만큼 조급하게 생각할 필요는 없겠지. 느긋하게 마음 먹고 틈틈이 작업하다 보면 프로그램도 모양새를 갖추고 파이썬 실력도 많이 늘을 것 같다.
2008/05/30 02:45 2008/05/30 02:45
Posted by 로키

작은 실험.

2008/05/02 07:24
최근 코딩 프로젝트에서 불러오는 피드가 많아지면서 페이지 로딩 속도가 느려졌다. 페이지 첫머리에  무조건 피드를 불러오고 데이터베이스에 입력하는 스크립트를 넣어놓고 있었으니 당연한 일이다. 사실 '대개의 사용자에게' 페이지 로딩을 빠르게 하는 법은 전부터 생각하고 있었지만, 안하고 있다가 이번에 스크립트 처리 속도가 2초를 넘어가는 걸 보고 마침내 실행에 옮겼다.

해결책은 꽤 간단하다. 모든 사용자의 접속 때마다 피드를 불러오고 처리하는 게 아니라, 피드 처리 링크를 따로 달아서 피드 업데이트를 자동화 대신 수동화하는 거다. 대부분의 사용자는 피드 처리를 거칠 필요가 없고, '새로운 기사 불러오기' 링크를 클릭하는 사용자만 1~2초 로딩 시간을 들여 피드를 불러오고 데이터베이스에 입력하는 스크립트를 발동하면 된다. 덕분에 페이지가 많이 가벼워져서, 스크립트 처리 속도는 2초를 넘는 수준에서 다시 0.6~0.8초 수준으로 떨어졌다.

사용자 전체로 보면 이쪽이 더 효율이 높은 것은 당연하다. 2초, 0.6초는 서버에서 스크립트를 처리하는 시간 얘기고 브라우저에서 서버에 페이지를 요청하고 사용자에게 보이도록 화면에 표시하는 시간은 또 따로 있다. 따라서 모든 사용자가 페이지에 들어올 때마다 그 몇 초를 허비하는 게 없어졌으니 시간 면에서도, 서버 부담 면에서도 절약한 것은 적지 않다.

다만, 전에 망설였던 것은 이게 전체 사용자로 보면 시간을 많이 절약해주는 대신 새 기사가 나왔는지 확인하려는 사용자에게 링크를 클릭하는 약간의 노력과 몇 초의 로딩 시간을 요구한다는 점이다. 즉, 페이지에 들어오는 모든 사용자에게 요구했던 1.2~1.4초의 시간을 하루에 한두 명의 사용자에게 전가한 셈이 된다.

그게 내게는 이 문제의 흥미로운 점이다. 전체적인 절약은 큰데, 어차피 이전의 낭비야 (나만 빼고) 별로 의식하지 않았던 반면, 새로 기사가 있나 확인하려고 링크를 클릭하는 사용자는 클릭하는 수고와 추가 로딩 시간을 의식할 수밖에 없다. 그래서 과연 시간 절약을 사람들이 체감할까, 차라리 다같이 시간을 조금씩 더 낭비하면서도 아무도 의식하지 못하는 편이 체감 면에서는 더 효율적이지 않을까 싶다. (비록 그 체감이 거짓이기는 하지만.)

이게 어떻게 보면 상당수의 정책이나 체계의 역설이기도 하지 않나 싶다. 전체의 부담을 소수에게 옮겨서 집단 전체의 효용은 큰데, 그 효용의 수혜자는 좋아진 걸 별로 의식하지 못하고 부담이 생긴 소수는 자신에게 생긴 새로운 부담을 의식하는. 비유하자면 자유무역이 그렇다. 사회 전체적으로 생기는 효용은 굉장하지만, 어차피 그 효용이라는 게 대개의 사람에게는 물건 값이 약간 내려간 정도인데 무역으로 도태되게 된 기업과 관련자는 생계 자체에 위협을 받으니까.

물론 내 피드 수집 페이지는 자유무역과는 여러모로 경우가 다르다. 규모는 말할 것도 없고, 링크를 클릭하는 노력은 아주 적은 건 둘째치고라도 자발적이니까. 전체적으로는 별로 의식하지 못하는 절감의 대가로 소수가 비용 증대를 의식해야 한다는 점에서만 약간 비슷한 데가 있을 뿐. 그런 의미에서 역시 이건 '작은' 실험이다. 업데이트를 수동화한 것이 업데이트 주기에 어떤 영향이 있을지, 새로운 기사가 있는지 보려면 따로 링크를 클릭해야 한다는 것을 사용자들이 부담으로 인식할까, 아니면 재밌어할지 궁금하다.

수정: 업데이트가 잦다면 몰라도 또 링크를 의미 없이 누르는 일이 너무 잦아지는 것 같아서 이제는 그냥 마지막 확인한 시점에서 6시간이 지났으면 불러오는 스크립트를 자동으로 돌리게 했다. chron이 안 되는 서버이지만 데이터베이스로 흉내는 내는 셈.
2008/05/02 07:24 2008/05/02 07:24
Posted by 로키
mIRC 로그를 RTF 파일로 정리하는 프로그램을 짜면서 초장에 제일 막혔던 건 RTF의 한국어 인코딩 부분이었다. PyRTF라고 파이썬으로 꽤 고레벨에서 RTF 파일을 만드는 모듈을 발견하기는 했는데, 이게 다국어 지원이 전혀 없다는 걸 깨닫고 욕이 나왔다. (..) 버전 0.45 정도에 과한 기대를 해도 안 되겠지만 말야.

위키피디아에서 RTF 표준에 대한 기사를 보고서도 한참 헤맨 게, utf-8 디코딩을 해서 유니코드를 내려고 해도 결과는 이런 식이었거든.

>> print '안녕'.decode('utf-8').encode('unicode_escape')
>> \uc548\ub155

문제는 저건 RTF 파일에서 받지를 않는다는 점. 워드프로세서로 열어보면 안 나온다. 그래서 역으로 워드프로세서로 '안녕'을 치고 에딧패드 (EditPad)로 열어보니 다음과 같이 나왔다.

\u50504\'3f\u45397\'3f\u33\'3f

이 차이를 가지고 한동안 끙끙대다가 ord 기능을 알게 되어서 결국 다음과 같은 식으로 해결했다.

def ordinalCode(string, enc):

    ordcode =''
    string = string.decode(enc)
    ordinals = [ord(x) for x in string]

    for ordinal in ordinals:
        #디버깅 코드
        #print ordinal
        ordcode = "%s\\u%d\\'3f" % (ordcode,ordinal)

    return ordcode

이 기능을 달아두고 RTF 파일을 만들 때는 예를 들어 추가할 내용을 string이라고 하면

enc = 'utf-8'
newline = ordinalCode(string, enc)
p.append(newline)
section.append(p)

하는 식이 된다. 사실 글자 뒤마다 붙은 \'3f가 무슨 뜻인지도 모르고 \u50504와 \uc538의 차이가 뭔지도 모르겠다. 그걸 알 만큼 공부했으면 프로그래머지, 허접 취미 코더겠나. 내가 아는 건 해보니 일단 됐다는 것. 그리고 내가 테스트해본 한 이렇게 하면 우리말로도 PyRTF를 이용해 RTF를 쓸 수 있겠다는 정도이다. 막혔던 문제에 대해 해결책을 찾아낸다는 건 즐거운 일~
2008/04/02 21:06 2008/04/02 21:06
Posted by 로키
스크립팅 언어 파이선 (Python)을 대충 보기 시작해서 여기 있는 스크립트를 기반으로 간단한 IRC 봇을 만들어봤다. 일단 IRC 연결과 서버에서 오는 텍스트 처리까지 됐으니 이제부터는 파이선의 텍스트 처리 기능을 하나하나 찾아가면서 왠만한 건 다 구현할 수 있을 거다. 시간과 노력, 참을성의 문제일 뿐. 이제 남은 큰 과제는 클래스를 써서 코드를 조립식으로 만들고 Tkinter를 배워서 외관상 독립적인 프로그램을 만드는 것.

이 프로젝트의 시작점인 테스트 스크립트는 사실 별 쓸모는 없다. 쓸모는커녕 남이 하는 말은 전부 따라하는, 열라 짜증나는 기능..(..) 하지만 이게 텍스트 처리를 해서 더 쓸모있는 기능으로 나아가는 첫 과정!

주사위군과 우트군의 혈투 (?)

옵 없으면 옵과 싸우지 말자


파이선으로 끄적거리다 보니 mIRC와 다른 점을 느낀 게, mIRC 스크립팅은 굉장히 높은 레벨에서만 돌아간다는 점이다. 이미 mIRC가 처리해서 이건 누가 보냈고 메시지 종류가 무엇인지 해석한 출력만 가지고 노니까 굉장히 간단하고 추상적이다. 예를 들어 주사위군이 채널에 '안녕'이라고 쳤다면 이게 주사위군이 채널에 보낸 '안녕'이라는 메시지라는 건 이미 알고 들어가고, 그 '안녕'이라는 글만 처리하면 된다.

반면 파이선은 IRC 클라이언트가 아니므로 서버에서 보내오는 텍스트를 일일히 처리해서 누가 보냈는지, 메시지의 구분이 무엇인지 등 처음부터 알아내야 한다. 주사위군이 채널에 '안녕'이라고 쳤다면 ':주사위군!~user@xyz.abc.net PRIVMSG #채널 :안녕'이라는 출력이 오고, 이걸 해석해서 주사위군이 보낸 '안녕'이라는 채널 메시지라는 걸 알아내는 스크립트를 써야 한다. 그래서 1년 이상 mIRC 스크립팅 하면서 배운 것보다 하루 파이선을 다루면서 IRC 프로토콜을 많이 배운 느낌.

이런 식으로 심심할 때마다 끄적끄적거리면서 프로젝트를 하나하나 완성해가야지. mIRC 스크립팅처럼 사용 환경에 좌우받지도 않고, 사용도 훨씬 간단한 파이선 IRC 로봇을! (사용 과정의 단순성과 제작 과정의 단순성은 거의 반비례하는 게 안습일 뿐..ㅠ_ㅠ)
2008/02/05 01:13 2008/02/05 01:13
Posted by 로키