웹 백엔드

Node.js - Sequelize

토리쟁이 2024. 2. 20. 06:08

 

이번 포스팅에서는, Node.js에서 ORM과 Sequelize를 공부해 보려고 한다.

 


 

우선, Sequelize에 대해 공부해보자. 

 

 

Sequelize는 ORM 라이브러리 중 하나이다.

그러니, Sequelize에 대해 공부하기 전에 ORM이 무엇인지 짚고 넘어가보자.

 

ORM이란 무엇일까?

 

 

ORM(Object Relational Mapping)

  • 객체와 데이터베이스 관계를 매핑(연결)하는 도구
  • 어플리케이션과 데이터베이스 연결시, SQL 언어가 아닌 어플리케이션 개발 언어로 데이터베이스를 접근할 수 있게 해주는 툴 → 개발 언어의 일관성 및 가독성 향상, 재사용 및 유지보수의 편리성 향상
  • 프로그래밍 언어의 객체와 데이터베이스 사이의 중계 역할
  • 데이터베이스와 어플리케이션의 연결을 '객체지향적'으로 도와줌

 

즉, ORM이란 데이터베이스와 어플리케이션을 연결하는데 쓰이는데, 특히 DB 접근시 평소 사용하는 SQL언어가 아닌 개발 언어로 DB에 접근하게 해주는 도구라고 이해하면 된다. ORM의 종류로는 개발 언어에 따라 여러 가지가 있는데, 대표적인 것은 Django에는 ORM cookbook이, Node.js에는 Sequelize가, Java에는 Hibernate, JPA 등이 있다.

 

이 포스팅에서는 Node.js에서 사용되는 Sequelize에 대해 자세히 다뤄볼 것이다.

 

 

 

 

Sequelize

  • 자바스크립트 구문을 알아서 SQL로 변환해주는 모듈
  • DB 작업을 쉽게 할 수 있도록 도와주는 ORM 라이브러리 중 하나
  • 다른 데이터베이스 전환시에도 유용하게 사용 가능

즉, Sequelize는 여러 종류의 ORM 중에서도 Node.js에서 사용되는 모듈이라고 보면 된다.

이제, Sequelize를 어떻게 사용하면 되는지 살펴보자.

 

 

Sequelize 사용을 위해서는 Sequelize 설치가 필요하다.

npm install sequelize sequelize-cli mysql2

 

  • sequelize: sequelize 패키지
  • sequelize-cli: DB가 구축되지 않더라도 설정, 서버 실행만 하면 자동으로 DB 테이블 생성을 해주는 라이브러리
  • mysql2: mysql과 sequelize를 연결하는 드라이버(도구)

(하나의 install 명령어만을 쓰고도 띄어쓰기로 구분이 되기 때문에 3개를 한 번에 설치 가능하다.) 

 

 

설치가 끝난 후에는, sequelize 구조를 생성하면 된다.

npx sequelize init

 

그럼, 다음과 같이 몇 개의 새로운 폴더가 생성됨을 확인할 수 있다.

 

 

 

config 폴더에서는 DB 설정을 해준다.

{
  "development": {
    "username": "유저명",
    "password": "비밀번호",
    "database": "데이터베이스",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "test": {},
  "production": {}
}

코드를 보면, development, test, prodction이 있는데 이는 각각 개발 환경, 테스트 환경, 배포 환경에서의 DB 설정을 의미하며, 나는 development만 사용하였다.

 

 

 

이제, models 폴더에서 sequelize를 사용해보자.

 

models > index.js

"use strict"; // 엄격 모드 설정

const Sequelize = require("sequelize");

const config = require(__dirname + "/../config/config.json")["development"];
const db = {};

console.log("config >> ", config);

// const sequelize = new this.sequelize(DB, 사용자명, 비밀번호, config 정보 전체);
const sequelize = new Sequelize(
  config.database,
  config.username,
  config.password,
  config
);
db.sequelize = sequelize; // db = {sequelize:sequelize}
db.Sequelize = Sequelize; // db = {sequelize:sequelize, Sequelize:Sequelize}

// 모델이 여러 개 있으면,
// 여러 개의 모델을 require한 이후에, sequelize, Sequelize를 전달해야 함
// 첫 번째 인자: sequelize db 설정 객체
// 두 번째 인자: 직접 설치한 Sequelize 모듈
db.Visitor = require("./Visitor")(sequelize, Sequelize);
// [추가할 때마다 아래 코드를 작성해주어야 함]
db.User = require("./User")(sequelize, Sequelize);
// db라는 변수를 내보내기
module.exports = db;
  • index.js에서는 설정 및 연결 코드가 작성되어 있다.
  • sequelize 모듈을 불러와 Sequelize 객체 생성
  • config 객체 생성시, ["development"]는 config에서 설정한 development 환경의 DB 설정을 적용하겠다는 의미
  • new Sequelize(); 를 사용하여 sequelize 객체 생성
    • 인자로는 DB 설정 정보가 들어가며, 위에서 생성해 놓은 config 객체에 접근하여 DB 정보를 넣는다.
    • (DB명, 사용자명, 비밀번호, 전체 설정 정보)
  • db 객체 생성 후, sequelize와 Sequelize를 저장함
  • 생성한 모델을 불러오는데, 인자로는 (sequelize , Sequelize)이 들어간다.

 

 

이제, 불러올 model을 살펴보자.

 

models > Visitor.js

const Visitor = function (Sequelize, DataTypes) {
  // Sequelize 는 model/index.js의 sequelize
  // Datatypes는 model/index.js의 Sequelize

  // const model = Sequelize.define(params1,params2,params3);

  // params1: 모델 이름 설정
  // params2: 컬럼 정의 (CREATE TABLE 제약조건 참고)
  // params3: 모델 옵션

  const model = Sequelize.define(
    "Visitor",
    {
      id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true,
        allowNull: false,
      },
      name: {
        type: DataTypes.STRING(10),
        allowNull: false,
      },
      comment: {
        type: DataTypes.TEXT("medium"),
      },
    },
    {
      tableName: "visitor",
      timestamps: false, // 생성/수정시 시간을 나타내는 컬럼 createAt, updatedAt
      freezeTableName: true, // 이름을 복수로 설정하지 않는다.
    }
  );
  return model;
};

