본문 바로가기
필수 개발지식/개념정리, 유용한팁

sequelize 이용하여 db, table생성하기

by 코딩하는짱구 2023. 4. 22.
반응형

sequelize 이용하여 db, table생성하기

1. 설계하기 - ERD

 

2. 필요한 라이브러리들, 파일, 폴더를 생성 후 config.json설정 

# 라이브러리를 설치합니다.
npm install express sequelize mysql2 cookie-parser jsonwebtoken

# sequelize-cli, nodemon 라이브러리를 DevDependency로 설치합니다.
npm install -D sequelize-cli nodemon

# 설치한 sequelize를 초기화 하여, sequelize를 사용할 수 있는 구조를 생성합니다.
npx sequelize init

이걸 해야 config, migrations, models등등이 생김 

 

#nodemon 이용하기

npx nodemon app.js

 

aws RDS엔드포인트: express-database.cv64ek1xh9gd.ap-northeast-2.rds.amazonaws.com / 포트: 3306

 

./config/config.json

{
  "development": {
    "username": "root",
    "password": "4321aaaa",
    "database": "sequelize_relation",
    "host": "",
    "dialect": "mysql"
  },
  "test": {
    "username": "root",
    "password": null,
    "database": "database_test",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "production": {
    "username": "root",
    "password": null,
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "mysql"
  }
}

 

# Users 모델
npx sequelize model:generate --name Users --attributes email:string,password:string
# UserInfos 모델
npx sequelize model:generate --name UserInfos --attributes UserId:integer,name:string,age:integer,gender:string,profileImage:string
# Posts 모델
npx sequelize model:generate --name Posts --attributes title:string,content:string
# Comments 모델
npx sequelize model:generate --name Comments --attributes UserId:integer,PostId:integer,comment:string

 

 

실제 환경 구축 화면
comments 기능을 제외한 경우에 테이블의 모습

3. migrations 파일들에 코드를 작성한 후에 아래의 코드로 DB를 생성하고 테이블을 만든다. 

 

# config/config.json에 설정된 DB를 생성합니다. 

npx sequelize db:create 

{
  "development": {
    "username": "root",
    "password": "4321aaaa",
    "database": "sequelize_assignment",
    "host": "express-database.cv64ek1xh9gd.ap-northeast-2.rds.amazonaws.com",
    "dialect": "mysql"
  },

즉 "database"란은 내가 DB를 저장할 저장소의 이름. 

# 해당 프로젝트에 Migrations에 정의된 Posts 테이블을 MySQL에 생성합니다.
npx sequelize db:migrate

DB에 내가 원하는 테이블들이 생성되었다.

 

DB와 테이블들이 정상적으로 설치되었는지 New Query를 열어 desc로 확인해본다.

각 테이블이 잘 생성되었다.

 

4. 이후 models file에 코드를 작성한다. 여기서 models는 테이블 그 자체라고 생각하면 될 듯. 

즉 테이블안의 컬럼 값의 속성을 정해주고, 테이블들 간의 관계성을 설정해주는 것. 

 

ex) models/users.js - 아래처럼 UserInfos 테이블과 1:1 관계를 설정해준다.

'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class Users extends Model {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    static associate(models) {
      // define association here

      // 1. Users 모델에서
      this.hasOne(models.UserInfos, { // 2. UserInfos 모델에게 1:1 관계 설정을 합니다. //this 는 현제 테이블, 즉 User 모델
        sourceKey: 'userId', // 3. Users 테이블의 userId 컬럼을
        foreignKey: 'UserId', // 4. UserInfos 모델의 UserId 컬럼과 연결합니다.
      });
    }
  }

  Users.init(
    {
      userId: {
        allowNull: false, // NOT NULL
        autoIncrement: true, // AUTO_INCREMENT
        primaryKey: true, // Primary Key (기본키)
        type: DataTypes.INTEGER,
      },
      email: {
        allowNull: false, // NOT NULL
        type: DataTypes.STRING,
        unique: true,
      },
      password: {
        allowNull: false, // NOT NULL
        type: DataTypes.STRING,
      },
      createdAt: {
        allowNull: false, // NOT NULL
        type: DataTypes.DATE,
        defaultValue: DataTypes.NOW,
      },
      updatedAt: {
        allowNull: false, // NOT NULL
        type: DataTypes.DATE,
        defaultValue: DataTypes.NOW,
      },
    },
    {
      sequelize,
      modelName: 'Users',
    }
  );
  return Users;
};

 

models/userinfos.js 

