내용 보기

작성자

관리자 (IP : 172.17.0.1)

날짜

2020-09-15 08:38

제목

[TypeScript] ORM 사용 with typeorm


타입스크립트에서 DB 연동시 ORM관련 모듈 typeorm에 관한 테스트 및 샘플 코드 정리 내용이다.

typeorm은 JPA계열의 ORM모듈이다.
간단히 JPA란, Java Persistence API의 약자로 Java에서 사용되는 객체지향적 데이터 관리와 엔티티 모델을 통해
CRUD를 수행하는(쿼리를 통한 CRUD가 아님)  ORM이라고 볼 수 있다.

typeorm은 https://typeorm.io/ 사이트에서 자세한 정보를 찾아 볼 수 있다.
현재 지원하는 데이터베이스로는

2020. 09 기준

  • MySQL
  • MariaDB
  • Postgres
  • CockroachDB
  • SQLite
  • Microsoft SQL Server
  • Oracle
  • SAP Hana
  • sql.js

총 9가지 데이터베이스를 지원하고 있다.

다음은 Node.js에서 typeorm 모듈을 사용한 간단한 샘플 예제를 설명한다.
※ TypeScript모듈은 이미 설치 되어 있는 환경 기준이다.


#모듈 설치

1. typeorm모듈을 설치한다.

npm install typeorm --save


2. 추가로 shim을 설치해야 한다.

npm install reflect-metadata --save


3. 사용하는 데이터베이스의 모듈을 설치한다.
여기서는 MSSQL을 사용한다.

npm install mssql --save



#프로젝트 생성

간단하게 아래 명령을 통해 기본 프로젝트를 생성할 수 있다.

typeorm init --name {프로젝트 이름} --database {데이터베이스 이름}

예]

typeorm init --name typeorm_sample --database TypeORMSample


프로젝트를 생성하면 아래의 구조로 폴더가 생성된다.

프로젝트이름
├── src // place of your TypeScript code
│ ├── entity // place where your entities (database models)
│ │ └── User.ts // sample entity
│ ├── migration // place where your migrations are stored
│ └── index.ts // start point of your application
├── .gitignore // standard gitignore file
├── ormconfig.json // ORM and database connection configuration
├── package.json // node module dependencies
├── README.md // simple readme file
└── tsconfig.json // TypeScript compiler options


#typeorm 설정

ormconfig.json에서 DB접속 및 orm관련 설정을 할 수 있다.

[ormconfig.json]
{
   "type""mssql",
   "host""127.0.0.1",
   "port"1433,
   "username""sa",
   "password""test",
   "database""TypeORMSample",
   
   "synchronize"true,
   "logging"false,
   "entities": [
      "src/entity/**/*.ts"
   ],
   "migrationsTableName""custom_migration_table",
   "migrations": [
      "src/migration/**/*.ts"
   ],
   "subscribers": [
      "src/subscriber/**/*.ts"
   ],
   "cli": {
      "entitiesDir""src/entity",
      "migrationsDir""src/migration",
      "subscribersDir""src/subscriber"
   }
}
cs

type 옵션에 현재 사용하는 데이터베이스의 종류를 기입하고
db의 접속 정보를 설정한다.

migrationsTableName옵션은 마이그레이션시 생성되는 테이블 이름을 직접 설정 하는 옵션이다.
entitiesDir의 경로나  migrationsDir경로 옵션의 값은 자동으로 기본 프로젝트 생성 폴더 구조대로 생성된다. 


#엔티티 생성

유저정보(User)와 게시판정보(Board)가 있다는 가정으로 두개의 엔티티정보를 생성한다.
두개의 엔티티는 N관계로 설정한다.
유저정보(User), 게시판정보(Board) 1:N 관계 이다.

[\entity\User.ts]
[User.ts]
import {BaseEntity,
    Entity,
    PrimaryGeneratedColumn,
    Column,
    OneToMany} from "typeorm";
 
import { Board } from './Board';
 
@Entity()
export class User extends BaseEntity {
 
    @PrimaryGeneratedColumn()
    no: number;
 
    @Column()
    id: string;
 
    @Column()
    password: string;
 
    @Column()
    name: string;
 
    @Column()
    level: number;
 
    @Column()
    etc: string;
 
    // User(1) <-> Board(*)
    @OneToMany(
        (type) => Board,
        (board) => board.user
        )
    boards!: Board[];
}
cs

