감사 일시: 2026-01-20
감사 대상: Parksy Audio v1.0.0
감사자: Senior Software Architect / Security Auditor
| 항목 | 점수 | 등급 | ||
| **전체 평가** | 72/100 | B+ | ||
|---|---|---|---|---|
| 아키텍처 설계 | 78/100 | B+ | ||
| 코드 품질 | 70/100 | B | ||
| 보안 | 58/100 | C+ | ||
| 성능/확장성 | 75/100 | B+ | ||
| 프로덕션 준비도 | 68/100 | B- |
"MVP 수준의 기능 구현은 완료되었으나, 프로덕션 배포 전 보안 강화와 에러 처리 개선이 필요함"
┌─────────────────────────────────────────────────────────────┐
│ Parksy Audio System │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ GitHub │ │ Cloud Run │ │ Flutter │ │
│ │ Pages │ │ API │ │ APK │ │
│ │ (Static) │ │ (Python) │ │ (Android) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ User Interface │ │
│ │ Landing Page ─── Secret Office ─── Mobile App │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Local Engine │ │
│ │ Python CLI (WD Passport) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
| 항목 | 평가 | 설명 | ||
| **마이크로서비스 분리** | ✅ 양호 | API, Web, App이 독립적으로 배포 가능 | ||
|---|---|---|---|---|
| **서버리스 채택** | ✅ 양호 | Cloud Run으로 비용 효율적 운영 | ||
| **오프라인 지원** | ✅ 양호 | Local Engine으로 네트워크 없이 사용 가능 | ||
| **기술 스택 선정** | ✅ 양호 | 성숙한 기술 (Flutter, FastAPI, FFmpeg) |
| 항목 | 심각도 | 설명 | ||
| **단일 장애점** | 🟡 중간 | API 서버 다운 시 온라인 변환 불가 | ||
|---|---|---|---|---|
| **상태 관리 부재** | 🟡 중간 | 작업 큐/진행률 추적 없음 | ||
| **모니터링 부재** | 🟠 높음 | 로깅, 메트릭, 알람 시스템 없음 | ||
| **데이터베이스 없음** | 🟡 중간 | 사용 기록, 파일 메타데이터 저장 불가 |
장점:
문제점:
# 문제 1: 메모리 누수 위험 (Line 63-66)
with tempfile.NamedTemporaryFile(delete=False, suffix=ext) as tmp_input:
content = await file.read() # 대용량 파일 시 메모리 풀 사용
tmp_input.write(content)
# 개선: 스트리밍 방식으로 변경 필요
# 문제 2: MIDI 파일 정리 누락 (Line 100-103)
finally:
if 'input_path' in locals() and os.path.exists(input_path):
os.unlink(input_path)
# midi_path 정리 누락! 디스크 누적 가능
점수: 68/100
장점:
문제점:
// 문제 1: 하드코딩된 Mock 데이터 (Line 355)
void _pickFile() {
setState(() => _selectedFile = 'sample_audio.mp3'); // Mock!
// 실제 file_picker 연동 필요
}
// 문제 2: 실제 API 호출 없음 (Line 477-497)
Future _convertToMidi() async {
await Future.delayed(const Duration(seconds: 2)); // Fake!
// 실제 HTTP 클라이언트 연동 필요
}
점수: 65/100 (UI만 구현, 실제 기능 미연동)
장점:
문제점:
# 문제 1: 예외 처리 범위 과도함 (Line 352)
except: # Bare except - 안티패턴
click.echo('MIDI API: ❌ Offline/Unreachable')
# 개선: except Exception as e: 로 변경
# 문제 2: 타입 힌트 없음
def run_ffmpeg(cmd, description="Processing"): # -> Tuple[bool, Optional[str]]
점수: 75/100
점수: 72/100
| ID | 취약점 | 위치 | 위험도 | 설명 | ||||
| SEC-001 | **하드코딩 PIN** | office/index.html:283 | 🔴 심각 | PIN이 소스코드에 평문 노출 | ||||
|---|---|---|---|---|---|---|---|---|
| SEC-002 | **클라이언트 인증** | office/index.html:290 | 🔴 심각 | sessionStorage 우회 가능 | ||||
| SEC-003 | **CORS 와일드카드** | server/main.py:22 | 🟠 높음 | `allow_origins=["*"]` 설정 |
// docs/office/index.html Line 283
const CORRECT_PIN = '1126'; // 누구나 소스보기로 확인 가능!
공격 시나리오:
1. 브라우저에서 F12 → Sources 탭
2. index.html 확인
3. PIN 값 즉시 노출
권장 조치:
// 우회 방법 (브라우저 콘솔에서)
sessionStorage.setItem('parksy_auth', 'true');
location.reload();
// → PIN 입력 없이 패널 접근 가능
# server/app/main.py Line 20-26
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 모든 도메인 허용 → CSRF 위험
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
권장 설정:
allow_origins=["https://dtslib1979.github.io", "http://localhost:*"]
| ID | 취약점 | 위치 | 설명 | |||
| SEC-004 | 파일 크기 제한 없음 | server/main.py:64 | DoS 공격 가능 | |||
|---|---|---|---|---|---|---|
| SEC-005 | Rate Limiting 없음 | server/main.py | API 남용 가능 | |||
| SEC-006 | 에러 메시지 노출 | server/main.py:98 | 내부 정보 유출 |
| 테스트 | 파일 크기 | 응답 시간 | 결과 | |||
| 작은 파일 | 1 MB | ~30초 | ✅ Pass | |||
|---|---|---|---|---|---|---|
| 중간 파일 | 8.7 MB | ~130초 | ✅ Pass | |||
| 대용량 파일 | 50 MB | 추정 10분+ | ⚠️ Timeout 위험 |
1. TensorFlow 모델 로딩: 콜드 스타트 시 15-30초
2. basic-pitch 변환: CPU 바운드, 파일 길이에 비례
3. Cloud Run 메모리: 2GB 설정, 대용량 처리 한계
| 항목 | 현재 | 확장 시 | ||
| 동시 요청 | 1-5개 | Cloud Run 자동 스케일링 | ||
|---|---|---|---|---|
| 파일 저장 | 임시 디스크 | GCS 버킷 필요 | ||
| 처리 큐 | 없음 | Cloud Tasks 필요 |
점수: 75/100
| 항목 | 상태 | 비고 | ||
| ✅ HTTPS 적용 | 완료 | GitHub Pages, Cloud Run 자동 | ||
|---|---|---|---|---|
| ✅ 도메인 설정 | 완료 | dtslib1979.github.io | ||
| ✅ 기본 에러 처리 | 완료 | try-catch 구현 | ||
| ❌ 로깅 시스템 | 미완료 | Cloud Logging 연동 필요 | ||
| ❌ 모니터링 | 미완료 | Uptime Check 필요 | ||
| ❌ 백업 전략 | 미완료 | 상태 없는 서비스라 낮은 우선순위 |
| 항목 | 상태 | 비고 | ||
| ❌ CI/CD 파이프라인 | 부분 완료 | APK 빌드만 자동화 | ||
|---|---|---|---|---|
| ❌ 테스트 코드 | 미완료 | 단위/통합 테스트 없음 | ||
| ❌ API 문서화 | 미완료 | OpenAPI/Swagger 없음 | ||
| ✅ 사용자 매뉴얼 | 완료 | MANUAL.md |
점수: 68/100
| 우선순위 | 항목 | 예상 공수 | ||
| 1 | PIN 인증을 서버사이드로 이동 | 4시간 | ||
|---|---|---|---|---|
| 2 | CORS 화이트리스트 설정 | 30분 | ||
| 3 | 파일 크기 제한 추가 (50MB) | 1시간 | ||
| 4 | Rate Limiting 적용 | 2시간 |
| 우선순위 | 항목 | 예상 공수 | ||
| 5 | Flutter 앱 실제 API 연동 | 8시간 | ||
|---|---|---|---|---|
| 6 | 에러 메시지 사용자 친화적으로 | 2시간 | ||
| 7 | Cloud Logging 연동 | 2시간 | ||
| 8 | 임시 파일 정리 로직 보완 | 1시간 |
| 우선순위 | 항목 | 예상 공수 | ||
| 9 | 단위 테스트 작성 | 16시간 | ||
|---|---|---|---|---|
| 10 | Swagger/OpenAPI 문서화 | 4시간 | ||
| 11 | 작업 큐 시스템 도입 | 16시간 | ||
| 12 | 진행률 표시 기능 | 8시간 |
현재 상태: MVP/Demo 수준
프로덕션 배포: 보안 이슈 해결 후 가능
상용 서비스: 추가 개발 필요 (예상 2-4주)
| 카테고리 | 점수 | 비고 | ||
| 아키텍처 | 78/100 | 설계는 양호 | ||
|---|---|---|---|---|
| 코드 품질 | 70/100 | 리팩토링 필요 | ||
| 보안 | 58/100 | **즉시 개선 필요** | ||
| 성능 | 75/100 | 기본 사용 충분 | ||
| 프로덕션 | 68/100 | 추가 작업 필요 | ||
| **종합** | **72/100** | **B+** |
보고서 작성: Senior Software Architect
검토일: 2026-01-20
다음 검토: 보안 이슈 수정 후