module.exports = Visitor;

 

  • Visitor은 앞서 index.js에서 설정한 (sequelize , Sequelize)를 인자로 받아 실행되는 함수이다.
    • Visitor 함수의 첫 번째 인자로는 index.js의 sequelize 즉, 설정한 DB 정보로 생성한 sequelize 객체
    • Visitor 함수의 두 번째 인자로는 index.js의 Sequelize 즉,  sequelize를 불러와서 생성한 객체
  • 함수 안에서, sequelize 모델이 정의된다. 이렇게 정의된 model이 mysql 테이블과 대응되는 것! 
  • Sequelize.define(param1, param2, param3);
    • param1: 모델(테이블) 이름 설정
    • param2: 컬럼 정의
      • 컬럼명:{컬럼 속성} (컬럼 속성은 아래 사진 참고)
      • (데이터 타입으로는 일반적으로 SQL에서 사용하는 데이터 타입과 살짝 다른 부분이 있는데, 문자열은 VARCHAR이 아닌, STRING(n)을 사용하고 날짜는 DATETIME이 아닌 DATE로 사용하는 등 비슷하면서도 다른 부분이 있다.)

컬럼 속성

  • param3: 모델 옵션 정의 (아래 사진 참고)

모델 옵션

 

 

  • 함수는 Sequelize.define(param1, param2, param3);을 통해 생성된 모델을 반환하고, 맨 아래에서 해당 함수를 exports함으로써 controller에서 해당 모델을 사용할 수 있게 되는 것이다.

 

 

이렇게 모델을 생성한 뒤에는, 해당 모델과 실제 DB를 연결해주기 위한 설정이 필요하다.

이를 위해 app.js에 아래의 코드를 작성해야 한다. 

const db = require("./models"); // 모델 불러오기

// { force: false } 쓰지 않아도 기본값이 false임 -> 테이블를 삭제하지 말고 만들어라
db.sequelize.sync({ force: false }).then((result) => {
  console.log("DB 연결 성공");
});

 

Sequelize의 sync() 함수 사용함으로써 DB와 연결된다.

 

 

sync()

  • 모델과 DB를 연결해주는 함수
  • DML뿐만 아니라, DDL도 사용할 수 있도록 도움
  • 기존에 있는 DB 테이블에 모델을 매핑할 수도 있지만, 새롭게 테이블을 만들 수도 있음
    • sync({force:true}): 테이블이 있다면 삭제 후 테이블 생성(테이블 전체를 삭제하므로 사용 지양)
    • sync({force:false}): 테이블이 있다면 해당 테이블 사용, 없다면 테이블 생성 → default

 

 

이렇게 app.js에서 모델과 실제 DB 연결이 끝난 후에는, controller에서 model에 sql 쿼리와 상응하는 Sequelize 메서드를 사용함으로써 함수 즉, 프로그래밍 언어를 통해 sql 쿼리를 실행시킬 수 있게 되는 것이다.

Sequelize 쿼리 메서드는 다음과 같으며, 프로미스를 반환하기 때문에 .then()을 붙여서 결과 값을 활용한 코드 작성이 가능하다.

 

 

 

이제, 위 함수들을 controller에서 사용해보자. 전체 코드는 다음과 같다.

 

controller > Cvisitors.js

const models = require("../models");

exports.main = (req, res) => {
  res.render("index");
};

// /GET visitors
exports.getVisitors = (req, res) => {
  models.Visitor.findAll().then((result) => {
    console.log("findAll >>", result);
    res.render("visitor", { visitorInfo: result }); // [{},{}]
  });
};

