Communitygithub.com

Razsk/VideoStudio

깊은 모듈 설계를 위한 공유 어휘입니다. 사용자가 모듈의 인터페이스를 설계하거나 개선하고, 심화 기회를 찾고, 경계(seam) 위치를 결정하고, 코드를 더 테스트 가능하게 만들거나 AI가 탐색하기 쉽게 만들고자 할 때, 또는 다른 스킬이 깊은 모듈 어휘를 필요로 할 때 사용하세요.

VideoStudio란 무엇인가요?

이 스킬은 소프트웨어 아키텍처에서 '깊은 모듈(deep module)' 설계 원칙을 정의하고 적용하기 위한 공유 어휘와 가이드를 제공합니다. 깊은 모듈은 작은 인터페이스 뒤에 많은 동작(behavior)을 숨겨 호출자에게는 레버리지(leverage)를, 유지보수자에게는 지역성(locality)을, 모두에게는 테스트 용이성(testability)을 제공합니다. 마이클 훼더스(Michael Feathers)의 '경계(seam)' 개념을 중심으로 모듈, 인터페이스, 구현, 깊이(depth), 어댑터(adapter) 등 핵심 용어를 정확히 정의하고, 얕은 모듈과 깊은 모듈을 시각적으로 비교하며 설계 원칙과 테스트 용이성 향상 기법을 제시합니다. 또한 '삭제 테스트(deletion test)', '인터페이스는 테스트 표면(interface is the test surface)', '하나의 어댑터는 가상의 경계, 두 개의 어댑터는 실제 경계'와 같은 원칙을 통해 실질적인 설계 판단을 돕습니다. 이 스킬은 특히 클로드 코드(Claude Code), 커서(Cursor), 코덱스(Codex) 등 AI 에이전트가 코드베이스를 분석하거나 리팩토링할 때 일관된 용어로 의사소통하고 설계 결정을 내리는 데 유용합니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Razsk/VideoStudio/tree/HEAD/.agents/skills/codebase-design

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

코드베이스 설계

깊은 모듈(deep module) 설계: 작은 인터페이스 뒤에 많은 동작을 숨기고, 깨끗한 경계(seam)에 배치하며, 해당 인터페이스를 통해 테스트 가능하게 만듭니다. 코드를 설계하거나 재구성할 때마다 이 언어와 원칙을 사용하세요. 목표는 호출자에게는 레버리지, 유지보수자에게는 지역성, 모두에게는 테스트 용이성입니다.

용어집

다음 용어를 정확히 사용하세요. '컴포넌트', '서비스', 'API', '경계(boundary)' 등으로 대체하지 마세요. 일관된 언어가 핵심입니다.

모듈(Module): 인터페이스와 구현을 가진 모든 것. 의도적으로 규모에 무관합니다: 함수, 클래스, 패키지, 계층을 가로지르는 조각 등. 피할 것: 유닛, 컴포넌트, 서비스.

인터페이스(Interface): 호출자가 모듈을 올바르게 사용하기 위해 알아야 할 모든 것: 타입 시그니처뿐만 아니라 불변 조건, 순서 제약, 오류 모드, 필요한 설정, 성능 특성까지 포함합니다. 피할 것: API, 시그니처 (너무 좁아서 타입 수준의 표면만 가리킴).

구현(Implementation): 모듈 내부에 있는 것, 그 코드 본체. **어댑터(Adapter)**와 구별됩니다: 어떤 것은 큰 구현을 가진 작은 어댑터(PostgreSQL 저장소)일 수도 있고, 작은 구현을 가진 큰 어댑터(인메모리 가짜 객체)일 수도 있습니다. 경계가 주제일 때는 '어댑터'를 사용하고, 그 외에는 '구현'을 사용하세요.

깊이(Depth): 인터페이스에서 얻는 레버리지. 호출자(또는 테스트)가 배워야 하는 인터페이스 단위당 실행할 수 있는 동작의 양. 모듈이 **깊다(deep)**는 것은 많은 동작이 작은 인터페이스 뒤에 숨겨져 있음을 의미하며, **얕다(shallow)**는 것은 인터페이스가 구현만큼이나 복잡함을 의미합니다.

경계(Seam) (마이클 훼더스): 해당 위치에서 직접 편집하지 않고도 동작을 변경할 수 있는 장소; 모듈의 인터페이스가 위치하는 지점. 경계를 어디에 둘지는 그 자체로 별도의 설계 결정이며, 그 뒤에 무엇을 넣을지와는 구별됩니다. 피할 것: boundary (DDD의 bounded context와 혼동됨).

어댑터(Adapter): 경계에서 인터페이스를 충족하는 구체적인 대상. 역할(어떤 슬롯을 채우는지)을 설명하며, 본질(내부에 무엇이 있는지)은 설명하지 않습니다.