클래스에 @Entity() 데코레이션을 붙여 엔티티 클래스 임을 정의할 수 있다.


@Column 데코레이션으로 실제 테이블과 컬럼을 매핑할 수 있고 타입을 지정할 수 있다.
@OneToMany 데코레이션으로 관계설정을 지정할 수 있다.

1:N 관계에서 1인 엔티티는 @OneToMany데코레이션을 지정하고 어떤 엔티티의 타입과 관계를 맺을지 상대 엔티티 타입과 속성을 지정한다.

boards!: Board[]; 추가로 FK 엔티티의 객체 속성을 추가한다.


[\entity\Board.ts]
[Board.ts]
import {BaseEntity,
    Entity,
    PrimaryGeneratedColumn,
    Column,
    ManyToOne,
    JoinColumn} from "typeorm";
 
import { User } from './User';
 
@Entity()
export class Board extends BaseEntity {
 
    @PrimaryGeneratedColumn()
    no: number;
 
    @Column()
    name: string;
 
    @Column()
    title: string;
 
    @Column()
    contents: string;
 
    @Column()
    recordDate: Date;
 
    @Column()
    level: number;
 
    @Column()
    etc: string;
 
    // Board(*) <-> User(1)
    @ManyToOne(
        (type) => User,
        (user) => user.id
    )
 
    @JoinColumn({
        name'user_id'
    })
    user: User;
}
cs

1:N 관계에서 N인 엔티티는 @ManyToOne데코레이션을 지정하고 어떤 엔티티의 타입과 관계를 맺을지 상대 엔티티 타입을 지정하고 FK의 컬럼(속성)을 지정한다.

@JoinColumn데코레이션으로 User 테이블의 FK로 사용 되는 컬럼이름을 지정할 수 있다.
※ 이름을 지정하지 않으면 기본 userID로 생성된다.

그리고 user: User; 추가로 FK 엔티티 객체 속성을 추가한다.


#ORM 다루기

A. DB접속, 엔티티 동기화

[\src\index.ts]
[index.ts]
import "reflect-metadata";
import {createConnection} from "typeorm";
import {User} from "./entity/User";
import {Board} from "./entity/Board";
import { getRepository } from 'typeorm';
 
const createDB = async () => {
    try {
        const connection = await createConnection();
        // typeORM db connection
        console.log('typeORM DB 커넥션 생성됨');
 
        // 엔티티 모델 기준으로 테이블 생성 및 관계설정
        connection.synchronize();
        console.log('typeORM DB 동기화 완료');
 
        return 'done';
    }
    catch (err) {
        console.log(err);
    }
};
 
