TypeORM이란
TypeORM은 TypeScript와 JavaScript를 위한 ORM(Object-Relational Mapping) 라이브러리로, 데이터베이스와의 상호작용을 쉽게 만들어준다. ORM은 관계형 데이터베이스의 데이터를 객체 지향 프로그래밍에서 사용하는 객체로 매핑해주는 도구이다.
TypeORM의 기능
- 엔티티(Entity) 및 리포지토리(Repository): TypeORM은 엔티티를 정의하고, 이를 데이터베이스의 테이블과 매핑한다. 엔티티는 데이터베이스의 테이블과 일치하며, 리포지토리를 통해 엔티티를 생성, 조회, 수정, 삭제할 수 있다.
- 마이그레이션(Migration): 데이터베이스 스키마의 변경 사항을 추적하고, 이를 코드로 관리할 수 있도록 해준다. 이를 통해 데이터베이스 스키마를 쉽게 업데이트하고, 버전 관리를 할 수 있다.
- 관계 설정: TypeORM은 다양한 관계를 설정할 수 있다. 일대일, 일대다, 다대일, 다대다와 같은 관계를 표현하고, 데이터베이스에서 관리한다.
- 트랜잭션(Transaction): 여러 쿼리를 하나의 논리적 단위로 묶어서 실행하고, 모든 쿼리가 성공하면 변경 사항을 커밋한다. 실패 시 롤백할 수 있다.
- 쿼리 빌더 및 검색(Query Builder & Querying): TypeORM은 쿼리 작성을 단순화하고, 복잡한 쿼리를 타입 안정성을 유지하며 작성할 수 있는 편리한 쿼리 빌더를 제공한다.
TypeORM을 사용하면 TypeScript와 JavaScript 환경에서 데이터베이스 작업을 쉽게 처리할 수 있으며, 객체 지향적인 방식으로 데이터를 관리할 수 있다. NestJS, Express 등의 프레임워크와 함께 자주 사용되어, 데이터베이스 관련 작업을 간편하게 수행할 수 있다.
TypeORM 예시
// index.ts
import { createConnection, Connection, Entity, PrimaryGeneratedColumn, Column, Repository } from 'typeorm';
// 엔티티(테이블) 정의
@Entity()
class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
username: string;
@Column()
email: string;
}
async function start() {
// 데이터베이스 연결 설정
const connection: Connection = await createConnection({
type: 'sqlite',
database: 'example.db',
entities: [User],
synchronize: true, // 개발 중에만 true로 설정 (스키마 자동 동기화)
});
// UserRepository 생성
const userRepository: Repository<User> = connection.getRepository(User);
// 새로운 사용자 생성 및 저장
const newUser: User = await userRepository.save({
username: 'john_doe',
email: 'john@example.com',
});
// 모든 사용자 조회
const allUsers: User[] = await userRepository.find();
console.log('모든 사용자:', allUsers);
// 특정 사용자 조회
const specificUser: User | undefined = await userRepository.findOne({ username: 'john_doe' });
if (specificUser) {
console.log('특정 사용자:', specificUser);
}
// 데이터베이스 연결 닫기
await connection.close();
}
TypeORM에서 자주 사용되는 함수와 메서드 예시
1. save() : 새로운 엔티티를 생성하거나 이미 존재하는 엔티티를 업데이트하고, 데이터베이스에 저장한다.
// 새로운 엔티티 생성 및 저장
const newPost = await postRepository.save({
title: '새로운 포스트',
content: '이것은 새로운 포스트의 내용입니다.',
});
2. find() : 조건에 맞는 모든 엔티티를 검색한다.
// 모든 사용자 검색
const allUsers = await userRepository.find();
console.log('모든 사용자:', allUsers);
3. findOne() : 주어진 조건에 따라 첫 번째로 일치하는 엔티티를 검색한다.
// username이 'john_doe'인 특정 사용자 검색
const specificUser = await userRepository.findOne({ username: 'john_doe' });
if (specificUser) {
console.log('특정 사용자:', specificUser);
}
4. findAndCount() : 조건에 따른 검색 결과와 일치하는 엔티티의 총 개수를 반환한다.
// username이 'john_doe'인 사용자 검색 및 검색된 사용자 수 반환
const [users, totalCount] = await userRepository.findAndCount({ username: 'john_doe' });
console.log('특정 사용자:', users);
console.log('총 사용자 수:', totalCount);
5. remove() : 엔티티를 삭제합니다. 데이터베이스에서 해당 엔티티를 삭제한다.
// 특정 사용자 삭제
const userToRemove = await userRepository.findOne({ username: 'john_doe' });
if (userToRemove) {
await userRepository.remove(userToRemove);
}
6. update() : 주어진 조건에 맞는 엔티티의 일부나 전체를 업데이트한다.
// username이 'john_doe'인 사용자의 이메일 업데이트
await userRepository.update({ username: 'john_doe' }, { email: 'new_email@example.com' });
7. createQueryBuilder() : QueryBuilder를 생성하고, 이를 통해 직접 SQL 쿼리를 작성하여 데이터를 검색할 수 있다.
// QueryBuilder를 사용하여 직접 SQL 쿼리 작성
const users = await userRepository.createQueryBuilder('user')
.where('user.username = :username', { username: 'john_doe' })
.orWhere('user.email = :email', { email: 'john@example.com' })
.getMany();
console.log('특정 사용자:', users);
8. createQueryBuilder().select() : QueryBuilder로 특정 열을 선택하여 조회한다.
// QueryBuilder를 사용하여 특정 열 선택
const usernames = await userRepository.createQueryBuilder('user')
.select('user.username')
.getRawMany();
console.log('유저네임 리스트:', usernames);
9. createQueryBuilder().update() : 데이터베이스에서 엔티티를 업데이트한다.
// QueryBuilder를 사용하여 username이 'john_doe'인 사용자의 이메일 업데이트
await userRepository.createQueryBuilder()
.update(User)
.set({ email: 'updated_email@example.com' })
.where('username = :username', { username: 'john_doe' })
.execute();
10. createQueryBuilder().delete() : 데이터베이스에서 엔티티를 삭제한다.
// QueryBuilder를 사용하여 username이 'john_doe'인 사용자 삭제
await userRepository.createQueryBuilder()
.delete()
.where('username = :username', { username: 'john_doe' })
.execute();
11. QueryBuilder의 Join 활용
// 두 엔티티 간의 Inner Join 수행
const result = await userRepository.createQueryBuilder('user')
.innerJoin('user.posts', 'post')
.where('user.id = :userId', { userId: 1 })
.getMany();
console.log('Inner Join 결과:', result);
12. QueryBuilder의 서브쿼리 활용
// 서브쿼리를 사용하여 특정 조건을 만족하는 사용자 검색
const users = await userRepository.createQueryBuilder('user')
.where(qb => {
const subQuery = qb.subQuery()
.select('post.userId')
.from('Post', 'post')
.where('post.title = :title', { title: 'TypeORM' })
.getQuery();
return 'user.id IN ' + subQuery;
})
.getMany();
console.log('서브쿼리 결과:', users);
13. 트랜잭션 활용
// 트랜잭션 내에서 여러 작업을 수행하고 롤백 기능 사용
await connection.transaction(async transactionalEntityManager => {
const newUser = new User();
newUser.username = 'alice';
await transactionalEntityManager.save(newUser);
const newPost = new Post();
newPost.title = 'New Post';
newPost.user = newUser;
await transactionalEntityManager.save(newPost);
// 여기서 오류 발생 시 롤백됨
throw new Error('트랜잭션 테스트를 위한 오류');
});
14. 페이징 및 제한된 결과 가져오기
// 페이징 처리하여 결과 가져오기
const pageSize = 10;
const pageNumber = 1;
const paginatedUsers = await userRepository.find({
take: pageSize,
skip: (pageNumber - 1) * pageSize,
});
console.log('페이징된 사용자 리스트:', paginatedUsers);
15. Raw Query 직접 실행
// Raw Query 직접 실행
const results = await connection.query('SELECT * FROM users WHERE age > ? AND active = ?', [20, true]);
console.log('Raw Query 결과:', results);
16. Column의 데이터 그룹화
// 데이터 그룹화를 통한 Column 조회
const groupedData = await userRepository.createQueryBuilder('user')
.select('user.role, COUNT(user.id) as userCount')
.groupBy('user.role')
.getRawMany();
console.log('데이터 그룹화 결과:', groupedData);
17. 데이터 변경 전 이벤트 리스너
// 데이터 변경 전 이벤트 리스너 등록
import { EventSubscriber, EntitySubscriberInterface, InsertEvent } from 'typeorm';
@EventSubscriber()
export class UserSubscriber implements EntitySubscriberInterface<User> {
listenTo() {
return User;
}
beforeInsert(event: InsertEvent<User>) {
console.log('데이터가 삽입되기 전:', event.entity);
}
}
// UserSubscriber를 사용하여 이벤트 구독자 등록
connection.subscribers.push(UserSubscriber);
18. Column 속성 변경
// Column 속성 변경
@Column({ type: 'varchar', length: 100, nullable: true })
address: string | null;
19. Entity의 상태 추적
// 엔티티 상태 추적 및 변경 감지
const user = await userRepository.findOne(1);
if (user) {
user.username = 'new_username';
const updatedUser = await userRepository.save(user);
console.log('변경된 엔티티:', updatedUser);
}
20. Relation 관계 설정
// 다대일 관계 설정
@Entity()
class Order {
@ManyToOne(() => User, user => user.orders)
user: User;
}
21. Custom Repository 사용
import { EntityRepository, Repository } from 'typeorm';
@EntityRepository(User)
class CustomUserRepository extends Repository<User> {
// 사용자 정의 메서드 작성
async findActiveUsers() {
return this.find({ isActive: true });
}
}
// CustomUserRepository를 사용하여 쿼리 실행
const customUserRepository = getCustomRepository(CustomUserRepository);
const activeUsers = await customUserRepository.findActiveUsers();
console.log('활성 사용자:', activeUsers);'개발 공부' 카테고리의 다른 글
| Node.js (0) | 2024.07.15 |
|---|---|
| 동기(synchronous) 와 비동기(asynchronous) 함수 (0) | 2024.07.15 |
| TypeORM - QueryBuilder 2 (0) | 2023.11.21 |
| TypeORM - QueryBuilder 1 (0) | 2023.11.20 |
| REST API와 GraphQL (0) | 2023.11.16 |