230112 - Javascript - Sequelize 테이블 만들기 , SQL 테이블 만들기 , SQL-JOIN, Sequelize-Join, NodeJS

728x90

오늘 내가 배운 것

1. 제약조건

2. 테이블 만들기 

  • 2-1. Query문을 이용한 테이블 만들기
  • 2-2. Sequlize - class를 이용한 테이블 만들기

3. 외래키 지정하기 (FK)

  • Query문을 이용한 제약사항 확인하기
  • 3-1. Query문을 이용한 외래키 (foreign key) 지정하기
  • 3-2. Query문을 이용한 외래키 (foreign key) 삭제하기
  • 3-3.  Query문을 이용한 외래키 (foreign key)  ON DELETE 옵션
  • 3-4. sequelize에서 외래키(foreign key) 지정하기

4. 테이블 JOIN 하기

  • 4-1. Query문을 이용한 JOIN
  • 4-2. sequelize를 이용한 JOIN

 

1. MySQL 제약조건

테이블에 데이터를 저장할 때 데이터의 정확성, 일관성, 유효성을 지키기 위해서(데이터의 무결성) 일정한 규칙을 정하기 위해 사용하는 방법이다.

NOT NULL

    -   NULL값을 가질 수 없다.

    -   설정된 필드는 무조건 데이터가 있어야 함.

기본키 (Primary key)
    -   중복되지 않는 고윳값
    -   Null 허용하지 않음
    -   테이블당 하나의 기본키만 지정 가능
고유키 (Unique)
    -   중복되지 않는 고윳값
    -   Null 가능
외래키 (foreign key)

    -   테이블을 다른 테이블과 연결해 주는 역할

    -   기준 테이블의 내용을 참조해서 레코드가 입력

    -   FOREIGN KEY를 설정할 때 참조되는 테이블의 필드는 반드시 UNIQUE나 PRIMARY KEY가 설정되어 있어야 함.

 

1:N 관계

1 - 부모테이블 [PK]
N - 자식테이블 [FK]
자식테이블에는 부모의 고유식별자(PK) 값을 받아야 한다.

EX) 게시판과 댓글의 관계  
하나의 게시판(부모)에 여러 개의 댓글(자식)을 만들 수 있다.
댓글 테이블에는 게시판 테이블의 PK를 FK로 받는다.

자식테이블에게 부모테이블의 고유 식별자 필드를 하나 추가한다.

 

 

2. 테이블 만들기 

2-1. Query문을 이용한 테이블 만들기

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


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

 

2-2. sequlize - class를 이용한 테이블 만들기

2-2-1. User

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),
                    allowNull: false,
                },
                username:{
                    type: Sequelize.STRING(20),
                    allowNull:false
                },
            },{
                sequelize,
            })
        }
        //fk설정하기
        static associate(models){
            
            //1:다 관계에서 부모인 user는 많이 줄 것이다라는 뜻으로 생각하면 된다.
            //hasMany의 ()안에는 sql에서 REFERENCES User(userid) 부분과 같다
            this.hasMany(models.Board, {
                foreignKey:"userid"
            })
        }
    }
    User.initialize()
    return User
}

2-2-2. Board

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(100),
                    allowNull:true, 
                }, 
            },{
                sequelize,
            })
        }
        //fk설정하기
        static associate(models){
            this.belongsTo(models.User, {
                foreignKey:"userid"
            })
        } 
    }
    Board.initialize()
    return Board
}

 

Model.init()와 sequelize.define()는 같은 역할이다.
Mode.init함수 안에는 2가지의 인자값이 필요하다.
2가지의 인자값은 객체형태를 이루고, 1번 인자값에는 생성할 column의 속성을 넣어주고,
2번 인자값에는 Sequelize 생성자에 제공되는 기본 정의 옵션을 넣어준다.

 

 

3. 외래키 지정하기 (Foreign Key)

 - Query문을 이용한 제약사항 확인하기

-- database 안에 table을 확인할 때 쓸 수 있다.
SELECT * FROM [database명].[table명]

-- information_schema 테이블의 모든 정보가 담겨있다. WHERE절을 붙여서 부분선택 가능
SELECT * FROM information_schema.table_constraints
WHERE TABLE_SCHEMA = "samples";

samples 데이터베이스 안에 모든테이블에 있는 제약 조건 확인하기

- WHERE 절 조건을 더 세분화해서 보고 싶은 영역만 확인하기.

WHERE 절을 더 자세하게 쓸 수 있다.

 

3-1. Query문을 이용한 외래키 (foreign key) 지정하기

 

-- 게시판
-- 부모테이블 : User, 자식테이블 : Board

