node.js 2) DB 연결

node.js를 서버측 프레임워크로 하는 관리자 페이지 작성을 진행하고 있다.

안드로이드 앱 ‘쌤통’의 관리자 페이지로서 DB는 MySQL을 사용하고 있다.
이번 포스팅에서는 지난 포스팅에 이어 node.js에서 MySQL DB를 사용하는 방법에 대해 알아보자.

node 진영의 가장 큰 장점 중에 하나는 바로 풍부한 모듈을 보유하고 있다는 것이다.
node.js에서 MySQL을 사용하기 위해 개발된 모듈이 바로 “db-mysql”과 “mysql”이다.(이것들 이외에도 node.js에서 MySQL을 사용하기 위해 작성된 모듈들은 많이 있다.) 자바로 따지면 JDBC와 비슷한 역할을 수행한다. 개발자가 이 두 모듈을 사용하면 복잡한 코드를 작성할 필요 없이 모듈에서 정의한 함수만 사용하여 DB에 접속, 조회, 수정, 삭제가 가능하다.

db-mysql과 mysql 모듈에 대한 정확한 정보는
db-mysql, MySQL에서 얻길 바란다.

가장 먼저, 테스트 용 MySQL 테이블과 데이터를 만들어놓고, db-mysql, mysql 모듈 순으로 설명을 하도록 한다.
본 포스팅 개발 환경은 CentOS release 5.8 (Final), MySQL 5.0.95, node.js v0.10.23, npm 1.3.17 버전이다.

일단 mysql 명령어를 커맨드 창에서 실행한다. 그 후에 테스트 용 테이블을 생성하고 데이터 몇 개를 insert 한다.

[root@getbanana ADMIN]# mysql
mysql> use test;
Database changed
mysql> create table teacher(
    -> no int(10) not null auto_increment primary key,
    -> name varchar(10) not null,
    -> grade int(10) not null
    ->);
mysql> insert into teacher(name, grade) values('jyp', 4);
Query OK, 1 row affected (0.15 sec)
mysql> insert into teacher(name, grade) values('gjh', 4);
Query OK, 1 row affected (0.15 sec)

1: mysql을 실행시킨다.
2: mysql을 실행하면 기본으로 생성되어 있는 ‘test’라는 database를 사용한다.
4~8: ‘teacher’ 테이블을 생성한다. 컬럼으로는 no, name, grade를 두었다.
9, 11: 데이터 두 개를 insert 한다.

mysql> select * from teacher;
| no | name | grade |
+----+------+-------+
|  1 | jyp  |     4 |
|  2 | gjh  |     4 |
+----+------+-------+
2 rows in set (0.00 sec)

데이터가 잘 insert 되었는지 확인해본다.

MySQL DB가 준비됐으니 다음으로는 db-mysql 모듈에 대해 알아보자.
db-mysql 모듈은 npm으로 설치가 가능하다.

[root@getbanana ADMIN]# npm install -g db-mysql

db-mysql 설치가 제대로 되었는지 확인하기 위해 node 콘솔 창을 열어 확인해보자.
다음 처럼 require(“db-mysql”)을 실행했을 때, 객체가 출력되면 설치가 제대로 된 것이다.

[root@getbanana ADMIN]# node

<blockquote>
  require(&quot;db-mysql&quot;)
  { Query: [Function],
  Database: 
    { [Function]
       COLUMN_TYPE_STRING: 0,
       COLUMN_TYPE_BOOL: 7,
       COLUMN_TYPE_INT: 2,
       COLUMN_TYPE_NUMBER: 3,
       COLUMN_TYPE_DATE: 4,
       COLUMN_TYPE_TIME: 5,
       COLUMN_TYPE_DATETIME: 6,
       COLUMN_TYPE_TEXT: 1,
       COLUMN_TYPE_SET: 8 } }
  

설치가 되었으니 이제는 node.js에서 MySQL에 접속하는 일만 남았다.
이제 db-mysql 모듈을 이용해 방금 전 insert 한 데이터를 조회하는 작업을 수행해보자.
app.js 스크립트를 작성한다.

var mysql = require('db-mysql');
new mysql.Database({
    hostname: 'localhost',
//    user: 'user',
//    password: 'password',
    database: 'test'
}).connect(function(error) {
    if (error) {
        return console.log('CONNECTION error: ' + error);
    }
    this.query().
        select('*').
        from('teacher').
        execute(function(error, rows, cols) {
            if (error) {
                    console.log('ERROR: ' + error);
                    return;
            }
            console.log(rows.length + ' ROWS found');
            for(idx in rows){
                console.log("name: " + rows[idx].name + ", grade: " + rows[idx].grade);
            }
        });
});

1: require 함수를 사용하여 db-mysql 모듈을 불러온다.
2~6: 연결할 대상 MySQL DB에 대한 설정 부분이다. hostname에는 db가 깔린 주소, user와 password는 각각 db의 아이디와 비밀번호를 입력한다. database는 사용할 db 이름을 입력한다. 본 포스팅에서는 MySQL DB에 아이디와 비밀번호를 설정하지 않았기 때문에 user와 password 부분은 주석처리했다.
7: connect 함수를 호출하면서 인자로 함수를 넘겨준다. 2~6에서 설정한 것들로 db에 접속을 시도하고 접속되면 인자로 넘겨주는 함수를 실행한다는 의미이다.
8~10: 접속이 실패할 수도 있기 때문에 에러처리를 하는 부분이다.
11~13: db-mysql은 sql을 직접 작성하는 것을 대신하게끔 하는 함수를 제공한다. 함수 이름이 매우 직관적이기 때문에 별다른 설명이 필요없어 보일 정도로 사용하기 쉽다. this.query() 함수의 리턴값을 이용하여 select() 함수, from() 함수를 사용한다. 이 외에도 order, where 함수 등을 제공한다.
함수를 사용하지 않고 raw한 sql문을 사용하고 싶을 경우에는 this.query() 함수 내부에 작성하면 된다.

