npx 를 이용한 시퀄라이즈 연결

Ywoosang·2021년 1월 30일
4

Sequelize

목록 보기
2/2
post-thumbnail

npx 로 만든 프로젝트 구조에서 시퀄라이즈 연결 방법에 대해 다룬다. 개요는 아래와 같다.

  • npx 를 이용한 구조 생성
  • config.js 수정
  • index.js 분석
  • model 생성
  • sync() 메서드를 호출해 sequelize 실행

npx 를 이용한 구조 생성

express 와 시퀄라이즈를 이용한 프로젝트를 구성하기 위한 기본적인 설정들이다.

package.json 생성

npm init -y 

mysql2 는 MySQL 데이터베이스를 의미하는 것이 아닌 Node.js 와 MySQL 을 연결해주는 역할을 하는 드라이버다. sequelize-cli 는 시퀄라이즈 명령어를 사용할 수 있게 해주는 패키지다.

npm i express sequelize mysql2
npm i -D sequelize-cli 

npx 명령어로 sequelize-cli 를 이용해 시퀄라이즈 구조를 생성한다.

npx sequelize init 

config 폴더, migrations 폴더, models 폴더,seeders 폴더가 생성된다.

npx
npx는 npm 레지스트리에 올라가있는 패키지를 쉽게 설치하고 관리할 수 있도록 도와주는 CLI 도구다. 쉽게 말해 npm 을 좀 더 편하게 사용하기 위해 npm 에서 제공해주는 것이다.

현재 npm 버전에 npx가 설치되어 있는지 확인한다.

which npx

config.json 수정

username 과 password , database 이름을 설정해준다.

{
  "development": {
    "username": "user_name",
    "password": "database_password",
    "database": "database_name",
  ... 
}

models/index.js 분석

기본적으로 npx sequelize init 명령어를 입력하면 models 폴더 안에 아래와같이 index.js 가 생성된다. models 폴더는 Model을 정의한 js 파일들을 모아놓은 폴더다.

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

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

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

자바스크립트 경험이 많이 없거나, 메소드에 대한 지식이 부족할 경우 index.js 파일이 무슨 역할을 하는지 알기 어려울 수도 있다. 하나하나 분석해보자.

'use strict';

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);

const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};

let sequelize;
if (config.use_env_variable) {
  sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
  sequelize = new Sequelize(config.database, config.username, config.password, config);
}

fs
  .readdirSync(__dirname)
  .filter(file => {
    return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
  })
  .forEach(file => {
    const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
    db[model.name] = model;
  });

Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

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

module.exports = db;

각각 fs 모듈, path 모듈, sequelize 라이브러리를 임포트한다.

fs 모듈은 FileSystem의 약자로 파일 처리와 관련된 모듈이다.

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');

path.basename(path) 은 인자로 경로를 받고 해당 파일이름을 출력한다.

아래 결과를 통해 직관적으로 이해할 수 있다.

console.log(__filename);
console.log(path.basename(__filename));

실행결과
/home/ywoosang/projects/test.js
test.js 

__filename 을 이용해 현재 파일의 경로를 입력하고, path.basename 을 이용해 파일 이름을 basename 변수에 할당한다.

const basename = path.basename(__filename);

node 환경변수 process.env 에 NODE_ENV 를 별도로 설정해놓았다면 그 값을 사용하고, 아니라면 기본 값으로 'development' 를 사용한다. 이렇게 해주면 NODE_ENV 가 따로 지정이 안되어 있더라도 development 모드로 지정되게 해줄 수 있다.

const env = process.env.NODE_ENV || 'development';

NODE_ENV 값을 설정하는 방법
Linux, Mac OS

export NODE_ENV=production

Windows

set NODE_ENV=production

아래 처럼 NODE_ENV 값에 따라 개발과 배포용 소스를 따로 분리해서 작업할 수 있다.

if (process.env.NODE_ENV == 'production') {
  console.log("Production Mode");
} else if (process.env.NODE_ENV == 'development') {
  console.log("Development Mode");
}

config 폴더 안에있는 config.json 에서 env 에 해당하는 오브젝트를 가져와 config 변수에 할당한다.

const config = require(__dirname + '/../config/config.json')[env];

예를들어 env='development' 일경우 아래 오브젝트가 할당된다.

{
    "username": "user_name",
    "password": "database_password",
    "database": "database_name",
  ... 
}

db 변수에 빈 오브젝트를 할당한다.

const db = {};

sequelize 변수를 선언한다. config.use_env_variable 가 존재한다면, 해당 설정으로 시퀄라이즈 인스턴스를 생성한다. 존재하지 않는다면 config 변수에 할당되어있는 오브젝트의 database, username, password 를 기반으로 시퀄라이즈 인스턴스를 생성한다.

