Skip to content

HBEA410_휴가신청

(Hereby) 직원 앱 > 결재 허브([[HBEA400_결재 허브]]) > 휴가신청

직원 앱 (Hereby Works, 포트 4002)
대상 사용자조건3 [EMPLOYEE] (self scope 인증 세션)
프로토타입TBU
관련 정책[[POL_결재워크플로우]] · [[POL_연차부여]]
Statusreview
Owner카나

개요

휴가신청은 결재 허브([[HBEA400_결재 허브]]) 첫 서브탭으로, 상단 연차현황 카드 + 신청 내역 리스트 + FAB(+) 휴가 신청 폼으로 구성된다. 신청은 leave_requests 레코드를 self scope로 생성(status=pending)하며, 승인/반려/취소는 관리자 결재([[HBAD251_연차 현황·결재]])가 처리한다 — 직원 측에는 상세 진입·취소 경로가 없다. 휴가 유형은 Admin 휴가 유형([[HBAD261_유형 설정 (연장근무·외근·휴가)]])을 소비하며, 커스텀 유형 문자열도 관측된다(census 실데이터 "ㅅㄷㄴㅅ"). 공통 서브탭 골격(상태 필터 칩·FAB·신청→결재 흐름)은 [[HBEA400_결재 허브]]을 참조한다(중복 정의 금지).

문서 히스토리

날짜작성자변경 내용
2026-07-06카나PM최초 작성
2026-07-10카나PM라이브 기능 검토 반영
2026-07-13카나PM버튼 행동 명세 보강
2026-07-21카나PMGC700 깊이 재작성
2026-07-21카나PM셀 스타일 확정판 적용 (콸 승인)
2026-07-21카나PMTC 크로스체크 확정분 반영 (🟡)
2026-07-23카나PM코드 변경 반영(2577546·425fac8·3b25572·318f7e1)
2026-07-24카나PM코드 변경 반영(04b1b82·7e16a37)

Data I/O

Server → Client

변수DB 엔티티.컬럼값 없을 때비고
$leave_grants.grantedDays − 승인 사용일수0/빈값 표기census "11 일 남음 / 올해 11일 중 0일 사용"
$leave_requests[] (self)Empty "휴가 신청 내역이 없어요"본인 신청만
$auth.getCurrentUser().leaveReasons (org 커스텀 휴가 사유)빈 select원천 = 조직 설정 organization.updateSettings({leaveReasons}) (HBAD 조직 설정). picker는 사유 id를 소비 (TC 확정: leave-request-custom-reason.test.ts). 모델 변경: 유형은 절대 시간이 아니라 차감 배수(종일 1 / 반차 0.5 / 반반차 0.25 / 차감 없음 0) + 시간 단위(hourly) 유형 2원 체계 (구 {hours, deductionDays} 서술 대체, 코드 반영: 318f7e1 leaveReasonSchema)
$leave_requests.status 집계카운트 0은 라벨만census "전체 3 / 대기중 2 / 취소 1"

Client → Server

액션전달 값tRPC procedure권한 토큰
휴가 신청 ("신청하기")leaveType·startDate·endDate·reason (·isHalfDay·halfDayPeriod). 시간 단위 유형 시: startTime·requestedMinutes(30분 단위) 추가leaveRequest.request (TC 라이브 확인) — leaveType은 enum 고정이 아니라 org 커스텀 휴가 사유 id를 수용. 커스텀 사유가 설정된 조직에서는 기본 LeaveType enum(ANNUAL 등) 전달 시 "Invalid leave type" throw(legacy enum 거부) (TC 확정: leave-request-custom-reason.test.ts). 서버 가드: 신청 기간 전체가 법정유급휴일(공휴일)이면 거절, 기간에 낀 공휴일은 차감 자동 제외 (코드 반영: 425fac8) / 당일 신청 시 이미 지난 시작 시간 차단 (코드 반영: 3b25572) / 유형별 허용 팀 아니면 본인 소속 팀 신청 차단 (코드 반영: 7e16a37) / 시간 단위 유형 선택 시 유형 defaultMinutes를 사용 시간에 프리필 (코드 반영: 04b1b82) (코드 반영: 2577546 LeaveRequest.requestedMinutes·startTime migration 1855)self
상태 필터statusKey리스트 필터 ([[HBEA400_결재 허브]])self

외부 통합 (cross-app)

외부 시스템데이터동기/비동기비고
관리자 결재 [[HBAD251_연차 현황·결재]]대기중 → 승인/반려/취소 상태 write-back비동기직원 측 취소 경로 없음
Admin 휴가 유형 [[HBAD261_유형 설정 (연장근무·외근·휴가)]]leaveType 원천동기select 옵션
연차 부여 기준 [[HBAD262_연차 부여 기준]]grantedDays 부여 기준동기잔여 산정

화면 플로우 (Mermaid 의무)


휴가신청 사양표