this.query("select * from teacher")...

14~22: select 질의문의 결과값을 받아와서 처리하는 부분이다.
execute 함수가 질의문을 실행하는 함수이고 인자로 또 다른 함수를 넘겨준다. 넘겨주는 함수에는 다시 인자가 세 개 있는데 err에는 질의문 실행 결과 에러가 났을 경우 해당 정보가 담겨 있고, rows에는 결과값이 배열 형태로 담겨있으며, cols에는 컬럼에 대한 정보가 담겨 있다.
19~21: rows에서 값에 접근하는 방식을 살펴보자. rows는 배열 형태이기 때문에 rows[index] 형식으로 각 row에 접근하고 row에서는 ‘.’ 연산자로 값에 접근한다.

위 app.js를 실행하면 다음과 같다.

[root@getbanana ADMIN]# node app.js
2 ROWS found
name: jyp, grade: 4
name: gjh, grade: 4

db-mysql의 사용법에 대해 알아봤다. 본 포스팅에서는 select까지만 살펴보고 나머지는 여기를 참조하기 바란다. select를 제대로 이해했다면 나머지 insert, update, delete는 쉽게 적용이 가능할 것이다.

다음으로는 mysql 모듈에 대해서 알아본다.
db-mysql 모듈과 사용법이 상당히 유사하다. 일단 npm으로 mysql 모듈을 설치한다.

[root@getbanana ADMIN]# npm install -g mysql

그리고는 mysql 모듈로 MySQL DB에 접속하는 스크립트를 작성한다.

var mysql = require('mysql');
var connection = mysql.createConnection({
    host: 'localhost',
//    user: 'me',
//    password: 'secret',
    database: 'test'
});

connection.connect();

connection.query('select * from teacher', function(err, rows, cols){
    if(err) throw err;

<pre><code>console.log(rows);
</code></pre>

});

connection.end();

1: mysql 모듈을 불러온다.
2~7: createConnection 함수에 db 접속 정보를 설정 한 값을 넘겨주면서 호출한다.
9: db에 연결한다.
11~17: query 함수의 첫 번째 인자에 sql 문을 작성하고 두 번째 인자는 질의문 수행 결과를 처리할 함수를 넘겨준다. 함수는 db-mysql의 질의문 처리 함수 인자와 동일하다.
19: 접속을 해제한다.

mysql 모듈은 db-mysql 모듈에 비해 좀 더 풍부한 기능을 제공한다. JDBC 사용자라면 preparedStatement 인터페이스로 sql문을 작성해 본 경험이 있을 것이다. sql 문 중간에 변수 값을 할당해야 할 때, ‘+’ 연산자로 이어붙이는 게 지저분하기 때문에 다음과 같은 방식으로 가독성 있게 코드를 작성하게끔 할 수 있었다.

PreparedStatment pstmt = connection.prepareStatement("insert into teacher(name, grade) values(?, ?);
pstmt.setString(1, "csj");
pstmt.setInt(2, 4);

mysql 모듈은 이와 비슷한 방식으로 sql문을 작성하게 도와주는 기능을 제공하고 있다.

var query = connection.query("insert into teacher(name, grade) values(?, ?)", ['csj', 4], function(){...});
console.log(query.sql);

1: query 함수의 첫 번째 인자로 sql문을 작성하는데 변수 값을 할당할 부분에 ‘?’를 채워넣는다. 두 번째 인자로는 ‘?’에 넣을 값들을 배열로 주고, 세 번째 인자는 질의문 처리 함수를 넘겨준다.
2: “insert into teacher(name, grade) values(‘csj’, 4)” 가 출력된다.

이밖에도 mysql 모듈은 connection pooling, Multiple statement queries 기능 들을 제공하기 때문에 좀 더 심화된 db 접속 기능이 필요하다면 db-mysql 모듈보다는 mysql 모듈을 추천한다.

또한 node.js 0.10 버전에서부터는 db-mysql 모듈이 version mismatch 되는 에러가 나고 있기 때문에 최신의 node.js를 사용하는 개발자라면 mysql 모듈이 최선의 MySQL 접속 모듈이 아닐까 한다.

다음 포스팅에서는 DB에서 가져온 데이터를 화면에 뿌려주는 기능을 express 모듈과 함께 설명하도록 하겠다.

융합은 도대체 뭘까요? – Mash Up

2012년 1학기에 학부장님이 수업하시는 ‘차세대 웹’ 수업에서 처음으로 mash up에 대해 알게 되었다.
위키백과에서는 매쉬업을 이렇게 정의한다.

매시업(Mashup)은 웹으로 제공하고 있는 정보와 서비스를 융합하여 새로운 소프트웨어나 서비스, 데이터베이스 등을 만드는 것을 말한다.

위 정의는 IT 분야에서 통용되는 mash up을 정의한 것이고, 사실 mash up이란 단어는 음악 쪽에서 먼저 쓰였고 지금도 더 활발하게 쓰이는 듯하다.

A mashup (also mesh, mash up, mash-up, blend, bootleg[1] and bastard pop/rock) is a song or composition created by blending two or more pre-recorded songs, usually by overlaying the vocal track of one song seamlessly over the instrumental track of another.

쉽게 말하면 서로 다른 두 음악을 섞어서 하나의 음악으로 재창조 한다는 의미이다.
유튜브에서 자신의 mash up 창작물을 업로드 하여 독자수가 꽤 많은 사람이 있다. SAMADALEINAD라는 아이디를 쓰는 뮤지션이다.
그의 방송국을 가보면 직접 mash up한 수백 곡의 노래들이 업로드되어 있다. 각 곡들의 장점, 특징만을 콕 찝어내서 하나로 섞는 능력이 가히 발군이라 할 수 있다. 노래를 섞어놓으면 자칫 뽕짝이 되어버리기 쉽다. 특히나 유명한 노래의 경우, 섞는 노래 각각을 이미 알고 있는 경우에는 흔히 말하길 노래가 “따로 들리는” 경우가 발생하는 것이다. 이 노래와 저 노래가 섞여서 조화를 내는 것이 아니라 불협화음을 내는 mash up 곡들도 다수 존재한다.

‘차세대 웹’ 수업 때 잠깐 들었던 mash up 곡들 중 아직까지도 듣고 있는 것들이 꽤 있는데 그 중에서 가장 mash up 다운 곡을 소개한다.

비틀즈와 오아시스를 각각 대표하는 곡인 ‘Let It Be’와 ‘Don’t Look Back In Anger’를 mash up 한 것으로 곡명은 ‘Don’t Let It Be Anger’이다.
두 곡은 코드가 비슷하기 때문에 섞어도 크게 튀는 부분이 없을 뿐더러 보컬의 음색마저도 조화롭게 들린다.

요즘 현대차에서 ‘융합은 도대체 뭘까요?’ 라는 질문과 함께 일반인들이 대답하는 광고를 내보내고 있는데, 나는 그 질문에 mash up이라 대답하고 싶다. 두 곡 또는 그 이상의 곡을 하나의 곡으로 합쳐서 지금까지는 없었던 전혀 새로운, 창조적인 곡을 만들어내는 음악 분야의 mash up.

음악 분야의 ‘Don’t Let It Be Anger’처럼 나도 개발자로서 웹 서비스 분야의 진정한 융합이라 할 수 있는 mash up을 해보고 싶다. 그러기 위해서는 개발 뿐만 아니라 더 많은 것들을 경험하고 배워야 할 것이다.

node.js – 1) 설치 및 Hello World

