본문 바로가기
포스코x코딩온

[포스코x코딩온] 풀스택 부트캠프 7주차 정리 1 - Mysql연동, sequelize

by 김선지 2023. 12. 5.

mysql

서버측에서 데이터베이스 중 하나인 mysql을 이용할 수 있게 하는 3 party pakage이다.

사용법은 아래 코드 참조.

아래서 볼 수 있듯이 연결된 DB 객체를 바탕으로 query라는 method를 이용해서 쿼리문을 그대로 적어주는 것이 문법이다.

다만 require("mysql2")로 import를 하면 콜백함수만 쓸 수 있고, promise는 사용할 수 없다.

 

콜백함수를 사용하는 이유 - (데이터베이스에 연결하는 것은 시간이 걸리는 작업이기 때문이다.(비동기),

밑에 있는 코드가 만일 데이터베이스의 결과를 이용하는 구문이라면, 즉 쿼리문이 실행되고 나서 실행되어야 하는 동작이라면, 해당 구문이 완료되기를 기다려야 하기 때문에 콜백함수를 이용한다.)

const mysql = require("mysql2");

// DB연결
const conn = mysql.createConnection({
  host: "localhost",
  user: "user",
  password: "1234",
  database: "kdt",
});

// select 
function getVisitors(cb) {
  conn.query(`SELECT * FROM visitor`, function (err, rows) {
    if (err) {
      throw err;
    }

    console.log("Visitor.js>", rows);
    /*
    Cvisitor.js> [
  { id: 1, name: '홍길동', comment: '내가 왔다' },
  { id: 2, name: '이찬혁', comment: '으라차차' }
]
        */
    cb(rows);
  });
}

// insert 
function postVisitor(data, cb) {
  console.log(data);
  const sql = 'INSERT INTO visitor (name, comment) VALUES (?, ?)';
  const values = [data.name, data.comment];
  conn.query(sql, values, (err, rows) => {
    if(err) throw err;

    console.log('Visitor.js>', rows)

    cb(rows.insertId); // rows.insertId의 정보를 controller에 넘겨줌.
  })
}

// update
function patchVisitor(data, cb) {
  const sql = 'UPDATE visitor SET name = ?, comment = ? where id= ?';
  conn.query(sql, [data.name, data.comment, data.id], function (err, rows) {
    if (err) {
      throw err
    }

    console.log('patchVisitor getVisitor.js >', rows);
    cb(rows);
  });
}

// delete 
function deleteVisitor(id, cb) {
  const sql = 'DELETE FROM visitor where id = ?'
  conn.query(sql, [id], function(err, rows) {
    if (err) {
      throw err
    }

    console.log('deleteVisitor visitor.js>', rows);
    cb(rows);
  });
}

 

mysql/promise

mysql에서 promise를 이용하고 싶다면 import와 createconnection이라는 method를 createpool로 바꿔주면 된다.

주의할 점은 받아온 promise 객체는 바로 사용할 수 없다는 점이다.

구조분해 할당을 한번 해 주어야 배열로 이용할 수 있다.

// 다른점 1
const mysql = require("mysql2/promise");

// DB연결
const conn = mysql.createPool({
  host: "localhost",
  user: "user",
  password: "1234",
  database: "kdt",
});

async function insertUser(data) {
    const sql = 'INSERT INTO USER (userid, name, pw) VALUES (?, ?, ?)'
    const userInfo = [
        data.id,
        data.name,
        data.pw,
    ]
    // 다른점 2
    const [result] = await conn.query(sql, userInfo)
    return result;
}

async function postLogin(data, res) {
    const sql = 'SELECT * FROM USER where userid = ?'
    try {
        const [result] = await conn.query(sql, [data.id]);

        if(result[0].pw == data.pw) {
            res.render('update', {userInfo: result[0]});
            return;
        } else {
            return ['아이디 혹은 비밀번호가 달라요.'];
        }
    }catch {
        return ['아이디가 없습니다.'];
    }
}

 


sequelize

sql의 원래 뜻인 sequel에서 차용해서 sequelize라고 한 3 party pakage인 듯하다.

mysql을 더 쉽게 이용할 수 있다. (promise 등 이용 가능)

이용하려면 import  이후 테이블 구조를 정의하고 전체 스키마와 전체 모델 구조를 정의해야한다.

// json파일. 파일명은 config.json
{
// 개발용 환경설정
    "development": {
        "username": "user",
        "password": "1234",
        "database": "kdt",
        "host": "127.0.0.1",
        "dialect": "mysql"
    },
    // 다른 환경설정이 필요할 시 적음
    "production": {},
    "test": {}
}

 

개별 테이블, 모델 구조 정의

