프로젝트 · 2026.07 –

Works Portal

모듈형 사내 업무 포털

인증·부서·전자서명·결재 엔진을 공통 기반으로, 연차·주간보고/수당·인력 이력카드·교육(VOD) 모듈을 얹은 사내 포털

nginx · TLSVOD 경로만 버퍼링 off연차결재 · 합의 · 참조주간보고 · 수당이력카드기술등급교육 · VODRange 206 · 구간 병합공통 기반 — 인증 · 부서 · 전자서명 · 결재 엔진권한은 조직 구조에서 파생 · 서명/시수 스냅샷FastAPIPostgreSQL
직접 구현저장소외부 시스템

문제

연차 현황, 수당 신청, 근무표, 인력 이력카드, 교육 이수 관리를 엑셀과 PDF 서식으로 따로따로 처리하고 있었다.

해결

공통 기반(인증, 부서, 전자서명, 결재 엔진) 위에 업무별 모듈을 얹는 구조로 포털을 만들었다. 엑셀 서식으로 하던 업무를 하나의 시스템으로 옮겼다.

모듈

  • 연차: 신청, 결재함(결재/합의/참조), 팀원 현황, 월별 리포트와 엑셀 출력, 전자서명
  • 주간보고·수당: 개인 보고, 부서 취합 요약, 수당 신청(직무·파견·야간·특별), 근무표
  • 이력카드: 프로필, 기술등급 계산기, 사내 교육 이력
  • 교육: 커리큘럼 맵(11개 도메인, 65과목), VOD 수강과 진도, 수료증 PDF 발급, 이수 인정 신청
  • 관리자: 사용자, 부서, 연차 부여, 공휴일, 메일, 수당 설정, 교육 과정과 직무 트랙 배정

설계 포인트

  • VOD 스트리밍 직접 구현
    • HTTP Range(206) 응답을 직접 구현하고, nginx에서는 이 경로만 버퍼링을 꺼서 탐색(seek)이 지연되지 않게 했다.
    • <video> 태그는 Authorization 헤더를 보낼 수 없어서, 차시 단위로 범위가 한정된 단기 토큰을 발급한다.
    • 실제로 본 구간을 병합하고, 토큰 버킷으로 인정 시간을 제한해 건너뛰기나 배속으로 진도를 부풀리지 못하게 했다.
    • MP4 메타데이터를 직접 파싱해 영상 길이를 구한다.
  • 권한은 조직 구조에서 파생: 역할 필드를 따로 두지 않고, 부서장 지정과 파견 여부로 결재권과 합의권을 계산한다. 연차 산정 공식도 서버 한 곳에서만 계산한다.
  • 스냅샷 설계: 결재 서명, 수료증 발급자 서명, 이수 시수는 기록 시점 값으로 고정해 나중에 원본이 바뀌어도 소급 변경되지 않는다.
  • 업로드 검증: 이미지는 재인코딩해 폴리글롯 파일을 제거하고, PDF는 매직바이트를 확인한 뒤 첨부(attachment)로만 내려준다.
  • 검증 자동화: 교육 모듈 검증 스크립트 106/106 통과. 커리큘럼 과목 간 교차참조 157건도 스크립트로 검증한다.
  • 배포: deploy.sh 하나로 의존성 동기화, 빌드, 재시작, health 체크까지 진행한다. SELinux 라벨링도 처리한다.

스택

Next.js 15 · React 19 · TypeScript · Tailwind CSS v4 · FastAPI · SQLModel · PostgreSQL · openpyxl · reportlab · nginx · systemd