← 목록
솔루션 2026-07-31 14KB 읽기 14분

크라우니솔루션 공사관리 — 접수·진행·결과리포트 (고객/스태프/관리자)

개요

solution.crowny.org(:9735)에 공사 접수 → 진행과정 → 결과 리포트 도구를 구축했다. 마스터템플릿 = 크라우니클린(clean.crowny.org:9624)의 견적→예약→시공→비포애프터 리포트 구조. 클린이 "청소 시공"이라면 솔루션은 "B2B 설비 공사"(화재경보·LED·PA·관제 설치)로 도메인 이식.

  • 라이브: https://solution.crowny.org/work.html · /work-staff.html · /work-admin.html (전부 200 실측)
  • 서비스 재기동: launchctl kickstart -k gui/$(id -u)/org.crowny.solution

무엇을 했는지

공정 7단계 (SSOT)

접수(received) → 현장실사(survey) → 견적·계약(contract) → 자재준비(prep) → 시공(construction) → 검수(inspection) → 완료(done)

역행·건너뛰기는 단계전이가능() 게이트가 400으로 차단. 관리자만 임의 단계 이동 허용.

3역할 도구

역할페이지기능
고객/work.html공사 접수(솔루션 카탈로그 연동) · 공사번호+연락처로 진행조회(7단계 스테퍼·진행률바·4상 상태배지·타임라인·공개사진) · 결과리포트 열람
스태프/work-staff.html스태프ID+PIN 로그인 · 배정목록 · 다음단계 보고 · 시공전/중/후 사진 업로드(base64) · 현장로그(진행/특이사항/자재/안전). 모바일 우선(터치타깃 44px)
관리자/work-admin.html기존 crew 세션 재사용 · 통계카드+단계별 카운트 · 접수함(필터·검색) · 상세(배정·예정일·상태변경) · 사진 공개토글 · 리포트 발행 · 스태프 관리

4상 상태판정

  • 티=정상 / 옴=임박(예정일 2일 이내) / 타=지연(예정일 경과) / 음=미배정

리포트 발행 게이트 (클린의 "비포애프터 신뢰구조" 이식)

검수통과판정(before사진수, after사진수, 체크통과수, 체크총수)
  • 1 = 발행 가능 (before·after 각 1장 이상 + 체크리스트 전항목 통과)
  • 0 = 체크리스트 미달 → 400
  • -1 = 사진 부족 → 400
발행 시 stage가 done으로 승격되고 completedAt 기록.

관련 파일

/Users/ef/crowny-solution/
├── 공사관리.한선        198줄  SSOT 엔진(단계전이·진행률·4상판정·검수통과·역할권한) + 자체테스트 34
├── lib/공사관리.js      103줄  JS 1:1 미러 (파리티 20케이스 PASS)
├── 공사서버.한선        110줄  서버 핵심로직 한선씨 동반
├── server.js                   /api/works* 라우트 25종 배선 (기존 라우트 무훼손)
├── data/공사/                  append-only PSV (work·workstage·workphoto·worklog·workreport·workstaff·seq)
└── web/
    ├── work.html/한선          고객 (420줄)
    ├── work-staff.html/한선    스태프 (384줄)
    └── work-admin.html/한선    관리자 (573줄)

저장 방식

클린과 동일한 append-only PSV, id별 last-wins. 상태 변경 이력이 통째로 남아 감사추적이 된다. 사진 실체는 data/공사/uploads/<workId>/.

검증 실측

임시포트 9739 종단(에이전트) + 라이브 9735 종단(메인):

항목결과
한선씨 자체테스트34/34 PASS
JS 미러 파리티20/20 PASS
접수 → lookup201 / 200 (진행률 14, 상태=음)
잘못된 연락처 조회404 (대조 게이트 작동)
단계 역행 / 건너뛰기400 ×2
정상 진행 3단계200 ×3
사진업로드·배정·공개200 전량
리포트 체크 미달400 + 사유
리포트 체크 통과200, stage→done
기존 라우트 회귀health/solutions/dashboard 200
공개 HTTPS 3페이지200 ×3
골격 게이트DOCTYPE·charset·viewport 3/3
디자인 대조crew.html 외 신규 hex 0건

