# 코딩 표준 (Coding Standards)

**버전**: 1.0 (2026-03-14)

모든 코드 작성 전 필수 참조. 간결하고 실용적인 규칙만 포함.

---

## 1. 코드 스타일

### Python
- **포맷터**: black (line-length 88)
- **Import 정렬**: isort
- **들여쓰기**: 4칸 공백
- **따옴표**: 쌍따옴표(`"`) 우선, f-string 내에서는 단따옴표 허용

### TypeScript/JavaScript
- **포맷터**: prettier
- **들여쓰기**: 2칸 공백
- **세미콜론**: 필수

---

## 2. 타입 체크

### Python
- **pyright strict mode** 준수
- 모든 함수에 **타입 힌트 필수**
- `Any` 사용 최소화, 구체적 타입 명시

```python
# ✅ Good
def get_user(user_id: int) -> Optional[User]:
    ...

# ❌ Bad
def get_user(user_id):
    ...
```

### TypeScript
- **strict mode** 활성화
- 명시적 `any` 금지, unknown + 타입 가드 사용

---

## 3. 네이밍 규칙

| 대상 | Python | TypeScript/JS |
|------|--------|---------------|
| 변수/함수 | `snake_case` | `camelCase` |
| 클래스 | `PascalCase` | `PascalCase` |
| 상수 | `UPPER_SNAKE_CASE` | `UPPER_SNAKE_CASE` |
| 프라이빗 | `_leading_underscore` | `_leadingUnderscore` |
| 파일명 | `snake_case.py` | `kebab-case.ts` / `PascalCase.tsx` |

---

## 4. 함수/파일 크기

- **단일 함수**: 50줄 이내 권장
- **단일 파일**: 200줄 이내 권장
- 초과 시 모듈 분리 고려

---

## 5. 에러 처리

### 금지 사항
```python
# ❌ Bad - bare except
try:
    ...
except:
    pass

# ❌ Bad - 너무 광범위
except Exception:
    pass
```

### 권장 사항
```python
# ✅ Good - 구체적 예외 타입
try:
    result = risky_operation()
except FileNotFoundError as e:
    logger.error(f"File not found: {e}")
    raise
except ValueError as e:
    logger.warning(f"Invalid value: {e}")
    return default_value
```

---

## 6. 보안

### 하드코딩 키/토큰 금지
```python
# ❌ Bad
API_KEY = "sk-abc123..."

# ✅ Good - 환경변수에서 로드
API_KEY = os.environ.get("API_KEY")
if not API_KEY:
    raise EnvironmentError("API_KEY 환경변수가 설정되지 않았습니다.")
```

### 키 파일 위치
- `.env.keys`에서 로드 (프로젝트 루트)
- 절대 커밋하지 않음 (`.gitignore`에 포함)

---

## 7. 테스트

### 필수 규칙
- 새 기능은 **테스트 동반**
- 기존 테스트 **깨뜨리지 않을 것**
- `pytest` 사용

### 테스트 파일 명명
- `test_<모듈명>.py`
- 테스트 함수: `test_<기능명>`

### 테스트-코드 동시 수정 의무
- **프로덕션 코드 변경 시 관련 테스트 동시 수정 필수**
  - 함수 시그니처, 필드명, 반환 타입, 파일 경로가 변경되면 해당 인터페이스를 참조하는 테스트도 함께 수정
  - "코드는 바꿨는데 테스트는 안 바꿈" = QC 실패
- **하드코딩된 검증값 주의**
  - 조직 구성원 수, 팀 목록, 모델명 등 가변 데이터를 테스트에서 매직 넘버로 검증 금지
  - 대신: 소스 데이터에서 기대값을 동적으로 로드하거나, `>=` 등 범위 검증 사용
- **일회성 테스트 vs 회귀 테스트 구분**
  - `test_task_*.py` 형태의 태스크 완료 확인 테스트는 해당 태스크 종료 후 정리 또는 회귀 테스트로 전환
  - done 파일 경로 등 인프라 변경에 취약한 일회성 검증은 작성 금지

---

## 8. Import 순서

isort로 자동 관리. 수동 정렬 시:

1. **표준 라이브러리** (stdlib)
2. **외부 패키지** (third-party)
3. **내부 모듈** (local)

```python
# 1. stdlib
import os
from pathlib import Path
from typing import Dict, List, Optional

# 2. third-party
import requests
from fastapi import FastAPI

# 3. local
from myapp.models import User
from myapp.utils import helper
```

---

## 9. 문서화

### Docstring (Python)
```python
def calculate_premium(age: int, coverage: int) -> float:
    """보험료 계산.
    
    Args:
        age: 가입자 나이
        coverage: 보장 금액
        
    Returns:
        계산된 보험료
        
    Raises:
        ValueError: 나이가 0 이하인 경우
    """
    ...
```

### 주석
- **무엇(What)**이 아닌 **왜(Why)**를 설명
- 자명한 코드는 주석 불필요

---

## 11. 팀/조직 정보 동적 로드 (필수)

### 원칙
팀 ID, 팀원 이름, 봇 매핑 등 조직 구조 정보는 **반드시** `organization-structure.json`에서 동적으로 로드해야 합니다. 하드코딩 금지.

### Python
```python
# ❌ Bad - 하드코딩
TEAM_BOT = {"dev1-team": "dev1", "dev2-team": "dev2", ...}
VALID_TEAMS = ["dev1-team", "dev2-team", "dev3-team"]

# ✅ Good - 동적 로드
from utils.org_loader import build_team_bot_map, get_dev_team_ids
TEAM_BOT = build_team_bot_map()
VALID_TEAMS = get_dev_team_ids()
```

### Shell
```bash
# ❌ Bad - 하드코딩
case "$BOT_ID" in
  dev1) TEAM_ID="dev1-team" ;;
  dev2) TEAM_ID="dev2-team" ;;
  dev3) TEAM_ID="dev3-team" ;;
esac

# ✅ Good - 패턴 일반화
if [[ "$BOT_ID" == dev* ]]; then
    TEAM_ID="${BOT_ID}-team"
fi
```

### JavaScript
```javascript
// ❌ Bad - 하드코딩
const teamLabels = {'dev1-team': '1팀', 'dev2-team': '2팀'};

// ✅ Good - 서버 제공 설정 사용
const teamLabels = window.TEAM_CONFIG?.teamLabels || {};
```

### 공통 유틸리티
- Python: `utils/org_loader.py` — 모든 팀/봇 매핑 함수 제공
- 데이터 소스: `memory/organization-structure.json`
- 팀 추가/삭제 시 JSON만 수정하면 모든 코드에 자동 반영

---

## 10. 기타 규칙

### 매직 넘버 금지
```python
# ❌ Bad
if status == 3:
    ...

# ✅ Good
STATUS_COMPLETED = 3
if status == STATUS_COMPLETED:
    ...
```

### Early Return 패턴
```python
# ✅ Good
def process(data: Optional[Dict]) -> Result:
    if not data:
        return Result.empty()
    
    # 메인 로직
    ...
```

---

**빠른 참조 체크리스트**:
- [ ] black/isort 통과
- [ ] pyright 에러 0건
- [ ] 타입 힌트 완료
- [ ] 하드코딩 키 없음
- [ ] 테스트 작성
