728x90
도입 배경: Controller에 쌓여가는 new 연쇄
처음 인증 서비스를 개발할 때는 다음과 같은 방식으로 객체를 조립하고 있었습니다.
const userRepository = new UserRepository(...);
const sessionRepository = new SessionRepository(...);
const tokenIssuer = new JwtTokenIssuer();
const tokenService = new TokenService(tokenIssuer, ...);
const registerUsecase = new RegisterUserUsecase(userRepository, sessionRepository, ..., tokenService);
const registerController = new RegisterController(registerUsecase);
이 코드는 문제 없이 동작하지만, 다음과 같은 구조적 문제를 내포하고 있었습니다.
문제점 정리
구분 | 문제 |
중복 | controller.ts, route.ts 등 여러 파일에서 반복적으로 인스턴스를 생성해야 함 |
유지보수 | 의존성 변경 시 모든 controller에서 일일이 수정 필요 |
테스트 어려움 | 개별 controller 단위 테스트를 위해 mock 객체 주입이 까다로움 |
역할 과중 | Controller가 로직 실행뿐 아니라 의존성 조립까지 담당하게 됨 |
이러한 문제는 프로젝트가 커질수록 유지보수 비용으로 이어졌고, 이를 해결하기 위해 의존성 조립 전용 파일, 즉 index.ts
를 도입하게 되었습니다.
해결 전략: Composition Root로서의 index.ts
Composition Root란 프로그램 실행 시점에 의존성을 생성하고 조립하는 시작점을 의미합니다.
이 구조는 DDD, Clean Architecture에서도 적극적으로 권장되는 방식입니다.
도입한 구조
// auth/controllers/index.ts
export const registerController = new RegisterController(registerUserUsecase);
export const loginController = new LoginController(loginUserUsecase);
// auth/use-cases/index.ts
export const registerUserUsecase = new RegisterUserUsecase(
userRepository,
sessionRepository,
refreshTokenRepository,
tokenService
);
// auth/routes/authRouter.ts
import { registerController } from '../controllers';
router.post('/register', (req, res) => registerController.handle(req, res));
이제 각 route에서는 단순히 .handle()
메서드만 호출하면 되며, 내부 조립은 전부 index 파일에 위임됩니다.
Composition Root 구조의 이점
항목 | 설명 |
유지보수성 | 의존성 변경 시 `index.ts`만 수정하면 됨 |
중복 제거 | route/controller 단에서 new 연쇄 제거 |
테스트 용이성 | Mock 객체를 주입한 조립 index를 따로 만들 수 있음 |
역할 분리 | controller는 실행만, usecase는 로직만, index는 조립만 담당 |
실무 사례와 비교
프레임워크 | Composition Root 역할 |
NestJS | `@Module()` |
Spring Boot | `@Configuration`, `@Bean` |
Node.js | 수동 DI 시 `index.ts` 조립 방식 사용 |
마무리
인증 서비스를 개발하면서, route 단에서 반복되는 인스턴스 생성 문제를 체감했고, 이를 실무적으로 해결하고자 조립용 index.ts
구조를 도입했습니다.
이 구조는 프로젝트가 커질수록 진가를 발휘하며, 유지보수성과 테스트 용이성을 동시에 확보할 수 있습니다.
서비스의 책임을 명확하게 나누고 싶다면, 지금 바로 Composition Root를 도입해보세요.
반응형
'백엔드 개발' 카테고리의 다른 글
Chat 도메인에서 TypeORM을 제거한 이유: 도메인 객체와 Persistence의 분리 (1) | 2025.06.09 |
---|---|
Spring 실무에서 꼭 알아야 할 디자인 패턴 4가지 – 구조부터 적용 시점까지 정리 (1) | 2025.05.29 |
Spring Boot에서 Java record를 활용한 DTO 설계법 (feat. JSON 직렬화) (1) | 2025.05.18 |
[Node.js] Redis + DB로 안전하게 토큰 관리하기 – 실전 적용기 (1) | 2024.01.02 |