SHEET 00 — COVER

사업화 개요

1. 해결하려는 업무문제

  • 5개 이상 해외 현장(Peaking Unit, Rumah, Nairyah, PP12, Turkistan)의 커미셔닝 인력 배치 현황이 프로젝트별 개별 엑셀로 관리되어, 전사 차원에서 "지금 KR/GE 인력이 어디에 몇 명, 언제 투입 예정인지"를 한눈에 파악하기 어려움
  • 담당자가 바뀌거나 파일이 이메일·공유폴더로 흩어지면서 어느 버전이 최신인지 혼선이 발생하기 쉬움
  • 조직도·마일스톤·인력 배치 현황을 보려면 매번 엑셀을 열고 수작업으로 보고자료를 다시 만들어야 함
  • 사내 보안 정책상 엑셀 파일 자체의 웹 업로드가 차단되어 있어, 일반적인 방식의 웹 대시보드 구축이 불가능했던 상황
  • 단기 출장(1개월 미만)과 장기 파견이 구분 없이 섞여 있어 인원수 집계에 혼선

2. 해결방안 / 업무 적용 시나리오

  • 기존에 쓰던 엑셀 양식을 그대로 유지 (사용자 워크플로우 변경 없음) — 담당자는 늘 하던 대로 엑셀만 채우면 됨
  • 그 엑셀을 대시보드에 업로드만 하면 조직도·월별 투입현황 차트·마일스톤 타임라인·KPI가 자동으로 갱신
  • 링크 하나로 사내 누구나 실시간 열람 가능 — PIN을 가진 담당자만 업데이트 권한
  • 적용 시나리오: 현장 담당자가 인력 변경사항을 반영한 엑셀을 업로드 → 그 즉시 서버에 반영 → 본사 담당자·경영진이 같은 링크에서 새로고침만 눌러 최신 배치 현황 확인 → 이메일로 파일을 주고받거나 별도 보고자료를 만들 필요 없음
  • 출장자와 파견 인력을 구조적으로 분리해서 관리, 주간/월간 단위로 "이번 주 현장에 갈 사람" 명단까지 바로 확인 가능
  • 필요 시 현재 시스템 데이터를 다시 엑셀로 추출해 오프라인 보고·타 시스템 연동에도 활용 가능

3. AI 활용방법

  • 요구사항을 대화로 설명하면 AI가 이를 구조화된 설계 명세서로 정리 → 승인 후 구현 → 검증하는 사이클을 반복하며 전 과정 진행
  • 기존에 존재하던 레거시 Excel 변환 스크립트(update_data.py)를 AI가 직접 분석해서, 처음부터 새로 만들지 않고 기존 로직을 최대한 재사용하며 확장
  • 사내 보안 정책으로 업로드가 막혔을 때, AI가 원인을 진단(웹 트래픽 정책이 xlsx 파일 자체를 차단)하고 브라우저 안에서 변환을 마친 뒤 JSON만 전송하는 우회 아키텍처를 직접 설계·구현
  • 신규 기능(출장자 관리, 대량 Excel 추출, 주간/월간 현황 패널) 요청 시, 실 서비스에 반영하기 전 격리된 복제 환경에서 실제 데이터로 자동 테스트(파일 파싱·왕복 검증·화면 렌더링)를 수행한 뒤에만 실제 폴더에 반영
  • 발생한 버그(팝업이 안 닫힘, 날짜가 하루씩 밀림, 실행 파일이 안 열림 등)를 AI가 직접 재현 스크립트를 만들어 근본 원인을 추적하고 수정
  • 이 설계 문서 자체도 매 단계마다 AI가 최신 상태로 정리해 축적

4. 기대효과 / 정량효과

  • 인력 현황 갱신 반영 시간: 기존 방식(파일 공유 → 수작업 취합) 대비 대폭 단축 — 업로드 후 수 초 내 전사 반영
  • 보고자료 재작성 공수 절감 — 별도 보고서 없이 대시보드 링크 공유로 대체
  • 데이터 버전 불일치 리스크 제거 — 단일 데이터(data.json) 기준으로 항상 최신 상태 하나만 존재
  • 확장성 — 신규 현장·프로젝트 추가 시 코드 수정 없이 엑셀 업로드만으로 새 탭 자동 생성, 추가 개발 비용 사실상 0
  • 정확도 향상 — 자동 검증 규칙(중복 ID, 필수 데이터 누락, 30일 이상 출장 등)으로 수작업 대비 입력 오류 조기 발견

