광고 차단 프로그램이 감지되었습니다

이 사이트는 광고 수익을 통해 무료로 콘텐츠와 서비스를 제공하고 있습니다.

더 나은 서비스를 위해 광고 차단 프로그램을 비활성화 해주세요.

광고 차단 해제 방법 보기
Loading...

라이센스 클라이언트 개발 기록

Ran tool

Ran tool

이전 다이어그램에서 오류가 발생하여 죄송합니다. Mermaid 구문, 특히 서브그래프 내에서 노드와 연결을 정의하는 방식에 실수가 있었던 것 같습니다. 이를 수정하고 제공된 코드와 구성을 기반으로 포괄적인 기술 보고서를 제공하겠습니다.

계획은 다음과 같습니다:

  1. Mermaid 다이어그램 수정: 더 나은 시각화를 위해 Mermaid 다이어그램 구문이 올바른지 확인하겠습니다.
  2. 기술 보고서 생성: 요청된 보고서의 각 섹션을 체계적으로 검토하고, 필요한 정보를 추출하기 위해 제공된 코드와 구성을 분석하겠습니다.

수정된 구문으로 다이어그램을 다시 생성해 보겠습니다.

Ran tool

다이어그램 구문 문제로 다시 한번 죄송합니다. Mermaid 구문이 매우 엄격하고, 특히 서브그래프 내에서 노드 정의와 연결이 잘못된 것으로 보입니다. 이번에는 더 단순화된 구조로 다이어그램을 생성하여 이 문제를 해결하고 보고서 작성에 집중하겠습니다.

라이센스 테스트 프로그램 기술 보고서

이 보고서는 제공된 라이센스 관리 시스템의 아키텍처, 주요 구성 요소, 데이터 흐름, 보안 구조, 운영 방법 및 배포 전략을 체계적으로 설명합니다.

1. 아키텍처 개요

이 시스템은 클라이언트 애플리케이션과 FastAPI 기반의 라이센스 서버로 구성된 분산 아키텍처입니다. 클라이언트는 PySide6로 구현된 GUI 애플리케이션이며, 서버는 MongoDB를 주요 데이터 저장소로 사용하고 Redis를 캐싱 및 동시 접속 제어에 활용합니다.

graph TD
    subgraph Client Application
        A[gui_license.py (Main UI)]
        B[client_system_info.py (System Info)]
        C[client_license_api.py (API Calls)]
        D[client_banner_loader.py (Banner Loading)]
        E[client_license_storage.py (License Key Storage)]
        F[consent_manager.py (User Consent)]

        A --> B
        A --> C
        A --> D
        A --> E
        A --> F
        C --> G[license_validator.py (SERVER_URL)]
        D --> G
    end

    subgraph License Server (FastAPI)
        H[server/main.py]
        I[MongoDB (License Data)]
        J[Redis (Active License Status)]
        K[Jinja2 Templates (Admin UI)]
        L[StaticFiles (Banners)]

        H -- Read/Write License Data --> I
        H -- Manage Active License TTL --> J
        H -- Renders Admin UI --> K
        H -- Serves Banner Images --> L
    end

    A -- HTTP POST /check (License Validation) --> H
    A -- HTTP GET /banners/{image} (Banner Fetch) --> H

주요 구성 요소 및 데이터 흐름:

  • 클라이언트 애플리케이션: 사용자 시스템에 설치되어 라이센스 키를 입력받고 검증 요청을 서버로 보냅니다. 시스템 정보(MAC 주소, IP 주소 등)를 수집하여 라이센스 바인딩에 활용하며, 서버로부터 배너 이미지를 로드하여 표시합니다. 라이센스 키는 로컬에 저장될 수 있으며, 주기적으로 라이센스 유효성을 재확인합니다.
  • 라이센스 서버 (FastAPI): 클라이언트의 라이센스 검증 요청을 처리하고, 관리자용 웹 인터페이스를 제공합니다.
  • MongoDB: 라이센스 키, 제품명, 사용자 이메일, 생성일, 만료일, 상태, 바인딩된 시스템 정보 등을 영구적으로 저장합니다.
  • Redis: 활성 라이센스 세션을 관리하여 동시 접속을 제어하고, 클라이언트의 Heartbeat을 통해 라이센스 활성 상태를 유지합니다.
  • 정적 파일 서비스server/images 디렉토리의 배너 이미지를 클라이언트에 제공합니다.
  • Jinja2 템플릿: 관리자 로그인 및 라이센스 관리 페이지를 렌더링합니다.

