controller 포트폴리오 노트

메인 인덱스 | Shield4U | 오버뷰 | GitHub

이 문서는 제가 controller 레포를 조금 더 길게 설명하려고 남긴 심층 메모입니다. 빠르게 훑어보실 때는 오버뷰부터 보시고, 구현 판단이나 한계까지 보고 싶으실 때 이 문서로 내려오시면 됩니다.

왜 이 레포를 먼저 설명하는가

controller는 여러 서비스를 하나의 작업 흐름으로 엮는 방식을 설명할 수 있는 레포입니다. 상태 전이, GUID 추적, 콜백 기반 오케스트레이션이 한 문서 안에서 연결되어 있어 마이크로서비스 구조의 중심축으로 보기 쉽습니다.

제가 맡은 범위

  • Shield4U 팀장 역할
  • 전체 서비스 구조 설계
  • 서비스 간 통신 규약
  • 크롤링과 서비스 연결부
  • 오케스트레이션 중심 흐름 정리

이 레포에서 드러나는 점

  • 짧은 시간 안에도 전체 파이프라인 구조를 먼저 세우고 구현으로 내리는 방식
  • 여러 서비스를 느슨하게 연결하되 상태 추적은 분명하게 설계하는 방식
  • 보안 스캐닝, LLM, 크롤링을 하나의 흐름으로 묶는 오케스트레이션 관점

개요

이 문서는 제가 controller를 포트폴리오에서 설명할 때 쓰는 기준 정리입니다. Shield4U Controller는 웹 취약점 평가 시스템의 중앙 오케스트레이터로, 크롤링, LLM 분석, 취약점 스캐닝, 보고서 생성 전체 워크플로우를 관리하는 Flask + Flask-RESTX 기반 시스템입니다.

1. 기능 (Functions/Features)

1.1 다단계 웹 취약점 스캔 워크플로우 오케스트레이션

Controller는 5단계의 체계적인 보안 스캔 워크플로우를 제공합니다. 각 단계는 workflow_manager.pyPHASES 리스트로 정의되어 있으며, pendingcrawlinganalyzingscanningreportingcompleted 순으로 진행됩니다.

  • Crawling 단계 (_start_crawling_phase): Selenium WebDriver를 활용한 웹사이트 자동 크롤링을 수행하며, 설정 가능한 깊이(1-10)까지 링크를 따라가며 DOM 구조를 추출합니다. 중복 URL 방지를 위한 normalized_url 기반의 중복 체크 로직이 구현되어 있습니다.

  • Analysis 단계 (_start_analysis_phase): 크롤링 결과를 OpenAI GPT-4 기반 LLM 분석 서비스에 전달하여 잠재적 취약점을 탐지하고, Nuclei YAML 템플릿을 생성합니다. LLMAnalysis 모델을 통해 분석 결과와 신뢰도 필드(confidence_score)를 저장합니다.

  • Scanning 단계 (_start_scanning_phase): LLM이 생성한 Nuclei 템플릿을 기반으로 실제 취약점 스캐닝을 수행하며, ScanResult 모델에 심각도(severity: info/low/medium/high/critical)와 함께 저장합니다.

  • Reporting 단계 (_start_reporting_phase): 모든 스캔 결과를 종합하여 경영진용 요약 보고서(executive_summary)와 개발팀용 상세 취약점 보고서(detailed_findings)를 생성합니다.

1.2 GUID 기반 분산 스캔 추적 및 모니터링

모든 스캔 요청은 UUID4 기반의 고유 GUID(parent_guid)로 식별되며, 이를 통해 마이크로서비스 간의 비동기 통신에서도 스캔 세션을 일관되게 추적할 수 있습니다.

  • GUID 생성 및 검증: services/guid_manager.pyGUIDManager 클래스는 UUID4 패턴 검증 정규식(UUID4_PATTERN)을 사용하여 GUID 형식을 엄격하게 검증하며, normalize_guid() 메서드를 통해 다양한 형식의 GUID를 표준 형식으로 변환합니다.

  • 태스크 진행 상황 추적: TaskProgress 모델을 통해 각 서비스별 태스크 상태(pending/in_progress/completed/failed)를 실시간으로 추적하며, request_dataresult_data JSON 필드를 통해 태스크별 상세 데이터를 저장합니다.

  • 스캔 상태 조회 API: /api/v1/scans/{parent_guid}/status 엔드포인트를 통해 전체 태스크 통계(총 태스크 수, 완료/진행 중/실패 수)와 진행률(%)을 계산하여 반환합니다.