번호항목/기능명타입사양 및 기능 설명연관/영향 범위비고
00화면 진입 조건-조건1 [직원 인증] · 결재 허브 "휴가신청" 서브탭 선택 시 노출인증 세션 (self)
01초기 상태Output연차현황 카드 + 상태 필터 칩 + 신청 내역 리스트. CASE01 [내역 있음] → 행 목록 / CASE02 [0건] → Empty 문구$
10연차현황 카드Card상단 카드. "11 일 남음" 강조 + "올해 11일 중 0일 사용" 보조. CASE01 [부여·사용 있음] → 잔여 계산값 표기leave_grants (grantedDays) · 사용일수 집계census 실데이터. TBU: 부여 데이터 부재 → 잔여 0/빈값 표기(추정)
20상태 필터 칩Filter• 신청 내역을 상태로 거르는 칩. "전체 3" / "대기중 2" / "승인" / "반려" / "취소 1"(카운트 0은 라벨만). 공통 사양 [[HBEA400_결재 허브]]
• 클릭 → 목록 필터
leave_requests.statuscensus 실 카운트
30신청 내역 리스트List본인 휴가 신청 행 목록. 필터 칩에 종속leave_requests[]TBU: ${leave_requests} 부재 → 리스트 비우고 Empty / 로드 실패 UI 미검증(무반응이면 버그 후보)
ㄴ31내역 행Row• 1행 = {유형} / {날짜} / {사유} / {상태 배지}. 상세 진입·직원 취소 경로 없음, 취소는 관리자 결재 [[HBAD251_연차 현황·결재]] 처리
• 클릭 → 무반응(비인터랙티브)
leave_requests (leaveType·startDate·reason·status)census: "ㅅㄷㄴㅅ 7월 14일 가족 행사dd 대기" 등
ㄴ32상태 배지Badge"대기" / 승인 / 반려 / 취소. CASE01 [대기] · CASE04 [취소] 관측leave_requests_status_enumTBU: enum 매핑
ㄴ30-EEmptyText신청 0건 → "휴가 신청 내역이 없어요" (원문 카피)
40신청 작성 FABButton (+)• 휴가 신청 폼을 여는 우하단 "+"(접근성 이름 "새 신청"). 조건3 [EMPLOYEE] self scope
• 클릭 → "휴가 신청" 폼 모달 오픈
휴가 신청 폼공통 FAB([[HBEA400_결재 허브]])

서브 화면 / 모달 사양표

휴가 신청 폼 (모달) 사양표

번호항목/기능명타입사양 및 기능 설명연관/영향 범위비고
00진입 조건-휴가신청 서브탭 FAB(+) "새 신청" 클릭 시 모달 오픈. 제목 "휴가 신청"40 FAB
01초기 상태-유형 미선택 / 날짜 미입력 / 사유 빈 textarea. "신청하기" 노출
10유형Select• 휴가 유형(사유)을 고르는 드롭다운. org 커스텀 휴가 사유를 소비 — 옵션 원천 = auth.getCurrentUser().leaveReasons, 제출 시 사유 id 전달
• 클릭 → 유형 드롭다운 오픈
 - 규칙: 커스텀 사유 설정 조직에서는 기본 LeaveType enum(ANNUAL 등) 전달 시 "Invalid leave type" throw (legacy enum 거부)
 - 규칙: 유형별 팀 제한 — 본인 소속 팀이 유형의 허용 팀이 아니면 신청 불가(서버 검증, 코드 반영: 7e16a37)
 - CASE01 [시간 단위(hourly) 유형 선택]: 시간 단위 연차 입력 UI(ㄴ21) 노출 + 유형의 defaultMinutes가 사용 시간에 프리필(30분 단위 조정 가능, 코드 반영: 04b1b82)
auth.getCurrentUser().leaveReasonsorganization.updateSettings({leaveReasons}) (HBAD 조직 설정)커스텀 사유 id 소비 (TC 확정: leave-request-custom-reason.test.ts) · 유형 = 차감 배수/시간 단위 2원 체계(코드 반영: 318f7e1) · 팀 제한(7e16a37)·프리필(04b1b82)
20시작일·종료일DatePicker• 휴가 적용 기간을 지정하는 "시작일" / "종료일" 2필드
• 입력 → 기간 설정
 - 규칙: 신청 기간 전체가 법정유급휴일(공휴일)이면 접수 거절, 기간에 낀 공휴일은 차감 자동 제외 (서버 가드)
leave_requests.startDate / endDate코드 반영: 425fac8
ㄴ21시간 단위 연차 입력Group시간 단위(hourly) 유형 선택 시에만 노출되는 입력 그룹 (하루 단위 신청만)
 - 시작 시간: 사용 시작 시각 입력. 당일 신청 시 이미 지난 시작 시간 차단 (서버 가드, 코드 반영: 3b25572)
 - 사용 시간: 30분 단위 입력 → leave_requests.requestedMinutes. 유형 선택 시 유형의 defaultMinutes가 프리필(이후 30분 단위 조정 가능, 코드 반영: 04b1b82) — 승인 컴포넌트도 유형 기준 사용 시간으로 초기화
 - 종료 시간 미리보기: 시작 시간 + 사용 시간으로 자동 계산 표시
