Skip to content

Cursor Rules Upgrade Plan

This content is not available in your language yet.

현재:
src/
├── routes/ # HTTP 라우트
├── services/ # 비즈니스 로직 (Cloudflare 의존성 포함)
├── schemas/ # Zod 스키마
└── lib/ # 유틸리티 (Cloudflare 코드 + 도메인 로직 혼재)
├── d1.ts # Cloudflare D1 코드
├── r2.ts # Cloudflare R2 코드
├── queue.ts # Cloudflare Queue 코드
├── domain.ts # 도메인 로직
└── liveness.ts # 도메인 로직

문제:

  • Cloudflare 코드와 도메인 로직이 혼재 (lib/ 디렉토리)
  • /utils, /helpers 같은 의미 없는 디렉토리 없지만, /lib이 비슷한 역할
  • 디렉토리가 “조직도”가 아님 (책임이 명확하지 않음)
  • 현재 파일들이 200-400줄을 넘는 경우 다수
  • 함수 크기 제한 없음
  • Worker entry point가 300줄 이상
현재 의존성:
routes → services → lib (Cloudflare 코드 포함)
Cloudflare API 직접 사용

문제:

  • Business Logic이 Edge를 알고 있음
  • 테스트 불가능한 구조
  • Cloudflare를 다른 인프라로 교체 불가능
현재:
- lib/queue.ts # 너무 일반적
- lib/d1.ts # 인프라 이름
- services/research.service.ts # 행동이 아닌 명사

문제:

  • 파일 이름이 행동을 나타내지 않음
  • 함수 이름이 일반적 (process(), handle())
// 현재 index.ts (300줄 이상)
export default {
fetch: app.fetch,
scheduled: async (event, env, ctx) => {
// 로직이 포함됨
},
queue: async (batch, env, ctx) => {
// 로직이 포함됨
}
};

문제:

  • Entry point에 로직이 포함됨
  • 얇지 않음

🎯 목표 구조 (실리콘밸리 표준)

섹션 제목: “🎯 목표 구조 (실리콘밸리 표준)”
src/
├── apps/ # Entry Points (Workers)
│ ├── api/ # HTTP Worker
│ │ └── index.ts # 얇은 진입점 (120 lines max)
│ ├── queue/ # Queue Consumer Workers
│ │ ├── research.ts # Research Queue Handler (150 lines max)
│ │ ├── contract.ts # Contract Queue Handler
│ │ └── liveness.ts # Liveness Queue Handler
│ └── cron/ # Cron Workers
│ └── metadata-sync.ts # Metadata Sync Cron (150 lines max)
├── domain/ # 순수 비즈니스 로직 (Cloudflare-free)
│ ├── research/ # Research 도메인
│ │ ├── discoverUrls.ts # URL 발견 로직 (200 lines max)
│ │ ├── validateDataset.ts
│ │ └── index.ts
│ ├── seed/ # Seed 도메인
│ │ ├── createSeedContract.ts
│ │ ├── validateSeed.ts
│ │ └── index.ts
│ ├── liveness/ # Liveness 도메인
│ │ ├── checkDomainHealth.ts
│ │ ├── calculateLivenessStatus.ts
│ │ └── index.ts
│ └── metadata/ # Metadata 도메인
│ ├── saveDatasetMetadata.ts
│ ├── queryMetadata.ts
│ └── index.ts
├── infra/ # 인프라 어댑터
│ └── cloudflare/ # Cloudflare 전용 코드
│ ├── d1/ # D1 어댑터
│ │ ├── saveDatasetMetadata.ts
│ │ ├── queryMetadata.ts
│ │ └── index.ts
│ ├── r2/ # R2 어댑터
│ │ ├── storeDataset.ts
│ │ ├── retrieveDataset.ts
│ │ └── index.ts
│ ├── kv/ # KV 어댑터
│ │ ├── getDomainCache.ts
│ │ ├── setDomainCache.ts
│ │ └── index.ts
│ ├── queue/ # Queue 어댑터
│ │ ├── enqueueResearchJob.ts
│ │ ├── enqueueContractJob.ts
│ │ └── index.ts
│ └── logpush/ # Logpush 어댑터
│ ├── logDeployment.ts
│ ├── logRequest.ts
│ └── index.ts
├── schema/ # Zod 스키마만 (계약 정의)
│ ├── research.ts # Research 스키마 (100 lines max)
│ ├── seed.ts # Seed 스키마
│ ├── queue.ts # Queue 메시지 스키마
│ └── index.ts
├── lib/ # Cross-domain 유틸리티
│ ├── crypto/ # 암호화
│ ├── hash/ # 해시
│ ├── time/ # 시간 유틸리티
│ └── errors/ # 에러 타입
└── db/ # Drizzle ORM (향후)
├── tables.ts # 테이블 정의
└── queries.ts # 쿼리 함수
.github/
└── scripts/ # CI/CD 스크립트
├── ci.sh
└── deploy.sh

