Intro
혼자서 프론트부터 백엔드까지 북치고 장구쳤던 프로젝트가 있었다.
그 코드 구조가
프론트에서 ajax로 통신 -> ajax route처리하는 부분에서 호출 url에 맞는 API 함수 호출 -> DB 실행 -> 데이터 return 형태로
하나의 파일에서 호출URL에 맞는 함수 호출 부분이 전부 담겨있고, 다른 하나의 파일에선 API 호출하는 함수들을 선언하는 부분들이 담당하는 구조였는데 이 부분을 개선하여 조금 더 REST API 형태에 비슷하게 하도록 sequelize를 사용하여 구조를 변경해 보았다.
각 호출에 필요한 Model을 만들었고, API 호출 구조와 동일하게 디렉토리 안에 index파일을 만들어 API 개발할 때 직관적으로 개발 할 수 있도록 만들어보았다.
어떻게 사용했는가?
예를 들어
GET localhost:3000/search/auto
라는 부분을 API로 호출해준다 했을 때
routes/search/index.js형태로 해서
router.get('/auto', async (req, res) => {
try {
const { keyword } = req.query;
if (!keyword) {
return res.status(400).json({ error: 'Keyword is required' });
}
const schools = await model.findAll({
//
});
return res.status(200).json(schools);
} catch (error) {
console.error(error);
return res.status(500).json({ error: 'Internal Server Error' });
}
});
형태로 넣어주었다.
삽질했던 부분
sequelize를 사용할 때 .env 파일 때문에 조금 삽질한 경험이 있었는데 sequelize 설정하는 부분에서
module.exports = {
database: process.env.RDB_DATABASE,
username: process.env.RDB_USERNAME,
password: process.env.RDB_PASSWORD,
host: process.env.RDB_HOST,
port: process.env.RDB_PORT,
timezone: '+09:00'
};
요렇게 사용하였는데 process.env 파일안에 있는 값들을 불러오지 못한 것이였다.
처음에는 서버가 처음 실행하는 부분에 넣어주면 되겠다 생각해서
app.js 부분에
require('dotenv').config();
를 넣었음에도 데이터를 가지고 오지 못했다. 오랜 삽질 끝에 sequelize 설정하는 부분에서도 넣어줘야 한다는 걸 알게되어
require('dotenv').config()
module.exports = {
database: process.env.RDB_DATABASE,
username: process.env.RDB_USERNAME,
password: process.env.RDB_PASSWORD,
host: process.env.RDB_HOST,
port: process.env.RDB_PORT,
timezone: '+09:00'
};
이런식으로 넣어주니 제대로 가져오기 시작했다.
짧게 사용하며 느낀점
기존에는 한 파일 내에서 모든 작업을 다 진행하다보니 한 곳이 에러나면 그 부분을 찾기가 조금 힘들었다.
물론 Ctrl + F를 사용하여 찾아볼 수 있었지만 확실히 지금 개선한 코드구조가 더 직관적이라서 작업하기 편했다.
사실 나는 ORM 형태의 코드보다는 low쿼리 형태를 선호했었다.
ORM형태의 코드 구조를 사용하려면 기존 쿼리에서 ORM 형태의 코드로 변환해야 하는 작업과 ORM 형태로 사용하기 위해 Model들을 코드 상에서 재정의 해줘야 하는 작업이 너무 번거로웠기 때문이다.
하지만 Sequelize를 구성하고, 사용해보면서 좋은 점을 몇 가지 느꼈었는데
1. 코드가 간결해지고, 유지보수하기 편해졌다.
low쿼리를 사용했던 코드와 Sequelize를 사용하는 코드랑 비교하며 실예를 들어보겠다.
// db_service.js
async inquiry_board (out, no) {
let sql = "SELECT * FROM board where Church_No = "+no+"";
console.log("sql", sql)
let conn = await this.dbc.getConnection();
let result = null;
let error = null;
try {
await conn.beginTransaction(); // 트랜잭션 적용 시작
let select_board = await conn.query(sql);
await conn.commit(); // 커밋
result = select_board[0];
out(error, result);
}catch (err) {
error = err;
console.log(err)
out(error, result);
await conn.rollback() // 롤백
// return res.status(500).json(err)
} finally {
conn.release() // con 회수
}
}
// board/index.js
router.get('/',async (req,res) => {
try {
const boards = await Board.findAll();
return res.status(200).json(boards);
} catch (error) {
console.error(error);
return res.status(500).json({ error: 'Internal Server Error' });
}
});
위 쪽은 low쿼리, 밑에 쪽은 Sequelize를 사용한 코드이다. 같은 기능을 하지만 코드의 양이 훨씬 줄어들었다.
물론, low쿼리에선 rollback()하는 기능도 들어있지만 이 부분을 제외하고 단순히 쿼리를 실행하는 부분만 보면 차이가 느껴진다.
2. fk가 필요할 때 코드상에서만 fk관계를 줄 수 있다.
이게 무슨 말이냐하면 실제 DB에선 FK관계를 유지보수의 문제, 삽입, 업데이트 및 삭제 작업의 성능이 저하로 잘 맺지 않는다.
하지만 작업을 하다보면 테이블 간 Join을 위해 FK관계가 필요할 때가 많다.
Sequelize는 코드 상에서만 FK관계를 맺도록 하는 기능이 있다.
// 1:N
static associate(db) {
db.User.hasMany(db.Comment, {foreignKey: 'commenter',sourceKey:'id'});
}
static associate(db) {
db.Comment.belongsTo(db.User, {foreignKey: 'commenter', targetKey:'id'});
}
// 1:1
static associate(db) {
db.User.hasOne(db.Comment, {foreignKey: 'UserId',sourceKey:'id'});
}
static associate(db) {
db.Comment.belongsTo(db.User, {foreignKey: 'UserId', targetKey:'id'});
}
// N:M
static associate(db) {
db.Post.belongsToMany(db.Hashtag,{through:'PostHashtag'});
}
static associate(db) {
db.Hashtag.belongsToMany(db.Post,{through:'PostHashtag'});
}
위와 같이 관계에 따라 코드상에서만 맺을 수 있다는 게 정말 좋았다.
'프로그래밍(Web) > 공부일기' 카테고리의 다른 글
[바미] 트리거는 왜 사용할까? (0) | 2024.02.19 |
---|---|
TypeORM vs Sequelize (0) | 2023.12.13 |
Node - koa 프레임워크를 사용하며 느낀점 (0) | 2023.10.17 |
[바미] 백엔드 개발자로 이직하며 접했던 면접 질문들 (0) | 2023.09.25 |
[바미] 실전 아파치 카프카 - 카프카의 구성요소 (0) | 2023.09.25 |