본문 바로가기
개발공부일지/NodeJS

NodeJs - ORM을 이용해서 backend 데이터 사용 예제 (Feat. Sequelize)

by Hynn1429 2023. 1. 16.
반응형

안녕하세요.

Hynn 입니다.

이번 포스팅에서는 NodeJS 환경에서 실제 Sequelize 를 사용해서, 어떻게 구현하는지를 예제 형태로의 코드를 작성해보도록 하겠습니다.

가장 간단한 환경에서 게시판을 구현하는 방식으로 진행하였으며, 코드의 대한 해설과 함께 진행해보도록 하겠습니다.

아래의 순서대로 작성이 진행됩니다.

=============

1. Sequelize Class Template 함수

2. 기본 동작 구문

=============

Sequelize 는 이전 포스팅에서 설치에 대한 것은 다루어 본 적이 있습니다.
이제 실제 Sequelize 를 이용해서 기초적으로 DB를 구성하는 틀을 작성해보려고 합니다.
여기서는 "Class" 를 이용해 간편하게 테스트를 할 수 있는 환경 위주로 작성을 할 예정입니다.

1) Sequelize 설정을 위한 Config 구성

const config = {
    db : {
        development : {
            username : 'Hynn',
            password : 'hyunsign.tistory.com',
            database : 'HynnBlog',
            port : '3306',
            host : "127.0.0.1",
            dialect : 'mysql',
            define : {
                freezeTableName : true,
                timestamps : false
            }
        }
    }
};

module.exports = config;

위와 같이 설정을 기초적으로 구성해야 합니다.

물론.env 와 같은 형태의 파일로 이러한 변수를 별도로 담아 둘 수는 있겠지만, 예제 코드를 작성하기 위해 위와 같이 설정을 먼저 하도록 하겠습니다.
여기서 다시한번 짚고 가야할 부분은 3가지가 되겠습니다.

Dialect : Sequelize 에서 사용할 DataBase 유형을 지정해야 합니다.
Define - FreezeTableName : 테이블 이름을 고정으로 설정합니다.
Define - timestamps : Table Field 에서 Timestamp 를 비활성화 하는 설정입니다.

위 사진을 예제로 참고하시면, TimeStamp 의 true,false 에 따른 TableField 의 변화를 살펴볼 수 있습니다.
이후에 활용이 된다면 유용하게 활용할 수도 있는 기능이기도 합니다.

이제 이를 설정했다면, "Models" 이라는 Directory 를 생성하고, 이곳에 각 Table 별 구성요소를 생성하도록 하겠습니다.

const Sequelize = require('sequelize');
const { db } = require("../config");
const env = process.env.NODE_ENV || "development";
const config = db[env];

const sequelize = new Sequelize(config.database, config.username, config.password, config);

;(async () => {
    await sequelize.sync ({force : true})
});

module.exports = {
    Sequelize,
    sequelize,
};

먼저 Sequelize Module 을 변수에 담아와야 합니다. 그리고 new 생성자를 사용해서 소문자와 대문자로 구분했습니다.

이 두개의 차이는 잘 사용을 해야하므로, 꼭 기억해두어야 합니다.

그리고 async 를 사용한 즉시함수를 설정하여, sync, 생성의 함수를 작성하고 {} 내에 Force를 설정해서, 함수가 실행될 때마다, DB Table 을 덮어쓰기 형태로 설정합니다.

이렇게 설정하면, 매번 테이블이 초기화되기때문에, 부담없이 테스트가 가능합니다.

이를 이용해서 다양한 함수를 테스트할 수 있습니다.

 

module.exports = (sequelize, Sequelize) => {
    class User extends Sequelize.Model {
        static initialize() {
            return super.init({
                userId :{
                    type: Sequelize.STRING(60),
                    primaryKey : true,
                },
                userPw : {
                    type: Sequelize.STRING(64),
                    allowNull : false,
                },
                username :{
                    type:Sequelize.STRING(32),
                    allowNull : false,
                },
                provider :{
                    type: Sequelize.ENUM("local", "kakao"),
                    allowNull : false,
                    defaultValue : "local",
                },
                snsId:{
                    type:Sequelize.STRING(128),
                    allowNull : true,
                },
            },
            {
                sequelize,
            }
            ) 
        }

        static associate(models){
            super.hasMany(models.Board, {
                foreignKey : "userId",
            })
            super.hasMany(models.Comment, {
                foreignKey : "userId",
            })
            super.belongsToMany(models.Board, {
                through : "Liked",
                foreignKey : "userId"
            })
        }
    }

    User.initialize()
}

위의 함수에서는 "Module.exports" 를 사용해서 그 내부에 "Class" 를 담아 바로 내보내기 형태로 작성이 되었습니다.

여기서 Sequelize 를 정의하는 "Define" 을 사용하는 대신 "init", "initialize" 를 사용하여 구성했습니다.

Class 명은 Table Name 으로 지정이 되고, Extended 에 들어가는 부모 요소로 지정되는 "Sequelize.model" 은 index 에서 사용된 new Sequelize 의 Model 객체를 가르킵니다.

각각의 속성을 Console.log 로 출력하면서 확인한다면 보다 이해가 쉬울 것으로 생각됩니다.

그리고 각각의 Field 의 ForeignKey 설정을 부여하면 Class 생성의 기초가 작성됩니다.

다른 테이블의 작성 역시 기본적인 작성은 같으므로 생략하겠습니다.

이제 이를 더미 데이터 형태로 삽입하여 생성까지만 같이 시도해보도록 하겠습니다.

