Skip to content

직원 추가 패널 (AddEmployeePanel) ​

직원을 새로 만드는 폼입니다. 좌측 카테고리를 자유롭게 오가며 채우고, 빨간 점이 남아 있으면 저장이 막힙니다. 화면은 패널 모달 위에 뜹니다.

Alpine 컴포넌트 · Web · 진행 중(WIP) · 문서 포맷 시범 적용

Interactive playground ​

옵션을 바꾸면 미리보기와 코드가 함께 바뀌어요.
미리보기 테마

기본정보

이 자리에 해당 카테고리의 입력 폼이 옵니다. 지금 카테고리에 필수값이 비어 있습니다.

실제 파셜의 좌측 메뉴 마크업 그대로예요 — 메뉴를 눌러 카테고리를 옮겨 보세요. 단계 게이트가 없어 어디로든 바로 갈 수 있습니다. 빨간 점은 그 카테고리에 필수값이 비어 있다는 뜻이고, 하나라도 남아 있으면 저장이 막힙니다. (오른쪽 입력 폼은 재현하지 않았습니다 — 999줄이고, 문서가 정해야 할 것은 메뉴 쪽 계약입니다.)
<!-- 위저드는 앱이 마운트하고, 좌측 메뉴 마크업은 파셜이 그린다 -->
<div x-data="addEmployeePanel({ onComplete: (id) => loadEmployees() })">
  <!-- include "../../packages/components/html/add-employee-panel.html" -->
</div>

// 빨간 점은 이 한 줄이 정한다 — 카테고리별 필수값 검사
addCatValid(id) {
  if (id === "basic")  return this.addBasicValid();
  if (id === "hr")     return this.addHrValid();
  if (id === "cert")   return this.addCertValid();
  return this.addContractValid();
}

// 지금 상태: basic · hr 에 점이 뜨고, 저장 버튼이 막힌다

폼 전체는 재현하지 않았습니다

입력 마크업만 999줄입니다. 문서가 정해야 할 것은 입력 칸이 아니라 좌측 메뉴의 계약 — 자유 이동과 필수 누락 표시 — 이라서 그쪽만 실제 마크업으로 옮겼습니다.

규칙 (강제) · 코딩 에이전트용

AddEmployeePanel 강제 규칙. 상세는 아래 본문.

마크업 / 마운트

  • x-data="addEmployeePanel({ onComplete })" + packages/components/html/add-employee-panel.html include. 폼 마크업을 손으로 짓지 마세요.
  • 카드는 .ds-modal ds-modal--panel — 모달 3종 중 패널 모달입니다.

⚠️ 트랩 ① 단계 게이트가 없습니다 — 순서를 강제하지 마세요 네 카테고리(기본정보 · 인사정보 · 자격증·면허 · 근로계약)는 자유 이동입니다. 「다음」으로 넘기는 단계형이 아니에요. 대신 각 카테고리에 빨간 점으로 미완성을 표시하고, 하나라도 남으면 저장을 막습니다. 이 방식을 단계형으로 「정리」하지 마세요.

⚠️ 트랩 ② 저장이 막히는 이유가 화면 밖에 있을 수 있습니다attemptSaveEmployee() 는 네 카테고리 전부를 봅니다. 그래서 지금 보고 있는 화면은 멀쩡한데 저장이 안 되는 상황이 정상적으로 생깁니다 — 점이 어디 떠 있는지가 유일한 단서이므로, 저장 버튼 옆에 이유를 적지 않는 한 점을 없애면 안 됩니다.

⚠️ 트랩 ③ 빈 값은 빈 문자열로 보내지 말고 빼세요 선택 항목을 안 채우면 "" 를 보내는 게 아니라 키 자체를 payload 에서 뺍니다(테스트가 이걸 고정하고 있습니다). 주민번호도 13자리가 유효할 때만 보냅니다. 근태 방식은 조직 기본값 그대로면 빼고, 사용자가 바꿨을 때만 넣습니다.

⚠️ 트랩 ④ ESC 는 위에서부터 하나씩만패널 폼 패턴 → ESC 층의 공유 규칙입니다 — 맨 앞에서 권한 팝업에 양보하고, 레이어를 하나씩 닫고, 막 닫은 뒤 500ms 는 패널을 닫지 않습니다. 레이어 순서도 그 페이지에 있습니다.

