Sequelize 시리즈


2019. 07. 20

1. ORM이란?

ORM( Object Relational Mappings )이란 프로그램 상의 객체(Object)와 DB의 테이블(Relation)일대일 대응하는 관계를 맺는 것(Mapping)을 의미합니다.

쉽게 받아들이려면, ORM에서 "객체 == 테이블"라고 생각해도 무방합니다.

 

ORM을 이용하면 query가 아닌 메서드로서 데이터를 조작할 수 있다는 것이 큰 장점입니다.

예를 들면, User 테이블의 데이터를 조회하기 위해,

  • MySQL
    • SELECT * FROM user; 쿼리 실행
  • ORM
    • User 테이블과 매핑된 객체를 user라 할 때, user.findAll(); 라는 메서드 호출

이렇게 ORM에서는 테이블과 매핑되는 객체의 메서드를 통해 쿼리를 조작합니다.

때문에 ORM을 사용하면 생산성을 높일 수 있습니다.

착각하지 말아야 할 점은, 개발자가 쿼리를 메서드로 호출할 뿐이지, 메서드를 호출하면 내부적으로 쿼리가 실행된다는 것입니다.

그래서 메서드가 원하는 대로 쿼리가 실행되고 있는지 파악할 수 있도록 쿼리 공부도 열심히 해야합니다.

 

그런데 쿼리가 복잡해지면 ORM으로 표현하는데 한계가 있고, 성능이 raw query에 비해 느리다는 단점도 있습니다.

요즘은( 2019년 7월 기준 ) ORM의 성능 및 안정성이 많이 좋아지고 있다고 합니다.

대부분의 ORM에서는 이런 경우를 대비해 raw 쿼리를 실행할 수 있는 방법도 제공하고 있습니다.

 

 

이 글에서는 Node.js의 대표적인 ORM인 Sequelize에 대해 알아보도록 하겠습니다.

  • 개발환경
    • express-generator 4.16.1
    • MySQL 8.0.16
    • sequelize 5.10.1
    • mysql2 1.6.5

 

 

 

2. Sequelize

Sequelize는 Node.js기반의 ORM으로 Promise 문법을 사용합니다.

( Promise는 비동기 코드를 깔끔하게 작성하도록 도와주는 JS 문법인데, Promise에 대한 자세한 내용은 여기를 참고해주세요. )

 

Sequelize를 사용하기 위해서는 Postgresql , MySQL , MS SQL,  SQLite 등의 RDB가 시스템에 설치되어있어야 합니다.

ORM의 특징은 특정 DB에 종속되지 않는다는 것입니다.

즉, DB와 커넥션만 연결되면 어떤 DB를 사용하던지 상관없이 동일한 메서드로 쿼리 수행이 가능합니다.

 

저는 MySQL을 사용할 것입니다.

RDB마다 Sequelize가 지원하는 것이 조금씩 다르므로 MySQL 외의 DB에 대해서는 문서를 참고해주세요.

 

 

먼저 Sequelize를 사용하기 위해서는 Sequelize와 RDB 모듈을 설치해야 하므로, sequelizemsyql2 두 모듈을 설치하도록 하겠습니다.

( mysql 모듈이 아닌 mysql2 모듈이며, 저는 express-generator로 프로젝트를 생성했습니다 )

# npm install sequelize mysql2

이후부터는 공식 문서를 따라가면서 Sequelize를 익히셔도 되는데, 저는 sequelize-cli를 통해서 스켈레톤을 만들어 사용하려고 합니다.

 

 

 

 

3. sequelize-cli

sequelize-cli 모듈은 sequelize를 조금 더 효율적으로 사용하기 위해서 몇 개의 폴더와 파일( 스켈레톤 )을 생성해줍니다.

( sequelize-cli에 대한 자세한 정보는 여기를 참고해주세요. )

 

sequelize-cli를 사용하려면 sequelize-cli 모듈을 설치하면 됩니다. ( 위에서 설치한 mysql2, sequelize 모듈을 먼저 설치해야 합니다. )

# npm install -g sequelize-cli

sequelize-cli 모듈을 전역으로 설치하면, 커맨더 창에서 sequelize 명령어를 실행할 수 있습니다.

 