※ 위 수치는 별도 실측(도입 전후 비교) 데이터가 아니라, 프로세스 변화의 방향성에 근거한 추정치입니다. 실 도입 후 정확한 수치 측정이 필요합니다.

5. 차별점 및 실무 적용성

  • 다른 사내 시스템 도입과 달리 IT 인프라 투자나 서버 신청 절차 없이 로컬 PC 한 대로 즉시 운영 가능
  • 보안 정책 위반이나 예외 승인 요청 없이, 아키텍처 설계만으로 제약을 우회 — 정책 준수와 실용성을 동시에 확보
  • 사용자가 기존에 쓰던 엑셀 양식을 그대로 사용 — 별도 교육이나 워크플로우 변경 불필요, 도입 저항 최소화
  • 조직도·마일스톤·인력현황을 동시에 아우르는 통합 대시보드는 개별 엑셀 관리 방식으로는 얻기 어려운 시각화
  • 이 패턴(기존 엑셀 워크플로우 + 브라우저 내 변환 + 실시간 대시보드)은 인력관리 외에 자재관리·품질검사 현황·공정 진도관리 등 유사한 현장 데이터를 다루는 다른 업무 영역에도 그대로 적용 가능한 재사용 가능한 구조

6. 구현 계획

기존 시스템(Excel 변환 스크립트, 화면 템플릿) 학습 및 설계 명세서 작성
Flask 기반 웹 업로드 기능 1차 구현
사내 보안 정책상 xlsx 업로드 차단 확인 — 아키텍처 재검토
Streamlit 전환 + 브라우저 내 Excel→JSON 변환 구조로 재설계
커스텀 컴포넌트로 업로드 완전 자동화 (파일 선택 한 번으로 끝)
실사용 중 발견된 버그(팝업/날짜/실행 진입점 등) 수정 및 검증
확장 기능 구현 — Excel 추출(다운로드), 출장자 관리, 주간/월간 현황 패널

다음 단계(조직도 화면 인력 추가 기능)는 SHEET 13 — 다음 작업 예정 참고.

SHEET 01 / 13

시스템 개요

Doosan Enerbility 현장 커미셔닝 인력 배치 현황을 보여주는 대시보드. 관리자가 프로젝트별 Excel(고정 양식)을 업로드하면 즉시 대시보드에 반영된다. 원래 Flask 백엔드로 시작했으나, 사내 보안 정책이 xlsx 파일의 웹 업로드 자체를 차단한다는 사실이 확인되어 최종적으로 아래 구조로 전환했다.

  • 백엔드를 Streamlit으로 전환 (Flask는 더 이상 사용하지 않음, 파일은 남겨둠)
  • Excel → JSON 변환을 서버가 아니라 사용자 브라우저 안에서 수행 — 네트워크로는 변환된 JSON만 나가므로 xlsx 업로드 차단 정책에 걸리지 않음
  • 반대로 JSON → Excel(추출/다운로드)은 서버(Python)에서 생성 — 다운로드 방향은 업로드 차단 정책과 무관하다고 보고 서버 생성 방식으로 설계
  • 사용자 경험은 "그냥 Excel 파일을 업로드/다운로드하는 것"과 동일하게 유지
SHEET 02 / 13

아키텍처

[사용자 브라우저] │ GET / (호스팅 링크 = 대시보드 그 자체) ▼ [Streamlit 서버: streamlit_app.py] │ templates/index.html + 현재 data.json을 합쳐 인라인 데이터 HTML을 만들어 st.iframe으로 표시 ▼ [대시보드 화면] ── "새로고침" "Excel 추출" "Excel 업로드" (전부 Streamlit 네이티브 버튼) │ │ │ │ st.rerun() │ st.dialog │ st.dialog │ ▼ ▼ │ [프로젝트 다중선택 팝업] [PIN + 파일선택 팝업] │ st.pills(selection=multi) components/excel_uploader (브라우저 내 SheetJS 변환) │ │ │ │ build_excel_from_project() process_upload_json() │ (프로젝트별 xlsx, 여러개면 zip) (PIN검증→아카이브→저장→재생성) │ ▼ ▼ │ st.download_button data.json 갱신 └──────────────────────────────────────┘ ▼ 다른 사용자는 "새로고침"으로 최신 데이터 확인

구성 요소

