# [TIL] 코드 한 줄 없이 서비스 하나를 설계해봤다

728x90

오늘은 코드를 거의 안 짰다. 대신 "레시피 공유 서비스"라는 사이드 프로젝트를 설계 문서만으로 처음부터 끝까지 밟아봤다. 그 과정에서 배운 것들을 정리한다.

1. 모노레포에서 설정을 상속받는 법 — tsconfig.base.json

pnpm workspace로 여러 패키지(api, web, shared, ui)를 굴리는 프로젝트를 만졌는데, tsconfig.json마다 strict: true 같은 공통 옵션을 매번 반복 작성하고 있었다.

알고 보니 TypeScript는 extends라는 필드로 다른 tsconfig를 상속할 수 있었다.

// tsconfig.base.json (공통 규칙)
{ "compilerOptions": { "strict": true, "target": "ES2022" } }
// packages/api/tsconfig.json (자기만의 규칙만 추가)
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": { "module": "CommonJS" }
}

tsconfig.base.json이라는 이름 자체는 아무 의미가 없다. TypeScript가 특별 취급하는 파일명이 아니라, 그냥 extends로 연결된 "부모 설정 파일"일 뿐이다. 부모 파일 하나만 고치면 상속받는 자식들 전체에 반영된다 — 4개 패키지 전부 고칠 필요 없이.

단, include/exclude는 상속되지 않는다. 그래서 각 패키지가 자기 include는 따로 적어줘야 한다.

2. ESLint가 완전히 바뀌었다

예전에 봤던 .eslintrc.js 파일을 만들려고 했는데, 최신 ESLint(9+)에서는 아예 인식을 안 했다. 이유는 ESLint가 "flat config"라는 새 설정 방식으로 갈아탔기 때문. 이제는 eslint.config.mjs가 그 자리를 대신한다.

// eslint.config.mjs
import tseslint from "typescript-eslint";

export default tseslint.config(
  { ignores: ["**/dist/**", "**/node_modules/**"] },
  ...tseslint.configs.recommended,
);

하는 일은 똑같다("어떤 규칙을 어디에 적용할지"). 문법만 module.exports = {...} (객체 하나) 에서 export default [...] (설정 배열) 로 바뀐 것. 최신 ESLint를 쓴다면 .eslintrc.js를 만들어도 그냥 무시된다.

3. CLI가 프로젝트마다 설정을 "기억"하게 만들기

Claude Code 같은 CLI 도구를 쓸 때, 매번 새 세션을 열 때마다 MCP 서버 같은 걸 다시 등록하기 귀찮았다. 답은 프로젝트 폴더 안에 설정 파일을 두는 것이었다.

  • .mcp.json — 이 폴더에서 CLI를 열 때마다 자동으로 읽어서 MCP 서버를 연결해줌
  • CLAUDE.md — 마찬가지로 자동으로 읽어서 프로젝트별 지침을 적용해줌

세션이 끝나도 "날아가는" 게 아니라, 애초에 파일로 저장해두면 세션과 무관하게 항상 로드된다는 걸 알았다.

4. 코드 짜기 전에 문서부터 쓰면 뭐가 좋은가

이번에 직접 겪은 사례로 설명하는 게 제일 빠르다.

레시피 공유 서비스를 설계하면서 "백엔드는 SQLite(개발용)" 라고 정해뒀는데, 리스크 분석 문서를 쓰다가 이런 문제를 발견했다:

SQLite는 파일 하나짜리 DB다. 그런데 만약 이 Next.js 앱을 Vercel 같은 서버리스 환경에 배포하면? 서버리스는 함수가 실행될 때마다 파일시스템이 초기화되거나 인스턴스마다 분리돼 있다. 즉 SQLite에 쓴 데이터가 그냥 사라진다.

이걸 코드를 다 짜고 배포한 후에 알았다면? DB를 Postgres로 바꾸고, ORM 스키마 다시 만지고, 이미 짠 API 코드를 재검증해야 했을 것이다 — 최소 며칠 작업.

문서 단계에서 잡으니 고치는 비용은 표 한 줄이었다. 이게 "설계 문서를 먼저 쓰라"는 말의 진짜 의미였다. 같은 실수라도 발견하는 시점에 따라 고치는 비용이 완전히 달라진다.

5. 설계 문서도 순서가 있다

이번에 밟은 순서:

프로젝트 초기화 → 기능 명세 → 아키텍처 → WBS → 리스크 분석

이 순서가 임의가 아니라, 각 단계가 다음 단계의 재료가 되는 구조였다.

  • 기능 명세 없이 아키텍처부터 그리면 → 뭘 위한 구조인지 모른 채 설계
  • 아키텍처 없이 WBS를 짜면 → 무슨 작업을 해야 하는지가 불분명
  • WBS 없이 리스크 분석을 하면 → 뭘 분석해야 할지 기준이 없음

그리고 이 각 단계는 다 이름 붙은 정식 출처가 있었다.

단계 출처
기능 명세 (사용자 스토리) Mike Cohn, User Stories Applied
아키텍처 문서 Malte Ubl, "Design Docs at Google"
WBS / 리스크 분석 PMI PMBOK Guide
아키텍처 결정 기록(ADR) Michael Nygard, "Documenting Architecture Decisions" (2011)

Rust나 Kubernetes 같은 큰 오픈소스 프로젝트들도 "코드 PR 전에 설계 문서(RFC) 먼저 승인받기"를 공식 프로세스로 쓰고 있었다 (Rust RFC, Kubernetes KEP).

6. ADR을 "Y-statement"로 쓰는 법

ADR(아키텍처 결정 기록)을 쓸 때, 한 문장으로 결정을 요약하는 포맷이 있었다:

[상황]에서, [문제]에 직면하여, 우리는 [선택한 옵션]을 택하고 [대안]을 택하지 않기로 했다. 이를 통해 [얻으려는 것]을 달성하고자 하며, [감수할 단점]을 감수한다.

이 한 문장 안에 "왜 이렇게 정했는지"가 전부 들어간다. 나중에 "왜 MongoDB 안 쓰고 Postgres 썼어요?" 라는 질문에 코드가 아니라 이 문서 한 줄로 답할 수 있게 된다.

오늘 만든 것들

프로젝트 만든 문서
online-shopping README, ADR(DB 선택), WBS, 리스크 분석, ESLint/Prettier/husky/VS Code 설정
recipe-share 기능 명세, 아키텍처 다이어그램(Mermaid), 리스크 분석

다음에 할 일

  • recipe-share: 실제 Prisma 스키마 + JWT 인증 + API 라우트 구현 시작
  • online-shopping: WBS 기반으로 GitHub 이슈 자동 생성 스크립트 실행
728x90