Spazio 앱추가 템플릿

📝 노트 2025-12-28
수정

좋은 지적입니다! 현재 구조에서는 새 앱을 추가할 때마다 app.py를 수동으로 수정해야 하므로 관리자 모드의 의미가 없죠.

동적 블루프린트 로딩으로 해결하겠습니다:

import os
import json
import importlib
from flask import Flask, render_template, session, redirect, url_for, flash
from config import Config
from auth import auth_bp, login_required
from admin.admin import admin_bp, load_settings, scan_services, init_context_processor

from werkzeug.middleware.proxy_fix import ProxyFix

app = Flask(__name__)
app.config.from_object(Config)

# Nginx 프록시 설정
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1)

# 세션 보안 설정
app.config.update(
    SESSION_COOKIE_SECURE=False, 
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE='Lax',
    PERMANENT_SESSION_LIFETIME=86400
)

# 기본 Blueprint 등록
app.register_blueprint(auth_bp, url_prefix='/auth')
app.register_blueprint(admin_bp, url_prefix='/admin')

# ===== 동적 서비스 블루프린트 로딩 =====
def load_service_blueprints():
    """services 폴더의 모든 서비스를 자동으로 스캔하고 블루프린트 등록"""
    services_dir = os.path.join(os.path.dirname(__file__), 'services')

    if not os.path.exists(services_dir):
        print("⚠️ services 폴더가 없습니다.")
        return

    loaded_count = 0

    for item in os.listdir(services_dir):
        item_path = os.path.join(services_dir, item)

        # __pycache__, admin 등 제외
        if item.startswith('__') or item == 'admin' or not os.path.isdir(item_path):
            continue

        # 서비스 모듈 파일 확인
        module_file = os.path.join(item_path, f'{item}.py')
        if not os.path.exists(module_file):
            continue

        try:
            # 동적 import: services.calc.calc 형태로 모듈 로드
            module_path = f'services.{item}.{item}'
            module = importlib.import_module(module_path)

            # 블루프린트 찾기 (관례: {service_name}_bp)
            bp_name = f'{item}_bp'
            if hasattr(module, bp_name):
                blueprint = getattr(module, bp_name)
                app.register_blueprint(blueprint, url_prefix=f'/{item}')
                loaded_count += 1
                print(f"✅ 블루프린트 등록: {item} -> /{item}")
            else:
                print(f"⚠️ {item}에 '{bp_name}' 블루프린트가 없습니다.")

        except Exception as e:
            print(f"❌ {item} 로딩 실패: {e}")

    print(f"\n📦 총 {loaded_count}개의 서비스 블루프린트가 로드되었습니다.\n")

# 서비스 블루프린트 자동 로딩 실행
load_service_blueprints()

# Admin Context Processor 초기화
init_context_processor(app)

# 전역 컨텍스트 프로세서
@app.context_processor
def inject_global_data():
    """사용자 정보와 기타 전역 데이터 주입"""
    user_data = session.get('user')
    if user_data:
        user_email = user_data.get('email')
        user_data['is_admin'] = Config.is_admin(user_email)

    settings = load_settings()
    login_bg_url = settings.get('login_background_image', '')

    return dict(
        user=user_data,
        login_bg_image=login_bg_url
    )

@app.route('/')
@login_required
def index():
    return render_template('index.html')

# 에러 핸들러
@app.errorhandler(404)
def not_found(e):
    return render_template('404.html'), 404

@app.errorhandler(500)
def server_error(e):
    return render_template('500.html'), 500

if __name__ == '__main__':
    app.run(host=Config.APP_HOST, port=Config.APP_PORT, debug=(Config.FLASK_ENV == 'development'))

서비스 구조 규칙:

각 서비스는 다음 구조를 따라야 합니다:

services/
├── calc/
│   └── calc.py          # calc_bp 정의
├── converter/
│   └── converter.py     # converter_bp 정의
├── timer/
│   └── timer.py         # timer_bp 정의
└── textcount/
    └── textcount.py     # textcount_bp 정의

서비스 파일 예시 (services/calc/calc.py):

from flask import Blueprint, render_template

# 블루프린트 이름 규칙: {폴더명}_bp
calc_bp = Blueprint('calc', __name__, template_folder='templates')

UTIL_INFO = {
    'name': '계산기',
    'icon': '🔢'
}

@calc_bp.route('/')
def index():
    return render_template('calc.html')

이제 새 서비스를 추가하려면:

  1. services/newapp/ 폴더 생성
  2. services/newapp/newapp.py 파일 생성
  3. newapp_bp 블루프린트 정의
  4. UTIL_INFO 추가
  5. 서버 재시작 → 자동으로 로드됨!
  6. 관리자 페이지에서 활성화 (UI 노출 제어)

완전한 비활성화가 필요하다면:

def load_service_blueprints():
    """settings.json의 active_services만 로드"""
    settings = load_settings()
    active_services = settings.get('active_services', [])

    services_dir = os.path.join(os.path.dirname(__file__), 'services')

    for item in os.listdir(services_dir):
        if item not in active_services:  # active_services에 없으면 스킵
            continue

        # ... 나머지 로딩 로직
상세 정보
생성일: 2025-12-28
수정일: 2025-12-29
이 아이템이 링크하는 문서

링크된 문서가 없습니다.

이 아이템을 참조하는 문서

참조하는 문서가 없습니다.

태그
spazio
액션
수정
공유 & 관리
복제
목록으로 메인으로
마지막 수정: 2025-12-29 00:47
이미지 URL 수정됨