Node.js에서 knex를 이용한 MySQL query
업무에서 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 |
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
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: 1, name: 'Big Ben', address: 'Westminster, London SW1A 0AA'})) exports.down = async knex => knex.schema.dropTableIfExists(table) | cs |
knex migration이 정상적으로 완료되면 해당 데이터베이스의 knex_migrations 테이블에 실행된 migration이 추가되는 것을 확인할 수 있다. 이 테이블을 참고하면 보유한 migration 파일들 중에서 실행되지 않은 migration을 찾을 수 있다.
만일 문제가 발생하여 migration을 취소해야 할 경우 migration 파일의 down() 함수를 실행하여 취소할 수 있는데 다음은 이 때 실행하는 명령이다.
knex migrate:rollback
좀 복잡해 보이는 서브 쿼리도 조금만 익숙해지면 쉽게 만들 수 있고, raw query도 실행할 수 있다. Response 시 호출될 함수를 등록할 수 있는데, 이 기능을 이용해 긴 response time을 갖는 쿼리를 찾아 최적화할 수도 있다. 2년 넘게 사용해왔는데 딱히 불만이 없는 라이브러리다. 다음 사용하게 될 데이터베이스가 MySQL이라면 또 이 라이브러리를 최우선으로 고려할 생각이다.