Post

TypeORM

한글로 번역된 사이트를 보고, 정리한 글입니다. https://orkhan.gitbook.io/typeorm/readme_ko

사용방법이 변경되어 타입오알엠 공식페이지도 꼭 확인해야합니다. https://typeorm.io/

typeorm

🧐 프로젝트 시작하기

1. 설치 명령어

1
npm install typeorm reflect-metadata

2. app.ts에 모듈을 등록

1
import "reflect-metadata";

3. DB 드라이버 설치 - mariaDB 또는 mysql

1
npm install mysql2

4. 타입스크립트 환경 설정

  • 3.3 버전 이상
  • tsconfig.json에 다음 설정 추가
1
2
"emitDecoratorMetadata": true,
"experimentalDecorators": true,

5. data-source.ts파일에 데이터베이스와 연결하는 코드 작성하기

1
2
3
4
5
6
7
8
9
10
11
12
13
export const AppDataSource = new DataSource({
  type: "postgres",
  host: "localhost",
  port: 5432,
  username: "test",
  password: "test",
  database: "test",
  synchronize: true,
  logging: true,
  entities: [Post, Category],
  subscribers: [],
  migrations: []
});

🧐 디렉토리 구조

1
2
3
4
5
6
7
8
9
10
11
MyProject
├── src
│   ├── entity       // 모델 저장
│   │   └── User.ts  // 샘플 모델
│   ├── migration    // 마이그레이션 파일 저장
│   ├── data-source.ts // ORM과 database을 연결하는 코드 작성
│   └── index.ts     // 애플리케이션 시작점
├── .gitignore       // gitignore file
├── package.json
├── README.md
└── tsconfig.json

🧐 데이터베이스와 연결하기

createConnection을 사용하여 데이터베이스와 연결할 수 있다. 주로 index.ts 또는 app.ts에 작성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import "reflect-metadata";
import { createConnection } from "typeorm";
import { Photo } from "./entity/Photo";

createConnection({
  type: "mysql",
  host: "localhost",
  port: 3306,
  username: "root",
  password: "admin",
  database: "test",
  entities: [Photo],
  synchronize: true,
  logging: false
})
  .then((connection) => {
    // here you can start to work with your entities
  })
  .catch((error) => console.log(error));

✅type

type에는 사용한 데이터베이스를 지정한다.

  • mysql, mariadb, postgres, cockroachdb, sqlite, mssql, oracle, cordova, nativescript, react-native, expo, or mongodb

✅entities

entities 목록이며, 사용할 엔터티들을 추가해야한다.

  • 매번 엔터티를 설정에 추가하기는 불편하기 때문에 다음과 같은 코드를 작성하여, 모든 엔터티를 불러올 수 있다.
1
2
3
entities: [
        __dirname + "/entity/*.js"
    ],

✅synchronize

synchronize를 true로 설정해두면, 애플리케이션이 실행될 때마다 엔터티가 데이터베이스와 동기화된다.

🧐 테이블 생성하기

타입오알엠에서의 모델은 데이터베이스의 테이블이다. 모델을 통해서 데이터베이스 테이블을 생성할 수 있다.

@Entity를 사용하여 모델 작성하기

1
2
3
4
5
6
7
8
9
10
11
import { Entity } from "typeorm";

@Entity()
export class Photo {
  id: number;
  name: string;
  description: string;
  filename: string;
  views: number;
  isPublished: boolean;
}

@Column를 사용하여 테이블 열 추가하기

상단의 @Entity를 사용하여 만든 모델을 보면, 테이블에 열이 존재하지 않는다. @Column를 사용하여 열을 추가할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { Entity, Column } from "typeorm";

@Entity()
export class Photo {
  @Column()
  id: number;

  @Column()
  name: string;

  @Column()
  description: string;

  @Column()
  filename: string;

  @Column()
  views: number;

  @Column()
  isPublished: boolean;
}

@Column()에 옵션 값을 입력할 수도 있다.

@예시 1

1
@Column({nullable:true,unique:true})

위의 코드는 다음과 같은 옵션을 지정한 것이다.

  • null을 허용하는지 결정하는 nullable
  • 유일한 값을 의미하는 unique

@예시 2

1
@Column({ length: 15, nullable: true, comment: '전화번호' })

위의 코드는 다음과 같은 옵션을 지정한 것이다.

  • null을 허용하는지 결정하는 nullable
  • 전체 길이를 지정하는 length
  • 칼럼에 대한 설명을 작성한 comment

@PrimaryColumn를 사용하여 기본 열 생성하기

우리는 @Column을 사용하여 테이블에 열을 추가해주었다. 하지만 테이블에는 기본 키가 있는 열이 꼭 존재해야한다. 이는 @PrimaryColumn하여 기본 열을 지정해줄 수 있다.

다음은 id 열을 기본 열로 생성한 코드 예시이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { Entity, Column, PrimaryColumn } from "typeorm";

@Entity()
export class Photo {
  @PrimaryColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  description: string;

  @Column()
  filename: string;

  @Column()
  views: number;

  @Column()
  isPublished: boolean;
}

@PrimaryGeneratedColumn를 사용하여 기본 열 생성하기

@PrimaryColumn을 사용하여 기본 열을 설정해주었다. 하지만 id는 자동으로 생성(자동으로 증가)되는 경우가 많다. 이렇게 자동 생성을 하기 위해서는 @PrimaryGeneratedColumn을 사용해야한다.

다음은 @PrimaryGeneratedColumn을 사용하여 id가 자동으로 생성되게 하는 코드 예시이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";

@Entity()
export class Photo {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  description: string;

