[TypeORM] 데이터베이스 연결과 모델링, Enum 타입 컬럼 대응하기

julia·2022년 1월 30일
2

Spoon Feed

목록 보기
1/5

TypeORM 사용을 시작하며 느낀 특징을 기록하고자 한다. 더불어 데이터베이스 모델링 도중 Enum 타입 컬럼을 도입하며 마주한 어려움들이 있었기에 작성하는 로그이기도 하다 🤓

진행 중인 프로젝트에 대해

우선 우리는 nodejs + express + typescript 로 개발 중이다. 전체적인 아키텍처는 아래와 같다. (Route53은 경우에 따라 사용하지 않을 수도 있다.)

사실 나는 리액트만 조금 해봤고 그마저도 리액트 개발자 분들이 보면 코웃음 칠 찍먹.. 노드 익스프레스 타입스크립트로 백엔드 개발이 처음이다. 동기 비동기도 더 많이 공부하고, 프로젝트로 실제 경험해보면서 블로그 작성을 해야겠다.
이제 DND 프로젝트 기간이 한달 남았고 그 안에 개발을 끝내야 하는 상황이라 부랴부랴 어제부터 모델 스키마 작성에 들어갔다.

우리는 ORM(객체-관계 매핑)으로 Sequelize 와 TypeORM 중 후자를 택했다. Sequelize와 비교했을 때의 장점을 찾아보니

  1. 타입스크립트 지원이 훨씬 잘 되고
  2. 상대적으로 속도가 빠르며
  3. TypeORM 코드작성이 raw 쿼리에 비교적 가깝다고 한다.

그리고 개발 시작하면서 내가 실제로 느낀 최고의 장점은 migration을 따로 하지 않아도 디비 적용이 바로 된다는 점!!

TypeORM 써보기

우선 TypeORM 에는 두 가지 방식이 있다. 하나는 간단한 쿼리를 지향하고 작은 프로젝트에 어울리는 Active Record 방식이고 / 다른 하나는 일반적으로 스프링 프로젝트를 할 때와 비슷한 느낌의 Data Mapper 방식이다. 내가 스프링과 비슷하다는 느낌을 받은 것처럼 repository를 거쳐 의존성을 줄이는, 큰 프로젝트에 어울리는 방식이다.
Active Record와 Data Mapper 방식 비교

이제 직접 디비 연결을 해보자.

  • TypeORM 설치
    npm install -g typeorm --save
    npm install mysql --save
    typeorm init --database mysql
    이 명령어들로 설치를 해주고 간단한 설정 파일을 생성해준다. 마지막 커맨드로 ormconfig.js파일이 생성되고, tsconfig.json 파일이 수정되는데 ormconfig.js 파일의 경로는 루트 디렉토리 바로 밑이다 절대 변경하지 말 것!

  • ormconfig.js

const dotenv = require('dotenv');

dotenv.config();

module.exports = {
    type: 'mysql',
    host: process.env.DB_HOST,
    port: process.env.DB_PORT,
    username: process.env.DB_USERNAME,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,
    synchronize: true,
    logging: false,
    dropSchema: true,
    entities: ['./srcs/entity/*.ts'],
    subscribers: ['srcs/migration/*.ts'],
    migrations: ['srcs/migration/*.ts'],
};

env 파일에 디비 관련 정보들을 숨겨놓은 뒤 config 파일에서 불러오도록 했다. migration 설정과 cli 설정이 더 있는데, 우리 프로젝트에서는 당장 필요한 것 같지 않아서 필요하게 되면 작성할 것이다. migration 관련 내용은 여기 에 잘 정리되어 있는 것 같다.

  • Enum 관리 - entity/common/Enums.ts
export enum IsHouseOwner {
    HOST = '세대주',
    MEMBER = '세대구성원',
    PRIVATE = '미공개',
}

내가 설정한 enum 들 중 하나를 예시로 가져왔다. 우리는 컬럼에서 enum을 사용할 부분들이 많았기 때문에 따로 파일을 분리해 관리하는게 좋을 것이라고 판단했다.

  • User Entity 작성 - entity/User.ts
import {
    Entity,
    Column,
    PrimaryGeneratedColumn,
    BaseEntity,
    CreateDateColumn,
    UpdateDateColumn,
} from 'typeorm';
import { IsHouseOwner } from './common/Enums';

@Entity()
export class User extends BaseEntity { // Active Record 방식은 BaseEntity를 꼭 extend 해줘야 함
    @PrimaryGeneratedColumn()
    id!: number;

    @Column('varchar', { name: 'nickname', unique: true, nullable: false, length: 50 })
    nickname!: string;

    @Column({ type: 'int', nullable: false })
    age!: number;

    @Column('varchar', { name: 'address', length: 50, nullable: false })
    address!: string;

    // 중략

    @Column({ type: 'enum', name: 'is_house_owner', enum: IsHouseOwner })
    isHouseOwner!: IsHouseOwner;

    @CreateDateColumn({
        type: 'timestamp',
        name: 'created_at',
    })
    createdAt: Date | undefined;

    @UpdateDateColumn({
        type: 'timestamp',
        name: 'updated_at',
    })
    updatedAt: Date | undefined;
}

export default User;

참고로 아직 프로젝트와 코드리뷰가 진행 중이기 때문에 형식은 바뀔 수 있다.
난 사실 typeorm이 처음이라 그런지 enum 타입 컬럼을 어떻게 정의해야될 지 많이 헤맸었는데,

Enum 타입 컬럼 대응하기

  1. (Enum을 따로 뺀 경우) Enums.ts 에서 export 해준 것을 User.ts 에서 import 해준 뒤
  2. @Column 애노테이션 옵션 중 enum: '이넘 이름' 을 넣어주고
  3. 데이터 타입 표시할 때 '컬럼명'!: '이넘 이름'; 을 입력해주면 되는 거였다.

아래 단계부터는 실제 서버와 디비를 연결해주는 작업인데, 이 부분은 같이 하는 멋진 팀원 분께서 맡아주셨다.

  • config/connectDB.ts
import { getConnectionOptions, createConnection } from 'typeorm';
import { User } from '../entity/User';

const connectDb = async (): Promise<void> => {
    const connectionOptions = await getConnectionOptions(process.env.NODE_ENV);

    await createConnection({ ...connectionOptions })
        .then((connection) => {
            connection.getRepository(User);
            console.log('DB connected');
        })
        .catch((err) => console.log(err));
};

export default connectDb;
  • config/index.ts
export { default as connectDB } from './connectDB';
  • index.ts
import app from './app';
import dotenv from 'dotenv';
import { connectDB } from './config/index';

dotenv.config();

const runServer = async () => {
    await connectDB();
    /* Run server */
    console.log('Set application...');
    app.listen(process.env.PORT, () => console.log(`server Run with port: ${process.env.PORT}`));
};

runServer();

npm start 했을 때 'DB connected' 와 'server Run with post: 포트번호' 메시지가 뜨고 localhost:포트번호 에 접속이 잘 되면 성공이다.

그리고 디비에 접속하면 자동으로 컬럼들이 sync 되어있는 것을 확인할 수 있다 - 난 이게 정말 큰 장점이자 단점이 될 수 있다고 생각한다. 아직 개발 초보이고 플젝 초기 단계라 장점으로 느껴지는게 더 크지만, 이런 작은 사이드 플젝이 아니라
실제 운영을 크게 해야 하는 규모 있는 서비스라면 migration을 직접 생성해주는 것이 관리에 좋을 것 같다.

profile
Move Forward

0개의 댓글