안드로이드 앱 ‘쌤통‘의 관리자 페이지 작성을 해야할 필요성이 생겼다.
쌤통의 서버 코드는 자바로 작성되었는데, 관리자 페이지 코드는 서버 코드와는 별개의 다른 언어로 작성하고 싶었다. DB 데이터만 가져오면 되기 때문에 서버 코드에는 영향을 주지 않으면서도 쉽고 간편하게 작성할 방법이 없나 찾던 중 Node.js가 생각 났다.

nodejs
Ryan dahl이 개발한 서버 프레임워크인 Node.js를 사실 1년 전에 살짝 건드려보긴 한 적이 있었다. 그때는 정말 간단하게 Hello World! 예제만 돌려보고 ‘와~ 신기하다’ 하고 끝냈었다.

이번에 작성해야 하는 관리자 페이지는 디자인 보다는 현재 ‘쌤통’ 앱의 가입자 수, 가입자 추세 같은 데이터 현황만 확인하면 되기 때문에 Node.js 라는 거의 처음 써보는 프레임워크로도 작성할 수 있겠다 하는 생각이 들었다.

  1. 먼저 리눅스에 Node.js를 설치한다.(설치 환경: CentOS release 5.8 (Final))
    Node.js 공식 페이지에 접속하여 설치 파일을 다운로드 한다.
    http://nodejs.org/download/ 에 접속하면 다음과 같은 화면이 나오는데 여기서 본인의 서버 환경에 맞는 링크를 선택하면 된다.
    Node.js 다운로드
    설치 환경이 리눅스 이기 때문에 리눅스 설치 파일을 다운로드 한 뒤 압축을 풀어준다.

    [root@getbanana test]# tar xvzf node-v0.10.22-linux-x64.tar.gz
    

    압축을 푼 디렉토리로 이동하면 다음과 같은 파일과 디렉토리 구조를 볼 수 있을 것이다.
    Node.js 디렉토리 구조
    여기서 bin 디렉토리로 이동하면 node와 npm 파일을 볼 수 있다. node 파일이 node.js를 실행하는 파일이다. 다음과 같이 명령어를 입력하여 node.js의 버전을 확인해보자.

    [root@getbanana test]# ./node -v
    [root@getbanana test]# v0.10.22
    

    이 글을 작성하는 시점의 node.js 버전은 0.10.22이다. 아직까지 1점대 버전이 아니라는 점이 눈에 띈다.
    node.js 설치를 위해 아직 한 단계 남았는데 node와 npm 파일의 심볼릭 링크를 걸어주는 것이다. 리눅스 환경에서 프로그램을 실행하려면 절대경로로 실행해야 하는데, node가 설치된 디렉토리와 작업공간이 상이할 경우, 이는 매우 불편하다. 그런데 node 파일을 특정 디렉토리에 심볼릭 링크로 걸어놓으면 절대경로 없이 ‘node’라는 키워드로만 실행시킬 수 있다. 여기서 특정 디렉토리는 ‘/usr/local/bin’이다. CentoOS 기준이기 때문에 다른 리눅스 버전에서는 다를 수 있으니 관련해서는 검색해보길 바란다.
    파일을 심볼릭 링크를 걸기 위해서는 다음과 같이 하면 된다.

    [root@getbanana test]# ln -s * /usr/local/bin/.
    

    현재 디렉토리에서 다른 디렉토리로 이동 후 ‘node -v’ 명령어를 실행해보길 바란다. 그랬을 때 위의 bin/ 디렉토리에서의 실행결과와 동일하다면 심볼릭 링크가 제대로 걸린 것이다.
    이것으로 node.js의 설치가 마무리 되었다.

  2. HelloWorld 예제 작성하기
    어떤 언어, 어떤 프레임워크를 사용하던지 가장 처음에는 Hello World! 예제를 작성해야 제맛이다.
    시작이 반이라는 말도 있지 않은가?(Hello world 예제를 작성할 정도로 열의가 있다면 해당 분야의 나머지도 분명 정복할 수 있을 것이다.)(1년 전 Hello world 예제 작성 후 중단한 것은 너무 바빠서 그랬던 것이다.)

