터미널에서 사용할 수 있는 트위터 클라이언트는 상당히 많은 편이다. 이전까지 node-tweet-cli를 사용하고 있었는데 스트림도 지원하고 간단하게 트윗을 하기엔 편했지만 멘션에 답하는 기능이 없어서 여간 불편했었다. 그러던 중에 rainbowstream을 보고 나서 한 눈에 반해 바로 옮겨타게 되었다.

이 클라이언트는 현존하는 어떤 클라이언트보다 가장 힙스터스러운 트위터 환경을 구축해준다. 쇼케이스 보면 반하지 않을 수 없다.

이 툴은 pip를 통해서 설치할 수 있다. 전역 설치를 하고 싶다면 그냥 설치하면 된다.

# 그냥... 설치
$ sudo pip install rainbowstream

설치 가이드에서는 venv를 사용하길 권장하고 있다.

# venv 설치
$ virtualenv venv
$ source venv/bin/activate
$ pip install rainbowstream

파이썬이 없거나 환경이 필요한 경우라면 리포지터리를 참고해서 설치하자.

처음으로 실행하면 트위터의 토큰을 발행하기 위한 로그인 창이 뜬다. 토큰을 발행해서 숫자를 집어 넣으면 그때부터 사용 가능하다.

트윗 작성은 t <내용>으로 할 수 있으며 h를 입력하면 도움말 전체를 확인할 수 있다. 가장 마음에 드는 부분은 아예 쉘처럼 동작한다는 점인데 실행하는 순간부터 스트림을 시작하고 매 스트림되는 메시지마다 id를 부여해서 rep <id> <내용> 식으로 멘션에 대한 응답도 쉽게 작성할 수 있다. 스트림은 p, r로 멈추고 다시 시작하는 것도 가능하다. 쉘 종료는 q로 가능하다.

이렇게까지 트위터 해야하냐고 그만 물어보세요…

심지어 테마도 지원하니 취향에 맞게 컬러 스킴도 지정해보자. theme으로 사용 가능한 테마 목록을 확인하고 theme <테마명>으로 변경할 수 있다.

tmux와 함께 사용한다면 타임라인과의 분리불안을 해소하는 것과 동시에 본업(?)을 지속할 수 있는 환경을 구성할 수 있으니 tmux를 필히 사용하자.

구글 검색을 위해서 파이어폭스를 켜며 문득, ‘그냥 터미널에서 구글 검색할 수 있는 방법은 없을까?’ 라는 생각이 들어서 검색해봤더니 역시 멋진 분이 googler라는 도구를 멋지게 만들어서 공유하고 있었다.

googler는 파이썬으로 작성되어 있어서 파이썬이 기본적으로 필요하다. OS X나 Linux 환경 대부분은 기본적으로 설치되어 있는 버전과 함께 잘 구동된다.

OS X 환경에서 Homebrew를 사용하고 있다면 brew로 쉽게 설치할 수 있다. 최근에 추가된 패키지라서 brew update가 필요할 수도 있다.

$ brew install googler

Linux 환경에서는 Homebrew Folk인 Linuxbrew를 사용해서 설치할 수 있다. 하지만 내 lubuntu 환경은 그렇게 공간이 넉넉한 편이 아니라서 git 리포지터리를 통해 바로 설치했다.

$ git clone https://github.com/jarun/googler/
$ cd googler
$ sudo make install

검색은 간단하다. googler <검색어>로 검색할 수 있는데 그 외 플래그 등 옵션은 googler를 입력하면 확인할 수 있다. 검색 결과에서 다음 검색 결과를 확인하는 것도 가능하며 열고 싶은 페이지의 숫자를 입력하면 웹브라우저로 열어준다.

how to use googler

구글 검색 결과를 아름답게 확인할 수 있다.

이제 구글 검색도 힙하게 터미널에서 하자!

지난 달 노트북을 구입하고 매일 들고 다니면서 유용하게 사용하고 있어 간단하게 사용기를 남겨본다. 4월이면 애플이 새로운 맥북을 내놓을 거라는 이야기가 계속 있어서 노트북을 구입하지 말고 기다려야 하나 고민했었다. 하지만 당장에 해야 할 일이 워낙에 많았던 터라 노트북이 필요했었는데 그렇다고 큰 돈 안쓰고 저렴한 노트북으로 알아보다가 구입하게 된 것이 Dell Inspiron 11 3000이다.

장점

배터리가 엄청 오래간다. 들고 다니면서 하루 1시간 정도 사용하는데 일주일에 한 번 정도 충전한다. 사양이 낮은 것인지 배터리 효율이 좋은 것인지는 모르겠다.

소음이 없다. 이건 팬이 달려 있지도 않을 뿐더러 스토리지가 메모리타입이기 때문에 별도의 팬이 필요하지 않은 것 같다. 다만 좀 과하게 사용하면(= 파이어폭스 창 2개 켜고 터미널 열면) 발열이 좀 있는 편이다. 팜레스트나 키보드보다는 노트북의 하판이 뜨거워지는 편이라서 그렇게 뜨거운걸 느끼진 못하지만 아마 더 많은 연산을 하면 키보드쪽도 뜨거워질 것 같다.

키보드가 준수한 편이다. 키감도 괜찮은 편이고 실제 키보드보다 사이즈가 작아지면 의례 레이아웃을 이상하게 만들거나 키 배치를 어색하게 만들어서 불편하기 마련인데 작은 사이즈에도 불편함이 거의 없는 편이고 방향키 레이아웃도 여유가 있어서 편하다. 키감은 펜타그래프 치고는 좀 얕게 느껴지지만 질이 꽤 좋다. 이런 키보드에서는 메타키를 Fn 조합으로 많이 제공하는데 Delete나 Insert는 일반 키로 설정되어 있어서 특히 마음에 들었다.

단점

모니터 시야각이 좀 심하다. 이건 TN 패널의 고질적인 문제라고 하는데 충분히 밝은 곳이 아니면 이상하게 눈이 쉽게 피곤해지는 느낌이다. 이 노트북에서 하는 작업이 코드를 작성하거나 이미지를 봐야 하는 작업이 아니라 대부분 터미널에서 글쓰고 일기쓰는 정도 일만 하고 있기 때문에 크게 민감하게 생각하고 있진 않지만 만약 그런 작업이 필요하다면 별로 추천하고 싶지 않다. 아쉽게도 이 가격대의 노트북에서 TN 안쓰는 경우를 찾기가 힘들다.

