이제 노드에서 MySQL 데이터베이스에 접속 해 볼것이다! 이때 Sequelize
라이브러리를 사용하면, MySQL 작업을 쉽게 진행 할 수 있다.
시퀼라이즈
는 ORM
으로 분류된다. 여기서 ORM
이란, 자바스크립트 객체와 데이터베이스의 릴레이션을 매핑 해 주는 도구를 말한다.
시퀼라이즈
는 MySQL 외에 다른 데이터베이스와도 함께 사용할 수 있다. 여기서 시퀼라이즈
를 쓰는 이유는, 자바스크립트 구문을 알아서 SQL
로 바꿔준다는 장점 때문이다.
이러한 장점 덕분에, SQL
언어를 직접 사용하지 않아도 자바스크립트 만으로 MySQL
을 조작 할 수 있다.
일단 시퀼라이즈
를 설치하기 전 새로운 프로젝트를 생성해준다.
express learn-sequelize --view=pug
learn-sequelize
라는 새로운 폴더가 생성되었다.
다음으로, cd learn-sequelize
명령어를 이용 해 해당 폴더로 이동한 뒤, npm 패키지
들을 설치해줬다.
npm i
그 다음, 시퀼라이즈에 필요한 sequelize
와 mysql2
패키지를 설치 해 준다. 그 다음, sequelize 커맨드
를 사용하기 위해 sequelize-cli
를 전역 설치 해 준다. 마지막으로 sequelize init
명령어를 호출 해 주면 된다.
npm i sequelize mysql2
npm i -g sequelize-cli
sequelize init
이것까지 완료하면,
이런 폴더가 완성된다.
이제 models
폴더 안의 index.js
를 확인 해 준다.
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const process = require('process');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
해당 파일을 그대로 사용 할 경우, 에러가 발생하기도 하고 필요 없는 부분도 포함되어 있다고 하니 파일을 수정해주자.
const path = require('path');
const Sequelize = require('sequelize');
const env = process.env.NODE_ENV || 'development';
const config = require(path.join(__dirname, '..', 'config', 'config.json'))[env];
const db = {};
const sequelize = new Sequelize(config.database, config.username, config.password, config);
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
이렇게 수정해 주면 된다 ㅎ.ㅎ
시퀼라이즈
을 통해 익스프레스 앱과 MySQL을 연결해야 한다.
app.js
에서 다음과 같이 구문을 추가해준다.
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var sequelize = require('./models').sequelize; //추가
var app = express();
sequelize.sync(); //추가
require('./models')
은 require('./routes/index')
와 같다. 폴더 내의 index.js
파일은 require시 생략 가능하다.
sync 메서드
를 사용하면 서버 실행 시 알아서 MySQL과 연동된다.
이제 MySQL
에서 정의한 테이블을 시퀼라이즈
에서도 정의 해 줘야 한다. MySQL
의 테이블은 시퀼라이즈
의 모델과 대응된다. 여기서 시퀼라이즈
는 MySQL의 테이블과 모델을 연결해주는 역할을 한다.
User과 Comment 모델을 만들어 users 테이블과 comments 테이블을 연결 해 보자!! 시퀼라이즈는 기본적으로 모델 이름은 단수
, 테이블 이름은 복수
로 지정한다.
먼저, models
폴더 안에 user.js
파일을 생성해줬다.
module.exports = (sequelize, DataTypes) => {
return sequelize.define('user', {
name: {
type: DataTypes.STRING(20),
allowNull: false,
unique: true,
},
age: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
},
married: {
type: DataTypes.BOOLEAN,
allowNull: false,
},
comment: {
type: DataTypes.TEXT,
allowNull: true,
},
created_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: sequelize.literal('now()'),
},
}, {
timestamps: false,
});
};
시퀼라이즈
는 알아서 id를 기본 키로 연결하므로 id 컬럼은 입력할 필요가 없다. sequelize
, define
메서드로 테이블명과 각 컬럼의 스펙을 입력 해 준다.
(MySQL 테이블과 컬럼 내용이 일치해야 한다.)
MySQL과 시퀼라이즈의 자료형은 다음과 같다.
MySQL | 시퀼라이즈 |
---|---|
VARCHAR | STRING |
INT | INTGER |
TINYINT | BOOLEAN |
DATETIME | DATE |
그 외에 추가적으로 알아야 하는 부분은 다음과 같다~!
그 다음, models
폴더 내에 comment.js
파일도 생성했다.
module.exports = (sequelize, DataTypes) => {
return sequelize.define('comment', {
comment: {
type: DataTypes.STRING(100),
allowNull: false,
},
created_at: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: sequelize.literal('now()'),
},
}, {
timestamps: false,
});
};
해당 구문을 보면, users 테이블과 관계를 정의 한 부분이 없는것을 확인 할 수 있는데, 따로 추가가 가능하다! 그건 좀 뒷부분에~
이제 모델을 다 생성했으니, index.js
와 연결 해 줘야 한다.
db.sequelize = sequelize;
db.Sequelize = Sequelize;
db.User = require('./user')(sequelize, Sequelize); //추가
db.Comment = require('./comment')(sequelize, Sequelize); //추가
module.exports = db;
db
라는 객체에 User와 Comment 모델을 담아두었다. 앞으로 db 객체를 require 하여 User와 Comment 모델에 접근 할 수 있다.
마지막으로, config
폴더 안에 있는 config.json
파일을 수정 해 준다.
"development": {
"username": "root",
"password": "[root 비밀번호]",
"database": "nodejs",
"host": "127.0.0.1",
"dialect": "mysql",
"operatorsAliases" : false
},
비밀번호는 본인이 지정한 비밀번호를 입력 해 주면 된다.
test 환경 일 경우에는 test 부분을, 배포 환경 일 때는 production 부분을 수정 해 주면 된다.
사용자 한 명은 댓글을 여러 개 작성할 수 있지만, 하나의 댓글에 사용자(작성자)가 여러명일수는 없다.
이러한 관계를 일대다
관계 라고 한다! 일대일
관계는 사용자와 사용자의 정보 테이블을 예를 들 수 있다. 사용자 한 명은 자신의 정보를 담고 있는 테이블과 관계가 있고, 정보 테이블도 한 사람만을 가리킨다.
다대다
관계로는 게시글 테이블과 해시태그 테이블을 예로 들 수 있는데, 한 게시글에는 해시태그가 여러 개 달릴 수 있고, 한 해시태그에도 여러 게시글이 달릴 수 있다.
MySQL
에서는 JOIN
이라는 기능으로 테이블 간의 관계를 파악해 결과를 도출한다. 시퀼라이즈는 JOIN 기능
도 알아서 구현 해 준다. 하지만, 시퀼라이즈에게 테이블 간에 어떤 관계가 있는지 알려줘야한다.
시퀼라이즈에서는 1:N 관계
를 hashMany
라는 메서드로 표현한다. users 테이블
의 로우 하나를 불러 올 때 연결된 comments 테이블
의 로우들도 같이 불러 올 수 있다.
반대로, belongsTo
메서드도 있다. comments 테이블
의 로우를 불러올 때 연결된 users 테이블
의 로우를 가져온다.
models/index.js에 추가로 코드를 넣어준다.
db.User = require('./user')(sequelize, Sequelize); //추가
db.Comment = require('./comment')(sequelize, Sequelize); //추가
db.User.hasMany(db.Comment, { foreignKey: 'commenter', sourceKey: 'id' });
db.Comment.belongsTo(db.User, { foreignKey: 'commenter', targetKey: 'id' });
module.exports = db;
시퀼라이즈
는 방금 정의한 대로 테이블 간 관계를 파악해서 commenter 컬럼
을 추가하고, 외래키도 추가한다.
외래키 컬럼은 commenter
고, users
의 id
컬럼을 가리키고 있다.
foreignKey 속성
에 commenter
를 넣어준다. hashMany 메서드
에서는 sourceKey 속성
에 id
를 넣어주고, belongsTo 메서드
에서는 targetKey 속성
에 id
를 넣어준다. User 모델
의 id
가 Comment 모델
의 commenter 컬럼
에 들어가는 것 이다.
이 상태에서 npm start
를 콘솔 창에 입력 해 주면,
Executing (default): SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME = 'users' AND TABLE_SCHEMA = 'nodejs'
Executing (default): SHOW INDEX FROM `users` FROM `nodejs`
Executing (default): SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME = 'comments' AND TABLE_SCHEMA = 'nodejs'
Executing (default): SHOW INDEX FROM `comments` FROM `nodejs`
다음과 같은 코드가 나온다! 시퀼라이즈가 스스로 실행 해 주는 SQL문
이라고 한다.
1:1
관계에서는 hasOne 메서드
를 사용한다. 사용자 정보를 담고 있는 가상의 info 모델
이 있다고 하면 다음과 같이 작성하면 된다.
db.User.hasOne(db.Info, { foreignKey: 'user_id', sourceKey: 'id' });
db.Info.belongsTo(db.User, { foreignKey: 'user_id', targetKey: 'id' });
belingsTo
와 hashOne
이 반대여도 상관없다. (일대일이니까)
시퀼라이즈
에서는 다대다
관계를 표현하기 위해 belongsToMany 메서드
를 사용한다. 게시글 정보를 담고 있는 가상의 Post 모델과 해시태그 정보를 담고 있는 가상의 Hashtag 모델이 있다고 하면 다음과 같이 표현할 수 있다.
db.Post.belongsToMany(db.Hashtag, { through : 'PostHashtag' });
db.Hashtag.belongsToMany(db.Post, { through : 'PostHashtag' });
다대다
관계 특성상 새로운 모델이 생성된다. through 속성
에 그 이름을 적어주면 된다. 새로 생성된 PostHashtag 모델
에는 게시글과 해시태그 아이디가 저장된다.
다대다
관계에서는 데이터를 조회할 때 여러 단계를 거쳐야 한다. 먼저, 노드 해시태그를 Hashtag 모델
에서 조회하고, 가져온 태그의 아이디(1)를 ㅏ탕으로 PostHashtag 모델
에서 hashtagId
가 1인 postId
들을 찾아 Post 모델
에서 정보를 가져온다.
시퀼라이즈는 이 과정을 편하게 하는 몇 가지 메서드를 제공한다.
async(req, res, next) => {
const tag = await Hashtag.find({where : {title : '노드'}});
const posts = await tag.getPosts();
}
먼저 해시태그를 찾으면 그 해시태그에서 바로 getPost
메서드를 사용할 수 있다. get+모델 이름의 복수형
이다.
async/await 형식이 익숙하지 않다면 프로미스 형식으로도 작성 가능하다.
HashTag.find({where : {title : '노드'}})
.then(tag => tag.getPosts())
.then(posts => console.log(posts));
비슷한 것으로 add+모델 이름의 복수형
메서드도 있다. 두 테이블과 다대다
관계를 추가 해 준다.
다음 코드는 title이 노드인 해시태그와 게시글 아이디가 3인 게시글을 연결하는 코드이다.
async(req, res, next) => {
const tag = await Hashtag.find({where : {title : '노드'}});
await tag.setPosts(3);
}
이렇게 작성하면 PostHashtag 모델에 PostId가 3이고 hashtagId가 1인 로우가 생성된다.