Hello World 예제 작성의 가장 바람직한 방법은 해당 언어, 프레임워크의 공식 페이지를 뒤져보는 것이다.
다시 node.js의 공식 페이지에 들어가보자, 친절하게도 메인 페이지 하단에 Hello World 예제가 있다.(링크)
Hello World 예제를 간략히 소개하자면, 웹 서버를 띄우고 사용자가 접속하면 Hello World 라는 문자열을 출력해주는 것이다.
공식 페이지의 예제를 가져온 것이 하단의 코드이다.

// 코드 1
var http = require('http');
var callbackFunction = function(req, res){
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
};

http.createServer(callbackFunction).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

2: http 라는 모듈을 불러온다.
node.js는 자바스크립트 모듈화 진영에서 de facto처럼 여겨지는 CommonJS를 사용한다. 위에서 require라는 함수를 사용한 것을 볼 수 있는데 여기서 바로 CommonJS를 사용하여 ‘http’ 모듈을 불러온다. 자바스크립트 모듈화와 관련해서는 이 글을 읽어보시길 바란다.

3: callbackFunction 함수를 선언한다. 이 함수는 웹 서버의 event listener의 역할을 수행한다.
코드의 4번째 줄에서는 Content-Type을 기술한다. 5번째 줄에서는 Hello World 라는 문자열을 내보냄과 동시에 출력을 종료한다.(end 함수) 출력을 계속 이어가고 싶으면 end 함수 이전에 res.write() 함수를 사용하면 된다.

8: 가장 중요한 부분. 2번째 줄에서 불러온 http 모듈의 createServer 함수를 호출한다. 함수를 호출하면서 2번째 줄에서 선언했던 callbackFunction 함수를 인자로 주는 것에 주목해야 한다. 함수 호출 후 돌려받는 객체에서 다시 listen 함수를 호출한다. listen 함수의 첫 번째 인자는 포트번호, 두 번째 인자는 호스트 이름이다. 웹 서버에 접속할 때의 주소 경로를 설정하는 것이다.
여기서 중요한 것은 node.js는 event 기반으로 작동한다는 것이다. event 기반으로 작동한다는 의미에 대해서는 아웃사이더 님이 okjsp에서 발표하신 http://www.slideshare.net/rockdoli/nodejs-at-okjsp 를 참고하길 바란다.
createServer 함수 호출 시 넘겨줬던 callbackFunction이 event listener이다. 즉, 사용자가 웹 서버에 접속하면 callbackFunction을 호출한다는 의미이다.
8번째 줄을 한 문장으로 요약하자면, “http://127.0.0.1:1337 로 사용자가 접속할 경우 callbackFunction 함수를 호출한다.” 이다.

example.js 라는 파일에 위 코드를 적고 다음과 같이 nodejs를 실행시켜 보자.

[root@getbanana test]# node example.js
[root@getbanana test]# Server running at http://127.0.0.1:1337/

그런 뒤, 브라우저를 열어서 http://localhost:1337로 접속해보자.
그러면 하얀 화면에 Hello World 라는 문자열만 떡하니 찍힌 것을 볼 수 있다.

사실 위에서 작성했던 example.js 코드(코드 1)는 nodejs.org에 있는 예제 코드를 내가 임의로 수정한 것이다. 설명하는데 편하도록 코드를 수정했는데 원래 코드는 다음과 같다.

// 코드 2
var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

코드 1에서 callbackFunction이 사라지고 createServer 함수의 인자에 함수 자체를 기술한다. 이 방식이 event 기반의 node.js에서 가장 대표적으로 사용되는 함수 기술 방식이다. http 모듈의 createServer 함수를 호출할 때 함수 자체를 넘겨준다는 것은 서버에 인자로 넘겨주는 함수를 listener로 등록한다는 의미이다.

본 포스팅은 여기서 마무리한다. 관리자 페이지를 직접적으로 작성하는 부분은 2편에서 다루도록 하겠다.

나만의 타임라인 만들기

지금까지 내가 살아온 삶의 궤적을 웹 페이지에서 시각적으로 보여주고 싶은 욕구가 생겼다.
그래서 깃헙을 이리저리 둘러보다 우연히 흥미가 가는 저장소(life)를 발견했다.
현재 Viki에서 근무하는 cheeaun이라는 분이 개발한 타임라인 라이브러리이다.
데모 페이지에 들어가보면 이 분이 언제 태어났는지, 유년기부터 지금까지 살면서 어떤 일들이 있었는지를 타임라인 형식으로 보여준다.

‘타임라인’을 잘 모르는 분들을 위해 소개하자면,
이벤트 리스트들을 시간 순서대로 보여주는 방식의 하나이다. 주로 가로로 긴 막대바를 배치하고 각 이벤트들에 관한 설명문 및 이미지를 바에서 왼쪽부터 시간 순서대로 나열하는 디자인형식이다.

위 이미지는 ‘아이폰 타임라인’이다.

시각적으로 이러한 타임라인을 만들기 위해서는 여러 툴들을 사용해서 이미지를 편집하는 방식을 써야 하는데, 웹 페이지에서 타임라인을 보여주면서도 뭔가 보는 사람이 즐길만한 꺼리를 같이 제공해주고 싶은 나로서는 이미지를 사용하고 싶지는 않았다. 그래서 웹 페이지에서 자바스크립트와 CSS, HTML을 사용하여 타임라인을 구현하기 쉽도록 구축된 라이브러리를 찾다가 위에서 설명한 life라는 저장소를 발견한 것이다.

본 포스팅에서는
1. life라는 라이브러리를 사용하여 나만의 타임라인을 만드는 법과
2. 이를 github의 서비스 중 하나인 github pages로 publish하는 법을 소개한다.

