마이크로서비스 아키텍처(MSA)는 서비스들을 독립적으로 개발하고 배포할 수 있게 해주며, 서로 다른 서비스 간 통신을 위해 다양한 메시지 패턴을 활용합니다.
이번 글에서는 NestJS를 활용한 User Microservice와 Order Microservice 간 TCP 통신을 구현하는 과정을 정리합니다.
이는 Next.js 프론트엔드와도 연동 가능한 백엔드 구조를 만드는 기반이 됩니다.
1. 프로젝트 구조 개요
서비스는 크게 4개의 주요 모듈로 나뉩니다.
User Microservice : 회원가입, 로그인, 토큰 발급 및 검증 담당 (PostgreSQL 사용)
Order Microservice : 주문 생성 및 관리 담당 (MongoDB 사용)
Product Microservice : 상품 관리 담당 (PostgreSQL 사용)
Payment Microservice : 결제 처리 담당 (PostgreSQL 사용)
각 서비스는 Docker Compose를 통해 독립 실행되며, 서로 다른 데이터베이스를 사용합니다. 중요한 점은 서비스 간 직접 DB 접근을 금지하고, 반드시 TCP 통신을 통해 필요한 데이터를 요청한다는 것입니다.
2. User Microservice – 인증 및 토큰 검증
AuthController
@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}
  @Post('register')
  registerUser(@Authorization() token: string, @Body() registerDto: RegisterDto) {
    if (!token) throw new UnauthorizedException('토큰을 입력해주세요!');
    return this.authService.register(token, registerDto);
  }
  @Post('login')
  loginUser(@Authorization() token: string) {
    if (!token) throw new UnauthorizedException('토큰을 입력해주세요!');
    return this.authService.login(token);
  }
  // TCP MessagePattern → Order MS에서 호출 가능
  @MessagePattern({ cmd: 'parse_bearer_token' })
  parseBearerToken(@Payload() payload: ParseBearerTokenDto) {
    return this.authService.parseBearerToken(payload.token, false);
  }
}
여기서 핵심은 @MessagePattern입니다. HTTP 요청이 아니라 TCP 기반 메시지를 받도록 설정하여 다른 마이크로서비스(Order 등)에서 호출할 수 있습니다.
AuthService 핵심 로직
Basic Token 파싱 : Basic base64(username:password) 구조 해석 → 회원가입, 로그인 시 활용
Bearer Token 파싱 : Bearer access_token 구조 해석 → Order 서비스가 사용자 정보를 가져올 때 사용
JWT 발급 : Access Token과 Refresh Token을 분리 발급
⚠️ 참고: 코드 일부 주석에서 Bearer와 Basic이 혼동된 부분이 있었는데, 실제 회원가입/로그인에서는 Basic 인증 방식을 사용합니다. 혼동을 피하기 위해 정확히 구분해야 합니다.
3. Order Microservice – 주문 생성
OrderService
@Injectable()
export class OrderService {
  constructor(@Inject('USER_SERVICE') private readonly userServiceClient: ClientProxy) {}
  async createOrder(createOrderDto: CreateOrderDto, token: string) {
    // 1) User Microservice에 JWT 토큰 검증 요청
    const user = await this.getUserFromToken(token);
    // 2) 주문 생성 로직 (DB 저장, 결제 요청 등)
    // TODO: 상품 가격 검증, 결제 시도 등 추가 구현 필요
    return { message: '주문 생성 완료', user, order: createOrderDto };
  }
  async getUserFromToken(token: string) {
    const resp = await lastValueFrom(
      this.userServiceClient.send({ cmd: 'parse_bearer_token' }, { token }),
    );
    return resp;
  }
}
Order 서비스는 User 서비스의 DB를 직접 조회하지 않고, 반드시 TCP 메시지 호출을 통해 사용자 정보를 검증합니다. 이는 MSA 원칙에 충실한 구현 방식입니다.
4. TCP 통신 설정
NestJS에서 TCP 통신을 설정하려면 ClientsModule과 connectMicroservice를 활용합니다.
AppModule (Order Service)
@Module({
  imports: [
    ClientsModule.registerAsync({
      clients: [
        {
          name: 'USER_SERVICE',
          useFactory: () => ({
            transport: Transport.TCP,
            options: { host: 'user', port: 3001 }, // User MS의 TCP 포트
          }),
        },
      ],
      isGlobal: true,
    }),
    OrderModule,
  ],
})
export class AppModule {}
main.ts (Order Service)
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.connectMicroservice<MicroserviceOptions>({
    transport: Transport.TCP,
    options: { host: '0.0.0.0', port: parseInt(process.env.TCP_PORT) || 3003 },
  });
  await app.startAllMicroservices();
  await app.listen(process.env.HTTP_PORT ?? 3003);
}
bootstrap();
⚠️ 주의: HTTP_PORT는 REST API 요청을 위한 포트이고, TCP_PORT는 마이크로서비스 간 통신용 포트입니다. 반드시 구분해서 설정해야 합니다.
5. Docker Compose 연동
services:
  user:
    build: ./apps/user
    command: pnpm run start:dev user
    ports:
      - '3001:3001'
  order:
    build: ./apps/order
    command: pnpm run start:dev order
    ports:
      - '3003:3003'
    depends_on:
      - mongo_order
User MS는 PostgreSQL(postgres_user)에 연결
Order MS는 MongoDB(mongo_order)에 연결
두 서비스는 TCP 통신을 통해 상호작용
6. 주문 처리 흐름 요약
클라이언트(Next.js) → Order MS /order API 호출 (JWT 토큰 포함)
Order MS → User MS로 TCP 메시지 { cmd: 'parse_bearer_token' } 전송
User MS → 토큰 검증 후 사용자 정보 반환
Order MS → 사용자 검증 성공 시 주문 생성 및 DB 저장
Order MS → 최종 응답 반환 (주문 완료)
7. 실무 보완 포인트
동기 vs 비동기 통신 : TCP는 동기 방식이므로 응답 대기 시간이 발생할 수 있습니다. 확장성을 위해 RabbitMQ, Kafka 같은 메시지 브로커 기반 비동기 통신을 고려할 수 있습니다.
에러 핸들링 강화 : 현재는 단순 예외 처리만 있지만, 토큰 만료, 사용자 없음, 네트워크 장애 등을 세분화하여 처리해야 합니다.
API Gateway 도입 : 모든 요청을 API Gateway를 거쳐 인증 및 라우팅을 통합 관리하면 보안성과 유지보수성이 크게 향상됩니다.
테스트 전략 : 단위 테스트뿐만 아니라 서비스 간 TCP 통신을 검증하는 통합 테스트(E2E Test)가 필수입니다.
8. 결론
이번 글에서는 NestJS 기반 마이크로서비스 간 TCP 통신을 통해 User MS와 Order MS가 상호작용하는 방식을 살펴보았습니다. 핵심 요약은 다음과 같습니다.
NestJS ClientsModule과 ClientProxy를 활용한 서비스 간 메시징 구현
@MessagePattern을 이용한 TCP 요청 처리
JWT 토큰을 User MS에서 검증 후 Order MS에서 활용
HTTP 포트와 TCP 포트를 명확히 구분
이 방식은 서비스 간 강한 결합을 피하면서도 안정적인 인증 흐름을 구현할 수 있습니다.
다음 단계에서는 Payment Microservice 연동과 메시지 브로커 기반 확장(RabbitMQ, Kafka) 을 적용해보면 더욱 견고한 MSA 구조를 만들 수 있습니다.













댓글 ( 0)  
댓글 남기기