7.1 데이터베이스란?

관련성을 가지며 중복이 없는 데이터들의 집합.
DBMS : 데이터베이스를 관리하는 시스템(DataBase Management System)
RDBMS : 관계형 데이터베이스 관리 시스템(Oracle, MySQL, MSSQL, PostgreSQL...)

7.2 MySQL 설치하기

$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

$ brew install mysql
$ brew services start mysql
$ mysql_secure_installation

$ mysql -h localhost -u root -p

7.3 워크벤치 설치하기

워크벤치는 데이터를 시각적으로 편리하게 관리하기 위한 프로그램
https://dev.mysql.com/downloads/workbench/ 를 통해 설치

7.4 데이터베이스 및 테이블 생성하기

7.4.1 mysql 접속 및 데이터베이스 생성하기

$ mysql -h localhost -u root -p

mysql > CREATE SCHEMA nodejs;
mysql > use nodejs;

세미콜론(;) 필수!
CREATE_SCHEMA 같은 예약어는 되도록이면 대문자로 입력 권장
nodejs 같은 사용자 정의 이름과 구분하기 위함

7.4.2 테이블 생성하기

mysql > CREATE TABLE nodejs.users(
    -> id INT NOT NULL AUTO_INCREMENT,
    -> name VARCHAR(20) NOT NULL,
    -> age INT UNSIGNED NOT NULL,
    -> married TINYINT NOT NULL,
    -> comment TEXT NULL,
    -> created_at DATETIME NOT NULL DEFAULT now(),
    -> PRIMARY KEY(id),
    -> UNIQUE INDEX name_UNIQUE (name ASC))
    -> COMMENT = '사용자 정보'
    -> DEFAULT CHARSET=utf8
    -> ENGINE=InnoDB;
  • INT : 정수를 의미함(소수까지 저장하고 싶다면 FLOAT, DOUBLE 자료형 사용)

  • CHAR(자릿수) : 반드시 자릿수의 문자열을 넣어줘야함

  • VARCHAR(자릿수) : 자릿수에 10을 입력시 0~10 길이의 문자열 입력 가능

  • TEXT : 몇백자 이내의 문자열은 VARCHAR로 처리하고, 그 이상일 경우에 TEXT 사용

  • TINYINT : -127부터 128까지의 정수를 저장할 때 사용. 0 또는 1만 사용시 boolean 값과 같은 역할을 할 수 있음

  • NULL : 빈칸 허용, NOT NULL 시 빈칸 불가하므로 데이터 입력 필수

  • AUTO_INCREMENT : 알아서 1부터 숫자를 저절로 올림

  • UNSIGNED : 숫자 자료형에 적용되는 옵션. 적용시 음수 입력 불가

  • ZEROFILL : 숫자의 자릿수가 고정되어 있을 때 사용.
    ex) INT(4) -> 숫자 1을 입력시 0001이 됨

  • now() : 현재 시각을 넣으라는 뜻 CURRENT_TIMESTAMP과 동일

  • PRIMARY KEY : 해당 로우(raw)를 대표하는 고유한 값(기본 키)

  • UNIQUE INDEX : 해당 값이 고유해야 하는지에 대한 옵션

  • COMMENT : 테이블에 대한 보충 설명(필수아님)

  • DEFAULT CHARSET : utf8 미설정시 한글 입력 불가하므로 반드시 설정!

  • ENGINE : 여러가지가 있지만, MylSAM과 InnoDB가 제일 많이 사용됨.

만들어진 테이블 확인 명령어

mysql > DESC users;

테이블 제거

mysql > DROP TABLE users;

사용자 댓글 저장 테이블 생성

mysql > CREATE TABLE nodejs.comments(
    -> id INT NOT NULL AUTO_INCREMENT,
    -> commenter INT NOT NULL,
    -> comment VARCHAR(100) NOT NULL,
    -> created_at DATETIME NOT NULL DEFAULT now(),
    -> PRIMARY KEY(id),
    -> INDEX commenter_idx(commenter ASC),
    -> CONSTRAINT commenter
    -> FOREIGN KEY (commenter)
    -> REFERENCES nodesjs.users (id)
    -> ON DELETE CASCADE
    -> ON UPDATE CASCADE)
    -> COMMNET = '댓글'
    -> DEFAULT CHARSET=utf8
    -> ENGINE=InnoDB;

commenter 컬럼에는 댓글을 작성한 사용자의 id를 저장
외래키(forign key) : 다른 테이블의 기본 키를 저장하는 컬럼

CONSTRAINT [제약조건명]
FOREIGN KEY [컬럼명]
REFERENCES [참고하는 컬럼명]
위의 명령어를 통해 다른 테이블의 컬럼값 참조 가능
ON UPDATE, ON DELETE를 CASCADE로 설정함으로써, 사용자 정보 수정 여부에 따라 그와 관련된 댓글 정보도 수정하거나 삭제됨

7.5, 7.6 sequelize를 통하여 CRUD 작업하기

sequalize.js는 node.js 에서 MySQL 작업을 쉽게 할 수 있도록 도와주는 라이브러리
sequelize는 ORM 으로 분류됨
ORM은 자바스크립트 객체와 데이터베이스의 릴레이션을 매핑해주는 도구

MySQL이외에도 MariaDB, PostgreSQL, SQLite, MSSQL 등 다른 데이터베이스도 사용가능

새 프로젝트 생성 후 sequelize 설치

npx express learn-sequelize --view=pug
cd learn-sequelize
npm install
npm install sequelize mysql2

npm install -g sequelize-cli
sequelize init