먼저 life 라이브러리를 사용하기 위해서는 두 가지 방법이 존재하는데 life 저장소로 이동한 후 파일을 내려받아 개발하는 것과 github 계정으로 로그인 하여 fork 받은 후 개발하는 것이다.
개인적으로 github 계정으로 로그인 하여 fork 하는 방식을 추천하는데, 이럴 경우 좀 더 체계적으로 코드를 관리할 수 있을뿐만 아니라 publish를 손쉽게 할 수 있는 이점이 있다.
파일을 내려받아 개발하는 것은 어차피 github에서 fork 한 후에 개발하는 것과 동일하기 때문에 github fork 기준으로 설명을 진행한다.

1) 먼저 github 계정으로 로그인한다(github 또는 git 이라는 소스 코드 관리 시스템을 처음 접하는 분들은 해당 사용법을 익힌 후 포스팅을 읽을 것을 권한다.)
2) life 저장소로 이동한 후 우측 상단의 fork 버튼을 누른다. 저장소를 fork 할 경우, fork 시점에서 저장소를 그대로 내 계정으로 가져온다. 나만의 타임라인을 만들어야 하기 때문에 저장소를 fork 한다고 생각하면 쉽다.
3) 본인 계정에 life 저장소가 생성된 것을 확인한 후 로컬 컴퓨터에서 방금 fork 한 저장소를 clone 한다. git에서 clone 하는 방법은 다음과 같다.

git clone https://github.com/{본인아이디}/life.git

4) clone 한 후에 branch를 master에서 gh-pages로 변경해줘야 한다. github에서 제공해주는 github pages 라는 서비스를 사용하기 위하기 위해서는 branch를 gh-pages로 한 후에 commit 해야 하기 때문이다.

git checkout -b gh-pages

5) 브랜치를 변경한 후에는 life.md 라는 파일을 생성해야 한다. 타임라인을 서술하는 파일인데 index.html에서 life.md 라는 파일을 읽어다가 타임라인을 그리기 때문에 life.md는 꼭 생성해야 하는 파일이다. 라이브러리 개발자가 미리 예제 형식으로 만들어놓은 life.example.md를 복사한다.
6) 나만의 타임라인을 작성하기 전에 먼저 commit을 한다. 생성한 life.md를 add 하고 커밋 후 push 하면 된다.

git add life.md
git commit -m "add life.md"
git push origin gh-pages -f

7) gh-pages 브랜치로 commit을 하게 되면 대략 10분 정도 지나서 본인만의 url로 github pages를 확인할 수 있다.
{본인아이디}.github.io/life 로 접속하면 된다.
8) 이제 나만의 타임라인을 본격적으로 작성하는 시간이다. life.md 파일을 열어서 편집을 하면 되는데, 약간의 규칙이 있다.
일단 날짜 설정을 해줘야 하는데 총 세 가지 방법이 있다.
①날짜를 하나만 적는데 일 단위, 월 단위, 연 단위로 적을 수 있다. 만약 2013/12로 설정할 경우 2013/12/01 에서부터 2013/12/31까지 설정한 것과 동일하게 적용된다. 설정 방법은 기간을 포함하는 날짜
②시작 날짜와 종료 날짜를 설정한다. 설정 방법은 시작날짜 – 종료날짜 ex) 2013/12/23-2013/12/25
③시작 날짜에서부터 현재 날짜까지 설정한다. 설정 방법은 시작날짜 -~ 2013/01/01-~
날짜 설정하고 나면 날짜 설정 바로 오른쪽에 공백을 사이로 놓고 해당 타임라인에 대한 설명문을 적는다.

이상이 나만의 타임라인을 만드는 방법이다. life.md를 수정했기 때문에 다시 해당 파일을 add 한 후 commit, push 하면 된다.

2) 리눅스 명령어 grep, 원하는 라인만 출력하기

리눅스에서 grep은 대단히 유용하면서 자주 쓰이는 명령어 중에 하나이다.
본 포스팅에서는 수십~수백만 라인 중에서 사용자가 원하는 라인만 골라서 출력해주는 grep 명령어의 사용법에 대해서 알아본다.

수십~수백 줄의 문자열로 가득한 파일에서 내가 원하는 라인만 출력하는 것은 인력으로 어느 정도 가능한 일이지만 천 줄이 넘어가면서부터는 사람의 힘으로는 도저히 불가능한 작업이 된다. 여기서 ‘내가 원하는 라인‘이란 쉽게는 ‘내가 찾는 문자 또는 문자열이 들어있는 라인‘, 좀 더 복잡하게는 ‘내가 찾는 패턴이 들어있는 라인‘으로 정의할 수 있다.

내가 찾는 문자열이 들어간 라인 출력

그럼, grep 명령어를 가장 쉽게 사용하는 방법인 문자열 검색부터 소개해본다.

grep 명령어를 배우기 전 효과적인 grep을 쓰기 위한 기초 명령어를 알아본다.
리눅스 명령어 중 ‘ps’는 현재 실행되고 있는 프로세스의 상태를 출력한다. 터미널에 ps 라고만 입력 후 엔터를 치면 보통 대부분 다음 화면같이 나올 것이다.

[root@kaka]ps 
PID    TTY     TIME     CMD
13480  pts/0   00:00:00 bash 
13851  pts/0   00:00:00 ps