파일역할
run.bat더블클릭 실행 진입점
streamlit_app.py대시보드 렌더링, 업로드/추출 팝업, PIN 검증, 아카이브/재생성, xlsx 생성
components/excel_uploader/index.html브라우저 내 Excel→JSON 변환 (SheetJS) — 커스텀 컴포넌트 통신 프로토콜 직접 구현
update_data.py파싱/검증 로직(parse_excel, validate_project 등) — 재사용, 출장자 파싱 추가됨
templates/index.html대시보드 화면 — 기존 내용은 그대로, 출장자 테이블·주간/월간 패널만 추가
SHEET 03 / 13

폴더 구조

manpower-board-release/ ├── run.bat 더블클릭 실행 진입점 ├── run.py (비권장 — .py 연결 불안정) ├── streamlit_app.py Streamlit 앱 본체 (업로드+추출 다이얼로그 포함) ├── app.py (Flask, 더 이상 사용 안 함) ├── update_data.py 파싱/검증 로직 (출장자 파싱 포함) ├── config.json 관리자 PIN ├── .streamlit/config.toml 호스트/포트 설정 ├── requirements.txt Streamlit, openpyxl ├── components/ │ └── excel_uploader/ │ └── index.html 브라우저 내 xlsx→JSON 변환 (출장자 포함) ├── templates/ │ └── index.html 대시보드 — 출장자 테이블·주간월간 패널 추가 ├── data.json 최신 반영 데이터 (자동 생성) ├── validation_report.json 검증 리포트 (출장 30일 경고 포함) ├── data_supplement.json Excel에 없는 프로젝트 보충용 └── uploads/ ├── manpower_*.xlsx 초기 원본 (아직 웹으로 갱신 안 된 프로젝트) ├── manpower_*.json 웹으로 갱신된 프로젝트 └── _archive/ 교체되어 밀려난 이전 버전 보관
SHEET 04 / 13

엔드투엔드 데이터 흐름 (Excel 업로드)

대시보드 링크 접속 → "Excel 업로드" 클릭 → 같은 화면 팝업(PIN + 파일 선택)
xlsx 선택 즉시 브라우저 안에서 JSON으로 자동 변환 (05_BUSINESS_TRIP 시트 포함)
PIN 확인 후 "업로드" → 변환된 JSON만 서버로 전송 (xlsx 원본은 네트워크 미전송)
서버: PIN 검증 → 같은 프로젝트 기존 파일을 _archive/로 이동 → 새 JSON 저장
uploads/ 전체(xlsx+json) 재스캔 → data.json / validation_report.json 재생성 (출장 30일 이상 경고 포함)
성공 메시지 + "확인" → 팝업 닫힘 + 대시보드 자동 새로고침
다른 사용자는 각자 "새로고침"으로 최신 데이터 확인
SHEET 05 / 13

데이터 스키마

project: {
  key, id, name, active, display_order, config, gm, pcod,
  stats: { total, named, tbd },
  manpower: [ manpower record, ... ],
  milestones: [ milestone record, ... ],
  business_trips: [ business trip record, ... ]   // 신규
}
manpower record: {
  id, pid, part, role, name, nat, ps, pe, as_, ae, note, count_yn
}
milestone record: {
  id, phase, name, planned, forecast, actual, status
}
business trip record: {                              // 신규
  id, name, nat, dept, purpose, start, end, note
}

Excel 원본 시트 매핑: 05_BUSINESS_TRIP — Trip_ID, Name, Nationality, Department, Purpose, Trip_Start, Trip_End, Note. 조직도 계층 정보(Parent_Org_ID 등)는 없음 — 출장자는 현장 조직도에 속하지 않는 본사 소속 인원이기 때문.

SHEET 06 / 13

Excel 추출(다운로드) 기능

구현 완료

흐름

툴바 "Excel 추출" 클릭 → 팝업
프로젝트들이 버튼 형태(st.pills)로 나열, 여러 개 동시 선택 가능
"다운로드 파일 생성" 클릭 → 서버가 선택된 각 프로젝트의 data.json 내용을 원래 컬럼명으로 되돌려 프로젝트당 독립된 xlsx 파일 생성 (01_PROJECT/02_MANPOWER_ORG/03_MILESTONES/04_PROJECT_CONFIG/05_BUSINESS_TRIP 5개 시트, 원본과 동일 구조)
1개 선택 → 그 xlsx 파일 그대로 다운로드 / 여러 개 선택 → 개별 xlsx들을 zip으로 묶어서 다운로드
PIN 불필요 (다운로드는 열람의 연장으로 간주 — 대시보드 자체가 이미 링크만으로 열람 가능하므로 별도 보호 없음)

