- 발행일
운영 2,732건이 92,948px 캔버스를 만들었다 — 데이터 맵 잎을 이름 접두어로 묶기까지
운영 2,732건이 92,948px 캔버스를 만들었다
데이터 맵은 트리맵 타일을 눌러 내려가는 화면입니다. 클래스 타일을 누르면 그 아래 인스턴스가 부채꼴로 펼쳐져요. dev에서는 kadif:DataFrame이 14건이라 예뻤습니다.
운영 데이터를 미러하고 kadif:DataFrameField 타일을 눌렀습니다. 캔버스 높이가 92,948px였어요. 인스턴스 2,732개를 부채꼴 잎으로 세로로 쌓은 결과입니다.
// renderClassTree — 상한이 없었다
const height = Math.max(420, kids.length * 34 + 60); // 2,732 × 34
부채꼴은 원래 클래스의 구성 속성을 그리려고 짠 배치입니다. 수십 개 규모예요. 인스턴스 단계가 같은 배치를 물려받으면서 모집단이 두 자릿수에서 네 자릿수로 뛰었습니다.
목록으로 보내지 않은 이유
처음 생각은 간단했습니다. 임계값을 넘으면 부채꼴을 아예 안 그리고 표준데이터 목록으로 보내자. 페이징·검색·유형 칩이 이미 있는 화면이니 그 규모를 훑는 일은 그쪽이 낫습니다.
| 방법 | 장점 | 포기하는 것 | 판단 |
|---|---|---|---|
| ① 임계값 초과 시 목록으로 보냄 | 이미 있는 화면 재사용 | 그림이 사라짐. 이 화면에 온 이유가 분포를 보는 것 | 기각 |
| ② 잎은 다 그리고 스크롤 상자로 묶음(임계 60) | 잎 손실 없음 | 2,732개를 여전히 다 그림. 상자 안 스크롤이 길기만 함 | 같은 날 오전 채택 → 오후 대체 |
| ③ 이름 접두어로 묶음 단을 끼움(임계 30) | 한 단이 30개 이하로 유지 | 묶음 라벨은 이름이 아니라 접두어. 단이 깊어짐 | 채택 |
이 화면에 오는 사람은 분포를 보러 옵니다. 그림을 없애면 트리맵에서 타일을 누른 이유가 사라져요. ①은 그래서 기각됐습니다.
②를 먼저 만들었습니다. 잎이 60개를 넘으면 캔버스를 감싼 상자를 max-h-[36rem]으로 잡고 상자 안에서 스크롤하게, 상자 밖에는 목록에서 N건 보기 → 한 줄. 문서 138절에 그렇게 적었어요.
그리고 같은 날 139절로 대체했습니다. 실규모로 보니 상자 안을 아무리 스크롤해도 2,732개는 2,732개였어요. 스크롤 상자는 더 못 쪼개는 동명 무리의 대비책으로만 남겼습니다.
접두어로 묶는다 — 한 단의 예산은 30
// 부채꼴 한 단이 감당하는 잎의 수. 넘으면 이름 접두어로 한 단을 더 끼운다(bucketizeTree).
// 30 은 "그보다 많으면 부채꼴이 답이 없다"는 실측 판단이다 — 101개를 세로로 쌓으면
// 캔버스가 3,494px 이 되고 뿌리가 화면 밖으로 나간다.
const FAN_MAX_LEAVES = 30;
잎이 30개를 넘는 자리에 bucketizeTree가 묶음 단을 끼웁니다. 클래스 → DF_A 33건 · DF_B 10건 … → 인스턴스 순으로 내려가요. 표준데이터 이름에는 클래스별 접두어(DF_, DE_, MSG_)가 있어서, 그 다음 첫 영문자부터 묶음 키로 씁니다.
단이 둘로 갈립니다. 묶음 단은 트리맵 타일, 이름을 읽는 마지막 단만 부채꼴. 처음엔 묶음까지 부채꼴로 그려 봤는데 곡선 29개로 그린 목록일 뿐 분포가 안 읽혔어요. 후보 넷(묶음 트리맵·가로 아이시클·알파벳 색인 그리드·현행 부채꼴)을 놓고 트리맵을 골랐습니다. 렌더러가 이미 있고, "타일 클릭=드릴다운" 문법이 상위 클래스 단계와 같아서예요.
// 단, 자식이 이름 묶음(bucketizeTree)이면 부채꼴 대신 **트리맵**으로 떨어뜨린다 —
// 묶음 단이 답하는 것은 이름이 아니라 "어느 묶음이 큰가"라 면적이 말하는 편이 낫고,
// 타일 클릭=드릴다운 문법도 상위 클래스 단계와 같다(2026-09-01 사용자 선택).
// 이름을 읽는 마지막 단(잎 ≤ FAN_MAX_LEAVES)만 부채꼴로 남는다.
한 글자씩만 깊어지는 사슬
실규모로 보니 또 다른 게 보였습니다. 이름이 촘촘한 구간이 한 글자씩만 깊어지는 사슬이 됐어요.
데이터 프레임 → DF_R 131건 → DF_RE 77건 → DF_REG 61건 → DF_REG- 42건 → 잎
5~6클릭입니다. 클릭할 때마다 타일 하나가 거의 전부를 먹고, 그걸 눌러도 또 하나가 거의 전부를 먹어요.
// 한 단의 예산(FAN_MAX_LEAVES) 안에서 넘치는 묶음을 같은 단에 미리 펼친다 —
// 안 하면 이름이 촘촘한 구간이 한 글자씩만 깊어지는 사슬이 된다
// (운영 실측: 데이터 프레임 → DF_R 131건 → DF_RE 77건 → DF_REG 61건 → … 5단).
// 못 쪼개는 묶음(더 볼 글자가 없거나 예산 초과)은 그대로 두고 재귀·스크롤 상자가 받는다.
묶음을 만들 때 한 단의 예산 30 안에서 넘치는 묶음을 같은 단에 미리 펼칩니다. 큰 것부터, 예산을 넘거나 더 쪼갤 글자가 없으면 중단. 최상위 묶음이 23개에서 29개로 늘고, DF_RE 안의 3단 사슬이 22개 묶음 한 단이 됐습니다. 최악 경로 5~6클릭이 3클릭.
작은 묶음이 늘어나는 대신 단이 얕아지는 트레이드오프예요. 묶음도 잎도 알파벳 순이라 색인처럼 훑는 데는 지장이 없다고 봤습니다.
티끌은 '그 외' 하나로
트리맵 면적이 건수 비례라 전체의 1% 미만 묶음은 라벨을 못 싣습니다. DE_Z 1건은 0.15%예요. 파란 조각으로만 남아서 뭔지 알 수 없었습니다.
// 티끌 꼬리 합치기 — 트리맵 면적이 건수 비례라 전체의 1% 미만 묶음은 라벨도 못 싣는
// 파란 조각으로 남는다(운영 실측: DE_Z 1건 = 0.15%). 그런 묶음이 둘 이상이면 작은
// 것부터 '그 외' 한 타일로 합친다. 합계는 예산(FAN_MAX_LEAVES) 이하로 지켜 클릭하면
// 곧장 이름 부채꼴로 떨어진다.
그런 묶음이 둘 이상이면 작은 것부터 '그 외' 한 타일로 합칩니다. 합계를 예산 이하로 지켜서 '그 외'를 누르면 곧장 이름 부채꼴이 나와요. 데이터 엘리먼트 기준 타일 29개가 24개로, 무라벨 타일 0개.
'그 외'는 색인의 일부가 아니라 꼬리라 글자 순에 안 끼우고 맨 뒤에 둡니다. 트리맵 배치는 어차피 면적순이라 오른쪽 아래로 가긴 해요.
이 그림을 보려면 운영 규모가 먼저 필요했다
dev에는 kadif:DataFrame이 14건뿐이라 묶음 단이 실제로 어떻게 갈리는지 볼 수 없었습니다. 그래서 운영 공개 API 두 개로 인스턴스를 통째로 가져오는 미러 명령을 먼저 만들었어요. 인스턴스 6,738건·값 36,468건·링크 21,127건. 다만 이 미러 명령 자체는 커밋하지 않았습니다. 수치는 문서에만 남았고, 명령은 작업 트리에서 바이트코드만 남은 채 유실됐어요.
추가만 하고 지우지 않습니다. dev 인스턴스에 신청서 항목·OID 연결이 CASCADE로 붙어 있어서 삭제 미러는 그것까지 지워요. 대응은 (클래스, 이름) 묶음 안에서 pk 순서끼리 맞춥니다. 재실행해도 같은 짝이 나오는 걸 확인했어요.
한 가지 함정이 있었는데, 그래프 API의 rdfs:label은 동명 구분 표기가 붙은 표시용이라 이름으로 못 씁니다. 실측 3,033건이 불일치했어요. 원본 이름은 다른 엔드포인트에서 가져와야 했습니다.
같은 날 아침, 첫 화면 모집단도 고쳤다
이 작업 직전에 잡은 게 하나 있습니다. 통합 탭의 첫 화면이 데이터 범주의 클래스 전부를 타일로 세우고 있어서 그릇인 dcat:Dataset까지 타일이 섰어요. 목록 화면과 홈의 건수는 발행 단위 세 클래스(STD_DATA_TYPE_CURIES)로 8월 29일에 확정돼 있었는데, 맵만 다른 걸 세고 있었습니다.
def test_container_classes_are_excluded(self):
"""그릇(`dcat:Dataset`)은 발행 단위가 아니다 — 목록·홈 건수와 같은 모집단을 쓴다."""
두 화면이 198건과 134건으로 세던 그 문제의 세 번째 화면이었어요. 이제 세 화면이 같은 상수를 봅니다.
이튿날 — 핵심어 탭을 붙였다가 그날 저녁에 뺐다
9월 2일 오후에 데이터 맵에 핵심어 탭을 붙였습니다. dcat:keyword 축으로 핵심어 → 클래스 → 이름 묶음 → 표준데이터 4단. 불용어·수치·한 글자·활용형을 거르고 대소문자를 접었어요.
같은 날 저녁에 그 탭을 뺐습니다. 탭은 다시 통합·카탈로그·개념체계 셋이에요.
// 🔄 2026-09-02 — 하루 붙였던 데이터 맵의 `핵심어` 탭을 다시 뺐다. 핵심어는 별도 축이 아니라
// **통합 탭이 보여 주는 내용**이 됐다(같은 날 결정). 두 화면의 기대값은 다시 셋으로 같다.
통합 탭의 첫 화면은 아침에 고친 그 세 클래스 타일이었는데, 세 칸의 면적이 곧 세 값이라 맵으로 볼 것이 없었습니다. 세 개 숫자를 보려고 트리맵을 그릴 이유가 없어요. 핵심어 분포를 통합 탭 내용으로 옮기고 클래스 단은 핵심어 아래로 한 단 내려갔습니다. 아침에 모집단을 고친 화면이 저녁에는 첫 화면에서 사라진 셈이에요.
소스 키는 std로 유지했습니다. ?src=std 북마크와 e2e 선택자를 보존하려고요. 없어진 ?src=keyword는 통합 탭으로 열립니다.
검증
- dev 서버 실측 — 통합 탭
kadif:DataFrame(688건) 묶음 트리맵 22타일, '그 외' 합침 후 한 화면에 들어가고 무라벨 타일 없음. DF_RE타일 → 하위 묶음 트리맵,DF_REG-R·DF_B타일 → 부채꼴 이름 나열, 잎 클릭 시 상세 이동에back_url로 묶음 경로가 보존되는 것 확인.- 3단 딥링크
?p=kadif:DataFrame/DF_RE/DF_REG-R복원, 검색어DF_Reg-RoadSideAlert가 묶음 단을 뚫고 잎을 강조하는 것 확인. - 온톨로지 화면의 「표준데이터 목록 보기」가 같은 트리맵을 그리는 것 확인. 이 경로는
setRootData를 안 거쳐 묶음 단이 안 끼었던 걸 응답 직후 직접 부르게 메웠습니다. - 미러는 dry-run → 실행 → 재실행(생성 0건)으로 멱등 확인.
- 첫 화면 모집단은
test_top_level_is_the_std_data_classes·test_container_classes_are_excluded두 건으로 잠금.
남은 것 · 한계
- 이름이 같은 인스턴스가 많습니다.
kadif:DataFrameField2,732건 중 1,033건이 같은 이름이에요. 프레임마다DEFAULT필드가 하나씩 있어서요. 어떤 배치를 써도 같은 글자가 반복되므로, 이 모집단을 그림으로 훑어 찾는 것은 애초에 안 됩니다. 찾기는 목록의 일이에요. 스크롤 상자는 그런 무리를 받는 대비책으로만 남았습니다. - 묶음 라벨은 완성된 이름이 아니라 접두어입니다. 부채꼴에서는 뒤에
…를 붙여 표시하는데, 트리맵 타일에는 안 붙습니다.DF_RE가 무슨 뜻인지는 화면이 말해 주지 않아요. - 같은 날 두 번, 이튿날 두 번 바꿨습니다. 스크롤 상자(138절)가 오후에 묶음 단(139절)으로, 핵심어 탭이 저녁에 통합 탭 내용으로. 문서에는 대체 표시를 남겼지만, 그 사이 스펙과 기대값이 따라 움직인 흔적이 e2e 파일마다 🔄 주석으로 남아 있습니다.
- 미러는 추가만 합니다. 그리고 그 명령은 저장소에 없습니다. 운영에서 지워진 인스턴스가 dev에는 남아요. 재실행이 멱등인 건 확인했지만, dev와 운영이 시간이 갈수록 갈라지는 건 막지 못합니다.
- 핵심어 정리 규칙(불용어·수치·한 글자·활용형)이 코드에 박혀 있습니다. 조사가 붙은 "객체의"와 과거형 "검지된"을 걸러내는 식인데, 목록에 없는 활용형은 그대로 타일이 됩니다.
관련 글: Django 프로젝트에 D3.js 시각화 붙이기 — 워드클라우드, 히트맵, 데이터 맵 · 같은 표준데이터를 두 화면이 198건과 134건으로 세고 있었다 · 데이터 맵에서 세 단계 들어간 자리를 주소에 남겼다 · 인스턴스 트리를 가상 스크롤로 옮겼다 · 관계도 이웃을 6개씩 펼치기로 했다가 같은 날 폐지했다