흰지가 조금 불안한 편이다. 무릎에 놓고 사용하기에는 모니터 각도가 충분하게 젖혀지지 않는데다가 약간 힘을 주면 흰지와 모니터 사이가 벌어지는 것을 볼 수 있다. 출퇴근하면서 사용할 때 다른 사람이 내리다가 가방 같은 것으로 모니터를 치면 이게 부러지거나 할 것 같아 불안하다. 그렇다고 아주 헐렁하거나 톡 쳐도 부서질 것 같은 내구성인 것은 아니다.

저가형 노트북 답게 트랙패드가 좋지 않다. 염가 노트북에서 투 핑거 휠까지 지원한다는 것이 놀랍긴 하지만 작은 키보드를 사용해서 입력하다보면 손뼘에 닿아 커서가 이동해버릴 때가 있다. linux에서 키보드 입력 중에 트랙패드 입력 차단하는 설정이 있어서 설정을 해뒀었는데 막상 커서를 사용하게 될 때는 불편함이 있어서 그냥 익숙해지기로 했다. 필요할 때는 민감하게 반응 안하고 필요하지 않을 때는 너무 민감해서 참 친해지기 어렵다.

그 외에는

그 외 짧은 코멘트는 다음과 같다.

  • Windows 10은 생각보다 무거워서 lubuntu를 설치했는데 만족하고 있다.
  • 무게는 적당하다. 무겁다고 생각해본 적은 없다.
  • 플라스틱이라 모서리가 깨질까 걱정이 되긴 한다.
  • HDMI 포트가 있는데 쓸 일이 아직 없어서 잘 모르겠다.
  • 마이크로SD를 지원한다. dropbox가 안되는 상황에서 백업용으로 사용하고 있다.

추천한다면

  • 리눅스 설치해서 터미널만 써도 상관 없는 사람
  • 간단한 웹서핑, 글만 쓰면 되는 사람
  • 300불 예산 언저리에서 구입해야 하는 사람
  • 노트북 소음이 싫은 사람
  • 충전 자주 하는 귀찮음이 싫은 사람
  • 데스크탑이나 다른 중요한 일을 처리할 수 있는 컴퓨터가 있는 사람
  • 심심한 사람

추천 안한다면

  • 조금 사양 높은 게임 해야하는 사람 (에뮬 게임이라면 뭐…)
  • 메인 노트북으로 사용해야 하는 사람
  • 탭 3개 이상 켜고 인터넷 하는 사람
  • 윈도만 써야 하는 사람 (XP 같은거 설치하면 빠를지도)
  • 좋은 디스플레이가 필요한 사람
  • 예산을 늘릴 수 있는 사람

지금까지 사용한 경험으로는 충분히 본전을 뽑을 것 같은 기분이 든다. 터미널에도 점점 친해지고 있고 웹브라우저 없이도 사는 방법을 배우고 있다. (뭔가 이상하지만) 새 맥북이 나오더라도 당분간은 이 노트북으로 계속 지내게 될 것 같다.

code.org 이후로 코딩 교육에 관한 이야기를 자주 보게 된다. 한국에서도 공통 교과에 코딩을 포함해야 하는가에 대한 논의를 많이 접할 수 있었다. 기술 발전에 따라 기초 학문으로 가치가 높아지고 있고 수학과 같이 논리적 사고력을 배양할 수 있다는 의견부터 현장에서 제대로 된 교육이 가능한가에 대한 의문, 코딩 학원과 같은 사교육 열풍만 불 것이라는 부정적인 의견까지 다양했다.

코딩 교육이 옳은가, 옳지 않은가에 관한 이야기보다는 실제로 교육을 위해 움직이고 있는 모습을 보면 먼 이야기가 아니라 이미 현실이다. 앞서 예로 들었던 code.org에서는 프로그래밍을 통해 데이터를 분석하거나 문제를 해결하는 과정을 제공하고 있다. 영국의 Code Club 프로그램에서는 만9-11세를 대상으로 스크래치부터 기본적인 웹개발(HTML, CSS), 파이썬 등을 학습하는 커리큘럼을 학교 현장에서 제공하기 위해 힘쓰고 있다. 찬반을 하기엔 이미 깊숙이 들어와 있고 시행착오를 넘어 성숙하고 있는 단계로 느껴진다.

코딩 커리큘럼을 제공하는 대표적인 단체, Code.org와 Code Club

어릴 때 프로그래밍을 가르치는 것이 정말 효율적인가에 대한 생각은 사실 미술과 음악, 작문과 같이 자기 생각과 관점을 표현하는 수단이라 생각하면 더 와 닿는다. 자신을 드러내는 또 다른 방법을 하나 더 배운다고 생각한다면 그게 크게 잘못되거나 어렵고 힘든 일이라고는 생각이 들지 않는다. 내 또래 세대는 방과 후 프로그램에서 배운 HTML로 학급 웹사이트를 만들고 포켓몬 도감을 만드는 데 시간을 썼다. 한 학년을 마치며 그 기간을 기억하기 위해 학급 문집을 만들고 각자 기념할 만한 물건을 타임캡슐에 넣어 묻었던 일을, 지금 어린 세대는 오늘을 기억하려는 방법으로 스크래치로 만든 애니메이션을 함께 공유하고 마인크래프트에서 함께 지은 건물과 탐험했던 지역을 보며 회상한다. 지금 당장에도 이 세대가 컴퓨터를 통해 만들어내는 수많은 미디어를 Youtube에서 직접 눈으로 확인할 수 있다. 지금의 어린 세대를 가까이서 관찰할 기회가 많지 않아 일반화하기 어렵지만 저스틴님 댁에 놀러 가서 아이들과 이야기할 때마다, 초등학교 다니는 우리 집 막냇동생이 운영하는 블로그를 들어갈 때마다 이 세대의 삶에서 컴퓨터를 어떻게 소화하고 활용하는지는 이미 내 상상 이상이다.

Scratch 웹사이트에 접속해보면 애니메이션, 그림 등 다양한 학생 작품을 접할 수 있다.

