MODULE_PLAN.md•16.7 kB
# WhaTap MXQL CLI - 모듈 구성 및 구현 계획
## 📊 모듈 의존성 다이어그램
```
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ CLI App │ │ MCP Server │ │
│ │ (commands) │ │ (tools) │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
└─────────┼──────────────────────────────┼─────────────────────┘
│ │
├──────────────────────────────┤
│ │
┌─────────▼──────────────────────────────▼─────────────────────┐
│ Core Layer (공유) │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ MxqlExecutor │────────▶│ WhatapClient │ │
│ │ (쿼리 실행 헬퍼) │ │ (API 호출) │ │
│ └──────────────────┘ └────────┬─────────┘ │
│ │ │
│ ┌─────────▼──────────┐ │
│ │ AuthManager │ │
│ │ (인증 관리) │ │
│ └─────────┬──────────┘ │
│ │ │
│ ┌─────────▼──────────┐ │
│ │ SessionStore │ │
│ │ (세션 저장소) │ │
│ └────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
## 🏗️ 모듈 구성 상세
### Layer 1: 기반 모듈 (의존성 없음)
#### 1. **Types** (`src/core/types/index.ts`)
- 공통 타입 정의
- 인터페이스 선언
- **의존성**: 없음
- **사용처**: 모든 모듈
```typescript
// 예시
export interface LoginCredentials {
email: string;
password: string;
serviceUrl?: string;
}
export interface Session {
email: string;
accountId: number;
cookies: { wa: string; jsessionid: string };
apiToken?: string;
serviceUrl: string;
createdAt: Date;
expiresAt: Date;
}
```
#### 2. **SessionStore** (`src/core/auth/SessionStore.ts`)
- 세션 암호화 저장/로드
- 파일 시스템 I/O
- **의존성**: Types만
- **사용처**: AuthManager
**핵심 기능**:
- `save(session: Session)`: 세션 암호화 후 파일 저장
- `load(): Promise<Session | null>`: 파일에서 세션 복원
- `clear()`: 세션 파일 삭제
### Layer 2: 인증 모듈
#### 3. **AuthManager** (`src/core/auth/AuthManager.ts`)
- WhaTap 인증 로직 (3단계)
- 세션 관리
- **의존성**: SessionStore, Types
- **사용처**: WhatapClient, CLI, MCP
**핵심 기능**:
- `login(credentials)`: 로그인 수행 (CSRF → Web → Mobile)
- `loadSession()`: 저장된 세션 복원
- `logout()`: 로그아웃
- `isAuthenticated()`: 인증 상태 확인
- `getCookieHeader()`: 쿠키 헤더 문자열 반환
### Layer 3: API 모듈
#### 4. **WhatapClient** (`src/core/api/WhatapClient.ts`)
- WhaTap REST API 호출
- Axios 래퍼
- **의존성**: AuthManager, Types
- **사용처**: MxqlExecutor, CLI, MCP
**핵심 기능**:
- `executeMxql(request)`: MXQL 쿼리 실행
- `getProjects()`: 프로젝트 목록 조회
- `getProject(pcode)`: 프로젝트 상세 조회
#### 5. **MxqlExecutor** (`src/core/api/MxqlExecutor.ts`)
- MXQL 쿼리 실행 편의 기능
- 시간 범위 자동 설정
- **의존성**: WhatapClient, Types
- **사용처**: CLI, MCP
**핵심 기능**:
- `execute(options)`: MXQL 쿼리 실행 (편의 메서드)
- `executeFromFile(pcode, filePath)`: 파일에서 쿼리 읽어서 실행
### Layer 4: 애플리케이션 레이어
#### 6. **CLI Commands** (`src/cli/commands/*.ts`)
- 사용자 명령어 처리
- **의존성**: 모든 Core 모듈
#### 7. **MCP Tools** (`src/mcp/tools/*.ts`)
- MCP Tool 핸들러
- **의존성**: 모든 Core 모듈
---
## 🔢 구현 순서 및 테스트 전략
### Phase 1: 프로젝트 초기화
**목표**: 개발 환경 구축
```bash
whatap-mxql-cli/
├── package.json
├── tsconfig.json
├── jest.config.js
├── .gitignore
└── README.md
```
**작업**:
1. ✅ Node.js 프로젝트 초기화
2. ✅ TypeScript 설정
3. ✅ Jest 테스트 설정
4. ✅ 디렉토리 구조 생성
**검증**:
- `npm run build` 성공
- `npm test` 실행 가능
---
### Phase 2: Types 모듈
**파일**: `src/core/types/index.ts`
**구현 내용**:
- Session 인터페이스
- LoginCredentials 인터페이스
- MxqlRequest, MxqlResponse 인터페이스
- Project 인터페이스
**테스트**: 타입 정의만 있으므로 컴파일 테스트만 수행
---
### Phase 3: SessionStore 모듈
**파일**:
- `src/core/auth/SessionStore.ts`
- `test/core/auth/SessionStore.test.ts`
**구현 순서**:
1. 기본 구조 (생성자, 파일 경로)
2. 암호화 키 생성/로드
3. 암호화/복호화 메서드
4. save/load/clear 메서드
**테스트 시나리오**:
```typescript
describe('SessionStore', () => {
test('새 세션을 저장할 수 있다', async () => {
const store = new SessionStore();
const session = { /* ... */ };
await store.save(session);
// 파일이 생성되었는지 확인
});
test('저장된 세션을 로드할 수 있다', async () => {
const store = new SessionStore();
const session = { /* ... */ };
await store.save(session);
const loaded = await store.load();
expect(loaded).toEqual(session);
});
test('만료된 세션은 null을 반환한다', async () => {
const store = new SessionStore();
const expiredSession = {
/* ... */
expiresAt: new Date(Date.now() - 1000) // 과거
};
await store.save(expiredSession);
const loaded = await store.load();
expect(loaded).toBeNull();
});
test('세션을 삭제할 수 있다', async () => {
const store = new SessionStore();
await store.save({ /* ... */ });
await store.clear();
const loaded = await store.load();
expect(loaded).toBeNull();
});
test('암호화된 데이터는 평문이 아니다', async () => {
const store = new SessionStore();
const session = { email: 'test@example.com', /* ... */ };
await store.save(session);
const fs = require('fs/promises');
const encrypted = await fs.readFile(store.sessionFile, 'utf-8');
expect(encrypted).not.toContain('test@example.com');
});
});
```
**검증**:
- ✅ 모든 테스트 통과
- ✅ 코드 커버리지 90% 이상
---
### Phase 4: AuthManager 모듈
**파일**:
- `src/core/auth/AuthManager.ts`
- `test/core/auth/AuthManager.test.ts`
**구현 순서**:
1. 기본 구조 (생성자, SessionStore 주입)
2. `getCsrfToken()` - CSRF 토큰 획득
3. `webLogin()` - 웹 로그인
4. `getMobileToken()` - API 토큰 획득
5. `login()` - 전체 로그인 흐름
6. `loadSession()`, `logout()`, `isAuthenticated()`
**테스트 시나리오**:
```typescript
describe('AuthManager', () => {
// Mock SessionStore
// Mock HTTP 요청 (axios-mock-adapter 사용)
test('CSRF 토큰을 가져올 수 있다', async () => {
// Mock GET /account/login?lang=en
// HTML 파싱 테스트
});
test('웹 로그인을 수행할 수 있다', async () => {
// Mock POST /account/login
// 쿠키 추출 테스트
});
test('모바일 API 토큰을 가져올 수 있다', async () => {
// Mock POST /mobile/api/login
});
test('전체 로그인 흐름이 성공한다', async () => {
// 3단계 모두 Mock
const authManager = new AuthManager(mockSessionStore);
const session = await authManager.login({
email: 'test@example.com',
password: 'password123'
});
expect(session.accountId).toBeDefined();
expect(session.apiToken).toBeDefined();
});
test('잘못된 비밀번호로 로그인 실패', async () => {
// Mock 401 response
await expect(authManager.login({ /* ... */ }))
.rejects.toThrow();
});
test('저장된 세션을 복원할 수 있다', async () => {
// SessionStore.load() Mock
});
test('세션이 만료되면 인증되지 않음', async () => {
// 만료된 세션 Mock
await authManager.loadSession();
expect(authManager.isAuthenticated()).toBe(false);
});
});
```
**검증**:
- ✅ 모든 테스트 통과
- ✅ 실제 WhaTap API로 통합 테스트 (선택적)
- ✅ 코드 커버리지 85% 이상
---
### Phase 5: WhatapClient 모듈
**파일**:
- `src/core/api/WhatapClient.ts`
- `test/core/api/WhatapClient.test.ts`
**구현 순서**:
1. 기본 구조 (Axios 인스턴스 생성)
2. 쿠키 인터셉터 설정
3. `executeMxql()` - MXQL 쿼리 실행
4. `getProjects()` - 프로젝트 목록
5. `getProject()` - 프로젝트 상세
**테스트 시나리오**:
```typescript
describe('WhatapClient', () => {
// Mock AuthManager
// Mock Axios
test('MXQL 쿼리를 실행할 수 있다', async () => {
// Mock POST /yard/api/flush
const client = new WhatapClient(mockAuthManager);
const result = await client.executeMxql({
type: 'mxql',
pcode: 12345,
params: { mql: 'CATEGORY app_counter', /* ... */ },
path: 'text'
});
expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
});
test('요청 시 쿠키 헤더가 포함된다', async () => {
// Axios 인터셉터 검증
});
test('프로젝트 목록을 가져올 수 있다', async () => {
// Mock GET /v2/project/list
});
test('인증되지 않으면 에러를 던진다', async () => {
// AuthManager.isAuthenticated() = false
await expect(client.executeMxql({ /* ... */ }))
.rejects.toThrow('Not authenticated');
});
test('API 에러를 적절히 처리한다', async () => {
// Mock 500 response
});
});
```
**검증**:
- ✅ 모든 테스트 통과
- ✅ 실제 API 호출 통합 테스트 (선택적)
- ✅ 코드 커버리지 85% 이상
---
### Phase 6: MxqlExecutor 모듈
**파일**:
- `src/core/api/MxqlExecutor.ts`
- `test/core/api/MxqlExecutor.test.ts`
**구현 순서**:
1. 기본 구조 (WhatapClient 주입)
2. `execute()` - 시간 범위 자동 설정
3. `executeFromFile()` - 파일에서 쿼리 읽기
**테스트 시나리오**:
```typescript
describe('MxqlExecutor', () => {
// Mock WhatapClient
test('기본 시간 범위로 쿼리를 실행한다', async () => {
const executor = new MxqlExecutor(mockClient);
const result = await executor.execute({
pcode: 12345,
mql: 'CATEGORY app_counter'
// stime, etime 없음 → 자동 설정
});
expect(result.data).toBeDefined();
expect(result.executionTimeMs).toBeGreaterThan(0);
});
test('커스텀 시간 범위를 사용할 수 있다', async () => {
const result = await executor.execute({
pcode: 12345,
mql: 'CATEGORY app_counter',
stime: Date.now() - 60000,
etime: Date.now()
});
// ...
});
test('파일에서 쿼리를 읽어서 실행한다', async () => {
// Mock fs.readFile
const result = await executor.executeFromFile(12345, 'query.mql');
expect(result.data).toBeDefined();
});
test('실행 시간을 측정한다', async () => {
const result = await executor.execute({ /* ... */ });
expect(result.executionTimeMs).toBeGreaterThan(0);
});
});
```
**검증**:
- ✅ 모든 테스트 통과
- ✅ 코드 커버리지 90% 이상
---
## 📦 구현 단계별 체크리스트
### ✅ Phase 1: 프로젝트 초기화
- [ ] package.json 생성
- [ ] TypeScript 설정
- [ ] Jest 설정
- [ ] 디렉토리 구조 생성
- [ ] README.md 작성
### ⏳ Phase 2: Types
- [ ] 인터페이스 정의
- [ ] 타입 컴파일 확인
### ⏳ Phase 3: SessionStore
- [ ] SessionStore 클래스 구현
- [ ] 암호화/복호화 구현
- [ ] 테스트 작성 (5개)
- [ ] 테스트 통과 확인
- [ ] 코드 리뷰
### ⏳ Phase 4: AuthManager
- [ ] AuthManager 클래스 기본 구조
- [ ] getCsrfToken() 구현
- [ ] webLogin() 구현
- [ ] getMobileToken() 구현
- [ ] login() 전체 흐름 구현
- [ ] loadSession(), logout() 구현
- [ ] 테스트 작성 (8개)
- [ ] 테스트 통과 확인
- [ ] 실제 API 통합 테스트 (선택)
- [ ] 코드 리뷰
### ⏳ Phase 5: WhatapClient
- [ ] WhatapClient 클래스 기본 구조
- [ ] Axios 설정 및 인터셉터
- [ ] executeMxql() 구현
- [ ] getProjects() 구현
- [ ] getProject() 구현
- [ ] 테스트 작성 (6개)
- [ ] 테스트 통과 확인
- [ ] 실제 API 통합 테스트 (선택)
- [ ] 코드 리뷰
### ⏳ Phase 6: MxqlExecutor
- [ ] MxqlExecutor 클래스 구현
- [ ] execute() 구현
- [ ] executeFromFile() 구현
- [ ] 테스트 작성 (4개)
- [ ] 테스트 통과 확인
- [ ] 코드 리뷰
---
## 🧪 테스트 전략
### 단위 테스트
- **프레임워크**: Jest
- **Mock 라이브러리**: jest.mock, axios-mock-adapter
- **커버리지 목표**: 85% 이상
### 통합 테스트
- 실제 WhaTap API 호출 (선택적)
- 환경 변수로 테스트 계정 설정
### E2E 테스트
- CLI 구현 후 진행
- 실제 명령어 실행 테스트
---
## 📋 각 Phase 완료 기준
각 Phase는 다음 조건을 만족해야 완료:
1. ✅ 코드 구현 완료
2. ✅ 테스트 작성 완료
3. ✅ 모든 테스트 통과
4. ✅ 코드 커버리지 목표 달성
5. ✅ 타입 체크 통과 (`tsc --noEmit`)
6. ✅ Lint 통과 (eslint)
7. ✅ 다음 Phase로 진행 가능한 상태
---
## 🔄 작업 플로우
각 모듈당:
1. **설계 확인** (5분)
- 인터페이스 확인
- 의존성 확인
2. **구현** (30-60분)
- 코드 작성
- 주석 추가
3. **테스트 작성** (20-40분)
- 테스트 케이스 작성
- Mock 설정
4. **테스트 실행** (5분)
- `npm test`
- 커버리지 확인
5. **수정 및 재테스트** (필요시)
6. **코드 리뷰** (10분)
- 코드 품질 확인
- 문서화 확인
7. **다음 모듈로 진행**
---
## 예상 시간
| Phase | 예상 시간 |
|-------|----------|
| Phase 1: 프로젝트 초기화 | 30분 |
| Phase 2: Types | 15분 |
| Phase 3: SessionStore | 1.5시간 |
| Phase 4: AuthManager | 2-3시간 |
| Phase 5: WhatapClient | 1.5시간 |
| Phase 6: MxqlExecutor | 1시간 |
| **총합** | **7-8시간** |
---
## 다음 단계
현재: **Phase 1 시작 준비 완료**
질문:
1. 프로젝트 이름: `whatap-mxql-cli` 괜찮으신가요?
2. 테스트는 실제 WhaTap API를 호출할 수 있는 환경이 있나요?
- Yes → 통합 테스트 포함
- No → Mock만으로 진행
준비되셨으면 **Phase 1 (프로젝트 초기화)**부터 시작하겠습니다!