// Visitor.js 파일
// TODO: visitor 모델 정의 
// 테이블 구조를 정의한다 라고 생각
// 시퀄라이즈 모델이랑 mysql 테이블 연결
const Visitor = function(Sequelize, DataTypes) {
    // Sequelize는 models/index.js에서의 sequelize (인스턴스(객체))
    // DataTypes는 models/index.js에서의 Sequelize (클래스 (객체청사진))

    const model = Sequelize.define(
        'visitor', // param1: 모델 이름 설정,, (1) -> !시퀄라이즈 에서의 모델명을 뜻함...! DB의 모델명은 (2)번 table name
        {   // id int AI PK
            id: {
                type: DataTypes.INTEGER,
                allowNull: false,
                primaryKey: true,
                autoIncrement: true
            },
            name: {
                // name: VARCHAR(10) NOT NULL
                type: DataTypes.STRING(10),
                allowNull: false
            },
            comment: {
                type: DataTypes.TEXT('medium')
            }
        }, // param2: 컬럼 정의
        {
            tableName: 'visitor', // 실제 DB에 있는 테이블 이름 명시  (2) 얘만 있어도 됨.
            freezeTableName: true, // 첫 번째 인자로 넘겨준 모델 이름을 그대로 테이블 이름으로 고정 (3): 2가 없을 시 1과 3만으로 테이블 생성 가능, 직접적으로 DB이름을 정하는 것은 2
            // 시퀄라이즈는 기본적으로 테이블 이름을 모델 + s로 가져감
            // charset, collate 값이 있는데 db 정의할 때 한글 인코딩 가능하도록 만들어 뒀기 때문에 따로 설정 불요

            timestamps: false
            // - timestamps: 데이터가 추가되고 수정된 시간을 자동으로 컬럼으로 만들어서 기록하는 옵션
            // true로 설정시 컬럼에 시간이 추가된다.
        } // param3: 모델 옵션 정의
    )
    return model;
}

module.exports = Visitor;

 

전체 모델 구조 정의

 

// index.js
// 전체 모델에 대한 정의
const Sequelize = require('sequelize');
// json에 있는 환경설정 가져오기. ['development']는 .표기법이 아닌 []표기법 이용
const config = require(__dirname + '/../config/config.json')['development'];

console.log('config > ', config);
/*
config >  {
  username: 'user',
  password: '1234',
  database: 'kdt',
  host: '127.0.0.1',
  dialect: 'mysql'
}
*/

const db = {};
// 시퀄라이즈 객체 선언 시에 매개변수로 다음 정보들을 받음.
const sequelize = new Sequelize(
    config.database,
    config.username,
    config.password,
    config
)

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

db.Visitor = require('./Visitor')(sequelize, Sequelize); // 첫번째 인자: 인스턴스, 두번째 인자: 객체 청사진(class)
// models/Visitor.js에서 정의한 모델이 db.Visitor 에 들어감
// db = { sequelize: sequelize, Sequelize: Sequelize, Visitor: ~~~}

module.exports = db;
// db라는 변수는 내보냄
// 시퀄라이즈 객체를 만들고 모듈로서 내보내서 "다른 파일에서 모두 같은 객체를 사용할 수 있게 하는 것"

 

app.js

const express = require('express');
const app = express();
const db = require('./models/index');


const router = require('./routes/index');
app.set('view engine', 'ejs');
// app.set('views', './views');
app.set('views', (__dirname + '/views'));
app.use( express.static('static'));
app.use(express.urlencoded( { extended: true}));
app.use(express.json());

app.use(router);

app.get('*', function(req, res) {
    res.status(404).render('404');
})

db.sequelize.sync({force: false}).then(function() {
    // force: false => 테이블이 없으면 생성. 
    // force: true => 테이블 무조건 생성 (만약 DB가 있다면 다 삭제하고 다시 생성 -> prod에서 사용)
    app.listen(8000);
})

 

1:1, 1:다 등 테이블 간의 관계 설정하기.

// 1) Player : Profile = 1: 1
// 한 선수당 하나의 프로필을 가지도록 함
Player.hasOne( Profile, { foreignKey: 'player_id',
sourceKey: 'player_id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'});

Profile.belongsTo( Player, {
    foreignKey: 'player_id',
    targetKey: 'player_id'
})

// 2) Team : Player = 1: N
// 한 팀에는 여러 선수가 존재
Team.hasMany(Player, {foreignKey: 'team_id',
sourceKey: 'team_id'});
Player.belongsTo( Team, {
    foreignKey: 'team_id',
    targetKey: 'team_id'
})

// TODO: 관계를 정의한 모델들을 db 객체에 저장
db.sequelize = sequelize;
db.Sequelize = Sequelize;
db.Player = Player;
db.Profile = Profile;
db.Team = Team;
module.exports = db;