const Sequelize = require('sequelize');
const { db } = require("../config");
const env = process.env.NODE_ENV || "development";
const config = db[env];
const fs = require("fs");
const path = require('path');

const sequelize = new Sequelize(config.database, config.username, config.password, config);

const dir = fs.readdirSync(__dirname)
    .filter((v)=> v.indexOf('model') !== -1)
    .forEach((filename)=>{
        require(path.join(__dirname, filename))(sequelize,Sequelize)
    })
const {models} = sequelize
for (const key in models){
    models[key].associate(models)
};

;(async () => {
    await sequelize.sync ({ force : true,});

    const { User, Board, Comment , Hash } = models;
    await User.create({userId : 'choihwoong1', userPw :'1234', username : 'Admin'});
    await User.create({userId : 'choihwoong2', userPw :'1234', username : 'Admin'});
    await User.create({userId : 'choihwoong3', userPw :'1234', username : 'Admin'});
    await User.create({userId : 'choihwoong4', userPw :'1234', username : 'Admin'});
    await User.create({userId : 'choihwoong5', userPw :'1234', username : 'Admin'});
    await User.create({userId : 'choihwoong6', userPw :'1234', username : 'Hynn'});

    await Board.create({ subject : "안녕하세요", content : "1234" , userId : "choihwoong1"});
    await Board.create({ subject : "안녕하세요", content : "1234" , userId : "choihwoong2"});
    await Board.create({ subject : "안녕하세요", content : "1234" , userId : "choihwoong3"});
    await Board.create({ subject : "안녕하세요", content : "1234" , userId : "choihwoong4"});
    await Board.create({ subject : "안녕하세요", content : "1234" , userId : "choihwoong5"});
    await Board.create({ subject : "안녕하세요", content : "1234" , userId : "choihwoong6"});

    await Comment.create({ content : "테스트입니당" , userId : "choihwoong6" ,boardId : "1"});
    await Comment.create({ content : "테스트입니당" , userId : "choihwoong6" ,boardId : "1"});
    await Comment.create({ content : "테스트입니당" , userId : "choihwoong6" ,boardId : "2"});
    await Comment.create({ content : "테스트입니당" , userId : "choihwoong5" ,boardId : "2"});
    await Comment.create({ content : "테스트입니당" , userId : "choihwoong4" ,boardId : "3"});
    await Comment.create({ content : "테스트입니당" , userId : "choihwoong3" ,boardId : "3"});
    await Comment.create({ content : "테스트입니당" , userId : "choihwoong4" ,boardId : "4"});
    await Comment.create({ content : "테스트입니당" , userId : "choihwoong3" ,boardId : "4"});
    await Comment.create({ content : "테스트입니당" , userId : "choihwoong2" ,boardId : "5"});
    await Comment.create({ content : "테스트입니당" , userId : "choihwoong2" ,boardId : "5"});
    await Comment.create({ content : "테스트입니당" , userId : "choihwoong1" ,boardId : "6"});
    await Comment.create({ content : "테스트입니당" , userId : "choihwoong1" ,boardId : "6"});
    await Comment.create({ content : "테스트입니당" , userId : "choihwoong1" ,boardId : "6"});

    const body = {
        subject : "새로운 글 등록",
        content : "테스트 오우버",
        Hashtag : ["#javascript", "#hello", "#world", "#Hynn", "#Tistory"]
    };

    const cookies = {
        userId : 'choihwoong1'
    };

    const req = {body, cookies};

    const {Hashtag, ...rest} = req.body;

    const board = await Board.create(rest);
    const Hashtags = Hashtag.map((tag)=> Hash.findOrCreate({where : {tag }}));
    const tags = await Promise.all(Hashtags);
    await board.addHashes(tags.map((v)=>v[0]));

    const Id = 7
    const view = await Board.findOne({
        where : {id : Id},
        include : {
            model : User,
            attributes : ["username"],
        }
    })

    const Comments = await view.getComments()
    const HashT = (await view.getHashes({raw : true})).map(v=>({tag:v.tag}));

    console.log("comments", Comments)
    console.log('hashs', HashT)

})();
module.exports = {
    Sequelize,
    sequelize,
}

긴것처럼 보일수도 있으나, 실제 추가된 항목은 많지 않습니다.

즉시함수 내에서 먼저 DB에 추가될 항목을 수동으로 담아주었습니다. 위의 Sample 데이터를 이용해서 User,Board,Comment , Hash 의 인스턴트를 생성합니다.

기존같다면 req.body , cookie, 등에 있는 데이터를 Query 구문을 이용해 담겠지만, 위와 같이 샘플 데이터를 사용하였으므로, 이를 각각 개별적으로 지정하고, 이를 이용해 Sequelize method 를 사용해 Query 를 대체하였습니다.

여기서 가장 중요한것은 각각의 변수, 각각의 객체들의 Console 이 어떻게 구성되었는지를 한단계씩 밟아나가면서 탐색하는 것이 필요합니다.

가령 예를 들면 아래와 같습니다.

아래의 Console.log 는 위의 Sample code 에서 "console.log(board.__proto__) 라는 것을 출력하면 나타나는 method 입니다.

이 각각의 Method 는 단어 그대로 쉽게 확인할 수 있고, 이를 이용해서 작성자는 적절한 method 를 활용해야 합니다.

이것이 ORM 에서 기존의 query 구문이 아니라, 객체중심의 작성으로 이루어지는 이유이기도 합니다.

코드 해설에 대해서는 필요하시다면 문의주시면 제가 아는 선에서 성실하게 답변 드리도록 하겠습니다.

감사합니다.

반응형

댓글