210603 Nodejs + Sequelize + MySQL

Nodejs + MySQL

Express.js 사용의 이유

아래와 같이 Node에서 제공하는 http 모듈 패키지를 사용해서 서버를 구현할 수 있다.
하지만 체계적으로 파일을 분리해서 관리하기에는 다소 어려움이 있다.
POST 방식의 처리와 GET 방식의 처리를 하기 위해서 req.method로 조건분기 처리를 하고, 그 안에서도 req.url을 통해서 요청받은 url을 구분해야 되기 때문에 조건처리하는 부분의 코드가 길어지게 되고, 코드의 가독성도 좋지 않다.

1
2
3
4
5
6
7
8
9
10
const http = require('http');
const server = http.createServer((req, res) => {
console.log(req.url, req.method);
res.write('<h1>SERVER TEST</h1>');
res.end('<h1>TEST END</h1>');
});

server.listen(3065, () => {
console.log('Server is running');
});

이러한 이유로 인해 Node.js의 프레임워크인 Express.js를 사용해서 서버를 구현하는 것이다.

head와 body

body는 res.json 또는 res.send로 보내지는 부분이다. head는 이 body(요청에 대한 응답)에 대한 부가적인 정보를 담고 있다.
(예를들어, 응답의 날짜, 데이터 타입, 용량 등의 정보를 담고 있다.)

Swagger API Documentation

Seagger 툴을 사용해서 API를 문서화할 수 있다.

[참고] : https://swagger.io/

REST API 방식

100% REST 방식을 지켜서 작성하기 어렵다. 어느정도 협상을 통해 REST 방식으로 API를 작성하도록 한다.

  • get : 취득
  • post : 생성
  • put : 전체 수정(통째로 덮어쓰기)
  • delete : 제거
  • patch : 부분 수정
  • options : 서버에 요청에 대한 확인
  • head : header 정보 취득

MySQL8

(1) MySQL Community server 설치
(2) MySQL workbench 설치 (terminal 말고 시각화해서 내부 데이터베이스 확인)

sequelize와 mysql2 driver 설치 및 설정

1
$ npm i sequelize sequelize-cli mysql2
  • mysql2 : node.js와 mysql을 연결해주는 드라이버

  • sequelize : 직접 query를 작성해주지 않아도 javascript를 sql로 변환해주는 라이브러리

