• Database name : 원하는 이름을 입력
  • Charset: utf8mb4 
    • 문자 인코딩 스타일을 셋팅 하는 부분
    • utf8mb4은 이모지까지 인코딩 가능
  • Collection : utf8mb4_unicode_ci
    • 정렬을 하는 방식을 선택하는 부분
    • 선택한 방식을 사람들이 많이 사용

Error 가 발생한 상황

content 에 1000 글자 이상의 문자를 입력 했을때 발생

출력된 Error  내용

ERROR message
{
    "name": "SequelizeDatabaseError",
    "parent": {
        "code": "ER_DATA_TOO_LONG",
        "errno": 1406,
        "sqlState": "22001",
        "sqlMessage": "Data too long for column 'content' at row 1",
        }
}

 

예상한 Error 위치

table 을 생성할 때 설정한 content에 입력할 수 있는 데이터(글자)의 길이를 초과

// model-Post.js
content: { type: DataTypes.STRING}

 

Sequelize 에서 STRING == VARCHAR

그래서 65535 글자 수를 가질 수있다고 생각했는데 에러 발생

 

시도 1 : TEXT 사용

content:{type:DataType.TEXT}

-->  긴 문장이 담아지는 것은 성공

 

!! 하지만 1000 글자를 넘겼을 경우 서버가 중단된다. --> res.send() 를 호출한 뒤 또 호출하여서 발생

 

 

해결

1. data가 길어서 에러가 뜨는 상황 : content:{type:DataType.TEXT} 로 변경하여 해결

 

2. 유효성 확인을 하는 코드가 res.send() 를 중복해서 호출하여 생기는 상황 

  • 문제의 코드
router.post("/", async (req, res)=>{
    const {title, content} = req.body;
    
    try{
        const newPost = await sequelize.transaction(async ()=>{
	// -------- res.send 중복 유발 --------------------
            if(title.length > 15){
                return res.status(400).send("Title must be under 15 characters.");
            }

            if(content.length > 1000){
                return res.status(400).send("Content must be under 1000 characters.");
            }
	//---------------------------------------------
            const post = await Post.create({
                title,
                content
            });
            console.log("A post is created")
            return post;
        })
        res.status(200).send(newPost);

    }catch (err){
        console.error("Error while create a post");
        res.status(500).send(err);
    }

})
  • 해결법 1 :  if 문 위치 변경
router.post("/", async (req, res)=>{
    const {title, content} = req.body;

    if(title.length > 15){
        return res.status(400).send("Title must be under 15 characters.");
    }

    if(content.length > 1000){
        return res.status(400).send("Content must be under 1000 characters.");
    }

    try{
        const newPost = await sequelize.transaction(async ()=>{

            const post = await Post.create({
                title,
                content
            });
            console.log("A post is created")
            return post;
        })
        res.status(200).send(newPost);

    }catch (err){
        console.error("Error while create a post");
        res.status(500).send(err);
    }
})

 

  • 해결법 2 : if, else if 사용
router.post("/", async (req, res)=>{
    const {title, content} = req.body;

    try{
        if(title.length > 15){
            return res.status(400).send("Title must be under 15 characters.");
        }else if(content.length > 1000){
            return res.status(400).send("Content must be under 1000 characters.");
        }

        const newPost = await sequelize.transaction(async ()=>{
            const post = await Post.create({
                title,
                content
            });
            return post;
        })
        res.status(200).send(newPost);

    }catch (err){
        console.error("Error while create a post");
        res.status(500).send(err);
    }
})

📖 Sequelize : https://sequelize.org/docs/v6/other-topics/transactions/

 

Transactions | Sequelize

Sequelize does not use transactions by default. However, for production-ready usage of Sequelize, you should definitely configure Sequelize to use transactions.

sequelize.org

<설치>

npm i cls-hooked

 

<종류>

  • Unmanaged transactions : manually committing and rolling back the transaction by the user

 

  • Managed transaction : automatically manage committing and rolling back
try {

  const result = await sequelize.transaction(async (t) => {

    const user = await User.create({
      firstName: 'Abraham',
      lastName: 'Lincoln'
    }, { transaction: t });

    await user.setShooter({
      firstName: 'John',
      lastName: 'Boothe'
    }, { transaction: t });

    return user;

  });

  // If the execution reaches this line, the transaction has been committed successfully
  // `result` is whatever was returned from the transaction callback (the `user`, in this case)

} catch (error) {

  // If the execution reaches this line, an error occurred.
  // The transaction has already been rolled back automatically by Sequelize!

}

If all queries are successful (in the sense of not throwing any error), but you still want to rollback the transaction, you should throw an error yourself

await sequelize.transaction(async t => {
  const user = await User.create({
    firstName: 'Abraham',
    lastName: 'Lincoln'
  }, { transaction: t });

  // Woops, the query was successful but we still want to roll back!
  // We throw an error manually, so that Sequelize handles everything automatically.
  throw new Error();
});

 

  • Automatically pass transactions to all queries
const cls = require('cls-hooked');
const namespace = cls.createNamespace('my-very-own-namespace');

//To enable CLS, you must tell sequelize which namespace to use by using a static method of the sequelize constructor
const Sequelize = require('sequelize');

