본문 바로가기

Database

[Sequelize] snake_case를 camelCase로 다루기

참고자료

https://github.com/sequelize/sequelize/issues/10857

https://github.com/sequelize/sequelize/issues/10857#issuecomment-534351978

https://stackoverflow.com/questions/42728789/sequelize-foreign-key-snake-case-in-db-and-camelcase-in-code-does-it-possible

 

 

목표

db의 column은 모두 snake_case로,

대신 JS에서 해당 컬럼을 다룰 때에는 camelCase로 다루기.

 

이유는 프론트 단과의 통신 과정에서 case 충돌을 막기 위함이다.

예를 들어 res.data.profile_image VS. res.data.profileImage를 고민한다면

일반적으로  프론트 단에서는 후자를 쓰는 것이 자연스럽다.

백에서는 data를 넘겨줄 때 camelCase로 넘겨주면 편할 것이다.

db에서 값을 가져올 때 column명 그대로 snake_case 상태로 가져온다면, camelCase로 변환하는 불편한 과정을 거쳐야할 수도 있다. 또는 그대로 snake_case를 넘겨주었을 때 프론트와의 통신에서 의도치 않은 불편함을 유발할 수 있다.

 

 

1. 기본 설정 방법

sequelize의 model 과정에서 간단한 설정이 필요하다.

camelCase로 컬럼을 작성하되, field 옵션으로 snake_case로 작명된 column을 가리키면 된다.

 

다음은 예시 코드이다.

import { Sequelize, DataTypes } from 'sequelize';

class User extends Sequelize.Model {
  static init(sequelize) {
    return super.init(
      {
        userId: {
          type: DataTypes.INTEGER,
          autoIncrement: true,
          primaryKey: true,
          field: 'user_id',
      	}
      },
      {
        intialAutoIncrement: 1,
        sequelize,
      },
    )
  }
}

export { User };

 

 

2. timestamp 자동 생성

timestamps로 created_at, updated_at 컬럼을 자동으로 생성하고 다룰 수도 있다.

import { Sequelize, DataTypes } from 'sequelize';

class User extends Sequelize.Model {
  static init(sequelize) {
    return super.init(
      {
        userId: {
          type: DataTypes.INTEGER,
          autoIncrement: true,
          primaryKey: true,
          field: 'user_id',
        },
      },
      {
        intialAutoIncrement: 1,
        sequelize,
        tableName: 'Users',
        modelName: 'User',
        timestamps: true,      // 1. timestamps 자동 생성 기능 설정
        underscored: true,     // 2. underscored 옵션을 설정
      }
    );
  }
}

export { User };

underscored 옵션은 JS에서 camelCased된 변수와 db단에서 snake_cased인 column을 자동으로 연결해준다. 

 

만약 underscored 설정을 하지 않으면 camelCase 형태로 컬럼에 접근하게 된다. ( createdAt, updatedAt )

따라서 첫 설정 시 이 옵션을 사용하지 않았다면 db에도 createdAt, updatedAt이 형성되었을 것이며,

snake_case로 설정하여 table을 생성했다 해도, 해당 컬럼에 접근할 수 없을 것이다.

const attributes = { exclude: ['userId', 'password', 'status', 'createdAt', 'updatedAt'] };

const user = await this.User.findOne({
  where: { userId },
  attributes,
});

실제 프로젝트에서 사용 중인 service 코드이다. 

exclude 옵션은 지정한 column을 제외한 모든 컬럼을 select하는 기능이다.

camelCase로 설정하였으나 접근할때에는 snake_cased된 column을 배제하여 아래와 같은 결과값을 가져온다. (postman)

 

 

3. foreignkey 설정

sequelize.associate 기능을 활용하여 table 간의 부모 자식 관계를 설정할 수 있다.

이 때 foreignkey 역시 camelCase로 통신할 수 있도록 설정할 수 있다.

import { Sequelize, DataTypes } from 'sequelize';

class Favor extends Sequelize.Model {
  static init(sequelize) {
    return super.init(
      {
        favorId: {
          field: 'favor_id',
          type: DataTypes.INTEGER,
          autoIncrement: true,
          primaryKey: true,
        },
        movie: {
          type: DataTypes.BOOLEAN,
          defaultValue: false,
        },
      },
      {
        intialAutoIncrement: 1,
        sequelize,
        timestamps: true,
        charset: 'utf8', // 한국어 설정
        collate: 'utf8_general_ci', // 한국어 설정
        tableName: 'Favors',
        modelName: 'Favor',
        underscored: true,
      }
    );
  }

  static associate(db) {
    db.Favor.belongsTo(db.User, {
      foreignKey: {
        name: 'userId',
        field: 'user_id',
      },
      sourceKey: 'userId',
    });
  }
}

export { Favor };

associate 함수에서 User 테이블에 있는 user_id column을 가져와 Favor table에도 만드려고 한다. 

sourceKey는 Favor table의 입장에서 User table을 접근할 때의 연결고리이며 

foreignKey는 sourceKey로 연동된 User table에서 user_id column에 접근한 후(field 옵션), 그 값을 Favor table에 userId라는 이름으로 설정(name 옵션)하는 것이다.

 

이제 userId를 가지고 User table 뿐만 아니라 Favor table에도 접근할 수 있게 된다.

프로젝트에서 활용한 사례 코드를 첨부하며 글을 마무리한다!

async addUser(email) {
  const newUser = await this.User.create({ email });
  
  const { userId } = newUser.dataValues;
  
  await this.Favor.create({ userId });
}