![Typescript - TypeORM 적용](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2dsFf%2FbtsCDd1QRTi%2F4bldtVGgdX8yR1fygzoh5K%2Fimg.png)
필요 배경 지식
- Typescript
Prerequisite
- TypeORM 라이브러리
TypeORM이란?
TypeORM은 TypeScript와 JavaScript (ES7, ES6, ES5)를 위한 ORM(Object-Relational Mapping) 라이브러리입니다.
이 라이브러리는 SQL 데이터베이스를 객체지향적으로 조작할 수 있게 해주며, Node.js 환경에서 주로 사용됩니다.
결국 TypeORM은 DB 쿼리문에 대한 직접적인 조작없이 객체지향적으로 DB 조작을 할 수 있게 도와주는 라이브러리다.
그래서 이를 잘 활용하면 코드의 가독성을 올리고 유지보수를 쉽게 할 수 있다는 장점이 생긴다.
TypeORM의 권장 디렉토리 구조
Entity Layer - 데이터 베이스 테이블을 클래스로 표현
엔티티는 데이터베이스 테이블을 클래스로 표현한 것입니다. 각 엔티티 인스턴스는 테이블의 한 로우(row)에 해당하며, 엔티티 클래스에는 테이블의 컬럼을 나타내는 프로퍼티들이 정의됩니다.
아래는 DB 정의와 그것을 Entity 코드로 정의한 예시이다.
PostgreSQL의 repo 정의 sql 코드
create table repos (
id serial primary key,
name varchar(255) not null,
is_internal boolean not null,
owner varchar(255) not null
);
repo.entity.ts 파일의 예시
import {Entity, PrimaryColumn, Column} from "typeorm";
@Entity()
export class Repos {
@PrimaryColumn()
id!: number
@Column()
name!: string
@Column()
is_internal!: boolean
@Column()
owner!: string
}
Repository Layer - 데이터 베이스 조작 구현 => Deprecated 라고 생각 됨
1. 목적: Repository Layer의 주요 목적은 데이터 액세스와 데이터 조작을 추상화하는 것입니다. 즉, 데이터베이스와의 모든 직접적인 상호작용이 이곳에서 처리됩니다.
2. 작업: 이 계층은 CRUD (Create, Read, Update, Delete) 작업, 데이터 조회, 필터링 등 데이터베이스와 관련된 작업을 담당합니다.
3. 분리된 관심사: Repository Layer는 비즈니스 로직에서 데이터 액세스 로직을 분리합니다. 이를 통해 데이터 소스가 변경되거나 다른 데이터 액세스 기술로 전환해야 하는 경우에도 비즈니스 로직에 미치는 영향을 최소화할 수 있습니다.
4. 단순화된 인터페이스: Repository는 일반적으로 데이터 모델이나 테이블과 밀접한 관련이 있으며, 이를 사용하는 서비스나 컨트롤러에게는 단순화된 인터페이스를 제공합니다.
근데 최근 TypeORM의 버전이 오르면서 굳이 Repository Layer를 두지 않아도 될것같다는 생각이 들었다.
기존의 Repository Layer를 둔 구조를 개선하기 위해서 Library 상에서 Repository를 나누어 둔 느낌?
예전 코드로는 connection 같은 메서드가 있었는데 최근 모두 deprecated 되어버렸다.
그래서 아래와 같은getRepository() 라는 코드로 모두 Repository를 설정 가능해져 버린듯
constructor(dataSource: DataSource) {
this.instance = dataSource.getRepository(Prs);
}
그래서 Service Layer로 Repository만 잘 넘겨주면 되는것 처럼 보였다.
그래서 Service Layer의 예시를 보자
Service Layer - 비즈니스 로직 구현
1. 목적: Service Layer의 주요 목적은 비즈니스 로직의 구현입니다. 여기서 애플리케이션의 '규칙'이나 '연산'이 정의되고 실행됩니다.
2. 비즈니스 로직: 이 계층에서는 데이터가 어떻게 사용되어야 하는지, 어떤 순서로 비즈니스 프로세스가 실행되어야 하는지 등을 다룹니다.
3. 조정자 역할: Service Layer는 여러 Repository를 조정하고, 다양한 비즈니스 작업을 수행하는 데 필요한 로직을 구현합니다. 예를 들어, 하나의 비즈니스 작업이 여러 데이터베이스 테이블에 걸쳐 있을 수 있습니다.
4. 트랜잭션 관리: 트랜잭션 관리는 주로 서비스 계층에서 수행됩니다. 이는 여러 데이터베이스 작업을 하나의 비즈니스 작업으로 묶는 데 중요합니다.
위와 같은 목적이 있으며, 결국은 프로그램에서 DB를 어떤식으로 구성할것이냐에 대한 로직을 여기에 추가하면 되는듯 했다.
내가 현재 만들고 있는 프로그램은 DB에 추가된 Github 레포지토리의 모든 Open PR을 읽어서 리뷰가 필요한 사람들에게 제공하기 위함인데 그 중 PR의 데이터를 처리하는 로직의 일부를 아래와 같이 Service Layer에 구현하였다.
import "reflect-metadata";
import {DataSource, Repository} from "typeorm";
import {Prs} from '../entity/pr.entity';
import {Users} from "../entity/user.entity";
import {Repos} from "../entity/repo.entity";
export class PrService {
private instance: Repository<Prs>;
constructor(dataSource: DataSource) {
this.instance = dataSource.getRepository(Prs);
}
async getPrList() : Promise<Prs[]>{
return this.instance.find();
}
async getPrListByRepoId(repoId: number) : Promise<Prs[]>{
return this.instance.find({where: {repo_id: repoId}})}
async getPrListByPrId(prId: number) : Promise<Prs[]>{
return this.instance.find({where: {pr_id: prId}})
}
async deletePrsByRepoId(repoId: number) : Promise<void>{
let prs = await this.instance.find({where: {repo_id: repoId}});
await this.instance.remove(prs);
}
async CreatePrs(githubPrResponse: any[], repoId: number) : Promise<Prs[]>{
var prs: Prs[] = [];
for (let pr of githubPrResponse) {
let prEntity = new Prs();
prEntity.author = pr.user.login;
prEntity.repo_id = repoId
prEntity.pr_name = pr.title;
prEntity.pr_id = pr.number;
prEntity.base_branch = pr.base.ref;
prEntity.is_closed = pr.state == "closed" ? true : false;
prEntity.created_at = pr.created_at;
prEntity.html_url = pr.html_url;
prEntity.requested_reviewers = pr.requested_reviewers.map((reviewer: any) => reviewer.login);
prEntity.requested_teams = pr.requested_teams.map((team: any) => team.name);
prs.push(prEntity);
}
return this.instance.save(prs);
}
getRequestedReviewersInPr(pr:Prs, users:Users[], repos:Repos[]) : Set<Users>{
var requestedReviewers : Set<Users> = new Set<Users>();
// 실제 user 처리
for (let reviewer of pr.requested_reviewers) {
for (let user of users) {
if (reviewer == user.github_id) {
requestedReviewers.add(user);
}
}
}
// requested teams 처리
for (let team of pr.requested_teams) {
for (let user of users) {
if (team.toLowerCase().includes(user.team_name.toLowerCase())){
requestedReviewers.add(user);
}
}
}
return requestedReviewers;
}
}
결론
TypeORM의 구조는 Entity, (Repository), Service 등의 형태로 레이어를 나눠서 사용하는것으로 확인을 했다
이와 같이 TypeORM의 기본을 이해할 수 있었으며 추가로 로직이 복잡해지면서 추가되어야할 부분은 나중에 추가 포스트로 정리를 할 예정이다.
'백엔드 > NodeJS | Typescript' 카테고리의 다른 글
Typescript vs Javascript 간단 비교 - Typescript를 중심으로 (0) | 2024.03.06 |
---|---|
Typescript - TypeORM 사용해서 DB 쿼리하기 (0) | 2023.12.13 |
Typescript 프로젝트의 naming convention (0) | 2023.12.07 |
Typescript 이해하기 - Async/Await 이해하기 (0) | 2023.11.28 |
Typescript 이해하기 - 제너레이터 이해하기 (0) | 2023.11.28 |
개발 및 IT 관련 포스팅을 작성 하는 블로그입니다.
IT 기술 및 개인 개발에 대한 내용을 작성하는 블로그입니다. 많은 분들과 소통하며 의견을 나누고 싶습니다.