데이터 흐름은 클라이언트가 시스템 정보를 포함한 라이센스 키를 서버로 전송하여 유효성을 검증하고, 서버는 MongoDB와 Redis를 통해 라이센스 정보를 조회 및 업데이트하여 응답을 반환하는 방식입니다. 배너 이미지는 클라이언트가 서버의 정적 파일 엔드포인트에서 직접 가져옵니다.

2. 각 구성요소(서버/클라이언트/DB 등)별 역할·책임

2.1. 클라이언트 애플리케이션

  • gui_license.py (메인 UI):
  • 라이센스 키 입력 및 검증 버튼, 결과 표시 영역, 기능 버튼 등의 GUI를 제공합니다.
  • client_system_info.py를 통해 시스템 식별자를 가져옵니다.
  • client_license_api.py를 통해 라이센스 검증 요청을 서버로 보냅니다.
  • client_banner_loader.py를 통해 배너 이미지를 로드하고 표시합니다.
  • client_license_storage.py를 통해 라이센스 키를 로컬에 저장하고 불러옵니다.
  • consent_manager.py를 통해 사용자 동의를 확인합니다.
  • 라이센스 유효성 결과에 따라 UI(버튼 활성화/비활성화, 창 제목)를 업데이트하고, 만료 또는 오류 시 메시지를 표시합니다.
  • 주기적인 라이센스 재검증 타이머를 관리합니다.
  • client_system_info.py (시스템 정보):
  • 클라이언트의 MAC 주소, OS 정보, 공인 IP 주소 및 영구적인 클라이언트 ID를 수집합니다.
  • 수집된 정보를 Dict[str, str | None] 형태로 반환하여 라이센스 API에 전달될 수 있도록 합니다.
  • client_license_api.py (API 호출):
  • FastAPI 서버의 /check 엔드포인트로 라이센스 키와 시스템 식별자를 포함한 HTTP POST 요청을 보냅니다.
  • 서버 응답을 처리하고, 유효성 여부 및 오류 정보를 반환합니다.
  • 서버 연결 오류, HTTP 오류 등 네트워크 관련 예외를 처리합니다.
  • client_banner_loader.py (배너 로딩):
  • QNetworkAccessManager를 사용하여 비동기적으로 서버의 /banners/{image_name} 엔드포인트로부터 배너 이미지를 다운로드합니다.
  • 다운로드된 이미지를 QLabel에 표시하고, 실패 시 오류 메시지를 설정합니다.
  • client_license_storage.py (라이센스 키 저장):
  • 사용자 홈 디렉토리에 .license_key 파일을 생성하여 라이센스 키를 영구적으로 저장하고 불러옵니다.
  • 라이센스 키 파일 저장 및 불러오기 중 발생할 수 있는 IOError를 처리합니다.
  • consent_manager.py (사용자 동의):
  • 사용자의 MAC 주소, IP 주소 등 시스템 정보 수집에 대한 동의 여부를 확인합니다.
  • 동의하지 않을 경우 QMessageBox를 통해 경고하고 프로그램을 종료합니다.
  • .license_checker_consent_given 파일을 통해 동의 상태를 유지합니다.
  • license_validator.py (공통 설정):
  • 라이센스 서버의 기본 URL(SERVER_URL)을 정의하고, 클라이언트 모듈 전반에 걸쳐 공통 로깅 설정을 제공합니다.

