문제
연차 현황, 수당 신청, 근무표, 인력 이력카드, 교육 이수 관리를 엑셀과 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