날짜 처리

JSON엔 "2026-12-15" 형태 문자열로 저장되어 있는데, 추출 시 진짜 Excel 날짜 셀로 복원(TBD/빈값은 텍스트 그대로)하여, 재업로드해도 문제없게 함. Python(openpyxl)만 사용한 순수 서버 사이드 왕복이라 브라우저 변환에서 발생했던 부동소수점 오차 문제(10번 항목 참고) 없음 — 실제 재파싱 테스트로 모든 필드 100% 일치 확인.

한계

시트 구조·컬럼·데이터 값은 원본과 완전히 동일하지만, 셀 서식(글꼴/색상/테두리/열너비), 원본에 있었을 수 있는 메모나 수식은 애초에 JSON 변환 단계에서 값만 추출했기 때문에 복원되지 않음 — 기본 스타일의 Excel 파일로 생성됨.

SHEET 07 / 13

출장자 관리 기능

구현 완료

왜 인력(파견) 데이터와 분리했나

02_MANPOWER_ORG는 조직도 한 자리를 차지하는 장기 배치 포지션이 기본 단위인데, 출장자는:

  • 한 사람이 같은 현장에 여러 번(예: 2주씩 여러 번) 다녀올 수 있어 "한 사람 = 한 자리" 가정이 안 맞음
  • 본사 소속이라 현장 조직도상 자리(Part/Role/상위 리더) 자체가 없음
  • 기간이 항상 1개월 미만으로 짧아 장기 파견과 성격이 다름

그래서 완전히 독립된 데이터(business_trips / 05_BUSINESS_TRIP 시트)로 설계했고, 기존 인원수 KPI(예상 투입/현재 근무 인력)에는 포함시키지 않음.

검증 규칙

출장 기간이 30일 이상이면 warning — "파견으로 등록해야 하는 건 아닌지 확인" 안내 (막지는 않음). 실제 50일짜리 테스트 데이터로 정상적으로 경고가 뜨는 것 확인, 3일·5일짜리는 경고 없이 정상 통과하는 것도 확인.

화면 반영

위치내용
프로젝트별 페이지"출장자 현황" 테이블 — 그 현장에 걸린 출장 전체 (기간 필터 없음), 이름/부서/목적/기간/상태(예정·진행중·종료)/비고
Summary 탭"주간 출장 현황" / "월간 출장 현황" 패널 — 08번 항목 참고

실제 테스트: 이번 주 걸친 출장(진행중), 다음 달 출장(주간·월간 패널 모두 제외), 과거 50일 출장(제외 + 경고)까지 프로젝트 페이지 테이블에서 전부 정확히 표시되는 것을 실제 데이터로 확인함.

SHEET 08 / 13

Summary 탭 — 주간/월간 파견·출장 패널

구현 완료

배치 순서 (기존 화면 요소는 그대로, 추가만)

  1. 예상 투입 인력 KPI
  2. 현재 근무 인력 KPI
  3. 월별 현장 인력 투입 현황 (기존 차트)
  4. 주간 인력 파견 현황 (신규)
  5. 월간 투입인력 현황 (신규)
  6. 주간 출장 현황 (신규)
  7. 월간 출장 현황 (신규)
  8. Project Overview 카드
  9. 인력 상세 현황 테이블

포함 기준

패널기준컬럼
주간/월간 인력 파견계획시작일(Plan_Start) 또는 실제시작일(Actual_Start)이 그 기간(이번 주 월~일 / 이번 달 1일~말일)에 속하는 인원 — 전 프로젝트 통합이름/현장/Part/Role/국적/계획기간/실제기간/상태
주간/월간 출장출장기간이 그 기간과 조금이라도 겹치면 포함 — 전 프로젝트 통합이름/현장/부서/목적/출장기간/상태

상태는 오늘 날짜 기준 자동 판정(예정/진행중/종료). 실제 데이터(기존 Turkistan TBD 인원 포함)로 월간 파견 패널에 교차 검증까지 완료.

SHEET 09 / 13

보안·운영 관련 트레이드오프