어릴 때 코딩을 배우는데 어렵게 느끼지는 않을까? 이미 추상화된 아이디어가 일반화된 세대에게는 Car, MyCar 클래스를 만들어 설명하기보다 “마인크래프트에서 블럭의 종류는 여럿이지만 블럭은 다 같은 크기고 가방에 넣을 수 있어.” 식으로 훨씬 쉽게 이해할 수 있게 된다. 놀이터에서 그네와 시소를 타고 학교에서는 그 과학적 원리에 대해 배웠던 것과 같이 이런 환경에 일찍 노출된 세대라면 코딩을 배운다는 것 자체가 어색하지 않다. 우리의 사고와 접근으로는 어렵다고 생각할지 몰라도 마우스와 키보드 인터페이스보다 터치 스크린을 먼저 접한 아이들이 이해하기에는 훨씬 간단하고 익숙한 내용일지도 모른다.

내가 교육 현장의 일선에 있는 것도 아니고 아직 자녀가 있는 처지는 아니라서 이게 좋고 나쁨을 이야기하기엔 쉽지 않다. 하지만 이런 변화는 지금의 세대에겐 논쟁거리가 될 수 있어도 이후 세대에겐 당연하고 필수적인 생활 요소가 될지도 모른다. 지하철에서 책을 읽지 않고 스마트폰만 만진다고 못마땅하게 생각하는 사람들이 있지만, 지금의 어린 세대에게는 그런 모습을 당연하다고 생각할 것이다.1 지금의 홈 오토메이션은 별 볼 일 없게 느껴지지만, 미래 세대는 훨씬 많은 모듈을 사용해 직접 로직을 구성하고 활용하게 될 것이고 이처럼 일상에서 프로그래밍적 사고가 필요한 일이 더욱 많아져 지극히 당연한 교육이 될지도 모른다. 그때쯤 되면 이런 논쟁이 있었다는 사실을 산업혁명 시기 러다이트 운동과 나란히 배울 수도 있다.

이런 이야기 이면에는, 영미권과 한국을 비교하게 되면 상대적으로 좁은 언어권에서 나타날 수밖에 없는 한계를 느끼게 된다. 영국의 Code Club의 커리큘럼이 호주로도 들어와서 호주판 Code Club 네트워크를 순식간에 구축한 것과 같이 동일 언어권에서는 빠르게 확산하고 지식이 공유된다. 한국어 사용자에게 있어서 상대적으로 소외되는 느낌이 들 수밖에 없다. 그렇다고 학습에서 영어를 직접 사용하거나, 생성자, 소멸자, 발생자와 같은 한문 조어를 통해 배우는 것도 부차적인 학습이 따라와 부담을 느낄 수밖에 없다. 이런 문제를 뒤로 밀어둔다 하더라도, 귤화위지라는 말과 같이, 해외 사례를 모델로 삼아 번안된 교육안으로 적용하는 일은 단순하게 여겨질지 몰라도 옮겨 심는다고 모든 일이 저절로 해결되지 않는다. 환경도 필요하고 인력도 필요하다.

최근 BBC에서 영국의 7세 아동을 대상으로 제작 배포한 micro:bit. 웹사이트를 보면 없던 개발욕도 생길 것 같다.

나도 방과 후 교육으로 HTML을 배우고서 웹을 처음 알게 되었고 그 이후로도 웹 프로그래밍이나 컴퓨터에 계속 관심을 두게 된 것은 자연스러웠다. 나는 문과를 선택했고 대학도 지리교육을 전공했다. 코딩을 배운 경험 자체에 대해서는 전혀 불평할 여지가 없었다. ‘문제를 해결하는 논리적 접근 방식을 배웠다.’ 식으로 경험을 말하기에는 쉽게 드러나지 않아 판별하기 어려운 부분이다. 하지만 프로그램을 작성하는 방식, 코딩을 배운 것이 내 삶에 도움이 되지 않았다고 생각하는 편이 더 이상하고 어색하다. 내 삶에 있어 도움이 되었다고 보고 있어서 이 교육이 필요하다고 마음으로 느끼고 있다.2 그래서 나에게는 더더욱 “코딩 교육이 필요하다, 필요하지 않다” 보다는 어떻게 해야 제대로 된 교육이 가능한가에 대해 더 관심이 간다.

