feat: Flask 애플리케이션 모듈화 및 웹 대시보드 구현

- Flask Blueprint 아키텍처로 전환 (dashboard, upload, backup, status)
- app.py 681줄  95줄로 축소 (86% 감소)
- HTML 템플릿 모듈화 (base.html + 기능별 templates)
- CSS/JS 파일 분리 (common + 기능별 파일)
- 대시보드 기능 추가 (통계, 주간 예보, 방문객 추이)
- 파일 업로드 웹 인터페이스 구현
- 백업/복구 관리 UI 구현
- Docker 배포 환경 개선
- .gitignore 업데이트 (uploads, backups, cache 등)
This commit is contained in:
2025-12-26 17:31:37 +09:00
parent 9dab27529d
commit 7121f250bc
46 changed files with 6345 additions and 191 deletions

View File

@ -71,9 +71,21 @@ def prepare_bulk_data(df):
def process_bulk_upsert(bulk_data, session, table, batch_size=1000):
"""
데이터 일괄 삽입/업데이트
Args:
bulk_data: 삽입할 데이터 리스트
session: DB 세션
table: 대상 테이블
batch_size: 배치 크기
Returns:
int: 삽입된 행 수
"""
if not bulk_data:
logger.info("[INFO] 삽입할 데이터가 없습니다.")
return
return 0
total = len(bulk_data)
inserted_total = 0
@ -96,8 +108,11 @@ def process_bulk_upsert(bulk_data, session, table, batch_size=1000):
raise
logger.info(f"[DONE] 총 {total}건 처리 완료 (insert+update)")
return inserted_total
def file_reader(queue, files):
"""파일 읽기 스레드"""
for filepath in files:
try:
logger.info(f"[READ] {os.path.basename(filepath)} 읽기 시작")
@ -111,6 +126,7 @@ def file_reader(queue, files):
def db_writer(queue, session, table):
"""DB 쓰기 스레드"""
while True:
item = queue.get()
if item is None:
@ -126,6 +142,66 @@ def db_writer(queue, session, table):
logger.error(f"[FAIL] {os.path.basename(filepath)} DB 삽입 실패 - {e}")
def process_upsolution_file(filepath):
"""
UPSOLUTION 파일을 처리하고 DB에 저장
웹 업로드 인터페이스에서 사용하는 함수
Args:
filepath (str): 업로드된 파일 경로
Returns:
dict: {
'success': bool,
'message': str,
'rows_inserted': int
}
"""
try:
logger.info(f"[WEB] UPSOLUTION 파일 처리 시작: {filepath}")
# 파일 읽기
df = load_excel_data(filepath)
logger.info(f"[WEB] 데이터 읽기 완료: {len(df)}")
# 데이터 준비
bulk_data = prepare_bulk_data(df)
logger.info(f"[WEB] 데이터 준비 완료: {len(bulk_data)}")
# DB 세션 및 테이블 가져오기
session = db.get_session()
engine = db.get_engine()
metadata = MetaData()
table = Table(
db_schema.get_full_table_name("pos_ups_billdata"),
metadata,
autoload_with=engine
)
# 데이터 삽입
inserted = process_bulk_upsert(bulk_data, session, table)
session.close()
logger.info(f"[WEB] UPSOLUTION 파일 처리 완료: {inserted}행 삽입")
return {
'success': True,
'message': f'{inserted}행이 저장되었습니다.',
'rows_inserted': inserted
}
except Exception as e:
logger.error(f"[WEB] UPSOLUTION 파일 처리 오류: {e}", exc_info=True)
return {
'success': False,
'message': f'파일 처리 중 오류: {str(e)}',
'rows_inserted': 0
}
def main():
engine = db.get_engine()
session = db.get_session()
@ -142,7 +218,7 @@ def main():
logger.info(f"[INFO] 처리할 파일 {len(files)}")
queue = Queue(maxsize=2) # 2개 정도 여유 있게
queue = Queue(maxsize=3) # 2개 정도 여유 있게
reader_thread = threading.Thread(target=file_reader, args=(queue, files))
writer_thread = threading.Thread(target=db_writer, args=(queue, session, table))