수리한 결함

  1. solutionName 미해석 (라이브 실측 발견) — 접수 시 solutionId만 저장되고 솔루션명이 빈 문자열이라 고객 조회·리포트에 이름이 안 보였다. workSolutionName() 헬퍼로 카탈로그 해석 배선 → Inter-M IPA 디지털화재경보 시스템 정상 표시 확인.
  2. workDaysUntil 헬퍼 누락 ReferenceError (에이전트 자체 발견·수리).
  3. lib/공사관리.js 동시작성 충돌 — 엔진판을 정본으로 확정하고 서버 호출부를 맞춤(ID 무하이픈 WK000001, 역할권한 한글 어휘, 검수판정 -1/0 매핑).

VM 함정 실측 재현

통과 = 통과 + 검증(...) 누적식이 최종 합계를 "1건"으로 손상 — 가드레일 기록된 x = x + 무거운함수() 함정. 처방: 변수 rN = 검증(...); 통과 = 통과 + rN 임시변수 경유.

잔여 이슈

  • 크라우니디자인 중앙CSS 미적용(의도) — 솔루션은 premium 다크 팔레트가 확정 아이덴티티라 형제 페이지(crew/partner/rental)와의 일관성을 우선했다. 중앙CSS 적용 시 대비 붕괴 우려. 적용 여부는 사용자 판단 필요.
  • 스태프 계정은 시드 3명(WS1 김현수/WS2 이도현/WS3 박서연, PIN 1111/2222/3333) — 실운영 전 교체 필요.
  • 사진 업로드 용량 상한·이미지 리사이즈 미배선.
  • 고객 조회는 공사번호+연락처 대조만(무인증) — 클린과 동일 수준. 민감 현장정보가 늘면 인증 승격 검토.
  • 크라우니AI 앱카드 등록 미실시.

추가 작업 (2026-07-31, 세션2) — P0 사진서빙 수리 + 태그 + 관리소 열람

무엇을 했는지

  1. P0 결함 수리: GET /api/works/:workId/files/:filename 신설. 경로탈출 차단(path.basename+resolve 검증), 확장자별 Content-Type, 스태프/crew/본인 관리소=전량 열람·그 외=published만. workPhotos()가 이제 각 사진에 url 필드를 항상 붙임(과거 실사고: p.url 참조하는데 서버가 안 만들어서 사진 전부 깨짐 → 수리 완료, curl 바이트 동일 검증).
  2. 사진 태그: WORKPHOTO_COLS에 tags 추가(append-only, 구행은 빈문자열로 안전 파싱). 업로드 API·GET 응답에 반영. work-staff.html에 태그 입력+퀵칩(균열·누수·마감·안전·자재), work-admin.html 사진 그리드에 태그뱃지.
  3. 관리소(발주처) 열람 계정: data/공사/workowner.psv(id|password|workId|name|createdAt), POST /api/works/owner/login·GET /api/works/owner/me·GET /api/works/owner/photos·GET /api/works/owner/logs 신설(WORK_OWNER_SESSIONS, X-Owner-Token).
  4. web/work-owner.html + web/work-owner.한선 신규: 로그인→대시보드(현장명·주소·단계·진행률·4상 상태배지, phase탭+태그필터 사진그리드+라이트박스, 타임라인, 로그). 기존 work.html/work-admin.html 토큰만 재사용(신규 hex 없음, grep 대조 완료).
  5. work-staff.html 로그인 비밀번호 입력 inputmode="numeric"→"text", 라벨 "PIN"→"비밀번호"(한글 비밀번호 입력 불가 버그 수리).
  6. 실케이스 1건 시딩: WK000001(천안 용곡 우림아파트, SOL-104, stage=construction), 스태프 아파트스태프, 관리소 천안용곡우림/12341234, workstage 접수~시공 5행, 샘플사진 2장(before/progress, 태그 포함, 캡션 "샘플 - 실제 현장사진으로 교체 예정").