2.2. 라이센스 서버 (FastAPI)

  • server/main.py:
  • FastAPI 애플리케이션의 메인 진입점입니다.
  • /check 엔드포인트:
  • 클라이언트로부터 받은 라이센스 키와 시스템 식별자(MAC 주소, IP 주소, 클라이언트 ID)를 기반으로 라이센스 유효성을 검증합니다.
  • MongoDB에서 라이센스 정보를 조회하고, 만료 여부를 확인하며, 필요에 따라 라이센스 상태를 expired로 업데이트합니다.
  • 하드웨어 바인딩 로직을 처리합니다:
  • 최초 바인딩 시 클라이언트 시스템 정보를 저장합니다.
  • 기존 바인딩된 MAC 주소와 요청 MAC 주소가 다를 경우, 바인딩 정보를 갱신합니다.
  • MAC 주소는 일치하나 IP 주소가 변경된 경우 IP 정보만 업데이트합니다.
  • Redis를 사용하여 동시 접속 제어를 구현합니다. 동일 클라이언트 ID로부터의 요청은 Heartbeat으로 간주하여 Redis TTL만 갱신하고, 다른 클라이언트 ID로부터의 요청은 라이센스 소유권 이전을 처리합니다.
  • cooldown_active 또는 in_use_by_other 상태일 경우 즉시 응답하여 클라이언트가 종료되도록 유도합니다.
  • /admin 엔드포인트 (관리자 UI):
  • 관리자 로그인(login.html) 및 라이센스 목록 조회/생성/삭제/수정(admin.htmledit_license.html) 기능을 제공하는 웹 인터페이스입니다.
  • Jinja2 템플릿을 사용하여 HTML 페이지를 렌더링합니다.
  • 세션 미들웨어를 통해 관리자 로그인 상태를 관리합니다.
  • 비밀번호 해싱(passlib.context.CryptContext)을 사용하여 관리자 비밀번호를 안전하게 관리합니다.
  • /banners 엔드포인트:
  • StaticFiles를 통해 server/images 디렉토리의 정적 이미지 파일을 제공합니다.
  • 데이터베이스 연결 관리startup_db_client 및 shutdown_db_client 이벤트를 통해 MongoDB 및 Redis 연결을 초기화하고 종료합니다.
  • 환경 변수 로드.env 파일에서 MONGODB_URLADMIN_USERNAMEADMIN_PASSWORD_HASHSESSION_SECRET_KEY 등의 환경 변수를 로드합니다.

2.3. 데이터베이스

  • MongoDB:
  • 주요 라이센스 정보를 영구적으로 저장하는 NoSQL 데이터베이스입니다.
  • 각 라이센스 문서는 license_keyproduct_nameuser_emailcreated_atexpires_atstatuslast_bound_mac_addressrebound_cooldown_untilbound_system_identifier 필드를 포함합니다.
  • 특히 bound_system_identifier는 클라이언트의 MAC 주소, IP 주소, OS 정보 등을 포함하여 라이센스 바인딩 정보를 저장합니다.
  • 날짜/시간 정보는 KST 시간대 정보가 있는 datetime 객체로 저장 및 처리됩니다.
  • Redis:
  • 메모리 내(In-memory) 데이터 저장소로, 주로 라이센스의 활성 세션 관리 및 동시 접속 제어에 사용됩니다.
  • LICENSE_ACTIVE_TTL_SECONDS (60초) 동안 라이센스 키를 키로, 클라이언트 ID 및 마지막 활동 시간을 값으로 하는 JSON 문자열을 저장합니다.
  • 클라이언트 Heartbeat 요청 시 해당 키의 TTL(Time-To-Live)을 갱신하여 라이센스 활성 상태를 연장합니다.
  • 다른 클라이언트가 동일 라이센스 키를 사용하려 할 때, Redis에 저장된 정보를 통해 "in_use_by_other" 상태를 감지하고 소유권 이전 로직을 수행합니다.

3. 보안 및 장애 대응 구조

