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 모듈과 함께 설명하도록 하겠다.

6 thoughts on “node.js 2) DB 연결

  1. node.js와 mysql을 가지고 코딩을 하는중에 문제점이 생겨서 구글 검색으로는 답이 안나와 글을 남깁니다.

    mysql 모듈의 경우 .query로 쿼리문을 실행한후 function에서 결과값을 가지고 진행을 하는데 제가 그 값을 가지고 다시 쿼리를 요청할 경우 result의 값에 접근이 안되는 문제가 있습니다. 혹시 어떻게 풀면 될까요? 필요하시면 소스를 보내드릴 수도 있습니다.

      • 소스는 뭐 간단히 다음과 같구요

        attendance.forEach(function(obj){
        aaa = ‘select SID from student where mac = “‘+obj+'”;’;
        console.log(aaa);
        dbconn.query(aaa, function(err, result){
        if(err) throw err;

        aaa = ‘insert into attendancebook values(“‘+result[0].SID+'”,str_to_date(“‘+req.query.year+req.query.month+req.query.day+'”,”%Y%m%d”),”출석”,”‘+req.query.classcode+'”);’;
        dbconn.query(aaa, function(err, result){
        if(err) throw err;
        });
        });
        });

        간단히 설명드리자면 attendance 배열 내의 값과 일치하는 맥주소를 가지고 있는 sid를 반환한뒤 그 값과 날짜를 다른 테이블에 입력할려고 하는 건데 result[0].sid로 접근이 안되더라구요. callback 함수를 써서 문제를 해결해야하는건가요?

    • attendance 배열을 forEach로 도는 구문에서 내부 변수 aaa가 쿼리문이 되는데, 이게 제대로 된 쿼리문인가요? obj는 객체가 될텐데 쿼리문에 객체 자체를 넣으면 아마도 aaa 변수는
      ‘select SID from student where mac = [object Object];’ 가 될 것입니다.
      이 쿼리문을 날려서 select 해도 결과가 없을 것입니다.
      아마도 이런 식으로 쿼리문을 생성해야 할 듯 싶습니다.
      aaa = ‘select SID from student where mac = ‘ + obj.sid;

      또한 aaa 변수가 제대로 되어서 쿼리문을 날린다고 해도 select 한 결과가 무조건 1개 이상이라는 보장은 없습니다. 따라서 insert 구문을 만들기 이전에 result값이 null은 아닌지, select 결과 몇 개의 값을 가져왔는지 체크하는 코드가 선행되어야 합니다.

      • attendance.forEach(function(obj){
        aaa = ‘select SID from student where mac = “‘+obj+'”;’;
        dbconn.query(aaa, function(err, result){
        if(err) console.log(‘attendance error ‘+err);
        if(result!=null){
        console.log(‘attendance’);
        console.log(result);

        }

        });
        });
        충고 해주신대로 먼저 result의 값이 null여부를 위와 같이 확인해봤는데요. 리턴값이 null로는 확인이 안되는걸 확인했습니다.
        attendance
        [ { SID: ‘2008122181’ } ]
        attendance
        []

        원래 의도한 바라면 위의 sid가 있는 값만 출력이 되어야 하지만,(매칭이 안되는 값이 포함되어서 2개의 쿼리가 총 날라가는 형식이였습니다.) 이런식으로 result가 아무 값이 없음에도 출력이 되어서
        null값을 확인하는 곳에서 result[0].SID!=null로 지정해 주니

        TypeError: Cannot read property ‘SID’ of undefined
        at Query._callback (C:\nodejs\ws\max_test03(mysql)\routes\checkAtt.js:111:20)
        at Query.Sequence.end (C:\nodejs\ws\max_test03(mysql)\node_modules\mysql\lib\protocol\sequences\Sequence.js:78:24)
        at Query._handleFinalResultPacket (C:\nodejs\ws\max_test03(mysql)\node_modules\mysql\lib\protocol\sequences\Query.js:143:8)
        at Query.EofPacket (C:\nodejs\ws\max_test03(mysql)\node_modules\mysql\lib\protocol\sequences\Query.js:127:8)
        at Protocol._parsePacket (C:\nodejs\ws\max_test03(mysql)\node_modules\mysql\lib\protocol\Protocol.js:197:24)
        at Parser.write (C:\nodejs\ws\max_test03(mysql)\node_modules\mysql\lib\protocol\Parser.js:62:12)
        at Protocol.write (C:\nodejs\ws\max_test03(mysql)\node_modules\mysql\lib\protocol\Protocol.js:37:16)
        at Socket.ondata (stream.js:51:26)
        at Socket.EventEmitter.emit (events.js:117:20)
        at Socket. (_stream_readable.js:746:14)

        이게 뜨더군요 이게 젤 처음에 제가 막힌 부분이였습니다. 쿼리의 결과값이 json형식이라 이것이 비어 있더라도 null확인이 안되는 듯하여 위에서 말씀하신 null로 확인이 힘들것 같은데 어떻게 하면 될까요?

  2. 빈결과값일때 저 에러가 뜨는데요 그러면 저 값에 접근하기 전에 이 값이 빈것이라는걸 어떻게 확인할수 있을까요?

Leave a comment