TypeORM에 관해 공부하다가 공식홈페이지를 방문하게 되었다.
다른 블로그 글보다 훨씬 깔끔하게 적혀있으며 이해하기 쉬웠다.
가능하면 공식홈페이지에서 보는 것을 추천한다.
다만 나는 내가 필요한 부분만 골라서 요약정리할 생각이다.
공식홈페이지 https://typeorm.io/
단계별 가이드
# 모델 생성
데이터베이스 작업은 테이블 생성에서 시작됩니다. TypeORM에게 데이터베이스 테이블을 생성하도록
어떻게 지시합니까? 답은 모델을 통해서입니다. 앱의 모델은 데이터베이스 테이블입니다.
export class Photo {
id: number
name: string
description: string
filename: string
views: number
isPublished: boolean
}
그리고 데이터베이스에 사진을 저장하려고 합니다. 데이터베이스에 물건을 저장하려면
먼저 데이터베이스 테이블이 필요하고 모델에서 데이터베이스 테이블이 생성됩니다.
모든 모델이 아니라 엔티티 로 정의한 모델만 해당됩니다 .
# 엔티티 생성
엔티티는 데코레이터 가 장식한 모델입니다. 이러한 모델에 대한 데이터베이스 테이블이 생성됩니다.
TypeORM의 모든 곳에서 엔티티로 작업합니다. 로드/삽입/업데이트/제거하고 다른 작업을 수행할 수 있습니다.
import { Entity } from "typeorm"
@Entity()
export class Photo {
id: number
name: string
description: string
filename: string
views: number
isPublished: boolean
}
우리는 데이터베이스 테이블을 생성했지만 열 없이 존재할 수 있는 테이블은 무엇입니까?
데이터베이스 테이블에 몇 개의 열을 만들어 보겠습니다.
# 테이블 열 추가 (최종)
데이터베이스 열을 추가하려면 데코레이터를 사용하여 열로 만들려는 엔터티의 속성을 장식하기만 하면 됩니다
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm"
@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number
@Column({
length: 100,
})
name: string
@Column("text")
description: string
@Column()
filename: string
@Column("double")
views: number
@Column()
isPublished: boolean
}
# 기본메소드
find(): 주어진 조건에 맞는 데이터를 조회하는 메소드입니다. 조회 결과는 배열 형태로 반환됩니다.
findOneBy(): 주어진 조건에 맞는 데이터 중 첫 번째 데이터를 조회하는 메소드입니다.
조회 결과는 객체 형태로 반환됩니다.
findAndCount(): 주어진 조건에 맞는 데이터를 조회하고,
해당 데이터의 총 개수를 반환하는 메소드입니다. 조회 결과는 배열 형태로 반환됩니다.
save(): 새로운 데이터를 추가하거나, 기존 데이터를 수정하는 메소드입니다.
만약 주어진 데이터가 이미 존재한다면 수정되고, 존재하지 않는다면 새로 추가됩니다.
remove(): 주어진 조건에 맞는 데이터를 삭제하는 메소드입니다.
updateOne(): 주어진 조건에 맞는 첫 번째 문서를 수정하는 메소드입니다.
updateMany(): 주어진 조건에 맞는 모든 문서를 수정하는 메소드입니다.
countDocuments(): 주어진 조건에 맞는 문서의 개수를 반환하는 메소드입니다.
distinct(): 주어진 필드에 대해 중복을 제거한 유일한 값을 반환하는 메소드입니다.
aggregate(): 복잡한 데이터 처리를 위한 파이프라인을 생성하여 데이터를 조회하는 메소드입니다.
populate(): 다른 컬렉션에 있는 데이터를 참조하고 있는 필드의 값을 해당 컬렉션의 데이터로 채우는 메소드입니다.
sort(): 조회 결과를 정렬하는 메소드입니다.
limit(): 조회 결과를 제한하는 메소드입니다.
skip(): 조회 결과에서 일부 데이터를 제외하는 메소드입니다.
# 일대일 관계 만들기
우리는 또한 @JoinColumn데코레이터를 추가하여 관계의 이 쪽이 관계를 소유할 것임을 나타냅니다.
관계는 단방향 또는 양방향일 수 있습니다. 관계형의 한쪽만 소유할 수 있습니다.
데코레이터를 사용하는 @JoinColumn은 관계의 소유자 측에서 필요합니다.
@Entity()
export class PhotoMetadata {
/* ... other columns */
@OneToOne(() => Photo)
@JoinColumn()
photo: Photo
}
# 관계의 반대면
관계는 단방향 또는 양방향일 수 있습니다. 현재 PhotoMetadata와 Photo 간의 관계는 단방향입니다.
관계의 소유자는 PhotoMetadata이고 Photo는 PhotoMetadata에 대해 아무것도 모릅니다.
이로 인해 사진 쪽에서 PhotoMetadata에 액세스하기가 복잡해집니다.
이 문제를 해결하려면 역 관계를 추가하고 PhotoMetadata와 Photo 간의 관계를 양방향으로 만들어야 합니다. 엔
@Entity()
export class PhotoMetadata {
/* ... other columns */
@OneToOne(() => Photo, (photo) => photo.metadata)
@JoinColumn()
photo: Photo
}
@Entity()
export class Photo {
/* ... other columns */
@OneToOne(() => PhotoMetadata, (photoMetadata) => photoMetadata.photo)
metadata: PhotoMetadata
}
# 다른 표기법(GPT 문의결과)
@OneToOne(() => PhotoMetadata, (photoMetadata) => photoMetadata.photo)
metadata: PhotoMetadata
@OneToOne(() => PhotoMetadata, (photoMetadata) => photoMetadata.photo)
metadata: Relation<PhotoMetadata>
결론은 두번째 코드를 사용하는 것이 좋아보인다.
첫 번째 코드에서는 metadata 속성이 PhotoMetadata 객체를 직접 참조하고 있습니다.
반면 두 번째 코드에서는 metadata 속성의 타입이 Relation<PhotoMetadata>으로 정의되어 있습니다.
이 차이점은 타입스크립트 타입과 ORM의 동작 방식과 관련이 있습니다.
첫 번째 코드에서는 metadata 속성이 PhotoMetadata 객체를 직접 참조하므로,
해당 속성을 사용하여 Photo 객체를 로드하면 PhotoMetadata 객체가 자동으로 연결됩니다.
반면 두 번째 코드에서는 metadata 속성의 타입이 Relation<PhotoMetadata>으로 정의되어 있으므로,
해당 속성을 사용하여 Photo 객체를 로드하더라도 PhotoMetadata 객체는 자동으로 연결되지 않습니다.
대신, 해당 속성을 사용하여 PhotoMetadata 객체를 직접 로드해야 합니다.
따라서 두 번째 코드에서는 metadata 속성을 사용하여 PhotoMetadata 객체를 로드하기 전에 Relation<PhotoMetadata> 타입으로 선언된 속성을 사용하여 필요한 쿼리를 생성하고,
이를 실행하여 PhotoMetadata 객체를 로드해야 합니다. 이 방식은 ORM의 성능을 최적화하고,
불필요한 데이터를 로드하지 않도록 도와줍니다.
# 관계와 함께 객체 로드하기
객체로드 하는 방법을 두가지가 있다. 차이점은 간단하게 할거냐, 상세하게 할거냐이므로 선택인듯 하다.
첫번째, TypeORM Repository Query
const photos = await photoRepository.find({
relations: {
metadata: true,
},
})
두번째, TypeORM Query builder
const photos = await AppDataSource.getRepository(Photo)
.createQueryBuilder("photo")
.innerJoinAndSelect("photo.metadata", "metadata")
.getMany()
# 캐스케이드를 사용하여 관련 객체를 자동으로 저장
다른 개체가 저장될 때마다 관련 개체를 저장하려는 경우 관계에서 캐스케이드 옵션을 설정할 수 있습니다.
export class Photo {
// ... other columns
@OneToOne(() => PhotoMetadata, (metadata) => metadata.photo, {
cascade: true,
})
metadata: PhotoMetadata
}
cascade옵션에 대한 설명
cascade 옵션은 관계형 데이터베이스에서 엔티티를 저장, 업데이트, 삭제할 때
연관된 엔티티의 동작을 제어하는 옵션입니다.
엔티티가 저장, 업데이트, 삭제될 때 관련된 PhotoMetadata 엔티티도 함께 저장, 업데이트, 삭제됩니다.
cascade 옵션은 일대일, 일대다, 다대일, 다대다 관계 모두에서 사용할 수 있습니다.
cascade 옵션은 아래와 같은 값들을 가질 수 있습니다.
- remove: 해당 엔티티가 삭제될 때 연관된 엔티티도 함께 삭제됩니다.
- save: 해당 엔티티가 저장될 때 연관된 엔티티도 함께 저장됩니다.
- update: 해당 엔티티가 업데이트될 때 연관된 엔티티도 함께 업데이트됩니다.
- soft-remove: 해당 엔티티가 삭제될 때 연관된 엔티티도 삭제 대신 삭제 플래그를 설정합니다.
- recover: 해당 엔티티가 복원될 때 연관된 엔티티도 함께 복원됩니다.
cascade 옵션은 데이터베이스 작업을 간편하게 해주지만, 관련된 모든 엔티티가 동일한 작업을 수행하기 때문에 주의해서 사용해야 합니다. 특히 대규모 애플리케이션에서는 적절한 cascade 옵션을 설정하는 것이 성능 및 데이터 무결성 관리에 중요합니다.
# 다대일/일대다 관계생성
@Entity()
export class Author {
@OneToMany(() => Photo, (photo) => photo.author)
//참고: 아래 Photo 클래스에 작성자 속성을 생성합니다.
photos: Photo[]
}
@Entity()
export class Photo {
@ManyToOne(() => Author, (author) => author.photos)
author: Author
}
# 다대다 관계생성
@Entity()
export class Album {
@ManyToMany(() => Photo, (photo) => photo.albums)
@JoinTable()
photos: Photo[]
}
export class Photo {
@ManyToMany(() => Album, (album) => album.photos)
albums: Album[]
}
애플리케이션을 실행한 후 ORM은 album_photos_photo_albums 접합 테이블을 생성합니다 .
AlbumORM에서 연결에 클래스를 등록하는 것을 잊지 마십시오 .
이제 데이터베이스에 앨범과 사진을 삽입해 보겠습니다.
import { AppDataSource } from "./index"
// create a few albums
const album1 = new Album()
album1.name = "Bears"
await AppDataSource.manager.save(album1)
const album2 = new Album()
album2.name = "Me"
await AppDataSource.manager.save(album2)
// create a few photos
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
photo.albums = [album1, album2]
await AppDataSource.manager.save(photo)
// now our photo is saved and albums are attached to it
// now lets load them:
const loadedPhoto = await AppDataSource.getRepository(Photo).findOne({
where: {
id: 1,
},
relations: {
albums: true,
},
})
'코딩공부 > Nest.js' 카테고리의 다른 글
Next.js의 서버 렌더링 (0) | 2024.11.01 |
---|---|
[🔥기록용🔥] Nest.js 기능구현때 배운 점 (0) | 2023.03.11 |
🔥 탄탄한 NestJS 필요부분 요약(소켓, 몽구스편) 🔥 (0) | 2023.03.03 |