제 6 장
데이터 센터 개발
이 장의 수치는 2026-09-30 운영 API 실측값이다. 화면이 읽는 것과 같은 엔드포인트(/api/data-center/*)에서 가져왔다.
1) 화면 구성
데이터 센터는 여섯 화면이고, 개요 하나가 나머지 다섯으로 갈라진다.
| 화면 | 보는 것 | 실측 |
|---|---|---|
/data-center |
KPI 넷 → 최근 경기 → 승률 상위 → 대회별 경기 수 | 선수 178 · 경기 99(끝난 63) · 대회 19(끝난 12) · 벨트 20 |
/data-center/wrestlers |
선수 목록 + 전적. 링네임·본명 검색, 브랜드 필터, 페이지네이션 | 178명 |
/data-center/matches |
경기 목록. 대회·상태·참가자 필터 | 99경기 |
/data-center/championships |
현 챔피언 보드 + 벨트별 획득 기록 | 획득 367건 · 챔피언 66명 |
/data-center/ple |
대회별 경기 구성(타이틀전/일반 누적 막대) | 대회 17개 |
/data-center/analytics |
브랜드 분포 · 경기 형식 · 타이틀전 비율 · 승률 순위 | — |
여섯 화면이 껍데기 컴포넌트 하나(DataCenterShell)와 부품 셋(StatTile · LoadingBlock · DataUnavailable)을 공유한다. 부품이 셋인 이유가 이 화면의 규칙 하나를 그대로 담고 있다 — 로딩과 “데이터 없음”이 다른 상태여서다. 값을 못 받으면 0을 그리지 않고 그 구역만 DataUnavailable로 비운다.
대회 수가 두 곳에서 다르게 보인다. 개요는 19, 대회 화면은 17이다. 개요의 19는 ple_events 테이블 행 수이고 대회 화면의 17은 경기가 하나라도 붙은 대회 수다. 차이 둘은 목록에서 감춰 둔 대회다. 같은 이름의 숫자가 다르게 보이는 것은 좋지 않지만, 한쪽을 다른 쪽에 맞추면 둘 중 하나가 자기 출처와 다른 값을 말하게 된다. 그래서 맞추지 않고 각 화면이 자기가 센 것을 적는다.
벨트도 같다 — 보드에는 20개가 서 있고 획득 기록에 나오는 벨트 이름은 29개다. 폐지·개명된 벨트의 이력이 남아 있기 때문이고, 그것을 지우면 존 시나의 27회 획득이 줄어든다.
페이지를 벗어난 요청을 에러로 만들지 않는다
목록 두 화면의 페이지네이션은 범위를 벗어난 page를 400으로 돌려보내지 않는다. 빈 목록과 전체 수를 함께 주므로 화면이 “그 페이지에는 아무것도 없다” 를 그대로 그릴 수 있다. 크기는 100에서 자른다.
2) 집계 파이프라인
세 층이 각자 한 가지만 한다.
DataCenterPgRepository 읽어만 준다. 세지 않는다
↓ (MatchRow · WrestlerRow · TitleRow)
data_center_stats 순수 함수. DB도 네트워크도 모른다
↓ (MatchFact · RecordCount · BeltStat …)
DataCenterInteractor 필터 · 정렬 · 페이지 자르기
계산 규칙은 가운데 층에만 있다. 인터랙터에 한 줄이라도 세는 코드가 들어가면 같은 규칙이 두 곳에 생기고, 그때부터 화면과 API가 다른 답을 낼 수 있다. 가운데가 순수 함수라 DB 없이 픽스처만으로 시험된다.
쿼리는 넷이다
리포지토리가 던지는 쿼리는 wrestlers 전체 · ple_matches ⨝ ple_events 전체 · title_acquisitions 전체 · 카운트 둘이다. 부분 조회를 하지 않는 이유는 선수별 전적이 어차피 전체 경기를 봐야 나오기 때문이다. 지금 규모(178 · 99 · 367)에서 이것이 가장 단순하고, 잘라 읽어서 얻을 것이 없다.
대신 경기를 한 번만 훑는다. 선수 한 명씩 프로필 API를 부르면 그 안에서 매번 전체 경기를 다시 스캔하는데, 목록 화면은 178명이라 그 방식이 178배가 된다. records_by_wrestler가 경기를 한 바퀴 돌며 모든 선수의 전적을 한꺼번에 쌓는다.
전적 규칙은 예측 쪽과 같은 모듈이다
승·패를 세는 규칙은 제 5 장 4절의 records_scoring 하나뿐이고, 데이터 센터도 그것을 부른다. 태그팀 이름을 개인으로 펼치고, winner_pick에서 승자를 되짚고, 승자를 정할 수 없으면 무효로 접는 그 규칙이다. 화면마다 다른 규칙으로 세면 같은 선수의 승률이 두 개가 된다.
그 재사용에 값을 치른 자리가 하나 있다. records_scoring이 인바운드 스키마를 끌어오는데, 집계 모듈이 그것을 파일 맨 위에서 import하면 kayfabe.adapter.inbound.api 패키지 초기화가 돌면서 라우터 전체가 깨어나고 — 그 라우터가 프로바이더·인터랙터를 거쳐 다시 집계 모듈을 부른다(순환 import). 그래서 함수 안에서 import한다. 집계 모듈이 아무것도 깨우지 않는 잎으로 남아야 그 고리가 끊긴다.
없는 숫자를 만들지 않는다
| 자리 | 데이터가 모자랄 때 |
|---|---|
| 승률 | 판정된 경기가 0이면 None — 0.0이 아니다 |
| 승률 순위 | 판정 3경기 미만은 아예 올리지 않는다 |
| 최장 재위 기간 | 계산하지 않는다 |
| 대회 정렬 | 달이 비어 있는 대회는 뒤로 — 날짜를 지어내지 않는다 |
None과 0.0의 차이가 화면에서 실제로 갈린다. 0.0은 “다 졌다”이고 None은 “아직 모른다”인데, 0으로 접으면 한 경기도 안 끝난 선수가 승률 0%로 목록에 앉는다.
승률 순위의 하한 3경기는 취향이 아니라 데이터의 모양에서 나왔다. 명부 178명 중에는 한 경기만 뛴 선수가 수두룩해서, 그들을 섞으면 순위표 위쪽이 전부 1승 0패 100%가 되어 순위가 아무것도 말하지 않는다. 값을 상수로 둔 이유는 화면이 그 조건(“3경기 이상”)을 함께 적어야 하기 때문이다.
최장 재위 기간은 지금 데이터로 만들 수 없다. 원본의 획득일이 "Payback — June 16, 2013" 같은 자유 텍스트이고 끝난 날짜가 없다. 날짜를 추정해 채우면 그것은 가짜 통계이므로 획득 횟수만 센다(2026-08-20 결정). 화면도 “재위 기간은 원본이 자유 텍스트라 계산하지 않습니다”라고 적는다.
3) 차트 설계
형태부터 고르고 색은 마지막이다
| 데이터가 하는 일 | 쓴 형태 | 자리 |
|---|---|---|
| 숫자 하나가 결론이다 | 차트가 아니라 KPI 타일 | 개요·대회·벨트 화면의 상단 넷 |
| 항목 간 크기 비교 | 세로 막대 | 브랜드별 선수 분포 |
| 부분과 전체 | 누적 막대 하나 | 대회별 타이틀전/일반 |
| 두 갈래 비율 | 미터 바 하나 + 숫자 | 경기 형식 · 타이틀전 비율 |
| 순위 | 가로 막대 + 표 | 승률 상위 |
원형 차트를 쓰지 않았다. 경기 형식(싱글 83 · 다인전 16)과 타이틀전 비율(52 · 47)은 둘로 갈리는 비율이라 파이의 후보였지만, 두 조각의 각도를 비교하는 것보다 막대 하나의 길이가 정확하게 읽힌다. 채워진 칸 사이에는 2px 표면 간격을 둔다 — 두 색이 맞닿으면 경계에서 밝은 쪽이 번져 비율이 실제와 다르게 보인다.
색은 새로 만들지 않는다
차트 색은 전부 --chart-* 토큰을 가리키고, 그 토큰은 밝기 대역 · 채도 바닥 · 색각 이상 분리 · 표면 대비 다섯 검사를 통과한 값이다(제 4 장). 공용 모듈(chart-theme.tsx)이 격자 · 축 · 툴팁 · 범례를 한 벌로 싸서 내보내므로, 화면은 색을 고르지 않고 seriesColor(index)를 부른다. 화면마다 hex를 적기 시작하면 그 검증이 무의미해진다.
계열 순서는 고정이다. 필터로 계열이 줄어도 색이 사람을 따라가고, 일곱 번째 계열에 새 색을 만들지 않는다 — 그 전에 “기타”로 접는 것이 맞다.
툴팁의 글자는 잉크 토큰이고 계열 색은 옆의 점이 든다. 값을 계열 색으로 칠하면 대비가 계열마다 달라져 어떤 줄은 안 읽힌다.
차트 옆에 표를 함께 둔다
승률 화면은 가로 막대와 표를 같이 세운다. 색을 못 읽는 경우에도 값이 남아야 하기 때문이다. 표의 숫자는 tabular-nums로 자릿수를 맞춘다.
표본 수를 적을 자리를 강제했다
차트 액자 컴포넌트(ChartFrame)는 제목 · 설명 · 표본 각주(note) 세 칸을 갖는다. 각주 칸이 구조에 있어서, 차트를 넣는 사람이 표본을 적을지 말지 고민하지 않는다. 99경기짜리 데이터에서 숫자만 크게 걸면 실제보다 단단해 보인다.
그리지 않은 차트
연도별 추이를 만들지 않았다. DB의 대회가 전부 2026년이라 점이 하나뿐이고, 한 점짜리 선그래프는 추이가 아니라 장식이다. 화면은 그 자리에 차트 대신 왜 없는지를 한 줄로 적는다.
/admin에 있던 목업 차트(하드코딩 레드 · 인라인 툴팁 스타일)도 베끼지 않았다. 그것은 가짜 데이터를 그리던 자리이고 스타일도 토큰 이전 것이다.
넓은 차트는 자기 안에서 가로로 스크롤한다. 페이지 본문이 옆으로 밀리면 모바일에서 화면 전체가 흔들린다.