// 특정 방명록 1개 조회
// /GET visitor/:id
exports.getVisitor = (req, res) => {
  models.Visitor.findOne({
    where: { id: req.params.id },
  }).then((result) => {
    res.send(result);
  });
};

// POST /visitor
exports.postVisitor = (req, res) => {
  console.log(req.body); // {id, name, comment}
  
  models.Visitor.create({
    name: req.body.name,
    comment: req.body.comment,
  }).then((result) => {
    console.log(result);
    res.send(result);
  });
};

// DELETE /visitor
exports.deleteVisitor = (req, res) => {
  console.log(req.body.id);
  models.Visitor.destroy({
    where: { id: req.body.id },
  }).then((result) => {
    console.log("sequelize destoy result", result); // result: 1(true) or 0(false)
    res.send(req.body.id + "번 방명록 삭제 완료");
  });
};

// PATCH /visitor
exports.patchVisitor = (req, res) => {
  console.log(req.body); // {id, name, comment}
  models.Visitor.update(
    {
      name: req.body.name,
      comment: req.body.comment,
    },
    {
      where: { id: req.body.id },
    }
  ).then((result) => {
    res.end();
  });
};

 

각각의 함수들 안에서 사용되고 있는 Sequelize 쿼리 메서드를 정리해보자.

  • findAll(): 조건에 해당하는 모든 데이터를 조회하는 함수
    • 인자로 {where: {조건 컬럼명:값}}이 들어감
  • findOne(): 조건에 해당하는 데이터 1개조회하는 함수
    • 인자로 {where: {조건 컬럼명:값}}이 들어감
  • create(): 테이블 생성 함수
    • 인자로 {컬럼명:값...} 객체 묶음이 들어감
  • update(): 수정 함수
    • 첫 번째 인자로는 수정할 {컬럼명:값...} 객체와 두 번째 인자로는 {where: {조건 컬럼명:값}}이 들어감
  • destroy(): 삭제 함수
    • 인자로 {where: {조건 컬럼명:값}}이 들어감

 

 

여러 함수들 중에서도, 방명록 목록들을 보여주는 함수인 getVisitors 함수를 살펴보자.

이 sequelize를 공부하기 전까지, DB를 연결하지도 않고 model에 데이터를 넣어 모델 자체를 임시 DB로 사용했었던 실습도 했었고, DB는 연결했지만 sequelize를 사용하지 않고 model에서 직접 DB에 쿼리를 실행시켰었던 실습도 했었다. 이번에 공부한 sequelize와 비교해보자.

 

 

1. 임시 DB 역할을 했던 model 사용

exports.getVisitors = (req, res) => {
  // DB 연결 전 임시 데이터베이스
  console.log( Visitor.getVisitors() ); // [(), {}] 배열 안에 여러 개의 객체가 들어있는 형태
  res.render("visitor", { visitorInfo: Visitor.getVisitors() });
  }

model에 있는 데이터를 view에게 응답

 

 

 

2. DB 연결, sequelize 사용x

exports.getVisitors = (req, res) => {
  // DB 연결 후
  // result: model에서 작성한 쿼리 결과값인 rows
  Visitor.getVisitors((result) => {
     res.render("visitor", { visitorInfo: result });
   });

}

model에서 sql 쿼리를 실행시킨 후 받은 쿼리 결과 값을 view에게 응답

 

 

 

3. DB 연결, sequelize 사용O

// /GET visitors
exports.getVisitors = (req, res) => {
  models.Visitor.findAll().then((result) => {
    res.render("visitor", { visitorInfo: result }); // [{},{}]
  });
};

model에서 직접 쿼리를 작성할 필요없이, controller에서 Sequelize 쿼리문 함수를 통해 실제 DB에 접근할 수 있는 것이다. 여기서,  Sequelize 쿼리문 함수는 promise를 반환함으로 .then()을 붙여서 결과 값을 활용할 수 있다. 

 

 

 

 

 


참고

ORM

https://jalynne-kim.medium.com/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B0%B1%EC%97%94%EB%93%9C-orm-object-relational-mapping-%EC%9D%98-%EA%B0%9C%EB%85%90%EA%B3%BC-%EC%A2%85%EB%A5%98-%ED%99%9C%EC%9A%A9%EB%B0%A9%EC%95%88-c43b69028957

 

 

Sequelize

https://any-ting.tistory.com/50

https://baeharam.netlify.app/posts/Node.js/Node.js-Sequelize-%EB%8B%A4%EB%A3%A8%EA%B8%B0

https://velog.io/@litien/Seqeulize-Doc-%EB%B2%88%EC%97%AD-Model-Basic

 

[Seqeulize 공식문서 번역하기] Model Basic

해당 자료는 seqeulize 5의 공식문서를 번역한 자료입니다. Model Basics 이번 튜토리에서 Seuqelize의 모델이 무엇이고 어떻게 사용하는지 배워보자 Concept Models은 Sequelize의 본질이다. Model은 데이터베이

velog.io