3.1. 보안 구조

  • 관리자 인증:
  • 관리자 페이지(/admin)는 세션 기반 인증을 사용합니다.
  • 관리자 비밀번호는 passlib.context.CryptContext를 사용하여 bcrypt 방식으로 해싱되어 .env 파일에 저장되며, 로그인 시 입력된 비밀번호와 해싱된 비밀번호를 비교하여 검증합니다.
  • 세션 비밀 키(SESSION_SECRET_KEY)는 환경 변수로 관리되어 무작위로 생성됩니다.
  • 데이터 수집 동의:
  • 클라이언트 애플리케이션은 사용자에게 MAC 주소, IP 주소 등 시스템 정보 수집에 대한 명시적인 동의를 요청합니다. 동의하지 않으면 프로그램이 종료됩니다. 이는 개인정보 보호 규정 준수에 중요합니다.
  • 라이센스 키 보안:
  • 라이센스 키는 클라이언트 로컬 파일(.license_key)에 숨김 파일 형태로 저장됩니다. 이는 키의 유출 위험을 줄이지만, 완벽한 보안을 보장하지는 않습니다. (잠재적 취약점)
  • 클라이언트와 서버 간의 통신은 HTTPS를 통해 이루어질 것으로 가정됩니다 (SERVER_URL이 https://로 시작).
  • 하드웨어 바인딩:
  • 라이센스는 특정 클라이언트의 시스템 식별자(MAC 주소, 클라이언트 ID)에 바인딩되어 불법 복제를 방지합니다.
  • 다른 시스템에서 동일 라이센스 키 사용 시 경고 및 이전 클라이언트 종료 로직을 통해 보안을 강화합니다.
  • 환경 변수 사용: 민감한 정보(DB URL, 관리자 비밀번호 해시, 세션 비밀 키)는 환경 변수로 관리하여 코드에 직접 노출되는 것을 방지합니다.

3.2. 장애 대응 구조

  • 서버 연결 오류 처리:
  • 클라이언트(client_license_api.py)는 서버 연결 오류, HTTP 오류 등 requests 라이브러리에서 발생할 수 있는 예외를 처리하여 사용자에게 적절한 메시지를 표시합니다.
  • 서버 응답이 유효하지 않을 경우, 클라이언트 UI의 모든 기능 버튼을 비활성화하고 주기적인 재검증 타이머를 중단합니다.
  • 데이터베이스 연결 복원:
  • FastAPI 서버는 startup 및 shutdown 이벤트를 통해 MongoDB 및 Redis 연결을 관리합니다. Redis 연결 실패 시 app.redis_client를 None으로 설정하여 이후 로직에서 연결 여부를 확인할 수 있도록 합니다.
  • Redis 연결 실패 시, 라이센스 검증 로직은 "서버 오류: Redis 연결 실패"를 반환하여 장애 상황을 알립니다.
  • 로깅:
  • 클라이언트와 서버 모두 logging 모듈을 사용하여 애플리케이션의 동작, 오류, 경고를 기록합니다. 이는 문제 발생 시 원인 분석 및 디버깅에 필수적입니다.
  • 중요한 사용자 피드백이나 오류 메시지는 INFO 레벨로 유지하고, 일상적인 정보성 로그는 DEBUG 레벨로 조정하여 로그의 중요도를 구분합니다.
  • 클라이언트 비정상 종료 감지:
  • Redis의 TTL 메커니즘을 통해 클라이언트의 Heartbeat이 갱신되지 않으면 일정 시간 후 라이센스 세션이 만료된 것으로 간주할 수 있습니다. 이는 클라이언트의 비정상 종료 시에도 라이센스 상태를 적절히 관리하는 데 도움을 줍니다.

4. 주요 폴더 및 파일 구조

라이센스테스트/
├── build_exe.py             # PyInstaller를 사용하여 클라이언트 실행 파일 빌드 스크립트
├── client_banner_loader.py  # 클라이언트 배너 이미지 로드 로직
├── client_license_api.py    # 클라이언트 라이센스 서버 API 통신 로직
├── client_license_storage.py# 클라이언트 라이센스 키 로컬 저장/로드 로직
├── client_system_info.py    # 클라이언트 시스템 정보(MAC, IP 등) 수집 로직
├── consent_manager.py       # 클라이언트 사용자 동의 관리 로직
├── gui_license.py           # 클라이언트 GUI 애플리케이션 메인 파일 (PySide6)
├── license_validator.py     # 서버 URL 등 공통 상수 정의
├── requirements.txt         # Python 종속성 목록
├── server/
│   ├── main.py              # FastAPI 라이센스 서버 메인 파일
│   ├── images/              # (예상) 배너 이미지 저장 폴더 (StaticFiles용)
│   └── templates/           # (예상) Jinja2 템플릿 파일 저장 폴더 (admin.html, login.html 등)
├── test_gui_license.py      # gui_license.py 테스트 모드 실행 스크립트 (클라이언트 데이터 초기화)
└── 진행상황.md              # 프로젝트 진행 상황 문서

설명:

  • 루트 디렉토리: 클라이언트 관련 파이썬 모듈과 빌드 스크립트, 문서 파일 등을 포함합니다.
  • server/: FastAPI 서버 관련 파일들을 포함하는 전용 디렉토리입니다. 이는 클라이언트와 서버 코드를 명확하게 분리하여 관리하기 용이하게 합니다.
  • server/images/: 서버가 클라이언트에 제공하는 배너 이미지 파일들이 위치합니다. (현재는 test_banner.png가 client_banner_loader.py에 하드코딩되어 있지만, 이 폴더를 통해 제공됩니다.)
  • server/templates/: FastAPI 서버의 관리자 웹 인터페이스를 구성하는 Jinja2 템플릿 파일들이 위치합니다.

5. 배포 및 운영 정책

5.1. 실 배포 환경에서의 통신

  • 클라이언트-서버 통신: 클라이언트와 서버 간의 모든 통신은 HTTP/HTTPS 프로토콜을 사용합니다. license_validator.py에 정의된 SERVER_URL이 https://로 설정되어 있다면, TLS/SSL을 통한 암호화된 통신이 보장됩니다.
  • 방화벽 및 네트워크 설정: 서버는 클라이언트로부터의 인바운드 요청(기본적으로 80/443 포트)을 허용하도록 네트워크 및 방화벽이 구성되어야 합니다.
  • Redis 및 MongoDB 통신: FastAPI 서버는 내부적으로 MongoDB 및 Redis와 통신합니다. 이들 데이터베이스는 서버와 동일한 네트워크 환경 (예: Docker compose 내부 네트워크)에 배포되거나, 안전한 내부 네트워크를 통해 접근 가능해야 합니다.

5.2. 모니터링

  • 애플리케이션 로그 모니터링: 클라이언트와 서버에서 생성되는 로그(특히 ERRORWARNING 레벨)를 중앙 집중식 로깅 시스템(예: ELK Stack, Grafana Loki)으로 수집하여 실시간으로 모니터링해야 합니다.
  • 클라이언트 로그는 사용자 환경에서 발생하므로, 오류 발생 시 사용자로부터 로그를 수집할 수 있는 메커니즘(예: 오류 보고 기능)이 필요할 수 있습니다.
  • 서버 성능 모니터링: FastAPI 서버의 CPU 사용량, 메모리 사용량, 요청 처리 시간, 에러율 등을 모니터링하여 병목 현상이나 성능 저하를 조기에 감지해야 합니다. (Prometheus, Grafana 등 활용)
  • 데이터베이스 모니터링: MongoDB 및 Redis의 상태, 디스크 사용량, 연결 수, 쿼리 성능 등을 모니터링하여 데이터베이스의 안정성과 성능을 보장해야 합니다.
  • Redis 활성 라이센스 모니터링: Redis에 저장된 라이센스 활성 정보를 모니터링하여 특정 라이센스의 동시 접속 상태나 Heartbeat 갱신 여부를 확인할 수 있습니다.

5.3. 스케일링 방식

  • FastAPI 서버 스케일링:
  • 수평 스케일링: FastAPI는 비동기 프레임워크이므로, Gunicorn이나 Uvicorn과 같은 WSGI 서버와 함께 여러 개의 워커 프로세스를 사용하여 CPU 코어를 효율적으로 활용할 수 있습니다. 로드 밸런서를 통해 여러 FastAPI 인스턴스에 트래픽을 분산하여 처리량을 늘릴 수 있습니다.
  • 클러스터링: Docker Swarm, Kubernetes와 같은 컨테이너 오케스트레이션 도구를 사용하여 FastAPI 서버를 여러 노드에 배포하고 관리할 수 있습니다.
  • MongoDB 스케일링:
  • 복제본 세트 (Replica Sets): 데이터의 가용성과 읽기 작업의 확장성을 제공합니다. 여러 노드에 데이터를 복제하여 주 노드 장애 시에도 서비스 중단 없이 운영될 수 있도록 합니다.
  • 샤딩 (Sharding): 데이터베이스의 데이터 양이 매우 커질 경우, 데이터를 여러 샤드(Shard)에 분산하여 저장함으로써 쓰기 및 읽기 성능을 선형적으로 확장할 수 있습니다.
  • Redis 스케일링:
  • Redis Sentinel: Redis 인스턴스의 고가용성을 제공하여, 마스터 노드 장애 시 자동으로 슬레이브 노드를 마스터로 승격시킵니다.
  • Redis Cluster: 데이터를 여러 Redis 노드에 분산 저장하고 처리하여 성능과 용량을 확장합니다.

6. 확장성·유지보수 고려사항

6.1. 확장성 고려사항

  • 모듈화된 클라이언트 아키텍처: 클라이언트 코드는 client_system_info.pyclient_license_api.py 등 역할별로 모듈화되어 있어, 새로운 기능 추가나 기존 기능 변경 시 특정 모듈만 수정하면 되므로 확장성이 좋습니다. 예를 들어, 새로운 시스템 정보 수집 방식이 필요하면 client_system_info.py만 수정하면 됩니다.
  • RESTful API 디자인: 서버의 API 엔드포인트는 RESTful 원칙을 따르므로, 새로운 클라이언트 유형(예: 웹 클라이언트, 모바일 앱)을 추가하기 용이합니다.
  • 데이터베이스 추상화: 현재는 motor.motor_asyncio를 통해 MongoDB를 직접 사용하지만, 데이터베이스 추상화 계층을 도입하면 향후 다른 데이터베이스로의 전환이나 여러 데이터베이스 지원이 용이해질 수 있습니다.
  • 설정 파일 분리license_validator.py와 같이 공통 설정을 한 곳에 모아두면, 서버 URL 변경 등 전역적인 설정 변경이 필요할 때 한 파일만 수정하면 되므로 관리가 용이합니다.
  • 배너 시스템 확장: 현재는 BANNER_IMAGE_PATH가 클라이언트에 하드코딩되어 있지만, 서버에서 활성 배너 목록을 제공하는 API를 추가하고 클라이언트가 이를 동적으로 로드하도록 변경하면 배너 시스템을 훨씬 유연하게 확장할 수 있습니다.

6.2. 유지보수 고려사항

  • 코드 가독성 및 주석: Python Docstring과 주석을 통해 각 모듈, 클래스, 함수의 목적과 사용법을 명확히 설명하고 있습니다. 이는 새로운 개발자가 코드를 이해하고 유지보수하는 데 큰 도움이 됩니다.
  • 로깅: 상세한 로깅은 문제 진단 및 디버깅에 필수적입니다. DEBUG 레벨 로그를 일상적인 정보에 사용하고, INFO 레벨은 중요한 이벤트를 기록하는 방식으로 로깅 레벨을 적절히 사용하여 로그의 양을 관리하고 있습니다.
  • 환경 변수 사용: 민감한 정보와 배포 환경에 따라 달라지는 설정(DB URL, 비밀 키 등)을 환경 변수로 관리하여, 개발/테스트/운영 환경 간의 전환을 용이하게 하고 보안을 강화합니다.
  • 종속성 관리 (requirements.txt): 모든 Python 종속성이 requirements.txt에 명시되어 있어, 개발 환경 설정 및 배포 시 필요한 라이브러리를 일관되게 설치할 수 있습니다.
  • 테스트 스크립트 (test_gui_license.py): 클라이언트 GUI 애플리케이션의 초기 상태 테스트를 위한 스크립트가 제공되어, 개발 및 유지보수 과정에서 버그를 조기에 발견하고 회귀 테스트를 수행하는 데 도움을 줍니다.
  • 타임존 처리zoneinfo 및 dateutil.parser를 사용하여 날짜/시간 정보를 KST로 일관되게 처리하는 로직은 시간대 관련 버그를 방지하고 유지보수성을 높입니다.

이 시스템은 핵심 라이센스 관리 기능을 잘 구현하고 있으며, 모듈화된 설계와 적절한 기술 스택 선택을 통해 확장성과 유지보수성을 고려하고 있습니다. 그러나 실제 서비스 환경에 배포하기 전에 보안 취약점 점검 및 부하 테스트를 통한 성능 검증이 추가적으로 필요합니다.

목차
목차를 불러오는 중...

댓글

Loading...

댓글 로딩 중...

구글 검색