#Backend

2. 유스케이스를 격리하고 의존성을 나누어 Lint로 규칙 세우기

2. 유스케이스를 격리하고 의존성을 나누어 Lint로 규칙 세우기
01

Summary

무려 2,199개의 가짜 Mock을 단 4개로 줄인 컴파일러 기반 아키텍처 개편기

사람의 의지와 문서 컨벤션을 뛰어넘어 시스템과 타입 제약으로 강제하는 완벽한 리팩터링의 기술

무조건 테스트를 통과시키는 가짜 안전감에서 벗어나, 실수하고 싶어도 실수할 수 없는 강력한 시스템적 제약을 설계해 나가는 과정을 조명합니다. 서비스를 잘게 쪼갠 UseCase 격리부터 포트와 어댑터 구조 설계, 그리고 TypeScript 컴파일 타임 검증을 통한 빈틈없는 인터페이스 동기화 기법까지 실제 아임웹 백엔드 팀의 집요한 개선 여정을 공유합니다. 말뿐인 문서보다 컴파일 에러가 아키텍처 규격을 더 확실히 지켜낸다는 실무적인 지혜를 만나보세요.

  • 01무분별한 Mock 가정을 원천 차단하고 대체 행동 지침을 제시하는 에러 메시지 탑재 커스텀 ESLint 룰 배포
  • 02비대해진 기존 서비스를 단일 책임을 지닌 UseCase 클래스로 분산 격리하여 테스트 필요 인자 수 대폭 축소
  • 03AsyncLocalStorage(ALS)를 활용한 트랜잭션 라이프사이클 격리로 리포지토리 시그니처에서 EntityManager 의존성 전면 제거
  • 04TypeScript abstract class를 통한 포트(Port) 설계로 NestJS DI 호환성 확보 및 유연한 런타임 교체 구조 확립
  • 05메서드 파라미터 누락을 잡아내기 위한 NoSignatureDrift 양방향 타입 일치 탐지 유틸리티 도입

RECOMMENDATION

프로젝트 규모가 커짐에 따라 Mock 테스트 유지보수에 고통받고 있거나, AI 코딩 에이전트 도입 후 아키텍처 규격이 점점 무너지는 것을 경험하고 있는 개발팀에게 적극 추천합니다. 컴파일러와 린트를 가장 든든한 코드 감시자로 임명하는 구조적 가이드라인을 확보해 보세요.

The Problem

기존 백엔드 시스템은 비대한 서비스 레이어와 가상 객체(Mock)의 무분별한 남용으로 인해 테스트 신뢰도가 낮았고, TypeORM의 BaseRepository 상속 구조 한계로 안전한 대역 사용이 어려워 'as never' 강제 캐스팅에 의존했습니다.

The Solution

컨트롤러와 UseCase를 분리해 단일 책임 원칙을 적용하고 인라인 쿼리를 리포지토리 내부로 캡슐화했습니다. 또한, AsyncLocalStorage를 통한 트랜잭션 컨텍스트 전파 기법과 포트 및 어댑터 아키텍처를 도입하고, 커스텀 ESLint 및 TypeScript 양방향 타입 일치 검사로 Mock 사용과 인터페이스 왜곡을 제한했습니다.

The Result

기존 약 2,199개에 달하던 Mock 호출 횟수를 검증 성격상 필수적인 HTTP 레이어 4개로 대폭 축소했으며, 통합 테스트 적용 파일을 기존 0개에서 143개로 확장하여 검증력이 높은 견고한 테스트 안전망을 구축했습니다.

Trade-off

사용하지 않는 인터페이스 메서드에 대해 명시적 예외(notUsed)를 던지도록 설계하면서 개발 과정에서 인메모리 어댑터를 수동으로 보완하거나 테스트의 성격에 따라 통합 테스트 영역으로 재배치하는 추가 작업 리소스가 지속해서 발생합니다.

03

Key Concepts

Concept · 01

Ports and Adapters Architecture

비즈니스 도메인의 핵심 로직(Port)을 외부 DB, 라이브러리, 전송 계층 등의 상세 구현 기술(Adapter)과 완전히 격리하여 시스템 유연성과 테스트 용이성을 극대화하는 소프트웨어 설계 패턴입니다.

  • BrandRepository를 외부 DB 기술과 완전히 분리된 추상 클래스 포트로 정의함
  • 운영 환경에는 TypeORM 어댑터를, 단위 테스트 환경에는 인메모리 어댑터를 매핑하도록 다형성 주입 구조를 설계함
Concept · 02

AsyncLocalStorage

Node.js에서 비동기 작업 흐름 전반에 걸쳐 고유한 상태 및 실행 컨텍스트 정보를 공유하고 추적할 수 있도록 제공하는 스레드 로컬 유사 메커니즘입니다.

  • EntityManager를 비동기 저장소에 담아 트랜잭션 전파 흐름을 중앙 제어함
  • 호출자가 매번 인자로 트랜잭션 관리 객체를 전달하지 않아도 리포지토리가 컨텍스트를 판별해 자동 합류하도록 개선함
Concept · 03

Bidirectional Type Drift Checker (NoSignatureDrift)

매개변수 가변성을 허용하는 TypeScript 타입 체커의 특성을 우회하여, 인터페이스와 구현체 간 파라미터 튜플 및 반환 타입을 양방향으로 완벽 대조하는 고급 타입 추론 기술입니다.

  • 포트의 메서드 시그니처가 변경되었을 때 인메모리 구현체가 에러 없이 조용히 오작동하던 현상을 감지함
  • 시그니처 불일치 발견 시 true 타입 대신 구체적인 컴파일 에러 메시지를 튜플 형태로 발생시켜 시각적으로 에러 원인을 고지함