1.3 깊이 기반 재귀적 웹 크롤링 관리

단순한 단일 페이지 크롤링을 넘어, 설정된 max_depth만큼 링크를 따라가는 재귀적 크롤링을 지원합니다.

  • 남은 깊이 추적: CrawlResult 모델의 remaining_depth 필드를 통해 각 URL의 남은 크롤링 깊이를 추적하며, _find_remaining_urls_with_depth() 메서드에서 양수인 URL만 다음 크롤링 대상으로 선정합니다.

  • 중복 URL 방지: _normalize_url() 메서드를 통해 쿼리 파라미터와 프래그먼트를 제거한 정규화된 URL을 생성하며, CrawlResult 테이블의 unique_url_per_request 유니크 제약조건과 함께 동일한 URL의 중복 크롤링을 방지합니다.

  • 동일 도메인 필터링: urlparse를 활용하여 대상 도메인과 다른 외부 링크는 자동으로 필터링하여 불필요한 크롤링을 방지합니다.

1.4 Flask-RESTX 기반 RESTful API 및 자동 문서화

Flask-RESTX 확장을 활용하여 Swagger/OpenAPI 기반의 자동 API 문서화를 제공합니다.

  • 네임스페이스 구조: ns_scans(스캔 관리), ns_reports(보고서 관리), ns_system(시스템 모니터링) 3개의 논리적 네임스페이스로 API를 구성했습니다.

  • API 모델 정의: api_models.py에서 ScanRequest, ScanResponse, ScanStatus, ReportDetail 등의 요청/응답 모델을 정의하여 Swagger UI에서 타입 힌팅과 예시 값을 제공합니다.

  • 상세 API 문서: 각 엔드포인트에 @ns_scans.doc() 데코레이터를 통해 설명, 파라미터, 응답 코드 등을 상세히 문서화했으며, Swagger UI는 /api/docs/ 경로에서 접근 가능합니다.

  • 레거시 API 지원: 하위 호환성을 위해 /api/v1/scan과 같은 단수형 엔드포인트도 별도로 유지하며, deprecated=True 표시로 사용자에게 마이그레이션을 안내합니다.

1.5 마이크로서비스 간 통신 및 상태 관리

Controller는 4개의 외부 마이크로서비스(Crawler, Scanner, LLM Analysis, LLM Report)와 HTTP 기반 REST API로 통신합니다.

  • 서비스 엔드포인트 설정: config.py의 환경변수(CRAWLER_URL, SCANNER_URL 등)를 통해 서비스 위치를 외부에서 설정 가능하며, 기본값은 로컬호스트 포트(5001-5004)입니다.

  • 재시도 로직: _start_crawling_phase 메서드에서 최대 3회의 재시도 로직을 구현하여, DNS 해석 문제나 서비스 시작 지연으로 인한 일시적 오류를 복구합니다. 각 재시도 간 2초씩 증가하는 백오프 전략을 사용합니다.

  • 태스크 완료 콜백: 마이크로서비스는 작업 완료 시 /internal/task/complete 엔드포인트를 호출하여 상태를 보고하며, Controller는 이를 받아 워크플로우를 다음 단계로 진행시킵니다.

1.6 보안 검증 및 입력값 검증

utils/validators.py를 통해 다층적 입력값 검증을 수행합니다.

  • URL 검증: validate_url() 함수는 스킴(http/https), 호스트네임 형식, 포트 범위(1-65535)를 검증하며, _is_private_address()를 통해 사설 IP(10.x.x.x, 172.16-31.x.x, 192.168.x.x)와 localhost 접근을 차단하는 SSRF 방지 메커니즘을 제공합니다.

  • 쿠키 검증: validate_cookies() 함수는 JSON 객체 형태의 쿠키를 검증하며, 쿠키 이름은 _is_valid_cookie_name()으로 알파벳/숫자/하이픈/언더스코어만 허용합니다.

  • 크롤링 깊이 제한: validate_depth() 함수를 통해 1-10 범위의 정수만 허용하여 과도한 크롤링으로 인한 리소스 남용을 방지합니다.


2. 특징 (Characteristics)

2.1 워크플로우 매니저 패턴을 통한 명확한 오케스트레이션