models 폴더가 생성됨. 필요 없는 부분도 많아 다음과 같이 수정

models/index.js

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

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

const sequelize = new Sequelize(config.database, config.username, config.password, config);

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

module.exports = db;

7.6.1 MySQL 연결하기

app.js

...
const sequelize = require('./models).sequelize;

sequelize.sync();
...

sync 메서드 사용하면 서버 실행 시 알아서 MySQL과 연동

7.6.2 모델 정의하기

models/user.js

module.exports = (sequelize, DataTypes) => {
    return sequelize.define('user', {
        name: {
            type: DataTypes.STRING(20),
            allowNull: false,
            unique: true,
        },
        age: {
            type: DataTypes.INTEGER.UNSIGNED,
            allowNull: false,
        },
        married: {
            type: DataTypes.BOOLEAN,
            allowNull: false,
        },
        comment: {
            type: DataTypes.TEXT,
            allowNull: true,
        },
        created_at: {
            type: DataTypes.DATE,
            allowNull: false,
            defaultValue: sequelize.literal('now()'),
        },
    }, {
        timestamps: false,
    });
};
  • sequelize.js 는 id 를 기본 키로 연결해 주므로 컬럼 추가 불필요
  • VARCHAR는 STRING
  • INT는 INTEGER
  • TINYINT는 BOOLEAN
  • DATETIME은 DATE
  • INTEGER.UNSIGNED는 UNSIGNED 옵션이 적용된 INT
  • allowNull은 NOT NULL
  • timestamps 속성이 true일시 createdAt, updatedAt 컬럼 자동 추가

models/comment.js

module.exports = (sequelize, DataTypes) => {
    return sequelize.define('comment', {
        comment: {
            type: DataTypes.STRING(100),
            allowNull: false,
        },
        created_at: {
            type: DataTypes.DATE,
            allowNull: true,
            defaultValue: sequelize.literal('now()'),
        },
    }, {
        timestamps: false,
    });
};

models/index.js

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

db.User = require('./user')(sequelize, Sequelize);
db.Comment = require('./comment')(sequelize, Sequelize);

module.exports = db;
...

config/config.json

{
    "development": {
        "username": "root",
        "password": "[root 비밀번호]",
        "database": "nodejs",
        "host": "127.0.0.1",
        "dialect": "mysql",
        "operatorsAliases": false
    },
    ...
}

password 에는 mysql 비밀번호 입력
operatorsAliases는 보안에 취약한 연산자를 사용할지 여부이므로 false 입력!

7.6.3 관계 정의하기

7.6.3.1 1:N

한 명의 사용자는 여러개의 댓글 작성 가능
하지만 댓글은 한명의 사용자만 참조가능
hasMany로 내보내줌
belongsTo로 가져옴

models/index.js

...
db.User.hasMany(db.Comment, {foreignKey: 'commenter', sourceKey: 'id'});
db.Comment.belongsTo(db.User, {foreignKey: 'commenter', targetKey: 'id'});

module.exports = db;

7.6.3.2 1:1

hasOne 사용

db.User.hasOne(db.Info, { foreignKey: 'user_id', sourceKey: 'id'});
db.Info.belongsTo(db.User, { foreignKey: 'user_id', targetKey: 'id'});

7.6.3.3 N:M

belongsToMany 사용

db.Post.belongsToMany(db.Hashtag, { through: 'PostHashtag' });
db.Hashtag.belongsToMany(db.Post, { through: 'PostHashtag' });

N:M 관계 특성상 새로운 모델이 생성됨

7.6.4 쿼리 알아보기

sequelize.js 쿼리를 통한 CRUD 작업
반드시 sequelize에서 정의한 자료형대로 넣어줘야됨

  • 로우 생성

    INSERT INTO nodejs.users (name, age, married, comment) VALUES ('zero', 24, 0, '자기소개1');
    const { User } = require('../models')
    User.create({
      name: 'zero',
      age: 24,
      married: false,
      comment: '자기소개1',
    });
    
  • users 테이블의 모든 데이터 조회

    SELECT * FROM nodejs.users;
    User.findAll({});
    
  • Users 테이블의 데이터 하나만 가져오는 SQL문

    SELECT * FROM nodejs.users LIMIT 1;
    user.find({});
    
  • attributes 를 통해 원하는 컬럼만 가져올 수 있음

    SELECT name, married FROM nodejs.users;
    User.findAll({
      attributes: ['name', 'married'],
    });
    
  • where 옵션을 통해 조건 나열

    SELECT name, age FROM nodejs.users WHERE married = 1 AND age > 30;
    const { User, Sequelize: { OP } } = require('../models');
    User.findAll({
      attributes: ['name', 'age'],
      where: {
          married: 1,
          age: { [Op.gt]: 30 },
      },
    });
    

    결괏값 : 기혼자 30살을 넘은 유저들의 이름과 나이만 가져옴

연산자

  • Op.gt : 초과
  • Op.gte : 이상
  • Op.lt : 미만
  • Op.lte : 이하
  • Op.ne : 같지 않음
  • Op.or : 또는
  • Op.in : 배열 요소 중 하나
  • Op.notIn : 배열 요소와 모두 다름
  • 로우 수정

    UPDATE nodejs.users SET comment = '바꿀 내용' WHERE id = 2;
    User.update({
      comment: '바꿀 내용',
    }, {
      where: { id: 2 },
    })
    
  • 로우 삭제

    DELETE FROM nodejs.users WHERE id = 2;
    User.destroy({
      where: { id: 2 },
    });