# 에이전트 개발 규칙 이 문서는 AI 에이전트가 이 프로젝트에서 코드를 작성하고 기여할 때 따라야 하는 규칙과 가이드라인을 정의합니다. ## 목차 - [코드 작성 규칙](#코드-작성-규칙) - [커밋 규칙](#커밋-규칙) - [보안 및 개인정보](#보안-및-개인정보) - [파일 구조](#파일-구조) - [코드 스타일](#코드-스타일) - [문서화](#문서화) - [테스트](#테스트) --- ## 코드 작성 규칙 ### 파일 크기 제한 **모든 코드 파일은 450줄 내외로 작성되어야 합니다.** - **최대 줄 수**: 450줄 - **권장 줄 수**: 300-400줄 - **초과 시 조치**: 파일이 450줄을 초과하면 기능별로 분리하여 모듈화 - **자동 검사**: `src/**/*.ts`의 450줄 제한은 CI에서 자동으로 강제 - **예외**: 현재 `src` 아래 예외는 없으며 OpenAPI 생성 산출물은 저장소 루트에 위치 #### 파일 분리 예시 ```typescript // ❌ 나쁜 예: 하나의 파일에 모든 기능 (600줄) // src/products.ts (600줄) // ✅ 좋은 예: 기능별로 분리 // src/products/search.ts (200줄) // src/products/filter.ts (150줄) // src/products/formatter.ts (100줄) // src/products/index.ts (50줄) ``` ### 코드 품질 - **명확성**: 코드는 명확하고 이해하기 쉽게 작성 - **재사용성**: 중복 코드를 최소화하고 공통 로직은 함수로 추출 - **타입 안정성**: TypeScript의 타입 시스템을 적극 활용 - **에러 핸들링**: 모든 비동기 작업과 외부 API 호출에 적절한 에러 처리 구현 #### 예시 ```typescript // ✅ 좋은 예 async function fetchData(url: string): Promise { try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP 에러: ${response.status}`); } return await response.json(); } catch (error) { console.error('데이터 가져오기 실패:', error); throw error; } } // ❌ 나쁜 예 async function fetchData(url) { const response = await fetch(url); return await response.json(); } ``` --- ## 커밋 규칙 ### 커밋 빈도 - **주기적인 커밋**: 논리적인 작업 단위마다 커밋 - **작은 단위**: 한 번에 하나의 기능이나 수정사항만 포함 - **완성된 코드**: 빌드 실패나 런타임 에러가 없는 상태에서만 커밋 ### 커밋 메시지 컨벤션 **접두사는 영어, 메시지는 한국어를 사용합니다.** #### 커밋 메시지 형식 ``` <타입>: <제목> <본문> (선택사항) <푸터> (선택사항) ``` #### 타입 종류 - `feat:` - 새로운 기능 추가 - `fix:` - 버그 수정 - `docs:` - 문서 수정 - `style:` - 코드 포맷팅, 세미콜론 누락 등 (로직 변경 없음) - `refactor:` - 코드 리팩토링 (기능 변경 없음) - `test:` - 테스트 코드 추가/수정 - `chore:` - 빌드 설정, 패키지 매니저 설정 등 - `perf:` - 성능 개선 - `ci:` - CI/CD 설정 변경 - `revert:` - 커밋 되돌리기 #### 커밋 메시지 예시 ```bash # ✅ 좋은 예 feat: 제품 검색 필터링 기능 추가 fix: 매장 찾기 시 거리 계산 오류 수정 docs: API 사용 예시 문서 업데이트 refactor: 재고 확인 로직을 별도 모듈로 분리 test: 가격 정보 조회 API 테스트 추가 chore: TypeScript 버전 5.7.2로 업데이트 # ❌ 나쁜 예 feat: add feature (영어로만 작성) update (타입 접두사 누락) fix: bug fix (구체적이지 않음) 여러 기능 추가 (타입 접두사 누락) ``` #### 제목 작성 규칙 - **명확하고 구체적으로**: 무엇을 변경했는지 명확히 기술 - **50자 이내**: 제목은 간결하게 작성 - **명령형**: "~했음" 대신 "~함" 또는 "~추가" 형태 사용 - **마침표 없음**: 제목 끝에 마침표를 붙이지 않음 #### 본문 작성 규칙 (선택사항) ```bash feat: 제품 카테고리별 필터링 기능 추가 사용자가 제품을 카테고리별로 필터링할 수 있는 기능을 추가했습니다. 지원 카테고리: - 주방/생활 - 문구 - 완구/취미 - 화장품/미용 관련 이슈: #123 ``` ### 커밋 전 체크리스트 커밋하기 전에 다음 사항을 반드시 확인하세요: - [ ] 코드가 빌드되는가? - [ ] TypeScript 타입 에러가 없는가? - [ ] 개인정보나 민감한 정보가 포함되어 있지 않은가? - [ ] API 키, 비밀번호, 토큰 등이 포함되어 있지 않은가? - [ ] 테스트가 통과하는가? (테스트가 있는 경우) - [ ] `npm run test:coverage`가 100% (Statements/Branches/Functions/Lines)를 만족하는가? - [ ] 주석이 한국어로 작성되어 있는가? - [ ] 파일이 450줄을 초과하지 않는가? --- ## 보안 및 개인정보 ### 절대 커밋하지 말아야 할 항목 **다음 항목들은 절대 Git 저장소에 커밋되어서는 안 됩니다:** #### 1. 인증 정보 ```typescript // ❌ 절대 금지 const API_KEY = 'sk-1234567890abcdef'; const PASSWORD = 'mypassword123'; const DATABASE_URL = 'postgresql://user:pass@localhost/db'; ``` ```typescript // ✅ 올바른 방법: 환경 변수 사용 const API_KEY = process.env.API_KEY; const PASSWORD = process.env.PASSWORD; const DATABASE_URL = process.env.DATABASE_URL; ``` #### 2. 개인정보 - 실제 이메일 주소 - 전화번호 - 주소 - 신용카드 정보 - 주민등록번호 - 기타 식별 가능한 개인정보 #### 3. 민감한 설정 파일 ```bash # ❌ 커밋 금지 .env .env.local .env.production .env.development secrets.json credentials.json service-account-key.json # ✅ .gitignore에 추가되어 있는지 확인 ``` #### 4. 프라이빗 키 및 인증서 - SSH private keys (`id_rsa`, `id_ed25519` 등) - SSL/TLS 인증서의 private key - JWT secret keys - OAuth client secrets ### 환경 변수 사용 민감한 정보는 반드시 환경 변수로 관리하세요. #### .env.example 파일 제공 ```bash # .env.example (커밋 가능) API_KEY=your_api_key_here DATABASE_URL=your_database_url_here SECRET_KEY=your_secret_key_here ``` ```bash # .env (커밋 금지, .gitignore에 포함) API_KEY=sk-real-api-key-1234567890 DATABASE_URL=postgresql://user:pass@localhost/db SECRET_KEY=super-secret-key-xyz ``` ### Cloudflare Workers Secrets Cloudflare Workers 환경 변수는 다음과 같이 관리: ```bash # 로컬 개발용 (.dev.vars 파일 - .gitignore에 포함) API_KEY=test-key-for-local-dev # 프로덕션 배포용 (wrangler를 통해 설정) npx wrangler secret put API_KEY ``` ### 보안 체크리스트 커밋 전 다음 사항을 확인하세요: - [ ] `.env` 파일이 `.gitignore`에 포함되어 있는가? - [ ] 하드코딩된 API 키나 비밀번호가 없는가? - [ ] `console.log()`에 민감한 정보가 출력되지 않는가? - [ ] 주석에 실제 계정 정보가 포함되어 있지 않는가? - [ ] 테스트 데이터가 실제 개인정보를 포함하지 않는가? --- ## 파일 구조 ### 플러그인 기반 아키텍처 이 프로젝트는 **확장 가능한 플러그인 아키텍처**로 설계되었습니다. 다이소 외에 편의점, 백화점, 영화관 등 다양한 서비스를 쉽게 추가할 수 있습니다. #### 핵심 설계 원칙 1. **ServiceProvider 인터페이스**: 모든 서비스가 구현해야 하는 표준 계약 2. **ServiceRegistry**: 서비스를 동적으로 등록하고 MCP 서버에 연결 3. **도구 이름 네임스페이스**: 서비스별 접두사 사용 (예: `daiso_`, `cu_`, `cgv_`) ### 디렉토리 구조 ``` daiso-mcp/ ├── src/ │ ├── index.ts # MCP 서버 진입점 (450줄 이하) │ ├── core/ # 핵심 모듈 (확장 기반) │ │ ├── types.ts # 공통 타입 정의 │ │ ├── interfaces.ts # ServiceProvider 인터페이스 │ │ └── registry.ts # ServiceRegistry 클래스 │ ├── services/ # 서비스 프로바이더 │ │ └── daiso/ # 다이소 서비스 │ │ ├── index.ts # 서비스 팩토리 │ │ ├── types.ts # 다이소 전용 타입 │ │ ├── api.ts # API 엔드포인트 중앙화 │ │ └── tools/ # 도구 구현 │ │ ├── searchProducts.ts │ │ ├── findStores.ts │ │ ├── checkInventory.ts │ │ └── getPriceInfo.ts │ └── utils/ # 유틸리티 함수 ├── docs/ # 네트워크 파싱/리서치 문서 ├── examples/ # 사용 예시 ├── tests/ # 테스트 파일 └── scripts/ # 빌드/배포 스크립트 ### 네트워크 파싱 기록 `docs/` 폴더에는 API 엔드포인트 분석을 위한 네트워크 파싱 기록이 저장됩니다. 새로운 서비스를 추가할 때 네트워크 분석 결과를 이 폴더에 `{서비스명}-` 접두사를 붙여 저장하세요. ``` docs/ ├── daiso-network-analysis-result.md # 다이소 API 분석 결과 ├── daiso-playwright-network-analysis.md # Playwright 네트워크 분석 ├── daiso-replay-session-test.html # 세션 테스트 HTML └── daiso-test-replay.js # 테스트 리플레이 스크립트 ``` ### 문서 기록 `docs/` 폴더에는 네트워크 파싱 기록과 API 리서치 문서를 함께 저장합니다. 새로운 서비스를 추가할 때 관련 문서를 이 폴더에 `{서비스명}-` 접두사로 저장하세요. ``` docs/ ├── daiso-network-analysis-result.md # 다이소 API 분석 결과 ├── daiso-playwright-network-analysis.md # Playwright 네트워크 분석 ├── daiso-replay-session-test.html # 세션 테스트 HTML └── daiso-test-replay.js # 테스트 리플레이 스크립트 ``` ### 새 서비스 추가 방법 새로운 서비스(예: CU 편의점)를 추가하려면: #### 1. 서비스 디렉토리 생성 ``` src/services/cu/ ├── index.ts # CuService 클래스 및 팩토리 ├── types.ts # CU 전용 타입 정의 ├── api.ts # CU API 엔드포인트 └── tools/ # CU 도구 구현 ├── searchProducts.ts └── findStores.ts ```` #### 2. ServiceProvider 구현 ```typescript // src/services/cu/index.ts import type { ServiceProvider } from '../../core/interfaces.js'; import type { ToolRegistration } from '../../core/types.js'; class CuService implements ServiceProvider { readonly metadata = { id: 'cu', name: 'CU 편의점', version: '1.0.0', description: 'CU 편의점 제품 검색 및 매장 찾기', }; getTools(): ToolRegistration[] { return [ // cu_search_products, cu_find_stores 등 ]; } } export function createCuService(): ServiceProvider { return new CuService(); } ```` #### 3. 레지스트리에 등록 ```typescript // src/index.ts import { createDaisoService } from './services/daiso/index.js'; import { createCuService } from './services/cu/index.js'; registry.registerAll([createDaisoService, createCuService]); ``` ### 도구 이름 규칙 서비스별 도구는 접두사로 구분합니다: | 서비스 | 접두사 | 예시 | | :----- | :------- | :---------------------- | | 다이소 | `daiso_` | `daiso_search_products` | | CU | `cu_` | `cu_search_products` | | CGV | `cgv_` | `cgv_search_movies` | ### 파일 명명 규칙 - **camelCase**: 함수, 변수명 - **PascalCase**: 클래스, 인터페이스, 타입명 - **kebab-case**: 파일명 (선택사항) - **SCREAMING_SNAKE_CASE**: 상수 ```typescript // 함수, 변수 const userName = 'John'; function getUserData() {} // 클래스, 인터페이스, 타입 class UserService {} interface UserData {} type UserId = string; // 상수 const MAX_RETRY_COUNT = 3; const API_BASE_URL = 'https://api.example.com'; ``` --- ## 코드 스타일 ### 주석 작성 **모든 주석은 한국어로 작성합니다.** #### 파일 헤더 주석 ```typescript /** * 제품 검색 도구 * * 다이소 제품을 검색하고 필터링하는 기능을 제공합니다. * 검색 조건: 키워드, 카테고리, 가격 범위 * * @module tools/searchProducts */ ``` #### 함수 주석 ```typescript /** * 제품을 검색하고 필터링된 결과를 반환합니다. * * @param query - 검색 키워드 * @param category - 제품 카테고리 (선택사항) * @param maxPrice - 최대 가격 (선택사항) * @returns 검색 결과 객체 * @throws {Error} API 호출 실패 시 */ async function searchProducts( query: string, category?: string, maxPrice?: number, ): Promise { // 구현 } ``` #### 인라인 주석 ```typescript // 사용자 입력 검증 if (!query || query.trim().length === 0) { throw new Error('검색어를 입력해주세요'); } // TODO: 캐싱 기능 추가 필요 const results = await fetchFromAPI(query); // FIXME: 대소문자 구분 없이 검색하도록 수정 필요 const filtered = results.filter((item) => item.name.includes(query)); ``` ### TypeScript 타입 정의 ```typescript // 인터페이스 정의 interface Product { id: string; name: string; category: string; price: number; description?: string; // 선택적 속성 inStock: boolean; } // 타입 별칭 type ProductId = string; type ProductCategory = '주방/생활' | '문구' | '완구/취미' | '화장품/미용'; // 유니온 타입 type SearchFilter = { query: string; category?: ProductCategory; maxPrice?: number; }; ``` ### 코드 포맷팅 - **들여쓰기**: 2 spaces - **따옴표**: single quotes (`'`) 우선 - **세미콜론**: 사용 - **줄 길이**: 최대 100자 권장 ```typescript // ✅ 좋은 예 const message = '안녕하세요'; const user = { name: '홍길동', age: 30, }; // ❌ 나쁜 예 const message = '안녕하세요'; const user = { name: '홍길동', age: 30 }; ``` --- ## 문서화 ### README.md 프로젝트의 메인 문서로 다음 내용을 포함: - 프로젝트 개요 - 설치 방법 - 사용 방법 - API 문서 - 배포 가이드 ### API 문서 각 도구의 입력/출력 스키마를 명확히 문서화: ````typescript /** * 제품 검색 API * * 요청 예시: * ```json * { * "name": "search_products", * "arguments": { * "query": "수납박스", * "category": "주방/생활", * "maxPrice": 5000 * } * } * ``` * * 응답 예시: * ```json * { * "content": [{ * "type": "text", * "text": "{ ... }" * }] * } * ``` */ ```` ### CHANGELOG.md (권장) 주요 변경사항을 기록: ```markdown # Changelog ## [1.1.0] - 2025-03-01 ### Added - 제품 카테고리별 필터링 기능 추가 - 가격 히스토리 조회 기능 추가 ### Fixed - 매장 찾기 시 거리 계산 오류 수정 ### Changed - API 응답 형식 개선 ``` --- ## 테스트 ### 테스트 작성 원칙 - **모든 도구는 테스트를 작성**해야 합니다 - **엣지 케이스**를 고려한 테스트 작성 - **에러 처리** 로직도 테스트 - **코드 커버리지 100% 유지**: 커밋 전 `npm run test:coverage`를 실행해 100%를 유지해야 합니다 ### 테스트 파일 명명 ``` src/tools/searchProducts.ts tests/tools/searchProducts.test.ts ``` ### 테스트 예시 ```typescript import { searchProducts } from '../src/tools/searchProducts'; describe('제품 검색', () => { test('키워드로 제품을 검색할 수 있다', async () => { const result = await searchProducts({ query: '수납박스' }); expect(result.content).toBeDefined(); expect(result.content[0].type).toBe('text'); }); test('존재하지 않는 제품은 빈 결과를 반환한다', async () => { const result = await searchProducts({ query: '존재하지않는제품xyz' }); const data = JSON.parse(result.content[0].text); expect(data.count).toBe(0); }); test('빈 검색어는 에러를 발생시킨다', async () => { await expect(searchProducts({ query: '' })).rejects.toThrow(); }); }); ``` --- ## 체크리스트 요약 ### 코드 작성 전 - [ ] 어떤 기능을 구현할지 명확히 이해했는가? - [ ] 파일이 450줄을 초과하지 않도록 설계했는가? - [ ] 적절한 파일 구조와 모듈 분리를 계획했는가? ### 코드 작성 중 - [ ] TypeScript 타입을 명확히 정의했는가? - [ ] 주석을 한국어로 작성했는가? - [ ] 에러 핸들링을 적절히 구현했는가? - [ ] 보안에 취약한 코드가 없는가? ### 커밋 전 - [ ] 빌드가 성공하는가? - [ ] 타입 에러가 없는가? - [ ] 개인정보나 민감한 정보가 포함되지 않았는가? - [ ] 커밋 메시지가 컨벤션을 따르는가? - [ ] 파일이 450줄 이하인가? - [ ] `npm run test:coverage` 결과가 100% (Statements/Branches/Functions/Lines)인가? ### 커밋 메시지 작성 시 ```bash # 형식 확인 <타입>: <한국어 제목> # 예시 feat: 제품 검색 필터링 기능 추가 fix: 매장 거리 계산 오류 수정 docs: API 사용 가이드 업데이트 ``` --- ## 릴리스 절차 npm 패키지를 배포할 때는 게시된 버전과 Git 기록이 같은 커밋을 가리키도록 아래 순서를 지킵니다. ```bash npm version patch npm run release:dry-run git push origin main --follow-tags npm publish npm view daiso version ``` - `npm version`은 버전 변경 커밋과 `vX.Y.Z` 태그를 함께 만듭니다. - `npm run release:dry-run`으로 `dist`, `openapi.json`, `openapi.yaml` 포함 여부를 확인합니다. - `git push origin main --follow-tags`가 성공한 뒤 `npm publish`를 실행합니다. - 게시 후 `npm view daiso version`으로 registry 반영 버전을 확인합니다. --- ## 운영 메모 - 이 저장소의 기본 작업 브랜치는 `main`이며, open PR 또는 open issue가 있으면 먼저 triage합니다. - 사용자가 `정기체크합시다`라고 요청하면 별도 확인 질문 없이 아래 항목을 자동으로 조회하고 요약합니다. - 열린 PR과 각 PR의 CI/Coverage/CodeQL 상태 - 열린 issue - Dependabot security alerts - CodeQL code scanning alerts - Secret scanning alerts - 최신 `main` 기준 `npm audit` 결과 - 최신 `main` 커밋과 최근 GitHub Actions 상태(CI, Coverage, CodeQL, Deploy, Health Checks, External Smoke) - 로컬 작업트리 상태 - 모든 `src/**/*.ts` 파일은 450줄 이하를 유지합니다. - npm `daiso` 배포 전에는 `npm run check`, `npm run test:coverage`, `npm audit`를 확인합니다. - 외부 사이트 응답 회귀는 `.github/workflows/external-smoke.yml`의 서비스별 CLI/MCP matrix로 야간 및 수동 확인합니다. - Actions 목록에서 수정 전 커밋의 과거 실패가 보일 수 있으므로, 최신 `main` 커밋의 실행 결과를 기준으로 판단합니다. - 개인 작업 메모는 `/Users/hm/Documents/personal-agent/projects/daiso-mcp/PROJECT.md`에 둘 수 있지만, 여러 에이전트가 알아야 하는 규칙은 이 파일에 반영합니다. --- ## 참고 자료 - [Conventional Commits](https://www.conventionalcommits.org/) - [TypeScript Handbook](https://www.typescriptlang.org/docs/) - [Cloudflare Workers Documentation](https://developers.cloudflare.com/workers/) - [MCP Documentation](https://modelcontextprotocol.io/) --- **이 규칙을 따라 일관되고 안전하며 유지보수 가능한 코드를 작성해주세요.**