尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
작업 승인 → 게이트 안전검증 → 작업이력·자격 관리를 하나로 묶은 산업 현장 출입통제 웹입니다. 2026 미래내일 일경험 지원사업 — AI·로봇 안전제어 솔루션 챌린지 4기 팀 Ctrl+Z · 이 저장소는 웹(프론트엔드 + 백엔드 + DB) 범위입니다. 키오스크는 NVIDIA Jetson Orin에서 별도 구현되며, 이 저장소와는 API 계약으로만 만납니다. 종이 작업허가서와 단순 출입카드는 "확인"에서 멈추고 출입 자체에는 개입하지 않습니다. 이 시스템은 세 가지를 시스템이 강제합니다. 1. 팀장 승인 없이는 키오스크에 작업이 뜨지 않습니다 → 입장 시도 자체가 불가능 2. 자격이 만료되면 검증 단계에 들어가기 전에 차단됩니다 3. 모든 인증·검증·승인이 자동으로 기록되어 사후 추적이 됩니다 /dashboard/sessions/[id] 는 관제에서 작업 제목을 눌러 들어가는 세션 상세입니다. 젯슨 문열림 시뮬레이션이 들어갈 자리를 비워둔 틀입니다.檢視完整 README ↗
작업 승인 → 게이트 안전검증 → 작업이력·자격 관리를 하나로 묶은 산업 현장 출입통제 웹입니다.
2026 미래내일 일경험 지원사업 — AI·로봇 안전제어 솔루션 챌린지 4기 팀 Ctrl+Z · 이 저장소는 웹(프론트엔드 + 백엔드 + DB) 범위입니다. 키오스크는 NVIDIA Jetson Orin에서 별도 구현되며, 이 저장소와는 API 계약으로만 만납니다.
종이 작업허가서와 단순 출입카드는 "확인"에서 멈추고 출입 자체에는 개입하지 않습니다. 이 시스템은 세 가지를 시스템이 강제합니다.
| 코드 | 경로 | 화면 | 누가 보나 |
|---|---|---|---|
| W1 | /login | 로그인 | 전원 |
| — | /signup | 가입 신청 | 계정 없는 사람 (인증 없이 열림) |
| W2 | /requests/new | 작업 승인 요청 | 작업자 · 팀장 |
| W3 | /approvals | 팀장 승인함 | 팀장 |
| W4 | /dashboard | 관제 대시보드 | 전원 |
| W5 | /me | 마이페이지 | 작업자 · 팀장 |
| — | /notes | 작업장별 특이사항 | 전원 |
| — | /admin | 계정 관리 | 안전관리자 |
/dashboard/sessions/[id] 는 관제에서 작업 제목을 눌러 들어가는 세션 상세입니다.
아래쪽에 게이트 3D 시뮬레이션(Unity WebGL, center-web/public/safety-gate-3d/)이 붙어 있어,
서버가 판정한 검증·문 열림·입장을 그림으로 다시 보여줍니다. 판정은 서버가 하고 3D 는 재생만 합니다.
| 구분 | 선택 | 이유 |
|---|---|---|
| 프레임워크 | Next.js 16 (App Router) | Route Handler가 백엔드 역할. 서버와 화면을 한 프로젝트에 |
| 언어 | TypeScript · React 19 | 권한·상태를 타입으로 좁혀 잘못된 값을 못 만들게 |
| 스타일 | CSS Modules + 디자인 토큰 | 경계선을 border가 아니라 inset box-shadow로 그리는 스펙이라 유틸리티 클래스와 안 맞음 |
| DB | Firebase Firestore | 실시간 구독이 관제 화면에 그대로 맞음 |
| 인증 | Firebase Auth + 커스텀 클레임 | 역할을 토큰에 박아 서버가 검증 |
| 실시간 | Server-Sent Events | 방향이 서버→클라이언트 한쪽뿐이라 WebSocket 불필요 |
클라이언트는 DB를 직접 만지지 않습니다.
Firestore 보안 규칙은 전부 잠겨 있고(center-web/firestore.rules), 서버만 Admin SDK로 접근합니다.
규칙에 로직을 흩어놓으면 나중에 무엇이 무엇을 막는지 추적이 안 됩니다.
권한은 토큰에서 읽습니다.
역할은 Firebase Auth 커스텀 클레임에 있고, 서버가 verifyIdToken으로 확인한 값만 씁니다.
요청 본문의 역할은 믿지 않습니다.
파생값은 저장하지 않습니다.
자격의 유효/임박/만료는 expiresOn에서, 연차는 hiredOn에서 매번 계산합니다.
상태를 컬럼으로 굳히면 배치 없이는 반드시 썩습니다.
젯슨은 관찰만 보고하고 상태를 주장하지 않습니다.
카드 UID를 09:04:12에 읽었다 ✅ / 인원 충족됐으니 문 열어 ❌
상태 전이와 화면 문구는 서버가 계산해 응답으로 내려줍니다.
계약 전문은 center-web/lib/gate-contract.ts 한 파일입니다.
cd center-web
npm install
cp .env.local.example .env.local # 값을 채워야 동작합니다
npm run dev
.env.local은 저장소에 없습니다. Firebase 콘솔에서 직접 받아야 합니다 —
웹 앱 config 6개(프로젝트 설정 → 내 앱)와 서비스 계정 키(프로젝트 설정 → 서비스 계정).
어떤 값이 어디서 오는지는 .env.local.example 주석에 적어뒀습니다.
node --env-file=.env.local scripts/check-firebase.mjs # 연결 확인
node --env-file=.env.local scripts/seed.mjs # 컬렉션 생성 + 시드 + 계정 발급
node --env-file=.env.local scripts/reset-requests.mjs # 시연 전 초기화
seed.mjs는 여러 번 돌려도 안전합니다. 진행중 세션은 실행 시점 기준으로 다시 만들어지므로,
시연 직전에 한 번 돌리면 관제 화면의 경과 시간이 그럴듯해집니다.
seed.mjs를 돌리면 Auth 계정이 다시 만들어져 열어둔 브라우저는 로그아웃됩니다. 다시 로그인하면 됩니다.
아이디 = 사번, 초기 비밀번호 = 사번 + 1234
| 사번 | 이름 | 역할 | 비밀번호 |
|---|---|---|---|
| 202533690 | 김병오 | 팀장 (승인자) | 2025336901234 |
| 202533795 | 윤지윤 | 안전관리자 | 2025337951234 |
| 202533872 | 정천호 | 작업자 | 2025338721234 |
| 202633671 | 박상하 | 작업자 | 2026336711234 |
사번은 scripts/seed-data.mjs의 TEAM 한 곳에서만 정의합니다 —
세션·요청·출입기록이 전부 사번을 참조해서, 흩어놓으면 하나 바꿀 때 참조를 빠뜨립니다.
main에 push하면 Vercel이 자동으로 재배포합니다. 따로 할 일이 없습니다.
ctrlz-2026/Ctrl-Center-web (조직 ctrlz-2026)center-web환경변수는 저장소가 아니라 Vercel 대시보드에 있습니다
(Settings → Environment Variables). 로컬 .env.local과 같은 10개입니다.
저장소가 Public인 이유 — Vercel Hobby 플랜은 조직의 Private 저장소를 배포하지 못합니다. 비밀키는
.env.local에만 있고 그 파일은.gitignore에 있어 올라가지 않으므로 공개로 돌렸습니다.
git clone https://github.com/ctrlz-2026/Ctrl-Center-web.git
cd Ctrl-Center-web/center-web
npm install
그다음 .env.local을 직접 넣어야 합니다 — 저장소에 없습니다.
가장 빠른 방법은 쓰던 컴퓨터의 center-web/.env.local을 그대로 복사하는 것이고,
없으면 Firebase 콘솔에서 다시 받습니다(.env.local.example 주석 참고).
node --env-file=.env.local scripts/check-firebase.mjs # 연결부터 확인
npm run dev
Firestore는 클라우드에 있으므로 시드를 다시 돌릴 필요가 없습니다. 데이터가 이미 들어 있고, 컴퓨터가 바뀌어도 그대로 보입니다.
center-web/
app/
(auth)/login/ W1
(auth)/signup/ 가입 신청 (인증 없이 열림)
(app)/requests/new/ W2
(app)/approvals/ W3
(app)/dashboard/ W4
sessions/[id]/ 세션 상세 — 젯슨 시뮬레이션 자리
(app)/me/ W5
(app)/notes/ 작업장별 특이사항
(app)/admin/ 계정 관리 (안전관리자)
icon.svg 파비콘 (ZC 키캡 로고)
api/
me/ · requests/ 웹용 (Firebase ID 토큰)
notes/ 작업장별 특이사항
signup/ 가입 신청 — 유일하게 인증 없이 열린 경로
admin/accounts/ 계정 목록 · 역할/비번/활성
[empNo]/profile/ 자격 · 사원증 · 얼굴등록 · 작업배정
[empNo]/face-template/ 얼굴 등록 상태 · 벡터 파일 올리기 · 삭제
admin/signups/[id]/ 가입 승인·거절
stream/requests/ SSE 실시간
gate/events/ 젯슨 수신구 (기기 키)
gate/[gateId]/ 번들 · 선택된 작업 · 얼굴 등록(사번 확인 · 완료 알림) · 얼굴 벡터 내려받기 (기기 키)
kiosk/[gateId]/ 키오스크 화면용 (작업 선택 · 사원증 태그 · 상태 · 종료)
components/ Button · Badge · Card · DataTable · Field · Logo · TopNav …
lib/
firebase/ client · admin · queries · dashboard · auth-guard · user
gate-contract.ts 젯슨과 맞추는 계약 (이 파일만 넘기면 됨)
types.ts 권한 함수가 여기 한 곳에만 있음
scripts/ 시드 · 초기화 · 연결확인
docs/
backend-design.md 스키마 · API · 실시간 설계 · 결정 기록
screenshots/ 화면 캡처
고칠 때 어디를 보나
| 바꾸고 싶은 것 | 파일 |
|---|---|
| 누가 무엇을 할 수 있나 | lib/types.ts (권한 함수가 여기에만 있음) |
| 색·모서리·여백 | app/tokens/center.css (개별 화면 CSS 말고 여기) |
| 사번·시드 데이터 | scripts/seed-data.mjs |
| 젯슨과의 약속 | lib/gate-contract.ts |
| 왜 그렇게 했는지 | docs/backend-design.md |
동작하는 것 — 로그인·권한 3역할, 회원가입과 관리자 승인, 계정 관리(비밀번호 초기화·역할 변경·비활성화), 자격/사원증/얼굴등록/작업배정 관리, 승인 워크플로우 (Firestore 영속), 실시간 관제, 작업장별 특이사항, 개인별 출입기록, 젯슨 수신구(중복 제거·형식 검증). 전부 배포돼 있습니다.
결정해서 굳힌 것 (배경은 docs/backend-design.md)
selfApproved로 기록에 남깁니다아직인 것
ppeItems.yoloClass가 helmet 외 전부 null.
모델이 못 잡는 항목을 작업코드 필수 보호구에 넣으면 착용하고 서 있어도 영원히 통과 못 합니다.employeeCards가 TEMP-* UID, pending: true
(관리자 콘솔에서 실물 UID를 넣을 수 있게는 해뒀습니다)A~H, J)를 씁니다. 팀장 「작업 기준 설계」
표는 A-3·B-7 형식이지만 뒤 숫자가 인원·시간·위험도 어느 것과도 무관한
임의값이라, 내용은 표 그대로 두고 표기만 줄였습니다(팀 결정 2026-09-01).
대조표는 scripts/seed-data.mjs 의 workCodes 주석 참고. I 는 숫자 1 로
읽혀 건너뜁니다