다음과 같이, 퓨어한 코드에서는 JS와 SQL문이 혼용된 복잡한 코드를 작성해야하지만 typeORM을 사용하면 간단한 코드로 끝낼수 있습니다
// Ashley가 작성하고 상태가 "DONE"인 task들을 가져온다.
// TypeORM
const tasks = await Task.find({ status: 'DONE', user: 'Ashley' })
// Pure JavaScript
let tasks;
db.query('SELECT * FROM tasks WHERE status = "DONE" AND user = "Ashley"', (err, result) => {
if (err) {
throw new Error("Could not retrieve tasks");
}
tasks = result.rows;
})
객체 지향적인 코드
재사용, 유지보수, 리팩토링이 용이
DBMS에 대한 종속성을 줄일 수 있다
ORM은 모든 것을 해결해주지 않는다
객체-관계 간의 태생적인 불일치는 해결해주지 않는다
따라서 ORM에 너무 의존한다면 이런 단점들에 의해 발생하는 문제들을 해결하는 방법을 터득하지 못할 확률이 높습니다
그래서 SQL 공부를 꾸준히 해야하지 않을까 합니다
매핑이란 해당 값이 다른 값을 가리키도록 하는 것
다른 데이터 셋과 대응 관계를 가지고 있는 일련의 데이터 셋을 지칭합니다
Active Record 패턴과 Data Mapper 패턴을 지원합니다
@Entity()
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
isActive: boolean;
static findByName(firstName: string, lastName: string) {
return this.createQueryBuilder('user')
.where('user.firstName = :firstName', { firstName })
.andWhere('user.lastName = :lastName', { lastName })
.getMany();
}
}
// 분리
// example how to save AR entity
const user = new User();
user.firstName = 'Timber';
user.lastName = 'Saw';
user.isActive = true;
await user.save();
// example how to remove AR entity
await user.remove();
// example how to load AR entities
const users = await User.find({ skip: 2, take: 5 });
const newUsers = await User.find({ isActive: true });
const timber = await User.findOne({ firstName: 'Timber', lastName: 'Saw' });
const timber = await User.findByName('Timber', 'Saw');
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
isActive: boolean;
}
// 분리
import { EntityRepository, Repository } from 'typeorm';
import { User } from '../entity/User';
@EntityRepository()
export class UserRepository extends Repository<User> {
findByName(firstName: string, lastName: string) {
return this.createQueryBuilder('user')
.where('user.firstName = :firstName', { firstName })
.andWhere('user.lastName = :lastName', { lastName })
.getMany();
}
}
// 분리
const userRepository = connection.getRepository(User);
// example how to save DM entity
const user = new User();
user.firstName = 'Timber';
user.lastName = 'Saw';
user.isActive = true;
await userRepository.save(user);
// example how to remove DM entity
await userRepository.remove(user);
// example how to load DM entities
const users = await userRepository.find({ skip: 2, take: 5 });
const newUsers = await userRepository.find({ isActive: true });
const timber = await userRepository.findOne({
firstName: 'Timber',
lastName: 'Saw',
});
TypeOrmModule.forRoot({
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_DATABASE,
entities: [__dirname + '/apis/**/*.entity.*'],
synchronize: true,
logging: true,
retryAttempts: 30,
retryDelay: 5000,
timezone: 'Z',
}),
typeORM을 설치할때 옵션을 보통 위와 같이 설정해서 typeORM 모듈을 app.module.ts의 import에 위치시킵니다
synchronize : 애플리케이션에 작동할 때마다 DB Schema가 자동으로 만들어지게 할 것인가를 설정
logging: 로깅을 할 것인가 하지 않을 것인가를 설정
entities: 연결에 사용되며 로드되는 entity. 설정한 entity class와 로드하기 위한 directory path를 허용
timezone: ORM의 타임존을 설정합니다. 저는 db를 도커로 올리는 과정에서 타임존을 한국시간으로 설정해놓았기 때문에, 저렇게 Z
만 입력해서 ORM이 database 자체의 타임존을 따라가도록 했습니다
엔티티나 레포지토리, 쿼리빌더, 쿼리러너 등 다양한 기능들이 있지만 일단 초기 설정 값만 포스팅하고 나머지는 따로 포스팅하겠습니다