⚠️ 트랩 ⑤ 입력한 걸 조용히 날리지 마세요 X · 취소 · ESC 는 전부 attemptClosePanel() 를 지나갑니다. 손대지 않은 폼은 바로 닫히고, 뭔가 입력했으면 저장확인이 뜹니다(열 때 찍어 둔 closeBaseline 스냅샷과 비교). 성공 화면에서는 잃을 게 없으므로 바로 닫힙니다.

  • 다크 모드(2026-10-19) — 공용 add-employee-panel.html 의 팔레트 유틸 45곳을 시멘틱으로 옮겼다(카테고리 내비 면 · 글자 · 필수 점 · 입력 라벨). 남은 1곳은 의도된 bg-neutral-900(어두운 띠). 이 파셜은 관리자 앱이 쓰므로 다크는 개발 · QA 때 localStorage hereby.theme.admin-qa = dark 로만 본다. 확인: Playground 「미리보기 테마 → 다크」.

무엇을 만드나 ​

기존 조직에 직원 한 명을 추가합니다. 기관과 첫 계정을 함께 만드는 RegisterForm 과 다릅니다.

네 카테고리로 나뉩니다.

카테고리담는 것
기본정보성·이름 · 사번 · 이메일 · 전화 · 생년월일 · 주민번호 · 주소
인사정보소속(팀·역할) · 입사일 · 고용형태 · 근무 그룹 · 근태 방식 등
자격증·면허 등록자격 유형별 행 (선택)
근로계약급여·호봉 등 계약 항목

단계형이 아닙니다 ​

「다음 → 다음」으로 넘기는 위저드가 아니라 아무 카테고리로나 바로 갈 수 있습니다. 대신 미완성을 빨간 점으로 알립니다.

점을 없애면 저장이 왜 막히는지 알 방법이 사라집니다

저장 검사는 보고 있지 않은 카테고리까지 봅니다. 예를 들어 자격증 등록을 켜 놓고 유형을 안 고르면, 기본정보 화면에 있어도 저장이 막힙니다. 그때 화면에는 아무 문제가 없어 보입니다 — 점이 유일한 단서예요.

닫기 가드 ​

X · 취소 · ESC 가 전부 같은 길(attemptClosePanel())로 가고, 손대지 않은 폼은 바로 닫히고 입력했으면 저장확인이 뜹니다. 두 패널이 공유하는 규칙이라 흐름도는 패널 폼 패턴 → 닫기 가드에 있습니다.

ESC 층 ​

패널 위 레이어를 위에서부터 하나씩만 닫고, 맨 앞에서 권한 팝업에 양보합니다. 순서와 500ms 유예는 패널 폼 패턴 → ESC 층에 있습니다.

카테고리별 필수값 ​

addCatValid(id) 가 넷으로 갈라 검사하고, 하나라도 false 면 저장이 막힙니다.

기본정보 — 성·이름은 필수, 사번은 직접 입력 또는 자동 생성 중 하나.

항목규칙
성 · 이름필수
사번입력하거나 자동 생성을 켜야 함
이메일선택이지만, 입력했으면 형식 검사
생년월일선택. 조직 시간대 기준 미래 날짜는 차단
주민번호선택. 13자리를 다 채웠을 때만 체크섬 검사
주소선택. 단 주소를 골랐으면 상세주소까지 필수

주민번호 앞자리는 생년월일에서 자동으로 옵니다

두 패널이 같은 유틸(utils/rrn.ts)을 씁니다 — 파생·자릿수·체크섬 규칙은 패널 폼 패턴 → 주민등록번호에 있습니다.

자격증·면허 — 등록을 켜 두고 유형을 하나도 안 고르면 폼 전체가 막힙니다. 「기타」도 정상 선택지이고, 유형이 선택된 행만 셉니다.

저장 payload ​

빈 값을 빈 문자열로 보내지 마세요

안 채운 선택 항목은 "" 가 아니라 키 자체를 뺍니다. 테스트가 이 동작을 고정하고 있습니다.

항목언제 넣나
전화 · 생년월일입력했을 때만
주민번호13자리가 유효할 때만. 부분 입력·체크섬 오류면 뺌
주소street · postalCode · detail 구조로. 아무것도 안 고르면 통째로 뺌
근태 방식조직 기본값과 다를 때만. 그대로면 뺌

주소 검색 ​

Daum 우편번호 서비스를 lazy 로드하는 공유 흐름입니다 — 규칙은 패널 폼 패턴 → 주소 검색에 있습니다.

마운트 ​

html
<div x-data="addEmployeePanel({
  onComplete: (createdId) => {
    loadEmployees();
    $store.toast.success('직원을 추가했습니다');
  },
})">
  <!-- include "../../packages/components/html/add-employee-panel.html" -->
</div>

onComplete 는 만들어진 직원 id 를 받습니다(못 받으면 undefined). 예전 시그니처(() => …)와 호환되므로 안 써도 됩니다.

여는 법 ​

showAddEmployee 를 켭니다. 앱에서는 보통 버튼이 그 값을 직접 만집니다.

html
<button @click="$data.showAddEmployee = true">직원 추가</button>

