Controller, Service, Repository 패턴에서 ORM을 어느 레이어로써 활용할 수 있는 두가지 방법에 대해서 설명하고, NestJS와 Prisma를 사용한 예시를 통해 두 접근법을 비교 분석해보려고 합니다.
Repository는 소프트웨어 개발에서 데이터 소스와의 상호작용을 캡슐화하는 디자인 패턴입니다. 이 패턴의 주요 목적은 애플리케이션의 비즈니스 로직과 데이터 액세스 로직을 분리하는 것입니다. 즉, 애플리케이션 코드에서 직접적으로 데이터베이스 쿼리를 호출하는 대신, Repository를 통해 데이터베이스와의 상호작용을 수행합니다. 이로써 애플리케이션의 유지보수성과 확장성을 향상시킬 수 있습니다.
Repository 패턴은 다음과 같은 장점을 제공합니다:
Repository 패턴은 3계층 아키텍처(표현 계층, 비즈니스 로직 계층, 데이터 액세스 계층)에서 주로 사용되며, 각 계층은 서로 명확하게 분리되어 있어야 합니다. 이러한 구조는 애플리케이션의 규모가 커지고 복잡해짐에 따라 그 중요성이 더욱 증가합니다.
ORM은 데이터베이스 테이블과 객체 간의 관계를 매핑하는 기술입니다. 이를 통해 개발자는 SQL 쿼리를 직접 작성하는 대신, 객체 지향적인 방식으로 데이터베이스 작업을 할 수 있습니다. ORM은 개발자가 데이터베이스 작업을 수행하기 위해 사용하는 도구나 라이브러리(예: Hibernate, Entity Framework, Sequelize, Prisma 등)를 말합니다.
ORM(Object-Relational Mapping)은 Repository 패턴과는 다른 개념이지만, 데이터 소스와의 상호작용을 캡슐화한다는 관점에서 ORM을 Repository로써 사용을 해도 괜찮다고 생각됩니다.
이 방식에서는 별도의 Repository 계층을 구현하여, 이 계층 내에서 ORM을 사용합니다. 이 접근 방식의 장점은 비즈니스 로직과 데이터 액세스 로직의 엄격한 분리로, 코드의 유지보수성과 확장성이 향상된다는 것입니다. 단점은 추가적인 코드 작성이 필요하다는 점입니다.
import { Injectable } from '@nestjs/common';
import { PrismaService } from './prisma.service';
import { User as UserModel, Prisma } from '@prisma/client';
@Injectable()
export class UserService {
constructor(private prisma: PrismaService) {}
async createUser(data: Prisma.UserCreateInput): Promise<UserModel> {
return this.prisma.user.create({
data,
});
}
// 기타 비즈니스 로직 구현
}
@Injectable()
export class UserRepository {
constructor(private userService: UserService) {}
async addUser(userData: Prisma.UserCreateInput): Promise<UserModel> {
return this.userService.createUser(userData);
}
// 기타 Repository 로직 구현
}
이 접근 방식에서는 ORM 객체를 직접 Repository로 사용하여 데이터베이스와의 상호작용을 관리합니다. 이 방법의 장점은 코드의 양을 줄이고, ORM의 기능을 최대한 활용할 수 있다는 것입니다. 단점은 프로젝트의 복잡성이 증가할 경우, ORM과 비즈니스 로직 간의 경계가 모호해질 수 있다는 점입니다.
import { Injectable } from '@nestjs/common';
import { PrismaService } from './prisma.service';
import { User, Prisma } from '@prisma/client';
@Injectable()
export class UserService {
constructor(private prisma: PrismaService) {}
async createUser(data: Prisma.UserCreateInput): Promise<User> {
return this.prisma.user.create({
data,
});
}
// 기타 사용자 관련 메소드 구현
}
ORM을 Repository 그 자체로 사용하는 방법과 별도의 Repository에서 ORM을 다루는 방법은 각각의 장단점을 가지고 있습니다. 프로젝트의 규모, 팀의 선호, 유지보수 및 확장성의 요구 사항을 고려하여 적합한 접근 방식을 선택해야 합니다. NestJS와 Prisma를 사용한 예시는 두 접근 방식을 구현하는 방법을 보여줍니다. 선택한 방식에 따라 애플리케이션의 구조와 유지보수성에 큰 영향을 미칠 수 있으므로, 프로젝트의 요구 사항과 팀의 작업 방식을 충분히 고려하여 결정해야 합니다.