• 규칙: 신청 시간 = 차감량(이후 정책 변경돼도 과차감 금지) / 같은 날 복수 신청 가능(시간대 겹침 불가, 합계 ≤ 1일 소정근로시간) / 같은 날 일 단위 휴가와 병존 불가 — 서버 가드 존재
leave_requests.startTime / requestedMinutes (migration 1855)신설 (코드 반영: 2577546) · defaultMinutes 프리필(04b1b82) · 세부 UI 배치 TBU
30사유Textarea• 휴가 사유 자유 입력. "사유 (선택)", placeholder "휴가 사유"
• 입력 → leave_requests.reason 채움
leave_requests.reason
ㄴ31사유 프리셋Button group• 사유 5종 원문 "개인 사정" / "병원 진료" / "가족 행사" / "건강 회복" / "여행"
• 클릭 → textarea에 해당 문구 삽입(자유편집 병행)
leave_requests.reasoncensus 원문 프리셋
40신청하기Button• 폼을 제출해 휴가 신청을 생성하는 버튼
• 클릭 → 즉시 제출(컨펌 없음), 성공: leave_requests(status=pending) 생성 + 내역 최상단 "대기" 행 추가 + 상태 칩 카운트 증가 + 모달 닫힘 → 관리자 결재([[HBAD251_연차 현황·결재]]) 인입
 - 서버 가드: 기간 전체 공휴일 거절(425fac8) / 당일 지난 시작 시간 차단(3b25572) / 유형별 허용 팀 제한 — 본인 소속 팀 미허용 시 신청 차단(7e16a37)
leaveRequest.request({leaveType, startDate, endDate, reason}) (시간 단위 유형 시 startTime·requestedMinutes 추가) — leaveType = org 커스텀 사유 id (enum 아님). 커스텀 사유 설정 조직에서 legacy enum(ANNUAL 등) 전달 시 "Invalid leave type" throw (TC 확정: 01:1.3 leave / leave-request-custom-reason.test.ts)코드 반영: 2577546·425fac8·3b25572·7e16a37(팀 제한)·04b1b82(프리필) · TBU: 제출 실패 UI(무반응이면 버그 후보) / 유형·날짜 미입력 시 제출 차단 여부

연동·플로우

  • 신청 흐름: STEP1 [신청] FAB("새 신청") → 휴가 신청 폼 "신청하기"(leaveRequest.request) → leave_requests(status=pending) 생성 → 내역 최상단 "대기" 행 추가·상태 칩 카운트 증가 → STEP2 [결재] 관리자 결재([[HBAD251_연차 현황·결재]]) 승인/반려/취소 → 상태 배지·필터 카운트 갱신. 직원 측 내역 행은 비인터랙티브(상세·취소 없음).
  • 연차 산정: 부여(leave_grants.grantedDays) − 승인 사용일수 = 잔여. 부여 기준은 Admin 연차 부여 기준([[HBAD262_연차 부여 기준]]) 소관. 모델 변경: 잔여 계산이 불변 원장(LeaveLedgerEntry, append-only) 기반으로 전환됨 (코드 반영: 318f7e1 — 상세 [[HBAD262_연차 부여 기준]]).
  • 부수 변경 (코드 반영: 2577546 본문): 외근 신청 사유가 선택 입력으로 전환, 연장·외근·문서 모달 문구 정비 — 해당 모달은 [[HBEA420]] 소관(본 문서 외).

권한 / 상태 요약

시나리오트리거결과비고
CASE01 [폼 제출 성공]"신청하기"대기 행 추가 + 칩 카운트 증가self scope
CASE02 [승인]관리자 결재상태 배지 "승인", 연차 차감[[HBAD251_연차 현황·결재]]
CASE03 [반려]관리자 결재 (사유 필수)상태 배지 "반려"반려 사유 placeholder "반려사유를 입력해 주세요."
CASE04 [취소]관리자/TM 경로상태 배지 "취소"직원 self-cancel 없음
조건3 [EMPLOYEE]내역 행 클릭무반응 (비인터랙티브)상세·취소 경로 없음

Open Question

  • 유형 커스텀 문자열("ㅅㄷㄴㅅ")이 Admin 유형 미선택 시 자유입력인지, 테스트 데이터 잔재인지. TBU.해소: leaveType은 org 설정 커스텀 휴가 사유(organization.updateSettings({leaveReasons:[{id, name, hours, deductionDays}]}))의 id를 수용하며, picker는 auth.getCurrentUser().leaveReasons를 소비. "ㅅㄷㄴㅅ" 등은 org 커스텀 사유 name (TC 확정: leave-request-custom-reason.test.ts).
  • 반차(isHalfDay/halfDayPeriod) 폼 노출 여부 및 잔여 차감 단위(0.5일). TBU.
  • 필수값 미입력·제출 실패 시 폼 동작(차단/토스트/무반응). 라이브 미검증. TBU.