NestJS 모듈과의 싸움을 멈추세요: Rikta를 만나보세요

발행: (2026년 1월 20일 오전 01:45 GMT+9)
9 분 소요
원문: Dev.to

Source: Dev.to

위에 제공된 링크에 있는 전체 텍스트를 여기 채팅에 붙여 주시면, 해당 내용을 한국어로 번역해 드리겠습니다. 코드 블록, URL 및 마크다운 형식은 그대로 유지됩니다.

The Module Problem

NestJS 모듈은 실제 문제인 의존성 경계를 해결합니다. 대규모 코드베이스에서 혼란을 방지하지만, 그에 따른 비용이 있습니다.

새로운 서비스마다 다음을 해야 합니다:

  1. @Injectable() 로 서비스를 생성합니다.
  2. 모듈의 providers 배열에 추가합니다.
  3. 다른 모듈에서 필요하면 exports 배열에 추가합니다.
  4. 사용되는 모든 곳에서 모듈을 import 합니다.

전형적인 NestJS 모듈은 다음과 같습니다:

// user.module.ts
@Module({
  imports: [
    DatabaseModule,
    ConfigModule,
    AuthModule,
    LoggingModule,
  ],
  controllers: [UserController],
  providers: [
    UserService,
    UserRepository,
    UserValidator,
    UserMapper,
  ],
  exports: [UserService, UserRepository],
})
export class UserModule {}

그것은 많은 보일러플레이트—순수한 설정 오버헤드가 빠르게 증가합니다.

중간 규모의 앱은 20개 이상의 모듈을 가질 수 있으며, 각 모듈마다 5~10개의 프로바이더가 있습니다. 비즈니스 로직을 작성하기보다 배열을 관리하는 데 더 많은 시간을 소비하게 됩니다.

가장 안 좋은 점은? 배열 중 어느 하나라도 오타가 있으면 전체 의존성 그래프가 깨지고, 디버깅에 몇 시간이 걸릴 수 있습니다.

Source:

모듈이 필요 없다고 가정한다면?

Rikta는 Fastify 위에 구축된 새로운 TypeScript 프레임워크입니다. NestJS에서 개발자들이 좋아하는 부분(데코레이터, DI, 구조)을 그대로 유지하면서 모듈 설정을 완전히 제거합니다.

  • imports 배열이 없습니다.
  • exports 배열이 없습니다.
  • providers 배열이 없습니다.

클래스에 데코레이터만 붙이면 모든 것이 동작합니다.

// user.service.ts
import { Injectable } from '@riktajs/core';

@Injectable()
export class UserService {
  getUsers() {
    return ['Alice', 'Bob'];
  }
}
// user.controller.ts
import { Controller, Get, Autowired } from '@riktajs/core';
import { UserService } from './user.service';

@Controller('/users')
export class UserController {
  @Autowired()
  private userService!: UserService;

  @Get()
  getUsers() {
    return this.userService.getUsers();
  }
}

그게 전부입니다—모듈 파일도, 별도 등록도 필요 없습니다. Rikta는 시작 시 코드를 스캔하고 의존성을 자동으로 해결합니다.

Zero‑Config 자동 와이어링 작동 방식

Rikta는 세 가지 메커니즘을 사용합니다:

  1. 자동 발견 – 시작 시 @Controller() 또는 @Injectable() 로 데코레이트된 클래스를 프로젝트 전체에서 스캔합니다. 생성자 매개변수와 @Autowired() 데코레이터를 기반으로 의존성 그래프를 구축합니다.
  2. 전역 제공자 레지스트리 – 모든 제공자는 단일 레지스트리에 저장됩니다. injectable 클래스는 앱 어디서든 사용할 수 있으며, 별도의 export 또는 import가 필요 없습니다.
  3. 의존성 해결 – Rikta는 TypeScript 메타데이터(reflect-metadata 활용)를 읽어 각 클래스가 무엇을 필요로 하는지 파악하고, 올바른 순서로 인스턴스를 생성하여 자동으로 주입합니다.

초기화 중에 순환 의존성이 감지되면 명확한 오류를 발생시킵니다:

Error: Circular dependency detected: 
UserService -> AuthService -> UserService

암호 같은 스택 트레이스도 없고, 런타임에서 깜짝 놀라움도 없습니다.

나란히 비교

NestJS에서 새로운 기능 만들기

// 1. Create the service
@Injectable()
export class PaymentService {
  constructor(private configService: ConfigService) {}
}