관련 파일

  • /Users/ef/crowny-solution/server.js (파일서빙 라우트, tags 컬럼, owner 세션/라우트 4종)
  • /Users/ef/crowny-solution/web/work-owner.html, /Users/ef/crowny-solution/web/work-owner.한선 (신규)
  • /Users/ef/crowny-solution/web/work-staff.html (태그 입력+퀵칩, 로그인 인풋모드)
  • /Users/ef/crowny-solution/web/work-admin.html (태그뱃지)
  • /Users/ef/crowny-solution/data/공사/{work,workstage,workstaff,workowner}.psv (시드 WK000001)

검증 (curl 실측)

  • 관리소 로그인 성공/실패(401) OK, /api/works/owner/me 진행률71%·상태 "정상" 확인
  • 스태프 로그인 OK, 사진 2장 업로드(캡션·태그 포함)
  • /api/works/owner/photos 2장 반환(url·tags 포함), 태그필터 ?tag=균열 1장만
  • 파일 GET: Content-Type image/png, 68바이트 원본과 byte-identical
  • 무인증 파일 GET → 403(비공개 사진 차단)
  • 회귀: /api/health /api/solutions /api/dashboard /work.html /work-staff.html /work-admin.html /work-owner.html 전부 200, https://solution.crowny.org/work-owner.html 200
  • work-owner.한선 hanseonc_rpn 컴파일 exit=0(기존 work.한선과 동일 경고 프로파일)

잔여 이슈

  • 없음. 단, work-owner.한선/work.한선류는 실제 페이지와 완전 1:1은 아니고 "구조 요약본"(레포 기존 컨벤션 그대로 답습).

추가 수리 (2026-07-31, 세션3) — 한글 비밀번호 입력 불가

증상: 스태프 계정 아파트스태프/아파트1234 로 폰에서 로그인하려는데 비밀번호 칸에 한글이 안 쳐짐.

원인: <input type="password">는 모바일 브라우저에서 IME를 ASCII로 강제한다. 앞선 세션에서 inputmode="numeric"→"text" 로 고쳤지만 그것으로는 안 풀리는 브라우저 레벨 제약. 서버는 문자열 비교라 정상이었고 API 검증(200)만 해서 못 잡았다 — 렌더/실기기 검증이 아니면 안 드러나는 유형.

수리: type="text" + CSS 마스킹(-webkit-text-security:disc) + 표시/숨김 토글. secure-entry가 아니라 시각적 마스킹만 하므로 IME가 살아 있다.

  • web/work-staff.html — 비밀번호 필드 교체, .pw-wrap/.pw-mask/.pw-toggle CSS, 토글 JS, 에러문구 "PIN"→"비밀번호"
  • web/work-owner.html — 동일 적용(아이디가 한글이라 비번도 한글로 바뀔 여지 대비)
  • 신규 hex 색상 0건(기존 토큰만 사용)
검증: 두 페이지 type="password" 잔존 0건, 로컬·공개 HTTPS 200, 서빙본에 수정 반영 확인, 크라우니AI검증.sh text 로 "비밀번호 / 표시 / 로그인" 렌더 확인.

메모리 기록: feedback_password필드_모바일IME_한글입력차단.md (재발 방지)


추가 수리 (2026-07-31, 세션4) — 모바일 4건

사용자 실기기 피드백 4건 전량 수리.

1. 한글 입력기가 아예 안 뜸 (복사·붙여넣기로만 로그인됨)

세션3의 CSS 마스킹만으로는 부족 — 기기/키보드에 따라 한글 IME가 안 올라온다. → ASCII 대체계정 도입. workstaff.psv/workowner.psv 끝에 altId|altPw 컬럼 추가, workPwMatch()로 한글·ASCII 어느 조합이든 인증.
  • 스태프: 아파트스태프/아파트1234 = aptstaff/apt1234 (교차조합 포함 4조합 전부 200, 오답 401)
  • 관리소: 천안용곡우림 = yonggok (pw 12341234)
  • 입력필드는 lang="ko" 추가, autocorrect/autocapitalize/spellcheck 제거