현재 터미널 쉘이 bash가 아니라면 다르게 나올 수도 있지만 큰 차이는 없을 것이다.
ps 명령어에는 보통 aux 옵션을 많이 사용한다. 각 옵션을 소개하자면,
a: 모든 사용자 프로세스 출력 옵션
u: 자세한 정보 출력
x: 제어터미널이 없는 프로세스도 출력
(정보 출처: http://finetia.egloos.com/1514615)

터미널에 ps aux라고 친 후 엔터를 누르면 수십, 수백 개의 프로세스 설명 리스트가 출력될 것이다.
문제는 여기서 발생한다.
수백 개의 리스트 중에서 내가 원하는 프로세스 설명만 출력할 순 없을까?
특정 프로세스 정보를 알아야 하는 경우, 이를테면 PID를 알아내서 강제종료해야 할 경우가 생길 수 있다. 이때 수백 줄이 넘는 프로세스 리스트를 일일이 눈으로 찾는 것은 너무 고달픈 일일 것이다.
여기서 바로 grep 명령어를 쓰면 모든 것이 해결된다.

만약 프로세스 정보 리스트(ps aux) 중에서 톰캣 프로세스를 찾고 싶을 경우는 다음과 같이 명령어를 사용하면 된다.

[root@kaka] ps aux | grep "tomcat"
root  14099  0.0  0.0  65344  840  pts/0  S+  18:48  0:00  grep tomcat
root  27472  0.6 54.6  176558 460448  ?   Sl  Nov29  19:36  /usr/local/java...(생략)

1: ps aux 명령 후 출력 리스트를 파이프 라인을 통해서 grep 명령어에 전달한다. grep 명령어에서는 input으로 들어오는 모든 라인 중에서 쌍따옴표 안에 있는 “tomcat” 이라는 문자열이 들어있는 라인을 출력한다.
2: ps aux 명령어는 실행 중인 모든 프로세스를 출력하기 때문에 1줄의 grep “tomcat” 조차도 출력한다. 따라서 1줄의 grep “tomcat” 명령어도 grep에 의해서 검색된다.
3: 우리가 찾고자 했던 프로세스 정보이다. PID, 실행시간, 실행 위치 등의 정보를 얻을 수 있다.

ps aux | grep “프로세스 관련 문자열” 조합은 실제 작업 중에서 빈번하게 사용된다. 특히 특정 프로세스를 찾아서 강제종료하고 싶은 경우가 많이 발생하는데 이때 유용하게 사용이 가능하다.

내가 찾는 패턴이 들어있는 라인 출력

단순 문자열 검색에 비해 좀 더 강력한 방법이다.

내가 찾는 패턴‘의 예를 들어보면 다음과 같다.
Christmas
X-Mas
Mascherano
위 세 라인이 test.txt라는 파일에 저장되어 있다고 했을 때,
test.txt 파일에서 크리스마스와 관련된 라인만 출력하고 싶은 경우가 생겼다. ‘Christmas’, ‘X-Mas’가 그 대상이다.
두 문자열 모두 ‘mas’가 들어가지만 1번째 줄의 mas는 전부 소문자이지만, 2번째 줄의 Mas는 ‘M’이 대문자이다.
따라서 grep "mas" test.txt로 치면 1번째 줄만 검색되고, grep "Mas" test.txt로 치면 2,3번째 줄이 출력된다.
명확히 출력하고자 하는 라인은 1,2번째 줄이기 때문에 단순 문자열 검색만으로는 가능하지 않다.

지금 첫 번째 문제는 1번째 줄과 2번째 줄의 ‘mas-Mas’의 대소문자 구분이고 두 번째 문제는 2번째 줄과 3번째 줄이 동일하게 ‘Mas’를 포함하고 있다는 것이다.

첫 번째 문제는 grep의 ‘-i’ 옵션을 사용하면 된다. -i 옵션은 대소문자를 구분하지 않고 검색한다는 의미이다.

[root@kaka] grep -i "mas" test.txt
Christmas
X-Mas
Mascherano

위와 같이 -i 옵션을 사용할 경우 ‘mas’와 ‘Mas’를 구분하지 않고 동일하게 취급한다. 뿐만 아니라 ‘MAS’, ‘mAs’, ‘MAs’와 같은 형태도 모두 검색된다.

첫 번째 문제를 해결했으니 두 번째 문제로 넘어가보자.
1,2번째 줄과 3번째 줄의 차이점은 ‘Mas’가 가장 뒤에 오느냐, 아니면 앞에 오느냐이다. 1,2번째 줄은 문자열의 맨 마지막에 오지만 3번째 줄은 가장 앞에 ‘Mas’가 위치한다.
이를 패턴화시켜 grep 에 사용해 볼 수 있다.

여기서 정규표현식이란 개념이 등장한다. 정규표현식에서 가장 중요한 것이 바로 ‘패턴’, ‘규칙’을 찾는 것이다. 정확한 규칙을 찾아 정규표현식의 문법에 맞게 작성만 하면 그 어떤 문자열도 찾아낼 수 있다.

정규표현식에서 모든 문자를 표현하는 게 바로 ‘.'(온점)이다. 여기서 주의할 점은 문자열이 아닌 문자라는 것이다. 이를 위 세 개 example에 적용해보자

1번째 줄의 Christmas에서 ‘tmas’는 ‘.mas’로 패턴화시킬 수 있다.
2번째 줄의 X-Mas에서 ‘-Mas’역시 ‘.mas’로 패턴화시킬 수 있다.
하지만 3번째 줄의 Mascherano에서는 ‘.mas’로 패턴화시킬 수 없다. ‘Mas’가 가장 앞에 오기 때문이다.

이를 이용하여 grep 명령어에 사용해보면 다음과 같다.

[root@kaka] grep -i -e ".mas" test.txt
Christmas
X-Mas

1: grep 옵션 중 -e 옵션은 정규표현식을 사용한다는 의미이다. -i(대소문자 구분x), -e(정규표현식 사용) 옵션이 사용된 것이다.
2~3: 처음에 의도했던 결과대로 출력이 된 것을 확인할 수 있다.

정규표현식은 grep 명령어 뿐만 아니라 awk, find 명령어 에서도 빈번하게 사용된다. 또한 리눅스 명령어 외에도 다른 다양한 프로그래밍 언어에서 지원하고 있기 때문에 사용법만 익힌다면 획기적인 생산성 향상을 이뤄낼 수 있다. 정규표현식을 쓰지 않고 특정 패턴을 포함하는 문자열을 찾는 것은 가능하긴 하지만 확실히 비효율적인 부분이 많게 된다.

grep 명령어의 두 가지 옵션을 알아봤는데 이외에도 많은 다양한 옵션이 있으니 찾아보면 좋을 것이다.
-i, -e와 많이 쓰이는 것이 -v 옵션인데 이는 쌍따옴표 내부의 문자열 또는 문자열 패턴을 포함하지 않는 라인만 출력한다.
-v 옵션을 사용하여 우리가 원하는 라인(1,2)만 출력하고자 한다면 다음과 같이 할 수 있다.

[root@kaka] grep -v -i -e "mas." test.txt
Christmas
X-Mas

1: grep -i -e “mas.” text.txt의 결과 값이 Mascherano이기 때문에 -v 옵션을 주면 Mascherano를 제외한 다른 라인이 출력된다.
2~3: 처음에 의도했던 결과대로 출력이 된 것을 확인할 수 있다.

퍼펙트 센스

13035539_perfect_sense_poster

이 영화를 제대로 표현할 수 있는 한 문장은 바로 이것이다.
“잃을 수록 완벽해진다.”

영화에서 인류는 조금씩 본인들의 감각을 잃어간다.
흔히 오감이라 말하는 감각 중 미각, 후각, 청각, 시각을 순서대로 잃어간다.

인류는 각각의 감각을 잃기 전 무언가 광기에 휩싸인 듯 특정한 행동을 보인다. 잃어가는 감각과 관련된 행동들인데 영화를 보면 너무나도 섬뜩하게 표현된다.
감각을 잃어가는 인류는 저마다 자신들의 현실을 받아들이고 또 그렇게 적응해나간다. 간신히 새로운 삶에 적응할 즈음에 또 다른 감각의 상실이 찾아온다. 상실 이전의 광기는 상실에 간신히 적응하려 했던 인류의 마지막 몸부림을 처참히 부숴버린다. 스스로 생각해낸, 자신들의 새로운 삶의 방식을 상실 이전의 광기가 모두 원위치 시킨다.

상실되는 감각을 살펴보면 뒤로 갈수록 점점 더 삶에 직접적인 영향을 끼치는 것들이다. 사실 오감 상실의 단계까지는 가지 않는 감각이 있는데 바로 ‘촉각’이다. 사람들은 마지막으로 시각을 잃고 나머지 하나 남은 ‘촉각’이 ‘완벽한 감각'(퍼펙트 센스)이라는 것을 깨닫는다. 남주와 여주는 헤어짐 끝에 간신히 만나고 서로를 인식하자마자 시각을 잃고는 ‘촉각’으로 대화하며 영화는 끝이 난다.

현대 사회에서 촉각은 인간의 오감 중 가장 덜 퍼펙트 하다고 여겨지는 감각이 아닐까 한다. 사실 촉각은 통증과 압력을 느끼는 중요한 역할을 하기 때문에 어찌 보면 가장 중요하다고 할 수 있는데, 영화에서 말하는 퍼펙트 센스로서의 촉각은 ‘다른 사람과의 교감’이 아닐까 한다. 자신과 타인의 의사소통에 있어 나머지 네 개의 감각도 물론 중요하지만 가장 중요한 것은 바로 ‘촉각’이라고…

1) 리눅스 명령어 awk, 원하는 항목만 추출하여 더하기