ALTER TABLE `Board` -- 자식테이블
    ADD CONSTRAINT fk_board_userid -- fk 삭제할 때 이용한다. fk_board_userid는 변수명 같은 느낌(제약명)
    FOREIGN KEY(userid) -- 자식테이블에서 부모의 식별자를 담는 필드명(자식 테이블의 외래키)
    REFERENCES User(userid)
    -- ON DELETE : 옵션... 밑에 따로 서술
    -- ON UPDATE : 옵션... 밑에 따로 서술

 

3-2. Query문을 이용한 외래키 (foreign key) 삭제하기

 

 

현재 상태

- 외래키 삭제

ALTER TABLE [테이블명] DROP CONSTRAINT [제약명]

ALTER TABLE `Board` DROP CONSTRAINT fk_board_userid;

 

삭제한 이후 확인

 

3-3.  Query문을 이용한 외래키 (foreign key)  ON DELETE 옵션

 

외래키 지정 시에 ON DELETE를 같이 작성하여 준다.

  • ON DELETE CASCADE
  • ON DELETE SET NULL
  • ON DELETE RESTRICT
ALTER TABLE `Board`
    ADD CONSTRAINT fk_board_userid
    FOREIGN KEY(userid)
    REFERENCES User(userid)
    ON DELETE CASCADE || SET NULL || RESTRICT

 

 

상황 1 : 제약사항은 테이블을 만들 때 구상해서 미리 설정해야 한다.
-- userid 변경하기
UPDATE Board SET userid = "admin2" WHERE id=6;

만약 userid를 하나 바꿨을 경우를 가정

user테이블의 row는 2개지만 board의 userid의 내용이 3가지여서 제약사항을 걸면 에러가 발생한다..

 

에러 발생!!!

결론 :

제약사항은 테이블 안에 레코드가 생성되기 전에 제약사항을 걸어줘야 한다. 그래서 테이블을 잘 구상하는 것이 중요하다.

 

 

상황 2 : ON DELETE CASCADE

부모테이블(User)에서 user 1개를 지우기

 

user 1개 지우기

 

 

자식 테이블의 변화 보기

결론:

제약 조건중 'ON DELETE CASCADE'를 사용하게 되면 부모테이블의 내용을 지우면 자식 테이블 데이터에 영향을 준다.

 

 

 

상황 3 : ON DELETE RESTRICT

자식 테이블에 부모 테이블과 관련된 내용이 있을 때 부모 테이블의 내용을 지울 수 없게 한다.

아예 실행이 되지 않는다..

 

상황 4 : 상황 4 : ON DELETE SET NULL 

자식테이블에서 fk가 NOT NULL이면 안됨

 

부모의 데이터가 지워져도 자식의 데이터를 지우지 않아야 하는 상황이 있다. (ex: 리뷰...)
부모 데이터(pk)를 지우면 자식테이블에 fk값에 NULL이 기입된 상태로 데이터가 남아 있다.

 

userid 가 NULL인 상태로 데이터는 남아있다.

 

3-4. sequelize에서 외래키(foreign key) 지정하기

- user.model.js (부모테이블)

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

- board.model.js (자식테이블)

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

 

위에 함수들을 실행하면 지정이 된다.

 

const { models } = sequelize

// 방법1. 하나씩 다 불러서 실행한다.
// models.Board.associate(models)
// models.User.associate(models)


// 방법2. for-in 반복문을 이용하여 실행
for (const key in models) {
    if (typeof models[key].associate !== "function") continue
    models[key].associate(models)
}

 

 

4. 테이블 JOIN 하기

4-1. Query문을 이용한 JOIN

userid 대신 username을 띄우고 싶은 상황이라면?

 

SELECT
    -- * 로 지정해서 보는 것 보다는 필요한 내용을 보는 것이 낫다.
    A.id,
    A.subject,
    A.register,
    A.hit,
    A.userid,
    B.username
FROM Board as A -- board를 잠깐 변수A로 만든다..(DB에서 바뀌는 것이 아니라 query문 안에서만..)
JOIN User as B
ON A.userid = B.userid -- 두개의 테이블 내용중 똑같은 것이 있어야한다.

 

JOIN의 방법 중에는  LEFT OUTER, RIGHT OUTER 등등 있지만 나중에 알아볼 예정 

기본 JOIN 먼저 하자... (INNER JOIN)

 

 

4-2. sequelize를 이용한 JOIN

sequelize를 이용한 방법은 3-4에서 했었던 FK가 잘 설정되어 있으면 문법 자체는 간단하다.

 

 const board = await Board.findAll({
        raw:true, // findAll하면 내역이 많은데 그중 값만 보고 싶을 때 사용
        nest:true, // 결과에 User를 객체로 따로 뽑아줌..
        include:[{model:User}] // join문..
    })

Board를 다 찾는데, 거기에서 User을 Join 해서 보여주겠다는 의미이다.

 

 

 

 


 

 

반응형