//All instances will share the same namespace, we cannot enable it only for some instances.
Sequelize.useCLS(namespace); 

new Sequelize(....);

 

 

간단한

>> Automatically pass transactions to all queries 이용

< 요약>

  1. npm i cls-hooked
  2. const cls = require('cls-hooked');
    const namespace = cls.createNamespace('my-very-own-namespace');
    const Sequelize = require('sequelize');
    Sequelize.useCLS(namespace); 

 

 

 

📖 wikipedia : https://en.wikipedia.org/wiki/Database_transaction

 

 

트랜잭션이란?

- 데이터베이스 시스템에서 실행되는 하나의 논리적인 작업 혹은 데이터 조작 단위를 나타낸다.

- 데이터베이스에 대한 일련의 작업을 묶어서 처리하는 방법을 제공하여 데이터 일관성무결성을 보장

 

특징

1. 원자성 ( Atomicity)
일련의 작업이 모두 성공적으로 완료되어야한다. 하나라도 실패할 시 처음 상태로 rollback(되돌림)된다.

 

2. 일관성 (Consistency)

트랜잭션이 실행 전과 후의 데이터베이스가 일관된 상태여야 한다. 이는 트랜잭션 실행후 데이터베이스가  데이터의 무결성(데이터베이스 내의 데이터가 일관된 규칙과 데이터 형식을 따르고, 고유키 또는 제약조건을 준수하는 것)을 유지, 비즈니스 논리와 일치, 관계 무결성(데이터베이스 내의 데이터간의 관계)를 유지 해야 한다는 것을 의미한다. 

 

3.지속성 (Durability)

트랜잭션이 성공적으로 완료된 후에는 그 결과가 영구적으로 저장되어야 한다. 시스템 장애 또는 전원 공급 장애와 같은 문제가 발생하더라도 트랜잭션의 결과는 유지되어야 한다.

 

필요한 이유?

데이터 조작을 안전하게 다루기 위함이다.

예) A가 은행에서 돈을 인출하고 B 에게 송금을 하려고 할때, A가 인출한 돈을 B에게 송금을 했는데 B의 계좌에 도착을 하지 않는 경우.
이때, 이 일련의 작업이 모두 성공적으로 완료 되지 않으므로 초기 상태로 롤백을 하여 송금 절차를 안전하게 수행 할 수 있도록 한다.

📖 Sequelize :  https://sequelize.org/docs/v6/core-concepts/assocs/

 

foriegn key 는 자동으로 가 아니라 1 이나 N에 해당하는 테이블이 있는 열에
1의 primary key(P.K) 에 해당하는 값을 넣어 연결시켜주는 다리 역할

 

❗️1 : 1  (One-To-One relationship)

The .hasOne and .bleongTo associstions 가 함께 사용된다.
특징> 두 테이블중 아무곳이나 다른 테이블의 P.K 값을 넣어주어도 상관없다.

const Profile = require('./profile');
const User = require('./user');

User.hasOne(Profile);
Profile.belongsTo(User);

//----
const saveUSer = await User.create({
	...
});

const saveProfile = await Profile.create({
    gender: "male",
    photo: "me.jpg",
    userId: saveUser.id // User table의 primary key(P.K) 입력하여 관계를 형성
});


// or

const saveProfile = await Profile.create({
    gender: "male",
    photo: "me.jpg",
});

const saveUser = await User.create({
	name: "name",
    userId: saveProfile.id
});

 

❗️ 1 : N  (One-To-Many relationship)

The .hasMany and .belongsTo associations 가 함께 사용된다.

특징> N 에 해당하는 테이블에 1에 있는 PK 값을 입력하여 두 테이블을 연결시킨다.

const User = require('./user');
const Post = require('./post');

User.hasMany(Post);
Post.belongTo(User);

const saveUSer = await User.create({
	...
});

const savePost = await Post.create({
    title: "demo",
    content: "nothing",
    userId: saveUser.id // User table의 primary key(P.K) 입력하여 관계를 형성
});

 

❗️N : M ( Many-To-Many relationship)

The .hasMany and .belongsToMany associations 가 함께 사용된다.

특징>  두 테이블을 연결시켜줄 새로운 테이블을 형성해 주고, 새로운 테이블에 두테이블의 P.K 값을 입력하여 연결시킨다.

const Post = require("./post");
const Hashtag = require("./hashtag");

const PostHashtag = sequelize.define('post_hashtag', {
  post_id: {
    type: DataTypes.INTEGER,
    references: {
      model: Post,
      key: 'id'
    }
  },
  hashtag_id: {
    type: DataTypes.INTEGER,
    references: {
      model: Hashtag, 
      key: 'id'
    }
  }
});

// PostHashtag 이용해서 두테이블을 연결해준다.
Post.hasMany(Hashtag, { through: 'PostHashtag'});
Hashtag.belongToMany(Post, { through: 'PostHashtag'});

 

'DataBase > Database 지식 기록' 카테고리의 다른 글

Sequelize을 이용한 Transaction  (0) 2023.10.05
트랜잭션(Transaction)  (0) 2023.10.04
[Feedback] DB modeling  (0) 2023.09.30
데이터베이스 정규화  (0) 2023.09.13
데이터베이스 이상 현상 (Anomaly)  (0) 2023.09.13

+ Recent posts