직원 수정 패널 (EditEmployeePanel)
이미 있는 직원을 고치는 폼입니다. 추가 패널과 같은 패널 모달 셸 위에 뜨지만 계약이 다릅니다 — 카테고리가 5개이고, 빨간 점이 없고, 바뀐 것만 보냅니다.
Alpine 컴포넌트 · Web · 진행 중(WIP) · 문서 포맷 시범 적용
Interactive playground
기본정보
이 자리에 해당 카테고리의 입력 폼이 옵니다.
<div x-data="editEmployeePanel({ onSaved: () => loadEmployees() })">
<!-- include "../../packages/components/html/edit-employee-panel.html" -->
</div>
// 카테고리 목록 — ⚠️ 추가 위저드와 id 가 다르다
// 추가: ["basic", "hr", "cert", "contract"]
editEmployeeCats: ["basic", "hr", "certification", "contract", "account"],
// 이 카테고리는 부모 '저장' 버튼(persistBasicEdits)이 **바뀐 것만** 보낸다수정 위저드 — 카테고리 5개. account(계정·로그인 보안)가 더 있고 점이 없습니다
기본정보
이 자리에 해당 카테고리의 입력 폼이 옵니다.
추가 위저드 — 카테고리 4개 + 빨간 점. 같은 셸인데 계약이 다릅니다
기본정보
이 자리에 해당 카테고리의 입력 폼이 옵니다. 지금 카테고리에 필수값이 비어 있습니다.
계정·로그인 보안 — 비밀번호는 부모 저장 버튼이 아니라 별도 경로로 저장됩니다
계정·로그인 보안
이 자리에 해당 카테고리의 입력 폼이 옵니다.
cert, 수정은 certification 입니다. 한쪽 규칙을 고치면서 다른 쪽을 같이 안 고치기 쉬운 구조예요(검토 필요 사항). 폼 전체는 재현하지 않았습니다
입력 마크업만 1,129줄입니다. 문서가 정해야 할 것은 입력 칸이 아니라 추가 패널과 무엇이 다른가라서, 좌측 메뉴를 나란히 놓는 데 집중했습니다.
규칙 (강제) · 코딩 에이전트용
EditEmployeePanel 강제 규칙. 상세는 아래 본문.
마크업 / 마운트
x-data="editEmployeePanel({ onSaved })"+packages/components/html/edit-employee-panel.htmlinclude.- 카드는
.ds-modal ds-modal--panel— 추가 패널과 같은 패널 셸입니다.
⚠️ 트랩 ① 추가 패널과 카테고리 id 가 다릅니다 추가는 ["basic", "hr", "cert", "contract"], 수정은 ["basic", "hr", "certification", "contract", "account"] 입니다 — cert ↔ certification. 한쪽 코드를 복사해 오면 조용히 안 맞습니다.
⚠️ 트랩 ② 이 패널에는 빨간 점이 없습니다추가 패널의 「필수 누락 점」은 여기 없습니다. 이미 저장된 직원을 고치는 화면이라 「필수 누락」이 성립하지 않기 때문입니다. 추가 쪽 규칙을 그대로 옮기지 마세요.
⚠️ 트랩 ③ 저장은 전체가 아니라 diff 입니다persistBasicEdits() 가 바뀐 것만 보냅니다 — 주소는 diff 로, 근로계약은 upsert 로 처리합니다. 폼 전체를 통째로 보내도록 「단순화」하면 손대지 않은 필드가 덮어써집니다.
⚠️ 트랩 ④ 비밀번호는 저장 버튼을 타지 않습니다account(계정·로그인 보안)의 비밀번호 변경은 별도 저장 경로입니다. 부모 「저장」 버튼(persistBasicEdits)과 같은 흐름이 아니에요 — 하나로 합치지 마세요.
⚠️ 트랩 ⑤ 사번을 바꾸면 로그인 ID 가 함께 바뀝니다employeeNumberChanged() 가 true 면 그 안내를 반드시 띄웁니다. 입력 중에는 본인을 제외한 중복 검사가 실시간으로 돌고, [수정] 로 확정하기 전까지는 저장이 막힙니다.
⚠️ 트랩 ⑥ ESC 는 위에서부터 하나씩만패널 폼 패턴의 공유 규칙입니다 — 맨 앞에서 권한 팝업에 양보하고, 레이어를 하나씩 닫고, 막 닫은 뒤 500ms 는 패널을 닫지 않습니다.
- 다크 모드(2026-10-19) — 공용
edit-employee-panel.html의 팔레트 유틸 101곳을 시멘틱으로 옮겼다(카테고리 내비 · 섹션 카드 · 라벨 · 안내 박스 · 관리자 확인 미니 모달 면bg-container). 남은 3곳은 브랜드 주황 테두리 · 글자. 인라인style의 팔레트 색 2곳도 토큰으로. 확인: Playground 「미리보기 테마 → 다크」.
추가 패널과 무엇이 다른가
같은 패널 셸을 쓰지만 계약이 셋 다릅니다. 한쪽만 고치기 쉬운 구조라 먼저 봅니다.
| 추가 | 수정 (이 페이지) | |
|---|---|---|
| 카테고리 | 4개 — basic hr cert contract | 5개 — basic hr certification contract account |
| 필수 누락 빨간 점 | 있음 (addCatValid) | 없음 |
| 저장 | 폼 전체를 생성 payload 로 (빈 값은 키를 뺌) | 바뀐 것만(persistBasicEdits — 주소 diff · 계약 upsert) |
| 비밀번호 | 없음 | 별도 저장 경로 (account) |
| 사번 | 입력 또는 자동 생성 | 바꾸면 로그인 ID 도 바뀜 → 확정 절차 |
| 미리보기 | 입력한 값 | 전체 표시 + 바뀐 항목 강조 |
| 파일 크기 | 1,185줄 + 999줄 | 1,783줄 + 1,129줄 |
왜 빨간 점이 없나
이미 저장된 직원을 고치는 화면이라 「아직 안 채운 필수값」이라는 상태가 없습니다. 저장돼 있는 값에서 출발하므로, 추가 패널처럼 「어디가 비었는지」를 메뉴에 표시할 대상이 없습니다.
대신 저장이 막히는 경우는 있습니다
사번을 바꿔 놓고 확정하지 않으면 저장이 막힙니다. 이건 「필수 누락」이 아니라 확정 절차라 메뉴 점이 아니라 그 자리의 인라인 안내로 알립니다.
사번을 바꾸는 흐름
사번은 로그인 ID 이기도 합니다. 그래서 다른 필드와 달리 확정 단계가 있습니다.
바뀌면 로그인 ID 안내를 반드시 띄우세요
employeeNumberChanged() 가 로드 시점 값과 비교해 true 를 돌려주면, 「로그인 ID 가 함께 바뀐다」 안내가 떠야 합니다. 사용자가 모르고 바꾸면 그 직원이 로그인하지 못합니다.
목록 로드에 실패하면 클라이언트 중복 검사는 건너뜁니다 — 서버가 저장 시 최종 검증하므로 화면을 막지는 않습니다.
미리보기
추가 패널의 미리보기가 「입력한 값」을 보여 주는 것과 달리, 여기는 전체를 보여 주고 바뀐 항목을 강조합니다. 무엇을 건드렸는지 저장 전에 확인하는 자리라서 그렇습니다.
ESC · 닫기
추가 패널과 같은 규칙입니다 — 순서와 500ms 유예는 패널 폼 패턴 → ESC 층에, 닫기 가드는 같은 페이지 → 닫기 가드에 있습니다.
카테고리 다섯
| id | 라벨 | 담는 것 |
|---|---|---|
basic | 기본정보 | 성·이름 · 사번(=로그인 ID) · 연락처 · 생년월일 · 주민번호 · 주소 |
hr | 인사정보 | 소속 · 입사일 · 고용형태 등 |
certification | 자격증·면허 | 자격 항목 |
contract | 근로계약 | 급여·계약 항목 (upsert) |
account | 계정·로그인 보안 | 비밀번호 변경 · SMS 로그인 허용 |
account 를 따로 뺀 이유는 코드 주석에 있습니다 — 인적사항과 성격이 다르기 때문입니다.
저장 경로가 둘입니다
둘을 합치지 마세요
비밀번호는 부모 저장 버튼을 타지 않습니다. 테스트도 password mode (separate save path) 로 이 분리를 고정하고 있습니다.
주민번호 · 생년월일
두 패널이 같은 유틸(utils/rrn.ts)을 씁니다 — 파생·자릿수·체크섬 규칙은 패널 폼 패턴 → 주민등록번호에 있습니다. 이 패널의 테스트 이름이 one-way autofill 인 것도 그 「한 방향」 규칙을 가리킵니다.
주소
추가 패널의 흐름을 그대로 이식한 것입니다 — 규칙은 패널 폼 패턴 → 주소 검색에 있습니다. 저장할 때 address → street, postalCode, addressDetail → detail 로 옮겨 담습니다.
마운트
<div x-data="editEmployeePanel({
onSaved: () => {
loadEmployees();
$store.toast.success('직원 정보를 저장했습니다');
},
})">
<!-- include "../../packages/components/html/edit-employee-panel.html" -->
</div>함께 필요한 것
hereby스토어 — 문구(t()),sdk, 조직 시간대.- 직원 목록 — 사번 중복 검사에 씁니다. 컴포넌트가 직접 불러오고(
fetchAllPages), 실패해도 화면을 막지 않습니다. - 네트워크 — 주소 검색이 외부 CDN 스크립트를 lazy 로드합니다.
API
editEmployeePanel(opts) Alpine.data factory.
| Prop | 타입 | 설명 |
|---|---|---|
onSaved | () => void | 저장 성공 후 호출 |
주요 상태
| 멤버 | 설명 |
|---|---|
editingEmployee | 지금 고치고 있는 직원 (없으면 폼이 안 뜸) |
editEmployeeCat · editEmployeeCats | 지금 카테고리 · 목록(5개) |
editForm · editFormErrors | 폼 값 · 인라인 오류 |
originalEmployeeNumber · employeeNumberValidated | 로드 시점 사번 · 확정 여부 |
addrSearchOpen · daumPostcodePromise | 주소 검색 레이어 · 스크립트 로드 프로미스 |
주요 메서드
| 멤버 | 설명 |
|---|---|
persistBasicEdits() | 바뀐 것만 저장 — 주소 diff · 계약 upsert |
saveEmployee() | 저장 진입점. 사번 미확정이면 막힘 |
onEmployeeNumberInput() | 확정 해제 + 목록 로드 + 실시간 중복 검사 |
checkEmployeeNumberDup() | 본인 제외 중복 검사 → 인라인 오류 |
confirmEmployeeNumber() | [수정] 클릭 — 이상 없으면 확정 |
employeeNumberChanged() | 로드 시점 값과 다른가 → 로그인 ID 안내 표시 근거 |
onBirthDateChange() | 생년월일 → 주민번호 앞자리 한 방향 채움 |
previewRows() | 미리보기 — 전체 표시 + 변경 강조 |
escapeClose() | ESC — 권한 팝업 양보 → 레이어 하나 → 패널 |
사용처
| 앱 | onSaved 에서 하는 일 |
|---|---|
| admin | 목록·미배정·통계 갱신, 토스트 |
| hr-system | employee-saved 커스텀 이벤트 발사 |
단일 출처
| 파트 | 파일 |
|---|---|
| Alpine 컴포넌트 | packages/components/src/components/EditEmployeePanel.ts |
| HTML partial | packages/components/html/edit-employee-panel.html |
| 동작 명세(테스트) | EditEmployeePanel.test.ts |
| ESC 양보 | utils/esc-guard.ts |
검토 필요 사항
두 패널이 함께 안고 있는 것(코드가 각자인 것 · 카테고리 id 어긋남 · 주소 검색 로더 세 벌 · 외부 CDN 의존)은 패널 폼 패턴 → 검토 필요 사항에 모아 뒀습니다.
변경 이력
| 날짜 | 내용 |
|---|---|
| 2026-09-09 | 본문의 「위저드」 표기를 **「패널」**로 맞췄습니다 — 첫 줄의 「추가 위저드」 포함(컴포넌트는 73fc32b0 에서 이미 개명). 「패널」은 받침이 있어 조사도 함께 바뀝니다(와→과 · 가→이) |
| 2026-09-09 | 첫 줄이 이 컴포넌트를 「패널 모달입니다」로 규정하고 있어 「폼」으로 바로잡고, 담기는 그릇은 패널 모달 링크로 넘겼습니다(직원 추가와 같은 정리) |
| 2026-09-08 | EditEmployeePanel 페이지 신규 작성 — 좌측 메뉴 Playground + 모아보기에 추가 패널과 나란히 비교. 사번 변경 흐름·저장 경로 둘 다이어그램, 추가 패널과의 차이표 포함. 폼 마크업(1,129줄)은 재현하지 않았습니다 |