함께 필요한 것 ​

  • hereby 스토어 — 문구(t()), sdk, 조직 시간대(생년월일 미래 검사에 씁니다).
  • 참조 데이터 — 팀 · 역할 · 근무 형태 · 근무 그룹 · 자격 유형 · 호봉. 컴포넌트가 직접 불러옵니다(fetchAllPages).
  • 네트워크 — 주소 검색이 외부 CDN 스크립트를 lazy 로드합니다.

API ​

addEmployeePanel(opts) Alpine.data factory.

Prop타입설명
onComplete(createdEmployeeId?: string) => void생성 성공 후 호출. id 를 못 받으면 undefined

주요 상태 ​

멤버설명
showAddEmployee패널 열림
addEmployeeCat · addEmployeeCats지금 카테고리 · 카테고리 목록(basic hr cert contract)
addEmployeeForm폼 값 전체
addFormErrors · rrnError중복 검사 오류(email · phone · employeeNumber) · 주민번호 오류
showCloseConfirm · closeBaseline저장확인 · 열 때 찍은 폼 스냅샷
notice공용 알림 팝업 { show, tone, message } — alert() 대체
previewOpen · addrSearchOpen · shiftRefOpen패널 위 레이어들
topLayerClosedAt레이어를 막 닫은 시각 — ESC 연타 유예(500ms) 키
dataReady참조 데이터 로드 완료

주요 메서드 ​

멤버설명
addCatValid(id)카테고리별 필수값 검사 → 빨간 점 표시의 근거
attemptSaveEmployee()네 카테고리 전부 통과 + 중복 없음일 때만 저장
saveNewEmployee()실제 생성 호출. 빈 선택 항목은 payload 에서 뺌
attemptClosePanel()손대지 않았거나 성공 화면이면 바로 닫고, 아니면 저장확인
escapeClose()ESC — 권한 팝업 양보 → 레이어 하나 → 패널
syncRrnFromBirth() · onResidentNumberInput()주민번호 앞자리 파생 · 뒷자리 입력 정리와 체크섬
showNotice(msg, tone) · closeNotice()공용 알림 팝업

사용처 ​

앱onComplete 에서 하는 일
admin목록·미배정·통계 갱신, 팀 구성 새로고침, 방금 추가 표시, 토스트
hr-systememployee-saved 커스텀 이벤트 발사

단일 출처 ​

파트파일
Alpine 컴포넌트packages/components/src/components/AddEmployeePanel.ts
HTML partialpackages/components/html/add-employee-panel.html
동작 명세(테스트)AddEmployeePanel.test.ts — 이 페이지 규칙 대부분이 여기 고정돼 있습니다
ESC 양보utils/esc-guard.ts

검토 필요 사항 ​

  • 저장이 막힌 이유를 저장 버튼 근처에서 알 수 없습니다. 빨간 점은 좌측 메뉴에만 있어서, 긴 폼을 스크롤하다 저장을 누르면 왜 안 되는지 바로 안 보입니다. 버튼 옆에 「미완성: 자격증·면허」 같은 한 줄을 둘지 검토가 필요합니다. 두 패널이 함께 안고 있는 것(알림 팝업 자체 구현 · 주소 검색 로더 세 벌 · 외부 CDN 의존 · 수정 패널과 규칙이 갈리는 것)은 패널 폼 패턴 → 검토 필요 사항에 모아 뒀습니다.

변경 이력 ​

날짜내용
2026-09-09본문의 「위저드」 표기를 **「패널」**로 맞췄습니다(컴포넌트는 73fc32b0 에서 이미 개명). ⚠️ 단계형이 아닙니다의 「넘기는 위저드가 아니라」는 일반명사라 그대로 둡니다 — 여기서 바꾸면 뜻이 정반대가 됩니다
2026-09-09첫 줄이 이 컴포넌트를 「패널 모달입니다」로 규정하고 있어 「폼」으로 바로잡고, 담기는 그릇은 패널 모달 링크로 넘겼습니다. 모달은 이 컴포넌트의 정체가 아니라 뜨는 방식이라, 사이드바에서도 「컴포넌트 — 폼」에 있습니다
2026-09-09규칙 블록의 ESC 트랩이 패널 폼 패턴 → ESC 층의 공유 규칙을 다시 쓰고 있어 직원 수정처럼 가리키기만 하도록 줄였습니다. 함께 적혀 있던 「레이어가 넷」은 다섯이 맞습니다 — escapeClose() 가 「근무 참고」까지 처리하는데 그 하나가 빠져 있었습니다
2026-09-08AddEmployeePanel 페이지 신규 작성 — 좌측 카테고리 메뉴 Playground(카테고리 × 완료 여부 4축) + 모아보기 4상태. 닫기 가드·ESC 층 다이어그램, 카테고리별 필수값·저장 payload 규칙 포함. 폼 마크업(999줄)은 재현하지 않았습니다