1
2
# 기본 sequelize 설정
$ npx sequelize init
  • 설정 순서

    npx sequelize init으로 초기화를 시켜주면, 프로젝트 내에 config, migrations, models, seeders폴더가 자동생성된다.


  • (1) config/config.json

    개발, 테스트, 배포 모드에 따라 사용되는 database를 다르게 설정할 수 있다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    {
    "development": {
    "username": "root",
    "password": "1q2w3e4r",
    "database": "development-db",
    "host": "127.0.0.1",
    "dialect": "mysql"
    },
    "test": {
    "username": "root",
    "password": null,
    "database": "test-db",
    "host": "127.0.0.1",
    "dialect": "mysql"
    },
    "production": {
    "username": "root",
    "password": null,
    "database": "production-db",
    "host": "127.0.0.1",
    "dialect": "mysql"
    }
    }
  • (2) models/index.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    const Sequelize = require('sequelize');
    // 별도로 env.NODE_ENV가 선언되어 있지 않으면 'development'로 초기화한다.
    const env = process.env.NODE_ENV || 'development';
    // config.json을 호출해서 [development]에 해당하는 설정부분 config 변수에 초기화한다.
    const config = require('../config/config')[env];

    const db = {};

    // Sequelize는 Node.js와 mysql을 연결해주는 역할을 한다.
    // Sequelize가 내부적으로 mysql2 driver를 사용하기 때문에 아래와 같이 연결에 필요한 정보를 인수로 넘겨서 Node.js와 mysql을 연결시켜주게 되는 것이다.
    const sequelize = new Sequelize(
    config.database,
    config.username,
    config.password,
    config
    );

    // 이하 설정부분은 기존에 자동으로 생성된 설정부분

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

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

    module.exports = db;
  • (3) DB 테이블 생성 / Sequelize model 생성 (models/)

    sequelize에서는 table을 model이라고 한다.
    model이 다른 model과 관련이 되어 있는 경우, model의 associate 부분에 정의를 해준다.
    우선적으로 독립적으로 분리할 수 있는 테이블들을 model로 정의를 해주고 나중에 관계를 엮을 부분을 associate에 정의하면 된다.

    독립적으로 분리된 상태의 sequelize model 작성

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    module.exports = (sequelize, DataTypes) => {
    // User : model name (mysql에서는 users로 자동변환되어 테이블이 생성된다. - 소문자로 변환되고 복수 이름으로 변환)
    const User = sequelize.define(
    'User',
    // 첫 번째 객체의 정보는 테이블의 column 정보
    {
    // 기본적으로 id가 column이 생성된다.
    email: {
    // 데이터 타입에는 STRING, TEXT, BOOLEAN, INTEGER, FLOAT, DATETIME가 있다.
    type: DataTypes.STRING(30),
    allowNull: false, // 필수
    unique: true
    },
    nickname: {
    type: DataTypes.STRING(30),
    allowNull: false
    },
    password: {
    // PASSWORD는 암호화를 하게 되면 길이가 늘어나기 때문에 100
    type: DataTypes.STRING(100),
    allowNull: false
    }
    },
    // 두번째 객체는 User model에 대한 setting 정보를 넣어준다.
    {
    charset: 'utf8',
    collate: 'utf8_general_ci' //한글 저장
    }
    );
    // model 간의 관계를 정의하는 부분
    User.associate = (db) => {};
    return User;
    };

    위와같이 작성을 해주면, sequelize가 자동으로 MySQL에 테이블을 만들어준다.

    Sequelize model 간의 관계 설정하기

    • 1:N : hasMany() / belongsTo() - 설정한 부분에 model의 id column이 생성된다.

      • 한 명의 User가 여러 개의 게시글을 작성
    • N:N : belongsToMany()
      다대다(N:N)로 정의를 하게 되면, 두 개의 model을 mapping 시키는 mapping table이 새롭게 생성이 된다. mapping table의 이름은 자동으로 부여되지만, through 속성을 통해 지정을 할 수 있으며, as 속성을 통해서 어떤 정보를 담고 있는지 구분할 수 있다.

      • 하나의 Hash tag에는 여러 개의 게시글이 연관, 하나의 게시글에 여러 개의 Hash tag가 포함
      • 사용자와 사용자 (팔로우)
      • 사용자와 게시물의 좋아요 관
    • 1:1 : hasOne() / belongsTo()

      • 사용자와 사용자 정보 관계

        1
        2
        3
        db.User.hasOne(db.UserInfo);

        db.UserInfo.belongsTo(db.User);

    models/user.js

    1
    2
    3
    4
    5
    6
    7
    8
    User.associate = (db) => {
    // User: Post = 1: N
    db.User.hasMany(db.Post);
    db.User.hasMany(db.Comment);
    // 사용자 - 좋아요 (through를 통해 중간 테이블 이름 지정)
    // post.getLikers 로 게시글 좋아요 누른 사람에 대한 정보를 가져올 수 있다.
    db.User.belongsToMany(db.Post, { through: 'Like', as: 'Likers' });
    };

    models/post.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Post.associate = (db) => {
    db.Post.belongsTo(db.User);
    // 다대다 관계 (N:N)
    db.Post.belongsToMany(db.Hashtag);
    db.Post.hasMany(db.Comment);
    db.Post.hasMany(db.Image);
    // 사용자 - 좋아요 (through를 통해 중간 테이블 이름 지정)
    // 아래와같이 as로 지정해주면,
    // post.getLikers 로 게시글 좋아요 누른 사람에 대한 정보를 가져올 수 있다.
    db.Post.belongsToMany(db.User, { through: 'Like', as: 'Likers' });
    };
  • belongsTo와 hasMany의 역할

    belongsTo로 associate 내에 선언을 해주게 되면, 해당 model의 column에 belongsTo로 선언된 model의 id column이 생성된다.

    1
    2
    3
    4
    Comment.associate = (db) => {
    db.Comment.belongsTo(db.User); // UserId: {}
    db.Comment.belongsTo(db.Post); // PostId: {}
    };

database 생성하기

1
$ npx sequelize db:create