대상최대 라인 수초과 시 조치
Worker entry point120 lines분해 필수
Queue consumer150 lines분해 필수
Domain module200 lines분해 필수
Infra adapter150 lines분해 필수
Zod schema100 lines분해 필수
Library/util200 lines분해 필수
Test file300 lines분해 권장

규칙:

  • 200줄 초과 = 설계 실패
  • 300줄 초과 = 즉시 분해
종류최대 라인 수초과 시 조치
일반 함수30 lines분해 필수
비즈니스 로직 함수50 lines분해 필수
핫패스 함수20 lines분해 필수

규칙:

  • 50줄 넘는 함수 = 두 개 이상의 책임
  • 화면에 한 번에 보여야 함
// ❌ 현재
lib/queue.ts
services/research.service.ts
// ✅ 개선
infra/cloudflare/queue/enqueueResearchJob.ts
domain/research/discoverUrls.ts
// ❌ 현재
process()
handle()
run()
// ✅ 개선
verifyInvoiceChecksum()
storeHarEvidence()
publishArticle()
enqueueSeedJob()
discoverUrlsFromSource()
validateDatasetIntegrity()

규칙:

  • 이름이 길어질수록 재사용 가능성 증가
  • 행동 중심 명명
  • 파일 이름 = 함수 이름과 일치
// ❌ 금지
/utils
/helpers
/common
// ✅ 권장
/domain/research # Research 조직
/domain/seed # Seed 조직
/infra/cloudflare/d1 # D1 인프라 조직

규칙:

  • 디렉토리 = 조직
  • 파일 = 직원
  • One folder = One responsibility
// ✅ 올바른 패턴
// apps/api/index.ts (120 lines max)
import { handleHttpRequest } from "./http-handler";
export default {
async fetch(req: Request, env: Bindings, ctx: ExecutionContext): Promise<Response> {
return handleHttpRequest(req, env, ctx);
},
async scheduled(event: ScheduledEvent, env: Bindings, ctx: ExecutionContext): Promise<void> {
return handleScheduledEvent(event, env, ctx);
},
async queue(batch: MessageBatch, env: Bindings, ctx: ExecutionContext): Promise<void> {
return handleQueueBatch(batch, env, ctx);
},
};
// ❌ 잘못된 패턴
export default {
async fetch(req, env, ctx) {
// 100줄의 로직...
}
};

6. Business Logic은 Edge를 몰라야 함

섹션 제목: “6. Business Logic은 Edge를 몰라야 함”
domain/research/discoverUrls.ts
// ✅ 올바른 패턴
export function discoverUrlsFromSource(
input: DiscoverUrlsInput // 순수 데이터
): DiscoverUrlsOutput {
// Cloudflare API 없음
// Request 없음
// env 없음
}
// ❌ 잘못된 패턴
export function discoverUrls(req: Request, env: Bindings) {
// Edge를 알고 있음
}

규칙:

  • Domain → Infra → Edge 순서
  • Domain은 순수 함수만
  • 테스트 가능해야 함
infra/cloudflare/d1/saveDatasetMetadata.ts
// ✅ 올바른 패턴
export async function saveDatasetMetadataToD1(
db: D1Database,
metadata: DatasetMetadata
): Promise<void> {
// D1 전용 코드
}
// domain/metadata/saveDatasetMetadata.ts
export function saveDatasetMetadata(
metadata: DatasetMetadata
): SaveMetadataCommand {
// Cloudflare 모름
return { type: "save", payload: metadata };
}

규칙:

  • /infra/cloudflare에만 Cloudflare API
  • Domain은 인프라를 모름
  • Adapter 패턴 사용
schema/queue/researchJob.ts
// ✅ 올바른 패턴
export const ResearchJobSchema = z.object({
jobId: z.string().uuid(),
source: z.string().url(),
country: z.enum(["SG", "GB", "US"]),
requestedAt: z.string().datetime(),
});
export type ResearchJob = z.infer<typeof ResearchJobSchema>;
// Producer와 Consumer가 같은 스키마 사용
import { ResearchJobSchema } from "../../schema/queue/researchJob";
// ❌ 잘못된 패턴
await queue.send({ type: "research", data: unknownData });

규칙:

  • Queue = Microservice boundary
  • Zod 스키마로 계약 정의
  • Producer/Consumer가 같은 스키마 공유
Allowed:
domain → infra
apps → domain
apps → infra
Forbidden:
domain → apps
domain → queue (직접)
infra → domain

규칙:

  • Domain은 최상위 레벨
  • Apps는 Domain과 Infra를 사용
  • Infra는 Domain을 모름
