← 목록
기타 2026-07-21 10KB 읽기 9분

crowny-space 서버(:9607) 기능 4종 통합 — 설정/페어링/알림/씬CRUD

개요

3개 검증완료 라이브러리(각 자체시험 ALL_PASS)를 라이브 서버(/Users/ef/crowny-space/서버.한선, :9607)에 배선하고, 신규로 씬(Scene) CRUD를 추가했다. 전부 실측 E2E로 검증했고, 배선 도중 실HTTP 상태코드 매핑 버그 1건을 라이브에서 발견·수정했다. 통합 과정에서 문자열 핸들 조기경고([STR] 90%) 현상을 실측 격리해 원인을 규명했다(상세 §잔여이슈).

배선 4종

1. 설정 (규격/설정저장.한선)

  • GET /api/settings?space= — 12키 병합(공간override→전역→기본값 3단 폴백) JSON
  • POST /api/settings {space,key,value} — 4상 검증(설정쓰기) 후 {"ok":1,"판정":"티"} 또는 거부
  • GET /api/settings/schema — 스키마 JSON
  • 상수 대체 지점 (기존 동작 불변 확인됨, 기본값=이전 하드코딩과 동일):
  • _재실전환체크()_재실전원밖유예초(900초 하드코딩) → 설정읽기(_설정파일,"home","away_grace_min")*60. 라이브 검증: away_grace_min=5(분)로 설정 후 재실.psv를 400초 경과로 조작→tick→presence.all_out 정확히 발동(기존 900초 유예였다면 미발동했을 조건). 실측으로 실제 코드경로 연결 확인.
  • _에너지적산()의 누진임계 280 하드코딩 → 설정읽기(_설정파일,"home","energy_alert_kwh"). 라이브 검증: energy_alert_kwh=100으로 낮춰 재설정 후 tick→감사.psv에 "누진 경계 접근: 이번달 171kWh" 힌트 정확히 발동(기본 280 유지 시 미발동했을 조건).
  • privacy_guest_days(손님 자동삭제 30일)는 실제 자동삭제 로직이 서버에 아예 없어 연결 지점이 없음 — 스키마/API로만 노출, 가짜 배선 없이 정직하게 보고.
  • 2. 페어링 (규격/페어링.한선, 페어링-통합명세.md 준수)

    • POST /api/pair/scan {protocol} → 서버 관리 단조증가 시드(스캔시드.psv) + 어댑터스캔() 발견목록 + 페어세션시작() 세션(무상태 체크섬 프레임) + 세션↔시드 캐시(페어세션.psv, TTL 없음이나 세션 자체가 5분 타임아웃)
    • POST /api/pair/register {session,code,space,device_id,area}페어검증() 4상 분기(옴=400/타=409/음=410/티=진행) → 캐시된 시드로 재스캔(클라이언트 스펙 위조 방지) → 기기등록() 팬아웃 → 신규 _엔티티페어등록()(cause="pair", 기존 _엔티티신규등록의 cause="hub"와 구분) → 세션사용처리()
    • GET /api/pair/session?session=&code= — 상태 조회(간이 %7C/%3A URL디코드)
    • E2E 검증: zigbee 스캔→3기기 발견→bulb 등록→/api/entitieslight.zigbee_bulb_1 정확히 등장, 로그북 cause="pair" 확인. 재전송(used 세션 재사용)→HTTP 409(첫 시도에 200으로 잘못 나가는 버그 발견·수정, 아래 참조). matter 스캔+오코드→HTTP 400 옴. 세션조회 GET 정상 매핑.

    3. 알림 (규격/알림엔진.한선, 알림엔진-통합명세.md 준수)

    • GET /api/notifications?space= / POST /api/notifications/dismiss {id}
    • _처리틱()_에너지적산() 다음에 _알림틱평가(지금초) 훅 추가 — (a)소모품 필터/배터리 정적값 (b)sensor.leak (d)대여 D-day(체계("date -j -f '%m-%d' ...")로 day-of-year 계산, 연도 미보유 데이터라 최선노력) (e)누진kWh (g)냉난방 편차(×10 정수스케일 파싱 후 정수도 절단) → 알림평가()심각도정렬()→규칙별 쿨다운체크()/쿨다운기록()data/알림.psv(idx 회전, 로그북과 동일 패턴) 기록, 음(긴급)은 로그북에도 기록
    • E2E 검증: sensor.leak=1→tick→알림 목록에 음(긴급) 등장→dismiss→목록에서 사라짐(dismiss 파일 스냅샷 컴팩션)→미지 id dismiss시 404. lease_due/filter_low도 정상 발동·쿨다운 경계 확인(같은 규칙이 24h 쿨다운 경과 후 재발동하는 것도 실측 확인).

    4. 씬 CRUD (신규 설계 — 라이브러리 없음)

    • GET /api/scenes?space= — 하드코딩 5씬(외출/귀가/취침/주일예배/영업시작) + 사용자정의 씬 병합
    • POST /api/scenes {space,name,states:[{entity,value}]}data/씬.psv(id=space:name, latest-wins, __DELETED__ tombstone)에 저장. states 배열은 _JSON값()을 조각 단위 재사용하는 소형 파서(_씬상태파싱)로 파싱(중첩객체 없는 flat object 가정)
    • POST /api/scenes/capture {space,name} — 해당 공간 전체 엔티티 현재 상태를 스냅샷 저장("씬=상태 묶음" 설계)
    • POST /api/scenes/delete {space,name} — tombstone
    • _씬실행(name,space) 확장 — 하드코딩 5씬 매칭 실패 시 사용자정의 씬 조회·실행(/api/scenes/run이 그대로 커버)
    • E2E 검증: 생성→목록 등장→run→엔티티 상태 실제 변경 확인→capture(26개 엔티티 캡처)→delete→목록에서 사라짐→미지 씬 삭제시 404. 재기동 후 전부 정상 복원(WAL 재생 무손상).

    발견·수정한 버그 1건 (통합 중)

    _응답()의 HTTP 상태텍스트 매핑 테이블에 404/400/503만 있고 409/410이 없어 실제로는 본문이 {"ok":0,...}인데 상태줄은 항상 200 OK로 나가는 함정(가드레일 §"HTTP 상태줄 테이블에 쓸 상태코드 없으면 200으로 나감" 항목과 정확히 일치하는 재현 사례). 페어링 중복세션 재전송 테스트에서 발견 → _응답()에 409/410 매핑 추가로 수정, 재검증 통과.

    함수명 충돌 (경고, 무해 확인)

    설정저장.한선/센서코드.한선/알림엔진.한선 3개 라이브러리가 각각 _필드(2곳) 또는 자체시험(3곳) 이름을 독립적으로 정의 — 가져오기 시 [경고/warn] 이름 충돌 발생(하드에러 아님, 실측 확인). 마지막 정의(= 서버.한선 자신의 _필드)가 전역에서 승리함을 별도 격리 테스트로 실증(main이 항상 최종 승자). 서버의 _필드분리()+_PSV복원() 기반이라 라이브러리들의 원래 char-scan 버전과 값 동등(백슬래시 이스케이프 없는 일반 PSV 필드에서 결과 동일) — 라이브 E2E 전체가 정상 동작한 것으로 안전성 재확인. 자체시험 충돌은 각 라이브러리의 self-test 게이트(CFG_SELFTEST/PAIR_SELFTEST/NOTI_SELFTEST)가 프로덕션에서 전부 미설정이라 영향 없음(다만 수동으로 어느 한 게이트를 켜면 의도와 다른 라이브러리의 자체시험이 실행될 위험 있음 — 디버그 전용 함정으로 문서화만).

    잔여 이슈 (중요 — P1)

    문자열 핸들 조기경고 [STR] 90%가 통합 후 더 빨리 도달함을 실측으로 확인:

    • 통합 전(기존 코드): tick 반복 호출 시 ~120회 tick(약 2시간, 60초 주기 기준)에서 90%(432000/480000) 최초 도달 — 가져오기 전 코드에도 이미 존재하던 사전 결함(신규 도입 아님, 격리 재현 완료).
    • 통합 후(4개 라이브러리 가져오기, 내 알림훅 비활성 상태): ~40회 tick에서 90% 도달.
    • 통합 후(전체, 알림훅 포함): ~31회 tick에서 90% 도달.
    • 격리 재현으로 380회 연속 tick(약 6.3시간 상당)까지도 실제 exit43 크래시는 관측되지 않음 — 90% 경고 이후 증가율이 급격히 둔화(플래토 추정, 로그가 edge-trigger라 확증은 못함). 즉 "임박한 크래시"가 아니라 더 이른 조기경고로, 실제 재기동 주기 단축 폭은 격리테스트 범위 내에서 확인 안 됨(더 긴 실측 필요).
    • 근본 원인: 4개 라이브러리(설정저장/센서코드/페어링/알림엔진, 합계 ~90KB 소스) 전체 가져오기가 프로그램의 정적 베이스라인을 높여, 기존에도 있던 tick당 문자열풀 누수(가드레일 기지 항목: "문자열 풀은 미복원")가 480000 상한에 더 빨리 근접하게 만듦. 내 알림훅 자체의 기여는 상대적으로 작음(40→31, ~25%).
    • launchd org.crowny.space.plist는 이미 KeepAlive=true+ThrottleInterval=10이라 exit43 발생 시 10초 내 자동재기동(WAL 재생으로 무손실 복구, 재기동 테스트로 실측 확인) — 완전한 서비스 다운은 아님, 다만 재기동 빈도가 늘어날 잠재 위험.
    • 권장 후속 조치: 가드레일이 지시한 "필요한 함수만 최소 이식"을 실제로 수행(4개 라이브러리 전체 가져오기 대신, 서버가 실제 호출하는 함수만 발췌 이식) — 이번 작업에서는 시간·리스크 트레이드오프상 시도하지 않음(대규모 재작성이 되레 새 버그를 낼 위험 판단, 기능은 이미 전량 검증 완료 상태). 모니터링: grep '\[STR\]' /tmp/crowny-space.err.

    관련 파일

    • /Users/ef/crowny-space/서버.한선 (본 통합의 핵심 수정 대상, 백업: 서버.한선.bak-통합전-1784603689)
    • /Users/ef/crowny-space/서버.toau (현재 라이브, 백업 다수: 서버.toau.bak-*)
    • /Users/ef/crowny-space/규격/{설정저장,센서코드,페어링,알림엔진}.한선 (미수정, 정본 그대로 재사용)
    • /Users/ef/crowny-space/규격/{페어링,알림엔진}-통합명세.md (명세 그대로 따름)
    • /Users/ef/crowny-space/data/{설정,씬,스캔시드,페어세션,알림,알림_idx,알림_쿨다운,알림_dismiss}.psv (신규 데이터 파일)

    크라우니코드

    • lookup: 사전조회 3건(씬CRUD 미스, PSV컴팩션 2건 HIT — 기존 서버 내부 패턴 그대로 재사용 확인)
    • generate: 설정/페어링/알림/씬 라우트+헬퍼 전량 신규 생성(라이브러리 자체는 기존 산출물 재사용)
    • learn: crownycode-brain.sh learn 1회 + crownycode-learn.sh add 3건(설정API공간별폴백/무상태페어링세션검증/알림틱쿨다운게이트)