2026๋ NestJS ๋ง์ดํฌ๋ก์๋น์ค: ์ํคํ ์ฒ, gRPC, ๋ฉด์ ์ง๋ฌธ ์๋ฒฝ ๊ฐ์ด๋
NestJS ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ์ ํต์ฌ ๊ฐ๋ , gRPC ํธ๋์คํฌํธ ๊ตฌ์ฑ, ์คํธ๋ฆฌ๋ฐ ํจํด, ์์ ์ฑ ํจํด, ๋ฉด์ ๋น์ถ ์ง๋ฌธ์ ์ค๋ฌด ์ค์ฌ์ผ๋ก ๋ค๋ฃน๋๋ค.

NestJS ๋ง์ดํฌ๋ก์๋น์ค๋ 2026๋ ํ์ฌ Node.js ๊ธฐ๋ฐ ๋ถ์ฐ ์์คํ ๊ตฌ์ถ์ ์ฌ์ค์ ํ์ค์ผ๋ก ์๋ฆฌ ์ก์์ต๋๋ค. gRPC ํธ๋์คํฌํธ, ์ด๋ฒคํธ ๊ธฐ๋ฐ ํต์ , ์ํท ๋ธ๋ ์ด์ปค ํจํด๊น์ง NestJS๊ฐ ์ ๊ณตํ๋ ๋ง์ดํฌ๋ก์๋น์ค ๋๊ตฌ๋ ๋๊ท๋ชจ ๋ฐฑ์๋ ์์คํ ์ ๋ณต์ก์ฑ์ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํฉ๋๋ค. ์ด ๊ธ์์๋ NestJS ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ์ ํต์ฌ ๊ฐ๋ ๋ถํฐ gRPC ๊ตฌ์ฑ, ์คํธ๋ฆฌ๋ฐ ํจํด, ์์ ์ฑ ํจํด, ๊ทธ๋ฆฌ๊ณ ๊ธฐ์ ๋ฉด์ ์์ ๋น์ถ๋๋ ์ง๋ฌธ๊น์ง ์ค๋ฌด ์ค์ฌ์ผ๋ก ๋ค๋ฃน๋๋ค.
๋ด๋ถ ์๋น์ค ๊ฐ ํต์ ์๋ gRPC๊ฐ ์ ํฉํฉ๋๋ค. Protocol Buffers ๊ธฐ๋ฐ ๋ฐ์ด๋๋ฆฌ ์ง๋ ฌํ๋ก JSON ๋๋น ํ์ด๋ก๋ ํฌ๊ธฐ๊ฐ ์๊ณ , HTTP/2 ๋ฉํฐํ๋ ์ฑ์ผ๋ก ์ง์ฐ ์๊ฐ์ด ๋ฎ์ต๋๋ค. ๋ฐ๋ฉด, ์ธ๋ถ ํด๋ผ์ด์ธํธ(๋ธ๋ผ์ฐ์ , ๋ชจ๋ฐ์ผ ์ฑ) ๋์ API์๋ REST๊ฐ ์ ๊ทผ์ฑ๊ณผ ํธํ์ฑ ์ธก๋ฉด์์ ์ ๋ฆฌํฉ๋๋ค. ๋๋ถ๋ถ์ ํ๋ก๋์ ํ๊ฒฝ์์๋ ์ธ๋ถ์ฉ REST์ ๋ด๋ถ์ฉ gRPC๋ฅผ ๋ณํํ๋ ํ์ด๋ธ๋ฆฌ๋ ๊ตฌ์กฐ๋ฅผ ์ฑํํฉ๋๋ค.
NestJS ๋ง์ดํฌ๋ก์๋น์ค ํธ๋์คํฌํธ ๋ ์ด์ด ์ํคํ ์ฒ
NestJS์ ๋ง์ดํฌ๋ก์๋น์ค ๋ชจ๋์ ์ ์ก ๊ณ์ธต์ ์์ ํ ์ถ์ํํฉ๋๋ค. TCP, Redis, NATS, RabbitMQ, Kafka, gRPC ๋ฑ ๋ค์ํ ํธ๋์คํฌํธ๋ฅผ ๋์ผํ ๋ฐ์ฝ๋ ์ดํฐ ๊ธฐ๋ฐ ์ธํฐํ์ด์ค๋ก ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ๋น์ฆ๋์ค ๋ก์ง์ ์์ ํ์ง ์๊ณ ๋ ์ ์ก ํ๋กํ ์ฝ์ ๊ต์ฒดํ ์ ์์ต๋๋ค.
ํต์ ํจํด์ ํฌ๊ฒ ๋ ๊ฐ์ง๋ก ๊ตฌ๋ถ๋ฉ๋๋ค. @MessagePattern ๋ฐ์ฝ๋ ์ดํฐ๋ ์์ฒญ-์๋ต(Request-Response) ํจํด์ ๊ตฌํํ๋ฉฐ, ํธ์ถ์๊ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ ๋๊น์ง ๋๊ธฐํฉ๋๋ค. ์ฃผ๋ฌธ ์์ฑ์ด๋ ๋ฐ์ดํฐ ์กฐํ์ฒ๋ผ ์ฆ๊ฐ์ ์ธ ์๋ต์ด ํ์ํ ์์
์ ์ ํฉํฉ๋๋ค. @EventPattern ๋ฐ์ฝ๋ ์ดํฐ๋ ์ด๋ฒคํธ ๊ธฐ๋ฐ(Fire-and-Forget) ํจํด์ ๊ตฌํํ๋ฉฐ, ๋ฐ์ ์๋ ์๋ต์ ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ๋ค์ ์์
์ ์ํํฉ๋๋ค. ์๋ฆผ ์ ์ก, ๋ก๊ทธ ๊ธฐ๋ก, ๋น๋๊ธฐ ์ํ ์
๋ฐ์ดํธ์ ์ ํฉํฉ๋๋ค.
import { Controller } from '@nestjs/common';
import { MessagePattern, EventPattern, Payload } from '@nestjs/microservices';
import { OrdersService } from './orders.service';
import { CreateOrderDto } from './dto/create-order.dto';
@Controller()
export class OrdersController {
constructor(private readonly ordersService: OrdersService) {}
// Request-response: caller waits for the created order
@MessagePattern('order.create')
async createOrder(@Payload() data: CreateOrderDto) {
return this.ordersService.create(data);
}
// Event-based: fire and forget, no response returned
@EventPattern('order.shipped')
async handleOrderShipped(@Payload() data: { orderId: string }) {
await this.ordersService.markAsShipped(data.orderId);
}
}order.create ํจํด์ ์ฃผ๋ฌธ์ ์์ฑํ๊ณ ์์ฑ๋ ์ฃผ๋ฌธ ๊ฐ์ฒด๋ฅผ ํธ์ถ์์๊ฒ ๋ฐํํฉ๋๋ค. order.shipped ์ด๋ฒคํธ๋ ๋ฐฐ์ก ์ํ ๋ณ๊ฒฝ์ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๋ฏ๋ก ํธ์ถ ์๋น์ค์ ์๋ต ์ง์ฐ์ ์ํฅ์ ์ฃผ์ง ์์ต๋๋ค. ์ด๋ค ํจํด์ ์ ํํ ์ง๋ ๋ฐ์ดํฐ ์ผ๊ด์ฑ ์๊ตฌ ์์ค๊ณผ ํธ์ถ์์ ์๋ต ๋๊ธฐ ํ์ ์ฌ๋ถ์ ๋ฐ๋ผ ๊ฒฐ์ ๋ฉ๋๋ค.
NestJS ๋ง์ดํฌ๋ก์๋น์ค์์ gRPC ํธ๋์คํฌํธ ๊ตฌ์ฑ
gRPC๋ Google์ด ๊ฐ๋ฐํ ๊ณ ์ฑ๋ฅ ์๊ฒฉ ํ๋ก์์ ํธ์ถ(RPC) ํ๋ ์์ํฌ์
๋๋ค. Protocol Buffers๋ฅผ ์ธํฐํ์ด์ค ์ ์ ์ธ์ด(IDL)๋ก ์ฌ์ฉํ์ฌ ์๋น์ค ๊ณ์ฝ์ ๋ช
์์ ์ผ๋ก ์ ์ํ๊ณ , ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ค์ํ ์ธ์ด์ ํด๋ผ์ด์ธํธ/์๋ฒ ์ฝ๋๋ฅผ ์๋ ์์ฑํฉ๋๋ค. NestJS๋ gRPC๋ฅผ ์ผ๊ธ ํธ๋์คํฌํธ๋ก ์ง์ํ๋ฉฐ, @nestjs/microservices ํจํค์ง์ ๋ชจ๋ ๊ธฐ๋ฅ์ด ํฌํจ๋์ด ์์ต๋๋ค.
gRPC ์๋น์ค ๊ตฌ์ฑ์ ์ฒซ ๋จ๊ณ๋ .proto ํ์ผ ์ ์์
๋๋ค. ์ด ํ์ผ์ ์๋น์ค์ RPC ๋ฉ์๋์ ๋ฉ์์ง ๊ตฌ์กฐ๋ฅผ ์ ์ธํ๋ฉฐ, ํด๋ผ์ด์ธํธ์ ์๋ฒ ์์ชฝ ๋ชจ๋์ ํต์ ๊ณ์ฝ ์ญํ ์ ํฉ๋๋ค.
syntax = "proto3";
package users;
service UsersService {
rpc FindOne (UserById) returns (User);
rpc FindMany (UserFilter) returns (stream User);
}
message UserById {
string id = 1;
}
message UserFilter {
string role = 1;
int32 limit = 2;
}
message User {
string id = 1;
string email = 2;
string name = 3;
string role = 4;
}syntax = \"proto3\"๋ Protocol Buffers 3 ๋ฒ์ ์ ๋ช
์ํฉ๋๋ค. ๊ฐ ํ๋์ ํ ๋น๋ ๋ฒํธ(1, 2, 3...)๋ ๋ฐ์ด๋๋ฆฌ ์ธ์ฝ๋ฉ์์ ํ๋๋ฅผ ์๋ณํ๋ ๋ฐ ์ฌ์ฉ๋๋ฏ๋ก, ํ ๋ฒ ๋ฐฐํฌ๋ ํ์๋ ๋ณ๊ฒฝํ๊ฑฐ๋ ์ฌ์ฌ์ฉํด์๋ ์ ๋ฉ๋๋ค. ํ๋๋ฅผ ์ญ์ ํ ๋๋ reserved ํค์๋๋ก ํด๋น ๋ฒํธ๋ฅผ ์์ฝํ์ฌ ํฅํ ์ถฉ๋์ ๋ฐฉ์งํด์ผ ํฉ๋๋ค.
.proto ํ์ผ ์ ์๊ฐ ์๋ฃ๋๋ฉด NestJS ์ ํ๋ฆฌ์ผ์ด์
์ gRPC ๋ง์ดํฌ๋ก์๋น์ค๋ก ๋ถํธ์คํธ๋ฉํฉ๋๋ค. NestFactory.createMicroservice ๋ฉ์๋์ Transport.GRPC ์ต์
์ ์ ๋ฌํ๋ฉด ์ ์ฉ gRPC ์๋ฒ๊ฐ ์์ฑ๋ฉ๋๋ค.
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
AppModule,
{
transport: Transport.GRPC,
options: {
package: 'users',
protoPath: join(__dirname, 'proto/users.proto'),
url: '0.0.0.0:5000',
},
},
);
await app.listen();
}
bootstrap();์ปจํธ๋กค๋ฌ์์๋ @GrpcMethod ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ์ฌ .proto ํ์ผ์ ์ ์๋ RPC ๋ฉ์๋๋ฅผ ๊ตฌํํฉ๋๋ค. ์ฒซ ๋ฒ์งธ ์ธ์๋ ์๋น์ค ์ด๋ฆ, ๋ ๋ฒ์งธ ์ธ์๋ ๋ฉ์๋ ์ด๋ฆ์ด๋ฉฐ, ์ด ๊ฐ๋ค์ด .proto ํ์ผ์ ์ ์์ ์ ํํ ์ผ์นํด์ผ ํฉ๋๋ค.
import { Controller } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';
import { UsersService } from './users.service';
@Controller()
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@GrpcMethod('UsersService', 'FindOne')
async findOne(data: { id: string }) {
return this.usersService.findById(data.id);
}
}@GrpcMethod ๋ฐ์ฝ๋ ์ดํฐ๊ฐ .proto ํ์ผ์ rpc FindOne ์ ์ธ๊ณผ ๋งคํ๋์ด, gRPC ์์ฒญ์ด ํด๋น ํธ๋ค๋ฌ๋ก ๋ผ์ฐํ
๋ฉ๋๋ค. ๋ฐํ ๊ฐ์ ์๋์ผ๋ก Protocol Buffers ํ์์ผ๋ก ์ง๋ ฌํ๋์ด ํด๋ผ์ด์ธํธ์ ์ ๋ฌ๋ฉ๋๋ค.
NestJS์ gRPC ์คํธ๋ฆฌ๋ฐ ํจํด
gRPC๋ ๋จํญ(Unary), ์๋ฒ ์คํธ๋ฆฌ๋ฐ, ํด๋ผ์ด์ธํธ ์คํธ๋ฆฌ๋ฐ, ์๋ฐฉํฅ ์คํธ๋ฆฌ๋ฐ์ ๋ค ๊ฐ์ง ํต์ ํจํด์ ์ง์ํฉ๋๋ค. NestJS์์๋ RxJS Observable์ ํ์ฉํ์ฌ ์คํธ๋ฆฌ๋ฐ์ ์์ฐ์ค๋ฝ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค.
์๋ฒ ์คํธ๋ฆฌ๋ฐ์ ํด๋ผ์ด์ธํธ๊ฐ ๋จ์ผ ์์ฒญ์ ๋ณด๋ด๋ฉด ์๋ฒ๊ฐ ์ฌ๋ฌ ๊ฐ์ ์๋ต์ ์์ฐจ์ ์ผ๋ก ๋ฐํํ๋ ํจํด์ ๋๋ค. ๋๋์ ๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ํ๊บผ๋ฒ์ ์ ์ฌํ์ง ์๊ณ ์ฒญํฌ ๋จ์๋ก ์ ์กํ ์ ์์ด, ๊ฒ์ ๊ฒฐ๊ณผ ๋ชฉ๋ก์ด๋ ์ค์๊ฐ ํผ๋ ์ ๋ฌ์ ์ ์ฉํฉ๋๋ค.
import { Observable, from } from 'rxjs';
import { map } from 'rxjs/operators';
import { GrpcMethod } from '@nestjs/microservices';
@GrpcMethod('UsersService', 'FindMany')
findMany(data: { role: string; limit: number }): Observable<any> {
// Stream users matching the filter one by one
const users$ = from(this.usersService.findByRole(data.role, data.limit));
return users$.pipe(
map((user) => ({
id: user.id,
email: user.email,
name: user.name,
role: user.role,
})),
);
}from() ์ฐ์ฐ์๊ฐ ๋ฐฐ์ด์ด๋ Promise๋ฅผ Observable ์คํธ๋ฆผ์ผ๋ก ๋ณํํ๊ณ , map() ์ฐ์ฐ์๊ฐ ๊ฐ ํญ๋ชฉ์ .proto ํ์ผ์ ์ ์๋ User ๋ฉ์์ง ํ์์ผ๋ก ๋ณํํฉ๋๋ค. ํด๋ผ์ด์ธํธ๋ ๊ฐ ํญ๋ชฉ์ด ๋์ฐฉํ ๋๋ง๋ค ์์ฐจ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ผ๋ฏ๋ก, ์ ์ฒด ์๋ต์ ๊ธฐ๋ค๋ฆด ํ์๊ฐ ์์ต๋๋ค.
์๋ฐฉํฅ ์คํธ๋ฆฌ๋ฐ์ ํด๋ผ์ด์ธํธ์ ์๋ฒ๊ฐ ๋์์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ํจํด์ ๋๋ค. ์ค์๊ฐ ์ฑํ , ํ์ ํธ์ง, ์ผ์ ๋ฐ์ดํฐ ๋ชจ๋ํฐ๋ง๊ณผ ๊ฐ์ ์๋๋ฆฌ์ค์์ ํ์ฉ๋ฉ๋๋ค. NestJS์์๋ ์ ๋ ฅ ๋งค๊ฐ๋ณ์์ ๋ฐํ๊ฐ ๋ชจ๋ Observable๋ก ์ฒ๋ฆฌํ๋ฉด ์๋ฐฉํฅ ์คํธ๋ฆฌ๋ฐ์ด ์๋์ผ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
gRPC ์คํธ๋ฆฌ๋ฐ ์ฐ๊ฒฐ์ ์ฅ์๊ฐ ์ ์ง๋ ์ ์์ผ๋ฏ๋ก, ์ ์ ํ ๋ฐ๋๋ผ์ธ(Deadline) ์ค์ ๊ณผ ์ฐ๊ฒฐ ํด์ ์ฒ๋ฆฌ๊ฐ ํ์์ ๋๋ค. ํด๋ผ์ด์ธํธ๊ฐ ์คํธ๋ฆผ์ ์๋นํ์ง ์์ผ๋ฉด ๋ฐฑํ๋ ์ (Backpressure) ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ผ๋ฉฐ, ์๋ฒ ์ธก ๋ฆฌ์์ค ๋์๋ก ์ด์ด์ง ์ ์์ต๋๋ค.
ํ์ด๋ธ๋ฆฌ๋ ์ ํ๋ฆฌ์ผ์ด์ : ๋์ผ ์๋น์ค์์ HTTP์ gRPC ๋์ ์ด์
ํ๋ก๋์ ํ๊ฒฝ์์๋ ๋จ์ผ ์ ํ๋ฆฌ์ผ์ด์ ์ด HTTP REST API์ gRPC ๋ง์ดํฌ๋ก์๋น์ค๋ฅผ ๋์์ ์ ๊ณตํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋น๋ฒํฉ๋๋ค. NestJS์ ํ์ด๋ธ๋ฆฌ๋ ์ ํ๋ฆฌ์ผ์ด์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด ํ๋์ ์ฝ๋๋ฒ ์ด์ค์์ ์ฌ๋ฌ ์ ์ก ๊ณ์ธต์ ๋์์ ์คํํ ์ ์์ต๋๋ค.
์ด ํจํด์ API ๊ฒ์ดํธ์จ์ด ๊ตฌํ์ ํนํ ์ ์ฉํฉ๋๋ค. ์ธ๋ถ ํด๋ผ์ด์ธํธ(์น ๋ธ๋ผ์ฐ์ , ๋ชจ๋ฐ์ผ ์ฑ)์๋ HTTP REST ์๋ํฌ์ธํธ๋ฅผ ์ ๊ณตํ๊ณ , ๋ด๋ถ ์๋น์ค ๊ฐ ํต์ ์๋ gRPC๋ฅผ ์ฌ์ฉํ์ฌ ์ฑ๋ฅ๊ณผ ์ ๊ทผ์ฑ์ ๋ชจ๋ ํ๋ณดํ ์ ์์ต๋๋ค.
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { AppModule } from './app.module';
async function bootstrap() {
// HTTP server on port 3000
const app = await NestFactory.create(AppModule);
// gRPC microservice on port 5000
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.GRPC,
options: {
package: 'users',
protoPath: join(__dirname, 'proto/users.proto'),
url: '0.0.0.0:5000',
},
});
await app.startAllMicroservices();
await app.listen(3000);
}
bootstrap();NestFactory.create๋ก HTTP ์๋ฒ๋ฅผ ์์ฑํ๊ณ , connectMicroservice๋ก gRPC ํธ๋์คํฌํธ๋ฅผ ์ถ๊ฐํฉ๋๋ค. startAllMicroservices()๊ฐ gRPC ์๋ฒ๋ฅผ ์์ํ ๋ค, listen(3000)์ด HTTP ์๋ฒ๋ฅผ ์์ํฉ๋๋ค. ๋์ผํ ์๋น์ค ํด๋์ค์ ์์กด์ฑ ์ฃผ์
์ปจํ
์ด๋๋ฅผ ๊ณต์ ํ๋ฏ๋ก, ๋น์ฆ๋์ค ๋ก์ง์ ์ค๋ณต ์์ด ๋ ํ๋กํ ์ฝ์ ์ง์ํ ์ ์์ต๋๋ค.
๋ ๊ฑฐ์ REST API๋ฅผ ์ ์งํ๋ฉด์ ์ ์ง์ ์ผ๋ก ๋ด๋ถ ํต์ ์ gRPC๋ก ์ ํํ๋ ๋ง์ด๊ทธ๋ ์ด์ ์๋๋ฆฌ์ค์์๋ ์ด ์ ๊ทผ ๋ฐฉ์์ด ํจ๊ณผ์ ์ ๋๋ค.
Node.js / NestJS ๋ฉด์ ์ค๋น๊ฐ ๋์ จ๋์?
์ธํฐ๋ํฐ๋ธ ์๋ฎฌ๋ ์ดํฐ, flashcards, ๊ธฐ์ ํ ์คํธ๋ก ์ฐ์ตํ์ธ์.
์๋น์ค ๊ฒฝ๊ณ์ ๋๋ฉ์ธ ์ฃผ๋ ์ค๊ณ
๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ์์ ๊ฐ์ฅ ์ด๋ ค์ด ๊ณผ์ ๋ ์๋น์ค ๊ฒฝ๊ณ๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ์ ์ํ๋ ๊ฒ์ ๋๋ค. ์๋ชป๋ ๊ฒฝ๊ณ ์ค์ ์ ๋ถ์ฐ ๋ชจ๋๋ฆฌ์ค(Distributed Monolith)๋ผ๋ ์ต์ ์ ๊ฒฐ๊ณผ๋ฅผ ์ด๋ํฉ๋๋ค. ๋คํธ์ํฌ ํธ์ถ์ ๋ณต์ก์ฑ์ ๋์ด๋์ง๋ง, ๋ ๋ฆฝ์ ๋ฐฐํฌ์ ํ์ฅ์ด๋ผ๋ ๋ง์ดํฌ๋ก์๋น์ค์ ๋ณธ์ง์ ์ด์ ์ ์ป์ง ๋ชปํ๋ ์ํฉ์ ๋๋ค.
๋๋ฉ์ธ ์ฃผ๋ ์ค๊ณ(DDD)์ ๋ฐ์ด๋๋ ์ปจํ ์คํธ(Bounded Context) ๊ฐ๋ ์ ์๋น์ค ๋ถ๋ฆฌ์ ํต์ฌ ๊ธฐ์ค์ ์ ๊ณตํฉ๋๋ค. ๊ฐ ๋ง์ดํฌ๋ก์๋น์ค๋ ํ๋์ ๋ฐ์ด๋๋ ์ปจํ ์คํธ๋ฅผ ๋ด๋นํ๋ฉฐ, ํด๋น ๋๋ฉ์ธ์ ์ฉ์ด์ ๊ท์น์ ๋ ์์ ์ผ๋ก ์ ์ํฉ๋๋ค. ์ ์์๊ฑฐ๋ ์์คํ ์ ์๋ก ๋ค๋ฉด, ์ฃผ๋ฌธ(Orders), ์ฌ์ฉ์(Users), ๊ฒฐ์ (Payments), ์ฌ๊ณ (Inventory)๊ฐ ๊ฐ๊ฐ ๋ ๋ฆฝ๋ ๋ฐ์ด๋๋ ์ปจํ ์คํธ์ ํด๋นํฉ๋๋ค.
์๋น์ค ๊ฐ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถ๋ ค๋ฉด ๋๊ธฐ์ ์ง์ ํธ์ถ๋ณด๋ค ๋๋ฉ์ธ ์ด๋ฒคํธ ๊ธฐ๋ฐ์ ๋น๋๊ธฐ ํต์ ์ ์ฐ์ ์ ์ผ๋ก ๊ณ ๋ คํด์ผ ํฉ๋๋ค. ์ฃผ๋ฌธ ์๋น์ค๊ฐ ๊ฒฐ์ ์๋น์ค๋ฅผ ์ง์ ํธ์ถํ๋ ๋์ , OrderCreated ์ด๋ฒคํธ๋ฅผ ๋ฐํํ๊ณ ๊ฒฐ์ ์๋น์ค๊ฐ ์ด๋ฅผ ๊ตฌ๋
ํ๋ ๋ฐฉ์์
๋๋ค. ์ด ๊ตฌ์กฐ์์๋ ๊ฒฐ์ ์๋น์ค์ ์ฅ์ ๊ฐ ๋ฐ์ํ๋๋ผ๋ ์ฃผ๋ฌธ ์๋น์ค์ ๊ฐ์ฉ์ฑ์ ์ํฅ์ ์ฃผ์ง ์์ต๋๋ค.
์ํฐ ๋ถํจ ๊ณ์ธต(Anti-Corruption Layer, ACL)์ ์ธ๋ถ ์์คํ ์ด๋ ๋ ๊ฑฐ์ ์๋น์ค์ ํตํฉํ ๋ ๋ด๋ถ ๋๋ฉ์ธ ๋ชจ๋ธ์ ์์์ฑ์ ๋ณดํธํ๋ ํจํด์ ๋๋ค. ์ธ๋ถ ๋ชจ๋ธ๊ณผ ๋ด๋ถ ๋ชจ๋ธ ์ฌ์ด์ ๋ณํ ๊ณ์ธต์ ๋์ด, ์ธ๋ถ ์์คํ ์ ๋ณ๊ฒฝ์ด ๋ด๋ถ ๋๋ฉ์ธ์ ์ง์ ์ ํ๋๋ ๊ฒ์ ์ฐจ๋จํฉ๋๋ค.
์์ ์ฑ ํจํด: ํ์์์, ์ฌ์๋, ์ํท ๋ธ๋ ์ด์ปค
๋ถ์ฐ ์์คํ ์์ ๋คํธ์ํฌ ์ฅ์ , ์๋น์ค ๊ณผ๋ถํ, ์ผ์์ ์ค๋ฅ๋ ํผํ ์ ์๋ ํ์ค์ ๋๋ค. ๋จ์ผ ์๋น์ค์ ์ฅ์ ๊ฐ ์ ์ฒด ์์คํ ์ผ๋ก ์ ํ๋๋ ์ฐ์ ์คํจ(Cascading Failure)๋ฅผ ๋ฐฉ์งํ๋ ค๋ฉด, ์ฒด๊ณ์ ์ธ ์์ ์ฑ ํจํด์ ์ ์ฉํด์ผ ํฉ๋๋ค.
ํ์์์์ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ์ด ์๋จ์ ๋๋ค. ์๋ต์ด ์ง์ ๋ ์๊ฐ ๋ด์ ๋์ฐฉํ์ง ์์ผ๋ฉด ์์ฒญ์ ์คํจ๋ก ์ฒ๋ฆฌํ์ฌ, ๋๋ฆฐ ์๋น์ค์ ๋ฌดํ์ ๋๊ธฐํ๋ ์ํฉ์ ๋ฐฉ์งํฉ๋๋ค. ์ฌ์๋ ํจํด์ ์ผ์์ ์ฅ์ ๋ฅผ ๊ทน๋ณตํ๋ ๋ฐ ์ ์ฉํ์ง๋ง, ์ง์ ๋ฐฑ์คํ(Exponential Backoff)์ ์งํฐ(Jitter)๋ฅผ ์ ์ฉํ์ง ์์ผ๋ฉด ์ฅ์ ์ํฉ์ ์ ํ์ํฌ ์ ์์ต๋๋ค.
import { Inject, Injectable } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { firstValueFrom, timeout, retry } from 'rxjs';
@Injectable()
export class OrdersService {
private usersService: any;
constructor(@Inject('USERS_PACKAGE') private client: ClientGrpc) {}
onModuleInit() {
this.usersService = this.client.getService('UsersService');
}
async getOrderWithUser(orderId: string, userId: string) {
// 3-second deadline, 2 retries with exponential backoff
const user = await firstValueFrom(
this.usersService.findOne({ id: userId }).pipe(
timeout(3000),
retry({ count: 2, delay: (err, retryCount) => {
const jitter = Math.random() * 100;
return new Promise(r => setTimeout(r, 1000 * retryCount + jitter));
}}),
),
);
return { orderId, user };
}
}์ ์ฝ๋์์ timeout(3000)์ 3์ด ์ด๋ด์ ์๋ต์ด ์์ผ๋ฉด TimeoutError๋ฅผ ๋ฐ์์ํต๋๋ค. retry ์ฐ์ฐ์๋ ์ต๋ 2ํ ์ฌ์๋๋ฅผ ์ํํ๋ฉฐ, 1000 * retryCount๋ก ์ง์ ๋ฐฑ์คํ๋ฅผ, Math.random() * 100์ผ๋ก ์งํฐ๋ฅผ ์ ์ฉํฉ๋๋ค. ์งํฐ๋ ๋ค์์ ํด๋ผ์ด์ธํธ๊ฐ ๋์์ ์ฌ์๋ํ๋ฉด์ ๋ฐ์ํ๋ ์ฌ๋๋ง ํ๋(Thundering Herd) ๋ฌธ์ ๋ฅผ ์ํํฉ๋๋ค.
์ํท ๋ธ๋ ์ด์ปค ํจํด์ ์ฐ์์ ์ธ ์คํจ๊ฐ ๊ฐ์ง๋๋ฉด ํด๋น ์๋น์ค๋ก์ ์์ฒญ์ ์ฆ์ ์ฐจ๋จํ์ฌ ์ฅ์ ์ ํ๋ฅผ ๋ฐฉ์งํฉ๋๋ค. Closed(์ ์), Open(์ฐจ๋จ), Half-Open(์ํ) ์ธ ๊ฐ์ง ์ํ๋ก ๋์ํฉ๋๋ค. NestJS์์๋ opossum ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ @nestjs/terminus ํฌ์ค ์ฒดํฌ ๋ชจ๋๊ณผ ๊ฒฐํฉํ์ฌ ๊ตฌํํ ์ ์์ต๋๋ค.
์ฌ์๋ ํจํด์ ์ ์ฉํ ๋๋ ํด๋น ์์ ์ด ๋ฉฑ๋ฑ์ฑ์ ๋ณด์ฅํ๋์ง ๋ฐ๋์ ํ์ธํด์ผ ํฉ๋๋ค. ์ฃผ๋ฌธ ์์ฑ์ฒ๋ผ ๋ถ์ ํจ๊ณผ๊ฐ ์๋ ์์ ์ ๋ฉฑ๋ฑ์ฑ ํค ์์ด ์ฌ์๋ํ๋ฉด ์ค๋ณต ์ฃผ๋ฌธ์ด ๋ฐ์ํ ์ ์์ต๋๋ค. ์กฐํ ์์ ์ ๋ณธ์ง์ ์ผ๋ก ๋ฉฑ๋ฑํ์ง๋ง, ๋ณ๊ฒฝ ์์ ์๋ ๋ณ๋์ ๋ฉฑ๋ฑ์ฑ ๋ฉ์ปค๋์ฆ์ด ํ์ํฉ๋๋ค.
NestJS ๋ง์ดํฌ๋ก์๋น์ค ๋ฉด์ ์ง๋ฌธ
๊ธฐ์ ๋ฉด์ ์์ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ๋ ์๋์ด ๊ฐ๋ฐ์์ ๋ฐฑ์๋ ์์ง๋์ด์๊ฒ ํต์ฌ ํ๊ฐ ํญ๋ชฉ์ ๋๋ค. ๋ค์์ NestJS ๋ง์ดํฌ๋ก์๋น์ค ๊ด๋ จ ๋น์ถ ์ง๋ฌธ๊ณผ ๊ทธ์ ๋ํ ๋ชจ๋ฒ ๋ต๋ณ์ ๋๋ค.
Q1: NestJS์์ @MessagePattern๊ณผ @EventPattern์ ์ฐจ์ด์ ์ ์ค๋ช
ํ์ญ์์ค.
@MessagePattern์ ์์ฒญ-์๋ต ํจํด์ ๊ตฌํํฉ๋๋ค. ํธ์ถ์๊ฐ ์๋ต์ ์์ ํ ๋๊น์ง ๋๊ธฐํ๋ฏ๋ก, ๋ฐ์ดํฐ ์กฐํ๋ ๋๊ธฐ์ ์ฒ๋ฆฌ๊ฐ ํ์ํ ์์
์ ์ ํฉํฉ๋๋ค. @EventPattern์ ์ด๋ฒคํธ ๊ธฐ๋ฐ ๋น๋๊ธฐ ํต์ ์ ๊ตฌํํ๋ฉฐ, ๋ฐ์ ์๋ ์๋ต์ ๊ธฐ๋ค๋ฆฌ์ง ์์ต๋๋ค. ์๋ฆผ ์ ์ก, ๊ฐ์ฌ ๋ก๊ทธ ๊ธฐ๋ก, ๋น๋๊ธฐ ์ํ ์
๋ฐ์ดํธ์ ์ฌ์ฉ๋ฉ๋๋ค. ์ ํ ๊ธฐ์ค์ ๋ฐ์ดํฐ ์ผ๊ด์ฑ ์๊ตฌ ์์ค๊ณผ ์๋น์ค ๊ฐ ๊ฒฐํฉ๋ ํ์ฉ ๋ฒ์์ ๋ฐ๋ผ ๊ฒฐ์ ๋ฉ๋๋ค.
Q2: gRPC๊ฐ ๋ง์ดํฌ๋ก์๋น์ค ๋ด๋ถ ํต์ ์์ REST๋ณด๋ค ์ ๋ฆฌํ ์ ์ ๋ฌด์์ ๋๊น?
gRPC๋ Protocol Buffers๋ฅผ ์ฌ์ฉํ ๋ฐ์ด๋๋ฆฌ ์ง๋ ฌํ๋ก JSON ๋๋น ํ์ด๋ก๋ ํฌ๊ธฐ๊ฐ ์๊ณ ํ์ฑ ์๋๊ฐ ๋น ๋ฆ
๋๋ค. HTTP/2 ๊ธฐ๋ฐ์ผ๋ก ๋จ์ผ ์ฐ๊ฒฐ์์ ์ฌ๋ฌ ์์ฒญ์ ๋์์ ์ฒ๋ฆฌํ๋ ๋ฉํฐํ๋ ์ฑ์ ์ง์ํ๋ฉฐ, ํค๋ ์์ถ(HPACK)์ผ๋ก ๋คํธ์ํฌ ์ค๋ฒํค๋๋ฅผ ์ค์
๋๋ค. .proto ํ์ผ์ ํตํ ๊ณ์ฝ ์ฐ์ (Contract-First) ์ค๊ณ๋ก ํ์
์์ ์ฑ์ ๋ณด์ฅํ๊ณ , ์๋ฒ/ํด๋ผ์ด์ธํธ ์คํธ๋ฆฌ๋ฐ ๋ฐ ์๋ฐฉํฅ ์คํธ๋ฆฌ๋ฐ์ ๋ค์ดํฐ๋ธ๋ก ์ง์ํฉ๋๋ค.
Q3: ๋ง์ดํฌ๋ก์๋น์ค ํ๊ฒฝ์์ ๋ถ์ฐ ํธ๋์ญ์ ์ ์ด๋ป๊ฒ ์ฒ๋ฆฌํฉ๋๊น?
๋ถ์ฐ ํ๊ฒฝ์์ 2PC(Two-Phase Commit)๋ ์ฑ๋ฅ๊ณผ ๊ฐ์ฉ์ฑ ๋ฌธ์ ๊ฐ ์์ผ๋ฏ๋ก, ์ฌ๊ฐ(Saga) ํจํด์ด ์ฌ์ค์ ํ์ค์ ๋๋ค. ์ฌ๊ฐ๋ ๊ฐ ์๋น์ค์ ๋ก์ปฌ ํธ๋์ญ์ ์ ์์ฐจ์ ์ผ๋ก ์คํํ๊ณ , ํน์ ๋จ๊ณ๊ฐ ์คํจํ๋ฉด ์ด์ ๋จ๊ณ์ ๋ณด์ ํธ๋์ญ์ (Compensating Transaction)์ ์คํํ์ฌ ์ต์ข ์ ์ผ๊ด์ฑ(Eventual Consistency)์ ๋ฌ์ฑํฉ๋๋ค. ์ฝ๋ ์ค๊ทธ๋ํผ ๋ฐฉ์์ ์ด๋ฒคํธ ๊ธฐ๋ฐ์ผ๋ก ๊ฐ ์๋น์ค๊ฐ ์์จ์ ์ผ๋ก ๋ฐ์ํ๋ฉฐ, ์ค์ผ์คํธ๋ ์ด์ ๋ฐฉ์์ ์ค์ ์ฝ๋๋ค์ดํฐ๊ฐ ์ํฌํ๋ก์ฐ๋ฅผ ๊ด๋ฆฌํฉ๋๋ค. ๋ณต์กํ ๋น์ฆ๋์ค ํ๋ก์ธ์ค์์๋ ์ค์ผ์คํธ๋ ์ด์ ์ด ๋๋ฒ๊น ๊ณผ ๋ชจ๋ํฐ๋ง ์ธก๋ฉด์์ ์ ๋ฆฌํฉ๋๋ค.
Q4: ์ํท ๋ธ๋ ์ด์ปค ํจํด์ ์ธ ๊ฐ์ง ์ํ์ ์ ํ ์กฐ๊ฑด์ ์ค๋ช ํ์ญ์์ค.
Closed ์ํ์์๋ ๋ชจ๋ ์์ฒญ์ด ๋์ ์๋น์ค๋ก ์ ์ ์ ๋ฌ๋ฉ๋๋ค. ์คํจ์จ์ด ์ค์ ๋ ์๊ณ๊ฐ(์: 50%)์ ์ด๊ณผํ๋ฉด Open ์ํ๋ก ์ ํ๋๋ฉฐ, ๋ชจ๋ ์์ฒญ์ด ์ฆ์ ์คํจ ์ฒ๋ฆฌ๋ฉ๋๋ค. ์ค์ ๋ ๋๊ธฐ ์๊ฐ์ด ๊ฒฝ๊ณผํ๋ฉด Half-Open ์ํ๋ก ์ ํ๋์ด, ์ ํ๋ ์์ ์ํ ์์ฒญ์ ๋ณด๋ ๋๋ค. ์ํ ์์ฒญ์ด ์ฑ๊ณตํ๋ฉด Closed๋ก ๋ณต๊ทํ๊ณ , ์คํจํ๋ฉด ๋ค์ Open ์ํ๋ก ์ ํ๋ฉ๋๋ค. ์ด ํจํด์ ์ฅ์ ์๋น์ค์ ๋ํ ๋ถํ์ํ ์์ฒญ์ ์ฐจ๋จํ์ฌ ์ ์ฒด ์์คํ ์ ์๋ต ์๊ฐ๊ณผ ๋ฆฌ์์ค๋ฅผ ๋ณดํธํฉ๋๋ค.
Q5: NestJS ํ์ด๋ธ๋ฆฌ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ค๋ฌด ํ์ฉ ์๋๋ฆฌ์ค๋ฅผ ์ค๋ช ํ์ญ์์ค.
API ๊ฒ์ดํธ์จ์ด ํจํด์ด ๋ํ์ ์ ๋๋ค. HTTP ์๋ํฌ์ธํธ๋ ์ธ๋ถ ํด๋ผ์ด์ธํธ์ RESTful API๋ฅผ ์ ๊ณตํ๊ณ , gRPC๋ ๋ด๋ถ ๋ง์ดํฌ๋ก์๋น์ค ๊ฐ ๊ณ ์ฑ๋ฅ ํต์ ์ ๋ด๋นํฉ๋๋ค. ๋ ๊ฑฐ์ REST API๋ฅผ ์ ์งํ๋ฉด์ ์๋ก์ด ๋ด๋ถ ํต์ ์ gRPC๋ก ๊ตฌ์ถํ๋ ์ ์ง์ ๋ง์ด๊ทธ๋ ์ด์ ์๋ ์ ์ฉํฉ๋๋ค. ๋จ์ผ ๋ฐฐํฌ ๋จ์์์ ์ฌ๋ฌ ํ๋กํ ์ฝ์ ์ง์ํด์ผ ํ๊ฑฐ๋, ์ด์ ๋ณต์ก์ฑ์ ์ค์ด๋ฉด์ ์ฑ๋ฅ์ ๊ฐ์ ํด์ผ ํ๋ ์ํฉ์์ ์ ํฉํ ์ ํ์ ๋๋ค.
Q6: ๋ง์ดํฌ๋ก์๋น์ค ๊ฒฝ๊ณ๋ฅผ ์ ์ํ ๋ ๊ฐ์ฅ ์ค์ํ ์์น์ ๋ฌด์์ ๋๊น?
๋๋ฉ์ธ ์ฃผ๋ ์ค๊ณ์ ๋ฐ์ด๋๋ ์ปจํ ์คํธ๋ฅผ ๊ธฐ์ค์ผ๋ก ์๋น์ค๋ฅผ ๋ถ๋ฆฌํ๋ ๊ฒ์ด ํต์ฌ์ ๋๋ค. ๊ฐ ์๋น์ค๋ ๋ ๋ฆฝ์ ์ผ๋ก ๋ฐฐํฌ ๊ฐ๋ฅํด์ผ ํ๋ฉฐ, ์์ฒด ๋ฐ์ดํฐ ์ ์ฅ์๋ฅผ ๋ณด์ ํด์ผ ํฉ๋๋ค(Database-per-Service ํจํด). ์๋น์ค ๊ฐ ๋ฐ์ดํฐ ๊ณต์ ๋ API๋ฅผ ํตํด์๋ง ์ด๋ฃจ์ด์ ธ์ผ ํ๋ฉฐ, ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ง์ ๊ณต์ ํ๋ฉด ๊ฒฐํฉ๋๊ฐ ๋์์ ธ ๋ ๋ฆฝ์ ๋ฐฐํฌ๊ฐ ๋ถ๊ฐ๋ฅํด์ง๋๋ค. ๊ฒฝ๊ณ๊ฐ ์ฌ๋ฐ๋ฅด์ง ์์ผ๋ฉด ์๋น์ค ๊ฐ ๊ณผ๋ํ ํต์ ์ด ๋ฐ์ํ์ฌ ๋ถ์ฐ ๋ชจ๋๋ฆฌ์ค๋ก ์ ๋ฝํ ์ ์์ต๋๋ค.
์ฐ์ต์ ์์ํ์ธ์!
๋ฉด์ ์๋ฎฌ๋ ์ดํฐ์ ๊ธฐ์ ํ ์คํธ๋ก ์ง์์ ํ ์คํธํ์ธ์.
๊ฒฐ๋ก
NestJS ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ๋ Node.js ๊ธฐ๋ฐ ๋ถ์ฐ ์์คํ ๊ตฌ์ถ์ ํ์ํ ํต์ฌ ๋๊ตฌ์ ํจํด์ ์ฒด๊ณ์ ์ผ๋ก ์ ๊ณตํฉ๋๋ค. ์ด ๊ธ์์ ๋ค๋ฃฌ ๋ด์ฉ์ ์์ฝํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ํธ๋์คํฌํธ ๋ ์ด์ด ์ถ์ํ: NestJS๋ TCP, Redis, NATS, RabbitMQ, Kafka, gRPC ๋ฑ ๋ค์ํ ์ ์ก ๊ณ์ธต์ ๋์ผํ ์ธํฐํ์ด์ค๋ก ์ถ์ํํ์ฌ, ๋น์ฆ๋์ค ๋ก์ง ๋ณ๊ฒฝ ์์ด ํ๋กํ ์ฝ์ ๊ต์ฒดํ ์ ์์ต๋๋ค
- ํต์ ํจํด ์ ํ:
@MessagePattern์ ๋๊ธฐ์ ์์ฒญ-์๋ต์,@EventPattern์ ๋น๋๊ธฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ์ ์ฌ์ฉ๋ฉ๋๋ค. ๋ฐ์ดํฐ ์ผ๊ด์ฑ ์๊ตฌ์ฌํญ์ ๋ฐ๋ผ ์ ์ ํ ํจํด์ ์ ํํด์ผ ํฉ๋๋ค - gRPC ํธ๋์คํฌํธ: Protocol Buffers ๊ธฐ๋ฐ์ ํ์ ์์ ํ ๊ณ ์ฑ๋ฅ ํต์ ์ ์ ๊ณตํ๋ฉฐ, ์๋ฒ ์คํธ๋ฆฌ๋ฐ๊ณผ ์๋ฐฉํฅ ์คํธ๋ฆฌ๋ฐ์ผ๋ก ๋๋ ๋ฐ์ดํฐ ์ ์ก๊ณผ ์ค์๊ฐ ํต์ ์ ๊ตฌํํ ์ ์์ต๋๋ค
- ํ์ด๋ธ๋ฆฌ๋ ์ํคํ ์ฒ: ๋จ์ผ ์ ํ๋ฆฌ์ผ์ด์ ์์ HTTP์ gRPC๋ฅผ ๋์์ ์ด์ํ์ฌ, ์ธ๋ถ์ฉ REST API์ ๋ด๋ถ์ฉ ๊ณ ์ฑ๋ฅ gRPC๋ฅผ ํจ์จ์ ์ผ๋ก ๋ถ๋ฆฌํ ์ ์์ต๋๋ค
- ๋๋ฉ์ธ ์ฃผ๋ ์ค๊ณ ๊ธฐ๋ฐ ์๋น์ค ๊ฒฝ๊ณ: ๋ฐ์ด๋๋ ์ปจํ ์คํธ๋ฅผ ๊ธฐ์ค์ผ๋ก ์๋น์ค๋ฅผ ๋ถ๋ฆฌํ๊ณ , ๋๋ฉ์ธ ์ด๋ฒคํธ๋ฅผ ํตํ ๋์จํ ๊ฒฐํฉ์ผ๋ก ๋ ๋ฆฝ์ ๋ฐฐํฌ์ ํ์ฅ์ ๋ฌ์ฑํ ์ ์์ต๋๋ค
- ์์ ์ฑ ํจํด ์ ์ฉ: ํ์์์, ์ง์ ๋ฐฑ์คํ ์ฌ์๋, ์ํท ๋ธ๋ ์ด์ปค ํจํด์ ์กฐํฉํ์ฌ ์ฐ์ ์คํจ๋ฅผ ๋ฐฉ์งํ๊ณ ์์คํ ๋ณต์๋ ฅ์ ํ๋ณดํด์ผ ํฉ๋๋ค
- ๋ฉด์ ๋๋น: ํธ๋์คํฌํธ ํจํด, gRPC์ ๊ธฐ์ ์ ์ฅ์ , ์ฌ๊ฐ ํจํด, ์ํท ๋ธ๋ ์ด์ปค ์ํ ์ ์ด, ์๋น์ค ๊ฒฝ๊ณ ์ค๊ณ์ ๋ํ ์ค๋ฌด ์์ค์ ์ดํด๊ฐ ์๊ตฌ๋ฉ๋๋ค
ํ๊ทธ
๊ณต์
๊ด๋ จ ๊ธฐ์ฌ

