본문 바로가기

Node.js

Node.js(18) - MySQL 제약조건, Foreign Key 추가 방법, JOIN, sequelize를 활용한 코드 구현

728x90
반응형

1) Node.js

   1-1) MySQL 제약조건

   1-2) Foreign Key(외래키) 추가 방법

   1-3) JOIN

   1-4) sequelize를 활용한 코드 구현

 

 

 

 

 

 

 

1) Node.js

1-1) MySQL 제약조건

기본적으로 제약조건은 세가지가 있는데 "기본키", "고유키", "외래키"이다!!

 

(1) 기본키(Primary Key)

  • 중복되지 않는 고유값
  • Null을 허용하지 않음
  • 테이블 당 하나의 기본키만 지정 가능함

(2) 고유키(Unique)

  • 중복되지 않는 고유값
  • Null을 허용함

(3) 외래키(Foreign Key)

자식 테이블에게 field 하나를 추가하는데 이때 추가하는 field는 부모 테이블의 고유한 식별자(Foreign Key)를 추가하면 됨

 

1:1

-->  User 테이블의 데이터 중 회원들로부터 꼭 받지 않아도 되는 데이터의 경우,

따로 User 상세 테이블을 생성하여 데이터가 있는 경우에만 데이터가 쌓이도록 함

(이에 대한 대안으로 Null 값을 허용하는 경우에는 쓸데없이 데이터가 차지하는 문제가 있고,

Null 값을 허용하지 않는 경우에는 회원들로부터 반드시 해당 데이터를 받을 수밖에 없는 문제가 발생함)

 

1:N(1: 부모 테이블, N: 자식 테이블)

-->  "1:N"의 경우, 자식 테이블에 부모 테이블의 고유한 식별자 값(Foreign Key)을 추가해야 한다!
--> 대부분의 경우, 1:N 관계로 처리가 됨!

 

N:M

--> 해시태그가 좋은 예시이다!

 

 

(TIP) DB 스키마 설계 시 테이블 당 field 수(Column 갯수)는 가능한 적게하되

field 별로 그 아래 쌓일 Record 혹은 데이터는 많은 것이 좋다!

 


ERD --> DB 스키마 설계

ERD(Entity Relationship Diagram) 정의
개체-관계 모델로 테이블 간의 관계를 설명해주는 다이어그램이라고 볼 수 있으며,

이를 통해 프로젝트에서 사용되는 DB의 구조를 한눈에 파악할 수 있다!
즉, API를 효율적으로 뽑아내기 위한 모델 구조도라고 생각하면 된다!

 

 

 

(1) One

  • 일대일 혹은 일대다 관계로서 주로 하나의 외래키(Foreign Key)가 걸린 관계라고 보면 됨

(2) Many

  • 다대다 관계로 중계 테이블을 통하여 여러 개의 데이터를 바라보고 있을 때 사용함

(3) One(and only one)

  • 위의 조건과 동일하게 1:1 관계이나, 하나의 row 끼리만 연결된 데이터임

(4) Zero or one

  • 1:1 혹은 1:N 관계를 가지고 있으나, 필수 조건이 아님을 의미함

(5) One or many

  • 1:1 혹은 다대다 관계를 가지고 있음을 의미함
  • 관계를 가지고 있으나, 참조되는 row 값들이 불명확함을 의미함

(6) Zero or many

  • 참조하는 테이블과의 관계가 불명확한 경우임
  • 장바구니처럼 row 생성값이 없을 수도, 하나일 수도, 여러 개일 수도 있는 경우임

 

 

1-2) Foreign Key(외래키) 추가 방법

테이블 생성

$ npm init -y
$ npm install mysql2 sequelize


디렉토리 구조
| -- models
| ---- index.js
| ---- user.model.js
| ---- board.model.js
| -- config.js

 


외래키를 추가하는 방법
(1) SQL: 직접 MySQL에 접속하여 테이블을 설정한 다음 외래키를 설정함!

CREATE TABLE User(
  userid VARCHAR(30) PRIMARY KEY,
  userpw VARCHAR(64) NOT NULL,
  username VARCHAR(20) NOT NULL
);

 

 

 

CREATE TABLE Board(
  id INT PRIMARY KEY AUTO_INCREMENT,
  subject VARCHAR(100) NOT NULL,
  content TEXT NOT NULL,
  userid VARCHAR(30) NOT NULL,
  registerDate DATETIME default now(),
  hit INT default 0
);

 



(2) Sequelize
외래키 설정

ALTER TABLE `Board` <-- 자식 테이블
ADD CONSTRAINT fk_board_userid
FOREIGN KEY(userid) <-- 자식 테이블에서 부모 테이블의 식별자를 담는 필드명(자식 테이블의 외래키 혹은 FK)
REFERENCES User(userid);
-- ON DELETE
-- ON UPDATE

Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0

SELECT * FROM information_schema.table_constraints
WHERE TABLE_SCHEMA = 'sample' AND TABLE_NAME = 'Board';

ALTER TABLE `Board` DROP CONSTRAINT fk_board_userid;

 

 

 

 

 

 

 

 

제약조건을 사용하면 삭제하려는 것과 관련된 테이블의 내용을 한 번에 삭제할 수 있다!

 

ALTER TABLE `Board`
ADD CONSTRAINT fk_board_userid
FOREIGN KEY(userid)
REFERENCES User(userid)
ON DELETE CASCADE;
-- ON UPDATE

 

 

UPDATE Board SET userid = 'admin2' WHERE id = 6;

 


위와 같이 제약사항을 안 지우고 UPDATE 구문을 실행하면 아래와 같은 에러가 발생함!
ERROR 1452 (23000): Cannot add or update a child row: a foreign key

constraint fails (`sample`.`board`, CONSTRAINT `fk_board_userid` FOREIGN KEY (`userid`)

REFERENCES `user` (`userid`) ON DELETE CASCADE)

 

 

 

 

 

 

DELETE FROM User WHERE userid='hsb7722';

 

 

 

 

 

ALTER TABLE `Board`
ADD CONSTRAINT fk_board_userid
FOREIGN KEY(userid)
REFERENCES User(userid)
ON DELETE RESTRICT;
-- ON UPDATE

 

 

 

ALTER TABLE `Board`
ADD CONSTRAINT fk_board_userid
FOREIGN KEY(userid)
REFERENCES User(userid)
ON DELETE SET NULL;
-- ON UPDATE

 

 

 

 

 

자식 테이블에서 Foreign Key 내용이 NOT NULL이 되어서는 안 된다!

 


CASCADE: 같이 삭제시킴
RESTRICT: 관련된 Record가 있으면 아예 실행이 안 되게 함(삭제를 못하게 함)!
SET NULL : 삭제하지 않고 Null로 바꿔줌(삭제하고 싶지 않을 때 사용하는 방법임)

제약사항은 테이블의 record를 넣기 전에 미리 다 구축해놓아야 한다!!

 

 

 

1-3) JOIN

SELECT * FROM Board as A JOIN User as B ON A.userid = B.userid;

SELECT A.id, A.subject, A.registerDate, A.hit, A.userid, B.username FROM Board as A JOIN User as B ON A.userid = B.userid;

SELECT A.id, A.subject, A.registerDate, A.hit, A.userid, B.username FROM Board as A LEFT OUTER JOIN User as B ON A.id = B.userid;

SELECT A.id, A.subject, A.registerDate, A.hit, A.userid, B.username FROM Board as A RIGHT OUTER JOIN User as B ON A.id = B.userid;

 

 

 

 

 

 

 

1-4) sequelize를 활용한 코드 구현

 

 

config.js

