08 사내 앱 — 차량관리와 ERP 시안
이 페이지 목차
목적만든 순서1 · 법인차량 1판 — 한 장짜리 HTML (8/10 11:55)2 · 같은 날 오후 — AI 산출물을 사내 운영용으로 (8/10 15:50)3 · 문 잠그기 — 익명 인증 · 규칙 · 회사 소유 프로젝트 (8/10 16:30~16:40)4 · 라이트 모드 글자가 안 보이던 문제를 숫자로 재다 (8/10 17:01~17:26, 8/28)5 · 쓰는 사람이 고치기 시작한 나흘 (8/25~8/28)6 · 일곱 날 예약이 하루만 보이던 일 (9/17 08:12~08:19)7 · 직원 명부와 버전 · 공지 (9/17 08:41~08:50)8 · 홈 화면 앱(PWA)과 매일 백업 (9/17 08:59)9 · 배포 길 셋 (8/10 → 9/27)10 · ERP 시안 셋 (8/24)💡 설명함정과 배운 것출처뒷이야기 · 8 / 17단계 · 2026-08-10 ~ 09-27 · 갈래 사내 앱
이전 단계: 07 계산 도구 — pvcalc 와 DWSIM · 다음 단계: 09 회사 자료를 파이스에 — 색인과 MCP 창구
핵심 요약
| 항목 | 내용 |
|---|---|
| 이 단계에서 한 일 | 사내 법인차량의 예약 · 운행 · 정비 · 주유를 기록하는 앱을 8월 10일 하루 안에 HTML 한 장에서 React 판으로 키우고, 같은 날 AI 가 만든 산출물에 섞여 있던 가짜 데이터를 걷어내고 문을 잠갔다. 8월 말 나흘 동안은 직원이 현장에서 불편한 것을 직접 고쳤고, 9월 17일 아침에는 47분 동안 여러 날 예약 · 직원 명부 · 홈 화면 앱 · 매일 백업을 붙였다. 배포 길은 세 가지를 거쳤다(두 번 바뀌었다). ERP 시안 세 장(8/24)은 구축 전략을 그린 시안으로 남았다. |
| 기간 | 2026-08-10 ~ 09-27 (ERP 시안 08-24) |
| 규모 | 차량 앱 커밋 21개(이 PC 사본. 원격에는 9/27 호스팅 이사 2개가 더 있어 23개) · 소스 37파일(TS/TSX 10,928줄 + CSS 340줄) · 1판은 단일 HTML 1,881줄 / ERP 시안 HTML 3개(합 3,238줄 · 228,199바이트) |
| 다룬 도구 | Firestore · React 19 · TypeScript · Vite · Tailwind 4 · PWA · GitHub Actions · GitHub Pages · Cloudflare Workers · Windows 작업 스케줄러 |
| 결과 | 단일 HTML 1,881줄 → React 37파일 · 라이트 모드 하단바 글자 명암비 2.6 → 7.6(기준 4.5) · 직원 명부 63명 연결 · 전체 데이터 매일 07:30 자동 백업 |
| 공개 범위 | 사내(로그인 뒤) — 🔒 운영 데이터(차량번호 · 예약자 · 운행 기록 · 직원 연락처)와 비밀번호 · 프로젝트 식별자는 넣지 않음 |
한 줄 요약 — 작은 업무 하나를 앱 하나로 만들 때 일은 짓는 데보다 걷어내고, 막고, 재는 데 있었다. AI 가 만든 그럴듯한 가짜를 걷어냈고, 열려 있던 문을 막았고, 안 보이던 글자를 숫자로 쟀다.
8단계 · 사내 앱
목적
법인차량은 누가 언제 어디로 가져갔고, 얼마나 탔고, 언제 정비했는지를 한곳에서 봐야 한다. 생산직 직원은 회사 계정이 없다. 그래서 로그인 없이 폰에서도 쓰고, 여럿이 동시에 보고, 값을 지어내지 않는 작은 앱이 필요했다. ERP 시안은 같은 질문을 구매 · 자재 · 원가로 넓힌 것이다. 무엇을 사서 쓰고 무엇을 직접 지을 것인가.
만든 순서
1 · 법인차량 1판 — 한 장짜리 HTML (8/10 11:55)
8월 10일 11:55에 첫 커밋을 남겼다. index.html 파일 하나(1,881줄 · 123,238바이트)와 Firestore(구글의 실시간 데이터베이스)가 전부이고 빌드 과정이 없다. GitHub Pages 에 올리면 1~2분 뒤 반영된다.
- 화면은 홈(운행 중 차량 · 반납 지연 · 엔진오일 교체 알림 · 빠른 등록), 예약(차량 × 7일 주간 현황표), 주행(출발 등록 · 복귀 입력, 계기판 사진), 기록(정비 · 주유), 리포트(집계 · 인쇄) 다섯이다.
- 저장은 Firestore 컬렉션 5개(차량 · 주행 · 정비 · 주유 · 예약)이고, 실시간 구독(
onSnapshot)이라 다른 사람이 입력한 것이 새로고침 없이 보인다. 첨부 사진은 긴 변 1280px JPEG 로 줄여 문서에 직접 넣는다(문서 하나가 1MB 를 넘지 못하기 때문이다). - 이 판의 README 가 스스로 한계를 적었다. 방어가 화면 안의 비밀번호뿐이라, 외부인을 진짜로 막으려면 인증과 데이터베이스 규칙이 필요하다고.
- 배운 것. 빌드 없는 한 장짜리는 만들고 올리기가 가장 가볍다. 대신 비밀을 숨길 곳이 없다. 이 한계가 3번 기록의 숙제가 됐다.
2 · 같은 날 오후 — AI 산출물을 사내 운영용으로 (8/10 15:50)
3시간 55분 뒤인 15:50에 같은 주소로 React 19 · TypeScript · Vite · Tailwind 4 판이 올라왔다(43파일 · +13,905줄). 이 저장소 첫 커밋 메시지 본문의 첫 줄이 「AI Studio 산출물을 사내 운영용으로 정리」다(AI 가 앱 초안을 만들어 준 도구의 결과물을 손봤다는 뜻이다. 어느 회사의 AI Studio 인지는 메시지에 없다). 정리의 대부분은 걷어내기였다.
- 가짜 경로. 출발지와 목적지 이름으로 좌표를 짐작해 사인 곡선 경로를 만들어 저장하던 코드를 지웠다. 지도에 뜨던 경로 · 속도 · 통과 시각이 전부 실제와 무관한 값이었다. 대신 단말의 실제 GPS 를 출발과 복귀 때 한 번씩만 기록한다. 권한을 거부하면 위치 없이 저장하고, 두 점뿐이면 「직선 거리」라고 밝힌다.
- 허구의 협력 정비소 3곳(상호 · 주소 · 전화번호 · 제휴 혜택)을 지웠다. 없는 번호로 전화가 걸리고 없는 할인이 안내되고 있었다.
- 근거 없는 기본값(평균 52 · 최고 82km/h)을 「-」로, Firestore 가 비면 시범 차량 · 운행을 진짜처럼 보여주던 시드 데이터를 제거했다.
- 저장 안정성. 사진을 1280px JPEG 로 줄였다(실측 11.8MB → 552KB, 331ms). 저장이 실패해도 콘솔에만 경고하고 브라우저 저장소에만 남기던 구조를 없애 실패하면 사용자에게 알린다. 한 번 읽고 마는 조회를 실시간 구독으로 바꾸고, 직접 만든 캐시 층은 Firestore 의 오프라인 캐시로 대체했다.
- 고친 것이 다른 것을 깼다. 가짜 경로를 지우자 경로 점이 빈 운행의 지도를 여는 순간 앱 전체가 멈췄다(
Cannot read properties of undefined). 기존 주행 기록은 모두 위치 기록이 없어 반드시 재현됐다. 1시간 16분 뒤(17:06) 고쳤다. - 배운 것. AI 가 만든 산출물에는 그럴듯한 가짜가 진짜처럼 섞여 있다. 기록 앱의 첫 규칙은 값을 지어내지 않는 것이고, 지어낸 값을 지우면 그 값에 기대던 화면이 같이 깨진다.
3 · 문 잠그기 — 익명 인증 · 규칙 · 회사 소유 프로젝트 (8/10 16:30~16:40)
무엇이 열려 있었나. 인증 없이 읽기 · 쓰기 · 삭제가 전부 열려 있었다. 프로젝트 식별자만 알면 밖에서 전사 기록을 지울 수 있는 상태였다. 식별자는 배포되는 JS 안에 반드시 들어가므로 저장소를 공개하든 말든 노출된다.
어떻게 막았나.
- 앱이 시작될 때 익명 토큰을 받고(
signInAnonymously), 토큰을 받은 뒤에만 구독하고 쓴다. 직원은 로그인 화면을 보지 않는다. - 보안 규칙(
firestore.rules)은 인증된 요청만, 그리고 실제로 쓰는 컬렉션 목록 안에서만 읽고 쓰게 한다(처음 5개, 9/17에 명부 컬렉션이 더해져 6개). - 16:40에 앱의 데이터베이스를 회사 소유 Firebase 프로젝트(서울 리전)로 새로 만들어 바꿨다. 옛 프로젝트가 회사 계정 소유가 아니어서 계정이 없어지면 데이터도 같이 사라질 상태였다.
- 공용 진입 화면을 달았다. 직원은 기기당 한 번만 입력하고 구글 계정은 필요 없다(생산직은 사원 계정이 없다). 관리자 비밀번호(삭제 승인)는 진입 비밀번호와 갈랐다. 같으면 전 직원이 삭제 권한을 갖기 때문이다.
한계를 규칙 파일 머리말이 적어 두었다. 이 규칙은 프로젝트 식별자만 알고 붙는 밖의 접근을 막는다. 앱 페이지를 실제로 연 사람은 앱이 토큰을 주므로 막지 못한다. 직원 한 사람 한 사람을 가려내는 통제가 필요하면 구글 로그인과 도메인 제한으로 올려야 한다(이 단계에서는 하지 않았고, 그 뒤 했는지는 확인하지 못했다).
- 배운 것. 화면에 비밀번호 칸을 두는 것은 안내일 뿐 방어가 아니다. 진짜 방어는 서버 쪽 규칙이다. 그리고 규칙이 막는 것과 못 막는 것을 파일 머리에 적어 두면 다음 사람이 과신하지 않는다.
4 · 라이트 모드 글자가 안 보이던 문제를 숫자로 재다 (8/10 17:01~17:26, 8/28)
라이트 모드(밝은 화면)에서 하단 메뉴바의 배경만 어두운 채 남아 글자가 안 보였다(17:01 커밋). 원인은 라이트 모드를 전역 CSS 로 덮어쓰는 방식에 있었다. 색 클래스를 하나씩 열거하는 구조라 새 클래스를 쓸 때마다 빠졌다.
- 투명도가 붙은 변형을 부분 일치로 한 번에 처리하고, 하단바는 속성 셀렉터로 따로 지정했다. 명암비를 쟀다. 라이트 모드 하단바 2.6:1 → 7.6:1(웹 접근성 AA 기준 4.5:1), 다크 모드는 6.8:1 유지.
- 17:26에 두 번째 수리가 이어졌다. 진한 색 버튼 위의 흰 글씨까지 어둡게 덮어쓰던 문제를 고쳤고, 월간 캘린더 3.29 → 5.42 · 알림 센터 1.00 → 6.46 등 화면별 값을 재서 모두 기준 4.5 위로 올렸다.
- 17:26 커밋에는 더보기 화면의 데이터 초기화(주행 · 정비 · 주유 · 예약을 항목별로 또는 한 번에, 차량 정보까지 지우는 버튼은 따로)도 들어갔다. 관리자 비밀번호를 확인한 뒤에만 돌고 지운 건수를 알려 준다.
- 휴대폰의 다크 모드 설정을 따르는 「자동」 테마를 기본값으로 넣었고, 앱으로 돌아올 때 다시 확인한다(해가 지는 사이 전환 이벤트를 놓칠 수 있기 때문이다).
- 8/28에 직원이 같은 계열을 두 번 더 고쳤다. 앱 최상위의
selection:bg-blue-500클래스가 「배경이 파란 요소」로 오인돼 글씨가 전부 흰색으로 강제되던 것, 어두운 그라디언트 패널 안의 글씨가 어둡게 바뀌던 것이다. - 배운 것. 겹겹이 덮어쓰는 보정은 네 번(8/10에 두 번 · 8/28에 두 번) 고쳐도 새 모서리가 나온다. 「눈으로 읽힌다」가 아니라 명암비 숫자로 잰다. (이 값들은 커밋 메시지에 적힌 측정이며 다시 재지는 않았다.)
5 · 쓰는 사람이 고치기 시작한 나흘 (8/25~8/28)
직원 한 명이 8/25 14:44부터 8/28 14:09까지 10개 커밋(8/25 7개 · 8/28 3개)을 올렸다. 현장에서 쓰다 불편했던 것을 직접 고친 기록이다.
- 입력 줄이기. 운전자 이름 자동완성(예약 · 출발 · 정비 · 주유 전체 기록에서 최근 입력 우선), 사용 목적 프리셋(택배 · 우편 · 차량정비 · 은행업무)을 고르면 목적지 자동 입력, 경유지 칸, 「목적지 방문 후 본사로 복귀」 체크. 저장 버튼을 두 번 눌러 운행 기록이 둘 생기던 것도 막았다.
- 틀린 입력 막기. 복귀 입력 때 도착 계기판이 비었거나 출발값 이하이면 저장을 막는다. 차량을 지우면 그 차량의 주행 · 정비 · 주유 · 예약도 함께 지운다(안 그러면 없는 차량을 가리키는 고아 기록이 남았다). 8/28에는 주행 기록 수정 기능이 들어갔다. 정비 · 주유 · 차량은 수정이 되는데 주행만 삭제밖에 안 돼서, 이름 오타 하나를 고치려면 지우고 다시 등록해야 했기 때문이다.
- 차량 현재 위치는 세 번 왔다 갔다 했다. ① 차량 문서에 현재 위치를 저장한다 → ② 저장값을 믿었더니 갱신이 한 번 누락되자 옛 위치에 계속 머문다 → 운행 기록에서 다시 계산한다 → ③ 그랬더니 관리자가 직접 고친 위치가 화면에 안 나온다 → 갱신 시각을 남겨 더 최근에 반영된 쪽을 따른다.
- 이 10개 커밋에는 Claude 공동 저자 표시가 없다(같은 저장소의 다른 작성자 11개 커밋에는 전부 있다).
- 배운 것. 출처가 둘이면 「어느 쪽을 믿을까」 대신 「어느 쪽이 더 최근인가」로 푼다. 한쪽만 우선하면 다른 쪽이 죽는다.
6 · 일곱 날 예약이 하루만 보이던 일 (9/17 08:12~08:19)
계기는 단순했다. 한 차량에 7일짜리 예약이 걸려 있는데 캘린더에는 시작일 하루만 표시돼, 그 기간에 예약하려던 사람이 왜 막히는지 알 수 없었다. 예약을 시작일에만 올려 두던 것을 시작일부터 종료일까지 매일 표시하도록 바꿨다(중간 날은 「종일」, 마지막 날은 「~종료시각」, 날짜가 깨진 데이터에서 무한 반복하지 않도록 상한을 뒀다).
- 종료 시각의 시와 분만 잘라 쓰던 탓에 「시작일 20:00 ~ 17:00」처럼 며칠짜리인지 알 수 없던 표시도 날짜까지 보이게 고쳤다. 중복 예약 경고도 예약자 · 연락처 · 전체 기간 · 며칠짜리인지를 모두 알려 준다.
- 예약 취소는 예약 때 정한 4자리 PIN 이나 관리자 비밀번호 중 하나로 된다. 전에는 PIN 만 허용해서 예약자가 잊으면 관리자도 못 지워 차량이 계속 묶여 있었다. 버튼 이름도 「삭제」에서 「예약 취소」로 바꿨다.
- 7분 뒤 같은 문제가 한 번 더 나왔다. 예약 탭의 캘린더(
CalendarDashboard)만 고쳤는데, 홈 화면은 별도 컴포넌트(DriveCalendar)를 써서 같은 문제가 남아 있었다. 홈에서 보면 기간 중간의 오늘은 진행 중인 예약이 하나도 없는 것처럼 보였다. - 배운 것. 같은 것을 그리는 화면 부품이 둘이면 고칠 때 둘 다 고친다. 사용자가 막히는 이유를 화면이 말해 주지 않으면 그게 버그다.
7 · 직원 명부와 버전 · 공지 (9/17 08:41~08:50)
- 예약자를 이름 글자로만 받던 것을 직원 명부에서 부서 → 이름을 고르면 직급 · 연락처가 채워지게 바꿨다. 명부(
staff컬렉션)는 63명을 넣었고, 보안 규칙의 허용 목록에 이 컬렉션을 더해 배포했다. 명부가 비어 있거나 명부에 없는 사람은 직접 입력으로 넘어가므로 명부가 없어도 예약이 막히지 않는다. - v1.20에서 업데이트 공지 팝업이 들어갔다. 지정한 날짜까지만 보이고 「다시 보지 않기」를 누르면 그 공지는 다시 안 뜬다. 새 공지는 설정 파일의 번호 · 기한 · 항목만 고쳐 배포하면 된다.
- 버전 규칙을 코드에 못박았다. 큰 변경이면 앞자리, 기능 하나면 가운데, 잔잔한 수정이면 뒷자리를 올리고 「1.20」처럼 적는다.
- 명부에는 개인 연락처가 들어 있으므로 이 페이지에는 내용을 싣지 않는다.
8 · 홈 화면 앱(PWA)과 매일 백업 (9/17 08:59)
v1.30(08:59)에서 휴대폰 브라우저의 「홈 화면에 추가」로 아이콘이 생기고 주소창 없이 전체 화면으로 뜨게 했다. 안드로이드와 아이폰 모두 되고, 스토어 등록 · 심사가 필요 없고 업데이트는 기존처럼 즉시 반영된다.
- 서비스 워커(앱 뒤에서 도는 중계 프로그램)는 네트워크 우선이다. 배포가 잦아서 캐시를 먼저 쓰면 옛 판이 계속 뜨기 때문이고, 서버가 안 될 때만 캐시를 쓴다. Firestore 같은 바깥 요청은 가로채지 않는다. 아이콘은 외부 부품 없이 PNG 를 직접 써서 만들었다(112줄).
- 매일 07:30에 전체 데이터를 JSON 으로 백업한다(30일 보관). Windows 작업 스케줄러에 등록했다. Firestore 의 시점 복구가 유료 요금제에서만 되기 때문에 마련한 대안이다.
- 10/8 에 확인했다. 작업은 등록돼 있고 마지막 실행은 10/8 07:30, 결과 코드 0이다. 백업 폴더에는 9/17 08:54부터 10/8 07:30까지 파일 23개가 있다.
- 이 날 아침 5개 커밋이 08:12부터 08:59까지 47분 안에 들어갔다.
- 한계. 백업은 이 PC 한 곳에서만 돈다. 다른 곳에 사본이 있는지는 확인하지 못했다.
9 · 배포 길 셋 (8/10 → 9/27)
| 시기 | 길 | 올리는 순서 |
|---|---|---|
| 8/10 오전 1판 | GitHub Pages 에 파일 그대로 | 파일 고침 → push → 1~2분 뒤 반영(빌드 없음) |
| 8/10 오후 React 판 | GitHub Actions → GitHub Pages | main 에 push → 타입 검사(오류가 있으면 배포 안 함) → 빌드 → 올림 |
| 9/27 16:01 | Cloudflare 워커 | main 에 push → Workers Builds 가 타입 검사 · 빌드 → 워커 배포 |
세 번째 길로 옮긴 까닭은 호스팅 요금제였다. 회사 저장소를 전부 비공개로 돌리기로 한 날이었는데, 조직이 무료 요금제라 비공개 저장소는 GitHub Pages 를 못 쓴다. 그냥 비공개로 돌리면 앱이 바로 꺼진다.
- 같은 빌드를 올려 대조했다. JS · CSS 파일의 해시가 전부 같았고,
index.html· 서비스 워커 · 매니페스트만 줄바꿈 방식(CRLF)이 달랐다(Windows 에서 내려받은 탓이고 자동 빌드는 리눅스라 원래와 같아진다). - 도메인은 끄고 켜는 순서를 지켰다. 대시보드에서 기존 도메인 연결(GitHub 쪽)을 지우고 곧바로 워커 도메인으로 붙였다. 옛 워크플로(48줄)와 도메인 파일은 비공개에서 실패만 쌓이므로 뺐다. 타입 검사 관문은 그대로 남겼다.
- 저장소 사본이 두 갈래다. 이 PC 사본은 21커밋이라 9/27 이사 커밋 2개(16:01 · 16:03)가 없다. 원격은 23커밋이다.
- 배운 것. 호스트를 옮길 때는 먼저 새 곳에 같은 결과를 올려 해시를 대조하고, 그다음에 비공개로 바꾼다. 거꾸로 하면 그 사이에 서비스가 꺼진다.
10 · ERP 시안 셋 (8/24)
차량 앱 작업 사이에 낀 8월 24일, ERP 시안 HTML 세 장이 하루 안에 나왔다(파일 수정 시각 13:11 · 16:23 · 18:44, 5시간 33분). 폴더가 저장소가 아니어서 커밋 이력은 없다.
- 마스터플랜(977줄). 시장 조사로 13가지 제품(글로벌 상용 3 · 국내 클라우드 4 · 오픈소스 6)을 표로 견주고, 압력용기 · EPC 제작업의 특수 요구 여섯(수주 건별 원가 · 건별 설계 · 자재 추적 · 용접 품질 · 도면 개정 · 외주)을 뽑았다. 전략 셋 가운데 C안(플랫폼 확장 하이브리드)을 추천했다. 회계 · 세무 · 급여는 상용(더존 · 이카운트)을 그대로 쓰고, 구매 · 자재 · 원가 · 품질 같은 운영 업무는 플랫폼에 직접 짓는다는 안이다. 요약 첫 줄이 「회계는 사고, 운영 ERP는 플랫폼에 직접 짓는다」다. 로드맵은 2026년 9월부터 2027년 12월까지 5단계(준비 → 기반+구매 → 핵심 원가 → 품질+회계연동 → 도면)다.
- 시안이 근거로 든 것. 시안은 플랫폼 코드를 분석해 8/24 시점의 플랫폼에 구매 · 자재 · 재고 · BOM · 원가 · 회계가 전혀 없고(완전한 빈터), 프로젝트 마스터 · WBS · 전자결재 · 권한 · NCR/CAR 은 있다고 적었다. 그래서 새로 지을 것은 업무 모듈 4개뿐이라고 봤다. (그날 플랫폼의
modules/목록에도 구매 · 자재 · 원가 모듈은 없었다.) - 구매 · 조달 프로토타입(564줄). 구매요청 → 발주 → 입고 → 거래처 네 화면. 데이터는 저장되지 않는다(화면 맨 위에 「UI 방향 검토용 프로토타입」 띠가 붙어 있고, 실제 판은 플랫폼
modules/erp/로 탑재할 예정이라고 적었다). 발주서 인쇄 양식과 권한별 보기(시뮬레이션)를 넣었다. - 스위트 v0.3(1,697줄). 화면 21개를 한 틀에 담았다(경영 대시보드 · WBS 통합 보드 · 결재함 · 견적수주 · 프로젝트 원가 · 구매요청 · 발주 · 외주 · 재고 · 작업지시 · 도면 · 설계변경 · 공수 · 인사 · 장비 · 자금 · 전산회계 · 품질 연계 · 거래처 등). 데이터는 메모리에만 있어 새로고침하면 초기화되고, 예상 금액이 5천만 원 이상이면 임원 결재가 자동으로 붙는 규칙을 시험해 볼 수 있다. ERPNext · Carbon · DocDokuPLM · Frappe HR · Bigcapital 같은 오픈소스 ERP 의 데이터 모델과 화면 흐름을 번안했다고 밝혔다. 코드를 가져온 것이 아니라 구조를 우리 업무로 옮겨 그렸다는 뜻이다. 원칙은 셋이다. 이미 있는 것(WBS · 결재 · 권한 · 품질)은 다시 만들지 않고 잇는다 · 법이 바뀌는 것(세무 · 급여)은 상용에 맡긴다 · 나머지는 오픈소스 구조를 번안해 새로 만든다.
- 화면 속 회사 이름과 금액은 가상 값이라고 마스터플랜이 명시했다. 시안일 뿐이다.
- 그 뒤. 10/8 현재 플랫폼의
modules/에erp폴더는 없다(시안이 탑재 예정이라 적은 이름이다). 이틀 뒤인 8/26에 직원이 구매부 업무일지 + 발주 목록 모듈(modules/purchase)을 따로 올렸다. 시안의 구매 흐름과 같은 것인지, C안을 채택했는지는 확인하지 못했다. 따로 있는ERP-Dashboard-main폴더는 파일이 0개다. - 배운 것. 시안은 「무엇을 사고 무엇을 지을지」의 경계를 그리는 도구다. 구축 결정과 시안은 따로 기록해 두어야 나중에 어디까지 갔는지 안다.
💡 설명
- Firestore: 구글이 운영하는 실시간 데이터베이스. 문서와 컬렉션(문서 묶음) 꼴로 저장한다.
- 실시간 구독(onSnapshot): 한 사람이 저장하면 같은 데이터를 보는 다른 화면이 새로고침 없이 바뀌는 방식.
- 익명 인증: 이름을 묻지 않고 앱에 임시 신분 토큰만 주는 로그인. 로그인 화면은 없지만 「토큰이 있어야 접근」이라는 규칙을 걸 수 있다.
- 보안 규칙: 데이터베이스가 읽기 · 쓰기를 허용할지 서버에서 판단하는 규칙 파일. 화면의 비밀번호 칸과 달리 우회가 안 된다.
- 명암비: 글자색과 배경색의 밝기 차이를 숫자로 나타낸 것. 웹 접근성 AA 기준은 일반 글자 4.5:1 이상이다.
- PWA: 웹 사이트를 휴대폰 앱처럼 홈 화면에 설치해 쓰게 하는 기술.
- 서비스 워커: 브라우저 안에서 앱과 인터넷 사이에 끼어 요청을 중계하는 작은 프로그램. 네트워크 우선이면 항상 서버를 먼저 확인한다.
- GitHub Pages · Actions: 저장소의 파일을 웹 사이트로 내보내 주는 서비스, 그리고 push 때마다 빌드를 자동으로 돌리는 서비스.
- Cloudflare 워커: 클라우드플레어가 정적 파일이나 작은 프로그램을 대신 내보내 주는 서비스. 이 앱은 빌드한 파일 묶음(
dist/)만 올린다. - 타입 검사(
tsc --noEmit): TypeScript 코드를 실행하지 않고 형이 맞는지만 검사하는 것. 통과하지 못하면 배포를 막는 관문으로 썼다. - ERP · WBS · MES · PLM: 회사 자원(구매 · 재고 · 원가 · 회계)을 한 시스템에서 관리하는 것 · 일을 쪼갠 작업 분류 체계 · 생산 현장 작업 실적 관리 · 도면과 설계변경 관리.
- 번안: 남의 프로그램의 코드를 가져오지 않고, 구조와 흐름만 우리 업무에 맞게 다시 그리는 것.
함정과 배운 것
- AI 가 만든 산출물에는 그럴듯한 가짜가 있다. 가짜 경로 · 허구의 정비소 · 시범 데이터를 걷어냈고, 걷어내자 그것에 기대던 화면이 멈췄다. 지운 뒤에는 기존 데이터로 모든 화면을 한 번씩 열어 본다.
- 화면의 비밀번호는 방어가 아니다. 진짜 통제는 서버 규칙이고, 그 규칙이 못 막는 것(페이지를 연 사람)은 파일에 적어 둔다.
- 문서가 코드보다 낡는다. 이 앱의 README 는 지금도 「현재 Firestore는 인증 없이 읽기 · 쓰기가 열려 있습니다」라고 적고 있다. 8/10에 규칙과 익명 인증이 들어갔는데 문서는 안 바뀌었다(원격 최신 README 의 58번째 줄).
- 덮어쓰기식 라이트 모드는 재발한다. 같은 문제를 네 번 고쳤다. 색을 클래스마다 보정하는 대신 한 곳의 변수로 두는 구조가 필요하다.
- 출처가 둘이면 시각으로 정한다. 현재 위치처럼 저장값과 계산값이 맞서면 어느 쪽이 더 최근인가로 푼다.
- 같은 것을 그리는 부품이 둘이면 둘 다 고친다. 예약 캘린더 하나만 고쳐서 홈 화면에 같은 버그가 남았다.
- 캐시 우선 서비스 워커는 옛 판을 붙잡는다. 배포가 잦은 앱은 네트워크 우선으로 한다.
- 무료 조직 요금제는 비공개 저장소의 Pages 를 못 쓴다. 비공개로 돌리기 전에 호스트부터 옮긴다.
- 이름이 같은 다른 물건. 차량 앱(별도 저장소)과 플랫폼의
modules/car(시정조치요구서)는 무관하다. 저장소를 찾을 때 헷갈린다. - 운영 데이터는 기록에 넣지 않는다. 이 앱의 커밋 메시지에도 실제 예약자와 차종이 적혀 있다. 이 페이지에는 쓰지 않았다.
출처
커밋(차량 앱 세종기술차량관리): 66931ef · 3c01ab5 · 18afc27 · c6eff62 · 17c8548 · 6694ab1 · 248b707 · 42f8b3e · fe38766 · a9ba65f · 85dbbd7 · 650f7ed · 5b63a95 · ea50667 · 8ed1606 · 20329ad · 7e92361 · c5f3dc3 · 9520c35 · b92bf83 · 6a16ae2 · 원격 7b68504 · 4b009ee. 커밋(1판): b8584b7. 커밋(플랫폼): e3a4979.
문서: 차량 앱 README.md · firestore.rules · src/lib/firebase.ts · public/sw.js · .github/workflows/deploy.yml · 원격 wrangler.jsonc, 1판 README.md, ERP 시안 sejong-erp-masterplan.html · erp-purchasing-prototype.html · sejong-erp-suite.html.
기억 노트: public-repo-exposure(호스팅 이사 배경). 글쓴이 crazy4eu(cwkim83). 숫자마다 근거는 checks.md.