WorkflowManager 클래스는 모든 스캔 단계의 조율을 담당하는 중앙 허브 역할을 합니다.

  • 상태 기계 패턴: 각 스캔 단계는 명확한 상태 전이를 가지며, _handle_crawling_completion, _handle_analysis_completion 등의 메서드가 단계 완료 시 다음 단계로의 전환을 관리합니다.

  • 비동기 태스크 처리: 마이크로서비스는 비동기적으로 작업을 수행하며, Controller는 태스크 상태를 폴링하지 않고 콜백 기반으로 완료 여부를 확인합니다. 이는 Controller의 리소스 사용을 최소화합니다.

  • 단계별 완료 조건 검사: 각 핸들러 메서드에서 해당 단계의 모든 태스크가 완료되었는지 확인(pending_crawl_tasks > 0 체크) 후 다음 단계로 진행하여, 불완전한 상태 전이를 방지합니다.

2.2 SQLAlchemy ORM을 활용한 견고한 데이터 모델링

6개의 핵심 엔티티(ScanRequest, TaskProgress, CrawlResult, LLMAnalysis, ScanResult, Report)를 통해 스캔 생애주기 전체를 관리합니다.

  • 관계형 매핑: ScanRequesttask_progress, crawl_results, reports와의 1:N 또는 1:1 관계를 가지며, cascade='all, delete-orphan' 설정으로 스캔 삭제 시 관련 데이터도 함께 정리됩니다.

  • 인덱스 최적화: parent_guid, status, service_name 등 조회 빈도가 높은 컬럼에 데이터베이스 인덱스를 설정하여 대용량 데이터에서도 빠른 조회가 가능합니다.

  • JSON 필드 활용: request_data, result_data, attack_vectors 등 구조가 유연한 데이터는 MySQL 5.7+의 JSON 타입을 활용하여 스키마 변경 없이 다양한 데이터를 저장할 수 있습니다.

  • 유니크 제약조건: CrawlResult(parent_guid, normalized_url) 유니크 제약조건을 통해 동일 스캔 내 중복 URL 저장을 데이터베이스 레벨에서 방지합니다.

2.3 깊이 기반 재귀 크롤링의 지능적 관리

기존의 단순 재귀 크롤링을 넘어, 남은 깊이(remaining_depth)를 추적하는 지능적인 크롤링 시스템을 구현했습니다.

  • 동적 깊이 감소: 각 크롤링 단계에서 remaining_depth - 1을 계산하여 하위 링크에 전달하며, _continue_crawling_with_remaining_depth 메서드에서 양수인 경우에만 추가 크롤링을 큐잉합니다.

  • 중복 방지 메커니즘: _find_remaining_urls_with_depth 메서드에서 이미 크롤링된 URL(existing_normalized_urls)과 진행 중인 태스크의 URL을 모두 추적하여, 동일한 URL이 여러 번 크롤링되는 것을 방지합니다.

  • 도메인 제한: base_domainparsed.netloc 비교를 통해 대상 도메인 외부로의 크롤링을 자동으로 차단하여 보안과 효율성을 동시에 확보합니다.

2.4 여러 에러 처리 및 복구 메커니즘

시스템의 안정성을 높이기 위해 다양한 계층에서 에러 처리를 구현했습니다.

  • 서비스 통신 재시도: _start_crawling_phase에서 3회 재시도 로직을 구현하여, 일시적인 네트워크 오류나 서비스 시작 지연 시 자동 복구합니다.

  • 태스크 실패 격리: _handle_task_failure 메서드는 개별 태스크 실패를 해당 태스크에만 국한시키고, 전체 워크플로우는 계속 진행 가능하도록 합니다. failed 상태의 태스크는 로깅되지만 다른 성공 태스크를 블록하지 않습니다.

  • 우아한 실패 처리: 크롤링 결과가 없는 경우에도(if not crawl_results) 빈 리스트로 분석 단계를 진행하여, 부분적 실패가 전체 스캔을 중단시키지 않습니다.

  • 데이터베이스 트랜잭션 관리: 각 워크플로우 단계에서 db.session.commit()을 적절히 호출하여, 데이터 일관성을 유지하면서도 장기 트랜잭션으로 인한 잠금 경합을 방지합니다.

2.5 환경 기반 설정 관리 및 배포 유연성