const config = {
  db: {
    username: "root",
    password: "Hhsb4114!@",
    host: "127.0.0.1",
    port: "3306",
    database: "sample",
    dialect: "mysql",
    define: {
      freezeTableName: true,
      timestamps: false,
    },
  },
};

module.exports = config;

 

 

models/index.js

const config = require("../config")["db"];
const Sequelize = require("sequelize");

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

// console.log(config);
// console.log(sequelize);

require("./user.model")(sequelize, Sequelize);
// console.log(sequelize.models); // { User: User }
require("./board.model")(sequelize, Sequelize);

const { models } = sequelize;
// models.User.associate(models);
// models.Board.associate(models);

for (const key in models) {
  if (typeof models[key].associate !== "function") continue;
  models[key].associate(models);
}

(async () => {
  await sequelize.sync({ force: true });

  const {
    models: { User, Board },
  } = sequelize;

  await User.create({
    userid: "hsb7722",
    userpw: "7890",
    username: "sangbeom",
  });
  await User.create({ userid: "admin", userpw: "7890", username: "관리자" });

  await Board.create({
    subject: "게시글1",
    content: "내용~~!!",
    userid: "hsb7722",
  });
  await Board.create({
    subject: "게시글2",
    content: "내용~~!!",
    userid: "hsb7722",
  });
  await Board.create({
    subject: "게시글3",
    content: "내용~~!!",
    userid: "hsb7722",
  });
  await Board.create({
    subject: "게시글4",
    content: "내용~~!!",
    userid: "hsb7722",
  });
  await Board.create({
    subject: "게시글5",
    content: "내용~~!!",
    userid: "admin",
  });
  await Board.create({
    subject: "게시글6",
    content: "내용~~!!",
    userid: "admin",
  });

  const board = await Board.findAll({
    raw: true,
    nest: true,
    include: [{ model: User }],
  });
  console.log(board);
})();

module.exports = {
  sequelize,
  Sequelize,
};

 

 

models/user.model.js

/*
CREATE TABLE `User`(
   userid VARCHAR(30) PRIMARY KEY,
   userpw VARCHAR(64) NOT NULL,
   username VARCHAR(20) NOT NULL
);
*/

module.exports = (sequelize, Sequelize) => {
  class User extends Sequelize.Model {
    static initialize() {
      return this.init(
        {
          userid: {
            type: Sequelize.STRING(30),
            primaryKey: true,
          },
          userpw: {
            type: Sequelize.STRING(64), // 영어 기준으로 64글자를 허용한다는 의미
            allowNull: false,
          },
          username: {
            type: Sequelize.STRING(20),
            allowNull: false,
          },
        },
        {
          sequelize,
        }
      );
    }

    static associate(models) {
      this.hasMany(models.Board, {
        foreignKey: "userid",
      });
    }
  }

  User.initialize();

  return User;
};

 

 

models/board.model.js

/*
CREATE TABLE `Board`(
   id INT PRIMARY KEY AUTO_INCREMENT,
   subject VARCHAR(100) NOT NULL,
   content TEXT NOT NULL,
   userid VARCHAR(30) NOT NULL,
   registerDate DATETIME default now(),
   hit INT default 0
);
*/

module.exports = (sequelize, Sequelize) => {
  class Board extends Sequelize.Model {
    static initialize() {
      return this.init(
        {
          subject: {
            type: Sequelize.STRING(100),
            allowNull: false,
          },
          content: {
            type: Sequelize.TEXT,
            allowNull: false,
          },
          registerDate: {
            type: Sequelize.DATE,
            allowNull: false,
            defaultValue: Sequelize.fn("now"),
          },
          hit: {
            type: Sequelize.INTEGER,
            defaultValue: 0,
          },
          userid: {
            type: Sequelize.STRING(30),
            allowNull: true,
          },
        },
        {
          sequelize,
        }
      );
    }

    static associate(models) {
      this.belongsTo(models.User, {
        foreignKey: "userid",
      });
    }
  }

  Board.initialize();

  return Board;
};