업무에서 MySQL을 쓰기 때문에 쿼리 처리를 위해 knex를 사용하고 있다. knex는 SQL query builder 라이브러리인데 사용이 쉽고 직관적이기 때문에 업무에서 사용해도 무리가 없다. 자세한 내용은 https://knexjs.org 에서 찾아볼 수 있다. 


설치 


우선 다음 명령으로 설치한다. 

npm install knex --save


우선 DB 정보를 이용해 knex 객체를 생성한다. 

1
2
3
4
5
6
7
8
9
const knex = require('knex')({
  client: 'mysql',
  connection: {
    host : HOST_ADDR,
    user : DB_USER,
    password : DB_PASSWORD,
    database : DB_NAME
  }
})
cs


CRUD


이 때 생성한 knex 객체를 이용해서 쿼리를 생성하면 되는데, 유저 테이블에서 유저 정보를 읽는 쿼리는 아래 코드처럼 간단하다.

1
knex.select('id''name''age').from('users')
cs


유저 테이블에 새로운 유저를 추가하는 코드는 다음과 같다.
1
knex('users').insert({ name:'James Kook', age: 20 })
cs

이 때 생성된 유저의 ID가 2일 때 나이를 변경하는 코드는 다음과 같다. 
1
knex('users').update({ age: 21 }).where('id'2)
cs


유저를 삭제하는 코드는 다음과 같다.

1
knex('users').del().where('id'2)
cs

JOIN


유저 테이블과 문서 테이블을 조인해 아이디 2 유저의 문서를 변경 시간 역순으로 나열하는 쿼리는 다음과 같다. 이 때 리턴되는 값은 JSON 배열이며 map() 함수를 이용해 각 레코드를 대상으로 함수를 호출할 수 있다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const { userId } = params
const userDocs = await knex('users AS u')
    .select('u.id''u.name''u.age''d.id AS doc_id''d.name AS doc_name''d.created_at''d.updated_at')
    .join('documents as d''u.id''d.user_id')
    .where('u.id', userId)
    .orderBy('d.updated_at''DESC')
    .map(r => ({
        userId: r.id,
        userName: r.name,
        userAge: r.age,
        docId: r.doc_id,
        docName: r.doc_name,
        docCreatedAt: r.created_at,
        docUpdatedAt: r.updated_at
    }))
cs


Migration


스키마를 변경하거나 데이터베이스를 업그레이드하려고 할 때에는 knex migration 명령을 이용할 수 있다. 우선 knex를 글로벌로 설치하고 knex init 명령을 실행하여  knexfile.js 파일을 생성한다. 이후 knexfile.js 파일을 열어 데이터베이스 정보를 입력한다.

npm install knex -g

knex init


아래 예제는 development 환경에서 사용할 목적으로 만든 예제이다. 서버 환경에 따라 staging, production의 데이터베이스 정보를 입력해야 할 수 있다. 

1
2
3
4
5
6
7
8
9
10
11
12
module.exports = {
  development: {    
    client: 'mysql',
    connection: {
      timezone: 'UTC',
      host:     HOST_ADDR,
      database: DATABASE_NAME,
      user:     DB_USER,
      password: DB_PASSWORD
    }
  }
}
cs


다음은 migration을 통해 새로운 테이블을 추가하는 예제이다.

knex migrate:make add-attractions-table


이 결과 [timestamp]_add-attractions-table.js 파일이 생성되는데, 아래는 테이블을 생성하고 레코드를 추가하는 예제이다.
1
2
3
4
5
6
7
8
9
10
const table = 'attractions'
 
exports.up = async knex => knex.schema.createTable(table, t => {
  t.increments('id').primary()
  t.string('name')
  t.string('address')
})
.then(() => knex(table).insert({ id: 1name'Big Ben', address: 'Westminster, London SW1A 0AA'}))
 
exports.down = async knex => knex.schema.dropTableIfExists(table)
cs

다음 명령은 최근에 추가된 migration 파일들을 찾아 실행한다. 명령이 정상적으로 완료되면 해당 데이터베이스에 attractions 테이블이 생성되는 것을 볼 수 있다. 
knex migrate:latest


knex migration이 정상적으로 완료되면 해당 데이터베이스의 knex_migrations 테이블에 실행된 migration이 추가되는 것을 확인할 수 있다. 이 테이블을 참고하면 보유한 migration 파일들 중에서 실행되지 않은 migration을 찾을 수 있다.


만일 문제가 발생하여 migration을 취소해야 할 경우 migration 파일의 down() 함수를 실행하여 취소할 수 있는데 다음은 이 때 실행하는 명령이다.

knex migrate:rollback


좀 복잡해 보이는 서브 쿼리도 조금만 익숙해지면 쉽게 만들 수 있고, raw query도 실행할 수 있다. Response 시 호출될 함수를 등록할 수 있는데, 이 기능을 이용해 긴 response time을 갖는 쿼리를 찾아 최적화할 수도 있다. 2년 넘게 사용해왔는데 딱히 불만이 없는 라이브러리다. 다음 사용하게 될 데이터베이스가 MySQL이라면 또 이 라이브러리를 최우선으로 고려할 생각이다.



Posted by 코딩새싹
,