티스토리 뷰
[목적]
지난 프로젝트에서는, 서비스 생성자에 엔티티모델을 주입하여
Service 단에서 데이터베이스를 호출하여 비즈니스로직을 만들었다.
클라이언트 >> 라우터 + 컨트롤러 >> 서비스 >> 데이터베이스
위와 같이 서비스 로직내부에서 데이터베이스를 호출시켜 트랜잭션을 처리하는 것은 여러모로 문제가 있다.
서비스 로직에서 데이터베이스를 호출하여 트랜잭션 처리를 할 수있다만...
비즈니스 로직에 집중하기 어렵다.
비즈니스 로직 자체를 테스트도 어렵고 중복된 코드가 발생한 가능성이 높다.
즉, 비즈니스로직이 길어지기도하고, 가독성이 떨어진다.
아주 바보같은 코드군.... if문안에 if를 넣었어.... 리팩토링마렵다.....
// 지난주 프로젝트 : /src/posts/posts.service.ts
import {
BadRequestException,
Injectable,
UnauthorizedException,
UseFilters,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { InsertResult, Repository } from 'typeorm';
import { Posts } from '../entities/Posts';
import { CreatePostDto } from './dto/create-post.dto';
import { PostType } from '../entities/enums/PostType';
import { Users } from '../entities/Users';
import { PRIVATE_PASSWORD_REGEX } from 'src/common/regex/regex';
@Injectable()
export class PostsService {
constructor(
@InjectRepository(Users)
private readonly usersRepository: Repository<Users>,
@InjectRepository(Posts)
private readonly postsRepository: Repository<Posts>,
) {}
async createPost(
user: Users,
createPostDto: CreatePostDto,
): Promise<InsertResult> {
if (createPostDto.postType === PostType.PRIVATE_POST) {
const originPassword = createPostDto.postPassword;
if (!originPassword) {
throw new BadRequestException('비밀번호를 입력해주세요');
}
// 비밀번호 정규표현식
const isRegex = originPassword.match(PRIVATE_PASSWORD_REGEX);
if (!isRegex)
throw new BadRequestException(
'비밀번호는 6자리 이상이며, 숫자는 최소 1개가 필요합니다.',
);
//입력받은 비밀번호를 암호화
const hashedPostPassword = await this.hash(originPassword);
createPostDto.postPassword = hashedPostPassword;
}
// Post 추가 ( 서비스 로직 내부에 db를 직접 호출하여 트랜잭션 실행 )
const result = await this.postsRepository
.createQueryBuilder('posts')
.insert()
.into(Posts)
.values({ ...createPostDto, userId: user.userId })
.execute();
return result;
}
...
}
이번에는 Service 로직 내부에서 트랜잭션을 처리하지 않고
Repository 단을 분리시켜서 트랜잭션 처리하는 것을
이것을 Repository 패턴을 적용시킨다고 한다.
클라이언트 >> 컨트롤러 + 라우터 >> 서비스 >> 래포지토리 >> 데이터베이스
래포지토리 라는 레이어를 통해서 데이터베이스에 접근하여 트랜잭션을 담당한다.
그리고 코드에서도 응집성은 낮추고, 독립성을 높일수록 좋은 코드라고 생각한다.
의존성이 너무 높아지면, 유지보수가 어려워진다. 그러므로 역할에 맞게 레이어를 분리시키는게
더 깔끔하고, 가독성이 높아질것이다!! 서비스 로직에는 비즈니스로직에만 집중하자 !!! 😤
자 그러면 한번 래포지토리 레이어를 추가해보자.
[ repository pattern 적용 전]
- market.service.ts
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { CreateMarketDto } from './dto/create-market.dto';
import { UpdateMarketDto } from './dto/update-market.dto';
import { Market, MarketDocument } from './schemas/markets.schema';
import { Product, ProductDocument } from '../products/schemas/product.schema';
@Injectable()
export class MarketsService {
constructor(
// 마켓 다큐먼트 주입
@InjectModel(Market.name) private marketModel: Model<MarketDocument>,
// 상품 다큐먼트 주입
@InjectModel(Product.name) private productModel: Model<ProductDocument>,
) {}
// 서비스 함수들
}
[ repository pattern 적용 후 ]
- market.service.ts
마켓서비스가 필요한 것 (MarketRepository) 을 서비스 생성자에 주입한다.
import { Injectable } from '@nestjs/common';
import { CreateMarketDto } from './dto/create-market.dto';
import { UpdateMarketDto } from './dto/update-market.dto';
import { MarketsRepository } from './markets.repository';
@Injectable()
export class MarketsService {
constructor(private readonly repository: MarketsRepository) {}
// 서비스 함수들
}
- market.repository.ts
래포지토리가 필요한 것(MarketDocument, ProductDocument) 를 래포지토리 생성자에 주입(넣는다)
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Market, MarketDocument } from './schemas/markets.schema';
import { Product, ProductDocument } from '../products/schemas/product.schema';
@Injectable()
export class MarketsRepository {
constructor(
// 마켓 다큐먼트 주입
@InjectModel(Market.name) private marketModel: Model<MarketDocument>,
// 상품 다큐먼트 주입
@InjectModel(Product.name) private productModel: Model<ProductDocument>,
) {}
// 래포지토리 함수들
...
}
- market.module.ts
import { Market, MarketSchema } from './schemas/markets.schema';
import { MongooseModule } from '@nestjs/mongoose';
import { Product, ProductSchema } from 'src/products/schemas/product.schema';
import { MarketsRepository } from './markets.repository';
@Module({
imports: [
// 모델(몽구스 모듈)
MongooseModule.forFeature([{ name: Market.name, schema: MarketSchema }]), // 마켓 몽구스 모듈 주입
MongooseModule.forFeature([{ name: Product.name, schema: ProductSchema }]), // 상품 몽구스 모듈 주입
],
controllers: [MarketsController], // 컨트롤러
providers: [
MarketsService, // 서비스
MarketsRepository, // 래포지토리
],
})
export class MarketsModule {}
모듈이란 녀석은 외부에서 서비스, 래포지토리, 모델 등과 같은 것들을 주입하도록 해주는 결정자/관리자 역할을 한다.
의존관계가 있는 것들을 관리하는 녀석이다.
그리고 market.module 도 커스텀 모듈이고, 이러한 커스텀 모듈들을 관리하는 녀석이 app.module 이다.
[의존성 주입]
의존성 주입 (DI, Dependency Injection)
"A 가 B 를 의존한다" = "의존대상 B 가 변하면, 그 변화가 A 에 영향을 미친다."
의존관계를 외부에서 결정하고 주입하는 것을 의미한다.
(1) '햄버거 요리사' 는 '햄버거 래시피' 에 의존한다
'햄버거 래시피'의 변화에 따라 '햄버거 요리사'는 햄버거를 만드는 방법을 수정해야한다.
(2) '햄버거 사장님'(외부) 은 '햄버거 래시피'를 결정한다.
어떤 햄버거를 만들지는 '햄버거 사장'이 결정한다.
'햄버거 요리사'가 의존하고 있는 '햄버거 래시피'를 외부에서 결정하고 주입하는 것이다.
의존관계를 분리하여 주입을 받는 방법 인 DI 에는 어떤 장점이 있을까?
1. 의존성이 줄어든다.
2. 재사용성이 높은 코드가 된다.
3. 테스트하기 좋은 코드가 된다.
4. 가독성이 높아진다.
[참고] Repository Pattern
- typeorm 의 경우 repository pattern 적용
앞으로의 숙제
- 디자인패턴 학습하여 정리하기.
왜 디자인패턴을 해야되는데?
- 어떤 패턴을 쓰느냐에 따라 코드의 유지보수와 관리의 난이도가 결정된다...😥
'Backend > 꾸준히 TIL' 카테고리의 다른 글
[NestJS] Joi: Cannot read properties of object undefined (0) | 2022.11.12 |
---|---|
[NestJS + MongoDB ] NestJS 프레임워크에 MongoDB 연결해보자 (0) | 2022.11.12 |
[NestJS + Logger] Custom Logger 와 winston Logger 적용하기 (0) | 2022.11.09 |
[NestJS] Custom 에러 만들기 와 Exception Filter (0) | 2022.11.08 |
[NestJS+TypeORM] Pagination 처리 (0) | 2022.11.07 |
- Total
- Today
- Yesterday
- 한달독서
- vscode
- 스마트폰중독
- typeORM
- 한달어스
- 나도 할 수 있다
- jest
- 개발용어
- 습관개선
- node.js
- 바이트디그리
- MySQL
- IT용어
- OS
- Mongoose
- git
- nestjs jest
- Jekyll
- 클린아키텍쳐
- 미완
- gem
- 갓생살자
- 참고
- nestjs
- 디지털디톡스
- MongoDB
- TypeScript
- RDBMS
- Nest.js
- TDD
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |