# 2026년 NestJS 마이크로서비스: 아키텍처, gRPC, 면접 질문 완벽 가이드 > NestJS 마이크로서비스 아키텍처의 핵심 개념, gRPC 트랜스포트 구성, 스트리밍 패턴, 안정성 패턴, 면접 빈출 질문을 실무 중심으로 다룹니다. - Published: 2026-05-26 - Updated: 2026-05-26 - Author: SharpSkill - Tags: nestjs, microservices, grpc, node.js, typescript, architecture - Reading time: 10 min --- NestJS 마이크로서비스는 2026년 현재 Node.js 기반 분산 시스템 구축의 사실상 표준으로 자리 잡았습니다. gRPC 트랜스포트, 이벤트 기반 통신, 서킷 브레이커 패턴까지 NestJS가 제공하는 마이크로서비스 도구는 대규모 백엔드 시스템의 복잡성을 효과적으로 관리합니다. 이 글에서는 NestJS 마이크로서비스 아키텍처의 핵심 개념부터 gRPC 구성, 스트리밍 패턴, 안정성 패턴, 그리고 기술 면접에서 빈출되는 질문까지 실무 중심으로 다룹니다. > **gRPC와 REST 중 어떤 것을 선택해야 할까?** > > 내부 서비스 간 통신에는 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) 패턴을 구현하며, 발신자는 응답을 기다리지 않고 다음 작업을 수행합니다. 알림 전송, 로그 기록, 비동기 상태 업데이트에 적합합니다. ```typescript // orders.controller.ts 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 메서드와 메시지 구조를 선언하며, 클라이언트와 서버 양쪽 모두의 통신 계약 역할을 합니다. ```protobuf // proto/users.proto 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; } ``` > **proto3 문법과 필드 번호 규칙** > > `syntax = \"proto3\"`는 Protocol Buffers 3 버전을 명시합니다. 각 필드에 할당된 번호(1, 2, 3...)는 바이너리 인코딩에서 필드를 식별하는 데 사용되므로, 한 번 배포된 후에는 변경하거나 재사용해서는 안 됩니다. 필드를 삭제할 때는 `reserved` 키워드로 해당 번호를 예약하여 향후 충돌을 방지해야 합니다. `.proto` 파일 정의가 완료되면 NestJS 애플리케이션을 gRPC 마이크로서비스로 부트스트랩합니다. `NestFactory.createMicroservice` 메서드에 `Transport.GRPC` 옵션을 전달하면 전용 gRPC 서버가 생성됩니다. ```typescript // main.ts 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( 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` 파일의 정의와 정확히 일치해야 합니다. ```typescript // users.controller.ts 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을 활용하여 스트리밍을 자연스럽게 구현할 수 있습니다. 서버 스트리밍은 클라이언트가 단일 요청을 보내면 서버가 여러 개의 응답을 순차적으로 반환하는 패턴입니다. 대량의 데이터를 메모리에 한꺼번에 적재하지 않고 청크 단위로 전송할 수 있어, 검색 결과 목록이나 실시간 피드 전달에 유용합니다. ```typescript // users.controller.ts — server streaming 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 { // 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를 사용하여 성능과 접근성을 모두 확보할 수 있습니다. ```typescript // main.ts — hybrid application 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({ 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로 전환하는 마이그레이션 시나리오에서도 이 접근 방식이 효과적입니다. ## 서비스 경계와 도메인 주도 설계 마이크로서비스 아키텍처에서 가장 어려운 과제는 서비스 경계를 올바르게 정의하는 것입니다. 잘못된 경계 설정은 분산 모놀리스(Distributed Monolith)라는 최악의 결과를 초래합니다. 네트워크 호출의 복잡성은 늘어나지만, 독립적 배포와 확장이라는 마이크로서비스의 본질적 이점은 얻지 못하는 상황입니다. 도메인 주도 설계(DDD)의 바운디드 컨텍스트(Bounded Context) 개념은 서비스 분리의 핵심 기준을 제공합니다. 각 마이크로서비스는 하나의 바운디드 컨텍스트를 담당하며, 해당 도메인의 용어와 규칙을 독자적으로 정의합니다. 전자상거래 시스템을 예로 들면, 주문(Orders), 사용자(Users), 결제(Payments), 재고(Inventory)가 각각 독립된 바운디드 컨텍스트에 해당합니다. 서비스 간 결합도를 낮추려면 동기적 직접 호출보다 도메인 이벤트 기반의 비동기 통신을 우선적으로 고려해야 합니다. 주문 서비스가 결제 서비스를 직접 호출하는 대신, `OrderCreated` 이벤트를 발행하고 결제 서비스가 이를 구독하는 방식입니다. 이 구조에서는 결제 서비스에 장애가 발생하더라도 주문 서비스의 가용성에 영향을 주지 않습니다. 안티 부패 계층(Anti-Corruption Layer, ACL)은 외부 시스템이나 레거시 서비스와 통합할 때 내부 도메인 모델의 순수성을 보호하는 패턴입니다. 외부 모델과 내부 모델 사이에 변환 계층을 두어, 외부 시스템의 변경이 내부 도메인에 직접 전파되는 것을 차단합니다. ## 안정성 패턴: 타임아웃, 재시도, 서킷 브레이커 분산 시스템에서 네트워크 장애, 서비스 과부하, 일시적 오류는 피할 수 없는 현실입니다. 단일 서비스의 장애가 전체 시스템으로 전파되는 연쇄 실패(Cascading Failure)를 방지하려면, 체계적인 안정성 패턴을 적용해야 합니다. 타임아웃은 가장 기본적인 방어 수단입니다. 응답이 지정된 시간 내에 도착하지 않으면 요청을 실패로 처리하여, 느린 서비스에 무한정 대기하는 상황을 방지합니다. 재시도 패턴은 일시적 장애를 극복하는 데 유용하지만, 지수 백오프(Exponential Backoff)와 지터(Jitter)를 적용하지 않으면 장애 상황을 악화시킬 수 있습니다. ```typescript // orders.service.ts 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` 헬스 체크 모듈과 결합하여 구현할 수 있습니다. > **재시도 패턴과 멱등성(Idempotency)** > > 재시도 패턴을 적용할 때는 해당 작업이 멱등성을 보장하는지 반드시 확인해야 합니다. 주문 생성처럼 부수 효과가 있는 작업을 멱등성 키 없이 재시도하면 중복 주문이 발생할 수 있습니다. 조회 작업은 본질적으로 멱등하지만, 변경 작업에는 별도의 멱등성 메커니즘이 필요합니다. ## 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의 기술적 장점, 사가 패턴, 서킷 브레이커 상태 전이, 서비스 경계 설계에 대한 실무 수준의 이해가 요구됩니다 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ko/blog/node-nestjs/nestjs-microservices-grpc-architecture