앞에서 이야기한 것과 같이 “필요 없다”고 말하기엔 이미 현실이다. 환경과 인력의 부족으로 포기하는 것보다 오히려 현업에 있는 사람들이 좀 더 제대로 된 커리큘럼과 프로그램을 가질 수 있도록 목소리를 더 내야 한다. 개인이기 때문에 할 수 있는 것이 없다고 느껴질지 모르지만, 개인의 경험이 공공의 지식이 되는 과정 없이는 교육적 토대와 사회적 기틀이 생겨나기 어렵다. 나는 어떻게 배웠고 어떤 부분이 쉽고 어려웠다는 이야기를 공유해야 한다. 내가 아는 지식을 타인, 더 나아가 다음 세대가 쉽게 이해할 수 있도록 나눠야 한다. 그리고 이런 지식 데이터베이스를 교육에 어떻게 녹일 수 있는지 담론을 구성해야 한다. 그 역할이 우리 세대가 해야 할 일이라 생각한다.

  • 나중에 그 세대가 자라면 공공장소에서 홀로그램 켜서 통화하는 사람은 예의 없다고 생각할지도 모를 일이다. 
  • 지금은 업계에 있어 설득이 덜하게 느껴지지만 5년 전만 해도 이렇게 웹개발을 할 것이란 생각을 전혀 못 했다. 
  • 맥 환경을 사용할 때는 Ctrl + Space를 사용하고 있지만 지금 lubuntu를 설치해서 사용하는 노트북은 키보드 사이즈가 작은 문제인지 Ctrl + Space가 손에 잘 익지 않았다. 그래서 한영 전환키로 우측 Alt 키를 배정해서 사용하고 있었는데 작성하다보면 변경이 되었다 말았다 하는 문제가 있었다. 단순히 입력기 문제라고 생각하고 있었는데 Alt키의 기본 동작이 동시에 나타나는 문제였다.

    lubuntu는 xkb를 통해 키보드 맵핑을 처리하고 있다. /usr/share/X11/xkb/symbols/altwin을 열어서 다음 부분을 확인한다.

    $ sudo vim /usr/share/X11/xkb/symbols/altwin
    
    // Meta is mapped to second level of Alt keys.
    partial modifier_keys
    xkb_symbols "meta_alt" {
        key <LALT> { [ Alt_L, Meta_L ] };
        key <RALT> { type[Group1] = "TWO_LEVEL",
                     symbols[Group1] = [ Alt_R, Meta_R ] };
        modifier_map Mod1 { Alt_L, Alt_R, Meta_L, Meta_R };
    //  modifier_map Mod4 {};
    };
    
    

    RALT 즉, 우측 Alt키에 Alt_R, Meta_R이 배정된 것을 확인할 수 있다. 이 키의 키맵을 아래와 같이 Hangul로 변경한다.

    // Meta is mapped to second level of Alt keys.
    partial modifier_keys
    xkb_symbols "meta_alt" {
        key <LALT> { [ Alt_L, Meta_L ] };
        key <RALT> { type[Group1] = "TWO_LEVEL",
                     symbols[Group1] = [ Hangul ] };
        modifier_map Mod1 { Alt_L, Alt_R, Meta_L, Meta_R };
    //  modifier_map Mod4 {};
    };
    

    키맵 설정은 컴파일 과정을 거쳐 /var/lib/xkb 위치에 xkm 파일로 저장되어 있다. 해당 파일을 제거한다. 제거하면 위에서 변경한 설정을 반영한 새로운 파일을 생성할 준비가 끝난다.

    $ sudo rm /var/lib/xkb/*.xkm
    

    이제 로그아웃, 로그인을 다시 해서 위에서 제거한 파일을 다시 생성한다. 오른쪽 Alt키가 Hangul키로 변경 된다. 한글 입력기에서 한영 전환키를 다시 설정하면 RALT 대신 Hangul 키로 잡히고 이제 입력이 올바르게 잘 동작하는 것을 확인할 수 있다.

    이상한모임 주관으로 진행된, 장소에 구애받지 않고 어디서나 참여할 수 있는 컨퍼런스, 이모콘 2016 S/S에 발표자로 참여했다. 이모콘은 누구든 자신의 경험을 공유할 수 있는 기회를 제공하는 형식으로 진행되는 온라인 컨퍼런스다. 지난 1회에서는 스탭으로 참여했지만 2회에서는 스피커로 참여할 수 있었다.

    주제 선정은 한참 고민했었지만 개인적인 일정에 생각보다 시간을 만들지 못해서 이전에 포스팅한 적이 있던 NodeJS의 제너레이터에 관한 내용을 선정했다. 더 미리 준비해서 시작했다면 다른 재미있는 내용을 했을텐데 좀 더 일찍 주제를 정했으면 하고 후회를 좀 했다. 그래도 이전에 작성한 포스트는 koa를 설명하기 보다는 generator와 spawn하는 함수를 작성하는데 너무 많은 분량을 할애해서 정작 koa 얘기가 너무 적었던 반면에 이번엔 koa를 더 설명해야지 하는 욕심이 있었다.

    라이브로 진행을 하고 싶었지만 긴장해서 진행에도 방해되고 발표도 신경쓰면 너무 정신 없을 것 같았고, 30분에 맞추기 위해 여러 차례 연습을 하는 것보다 단 편집을 고려해서 한 번 촬영 후 편집으로 30분에 맞추는게 낫겠다 판단해서 녹화를 했다. 슬라이드 준비에 1시간 반 정도 걸렸고 촬영에 45분, 편집에 2시간 반 정도 걸려서 총 4시간 40분 정도 시간을 사용했다. 결과적으로는 연습해서 그냥 하는 시간과 별 차이 없는 느낌이 들었지만 중간에 불필요한 설명을 자른다거나, 미리 넘어가버린 슬라이드를 보정한다거나 하는 부분에 있어서는 많이 편했다. 아무래도 이모콘 특성 상 녹화로 대체할 수 있다는 것은 장점이겠지만 다음에는 라이브로 할 수 있도록 준비를 미리 해야겠다는 생각을 편집하는 내내 했다.

    영상 편집은 Screenflow 라는 도구를 사용했다. 예전에 번들로 구입한 적이 있었는데 한번도 사용해보지 않다가 처음으로 제대로 사용했다. 스크린/사용자 녹화, 편집기까지 간단한 기능은 모두 제공한다. 영상을 자르고 편집하는 단축키가 마스터링 도구(어도비 프리미어나 애플 파이널컷 같은)에 비해서 좀 모자란 편이라서 마우스로 일일이 한 탓에 편집 시간이 더 들긴 했지만 그래도 편집하는 도중에 멈춘다거나 하는 일 없이 끝까지 무사히 편집할 수 있었다.

    슬라이드는 이전 이상한모임 멜번 모임에서 사용했던 적이 있는 reveal.js를 사용했다. 사실 기본 테마나 설정도 있지만 영어에는 이뻐 보이는데 한글에서는 좀 다르게 느껴져서 인라인 스타일을 막 넣어 편집해서 사용했다. GIF 이미지는 Giphy라는 사이트에서 좋아하는 미드 장면들을 찾아서 넣었다. 슬라이드 작성에는 크게 어려움이 없었는데 최근에 koa로 토이 프로젝트를 진행하고 있었기도 했고, 이전에 작성한 적이 있던 내용이라서 크게 막히는 부분은 없었다. (매번 발표 때마다 그림만 기억나고 내용은 기억나지 않는다는 피드백을 자주 받아서 한편으론 기쁘면서도 슬프다. 다음엔 꼭 제대로 된 내용을…)

    발표를 보면서 고쳤으면 하는 점을 적었다.

    • 아, 음 같이 횡설수설하는 말과 반복해서 사용하는 미사여구를 줄일 것
    • 말이 상당히 여유로운데 조금 더 밀도있게 진행하면 좋지 않을까
    • 침묵 속에서 라이브코딩을 할 거라면 미리 작성한 코드를 설명하는 쪽을 택할 것
    • 주제의 기승전결이 좀 더 체계적인 느낌이 들도록 내용을 작성할 것
    • 연습, 연습, 연습

    특히 이번 이모콘에서 다르게 느꼈던 점은 스피커로 참여해서 느낄 수 있던 부분이였다. 각각 선정해서 발표한 내용도 너무 재미있었지만 그 외에 부수적인 부분도 많이 눈에 들어왔다. 다른 발표를 보면서 어떤 방식으로 내용을 풀어가는지, 어떻게 내용을 설명하고 끊기지 않고 자연스럽게 진행하는지, 슬라이드나 영상 자료를 어떻게 활용하는지, 그 내공을 엿보고 배울 수 있어서 좋았었다. (세상은 넓고 초고수는 많다!)

    이모콘 이름으로 좋은 기회 제공해주고 함께 참여 및 준비한 모든 분들께 감사의 말씀을 전하고 싶다. 다음 이모콘에서도 스피커로 참여할 기회가 된다면 꼭 참여하고 싶고 그 시기를 위해 미리미리 준비해야겠다.

    발표자료

    요즘 타이핑을 하는데 손가락의 움직임이 점점 둔해지는 기분이 들어서 하루 5분이라도 짧게 연습하자는 목표를 만들었다. 터미널에서 간단하게 예제 문장을 보여주고 타이핑 하는 정도만 생각했는데 이미 GNU 프로젝트로 gtypist라는 타자연습 패키지가 존재했다.

    gtypist 화면

    화면에서 벌써 힙력이 느껴진다!

    “엄청 유명한데 나만 모르고 있었음” 패키지 같아서 따로 포스팅 안하려고 했는데 꾸준히 타자 연습하겠다는 목표를 적는 핑계와 함께 @justinchronicle님께 터미널이 힙하다는 얘기를 듣고 힙터지는 터미널 도구 연재를 해볼까 생각도 들어서 급 포스팅을 하고 있다.

    설치는 간단하다. 대부분의 패키지 관리자를 통해서 간단하게 설치 가능하다.

    $ brew install gtypist # in OS X
    $ apt-get install gtypist # in Ubuntu
    

    그리고 gtypist라고 입력하면 콘솔에서 타자연습 화면이 나온다. 한메타자교사 같이 화려한(지금 보면 뭐지 싶겠지만) 화면은 아니지만 그래도 타자 연습 예제를 제공하고 타수나 오타율을 확인할 수 있다.

    타이핑이 느려진다거나 코드 읽는 눈이 느려진다거나 하는 기분이 들면 실제로 그런지 안그런지는 사실 잘 모르겠지만 의식적으로 연습을 하지 않는다면 현행 유지 내지는 더 둔해지는 것은 당연한 것 같다. 내가 다른 일을 하고 있다면 몰라도 내 일에 가장 많은 시간을 할애하고 있는 기술인데 그냥 사소하게만 여겨서는 안될 것 같다. 사소한 부분도 의식하고 작은 시간이라도 투자해서 꾸준히 연습해야겠다.

    최근에 구입한 Dell 노트북에 조금이라도 가볍게 사용해보려고 Lubuntu를 설치해서 사용하고 있다. 트랙패드가 예전에 비해 많이 나아지긴 했지만 아무래도 맥북에서 사용하던 것과는 많이 달라서 좀 더 키보드 친화적인 환경을 꾸려야겠다는 생각이 들었다. 그러던 중 tmux와 다시 친해질 기회인 것 같아서 tmux를 설치하게 되었다.

    어제 tmux 이야기를 트위터에 올렸더니 ujuc님이 powerline이란 멋진 tmux 플러그인을 소개해주시고, 사용하는 rc 파일을 공유해주셨다.

    만약 tmux 플러그인에 관심이 있다면 이 페이지가 도움이 된다.

    그 외에도 간단하게 설치할 수 있는 것도 많이 보였다.

    구글 검색해보면 이것저것 유용한 도구가 많이 나온다.

    tmux 설정 되돌리기

    tmux.conf 재미있는 점이 default 파일이 존재하지 않는다는 점이다. 설정 하나를 변경하면 기존 설정을 알지 못하는 이상 다시 기본 설정으로 돌아갈 수가 없다. 그 눈 아픈 기본값 초록색 상태 막대로 한번에 돌아갈 방법이 없다는 뜻이다.

    그래서 tmux 기본 설정을 어딘가 추출해서 보관해두면 다시 돌아오는데 편리하다. 현재 tmux에 설정된 값은 다음 명령어로 추출할 수 있다.

    $ tmux show -g | sed 's/^/set-option -g /' > ~/.tmux.current.conf
    

    구글링 해보면 멋지게 꾸며진 tmux.conf를 많이 볼 수 있다. 나처럼 설정을 잘 모르고 적용했다가 명령을 시작하기 위해 사용하는 프리픽스인 Ctrl + b를 이상한걸로 변경해서 종료도 못하고 오고가도 못하는 상황을 마주할 수도 있으니 꼭 기본 설정을 추출해두자.

    tmux.conf를 적용하는 명령은 source-file이다.

    $ tmux source-file ~/.tmux.current.conf
    

    직접 설정 변경하기

    내 경우는 터미널 폰트를 비트맵으로 사용하고 있어 앞서 powerline을 적용하니 대다수가 깨져 이쁘게 적용되질 않았다. 게다가 사양 탓인지 좀 느려지는 기분이라서 간단하게 색상 바꾸고 필요한 것만 설치하기로 했다.

    tmux에서 가장 필요했던 부분은 배터리 잔량 표시와 일자/시간 표시였다. 일자/시간은 기본적으로 가능한 부분이라 배터리 잔량 표시는 다음 프로그램을 설치했다.

    아쉽게도 잔량 표시 그림은 그려지지 않지만 수치가 나오니 그럭저럭 만족하고 있다.

    기분 전환 겸 상태 막대 색상도 초록에서 연한 회색(colour235)로 변경했다. 사용할 수 있는 색상은 다음 스크립트로 확인할 수 있다.

    for i in {0..255} ; do
        printf "\x1b[38;5;${i}mcolour${i}\n"
    done
    

    누가 이 결과를 보기 좋게 github에 올려뒀다.

    원하는 색상이 나오지 않을 때

    이 색상 설정은 256color 모드로 실행하지 않은 터미널에서는 동작하지 않는다. 색상이 적용되지 않는다면 다음 설정을 참고하자.

    # .bashrc or .zshrc 에 추가
    export TERM=xterm-256color
    alias tmux="tmux -2"
    
    # .tmux.conf 에 추가
    set -g default-terminal "screen-256color"
    

    OS X의 터미널은 기본적으로 256color로 설정되어 있다.


    tmux의 기본적인 기능은 예전 요약했던 내용이나 nanhapark님의 포스트를 참고하면 되겠다. 물론 이런 요약본도 tmux.conf 한방에 모두 변경될 수 있어서 tmux.conf를 조심하자는 이상한 결론을 내려본다.

    그간 사용하던 맥북 에어를 보내고 집에 있던 넷북으로 간간히 작업을 하고 있었는데 어느 날 갑자기 넷북 디스플레이가 나가버렸다. SSD까지 교체해서 그나마 빨라졌나 했더니 더이상 사용할 수 없게 되어버렸다. 새 맥북이 조만간 나온다길래 기다리려 했는데 해야 할 일이 많아 더이상 미룰 수 없어 맥북 나오기 전까지 사용할 용도로 저렴한 노트북을 하나 구입하게 되었다.

    터미널 구동할 정도만 되면 충분하다는 생각에 11인치 정도 선에서 가장 저렴한 노트북을 알아봤다. 작은 사이즈를 고르려니 선택할 수 있는 폭이 좁았다. 후보로 Lenovo ideapad 100s(한국서는 i-slim어쩌고), HP Stream 11, Dell inspiron 11 3000 을 골랐다. Lenovo 제품은 Officeworks에서 행사중이라서 250불 가량이고 HP와 Dell은 300불 정도였다. 스펙은 셋 다 비슷해서 가서 만져보고 구입하기로 결정했다.

    키보드는 Lenovo가 마음에 들었지만 Dell 키보드도 괜찮았고 크기도 적당해서 Dell로 구입했다. 사진으로는 HP Stream가 가장 괜찮아 보였는데 실제로 보니 키보드도 그렇고 생각보다 별로였다. Dell inspiron 11 3000을 JB HiFi에서 워런티 포함 AUD305 주고 구입했다. 친구랑 빨간색 사야한다 얘기하면서 갔는데 흰색만 있어서 흰색을 구입했다.


    Dell Inspirion 11 3000

    Dell inspiron 11 3000 작년 모델은 2-in-1 인데 2016 모델은 그냥 랩탑으로 나왔다. 모델도 두 종류인데 하나는 셀러론 모델이고 하나는 펜티엄 모델이다. 구입한 셀러론 모델의 스펙은 다음과 같다.

    • Intel® Celeron® Processor N3050 (2M Cache, up to 2.16 GHz)
    • Windows 10 Home with OneDrive 64-bit English
    • 2GB Single Channel DDR3L 1600MHz
    • 32GB eMMC Storage
    • Intel® HD Graphics
    • 11.6-inch HD (1366 x 768) Anti-Glare LED-Backlit Display
    • Wireless 802.11ac + Bluetooth 4.0, Dual Band 2.4&5 GHz, 1×1
    • 32 WHr, 2-Cell Battery (Integrated)
    • Integrated Widescreen HD (720p) Webcam with Digital Microphone
    • 45 Watt AC Adapter
    • Height: 18.45-19.88mm (0.73-0.78″) x Width: 292mm (11.5″) x Depth: 196mm (7.72″)
    • Starting at weight 1.18Kg (2.6lbs)
    • up to 9 hours 34 mins
    • HDMI v1.4a, USB 3.0 x 1, USB 2.0 x 1
    • Noble lock security slot, Micro SD card reader (SD/SDHC/SDXC), Headphone/Mic

    이 가격대는 브랜드를 불문하고 같은 스펙이라서 사실 큰 의미는 없었다.

    Dell Inspirion 11 3000 keyboard

    외관은 구 맥북의 미니어처 버전 정도 되는 인상을 받았다. 뚜껑도 반들반들해서 먼지 잘 붙을 것 같지만 흰색이라 티가 잘 안난다. 키보드는 검정이라 괜찮지만 팜래스트는 흰색이라서 때가 좀 탈 것 같다. 디스플레이도 저가형을 감안하고 화면이 나온다는 데에 의의를 둔다면 그럭저럭 이해 할 만 하지만 시야각이 좀 많이 좁은 편이다. 액정도 살짝 어두워서 밝은 곳에서는 조금 침침하게 느껴진다. 스피커 음량은 적당한 편이다.

    하드웨어에서 가장 아쉬운 부분은 전원이 켜졌거나 충전중임을 표시하는 인디케이터가 전혀 없다는 점이다. 그리고 메모리타입이라 그런지 fan도 들어있지 않아 소리가 전혀 나지 않는다. 이게 장점인지 단점인지 미묘한데 가볍게 사용하면 크게 발열은 나지 않는데 발열이 조금 나기 시작하면 잘 식지 않는 것 같다.

    Dell Inspirion 11 3000

    기본적으로 Windows 10이 설치되어 있는데 Dell 제품이 다 그런지 몰라도 McAfee 한 달 이용권이랑 Dropbox 20GB 1년 이용권이 있었다. 켜자마자 맥아피 제품이 구동되며 컴퓨터가 멈춰서 버벅임을 뚫고 바로 McAfee를 삭제했다. 환경 구성하는데 시간을 딱히 사용하고 싶지 않아서 그냥 windows로 사용하려고 했었는데 OEM이라 그런지 설치되어 있는 기본 프로그램이 너무 많아 상당히 무거워서 lubuntu를 설치했다.


    장점은 저렴한 가격과 휴대성이고 단점은 성능 정도 될 것 같다. 구 맥북 디자인을 좋아한다면 디자인도 장점이 될 수 있을 것 같다. 할 일이 많은데 도움이 되었으면 좋겠다.

    사이드 프로젝트에서 Express를 오랜 기간 사용했었는데 hapi 가 좋다는 얘기를 듣고는 hapi를 많이 사용해왔다. Hapi도 단순하긴 하지만 “설정만 넣으면 되는” 단순함이라서 설정에 들어가는 수고가 꽤 컸다. 최근에는 토이 프로젝트에서 API를 작성하는데 에러 발생 여부에 따라서 {"ok": true} 하나 넣어주는 작업에 오만가지 코드를 작성해야 했다. express와 다르게 미들웨어에서 request, response에 접근할 수 있는 포인트가 워낙에 많아 더 복잡하게 느껴졌다. 그러던 중 예전에 잠시 비교글로 봤던 koa를 살펴봤는데 지금 필요한 상황에 맞는 것 같아 koa로 다시 코드를 작성했고 마음에 드는 구석이 많아서 간단한 소개를 작성한다.

    Koa는 ES2015의 문법 중 하나인 제너레이터를 적극적으로 활용하고 있는 웹 프레임워크다. 모든 요청과 처리를 제너레이터를 활용해 파이프라인을 만드는 것이 특징이며 그 덕분에 깔끔한 async 코드를 손쉽게 작성할 수 있다. Express 만큼은 아니더라도 다양한 라이브러리를 제공하고 있고, express의 라이브러리나 미들웨어도 thenify나 co로 변환해서 활용할 수 있을 만큼 확장성이 높다.

    이 포스트는 제너레이터를 먼저 살펴보고, 제너레이터를 유용하게 사용할 수 있는 co를 살펴본 후, KoaJS를 간단하게 살펴보는 것으로 마무리한다.


    제너레이터 Generator

    다른 언어에도 이미 존재하고 있기 때문에 크게 특별한 기능은 아니지만 ES6에서의 구현을 간단히 정리하려고 한다.

    일반적인 함수의 경우, 매 실행마다 같은 흐름으로 모든 코드를 실행하지만 Generator 함수는 실행 중간에서 값을 반환할 수 있고, 다른 작업을 처리한 후에 다시 그 위치에서 코드를 시작할 수 있다. 이 제너레이터는 반복 함수 iterator를 next()로 제공하고 결과를 value로, 진행 상황을 done으로 확인할 수 있다.

    구구단을 제너레이터로 작성하면 다음과 같다.

    function* nTimesTable(n) {
      for(var i = 1; i <= 9; i++) yield n * i;
    }
    

    제너레이터는 위와 같이 function* fnName(){} 식으로 *을 넣어 선언한다. 익명 함수의 경우도 function*(){} 식으로 선언한다.

    이제 이터레이터(iterator)를 nineTimesTable에 반환 받는다.

    var nineTimesTable = nTimesTable(9);
    

    이터레이터는 next()를 통해 실행할 수 있다. 이 함수로 중단한 위치의 결과가 반환된다.

    var result = nineTimesTable.next();
    console.log(result); // { value: 9, done: false }
    result = nineTimesTable.next();
    console.log(result); // { value: 18, done: false }
    result = nineTimesTable.next();
    console.log(result); // { value: 27, done: false }
    
    // keep calling...
    
    result = nineTimesTable.next();
    console.log(result); // { value: 72, done: false }
    result = nineTimesTable.next();
    console.log(result); // { value: 81, done: false }
    result = nineTimesTable.next();
    console.log(result); // { value: undefined, done: true }
    

    매 반복 실행에서 value를 반환하지만 동시에 done으로 해당 함수가 yield 결과 없이 종료되었는지 확인할 수 있다. 마지막에 별도의 return 값이 없기 때문에 valueundefined가 된다.

    이런 이터레이터의 반환 특징을 이용하면 다음과 같이 iterator를 호출하는 함수를 작성할 수 있다.

    function caller(iter) {
      var result, value;
      while(result = iter.next()) {
        if(result.done) break;
        value = result.value || value;
      }
      return value;
    }
    
    var result = caller(nTimesTable(3));
    console.log(result); // 27
    

    donetrue를 반환할 때까지 해당 이터레이터를 실행해 결과값을 가져오는 caller를 작성했다. 만약 매 반복에서 특정 함수를 실행하고 싶다면 다음처럼 작성할 수 있다. 앞서 작성한 nTimesTable 함수가 더 많은 내용을 반환하도록 수정했다.

    function * nTimesTable(n) {
      for(var i = 1; i <= 9; i++) yield { n: n, i: i, result: n * i };
    }
    
    function caller(iter, func) {
      var result, value;
      while(result = iter.next()) {
        if(result.done) break;
        value = result.value || value;
        if(func) func(value);
      }
      return value;
    }
    
    caller(nTimesTable(3), value => {
      console.log('%d x %d = %d', value.n, value.i, value.result);
    });
    

    앞서 작성한 caller는 제너레이터 내의 yield에 대해서는 처리를 하지 못한다. 제너레이터에서 이터레이터를 반환하고 진행을 중단했을 때 해당 이터레이터를 처리해서 다시 반환해야 한다. 결과를 넣고 다시 진행할 수 있도록 작성해야 하는 것이다.

    function* getAnimalInCage() {
      yield "Wombat";
      yield "Koala";
      return "Kangaroo";
    }
    
    function* Cage() {
      var cageAnimals = getAnimalInCage();
    
      var first = yield cageAnimals;
      var second = yield cageAnimals;
      var third = yield cageAnimals;
    
      console.log(first, second, third);
    }
    

    Cage 제너레이터를 실행하면 yield를 3번 사용했기 때문에 최종 console.log가 출력하는 결과를 보기까지 4번에 걸쳐 실행된다.

    var cage = Cage();
    var firstStop = cage.next();
    // {value: iterator, done: false}
    

    첫 번째 yield 결과가 firstStop에 저장되었다. cageAnimals는 위에서 코드에서와 같이 getAnimalInCage 제너레이터가 생성한 이터레이터다. 이 이터레이터에 next() 메소드로 값을 받은 후, 그 값을 다시 first 변수에 다음과 같이 반환한다.

    var firstAnimal = firstStop.value.next();
    // firstAnimal: {value: "Wombat", done: false}
    var secondStop = cage.next(firstAnimal.value);
    

    next의 인자값으로 첫 결과인 Wombat을 넣었다. 이전에 멈췄던 위치인 첫 번째 yield로 돌아가 함수 내 first에는 Wombat이 저장된다. 나머지도 동일하게 진행된다.

    var secondAnimal = secondStop.value.next();
    // secondAnimal: { value: 'Koala', done: false }
    
    var thirdStop = cage.next(secondAnimal.value);
    var thirdAnimal = thirdStop.value.next();
    // thirdAnimal: { value: 'Kangaroo', done: true }
    
    var lastStop = cage.next(thirdAnimal.value);
    
    // Wombat Koala Kangaroo
    

    마지막 Kangaroo는 yield가 아닌 return이기 때문에 done이 true를 반환한다. 앞서 직접 호출해서 확인한 코드는 반환하는 값이나 호출하는 형태가 일정한 것을 볼 수 있다. 즉 재사용 가능한 형태로 만들 수 있다는 의미다.

    다음은 catchEscapedAnimal()getTodaysZookeeper() 함수를 이용한 Zoo 제너레이터 예시다.

    function catchEscapedAnimal() {
      return function(done) {
        setTimeout(function() {
          done(null, {name: 'Kuma', type: 'Bear'});
        }, 1000);
      };
    }
    
    function* getTodaysZookeeper() {
      yield {status: 'loading'};
      return {status: 'loaded', name: 'Edward'};
    }
    
    function* Zoo() {
      var animal = yield catchEscapedAnimal();
      var zookeeper = yield getTodaysZookeeper();
    
      console.log('%s catches by %s', animal.name, zookeeper.name);
    }
    

    catchEscapedAnimal()은 ajax를 사용하는 경우를 가정해서 setTimeout을 이용해 콜백을 호출하는 형태로 작성되었다. getTodaysZookeeper()는 일반적인 제너레이터 함수로 첫 호출에는 loading을, 두번째 호출에서 최종 값을 전송한다. Zoo도 앞에서 본 Cage처럼, 중간에 yield를 사용한다. 이 함수를 처리하기 위한 compose 함수는 다음과 같다.

    function compose(iter, value, next) {
      var result = iter.next(value);
      if(result.done) return next ? next(value) : value;
      else if(typeof result.value == 'function') {
        return result.value(function(err, data) {
          if(err) throw err;
          compose(iter, data);
        });
      } else if(typeof result.value.next == 'function') {
        var _iter = iter;
        next = function(result){
          compose(_iter, result);
        };
        iter = result.value;
        result = iter.next();
      }
      return compose(iter, result.value, next);
    }
    

    compose 함수는 다음과 같은 경우의 수를 다룬다.

    • yield 된 값이 함수일 때, 호출 체인을 연결할 수 있도록 next 함수를 넘겨줌 (기존 callback 방식)
    • yield 된 값이 이터레이터일 때, 이터레이터가 done을 반환할 때까지 호출한 후 최종 값을 반환
    • 그 외의 결과를 반환할 때, 해당 값을 이터레이터에 넣고 다시 compose를 호출
    • 이터레이터가 종료(done == true)되었을 때, next 함수가 있다면 해당 함수로 호출을 진행하고 없으면 최종 값을 반환하고 종료

    이 함수를 이용한 결과는 다음과 같다. setTimeout()에 의해 중간 지연이 진행되는 부분도 확인할 수 있다.

    compose(Zoo());
    // Kuma catches by Edward
    

    제너레이터를 코루틴으로, co

    나름 잘 동작하지만 흐름을 보기 위해서 만든 함수라서 허술한 부분이 많다. 이런 부분에서 사용할 수 있는 것이 바로 co다. co는 제너레이터를 코루틴처럼 사용할 수 있도록 돕는 라이브러리로 앞서 작성했던 compose 함수와 같은 역할을 한다.

    var co = require('co');
    co(Zoo());
    // Kuma catches by Edward
    

    이 라이브러리는 내부적으로 Promise 패턴을 사용하고 있어서 callback이든 Promise든 제너레이터든 모두 잘 처리한다. 실제로 제너레이터를 사용하고 싶다면 이 라이브러리를 사용하는 것이 큰 도움이 된다.

    Koa

    Koa는 앞서 이야기한 co 라이브러리를 기본적으로 적용하고 있는 HTTP 미들웨어 라이브러리로 경량에 간단한 기능을 제공하는 것을 특징으로 한다. 제너레이터를 기본적으로 사용할 수 있어서 앞서 배운 내용을 손쉽게 적용할 수 있다.

    코드를 작성하기에 앞서 간단하게 koa를 설치한다.

    $ npm install --save koa
    

    Hello World를 작성하면 다음과 같다.

    var koa = require('koa');
    var app = koa();
    
    app.use(function* () {
      this.body = {"message": "Hello World"};
    });
    
    app.listen(3000);
    

    이제 http://localhost:3000에 접속하면 해당 json이 출력되는 것을 확인할 수 있다.

    앞서 작성한 코드도 포함해보자.

    var koa = require('koa');
    var app = koa();
    
    function catchEscapedAnimal() {
      return function(done) {
        setTimeout(function() {
          done(null, {name: 'Kuma', type: 'Bear'});
        }, 50);
      };
    }
    
    function* getTodaysZookeeper() {
      yield {status: 'loading'};
      return {status: 'loaded', name: 'Edward'};
    }
    
    function* Zoo() {
      var animal = yield catchEscapedAnimal();
      var zookeeper = yield getTodaysZookeeper();
    
      this.body = { message: animal.name + ' catches by ' + zookeeper.name };
    }
    
    app.use(Zoo);
    app.listen(3000);
    

    Koa의 모든 추가 기능은 미들웨어 구조로 제너레이터를 통해 작성하게 된다. callback은 물론 Promise 패턴도 더 깔끔하게 사용할 수 있다.

    요청과 응답은 모두 this에 주입되서 전달되고 흐름은 첫 인자에 next를 추가해 제어할 수 있다. 요청에 대한 응답 내용이 있으면 ok를 추가해보자.

    app.use(function* (next) {
      yield next;
      if(this.body) {
        this.body.ok = true;
      } else {
        this.body = { ok : false };
      }
    });
    

    다음과 같은 방식으로 토큰 검증도 가능하다.

    app.use(function* (next) {
      var requestToken = this.request.get("Authorization");
      var accessToken = yield AccessTokensModel.findAccessTokenAsync(token);
      if(accessToken) {
        yield next;
      } else {
        this.body = { error: 'invalid_token' };
      }
    });
    

    세부적인 내용은 koa 웹페이지에서 다루고 있다. 단순하고 간편한 기능을 원한다면 꼭 살펴보자. 실제 사용하게 될 때는 koa-bodyparser, koa-router와 같은 패키지를 같이 사용하게 된다. 패키지 목록은 koa 위키에서 확인할 수 있다.

    제너레이터도 충분히 편한 기능이지만 koa는 현재 await/async 문법을 지원하기 위한 다음 버전 개발이 진행되고 있다. 더 가독성도 높고 다른 언어에서 이미 구현되어 널리 사용되고 있는 문법이라 더 기대된다.


    더 읽을 거리

    색상을 바꿔요

    눈에 편한 색상을 골라보세요 :)

    Darkreader 플러그인으로 선택한 색상이 제대로 표시되지 않을 수 있습니다.