항목내용
원본 xlsx 미보관업로드 시 서버엔 JSON만 도착 — 서식·수식·코멘트는 보관 안 됨. 추출 기능으로 다시 xlsx를 만들 수 있으나 이 역시 값만 복원(06번 항목 한계 참고)
PIN 범위업로드만 PIN(12345)으로 보호. 열람과 Excel 추출은 링크만 있으면 누구나 가능
서버 상시 구동 필요파일 단독 배포 불가 — 서버 꺼지면 열람도 업로드도 다 안 됨
다른 컴퓨터로 이전폴더 전체 복사 + Python/의존성 설치 + run.bat. host를 0.0.0.0으로 둬서 접속 링크만 그 컴퓨터 IP로 바뀜. 유동 IP면 고정 IP 권장
동시 편집 충돌 미고려같은 프로젝트를 거의 동시에 업로드하면 나중 것이 우선 (이전 것은 archive로 보관되어 완전 유실은 아님)
SHEET 10 / 13

발견 및 수정된 버그 이력

BUG-01

임베드 시 Streamlit 기본 여백/툴바로 화면이 안쪽으로 밀려 보임

원인 — Streamlit 기본 padding + 상단 자체 툴바.
수정 — CSS 주입으로 block-container padding 제거, 헤더 숨김, 버튼 줄을 대시보드 헤더와 같은 색으로 스타일링.
BUG-02

새로고침 버튼이 두 개 보임

원인 — 대시보드 내장 "Refresh"는 인라인 데이터를 다시 그릴 뿐 실제 재조회가 아님.
수정render_dashboard_html() 출력 HTML에만 .reload-btn{display:none} 후처리로 숨김 (원본 파일은 안 건드림).
BUG-03

업로드 성공 후 "확인"을 눌러도 팝업이 안 닫힘

원인 1 — "확인" 클릭 감지 코드가 "업로드" 클릭 순간에만 존재하는 조건 안에 중첩되어 다음 rerun에서 무시됨.
원인 2st.dialog 안에서 st.rerun()을 부르면 그 즉시 팝업이 닫히는 특성 — 성공 메시지 보여주려던 중간 호출이 메시지가 뜨기도 전에 닫아버림.
수정st.session_state로 성공 상태 저장, 성공 화면은 같은 실행에서 바로 렌더링, st.rerun()은 실제 "확인" 클릭 시 한 번만 호출.
BUG-04

Excel 날짜가 하루 이르게 표시됨 (2026-12-15 → 2026-12-14)

원인 — SheetJS가 Excel 날짜 시리얼을 JS Date로 바꿀 때 부동소수점 오차로 자정 약 52초 전이 되는 문제.
수정 — 변환 함수에서 1분을 더한 뒤 연/월/일 추출. 실제 파일로 재현 후 검증 완료.
BUG-05

run.py 더블클릭이 안 됨

원인 — 이 컴퓨터에서 .py 확장자가 아무 프로그램과도 연결 안 되어 있었음.
수정.bat은 항상 실행 가능하므로 run.bat 신설, Python 경로 직접 지정.
NOT-A-BUG

"새로고침을 눌러도 아무 변화가 없다"

새로고침은 최신 데이터를 다시 불러오는 기능 — 그 사이 아무도 업로드하지 않았다면 가져올 새 데이터가 없어 화면이 그대로인 게 정상. 다른 컴퓨터에서 업로드 직후엔 정상 반영 확인됨.
결론 — 정상 동작, 수정 없음.
SHEET 11 / 13

실행 방법

manpower-board-release 폴더에서 run.bat 더블클릭
콘솔에 Network URL: http://<IP>:8501이 뜨면 정상 기동
그 링크를 다른 사용자들에게 공유 — 접속하면 바로 대시보드
종료가 안 되면(간헐적으로 발생) 작업관리자에서 관련 python.exe 강제 종료
SHEET 12 / 13

승인 체크리스트

SHEET 13 / 13

다음 작업 예정

미착수

조직도 화면에서 인력을 바로 추가하는 기능 — 아직 설계만 되어있고 구현 전.

  • 조직도 각 트리 말단(리프) 노드 아래 (+) 버튼, 조직도 패널 우측 상단 "인력 추가" 버튼 — 클릭 시 팝업으로 직무/직책/이름/국적/계획·실제 파견기간 입력
  • 저장 시 해당 프로젝트 데이터에 실제 행으로 추가되고, 다른 차트·테이블에도 자동 반영
  • PIN 보호, Role은 고정 드롭다운(Leader/Manager/Engineer), 저장은 아카이브 없이 같은 파일에 바로 append하는 방향으로 설계됨 (착수 시 상세 재확인 필요)