리눅스 명령어 중 awk를 사용하여 여러 줄의 문자열 중 각 라인에서 원하는 부분만 추출하여 더하는 간단한 스크립트를 작성한다.
예를 들어 다음과 같은 문자열 파일이 있을 때(각 항목은 버티컬 바(‘|’)로 구분되어 있다, 파일 이름은 ‘sales.txt’)
apple|1|3000
banana|2|2400
grape|4|3500

첫 번째 항목은 품목명을 나타내고 두 번째 항목은 판매 개수, 세 번째 품목은 판매 단가를 나타낸다.
여기서 총 판매액을 구하고 싶을 때, 각 라인의 판매 개수 * 판매 단가를 모두 더하면 되는데 이를 awk 라는 명령어를 사용하면 편하게 구현할 수 있다.

먼저 세 가지 항목 중 첫 번째는 제외하고 나머지 두 번째와 세 번째 항목을 추출해야 한다.

awk '{ \
      split($0, split_line, "|");  \
      print split_line[2];  \
      print spilt_line[3]; \
}' sales.txt

awk 명령어를 사용하는 방법은 크게 두 가지가 있는데 첫 번째는 위와 같이 awk 명령어 기술 후 awk 문을 바로 이어서 붙이는 것이고
다른 하나는 awk문을 파일에 저장한 후 ‘awk -f awk파일명’ 과 같은 방식으로 사용하는 것이다.
awk문이 길어질 경우 명령행에 적는 것이 복잡해지기 때문에 awk문 파일로 관리하는 것이 더 합리적이다.

이제 위 코드 설명에 들어가자면,
awk문은 ‘{ }’ 사이에 기술한다. 위 코드에서는 2~4번째 줄이 awk문이 되는 것이다.
2: split 함수를 사용한다. awk는 다양한 내장함수를 사용할 수 있는데, 구분자를 기준으로 문자열을 자르고 싶을 때 사용하는 함수가 바로 split이다.
split 함수의 첫 번째 인자는 자르고자 하는 대상 문자열이며, 위 코드에서는 ‘$0’이다. awk에서 $0은 라인 전체를 의미한다.
split 함수의 두 번째 인자는 대상 문자열을 자르고 난 후 저장할 배열 변수이다. 위 코드에서는 split_line이라는 변수에 저장시킨다.
세 번째 인자는 구분자를 지정한다. sales.txt 파일에서 각 라인의 구분자는 버티컬 바(‘|’)이기 때문에 구분자를 “|”으로 지정해준다.