createDB()
    .then((text: string = 'finished'=> {
        console.log(text);
    })
    .catch((err) => {
        console.error(err);
    });
 

// 출력
typeORM DB 커넥션 생성됨
typeORM DB 동기화 완료
[]
done
cs

createConnection()함수로 DB접속과 동시에 Connection객체를 받고 synchronize()함수로 엔티티 모델과 동기화
처리를 한다.


B. 데이터 INSERT

[\src\index.ts]
[index.ts]
const write_User = async (user: User) => {
    const connection = await createConnection();
 
    let users = await getRepository(User).find();
    let duplicateID = users.some( p => p.id == user.id);
    if(duplicateID) {
        console.info('이미 등록된 아이디 입니다.');
        return 'ID already registered';
    }
 
    try {
        user.save();
        return 'ok';
    }
    catch(err) {
        console.error(err);
        return err;
    }
};
 
const write_Board = async (board: Board, user: User) => {
    const connection = await createConnection();
 
    let users = await getRepository(User).find( {id: user.id} );
    if(users == null || users.length <= 0) {
        console.info('등록되어 있지 않은 아이디 입니다.');
        return 'Unregistered user';
    }
 
    board.user = users[0];
 
    try {
        board.save();
        return 'ok';
    }
    catch(err) {
        console.error(err);
        return err;
    }
};
cs

getRepository()함수를 통해 엔티티 모델을 가져 올 수 있고, find() / findOne() 함수로 실제 매핑된 데이터를
불러 올 수 있다.

// SQL : SELECT * FROM User WHERE id = '{userID}'
let user = await getRepository(User).findOne( {id: userID} );


// SQL : SELECT * FROM Board WHERE user_id = {user.id}
let boards = await Board.find( { user: user} );


그리고 엔티티모델의 save()함수를 통해 데이터를 저장한다.


C. 데이터 SELECT

[\src\index.ts]
[index.ts]
const getBoard = async (userID: string | null=> {
    const connection = await createConnection();
 
    // SQL : SELECT * FROM Board
    if(userID == null) {
        let boards = await Board.find();
        console.log(boards);
        return;
    }
 
    // SQL : SELECT * FROM User WHERE id = '{userID}'
    let user = await getRepository(User).findOne( {id: userID} );
    if(user == nullreturn;
 
    // SQL : SELECT * FROM Board WHERE user_id = {user.id}
    let boards = await Board.find( { user: user} );
    console.log(boards);
    return;
};
cs

마찬가지로 getRepository()함수를 통해 엔티티 모델을 가져 오고 find() / findOne() 함수를 통해 데이터를 불러온다.


다음은 위 코드조각의 전체 코드이다.

[\src\index.ts]
[index.ts]
import "reflect-metadata";
import {createConnection} from "typeorm";
import {User} from "./entity/User";
import {Board} from "./entity/Board";
import { getRepository } from 'typeorm';
 
const createDB = async () => {
    try {
        const connection = await createConnection();
        // typeORM db connection
        console.log('typeORM DB 커넥션 생성됨');
 
        // 엔티티 모델 기준으로 테이블 생성 및 관계설정
        connection.synchronize();
        console.log('typeORM DB 동기화 완료');
 
        return 'done';
    }
    catch (err) {
        console.log(err);
    }
};
 
const write_User = async (user: User) => {
    const connection = await createConnection();
 
    let users = await getRepository(User).find();
    let duplicateID = users.some( p => p.id == user.id);
    if(duplicateID) {
        console.info('이미 등록된 아이디 입니다.');
        return 'ID already registered';
    }
 
    try {
        user.save();
        return 'ok';
    }
    catch(err) {
        console.error(err);
        return err;
    }
};
 
const write_Board = async (board: Board, user: User) => {
    const connection = await createConnection();
 
    let users = await getRepository(User).find( {id: user.id} );
    if(users == null || users.length <= 0) {
        console.info('등록되어 있지 않은 아이디 입니다.');
        return 'Unregistered user';
    }
 
    board.user = users[0];
 
    try {
        board.save();
        return 'ok';
    }
    catch(err) {
        console.error(err);
        return err;
    }
};
 
const getBoard = async (userID: string | null=> {
    const connection = await createConnection();
 
    // SQL : SELECT * FROM Board
    if(userID == null) {
        let boards = await Board.find();
        console.log(boards);
        return;
    }
 
    // SQL : SELECT * FROM User WHERE id = '{userID}'
    let user = await getRepository(User).findOne( {id: userID} );
    if(user == nullreturn;
 
    // SQL : SELECT * FROM Board WHERE user_id = {user.id}
    let boards = await Board.find( { user: user} );
    console.log(boards);
    return;
};
 
// DB 동기화
 
createDB()
    .then((text: string = 'finished'=> {
        console.log(text);
    })
    .catch((err) => {
        console.error(err);
    });
 
 
// User 모델 생성
let user: User = new User();
user.id = 'test2';
user.password = 'aaaa'
user.name = '테스트';
user.level = 1;
user.etc = '기타';
 
// User INSERT
console.info('User INSERT');
write_User(user)
    .then((result: string) => {
        console.info('result', result);
    })
    .catch((err) => {
        console.error(err);
    });
 
// Board 모델 생성
let board: Board = new Board();
board.name = "testBoard";
board.title = '제목';
board.contents = '내용';
board.level = 0;
board.etc = '기타';
board.recordDate = new Date();
 
let board_user: User = new User();
board_user.id = 'test2';
 
// Board INSERT
console.info('Board INSERT');
write_Board(board, board_user)
    .then((result: string) => {
        console.info('result', result);
    })
    .catch((err) => {
        console.error(err);
    });
 
// Get Board
getBoard('test2');
cs


간단하게 유저정보(User)와 게시판정보(Board)의 엔티티를 생성하고 데이터 다루는 방법 샘플 코드를 작성해 보았다.

DB테이블 스키마

실제 DB 데이터 조회

출처1

출처2




2020-09-15 09:17
실행은 npm start 명령으로 실행한다.
관리자
(172.17.0.1)