교대(변형)근로 관리 스펙
개요
교대(변형)근로 관리는 Simple HR 프로젝트에서 교대근무 및 변형근무 부서를 조회·운영하는 기능이다. 근무 정책과 밀접하게 연동되며, 근태 관리의 실제 근무 패턴 기록에 영향을 준다. 2025-06-23 기준 최종 수정 현황이 반영되었다. fix-task-0623 (26년 06월 23일 수정사항) 2025-06-24 수정사항 추가 반영. fix-task-0624 (26년 06월 24일 수정사항) 2025-06-25 수정사항 추가 반영. fix-task-0625 (26년 06월 25일 수정사항) 2026-07-13 수정사항 추가 반영. fix-task-0713 (26년 07월 13일 수정사항) 2026-07-14 수정사항 추가 반영. fix-task-0714 (26년 07월 14일 수정사항) 2026-07-15 수정사항 추가 반영. fix-task-0715 수정사항
2025-06-23 수정 현황
사용자 교대일정 (/user/shift-schedule) — Mock→실 API 연동
| 항목 | 상태 | 상세 |
|---|---|---|
| 캘린더(월/주간) | ❌ 미구현 | generateMonthShifts() mock → shiftScheduleApi / calendarApi.listAutoLayers() 실 연동 필요 |
| 다가오는 교대 사이드바 | ❌ 미구현 | 하드코딩된 목 데이터 → 실 데이터에서 오늘 이후 N건 추출 로직 필요 |
| 교대변경 이력 | ❌ 미구현 | Mock 테이블 → 사용자용 ShiftSwapRequest 조회 API 필요 (백엔드 미구현) |
| 교대변경 요청 모달 | ❌ 미구현 | UI만 존재 → 사용자용 ShiftSwapRequest 생성 API 필요 (백엔드 미구현) |
| 교대 희망 신청 | ❌ 미구현 | UI만 존재 → 백엔드 모델+API 전체 신규 필요 |
| 모바일 연장근로 메뉴 접근 경로 | ❌ 확인 필요 | 모바일 네비게이션에서 연장근로 메뉴 접근 경로 확인 필요 |
fix-task-0623 (26년 06월 23일 수정사항) fix-task-0624 (26년 06월 24일 수정사항) fix-task-0625 (26년 06월 25일 수정사항)
교대(변형)근로관리 페이지 (/admin/shift)
| 항목 | 상태 | 상세 |
|---|---|---|
| 우측 상단 날짜 필터 동작 불가 | ✅ 완료 | 교대 근로 관리 필터 작동 복구 fix-task-0612.md |
| 휴가 승인·취소 시 스케줄 복원 | ❌ 미지원 | 휴가 승인 시 기존 ShiftSchedule 행 삭제 후 VACATION 삽입 → 취소 시 원래 스케줄 복원되지 않음. 직원 휴가 신청 시 해당 날짜를 휴가로 표시하고 스케줄 배정은 차단 필요 fix-task-0612.md |
| 변형 근로 동적 수정 확인 | ✅ 완료 | 변형근무 슬롯 선택 팝업 레이아웃 수정 fix-task-0612.md |
| 페이징 처리 통일 | ✅ 완료 | 전체 모듈에 일관된 페이징 방식 적용 fix-task-0623 (26년 06월 23일 수정사항) |
2025-06-24 추가 수정 현황
근무일 변경 기준시간 및 근로정책 적용 시점
| 항목 | 상태 | 상세 |
|---|---|---|
shift_day_boundary (근무일 변경 기준시간) | ❌ 미구현 | DB 저장되지만 근태/급여 로직 미반영. 야간 교대근무 시 근무일 판정 기준으로, 미구현 시 휴일근로/야간시간 집계 오류 가능 |
| 근로정책 수정 시점 지정 | ✅ 완료 | 변경 시점 이후에만 새 정책 적용, 이전 근태 기록 보존. 삭제 시에도 과거 기록 보존. fix-task-0714 (26년 07월 14일 수정사항) |
fix-task-0624 (26년 06월 24일 수정사항)
퇴근 미체크 자동 처리 스케줄러
| 항목 | 상태 | 상세 |
|---|---|---|
| 퇴근 미체크 자동 처리 배치 | ✅ 완료 | 매일 KST 00:15 실행. 최근 7일 이내 check_out_time IS NULL 레코드를 unchecked_checkout_policy 설정에 따라 자동 처리 |
AUTO_END_TIME | ✅ 완료 | scheduled_end 시간으로 퇴근 자동 기록 + 근무시간/상태 재계산 |
LEAVE_BLANK | ✅ 완료 | 스킵(빈 값 유지) |
| advisory lock 중복 방지 | ✅ 완료 | lock(820620)으로 multi-worker 중복 실행 방지. 급여 영향 없음(급여는 OvertimeRequest 기반) |
| HNT 수동 보정 대상 | ✅ 완료 | 6/17~6/24 19건은 스케줄러 자동 처리 대상. 2~3월 4건(scheduled_end NULL)만 관리자 UI에서 수동 처리 필요 |
fix-task-0624 (26년 06월 24일 수정사항)
휴가와의 연동 흐름
2025-06-25 추가 수정 현황
사용자 교대일정 (/user/shift-schedule) — 연동 상태 업데이트
아래 항목은 2025-06-25 기준으로도 미구현 상태가 유지된다. fix-task-0625 (26년 06월 25일 수정사항)
| 항목 | 상태 | 상세 |
|---|---|---|
| 캘린더(월/주간) 연동 | ❌ 미구현 | 실 API 연동 미완료 |
| 다가오는 교대 사이드바 연동 | ❌ 미구현 | 실 데이터 추출 로직 미완료 |
| 교대변경 이력 API | ❌ 미구현 | 백엔드 API 미구현 |
| 교대변경 요청 모달 API | ❌ 미구현 | 백엔드 API 미구현 |
| 교대 희망 신청 | ❌ 미구현 | 백엔드 모델+API 전체 신규 필요 |
| 모바일 연장근로 메뉴 접근 경로 | ❌ 확인 필요 | 모바일 네비게이션에서 연장근로 메뉴 접근 경로 확인 필요 |
근로정책 급여 연동 — 2025-06-25 완료/미완료 항목
| 항목 | 상태 | 상세 |
|---|---|---|
| 근무제 연동(LINKED) 정산방식 급여 반영 | ✅ 완료 | |
저녁시간 연장근로 제외(exclude_dinner_from_overtime) | ✅ 완료 | |
연장근로 입력 단위 제한(overtime_input_unit) | ✅ 완료 | |
연장근로 최소시간 제한(overtime_min_input) | ✅ 완료 | |
점심시간 휴게시간 차감(use_lunch_as_break) | ✅ 완료 | |
선택근무 퇴근 허용시간(checkout_allow_from/to) | ✅ 완료 | |
선택/탄력근무 출근 허용시간(flex_start_from/to) | ✅ 완료 | 출퇴근 허용시간 검증 및 에러 메시지 형식 통일 포함 |
법정근로 내 연장 구분(legal_overtime_split) | ✅ 완료 | |
| 모바일 연장근로 메뉴 접근 경로 | ❌ 확인 필요 | |
대체휴일/대체근무(substitution_enabled) | ❌ 미구현 | |
근무일 변경 기준시간(shift_day_boundary) | ❌ 미구현 | DB 저장되지만 근태/급여 로직 미반영 |
출장 일정 사용(business_trip_enabled) | ❌ 미구현 | |
탄력근로 시작 날짜(flexible_start_date) | ❌ 미구현 |
fix-task-0625 (26년 06월 25일 수정사항)
연장근로 시간대 겹침 방지 (신규)
2026-07-13 미구현 이슈 현황
수동 배정 시 최소 연속 휴식 시간 검증 미구현
근무 정책에 min_consecutive_rest_hours 값의 저장·로드는 정상 동작하며, 변형근무 자동배정(auto_assign_variant_shifts)에서는 rest_hours 검증이 구현되어 있다. 그러나 수동 셀 클릭 배정(assign_shift_roster, bulkCreate) 시에는 전일 퇴근~당일 출근 간격 검증이 없다.
- 예: 야간(18:00~06:00) 다음날 바로 주간(06:00~18:00) 수동 배정 시 휴식 0시간이지만 경고/차단 없음
- 수정 대상:
backend/app/services/policy_service.py—assign_shift_roster()+ 프론트 셀 배정(bulkCreate) 시 전일 퇴근~당일 출근 간격 검증 추가
fix-task-0713 (26년 07월 13일 수정사항) fix-task-0715 수정사항
교대근로 순환 패턴(cycle_pattern) 기반 자동 배정 미구현
cycle_pattern(예: ["D","D","D","OFF","N","N","OFF"])과 rotation_cycle_days 저장·로드는 정상이지만, 교대근무(SHIFT) 모드에서 패턴 기반 자동 순환 배정 로직이 없다.
- 현재 자동배정(
auto_assign_variant_shifts)은 변형근무(VARIANT) 모드 전용. SHIFT 모드는 관리자가 수동으로 셀 배정해야 함 - 프론트에서 패턴 기반 법정한도 체크(주간시간 검증)는 구현됨. 백엔드 저장 시 패턴 유효성 재검증은 없음
fix-task-0713 (26년 07월 13일 수정사항) fix-task-0715 수정사항
직원 상세 드로어에서 예약된 근로정책 변경 표시 미구현
교대근무 법정 기준 초과 시 저장 차단 → 경고 후 저장 허용으로 변경 (2026-07-14 완료)
기존에는 교대근무 패턴이 주 52시간을 초과하면 저장 버튼이 비활성화되어 정책 등록 자체가 불가능했다. 삼성서울병원 전화예약실 등 실제 교대근무 고객사가 테스트 중 등록 불가 문제를 겪었다. fix-task-0714 (26년 07월 14일 수정사항)
- 수정 완료 — 법정 기준 초과 시에도 저장 버튼이 활성화됨
- 저장 클릭 시 "법정 기준을 초과합니다. 저장하시겠습니까?" 확인 팝업 표시
- 확인 누르면 정상 저장; 화면에 "법정 기준 초과 — 초과분은 대체 휴가로 해결할 수 있습니다" 안내 표시
직원 개별로 근로정책 변경 시 "날짜 지정"으로 미래 적용일을 선택하면 EmployeeWorkPolicy에 effective_from=미래날짜로 저장되지만, 드로어에서는 현재 적용 중인 정책명만 표시된다.
- 예: "2026-08-01 부터 '야간정책' 변경 예정" 같은 안내 표시 필요
- 수정 대상: 백엔드
EmployeeDetail스키마에pending_work_policy_name,pending_work_policy_effective_from추가 +_employee_to_detail()에서 미래 EWP 조회 + 프론트 드로어 읽기 모드에 표시
fix-task-0713 (26년 07월 13일 수정사항)
같은 날 연장근로 신청 시
근로정책 수정 시 적용 시점 이전 데이터 소급 적용 문제 (2026-07-14 완료)
근로정책 수정 시 과거 데이터까지 소급 적용되던 문제가 수정되었다. fix-task-0714 (26년 07월 14일 수정사항)
- 수정 완료 — 근로정책 변경 시 변경 시점 이후에만 새 정책이 적용되고, 이전 근태 기록은 변경 전 정책이 그대로 유지됨
- 근로정책 삭제 시에도 과거 출퇴근·급여 기록에 저장된 정책 정보는 삭제되지 않고 보존됨
- 적용 시점 모달: 사용 중인 정책은 내일 이후만 선택 가능하며, 미래 날짜 선택 시 예약 변경으로 저장되고 변경 이력 조회/취소 가능
- 값 변경 없이 저장 시 "변경된 항목이 없습니다" 토스트만 표시
→ 근무 정책 스펙 참조
work_days OFF 요일 출근 차단 미구현 (2026-07-14 신규 이슈, 2026-07-15 수정 완료)
2026-07-15 수정사항 현황
work_days OFF 요일 출근 차단 수정 완료 (2026-07-15)
근태 관리의 _resolve_scheduled_for_day()에 work_days OFF 체크가 추가되고, get_user_dashboard()에 주휴일 판정이 추가되어 출근 버튼 비활성화가 구현되었다. OT_HOLIDAY 승인 시 출근 허용 흐름은 동일하다. (커밋: 0e9ac95) fix-task-0715 수정사항
근로정책 적용시점 반영 및 이력 덮어쓰기 수정 완료 (2026-07-15)
근무 정책의 적용시점 저장 및 이력 관리가 전면 개선되었다. fix-task-0715 수정사항
| 수정 항목 | 내용 |
|---|---|
| 이력 덮어쓰기 방지 | 항상 신규 EWP INSERT, 기존 EWP는 effective_to 설정으로 종료 |
| 날짜 기반 활성 판정 | is_primary 플립 대신 effective_from/to 날짜 범위 + ORDER BY effective_from DESC로 활성 정책 결정 |
| 미래 예약 처리 | 적용일 도래 시 Read 쿼리가 자연 전환; 스케줄러(close_superseded_ewps)가 이전 EWP의 effective_to 자동 채움 |
| 적용시점 표시 | EmployeeDetail 응답에 work_policy_effective_from, scheduled_work_policy_name, scheduled_work_policy_date 추가, 드로어에 표시 |
| 이력 모달 | GET /employees/{id}/work-policy-history API + 프론트 변경이력 모달 (적용중/예약중/취소 배지) |
- 배포 체크리스트:
docs/fix_tasks/0715/report/deploy-checklist.md
미구현 항목 (2026-07-15 기준)
근태 관리에서 _resolve_scheduled_for_day()가 ShiftSchedule과 Holiday만 확인하고, work_days의 OFF 요일은 체크하지 않는다. fix-task-0714 (26년 07월 14일 수정사항)
- 예: 토요일을 OFF로 설정해도
Holiday로 등록하지 않으면 출근이 차단되지 않음 - 조회 시(
get_my_attendance)에는 합성ATT_HOLIDAY레코드로 "휴무" 표시는 되지만,check_in()시점에is_off판정에work_days가 반영되지 않아 실제 출근 차단 안 됨 - 수정 대상:
backend/app/services/attendance_service.py—_resolve_scheduled_for_day()함수에work_daysOFF 요일 →is_off=True반환 추가 - 수정 후 기존
check_in()흐름(is_off=True→ OT_HOLIDAY 승인 확인 → 없으면 차단, 있으면 허용)을 그대로 타므로 연장근로(휴일근무) 승인 시 비근무 요일에도 출근 가능
2026-07-15 수정 완료._resolve_scheduled_for_day()에work_daysOFF 체크 추가 및get_user_dashboard()에 주휴일 판정 추가. fix-task-0715 수정사항
시간대가 겹치는 경우를 방지하는 OT_TIME_OVERLAP 검증이 추가되었다. fix-task-0625 (26년 06월 25일 수정사항)
- 백엔드+프론트엔드 동시 적용
- PC 위자드, 대시보드 빠른 신청, 모바일 3곳에서 날짜/시간 입력 시 겹침 실시간 검증
- 겹침 감지 시 빨간 글씨 안내 + 신청 버튼 차단
- 통합 테스트(
test_checkin_allow_time.py,test_overtime_time_overlap.py) 추가
현재 상태: 휴가 취소 시 원래 스케줄이 복원되지 않는 상태. 직원 휴가 신청 시점에 해당 날짜 스케줄 배정을 차단하는 로직 필요. fix-task-0612.md
핵심 구현: 교대 부서 조회
policy_service.py 내 list_shift_departments, _lookup_policy_for_department 두 함수에서 교대/변형근무 부서를 찾는 경로가 2개 존재한다. 시드 데이터로 인해 별도의 경로를 구현한 결과이다.
1차 — DepartmentWorkPolicy 경로
- 관리자가 부서별 근로정책 UI에서 교대/변형 정책을 부서에 직접 할당한 경우 동작
DepartmentWorkPolicy테이블 JOIN으로 조회- 직원에게
shift_team_name이 없어도 부서가 목록에 노출됨
2차 — Employee fallback 경로
- 시드 데이터 등 직원에게 직접
shift_team_name+work_policy_id가 배정된 경우 동작 Employee.shift_team_name IS NOT NULL조건으로 부서 역추적DepartmentWorkPolicy레코드가 없는 기존 데이터에서 동작
관련 페이지
- 근무 정책 스펙 — 교대/변형 정책 원본 설정 화면
- 근태 관리 스펙 — 실제 근무 기록과 연동
- 휴가 관리 스펙 — 휴가 승인·취소 연계 기능
- 직원 재직 상태 변경 워크플로 — 직원 상태 관리 기능
- 근무 정책 ↔ 급여 연동 현황 및 이슈 —
shift_day_boundary등 근로정책-급여 미연동 항목 - 휴일 관리 스펙 — 휴일 생성 시 교대근무 스케줄 연동
- 액션 아이템 — 전체 수정 작업 목록
- fix-task-0624 (26년 06월 24일 수정사항) — 2025-06-24 원본 수정 소스
- fix-task-0625 (26년 06월 25일 수정사항) — 2025-06-25 원본 수정 소스
- fix-task-0713 (26년 07월 13일 수정사항) — 2026-07-13 원본 수정 소스
- fix-task-0714 (26년 07월 14일 수정사항) — 2026-07-14 원본 수정 소스
- fix-task-0715 수정사항 — 2026-07-15 원본 수정 소스