레버리지(Leverage): 호출자가 깊이로부터 얻는 것. 배운 인터페이스 단위당 더 많은 기능. 하나의 구현이 N개의 호출 지점과 M개의 테스트에 걸쳐 보답합니다.

지역성(Locality): 유지보수자가 깊이로부터 얻는 것. 변경, 버그, 지식, 검증이 여러 호출자로 퍼지지 않고 한 곳에 집중됩니다. 한 번 고치면 모든 곳에서 고쳐집니다.

깊은 vs 얕은

깊은 모듈 = 작은 인터페이스 + 많은 구현:

┌─────────────────────┐
│   작은 인터페이스    │  ← 적은 메서드, 단순한 매개변수
├─────────────────────┤
│                     │
│  깊은 구현          │  ← 복잡한 로직이 숨겨짐
│                     │
└─────────────────────┘

얕은 모듈 = 큰 인터페이스 + 적은 구현 (피할 것):

┌─────────────────────────────────┐
│       큰 인터페이스             │  ← 많은 메서드, 복잡한 매개변수
├─────────────────────────────────┤
│  얇은 구현                     │  ← 그냥 통과만 시킴
└─────────────────────────────────┘

인터페이스를 설계할 때 다음을 질문하세요:

  • 메서드 수를 줄일 수 있는가?
  • 매개변수를 단순화할 수 있는가?
  • 더 많은 복잡성을 내부에 숨길 수 있는가?

원칙

  • 깊이는 인터페이스의 속성이지 구현의 속성이 아니다. 깊은 모듈은 내부적으로 작고, 모킹 가능하며, 교체 가능한 부품들로 구성될 수 있습니다. 그 부품들은 단지 인터페이스의 일부가 아닐 뿐입니다. 모듈은 내부 경계(구현에 비공개이고 자체 테스트에서 사용됨)와 외부 경계(인터페이스에 있는)를 모두 가질 수 있습니다.
  • 삭제 테스트. 모듈을 삭제한다고 상상해보세요. 복잡성이 사라진다면 단순한 통과(Pass-through)였던 것입니다. 복잡성이 N개의 호출자에 걸쳐 다시 나타난다면 그 모듈은 제 역할을 하고 있었던 것입니다.
  • 인터페이스는 테스트 표면이다. 호출자와 테스트는 동일한 경계를 넘습니다. 인터페이스 너머를 테스트하려 한다면 모듈의 형태가 잘못되었을 가능성이 높습니다.
  • 하나의 어댑터는 가상의 경계를 의미한다. 두 개의 어댑터는 실제 경계를 의미한다. 어떤 것이 실제로 그 경계를 가로질러 변하지 않는 한 경계를 도입하지 마세요.

테스트 용이성을 위한 설계

좋은 인터페이스는 테스트를 자연스럽게 만듭니다:

  1. 의존성을 받아들이고, 생성하지 마세요.

    // 테스트 가능
    function processOrder(order, paymentGateway) {}
    
    // 테스트 어려움
    function processOrder(order) {
      const gateway = new StripeGateway();
    }
    
  2. 결과를 반환하고, 부수 효과를 만들지 마세요.

    // 테스트 가능
    function calculateDiscount(cart): Discount {}
    
    // 테스트 어려움
    function applyDiscount(cart): void {
      cart.total -= discount;
    }
    
  3. 작은 표면적. 메서드가 적을수록 필요한 테스트도 적습니다. 매개변수가 적을수록 테스트 설정이 더 간단해집니다.

관계

  • 모듈은 정확히 하나의 인터페이스(호출자와 테스트에 제공하는 표면)를 가집니다.
  • 깊이는 모듈의 속성이며, 인터페이스를 기준으로 측정됩니다.
  • 경계는 모듈의 인터페이스가 위치하는 곳입니다.
  • 어댑터는 경계에 위치하며 인터페이스를 충족합니다.
  • 깊이는 호출자에게 레버리지를, 유지보수자에게 지역성을 제공합니다.

거부된 프레이밍

  • 구현 라인 수와 인터페이스 라인 수의 비율로서의 깊이(Ousterhout): 구현을 부풀리도록 유도합니다. 대신 레버리지로서의 깊이를 사용합니다.
  • TypeScript interface 키워드 또는 클래스의 public 메서드로서의 '인터페이스': 너무 좁습니다. 여기서의 인터페이스는 호출자가 알아야 할 모든 사실을 포함합니다.
  • '경계(Boundary)': DDD의 bounded context와 혼동됩니다. seam 또는 interface라고 말하세요.

더 깊이 들어가기

  • 의존성을 고려한 클러스터 심화, DEEPENING.md 참조: 의존성 범주, 경계 규율, 계층화 테스트 대신 교체 테스트.
  • 대체 인터페이스 탐색, DESIGN-IT-TWICE.md 참조: 병렬 하위 에이전트를 실행하여 인터페이스를 근본적으로 다른 여러 방식으로 설계한 후, 깊이, 지역성, 경계 배치 측면에서 비교하세요.

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

관련 스킬