  @Column()
  filename: string;

  @Column()
  views: number;

  @Column()
  isPublished: boolean;
}

🧐 데이터베이스 관계 생성하기

@OneToOne

@OneToOne을 사용하면 1:1 관계를 만들 수 있으며, type => 엔터티 클래스를 사용하여 관계를 만들고자 하는 엔터티의 클래스를 명시해주어야한다.

관계를 소유하는 쪽에 @JoinColumn를 작성하여 관계를 소유하고 있음을 명시해주어야한다.

예시 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import {
  Entity,
  Column,
  PrimaryGeneratedColumn,
  OneToOne,
  JoinColumn
} from "typeorm";
import { Photo } from "./Photo";

@Entity()
export class PhotoMetadata {
  @PrimaryGeneratedColumn()
  id: number;

  @Column("int")
  height: number;

  @Column("int")
  width: number;

  @OneToOne((type) => Photo)
  @JoinColumn()
  photo: Photo;
}

@OneToMany/@ManyToOne

@OneToMany은 1:N의 관계(일대다)를 생성할 때 사용하고, @ManyToOne은 N:1 관계(다대일)를 생성할 때 사용한다. @OneToMany@ManyToOne없이는 존재할 수 없습니다. 이 관계에서 소유자는 항상 다대일(@ManyToOne)입니다. @ManyToOne사용하는 객체가 관련 객체의 id를 저장한다는 의미입니다.

예를 들어, 하나의 주문이 여러개의 포함할 수 있습니다. 여기서 주문은 1이고, 제품은 N이 됩니다. 이 관계를 구현할 때 주문 엔터티에는 여러 제품이 있으므로 주문 엔터티가 “다” 이며, 이것이 일대다 관계(@OneToMany)입니다. 그리고 제품 엔터티에는 주문과 관련된 주문 id가 있으므로, 여러 제품이 하나의 주문에 속할 수 있습니다. 이것이 다대일 관계(@ManyToOne)입니다.

#주문 엔터티

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Order.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm";
import { Product } from "./Product";

@Entity()
export class Order {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  orderName: string;

  @OneToMany(() => Product, (product) => product.order)
  products: Product[];
}

#제품 엔터티

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Product.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm";
import { Order } from "./Order";

@Entity()
export class Product {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  productName: string;

  @ManyToOne(() => Order, (order) => order.products)
  order: Order;
}

🧐 데이터베이스에 데이터 추가하기

index.ts 를 실행하면 자동으로 데이터베이스와의 연결이 초기화가 되며, 테이블이 생성된다.

save를 사용하여 데이터를 추가할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
import { Photo } from "./entity/Photo";
import { AppDataSource } from "./index";

const photo = new Photo();
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg";
photo.views = 1;
photo.isPublished = true;

await AppDataSource.manager.save(photo);
console.log("Photo has been saved. Photo id is", photo.id);

🧐 데이터베이스에 데이터 조회하기

find()를 사용하여 여러개의 데이터를 조회하고, findOne()을 사용하여 특정 데이터를 조회한다.

예전 메소드

  • find(): 전체 조회
  • find({조건}): 조건에 맞는 모든 데이터 조회
  • findOne(id): 아이디값을 사용하여 데이터 조회
  • findOne({조건}): 조건에 맞는 데이터 조회

변경된 메소드

  • find(): 전체 조회
  • findBy({조건}): 조건에 맞는 모든 데이터 조회
  • findOneBy({조건}): : 조건에 맞는 데이터 조회
  • findAndCount(): 모든 데이터를 조회하고, 데이터의 갯수 반환

예시 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { Photo } from "./entity/Photo";
import { AppDataSource } from "./index";

const photoRepository = AppDataSource.getRepository(Photo);
const allPhotos = await photoRepository.find();
console.log("All photos from the db: ", allPhotos);

const firstPhoto = await photoRepository.findOneBy({
  id: 1
});
console.log("First photo from the db: ", firstPhoto);

const meAndBearsPhoto = await photoRepository.findOneBy({
  name: "Me and Bears"
});
console.log("Me and Bears photo from the db: ", meAndBearsPhoto);

const allViewedPhotos = await photoRepository.findBy({ views: 1 });
console.log("All viewed photos: ", allViewedPhotos);

const allPublishedPhotos = await photoRepository.findBy({ isPublished: true });
console.log("All published photos: ", allPublishedPhotos);

const [photos, photosCount] = await photoRepository.findAndCount();
console.log("All photos: ", photos);
console.log("Photos count: ", photosCount);

🧐 데이터베이스에 데이터 업데이트하기

findOneBy을 사용하여 업데이트할 데이터를 찾고, save를 사용하여 데이터를 업데이트한다.

예시 코드

1
2
3
4
5
6
7
8
9
import { Photo } from "./entity/Photo";
import { AppDataSource } from "./index";

const photoRepository = AppDataSource.getRepository(Photo);
const photoToUpdate = await photoRepository.findOneBy({
  id: 1
});
photoToUpdate.name = "Me, my friends and polar bears";
await photoRepository.save(photoToUpdate);

🧐 데이터베이스에 데이터 삭제

findOneBy을 사용하여 삭제할 데이터를 찾고, remove를 사용하여 데이터를 삭제한다.

예시 코드

1
2
3
4
5
6
7
8
import { Photo } from "./entity/Photo";
import { AppDataSource } from "./index";

const photoRepository = AppDataSource.getRepository(Photo);
const photoToRemove = await photoRepository.findOneBy({
  id: 1
});
await photoRepository.remove(photoToRemove);
This post is licensed under CC BY 4.0 by the author.