# sequelize init

sequelize init 명령어를 실행하면 몇 개의 폴더,파일이 생성됩니다.

 

각 파일들의 내용은 다음과 같습니다.

 

 

1) /config/config.json

config.json 파일에서 DB 커넥션 정보를 각 환경에 맞게 설정할 수 있습니다.

지금은 예제를 위해, development에서 아래처럼 작성하시면 됩니다.

{
    "development": {
    "username": "root",
        "password": "비밀번호를 입력해주세요.",
        "database": "clitest",
        "host": "127.0.0.1",
        "dialect": "mysql",
        "operatorsAliases": false
    },
    ...
}
  • username
    • DB 사용자명
  • password
    • DB 비밀번호
  • database
    • RDB에서 사용할 database이름
  • host
    • host주소
  • dialect
    • 사용할 RDB 이름 
  • operatorsAliases
    • 연산자에 대한 별칭을 사용할 것인지 ( 참고 )

물론 시스템에 설치된 DB에 설정 파일에 기록한 정보와 관련된 user, database가 존재해야 합니다.

 

참고로 커넥션 풀을 사용하고 싶으면 위와 같이 작성하면 되는데요, 그 밖에 더 많은 옵션에 대해서는 여기를 참고해주세요.

 

 

2) models/index.js

models 폴더는 Model을 정의한 js 파일들을 모아놓은 폴더입니다.

( 모델을 정의하는 방법은 다음 글에서 살펴보겠습니다. )

 

models/index.js 파일은 다음을 과정을 수행합니다.

  1. /config/config.json 파일의 설정 값을 읽어 sequelize를 생성
  2. models 폴더 아래에 존재하는 js 파일을 모두 로딩
  3. db 객체에 Model을 정의하여 반환

정리하면, 여러 모델들을 한 객체( db )에 담아 반환하는 역할을 합니다.

그렇다면 어떻게 models 폴더의 모든 파일들을 불러와서 db객체에 모델을 정의할 수 있을까요?

 

 

 

첫 번째 밑줄을 보시면 반복문을 돌면서 import를 통해 현재 폴더 내의 모든 파일들을 불러오고 있습니다.

import 메서드는 파일에 있는 Model 정의들과 완벽히 같은 object를 생성합니다.

따라서 models 폴더 내에 Model을 정의하면, 반복문을 돌면서 Model들을 취합합니다.

그런데 이는 모델을 정의만 하는 것일 뿐, 실제로 프로젝트 내에 모델을 등록하는 것은 뒤에서 살펴 볼 sync() 메서드에 의해서 이루어집니다.

 

두 번째 밑줄을 보시면 반복문을 돌면서 associate() 메서드를 통해 Model 사이의 관계(N:M 등..)를 정의합니다.

관계 설정은 Model을 정의할 때 작성하며, 작성 방법은 다음 글에서 살펴보도록 하겠습니다.

 

 

정리를 하면,

  • /config/config.js
    • sequelize를 사용하기 위해 환경을 설정하는 부분
  • /models/index.js
    • Model을 정의하고 관계를 설정해주는 역할

 

 

 

 

4. sync() 메서드를 호출하여, sequelize 실행 테스트

sequelize CRUD의 데이터 조작DML : Data Manipulation Language ) 뿐만 아니라데이터 정의( DDL : Data Definition Language )도 지원합니다.

 

따라서 이미 만들어진 테이블에 Model을 매핑할 수 있을 뿐만 아니라DB에 테이블이 없는 상태라면 정의한 Model을 바탕으로 테이블을 생성할 수도 있습니다.

이를 수행하는 메서드가 sync() 이며, sync()는 모델에서 정의한 이름을 갖는 테이블이 존재하지 않을 경우에만 동작합니다.

 

쉽게 말하면, /models/index.js 파일에서 정의한 모델들을 바탕으로 실제로 Model을 등록하는 일을 합니다.

따라서 sync() 메서드를 호출하면 모델을 사용할 준비가 되는 것입니다.

 

 

sync() 메서드를 호출하는 방법은 /app.js 파일 상단에 아래의 코드를 작성하면 됩니다.