config.py는 4가지 환경(development, production, testing, default)을 지원하며, 환경변수 기반 설정으로 컨테이너 환경에 최적화되어 있습니다.

  • 12-Factor App 준수: 데이터베이스 연결 정보, 서비스 URL, 비밀키 등 모든 설정은 환경변수(os.environ.get)에서 읽어와 컨테이너 오케스트레이션(Kubernetes, Docker Compose)에 적합합니다.

  • 데이터베이스 연결 풀링: SQLALCHEMY_ENGINE_OPTIONS에서 pool_pre_ping=True, pool_recycle=300 등을 설정하여, 데이터베이스 연결 끊김을 자동 감지하고 재연결하며, 5분마다 연결을 재활용하여 리소스 누수를 방지합니다.

  • Dockerfile 포함: Python 3.11 slim 기반 이미지에 MySQL 클라이언트 라이브러리를 포함하여, 프로덕션 배포를 위한 완전한 컨테이너화를 지원합니다.

2.6 REST API와 Web UI의 이중 인터페이스

API 기반의 헤드리스 사용과 브라우저 기반 GUI 사용을 모두 지원합니다.

  • 템플릿 기반 웹 UI: Flask의 Jinja2 템플릿을 활용하여 / (대시보드), /progress/<guid> (진행 상황), /reports (보고서 목록), /report/<guid> (보고서 상세) 페이지를 제공합니다.

  • 정적 파일 서빙: static/cssstatic/js 디렉토리를 통해 프론트엔드 리소스를 제공하며, progress.html의 12,463바이트와 report_detail.html의 14,665바이트로 상당한 규모의 UI를 구현했습니다.

  • 하이브리드 오류 처리: /api/* 경로의 요청은 JSON 형태의 오류 응답을 반환하고, 웹 UI 요청은 HTML 템플릿을 반환하여 사용 맥락에 맞는 오류 표시가 가능합니다.


3. 부족한 점 (Limitations)

3.1 동기식 HTTP 통신으로 인한 서비스 의존성 및 성능 저하

현재 Controller는 모든 마이크로서비스와 동기식 HTTP 요청(requests.post)으로 통신하며, 이는 다음과 같은 문제를 야기합니다.

  • 블로킹 I/O: _start_analysis_phase에서 LLM 분석 서비스로 동기 요청을 보낼 때(timeout=120), 해당 서비스가 응답할 때까지 Controller 스레드는 블로킹됩니다. 동시에 여러 스캔을 처리할 때 스레드 풀이 고갈될 수 있습니다.

  • 장애 전파: 한 마이크로서비스의 응답 지연이나 장애가 Controller의 전체 성능에 영향을 미칩니다. 예를 들어 LLM 서비스가 느려지면 해당 스캔의 워크플로우 진행이 중단됩니다.

  • 비효율적인 리소스 사용: 태스크 상태를 폴링하지 않는 대신 콜백을 사용하지만, 워크플로우 진행 메서드들이 동기적으로 실행되어 병렬 처리가 제한적입니다.

코드 참고: workflow_manager.py:378-382requests.post(f"{Config.LLM_ANALYSIS_URL}/analyze", ...) 호출은 동기식입니다.

3.2 내장된 메시지 큐 부재 및 상태 관리 한계

요구사항에 Celery가 requirements.txt에 포함되어 있지만, 실제 코드에서는 사용되지 않고 있어 다음과 같은 한계가 있습니다.

  • 메모리 내 상태 관리: 모든 태스크 상태는 MySQL에 저장되지만, 태스크 스케줄링과 큐잉은 Controller의 로컬 메모리에서 관리됩니다. Controller 인스턴스가 여러 개일 경우 상태 불일치 문제가 발생할 수 있습니다.

  • 태스크 우선순위 부재: 현재는 FIFO(선입선출) 방식으로 태스크를 처리하며, 긴급 스캔이나 사용자 등급에 따른 우선순위 처리가 불가능합니다.

  • 지연 실행 미지원: 특정 시간에 스캔을 예약하거나 주기적 스캔을 설정하는 기능이 없습니다. APScheduler는 requirements.txt에 있지만 코드에서 활용되지 않습니다.

3.3 마이크로서비스 상태 모니터링 및 헬스체크 미흡

SystemHealth 엔드포인트의 TODO 주석에서도 확인할 수 있듯이, 외부 서비스의 실제 상태 확인이 구현되지 않았습니다.

  • 데이터베이스 연결만 확인: 현재 헬스체크는 MySQL 연결만 검증하며, Crawler(5001), Scanner(5002), LLM Analysis(5003), LLM Report(5004) 서비스의 실제 가용성은 “unknown” 상태로 반환됩니다.

  • 서비스 장애 감지 지연: 외부 서비스가 다운되어도 Controller는 태스크 실행 시점이 되어서야 오류를 감지하며, 사전 예방적 조치가 불가능합니다.

  • 서킷 브레이커 부재: 특정 서비스가 지속적으로 실패할 때 자동으로 해당 서비스를 차단하고 대체 로직을 실행하는 서킷 브레이커 패턴이 없어, 반복적인 실패로 리소스를 낭비합니다.

코드 참고: api_endpoints.py:417-430의 “TODO: Add health checks for other services” 주석.

3.4 단일 데이터베이스 의존성 및 확장성 제한

현재 아키텍처는 단일 MySQL 인스턴스에 강하게 의존하며, 이는 다음과 같은 확장성 문제를 야기합니다.

  • 읽기/쓰기 분리 부재: 모든 데이터베이스 작업이 단일 연결 풀을 통해 처리되며, 스캔 결과 조회(읽기)와 태스크 상태 업데이트(쓰기)가 동일한 데이터베이스에 집중됩니다.

  • 분산 트랜잭션 미지원: 여러 마이크로서비스가 참여하는 워크플로우에서, 각 서비스의 로컬 데이터베이스와의 분산 트랜잭션 관리가 없어 데이터 일관성 보장이 어렵습니다.

  • 지리적 분산 한계: 다중 리전 배포 시 데이터베이스 동기화 문제가 발생하며, 글로벌 서비스 확장에 제약이 있습니다.

3.5 보안 및 접근 제어 부재

현재 구현에서는 API 인증 및 권한 관리가 전혀 구현되지 않았습니다.

  • 인증 메커니즘 부재: JWT 비밀키는 config.py에 설정되어 있으나(JWT_SECRET_KEY, JWT_ACCESS_TOKEN_EXPIRES), 실제 API 엔드포인트에 인증 데코레이터가 적용되지 않았습니다.

  • 요청 출처 검증 부족: /internal/* 엔드포인트는 내부 서비스용이지만, IP 화이트리스트나 인증 토큰 검증 없이 누구나 접근 가능합니다.

  • Rate Limiting 미구현: Flask-Limiterrequirements.txt에 포함되어 있지만 실제 코드에서는 @limiter.limit() 데코레이터가 적용되지 않아, DDoS 공격에 취약할 수 있습니다.

3.6 테스트 및 모니터링 인프라 부족

코드베이스에서 테스트 코드와 모니터링 메트릭 수집이 전혀 보이지 않습니다.

  • 단위 테스트 부재: pytest나 unittest 기반의 테스트 파일이 없으며, _is_analysis_phase_complete 같은 복잡한 로직의 정확성을 검증하기 어렵습니다.

  • 메트릭 수집 부재: Prometheus나 StatsD 연동이 없어, 스캔 처리량, 워크플로우 단계별 소요 시간, 서비스 호출 성공률 등의 메트릭을 수집할 수 없습니다.

  • 로그 집계 한계: SystemLog 모델은 존재하지만, 중앙 집중식 로깅(ELK 스택 등)과 연동되지 않으며, 분산 환경에서 로그 추적이 어렵습니다.


4. 개선 방향 (Improvement Directions)

4.1 Celery + Redis 기반 비동기 작업 큐 도입

동기식 HTTP 통신의 한계를 극복하고 확장성을 확보하기 위해 Celery 기반의 비동기 작업 큐를 도입해야 합니다.

  • 비동기 태스크 실행: _start_analysis_phase의 LLM 분석 호출을 @celery.task 데코레이터가 적용된 비동기 함수로 변환하여, Controller는 태스크를 큐에 넣고 즉시 반환할 수 있습니다. 이를 통해 동시 처리량이 10배 이상 향상될 수 있습니다.

  • 분산 태스크 처리: 여러 개의 Worker 인스턴스가 Redis 브로커를 통해 태스크를 분산 처리하며, 부하에 따라 Worker 수를 동적으로 조정할 수 있습니다.

  • 결과 백엔드 활용: Celery의 결과 백엔드를 MySQL이나 Redis로 설정하여, 태스크 완료 상태를 효율적으로 조회할 수 있습니다.

  • 예상 효과: requirements.txt에 이미 celery==5.3.4redis==5.0.1이 포함되어 있으므로, 기존 인프라를 활용하여 적은 노력으로 큰 성능 향상을 얻을 수 있습니다.

4.2 메시지 브로커 기반 이벤트 아키텍처 전환

HTTP 콜백 기반 통신을 RabbitMQ나 Apache Kafka 기반의 이벤트 브로커로 전환하여 느슨한 결합을 달성합니다.

  • 이벤트 주도 아키텍처: TaskCompleted, ScanPhaseChanged 등의 도메인 이벤트를 정의하고, 각 마이크로서비스는 관심 있는 이벤트를 구독하여 처리합니다. Controller는 이벤트를 발행하기만 하면 되므로 서비스 간 의존성이 감소합니다.

  • 서비스 독립성 확보: 새로운 서비스를 추가하거나 기존 서비스를 변경할 때 Controller 코드를 수정할 필요가 없어져, 마이크로서비스 아키텍처의 장점을 극대화할 수 있습니다.

  • 이벤트 소싱 고려: 스캔 워크플로우의 모든 상태 변경을 이벤트 스트림으로 저장하면, 장애 발생 시 특정 시점으로 복구하거나 워크플로우 재생이 가능합니다.

  • 기술 선택: RabbitMQ는 복잡한 라우팅이 필요한 경우, Kafka는 높은 처리량과 이벤트 재생이 필요한 경우에 적합합니다.

4.3 서킷 브레이커 및 재시도 정책 고도화

외부 서비스 장애로부터 시스템을 보호하고 사용자 경험을 향상시키기 위해 resilience 패턴을 도입합니다.

  • 서킷 브레이커 구현: pybreaker 라이브러리를 사용하여, 특정 서비스의 실패율이 임계값(예: 50%)을 초과하면 해당 서비스를 자동으로 차단합니다. 차단 상태에서는 즉시 오류를 반환하거나 대체 로직을 실행하여 불필요한 리소스 낭비를 방지합니다.

  • 지수 백오프 재시도: 현재의 고정 대기 시간 재시도를 tenacity 라이브러리를 활용한 지수 백오프(2초, 4초, 8초…)로 변경하여, 일시적 문제로부터 효과적으로 복구하면서도 장기간 문제 발생 시 빠르게 실패합니다.

  • 타임아웃 정책 세분화: SERVICE_REQUEST_TIMEOUT(120초)를 서비스별로 세분화하여, LLM 서비스는 300초로 늘리고 Scanner는 60초로 줄이는 등 각 서비스의 특성에 맞게 조정합니다.

4.4 데이터베이스 확장 및 캐싱 레이어 도입

데이터베이스 병목을 해소하고 읽기 성능을 향상시키기 위해 다중 데이터 저장소 전략을 구현합니다.

  • 읽기 복제본 활용: MySQL의 읽기 복제본을 구성하고, ScanStatus 조회와 같은 읽기 작업은 복제본에서 수행하도록 SQLAlchemy 바인드를 분리합니다. 이를 통해 쓰기 부하를 줄이고 읽기 성능을 3-5배 향상시킬 수 있습니다.

  • Redis 캐싱: parent_guid 기반의 스캔 상태 조회는 Redis에 캐싱하여, 데이터베이스 부하를 줄이고 응답 시간을 50ms 이하로 단축합니다. TTL은 60초로 설정하여 데이터 일관성을 유지합니다.

  • 시계열 데이터 분리: SystemLog와 같은 시계열 데이터는 InfluxDB나 TimescaleDB로 마이그레이션하여, 대량의 로그 데이터 처리 성능을 향상시킵니다.

  • PostgreSQL 마이그레이션: 향후 PostgreSQL로 마이그레이션하여 JSONB 인덱싱, 배열 타입, CTE 등의 고급 기능을 활용하고, CrawlResult의 JSON 필드 쿼리 성능을 개선합니다.

4.5 인증 및 권한 관리 시스템 구현

API 보안을 강화하고 다중 테넌트 환경을 지원하기 위해 인증 체계를 구축합니다.

  • JWT 기반 인증: flask-jwt-extended를 활용하여 API 키 또는 OAuth2 기반의 JWT 인증을 구현합니다. 모든 /api/v1/* 엔드포인트에 @jwt_required() 데코레이터를 적용하고, 토큰 만료는 24시간으로 설정합니다.

  • 역할 기반 접근 제어(RBAC): admin, analyst, viewer 등의 역할을 정의하고, 각 역할별로 API 접근 권한을 제한합니다. 예를 들어 viewer는 GET 요청만 가능하고, 스캔 생성은 불가능하도록 합니다.

  • 내부 엔드포인트 보호: /internal/* 엔드포인트는 IP 화이트리스트(172.16.0.0/12 등)와 내부 인증 토큰을 검증하여 외부 접근을 차단합니다.

  • API Rate Limiting 활성화: Flask-Limiter를 실제로 적용하여, IP별 10회/분, 사용자별 100회/분 등의 제한을 설정하여 악의적 사용을 방지합니다.

4.6 모니터링 및 관찰 가능성(Observability) 인프라 구축

시스템의 건강 상태를 실시간으로 파악하고 문제를 신속히 진단하기 위해 관찰 가능성 인프라를 구축합니다.

  • Prometheus 메트릭 수집: prometheus-flask-exporter를 도입하여, HTTP 요청 지연 시간, 스캔 처리량, 워크플로우 단계별 소요 시간, 외부 서비스 호출 성공률 등의 메트릭을 수집합니다.

  • 분산 추적(Distributed Tracing): OpenTelemetry를 활용하여, 하나의 스캔 요청이 여러 마이크로서비스를 거치는 과정을 추적합니다. parent_guid를 trace ID로 활용하여 전체 요청 흐름을 시각화합니다.

  • 중앙 집중식 로깅: Python 로거를 ELK(Elasticsearch, Logstash, Kibana) 스택이나 Grafana Loki와 연동하여, 분산된 로그를 중앙에서 검색하고 분석할 수 있습니다.

  • 알림 시스템: Prometheus Alertmanager를 설정하여, 스캔 실패율 10% 초과, 데이터베이스 연결 풀 80% 사용, 외부 서비스 응답 시간 5초 초과 등의 조건 발생 시 Slack이나 이메일로 알림을 발송합니다.

4.7 테스트 자동화 및 CI/CD 파이프라인 구축

코드 품질을 보장하고 안전한 배포를 위해 테스트 및 자동화 인프라를 구축합니다.

  • 단위 테스트: pytestpytest-flask를 활용하여 WorkflowManager의 각 메서드와 validators.py의 함수를 테스트합니다. Mock을 사용하여 외부 서비스 의존성 없이 테스트할 수 있도록 합니다.

  • 통합 테스트: docker-compose 기반의 통합 테스트 환경을 구축하여, 실제 MySQL과 연동한 테스트를 수행합니다. 각 워크플로우 단계가 정상적으로 전환되는지 검증합니다.

  • 부하 테스트: locustk6를 사용하여, 동시 100개 스캔 요청 처리 시 성능과 안정성을 검증합니다.

  • CI/CD 파이프라인: GitHub Actions나 GitLab CI를 설정하여, PR 생성 시 자동 테스트 실행, main 브랜치 병합 시 Docker 이미지 빌드 및 레지스트리 푸시, 스테이징 환경 자동 배포를 구현합니다.


결론

Shield4U Controller는 Flask 기반의 견고한 마이크로서비스 오케스트레이터로, 5단계 워크플로우와 GUID 기반 추적, 깊이 기반 크롤링 등 핵심 기능을 효과적으로 구현했습니다. SQLAlchemy ORM과 체계적인 데이터 모델링, 환경 기반 설정 관리 등 프로덕션 환경에서 필요한 기반 요소들이 잘 갖추어져 있습니다.

그러나 동기식 HTTP 통신, 메시지 큐 미사용, 미흡한 서비스 모니터링, 테스트 부재 등 마이크로서비스 아키텍처의 성숙도를 높일 필요가 있는 영역들이 존재합니다. 제시된 개선 방향들을 단계적으로 적용한다면, 더 높은 확장성과 안정성, 관찰 가능성을 갖춘 엔터프라이즈급 취약점 스캐닝 플랫폼으로 발전할 수 있을 것입니다.

특히 이미 requirements.txt에 포함된 Celery, Redis, Flask-Limiter 등의 라이브러리를 활성화하고, Prometheus와 OpenTelemetry 기반의 관찰 가능성 인프라를 구축하는 것이 가장 높은 ROI를 가져올 것으로 예상됩니다.