let sequelize;
if (config.use_env_variable) {
  sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
  sequelize = new Sequelize(config.database, config.username, config.password, config);
}

fs 모듈의 readdirSync() 메소드를 이용해 인자로 받은 디렉토리의 파일들을 동기적으로 읽는다. filter 반복문을 돌면서 .으로 시작하지 않고 이름이 index.js 가 아니며 .js 확장자를 가진 파일들을 배열로 반환한다. forEach 를이용해 읽어온 js 파일들 하나씩 반복한다. 현재 index.js 가 있는 디렉토리는 models 디렉토리기 때문에 js 파일들은 모두 데이터베이스 모델이라고 가정하고 각 모델들을 모델이름을 key 로 모델을 value 로 db 오브젝트에 집어넣는다.

fs
  .readdirSync(__dirname)
  .filter(file => {
    return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
  })
  .forEach(file => {
    const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
    db[model.name] = model;
  });

반복문을 돌면서 associate() 메서드를 통해 Model 사이의 관계를 정의한다.

Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

모델 정의

데이터베이스 모델을 정의하는 방법에 대해서는 공식문서 번역글 에서 자세하게 다루고 있다.

별도로, npx 를 이용해 생성한 index.js 를 이용하기 위해서는 데이터베이스 모델을 다르게 정의할 필요성이 있다.

위 index.js 에서 다음과 같이 require 로 임포트한 부분을 볼 수 있는데, 각 모델을 정의한 파일에서 export 해온 대상이 오브젝트가 아니라 함수인 것을 유추할 수 있다.

require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);

혹시 이해가 가지 않는다면 아래 의미를 생각해보자. 함수를 require 로 불러오고 실행한 것이다.

require(..)(sequelize, Sequelize.DataTypes) 

따라서 모델을 정의한 js 파일에서 다음과 같이 function 을 export 해준다.

module.exports = function(sequelize, DataTypes){
  ...
} 

모델을 정의하는 방법 중 하나인
define() 을 이용한다. sequelize.define('객체 이름', 스키마 정의, 테이블 설정) 처럼 사용한다. 예를 들기 위해 사용자를 나타내는 user 테이블을 정의해보겠다. sequelize 는 index.js 에서 인자로 전송된 변수로, Sequelize 인스턴스를 담고 있다. DataTypes 는 index.js 에서 인자로 넘겨준 Sequelize.DataTypes 이다.

# user.js
module.exports = function(sequelize, DataTypes){
    let user = sequelize.define("User", {
        userID: {
            filed: "user_id",
            type: DataTypes.STRING(50),
            unique: true,
            allowNull: false
        },
        password: {
            field: "password",
            type: DataTypes.STRING(30),
            allowNull: false
        }
    }, {
        underscored: true,
        freezeTableName: true,
        tableName: "user"
    });
    return user;
}

이렇게 테이블을 정의하면, /models/index.js에서 반복문을 돌면서 models 폴더 내에 있는 파일들을 읽어들여 모델들을 db 오브젝트 안에 취합한다. 그 후 다시 반복문을 돌면서 관계가 정의된다. 따라서, index.js에서 users.js 파일을 읽어들여 모델로 정의되고 관계가 생긴다.

sync() 를 이용해 시퀄라이즈 연결

시퀄라이즈는 데이터 조작과 정의를 지원하기 때문에 이미 만들어진 테이블에 Model을 매핑할 수 있고, DB에 테이블이 없는 상태라면 정의한 Model을 바탕으로 테이블을 생성할 수도 있다.

이를 수행하는 메소드가 sync() 이며, sync()는 모델에서 정의한 이름을 갖는 테이블이 존재하지 않을 경우에만 동작한다. /models/index.js 파일에서 정의한 모델들을 바탕으로 실제로 Model을 등록하는 역할을 한다.

app.js 파일 상단에 다음 코드를 작성한다.

... 
sequelize.sync({force : false})
.then(()=> {
    console.log('database connect');
}).catch((err)=> {
    console.err(err);
});
... 

콘솔에 database connect 가 출력되면 정상적으로 연결된 것이다.

참고
path 관련 -
https://yohanpro.com/posts/js/node-path
Sequelize 환경변수 -
https://yangeok.github.io/node.js/2019/06/11/sequelizerc.html
모델 정의 -
https://analogcoding.tistory.com/125
시퀄라이즈 공식문서 (인스턴스) -
https://sequelize.org/master/class/lib/sequelize.js~Sequelize.html#instance-constructor-constructor
sync 메소드 -
https://victorydntmd.tistory.com/26?category=677306

profile
백엔드와 인프라에 관심이 많은 개발자 입니다

0개의 댓글