// ✅ 테스트 가능
export function discoverUrls(input: DiscoverUrlsInput): DiscoverUrlsOutput {
// new Request() 없음
// env.KV 없음
// ctx.waitUntil 없음
}
// ❌ 테스트 불가능
export async function discoverUrls(req: Request, env: Bindings) {
await env.KV.get("key");
ctx.waitUntil(...);
}

규칙:

  • 모든 함수는 순수 함수 또는 의존성 주입
  • Cloudflare API 직접 사용 금지
  • Domain 함수는 단위 테스트 가능해야 함

새 파일 생성 전 질문:

질문Yes/No
Queue Consumer에서 쓸 수 있는가?
Cron Job에서 쓸 수 있는가?
API Handler에서 쓸 수 있는가?
다른 Worker에서 쓸 수 있는가?

3개 이상 Yes여야 생성 가능

infra/cloudflare/logpush/logDeployment.ts
// ✅ 올바른 패턴
export function logDeployment(
version: string,
commit: string,
environment: string
): void {
console.log(JSON.stringify({
type: "deployment",
version,
commit,
environment,
timestamp: new Date().toISOString(),
}));
}
// 모든 Worker entry point에서 호출
logDeployment(BUILD_VERSION, GIT_COMMIT, CF_ENV);

규칙:

  • 모든 Worker에서 배포 정보 로깅
  • 구조화된 JSON 로그
  • Logpush → R2 → 법적 증거

  1. 새 디렉토리 생성

    src/
    ├── apps/ # 신규
    ├── domain/ # 신규
    ├── infra/ # 신규
    └── schema/ # 기존 schemas/ 이동
  2. 기존 코드 점진적 이동

    • lib/domain.tsdomain/research/normalizeDomain.ts
    • lib/d1.tsinfra/cloudflare/d1/
    • lib/r2.tsinfra/cloudflare/r2/
    • lib/queue.tsinfra/cloudflare/queue/
  3. Entry Point 얇게 만들기

    • src/index.tsapps/api/index.ts
    • 로직을 별도 파일로 분리
  1. 큰 파일 분해

    • 200줄 이상 파일 식별
    • 책임별로 분해
    • 함수 크기 50줄 이하로 제한
  2. 이름 개선

    • 일반적인 이름 → 행동 중심 이름
    • 파일 이름 = 주요 함수 이름
  1. Import 방향 수정

    • Domain → Infra 의존성 제거
    • Apps → Domain/Infra 의존성만 허용
  2. 테스트 가능성 개선

    • Cloudflare API 직접 사용 제거
    • 의존성 주입 패턴 적용

  • ✅ Queue Consumer 재사용 가능
  • ✅ Cron Job 재사용 가능
  • ✅ 다른 Worker에서 재사용 가능
  • ✅ 10년 후에도 리팩터링 가능
  • ✅ Cloudflare 교체 가능
  • ✅ 마이크로서비스화 가능
  • ✅ 단위 테스트 쉬움
  • ✅ 통합 테스트 쉬움
  • ✅ Mock 불필요
  • ✅ Fork-News, ShotCert, OCL 등 다른 사업에 재사용
  • ✅ 하나의 엔진으로 10개의 사업 가능

  • 한 번에 모든 것을 바꾸지 말 것
  • 기존 코드와 새 코드 공존 허용
  • 단계적으로 마이그레이션
  • 기존 API 엔드포인트 유지
  • 기존 Queue 메시지 형식 유지
  • 점진적 개선
  • 새로운 구조 문서화
  • 마이그레이션 가이드 제공
  • 팀 교육

  1. 파일 크기 규칙 (신규)
  2. 함수 크기 규칙 (신규)
  3. 디렉토리 구조 표준 (대폭 수정)
  4. 이름 규칙 강화 (수정)
  5. Entry Point 규칙 (신규)
  6. Business Logic 격리 규칙 (신규)
  7. Cloudflare 코드 격리 규칙 (신규)
  8. Queue 메시지 계약 규칙 (신규)
  9. Import 방향 규칙 (신규)
  10. 테스트 가능성 규칙 (신규)
  11. 재사용 가능성 체크리스트 (신규)
  12. Logpush 통합 규칙 (신규)
  1. Directory Structure - 완전히 재작성
  2. Coding Conventions - 파일/함수 크기 규칙 추가
  3. Architecture Layers - Domain/Infra 분리 명시
  4. Common Mistakes - 새로운 규칙 위반 사례 추가
  • 없음 (기존 내용은 유지하되 확장)

“Cloudflare + Queue + Logpush는 서버가 아니라 디지털 증거 공장”

이 규율을 따르면:

  • ✅ 10년 후에도 유지 가능
  • ✅ 여러 사업에 재사용 가능
  • ✅ 법적 증거 확보 가능
  • ✅ 마이크로서비스화 가능