2. 사진이 현장 촬영만 되고 앨범 선택 불가

원인: <input type="file" capture="environment"> — capture 속성이 있으면 카메라만 열린다. → 입력을 분리: #photoCam(촬영, capture 유지) / #photoFile(앨범, multiple 다중선택). 한쪽 선택 시 다른 쪽 초기화, 선택 파일명 표시, 다중 업로드 루프(성공/실패 건수 토스트).

3. 태그가 도메인과 안 맞음

아파트 수리가 아니라 방송시스템 구축/수리 도메인. → 퀵칩 교체: 균열·누수·마감·안전·자재 → 현황·고장·진단·탈거·양중·설치·결선·수리·청소·시운전 → 케이스 솔루션도 SOL-104(화재경보) → SOL-201 Inter-M 전관방송·PA 시스템, desc "공동주택 전관방송 시스템 구축"

4. 샘플 이미지가 물음표(?)로 표시

원인: 세션2 검증에 쓴 파일이 1×1 픽셀 회색 PNG(68바이트)라 렌더 불가. → 샘플 사진 2장·PSV 행 전량 제거(실제 현장사진만 올라가게). 64×64 유효 PNG로 업로드→서빙 왕복 검증 후 그것도 제거.

검증

  • 로그인 4조합 200 / 오답 401, 관리소 2조합 200
  • owner/me 솔루션명·desc 방송시스템 전환 확인, 사진 0장(샘플 제거)
  • 유효 PNG 업로드 200 ×2 → 파일 GET 200 image/png 64×64 정상
  • 서빙본에 촬영·앨범·신규태그 10종 전부 반영, #photoFile에 capture 없음 확인
  • 공개 HTTPS 6경로 200, 렌더 확인

잔여

  • 한글 IME는 기기 설정 문제라 웹에서 완전 보장 불가 → ASCII 대체계정이 실질 해법
  • 사진 용량 상한·리사이즈 여전히 미배선(고화질 다중 업로드 시 base64 팽창)

브라우저 실행 검증 (메인 세션 독립 실측 — 2026-08-01)

에이전트가 "canvas 축소 경로는 curl 로 재현 불가"라며 Node 바이트검증으로 대체했던 구간을 실제 WKWebView 에서 실행해 메꿨다.

방법: work-staff.html 의 EXIF 모듈+processImageFile 을 그대로 추출한 하네스 HTML 을 web/ 에 만들고(WKWebView loadFileURL 부모디렉토리 제한 회피), crowny-ai-browser MCP 로 열어 결과를 읽음. 입력=exiftool 로 GPS·DateTimeOriginal·Make/Model·Artist(한글)·Orientation=6 을 심은 3600×2400 JPEG(619,547B).

항목실측
EXIF 파싱orientation=6 · gps=36.815100,127.113900 · shotAt=2026:07:31 14:22:31 · Apple iPhone 15 Pro · artist=아파트현장팀(한글 정상)
축소619,547B → 214,813B (65% 감소)
서버 전달 메타shotAt·gps·device·shooter 전부 채워짐
APP1 바이트 대조원본 556B vs 축소본 556B — 1바이트만 상이(byte 66: 6→1 = Orientation 정규화, 의도된 동작). 나머지 555B 완전 동일
원본 APP1 은 dd 로 브라우저와 독립 추출해 대조 — 같은 파서로 재파싱하는 순환검증을 피했다. 하네스는 검증 후 삭제(공개 경로 조회 시 SPA 폴백만 반환, __B64__ 유출 0건 확인).

중간 발견: 하네스 1차 시도가 window.onerror: Script error. 로 죽음 → 추출 구간 끝줄이 함수 중간이라 문법 파손. node --check 로 경계를 이분탐색해 확정. 추출식 검증은 경계 문법검사가 선행돼야 한다.