'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class UserInfos extends Model {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    static associate(models) {
      // define association here

      // 1. UserInfos 모델에서
      this.belongsTo(models.Users, {
        // 2. Users 모델에게 1:1 관계 설정을 합니다.
        targetKey: 'userId', // 연결될 컬럼을 입력
        foreignKey: 'UserId', // 4. UserInfos 모델의 UserId 컬럼과 연결합니다.
      });
    }
  }

  UserInfos.init(
    {
      userInfoId: {
        allowNull: false, // NOT NULL
        autoIncrement: true, // AUTO_INCREMENT
        primaryKey: true, // Primary Key (기본키)
        type: DataTypes.INTEGER,
      },
      UserId: {
        allowNull: false, // NOT NULL
        type: DataTypes.INTEGER,
        unique: true, // UNIQUE
      },
      name: {
        allowNull: false, // NOT NULL
        type: DataTypes.STRING,
      },
      age: {
        allowNull: false, // NOT NULL
        type: DataTypes.INTEGER,
      },
      gender: {
        allowNull: false, // NOT NULL
        type: DataTypes.STRING,
      },
      profileImage: {
        type: DataTypes.STRING,
      },
      createdAt: {
        allowNull: false, // NOT NULL
        type: DataTypes.DATE,
        defaultValue: DataTypes.NOW,
      },
      updatedAt: {
        allowNull: false, // NOT NULL
        type: DataTypes.DATE,
        defaultValue: DataTypes.NOW,
      },
    },
    {
      sequelize,
      modelName: 'UserInfos',
    }
  );
  return UserInfos;
};

 

Users모델은 posts와도 연결되기 때문에 1:N관계를 추가해준다.

'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class Users extends Model {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    static associate(models) {
      // define association here

      // 1. Users 모델에서
      this.hasOne(models.UserInfos, {
        // 2. UserInfos 모델에게 1:1 관계 설정을 합니다. //this 는 현제 테이블, 즉 User 모델
        sourceKey: 'userId', // 3. Users 테이블의 userId 컬럼을
        foreignKey: 'UserId', // 4. UserInfos 모델의 UserId 컬럼과 연결합니다.
      });

      // 1. Users 모델에서
      this.hasMany(models.Posts, {
        // 2. Posts 모델에게 1:N 관계 설정을 합니다.
        sourceKey: 'userId', // 3. Users 모델의 userId 컬럼을
        foreignKey: 'UserId', // 4. Posts 모델의 UserId 컬럼과 연결합니다.
      });
    }
  }

  Users.init(
    {
      userId: {
        allowNull: false, // NOT NULL
        autoIncrement: true, // AUTO_INCREMENT
        primaryKey: true, // Primary Key (기본키)
        type: DataTypes.INTEGER,
      },
      email: {
        allowNull: false, // NOT NULL
        type: DataTypes.STRING,
        unique: true,
      },
      password: {
        allowNull: false, // NOT NULL
        type: DataTypes.STRING,
      },
      createdAt: {
        allowNull: false, // NOT NULL
        type: DataTypes.DATE,
        defaultValue: DataTypes.NOW,
      },
      updatedAt: {
        allowNull: false, // NOT NULL
        type: DataTypes.DATE,
        defaultValue: DataTypes.NOW,
      },
    },
    {
      sequelize,
      modelName: 'Users',
    }
  );
  return Users;
};

 

models/posts.js 를 Users와 N:1의 관계를 설정해준다.

'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class Posts extends Model {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    static associate(models) {
      // define association here

      // 1. Posts 모델에서
      this.belongsTo(models.Users, {
        // 2. Users 모델에게 N:1 관계 설정을 합니다.
        targetKey: 'userId', // 3. Users 모델의 userId 컬럼을
        foreignKey: 'UserId', // 4. Posts 모델의 UserId 컬럼과 연결합니다.
      });
    }
  }

  Posts.init(
    {
      postId: {
        allowNull: false, // NOT NULL
        autoIncrement: true, // AUTO_INCREMENT
        primaryKey: true, // Primary Key (기본키)
        type: DataTypes.INTEGER,
      },
      UserId: {
        allowNull: false, // NOT NULL
        type: DataTypes.INTEGER,
      },
      title: {
        allowNull: false, // NOT NULL
        type: DataTypes.STRING,
      },
      content: {
        allowNull: false, // NOT NULL
        type: DataTypes.STRING,
      },
      createdAt: {
        allowNull: false, // NOT NULL
        type: DataTypes.DATE,
        defaultValue: DataTypes.NOW,
      },
      updatedAt: {
        allowNull: false, // NOT NULL
        type: DataTypes.DATE,
        defaultValue: DataTypes.NOW,
      },
    },
    {
      sequelize,
      modelName: 'Posts',
    }
  );
  return Posts;
};

 

 

