이전 포스팅에서 MVC 모델에 대하여 간략히 정리하였다.
ORM은 바로 Model을 기술하는 도구이다. 이를 통해 데이터베이스 세계와 프로그래밍 언어 사이의 개념의 간극을 줄여준다(개발자들에겐 객체지향적 프로그래밍이 익숙하므로). 이전에 쇼핑몰 데이터베이스 구축 시 SQL 명령문을 직접 사용하였다면, 이번엔 ORM을 이용하여 SQL 문을 직접 작성하지 않고 엔티티를 객체로 표현하는 방법을 학습해보았다.
git clone, npm install 을 한다.
Sequelize ORM 통해 sequelize 설치
npm install --save sequelize
package.json에 최신버전(v6)으로 설치됐는지 확인한다.
dependencies 항목에 추가되어 있다.
Sequelize - Migrations 통해 sequelize-cli 를 설치
npm install --save-dev sequelize-cli
마이그레이션을 할 수 있도록 돕는 툴로, CLI에서 모델을 생성해주거나, 스키마 적용을 할 수 있도록 돕는다.
공식문서를 참고하여 cli를 통해 ORM을 잘 사용할 수 있도록 bootstraping(프로젝트 초기 단계를 자동으로 설정할 수 있도록 도와주는 일)을 해주었다.
npx sequelize-cli init
명령어를 입력하면 네 개의 폴더들이 생성된다.
config/config.json
models/
migrations/
seeders/
그리고 config.json 파일에서 비밀번호를 변경한다.
개발용, 테스트용, 배포용 환경중 개발용 환경을 기본적으로 사용한다.
models폴더 index.js 를 보면
const env = process.env.NODE_ENV || 'development';
해당 코드를 확인할 수 있다.
export NODE_ENV=production
하면 다른 환경으로 바꿀 수 있다.
{
"development": {
"username": "root",
"password": null, // 비밀번호 변경
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql"
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql"
}
}
모델은 엔티티를 객체로 표현한 형태로, 데이터 구조를 기술하고, 데이터에 수행할 수 있는 명령의 모음을 의미한다.
1. 데이터베이스 생성
mysql -u root -p mysql
을 실행
CREATE DATABASE database_development;
DB생성
USE database_development;
해당 DB를 사용
npx sequelize-cli model:generate --name User --attributes firstName:string,lastName:string,email:string
해당 명령어(예시)로 모델을 생성하였다. 모델명과 속성들을 각각 정의해주었다. url, title, visits
필드를 생성하였고, id, createdAt, updatedAt
필드는 자동으로 생성되었다.
npx sequelize-cli db:migrate
명령어를 실행하여,
마이그레이션을 실행함으로써 데이터베이스에 해당 테이블을 생성해주었다.
npx sequelize-cli db:migrate:status
해서 상태가 up이면npx sequelize-cli db:migrate:undo
로 실행취소 , down으로 해놓고 수정후 다시 1번 명령어 실행한다.https://bitly.com/ bitly는 긴 url을 축약해서 짧게 만들어주는 어플리케이션니다. 이 어플리케이션의 모델파트를 구현해보았다.
일단 구현해야 하는 내용을 간단히 하자면 아래와 같다.
sequlize 메소드는 기본적으로 모두 promise 객체를 반환한다. 따라서 객체 내 result 값을 꺼내오려면 .then
이나 async/await
을 사용해야 한다.
GET/links 요청을 처리할 때 사용하였다. sql구문에서 SELECT * FROM TABLE
과 똑같은 처리를 한다.
const { url: URLModel } = require("../../models"); //먼저 모델폴더에서 url 모델객체를 가져온다
get: async (req, res) => {
const result = await URLModel.findAll();
res.status(200).json(result);
},
POST/links 요청을 처리할 때 사용하였다. 요청의 payload에 아래와 같이 단축시키고 싶은 URL이 url 속성에 담겨있다. findOrCreate 메소드는 해당데이터를 찾고 그 조회 결과가 없는 경우에 엔트리를 테이블에 추가한다. create는 sql 구문에서 INSERT INTO ___ VALUES___
와 똑같은 처리를 한다.
{
"url": "https://www.github.com"
}
const [result, created] = await URLModel.findOrCreate({
where: {
url,
},
default: {
title,
},
});
if (created) {
return res.status(201).json(result); // 새로 생성된 경우 create
}
res.status(201).json(result); // 조회한 경우 find
GET /links/:id 요청을 처리할 때 사용하였다. findOne 메소드는 조건을 만족하는 첫번째 엔트리를 찾아낸다.
해당 id 값 바탕으로 url 모델을 찾아 리디렉션해주었다.
ex) 원본 URL이 https://www.github.com 인 모델 id가 1일 경우, http://localhost:3000/links/1 로 접속하면 원본 URL로 리디렉션
const result = await URLModel.findOne({
where: {
id: urlId,
},
});
/links/:id URL로 접근할 경우 visits 필드에 카운트가 1씩 증가해줘야 하는데 그 때 필드값을 업데이트 해주는 해당 메소드를 사용하였다.
await result.update({
visits: result.visits + 1,
});
npx sequelize-cli model:generate --name User --attributes firstName:string,lastName:string,email:string
cli 명령어를 입력하여 새로운 모델을 생성하고, npx sequelize-cli db:migrate
명령어를 실행하여, 마이그레이션 해주었다.
npx sequelize-cli migration:create --name addUserIdColumn
명령어를 입력하여 새로운 마이그레이션 파일을 생성하였다. up/down 속성이 비워진 상태로 새 마이그레이션 파일(Migration Skeleton)이 생성되었다.
해당 마이그레이션 파일내에서 아래와 같이 up/down 속성을 정의해주었다.(field 추가 & FK 설정)
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
// field 추가
await queryInterface.addColumn('urls', 'userId', Sequelize.INTEGER);
// foreign key 연결
await queryInterface.addConstraint('urls', {
fields: ['userId'],
type: 'foreign key',
name: 'FK_any_name_you_want',
references: {
table: 'users',
field: 'id'
},
onDelete: 'cascade',
onUpdate: 'cascade'
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.removeConstraint('urls', 'FK_any_name_you_want');
await queryInterface.removeColumn('urls', 'userId');
}
};
/models/index.js 파일에서 아래와 같이 associations를 설정해주었다. (user:url = 1:N)
const { url, user } = sequelize.models;
url.belongsTo(user);
user.hasMany(url);