ORM은 기존의 SQL구문 대신 객체를 활용하여
관계형 데이터베이스의 데이터를 조작하는 기술을 말합니다
이번 포스팅에서는 ORM의 일종인 Sequelize
를 활용해서
ORM으로 데이터베이스를 다루는 방법에 대해 알아보겠습니다
(공식문서: https://sequelize.org/docs/v6/)
다만 ORM을 사용하기에 앞서서 약간의 사전작업을 거치려 합니다
dotenv
는 환경변수를 관리하기 위해 사용하는 라이브러리입니다
1) 설치
npm install dotenv
2) .env
파일을 생성해서 관리할 환경변수를 기입합니다
변수명=값
형태로 기입해야 합니다 (띄어쓰기 조심!).gitignore
에 추가해서 git의 관리목록에서 제외합니다PORT=3001
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=[mysql 계정명]
DB_PASSWORD=[비밀번호]
DB_DATABASE=[사용할 데이터베이스명]
3) Config 파일 생성
require("dotenv").config()
const HttpException = require('./exceptions/HTTPExceptions')
const host = process.env.DB_HOST || "";
const port = process.env.DB_PORT || "";
const user = process.env.DB_USER || "";
const password = process.env.DB_PASSWORD || "";
const database = process.env.DB_DATABASE || "";
const config = {
exception: {
HttpException,
},
env: process.env.NODE_ENV || 'development',
port: process.env.PORT || 3000,
db: {
development: {
username: user,
password: password,
database: database,
port: port,
host: host,
dialect: "mysql",
// sequelize는 다양한 DB를 지원합니다. 그 중 mysql을 사용하겠다는 의미의 코드
},
test: {
// 테스트용 객체도 따로 만들어두는 편이 좋습니다
username: user,
password: password,
database: database,
host: host,
port: port,
dialect: "mysql",
logging: false
}
}
}
module.exports = config
파일 작성이 끝나면 server.js 최상단에 다음 코드를 추가합니다
require("dotenv").config()
테이블(Comments)을 생성하기 위한 코드입니다
[comment.model.js] ~ 테이블 필드 내용을 객체로 표현합니다
module.exports = (sequelize, DataTypes) => {
// 두 가지 인자를 받습니다
return sequelize.define(
// define : 모델(객체)을 생성하는 메서드입니다. 다음 3가지 인자를 받습니다
// 1. 객체의 속성명 2. 테이블 필드 정보 3. 테이블 옵션 정보
"Comment",
{
// id 필드는 자동으로 생성됩니다
// id: {
// type: DataTypes.INTEGER,
// primaryKey: true,
// autoIncrement: true,
},
userid: {
type: DataTypes.STRING(30),
allowNull: false,
},
content: {
type: DataTypes.TEXT,
allowNull: false,
},
},
{
// freezeTableName: true, // true일 때 객체 속성명이 그대로 테이블명이 됩니다 (false일 경우 속성명+s(복수형))
// tableName: "Test", // 테이블명을 따로 설정할 수도 있습니다
charset:'utf8mb4', // utf8 + 이모지 사용가능
callate: "utf8_general_ci",
// timestamps: false
// createdAt, updatedAt 필드를 생성하지 않습니다
}
)
}
[index.js] ~ model 파일들을 모아서 관리할 모듈입니다
const Sequelize = require("sequelize") // class
const config = require("../config")
const db = config.db[config.env]
// === config.db['development']
const sequelize = new Sequelize(
// 1 - DB명, 2 - 유저명, 3 - 패스워드, 4 - DB
db.databse,
db.username,
db.password,
db
)
require('./user.model')(sequelize, Sequelize)
// 인자를 대응시키고 불러온 함수를 호출해야 합니다
// Sequelize 클래스 안에는 DataTypes 속성이 들어있어서 접근이 가능합니다
// console.log(sequelize.models)
// { User: User }
module.exports = {
Sequelize,
sequelize
}
모델 정보를 모듈에서 불러오고, 서버가 켜질 때 테이블을 생성하기 위해 서버에도 다음 코드를 추가합니다
modules
const { sequelize: { models: { Comments } }, sequelize }
= require("../models/index");
server
app.listen(config.port, async () => {
await sequelize.sync({ force: true }) // 서버가 실행될 때 테이블을 생성합니다
console.log(`listening on ${config.port}`);
});
실행결과는 다음과 같습니다
mysql> DESC Comments;
+-----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| userid | varchar(30) | NO | | NULL | |
| content | text | NO | | NULL | |
| createdAt | datetime | NO | | NULL | |
| updatedAt | datetime | NO | | NULL | |
+-----------+-------------+------+-----+---------+----------------+
Repository
class CommentRepository {
constructor({ Comments }) {
this.Comments = Comments;
}
// SELECT * FROM Comments ORDER by id DESC
async findAll() {
try {
const comments = await this.Comments.findAll({
order: [["id", "DESC"]],
});
// this는 constructor의 인자를 가리킵니다
return comments;
} catch (e) {
throw new Error(e);
}
}
// INSERT INTO Comments(userid,content) VALUES('${userid}', '${content}'
async create(commentData) {
try {
const comment = await this.Comments.create(commentData);
return comment;
} catch (e) {
throw new Error(e);
}
}
// UPDATE Comments SET content = '${content}' WHERE id = '${id}'
async update({ id, content }) {
try {
const update = await this.Comments.update(
{ content: content },
{ where: { id: id } }
);
return update; // [1] ~ 배열을 리턴합니다
} catch (e) {
throw new Error(e);
}
}
// DELETE FROM Comments WHERE id = '${id}'
async destroy(id) {
console.log("repo :", id)
try {
const destroy = await this.Comments.destroy({
where: { id: id },
});
return destroy; // 1
} catch (e) {
throw new Error(e);
}
}
}
module.exports = CommentRepository;
각각의 메서드는 아래의 SQL 구문에 대응합니다
findAll()
&findOne()
:SELECT ~ FROM (~ WHERE)
create()
:INSERT ~ INTO
update()
:UPDATE ~ SET
destroy()
:DELETE FROM
*findOne()
은 값을 못찾았을 때 null
값을 리턴합니다
+) SQL 쿼리문도 사용할 수 있습니다
const [query] = await sequelize.query("SELECT * FROM Comments") // console.log(query);
사실 ORM을 반드시 사용해야만 할 이유는 없어보입니다
복잡한 SQL 구문을 전부 ORM으로 대체하는 것이 힘들기도 하고
(특히 JOIN문 구현에 어려움이 많다고 하네요)
초기 설정을 잘못하면 통신속도가 저하될 우려도 있습니다
대신 객체 조작만으로 DB 데이터를 불러오고 사용할 수 있다는 점,
그리고 위와 같은 테이블 생성 모델을 gitHub에 업로드해서
모든 팀원이 간편하게 테이블 정보를 공유할 수 있는 등,
협업에서의 강점이 특히 뛰어나기 때문에 ORM 사용이 권장되는 것이 아닐까 싶네요