NestJS ๋ฉด์ : Guards, Interceptors, ๋ชจ๋ํ ์ํคํ ์ฒ
Guards, Interceptors, ๋ชจ๋ํ ์ํคํ ์ฒ์ ๊ดํ NestJS ๊ธฐ์ ๋ฉด์ ์ ๋น์ถ ์ง๋ฌธ์ ๊ตฌ์ฒด์ ์ธ TypeScript ์ฝ๋ ์์ ์ ๊ธฐ์ ์ ์ค๋ช ๊ณผ ํจ๊ป ๋ค๋ฃน๋๋ค.

NestJS + Prisma: Node.js๋ฅผ ์ํ ๋ชจ๋ ๋ฐฑ์๋ ์คํ
NestJS์ Prisma๋ก ๋ชจ๋ํ ๋ฐฑ์๋ API๋ฅผ ๊ตฌ์ถํ๊ธฐ ์ํ ์์ ํ ๊ฐ์ด๋์ ๋๋ค. ์ค์ , ๋ชจ๋ธ, ์๋น์ค, ํธ๋์ญ์ ๋ฐ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ์ค๋ช ํฉ๋๋ค.

NestJS: ์์ ํ REST API ๊ตฌ์ถ ๊ฐ์ด๋
NestJS๋ก ์ ๋ฌธ์ ์ธ REST API๋ฅผ ๊ตฌ์ถํ๋ ์๋ฒฝ ๊ฐ์ด๋์ ๋๋ค. ์ปจํธ๋กค๋ฌ, ์๋น์ค, ๋ชจ๋ ๊ตฌ์ฑ, class-validator๋ฅผ ํ์ฉํ ์ ํจ์ฑ ๊ฒ์ฌ, ์๋ฌ ํธ๋ค๋ง์ ์ค์ ์ฝ๋๋ก ์ค๋ช ํฉ๋๋ค.