#1. Model과 DB 연결(Sequelize)

toto9602·2022년 2월 22일
0

첫 Express 프로젝트

목록 보기
2/7

참고자료: Sequelize 문서
참고자료: 생활코딩 MYSQL

오늘은 DB 연결과 Model 작성하기!

이전에 썼던 Django는 DB와 연결도 자동으로 되어있다시피 하고,
ORM 관련해서도 딱히 신경 쓸 것이 없어서
프로젝트와 DB의 연결이라는 걸 해 본 게 사실상 처음이어따..

그래서 ORM이 뭔지도 모르다가 이번에야 개념을 좀 알게 되었다.. 사실 아직도 잘 모름..

프로젝트를 준비하면서 <생활코딩> MYSQL강의를 들으면서 속성으로(?) SQL 기초를 찍먹하긴 했지만, 프로젝트에서 적용해 볼 수 있는 수준은 아닌 것 같기도 하고..

이래저래 ORM 방식을 사용하기 위해 sequelize를 사용해 보기로 했다!
그래서 작성 순서는..

  1. Sequelize instance 생성하기
  2. Model 작성하기
  3. 테이블 간 관계 정의하기
  4. app.js에서 DB와 연결하기

1. Sequelize instance 생성하기

config/db.js

const Sequelize = require('sequelize');
//Sequelize instance
//parameter를 전달하는 방식
//데이터베이스로의 연결을 represent
const sequelize = new Sequelize({
    dialect:'sqlite',
    storage:'../dropby_db.db'
});

module.exports = sequelize;

참고자료에 작성한 Sequelize 문서에 따르면, Sequelize 객체를 생성하는 방법에는 3가지 정도가 있다고 하는데, 나는 2번째 방법을 사용했다.

이 글을 작성하면서 잠시 찾아 본 바로는,
dialect란 어원적으로 '방언'이라는 뜻으로, sequelize에서는 각각의 db 언어를 '방언'으로 정의한 것 같다고 한다.
그래서 어떤 DB를 사용할 것인지 명시해 주는 느낌인 듯..?!

storage는 참고자료에 나와있듯, DB 파일이 있는 경로를 적어주었다.

2. Model 작성하기

models/drop.js

const sequelize = require('../config/db');
const {DataTypes} = require('sequelize');

const Drop = sequelize.define("Drop", {
                pk: {
                    type:DataTypes.INTEGER,
                    allowNull:false,
                    primaryKey:true, //primaryKey로 사용
                    autoIncrement:true, //primaryKey는 자동으로 1씩 증가
                },
                content: {
                    type:DataTypes.TEXT,
                    allowNull:false,
                },
                createdAt:{
                    type:DataTypes.DATE,
                    defaultValue: DataTypes.NOW
                }
            }, {
                freezeTableName:true,
                timestamps:false
            }
        );

module.exports = Drop;

Drop이란 이 프로젝트에서 특정 장소에 남기는 글..? 정도의 개념이다.
생성한 sequelize 객체와, sequelize 패키지에서 DataTypes를 가져와 주고

위와 같이 테이블을 정의해 주었다!

2번째 인자로는 옵션으로 설정할 내용을 넣어줄 수 있다.

freezeTableName : sequelize는 자동으로, 설정한 이름의 복수형(ex. Drop -> Drops)으로 테이블 이름을 생성하는데, 해당 값을 true로 설정하면 설정한 이름 그대로 테이블 이름을 만들어 준다.

timestamps : 역시, sequelize는 자동으로 createdAt, updatedAt row에서 데이터의 생성 시점, 업데이트 시점을 저장하는데, createdAt(생성 시점)은 사용하지만 updatedAt은 사용하지 않을 거라 해당 값을 false로 두고 createdAt은 직접 정의해 주었다.

models/place.js

const sequelize = require('../config/db');
const { DataTypes } = require('sequelize');

const Place = sequelize.define("Place", {
    pk: {
        type: DataTypes.INTEGER,
        allowNull: false,
        primaryKey: true,
        autoIncrement: true,
    },
    name: {
        type: DataTypes.STRING(20),
        allowNull: false,
    },
    latitude: {
        type: DataTypes.FLOAT,
        allowNull: false,
    },
    longitude: {
        type: DataTypes.FLOAT,
        allowNull: false,
    },
}, {
    freezeTableName: true,
    timestamps: false
}
);

module.exports = Place

place에서는 특정한 장소(장소명, 좌표) 등을 저장해 줄 것이라 위와 같이 작성했다!

models/user.js

const sequelize = require('../config/db');
const {DataTypes} = require('sequelize');

const User = sequelize.define("User", {
    pk: {
        type:DataTypes.INTEGER,
        allowNull:false,
        primaryKey:true,
        autoIncrement:true,
    },
    nickname: {
        type:DataTypes.STRING(20),
        allowNull:false,
    },
    email: {
        type:DataTypes.STRING(20),
        allowNull:false,
        unique:true,
        validate:{
            isEmail:true
        }
    },
    password: {
        type:DataTypes.STRING,
        allowNull:false,
    },
    createdAt:{
        type:DataTypes.DATE,
        defaultValue:DataTypes.NOW
    },
    Refresh:{
        type:DataTypes.TEXT,
        allowNull:true,
    }
}, {
    freezeTableName:true,
    timestamps:false
}
);

module.exports = User

user 테이블 쪽도 email에 validator를 적용한 것 빼곤 크게 다를 게 없었다!
저장되는 값이 이메일 형식인지를 확인해 주는 부분인데,
validator는 종류가 워낙 많아 Sequelize 공식문서에서 확인해 보면 좋을 듯!!

P.S 추후에 언급하겠지만, 현재 JWT 인증 방식을 사용 중이고 Refresh Token을 DB에 저장하는 방식을 취하고 있다.
사실 이게 맞는지 고민이라.. 해당 부분은 추후 바꿀 지도!!

3. 테이블 간 관계 정의하기

models/index.js

const Sequelize = require('sequelize')
const sequelize = require('../config/db')
const db = {};

db.Drop = require('./drop');
db.User = require('./user');
db.Place = require('./place'); //정의한 모듈들 가져오기

db.sequelize = sequelize;
db.Sequelize = Sequelize;
// 한 사용자는 여러 개의 드롭을 가질(만들) 수 있음.
db.User.hasMany(db.Drop, {
    foreignKey:{
        name:'creatorPk',
        allowNull:false
    },
    onDelete:'SET NULL'
});

db.Drop.belongsTo(db.User, {
    foreignKey:{
        name:'creatorPk',
        allowNull:false
    },
    onDelete:'CASCADE'
});

// 한 장소에는 여러 개의 드롭이 존재할 수 있음
db.Place.hasMany(db.Drop, {
    foreignKey:{
        name:'placePk',
        allowNull:false
    },
    onDelete:'CASCADE'
});

db.Drop.belongsTo(db.Place, {
    foreignKey:{
        name:'placePk',
        allowNull:false
    },
    
})
module.exports = db;

처음에 sequelize 관련 자료를 찾아보면서..

db.sequelize = sequelize;
db.Sequelize = Sequelize;

이 두 코드가 제일 무슨 소리인가.. 싶은 구간이었다.

const Sequelize = require('sequelize')
const sequelize = require('../config/db')

제일 상단의 이 코드들과 같이 보면,

  • sequelize 관련 기능을 사용하기 위해 Sequelize 객체를 넣어두는 부분 (대문자)
  • 정의한 연결 객체를 db에 넣는 부분 (소문자)

정도 느낌으로 일단 구분 가능한 듯..!

하단에는 hasMany, belongsTo를 사용해서
User와 Drop, 그리고 Place와 Drop을 일대다 관계로 Mapping해 주었다.

두 방향 다 정의해 두면 나중에 양쪽에서 다 서로를 참조할 수 있다고 함!

4. app.js에서 DB와 연결하기

app.js

const {sequelize} = require('./models/index'); 
// 다른 require문은 일단 생략
const ConnectDB = async () => {
    try {
        await sequelize.authenticate().then( 
            () => console.log('데이터베이스 연결 성공!')
        );
        await sequelize.sync().then(
            () => console.log('동기화 완료!')
        );
    } catch (error) {
        console.error('DB 연결 및 동기화 실패', error);
    }
}
// DB와 연결 및 동기화
ConnectDB();

해당 부분은 역시 참고자료Connecting to a Database 부분, Model Basics의 Model Synchronization을 참고해서 작성하였다.

sequelize.authenticate()

해당 코드를 통해 연결이 정상적인지 확인하고,

sequelize.sync()

를 통해 동기화해 준다!

cf) sync() 안에는

{force:true}

혹은

{alter:true}

속성을 넣어줄 수 있는데,

force:true =) 이미 테이블이 존재한다면, Drop했다가 강제로 다시 생성해 준다.

alter:true =) 모델에 맞게, 변경사항만 반영해 준다!

여기까지 작성하고,

node app.js

혹은

npx nodemon app.js

등으로 app.js를 실행해 주면

cf)nodemon은 변경사항을 관찰해서, 변경사항이 있다면 자동으로 서버를 재시작해 준다!

위와 같이
데이터베이스 연결 성공! 문구,
정의한 모델에 따라 TABLE들을 생성하는 명령어,
그리고 동기화 완료! 라는 문구가 정상적으로 차례로 표시된다 :)

한 번에 묶어 쓰는 게 좋을 것 같아 쓰다 보니 조금 길어졌지만..ㅎㅎ
그래도 정리하면서 한 번씩 다시 복습할 수 있는 게 좋은 것 같다 ㅎㅎ

profile
주니어 백엔드 개발자입니다! 조용한 시간에 읽고 쓰는 것을 좋아합니다 :)

0개의 댓글