// 2. Create the module
@Module({
  imports: [ConfigModule],
  providers: [PaymentService],
  exports: [PaymentService],
})
export class PaymentModule {}

// 3. Import in app.module.ts
@Module({
  imports: [PaymentModule, /* ... */],
})
export class AppModule {}

// 4. Import in any module that needs it
@Module({
  imports: [PaymentModule],
  providers: [OrderService],
})
export class OrderModule {}

Rikta에서 동일한 기능 만들기

@Injectable()
export class PaymentService {
  @Autowired()
  private configService!: ConfigService;
}

완료.

성능 이점

Rikta는 HTTP 레이어로 Fastify를 사용합니다. Fastify는 벤치마크에서 초당 30,000 요청을 처리합니다.

공식 벤치마크 결과:

MetricRikta vs. NestJS
Startup time43 % 더 빠름
GET requests41 % 더 빠름
POST requests25 % 더 빠름
Route parameters46 % 더 빠름

Rikta는 기본 Fastify에 비해 **2‑5 %**의 오버헤드만 추가하며, 속도를 희생하지 않고 DI와 데코레이터를 제공합니다.

내장 Zod 검증

NestJS는 class-validator 데코레이터에 의존하므로 타입을 두 번 정의해야 합니다: 한 번은 TypeScript용, 한 번은 검증용입니다.

Rikta는 Zod를 기본적으로 통합합니다. 스키마를 한 번 정의하면 검증과 TypeScript 타입을 자동으로 얻을 수 있습니다.

import { Controller, Post, Body, z } from '@riktajs/core';

const CreateUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(2),
  age: z.number().optional(),
});

@Controller('/users')
export class UserController {
  @Post()
  create(@Body(CreateUserSchema) user: z.infer<typeof CreateUserSchema>) {
    // 'user' is validated AND typed
    // Invalid requests return 400 automatically
    return { created: user };
  }
}

중복된 타입 정의가 없고, 검증 실패에 대한 수동 오류 처리도 필요 없습니다.

Rikta를 언제 사용해야 할까

Rikta가 가장 잘 작동하는 경우:

  • 스타트업 및 MVP(Minimum Viable Product)에서 개발 속도가 중요한 경우.
  • 소규모~중간 규모 팀(1‑15명 개발자)으로, 깔끔하고 설정이 필요 없는 DI 시스템을 원하는 경우.

NestJS의 사용 편의성을 좋아하지만 모듈 보일러플레이트가 지겹다면 Rikta를 시도해 보세요.

  • 마이크로서비스 – 각 서비스가 집중된 상태를 유지할 때.
  • NestJS를 알고 있는 개발자이며 보일러플레이트를 줄이고 싶을 때.

NestJS를 고려해야 할 경우:

  • 엄격한 모듈 경계가 필요한 대규모 팀이 있을 때.
  • 방대한 NestJS 생태계(특정 어댑터, 플러그인 등)가 필요할 때.
  • 조직에서 명시적인 의존성 문서화를 요구할 때.

Getting Started

Create a new project in seconds:

npx @riktajs/cli new my-app
cd my-app
npm run dev

Your API runs at http://localhost:3000.

The CLI generates a complete project with:

  • Rikta에 최적화된 TypeScript 설정
  • REST 엔드포인트가 포함된 예제 컨트롤러
  • 의존성 주입이 포함된 예제 서비스
  • 핫‑리로드 개발 서버

Resources

  • Documentation: (link)
  • GitHub: (link)
  • NPM: (link)

Rikta는 MIT 라이선스를 가지고 있으며 오픈 소스입니다.


제로‑컨피그 프레임워크를 사용해 보셨나요? 모듈과 자동 와이어링 간의 트레이드‑오프에 대해 어떻게 생각하시나요? 댓글로 경험을 공유해주세요.

Back to Blog

관련 글

더 보기 »

graphql-complexity-validation 소개

✨ 특징 - 런타임 의존성 없음 - 완전 타입 지정된 TypeScript - fragments 및 inline fragments 지원 - 기본적으로 Introspection 무시 지원 대상: - Apollo…

Nest.js에서 MonoRepo 설정

Monorepos와 Nest.js Monorepos는 하나 이상의 서비스나 공유 라이브러리를 관리하는 백엔드 팀에게 기본 선택이 되고 있습니다. Nest.js는 매우 잘 작동합니다.