3: 구분자로 잘라진 문자열들이 저장된 배열 변수 split_line의 두 번째 인덱스를 출력한다.
다른 프로그래밍 언어의 배열과 달리 첫 번째 인덱스가 1부터 시작하기 때문에 split_line 변수에는 1:apple, 2:1, 3:3000 과 같은 식으로 저장된다.
두 번째 인덱스를 출력했기 때문에 sales.txt 파일의 각 라인의 두 번째 항목인 판매 개수가 출력된다.

4: 세 번째 인덱스를 출력한다.
세 번째 인덱스는 sales.txt 파일의 각 라인의 세 번째 항목인 판매 단가가 출력된다.

우리는 판매 개수와 판매 단가를 곱한 후 모두 더한 값을 구하는 것이 최종 목표이기 때문에 위 코드로는 구할 수 없다.
위 코드를 고쳐서 판매 개수와 판매 금액을 곱한 후 출력해보자

awk '{ \
      split($0, split_line, "|");  \
      line_sum = split_line[2] * spilt_line[3];  \
      print line_sum; \
}' sales.txt

2: 판매 개수(split_line[2])와 판매 금액(split_line[3])을 곱한 후 line_sum 변수에 저장한다.
3: line_sum 변수를 출력한다.

위 코드를 실행시키면 다음과 같은 출력값을 확인할 수 있다.

3000
4800
14000

이제 세 개의 값을 더한 최종 판매 금액만 구하면 된다.

awk 'BEGIN{ \
    total_sum=0; \
}{ \
    split($0, split_line, "|");  \
    total_sum += split_line[2] * split_line[3]; \
}END{ \
    print total_sum; \
}' sales.txt

BEGIN과 END라는 키워드가 눈에 띌 것이다. awk문 내부는 해당 파일의 각 라인을 읽으면서 수행된다. sales.txt는 총 라인수가 3이기 때문에 awk ‘{}’ 내부는 세 번 실행되는 것이다. 그런데 이번 예제와 같이 각 라인에 영향을 받지 않고 파일 전체에서 관리하고 싶은 코드가 있을 경우에는 BEGIN과 END를 사용한다. 키워드의 의미 그대로 파일을 처음 읽어들일 때 BEGIN{ } 내부가 실행되고 파일을 전부 읽어들인 후 END{ } 내부가 실행된다.
2: BEGIN 내부에 total_sum 이라는 변수를 선언하며 0을 대입하고 있다.
5: 2번째 줄에서 선언한 total_sum 변수에 판매 개수 * 판매 단가 값을 누적 합산해주고 있다. ( += )
7: 마지막으로 프로그램 종료 전, total_sum을 출력한다.

위 코드를 실행하면 다음과 같은 출력값을 확인할 수 있다.

21800

따라서 총 판매 금액은 21,800이 되는 것이다.
동일한 기능을 수행하는 프로그램을 파이썬이나 자바로도 충분히 작성할 수 있지만, 이런 간단한 스크립트 연산만으로도 원하는 값을 구할 수 있다면 굳이 파이썬, 자바 코드까지 사용할 필요는 없다는 게 내 생각이다.

awk는 사용자의 능력에 따라서 무궁무진하게 이용될 수 있는 명령어이다. 위에서 소개된 것들은 awk의 전체 기능 중에서도 가장 간단한 것들만 사용한 것으로
나머지 awk 기능을 자유자재로 사용할 수 있다면 그것만으로도 awk라는 또다른 강력한 언어 스킬을 보유하게 되는 것이다.

오만과 편견

영화 제목만 들었을때는 무거운 주제의 영화가 아닌가 싶었고
영화를 보는 내내 도대체 영화와 제목이 무슨 상관인가 생각했으며
영화가 끝나고 나서야 영화 제목을 잘 지었다고 감탄하게 된다.

실제로 영화 대사 도중 ‘오만’과 ‘편견’이라는 단어가 자주 등장하여 남주인공의 성격을 묘사한다. 묘사의 주체는 주로 여주인공이다.

영화의 배경은 17~18세기 영국으로 당시의 생활풍습이 잘 나타나있다.
여주인공 역할을 맡은 배우 키이라 나이틀리는 영화 특유의 색감과 더불어 매력적으로 출연하는데 이또한 영화의 볼거리 중 하나이다.

쉘 스크립트에서 sqlite3 작업하기

보통 파이썬 환경에서 sqlite3를 작업하게 되는 경우가 많은데 이는 sqlite3 모듈이 굉장히 잘 되어있기 때문이다.
하지만 파이썬까지 가지 않고 쉘에서 db 작업을 하고 싶을 때가 있는데 이는 다음과 같이 하면 된다.

#insert
sqlite3 dbname.db "insert into tablename (date, col1, col2) \
                   values ('2013-11-11', 'data1', 'data2');"

#select
echo "select * from tablename;" | sqlite3 dbname.db > target.txt

insert 작업을 할 때는 sqlite3 뒤에 데이터베이스 이름을 써주고 그 뒤에 sql문을 적어주면 된다.
select 작업에서는 echo 명령어 뒤에 sql문을 적은 뒤 파이프 라인 후 sqlite3 데이터베이스이름, 그리고 select 결과물을 저장할 파일 이름을 기술하면 된다.

하둡 스트리밍 시 대용량 파일을 -file 파라미터로 사용할 경우

하둡 스트리밍 시 대용량 파일을 -file 파라미터로 사용할 경우에 “No space left on device” 에러가 날 수 있다.
문자 그대로 사용 가능한 용량이 부족하여 발생하는 에러인데 이럴 때는 하둡 스트리밍 파라미터로 해결이 가능하다.

 -D stream.tmpdir=(로컬 temp 디렉토리 경로)

로컬 temp 디렉토리 경로는 로컬 내부에 존재해야 하며 -file 파라미터로 주는 대용량 파일이 .jar 형태로 저장된다.