**DB를 삭제하는 명령어 npx sequelize db:drop

**DB를 생성하는 명령어 npx sequelize db:create

 

5. DB, migrations, models(tables)을 생성하고 연결관계를 만들어 준 후에 app.js, routes들을 설정한다

// app.js

const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
const PORT = 3018;

app.use(express.json());
app.use(cookieParser());
app.use('/api', []);

app.listen(PORT, () => {
  console.log(PORT, '포트 번호로 서버가 실행되었습니다.');
});

 

email, password은 body 형태로 Users 테이블에 들어갈 것이고,

name, age, gender, profileImage는 body 형태로 UserInfos 테이블에 들어갈 것이다. 

 

어떤 정보가 어디로 들어갈지 생각을 한 후에 router를 구성해준다!

 

userInfo는 user의 id값을 기반으로 만들어진다는 점 인지.

// routes/users.route.js
const express = require('express');
const { Users, UserInfos } = require('../models');
const jwt = require('jsonwebtoken');
const router = express.Router();

// 회원가입
router.post('/users', async (req, res) => {
  try {
    const {
      email,
      password,
      confirmPassword,
      name,
      age,
      gender,
      profileImage,
    } = req.body;

    //동일한 가입자가 있는지 email을 통해 확인한다.
    const isExistUser = await Users.findOne({ where: { email } });

    if (isExistUser) {
      return res.status(409).json({ message: '이미 존재하는 이메일입니다.' });
    }

    //닉네임 길이 제한
    if (name.length < 3) {
      res
        .status(412)
        .json({ errorMessage: '닉네임 형식이 일치하지 않습니다.' });
      return;
    }

    //닉네임 형식
    const nameRegex = /^[a-zA-Z0-9]+$/;
    if (!nameRegex.test(name)) {
      res
        .status(412)
        .json({ errorMessage: '닉네임 형식이 일치하지 않습니다.' });
      return;
    }

    //닉네임 4자리 이상, 닉네임과 같은 값 포함
    if (password.length < 4 || password.includes(name)) {
      res
        .status(412)
        .json({ errorMessage: '패스워드에 닉네임이 포함되어있습니다.' });
      return;
    }

    //비번 재확인
    if (password !== confirmPassword) {
      res.status(412).json({
        errorMessage: '확인용 패스워드가 일치하지 않습니다.',
      });
      return;
    }
    //Users 테이블에 사용자를 추가합니다.

    const user = await Users.create({ email, password });
    // UserInfos 테이블에 사용자 정보를 추가합니다.
    const userInfo = await UserInfos.create({
      UserId: user.userId, // 생성한 유저의 userId를 바탕으로 사용자 정보를 생성합니다.
      name,
      age,
      gender: gender.toUpperCase(), // 성별을 대문자로 변환합니다.
      profileImage,
    });

    return res.status(201).json({ message: '회원가입이 완료되었습니다.' });
  } catch (error) {
    console.error(error);
    return res.status(400).json({ errorMessage: '회원가입에 실패하였습니다.' });
  }
});

// 로그인
router.post('/login', async (req, res) => {
  try {
    const { email, password } = req.body;
    //사용자가 존재하는지 찾아보자
    const user = await Users.findOne({ where: { email } });
    if (!user) {
      return res
        .status(401)
        .json({ message: '닉네임 또는 패스워드를 확인해주세요.' });
    } else if (user.password !== password) {
      return res.status(401).json({ message: '비밀번호가 일치하지 않습니다.' });
    }

    //jwt를 생성하고
    const token = jwt.sign({ userId: user.userId }, 'customized_secret_key'); //user안에 있는 userId,
    //쿠키를 발급
    res.cookie('authorization', `Bearer ${token}`);
    //response할당
    return res.status(200).json({ message: '로그인 성공' });
  } catch (error) {
    console.error(error);
    return res.status(400).json({ message: '서버 에러' });
  }
});

// 사용자 조회
router.get('/users/:userId', async (req, res) => {
  const { userId } = req.params;

  const user = await Users.findOne({
    where: { userId },
    attributes: ['userId', 'email', 'createdAt', 'updatedAt'],
    include: [
      {
        model: UserInfos, // 1:1 관계를 맺고있는 UserInfos 테이블을 조회합니다.
        attributes: ['name', 'age', 'gender', 'profileImage'],
      },
    ],
  });

  return res.status(200).json({ data: user });
});

module.exports = router;

 

 

반응형