/app.js

...

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

models.sequelize.sync().then( () => {
    console.log(" DB 연결 성공");
}).catch(err => {
    console.log("연결 실패");
    console.log(err);
})

...

 

# npm start

이렇게 작성을 한 후, 서버를 실행하여 "DB 연결 성공" 로그가 출력 되면 정상적으로 Model이 등록된 것이고, sequelize가 잘 연동된 것입니다.

지금은 Model을 정의한 것이 아무것도 없기 때문에, 아무런 변화가 없을 것입니다.

 

 

 

 

이상으로 sequelize가 무엇이고, sequelize를 효율적으로 사용하기 위한 sequelize-cli가 무엇인지, 각 파일의 역할이 무엇인지 살펴보았습니다.

다음 글은 이어서 Model을 정의하는 법에 대해 알아보도록 하겠습니다.

 

[ 참고 ]

http://docs.sequelizejs.com/manual/getting-started.html

http://webframeworks.kr/tutorials/expressjs/expressjs_orm_one/

https://hyunseob.github.io/2016/03/27/usage-of-sequelize-js/

댓글 펼치기 👇
  1. Favicon of http://https://victorydntmd.tistory.com/25?category=677306 chanseok 2019.11.23 15:00

    ./models/index.js 에서
    .filter(file => {
    return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === 'index.js');
    })
    ### .js --> index.js로 수정해야 db연결 성공이 나옵니다!

    • Favicon of https://victorydntmd.tistory.com victolee 우르르응 2019.11.23 15:12 신고

      file.slice(-3) === 'index.js' 가 의미상 맞지 않는것 같아요.
      file.slice(-3)는 문자열 3개를 반환하며, 해당 파일이 .js 확장자인지 식별하는 용도이거든요.

      /models/index.js는 sequelize에서 제공하는 core 부분이기에, 수정하지 않아도 DB 커넥션 정보가 잘 맞다면 문제없이 연결될 것으로 생각됩니다!

  2. Favicon of http://https://victorydntmd.tistory.com/25?category=677306 chanseok 2019.11.23 16:00

    db 커넥션 정보라는게 config.json의 development의 정보들과 지난번 글에서 /routes/index.js 이부분의 db와 정보가 같으면 되는게 맞는거죠??
    index.js를 사용하지 않고 .js만 사용하면
    D:\etc\mixed\node_modules\sequelize\lib\sequelize.js:486
    this.importCache[importPath] = defineCall(this, DataTypes);
    TypeError: defineCall is not a function

    이렇게 에러가 나오는데 혹시 왜 그런지 알 수 있을까요???

    • Favicon of https://victorydntmd.tistory.com victolee 우르르응 2019.11.23 16:19 신고

      아니요, sequelize에서는 커넥션 정보를 config.json에서만 관리하고있어요.

      https://victorydntmd.tistory.com/25
      해당 글에서는 index.js에 연결정보가 있기 때문에, file.slice(-3) === 'index.js'가 성공한 것처럼 보일 수 있겠네요.
      사실 file.slice(-3) === 'index.js' 이거는 항상 false이구요.

      말씀 주신 에러는 구글링 하셔서 해결하셔야 할 것 같습니다...
      sequelize TypeError: defineCall is not a function
      ->
      .filter((file) => {
      return (file.indexOf(".") !== 0) && (file !== "index.js");
      })
      이걸로 해결이 될지 잘 모르겠네요

  3. hhk1013 2020.05.20 17:20

    Error: Cannot find module 'C:\Users\gurrl\node\HelloExpress\models/../config/config.json'
    다음과 같이 오류가 나오는데 환경변수 설정을 어떻게 만져야 하나요?

    • Favicon of https://victorydntmd.tistory.com victolee 우르르응 2020.05.23 13:35 신고

      config/config.json 파일은 별다른 설정없이 자동으로 읽어들여야 하는데, 해당 파일이 올바른 경로에 있는지 확인해주셔야 할것 같습니다.

  4. Rave 2020.11.03 15:39

    node 자료 찾아서 떠돌던 중 방문 했습니다.

    아주 잘 정리가 되어 있네요.

    감사합니다.