Skip to content

feat: 운영 상태를 한 화면에서 보는 관제 화면을 더한다 - #157

Open
RosieOh wants to merge 1 commit into
mainfrom
feat/control-plane
Open

RosieOh wants to merge 1 commit into
mainfrom
feat/control-plane

Conversation

@RosieOh

@RosieOh RosieOh commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

Closes #156

무엇을 했나

운영 상태를 한 화면에 모았다. /control-plane 에 정적 HTML 한 장을 두고, 백엔드에 새
엔드포인트를 만들지 않고
이미 있는 네 경로만 읽는다.

경로 보여주는 것
/api/admin/sync/status 데이터 신선도
/api/admin/dashboard 가입 추이, 최근 활동, 집계
/api/admin/policies/verification-status 지역별 정책 금액 검증률
/facilities/forecast-accuracy 입소 예측 정확도

정확도는 공개 지표와 같은 경로를 쓴다. 같은 숫자를 두 곳에서 다르게 계산하지 않기 위해서다.

첫 자리를 신선도에 준 이유

급할 때 제일 먼저 알아야 하는 것이 "어떤 데이터가 낡았는가" 인데, 지금은 동기화 응답을 눈으로
읽어야 알 수 있었다.

작업마다 기준 시간이 다르다(어린이집 24시간, 유치원 168시간). 그래서 "몇 시간 지났다" 는
작업끼리 비교가 안 된다. 기준선을 트랙의 같은 지점에 고정하고 그 대비로 채운다 — 막대
길이가 곧 "기준의 몇 %를 썼는가" 이고, 기준을 넘긴 작업은 선 너머까지 채워져 넘침이 보인다.
기준이 작업마다 다르다는 사실 자체가 읽혀야 하므로 기준 시간을 이름 아래에 적었다. 낡은 것이
위로 온다.

경계 — 무엇이 공개이고 무엇이 아닌가

껍데기는 공개, 수치는 전부 관리자 토큰을 요구한다. 껍데기를 막으면 로그인조차 할 수 없고,
수치 경로가 열리면 관제 화면이 인증 없이 읽히는 창구가 된다.

ControlPlaneTest 6개가 그 분리를 고정한다.

  • 껍데기가 비로그인으로 열린다
  • 껍데기가 서버 렌더링 없는 정적 파일이다 — 응답과 클래스패스 리소스를 바이트로 비교한다.
    처음엔 "템플릿 문법 ${ 가 없다" 로 보려 했는데 자바스크립트 템플릿 리터럴에 걸렸다.
    그건 서버 렌더링과 무관하고, 바이트가 같으면 끼워 넣을 자리가 없다는 더 강한 증명이다.
  • 수치 경로 3개가 비로그인에 401/403 이다
  • /facilities/forecast-accuracy 는 공개로 남는다

더하지 않은 것

  • 의존성 0. 외부 스크립트도, 번들한 폰트도 없다. 단일 파일이고 이미지 크기가 늘지 않는다
    (Pretendard woff2 가 2.0MB 라 넣지 않고 시스템 폰트로 떨어뜨린다).
  • 한 패널이 실패해도 나머지는 그린다. 전부 비어 있는 관제 화면이 가장 쓸모없다.
  • 토큰은 sessionStorage 에만 둔다.

좋아지지 않은 것

  • 수치는 열 때 한 번 불러온다. 자동 갱신이 없다 — 새로고침해야 한다. 폴링을 넣으면
    관리자 수만큼 /api/admin/dashboard 가 주기적으로 돌고, 그 집계는 가볍지 않다.
  • 화면에서 할 수 있는 조치가 없다. 보기만 한다. 동기화를 여기서 돌리려면 그 엔드포인트의
    권한·중복 실행 방지를 먼저 봐야 하는데 이 PR 범위가 아니다.
  • 모바일 폭은 1단으로 접히지만, 신선도 막대는 좁은 화면에서 읽기 불편하다.

확인

./gradlew test --tests '*ControlPlaneTest'   # 6 tests, 0 failures

목 데이터를 넣고 렌더해 눈으로도 확인했다.

운영 상태를 확인하려면 서버에 붙어 로그를 보거나 /api/admin/* 을 하나씩
호출해야 했다. 정작 급할 때 제일 먼저 알아야 할 것 - 어떤 데이터가 낡았는가 -
는 동기화 응답을 눈으로 읽어야 알 수 있었다.

백엔드에 새 엔드포인트를 만들지 않는다. 이미 있는 네 경로만 읽는다.

  /api/admin/sync/status                  데이터 신선도
  /api/admin/dashboard                    가입 추이, 최근 활동, 집계
  /api/admin/policies/verification-status 지역별 정책 금액 검증률
  /facilities/forecast-accuracy           입소 예측 정확도

정확도는 공개 지표와 같은 경로를 쓴다. 같은 숫자를 두 곳에서 다르게 계산하지
않기 위해서다.

화면의 첫 자리는 신선도다. 작업마다 기준 시간이 달라 "몇 시간 지났다" 로는
비교할 수 없으므로, 기준선을 트랙의 같은 지점에 고정하고 그 대비로 채운다 -
막대 길이가 곧 "기준의 몇 %를 썼는가" 이고, 넘긴 작업은 기준선 너머까지
채워진다. 기준이 작업마다 다르다는 사실 자체가 읽혀야 하므로 기준 시간을
이름 아래에 적는다. 낡은 것이 위로 온다.

경계:

껍데기(정적 HTML)는 공개, 수치는 전부 관리자 토큰을 요구한다. 껍데기를 막으면
로그인조차 할 수 없고, 수치 경로가 열리면 관제 화면이 인증 없이 읽히는 창구가
된다. 테스트가 그 분리를 고정한다 - 껍데기가 서버 렌더링 없는 정적 파일임을
응답과 클래스패스 리소스의 바이트 비교로 확인하고("템플릿 문법이 없다" 로
보려 했더니 자바스크립트 템플릿 리터럴에 걸렸다), 수치 경로 셋이 비로그인에
401/403 인지 확인한다.

의존성을 더하지 않는다. 외부 스크립트도, 번들한 폰트도 없다 - 단일 파일이고
이미지 크기가 늘지 않는다. 한 패널이 실패해도 나머지는 그린다. 전부 비어 있는
관제 화면이 가장 쓸모없다.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ Core ] 운영 상태를 한 화면에서 본다 - 관제 화면

1 participant