발행일

관계도 11개 화면을 렌더러 한 곳에서 바꿨다 — 클릭이 이동에서 선택으로

관계도 11개 화면을 렌더러 한 곳에서 바꿨다

이 시스템에는 관계도가 11개 화면에 있습니다. 공개 6개, 관리자 5개. 카탈로그·개념체계·카테고리·클래스·표준데이터가 각각 자기 관계도를 갖고 있고, 관리자 쪽은 공개 뷰를 상속해서 같은 그림을 그립니다.

문제는 조작이었습니다. 노드를 클릭하면 그 자원의 화면으로 곧바로 이동합니다. 그래서 "이 노드가 뭐지"를 확인하려고 누르는 순간 보던 그림에서 떠나요. 뒤로 돌아오면 힘 시뮬레이션이 다시 돌아 배치가 달라져 있고, 방금 보던 노드가 어디 있었는지 다시 찾아야 합니다.

호버 툴팁이 있긴 했는데, 마우스를 떼면 사라지니까 그 안의 URI를 복사할 수도 없고 두 노드를 비교할 수도 없었습니다.

클릭이 이동이면 볼 수가 없다

방법장점포기하는 것판단
① 클릭=이동 유지, 툴팁을 더 자세히변경 최소마우스를 떼면 사라지는 건 그대로. 복사·비교 불가기각
② 클릭=이동, 우클릭=메뉴이동이 여전히 한 번에 됨우클릭 메뉴는 아무도 안 찾음. 터치에서는 아예 없음기각
③ 클릭=선택, 다음 행동은 우측 패널에서보는 것과 가는 것이 분리됨. 선택 상태가 유지돼 복사·비교 가능이동이 두 번 클릭이 됨채택
④ 클릭=선택, 더블클릭=이동이동이 여전히 한 동작더블클릭을 이동에 쓰면 펼치기에 줄 조작이 없어짐기각

④를 기각한 게 이 화면의 성격을 정한 셈입니다. 관계도에서 제일 자주 하는 일은 이동이 아니라 "이 노드에 뭐가 더 붙어 있나" 를 보는 것이거든요. 그 동작이 조작 하나를 온전히 가져가야 한다고 봤습니다.

정리하면 이렇게 됐습니다.

조작동작
노드 클릭선택 — 우측 패널에 표시. 이동하지 않는다
노드 더블클릭제자리 펼치기 / 다시 누르면 접기
패널 이 항목으로 이동그 자원의 상세 화면
패널 이 노드 중심으로 보기중심을 옮겨 다시 그림
범례 칩 클릭그 타입 숨기기 / 보이기

툴팁이 하던 일을 패널이 인수했다

호버 툴팁은 지웠습니다. 안에 있던 내용은 우측 선택한 노드 패널로 옮겼어요.

같은 정보인데 성질이 달라집니다. 툴팁은 마우스가 거기 있는 동안만 존재하니까 정보를 담을 수 있는 양이 손의 자세에 묶여 있어요. 패널은 선택을 바꾸기 전까지 남아 있으니 URI를 그대로 두고 복사할 수도, 다른 창과 비교할 수도 있습니다.

패널은 화면에 들어오는 순간 중심 노드를 미리 골라 둡니다. 빈 패널로 시작하면 "여기 뭐가 나오는 자리인지"를 사용자가 먼저 알아내야 하니까요.

화살표가 없어서 방향을 알 수 없었다

공개 화면은 엣지 라벨을 끈 상태였습니다. 관계 속성 이름이 화면을 가득 채워서 껐던 건데, 끄고 나니 A와 B가 이어져 있다는 것만 알고 어느 쪽이 주체인지 알 방법이 없어졌습니다. 관리자 화면은 라벨이 남아 있어 이름으로 읽을 수 있었지만 공개는 아니었어요.

방향 화살표를 새로 넣었습니다. 저장소 전체에 <marker>가 하나도 없던 상태라 새로 만들어야 했고, 라벨을 지우면서 잃은 정보를 라벨 없이 되돌리는 방향입니다.

범례를 읽는 것에서 거르는 것으로

범례는 원래 "이 색이 무슨 타입인지" 알려주는 표였습니다. 컬러 칩으로 바꾸고 누르면 그 타입이 숨겨지게 했어요. 읽기만 하던 것이 도구가 됐습니다.

[● 카탈로그] [● 표준데이터] [● 메시지] [● 카테고리]
     ↑ 누르면 그 종류의 노드가 사라진다

여기에 안전장치를 하나 뒀습니다 — 중심 노드와 최소 한 종류는 남깁니다. 전부 끄면 빈 캔버스가 되는데, 그건 사용자가 원한 결과가 아니라 "실수로 다 껐다"에 가깝고 되돌리는 방법도 화면에 안 보입니다.

설정을 템플릿 set에 두면 include로 안 넘어간다

공용 본문 _d3_graph_body.html.j2는 각 화면이 include로 끼워 씁니다. 노드 크기·범례 순서 같은 그리기 설정은 화면마다 달라서, 처음에는 부르는 쪽 템플릿에서 {% set %}으로 주고 있었어요.

그런데 어떤 화면은 설정이 안 먹혔습니다. 오류는 없고 그냥 기본값으로 그려집니다.

def _instance_graph_context(embedded=False) -> dict:
    """...
    ⚠️ 템플릿의 `{% set %}` 이 아니라 뷰 컨텍스트인 이유가 있다. `_d3_graph_body` 는
    include 로 끼워지는데, `{% block %}`·`{% with %}` 안에서 set 한 값은 그 블록의
    지역 프레임에만 남아 include 로 넘어가지 않는다. 그러면 설정이 조용히 기본값으로
    떨어진다.
    """

{% block %} 안에서 set 한 값은 그 블록의 지역 프레임에 삽니다. include는 그 프레임을 못 봐요. 그래서 같은 set 문이 블록 밖에 있으면 되고 블록 안에 있으면 안 되는 상태였습니다. 화면마다 템플릿 구조가 달라서 어떤 건 되고 어떤 건 안 됐던 거예요.

설정을 뷰로 올렸습니다. 뷰 컨텍스트는 블록 구조와 무관하게 템플릿 전체에서 보이니까요. 딸려서 좋아진 게 하나 있는데, 설정이 파이썬 함수 하나에 모이면서 상세 화면에 심는 경우와 단독 관계도 화면의 차이를 인자 하나로 표현할 수 있게 됐습니다.

관리자 5종이 조용히 갈라지는 것을 막기

관리자 관계도는 공개 뷰를 상속합니다. 편한 구조인데 위험도 같이 옵니다 — 공개 쪽에만 무언가를 붙이면 관리자는 조용히 옛 화면으로 남고, 아무도 오류를 안 봅니다. 관리자 화면은 상대적으로 덜 열어보니까 한참 뒤에 발견돼요.

그래서 관리자 5종이 같은 구조를 쓰는지 잠그는 스펙을 새로 뒀습니다.

// 캔버스와 우측 패널이 나란히 — 패널은 첫 화면부터 중심 노드를 골라 둔다
await expect(page.locator('.graph-canvas svg')).toBeVisible()
await expect(page.locator('#graph-side-panel')).toContainText('선택한 노드')

// 관계 방향 화살표 + 컬러 칩 범례
expect(await page.locator('line[marker-end]').count()).toBeGreaterThan(0)
expect(await page.locator('rect.legend-chip').count()).toBeGreaterThan(0)

// 관리자만의 차이 — 엣지에 관계 속성 이름이 남는다
expect(await page.locator('g.link-labels > text').count()).toBeGreaterThan(0)

마지막 단언이 중요합니다. 공개와 관리자가 갈리는 지점이 딱 하나(엣지 라벨)라는 것을 스펙이 명시해 둬요. 다음에 누가 공개에만 뭔가를 붙이면 앞의 세 단언 중 하나가 빨개지고, 라벨은 갈려도 되는 것이라고 적혀 있습니다.

검증

  • 공개 6 + 관리자 5, 11개 화면 전부 열어 패널·화살표·범례 칩 확인.
  • 관리자 5종은 e2e/scenarios/admin/graph-shared-shell.spec.js 38줄로 잠금. 공개에만 붙이고 관리자에 안 붙인 상태를 일부러 만들어 레드를 먼저 확인했습니다.
  • 더블클릭 펼치기 → 다시 더블클릭 접기로 노드 수가 원래대로 돌아오는 것 확인.
  • 범례 칩을 전부 꺼도 중심 노드와 한 종류가 남는 것 확인.
  • 상세 화면 본문에 심은 관계도에서 뒤로가기 버튼이 안 나오는 것 확인 — 그 화면의 버튼 줄을 쓰기 때문입니다.
  • 변경 범위 18개 파일 1147줄 추가 379줄 삭제. 그중 d3-graph-common.js가 533줄로, 한 파일이 11개 화면을 옮겼습니다.

남은 것 · 한계

  • 11개 중 하나는 규칙이 다릅니다. 클래스 관계도는 더블클릭 펼치기를 안 씁니다 — 원래 두 고리로 한 단계 더를 이미 그리고 있어서 펼칠 게 없거든요. 조작 체계를 통일했다고 썼지만 정확히는 10개입니다.
  • 툴팁을 지워서 훑기가 느려졌습니다. 예전에는 마우스만 굴리면 여러 노드를 빠르게 훑을 수 있었는데, 이제 한 번씩 클릭해야 합니다. 선택 상태가 남는 대가로 훑는 속도를 내줬어요. 어느 쪽이 나은지는 실사용을 안 봤습니다.
  • 조작 안내를 다시 툴팁으로 넣었습니다. 그림 아래 상시 한 줄로 두던 것을 제목 옆 ? 툴팁으로 되돌렸는데, 항목이 다섯 줄이라 늘 깔려 있으면 소음이라고 판단했기 때문입니다. 대신 발견성을 잃었습니다? 를 안 누르면 더블클릭으로 펼칠 수 있다는 걸 모릅니다. 패널 버튼에 관계 펼치기를 같이 둔 게 유일한 대비책이에요.
  • 그 툴팁도 위로 못 폅니다. 감싸는 .model-section에 둥근 모서리용 overflow-hidden이 걸려 있어 머리글 위로 나가는 부분이 잘려요. 아래로 펴는 것으로 피했는데, 레이아웃 제약 때문에 정한 방향이라 다른 화면에 옮기면 다시 봐야 합니다.
  • graph_embedded 같은 분기가 늘면 공용 본문이 다시 갈라집니다. 지금은 하나지만, 화면마다 예외를 이 파일에 더하기 시작하면 "공용"이라는 이름만 남습니다. 몇 개까지가 한계인지는 안 정했습니다.
  • 관리자 스펙은 구조만 봅니다. 패널이 있는지, 화살표가 있는지는 보는데 더블클릭으로 실제 펼쳐지는지는 공개 스펙에만 있어요. 관리자에서 동작이 깨지면 이 스펙은 초록입니다.

관련 글: 노드 두 개가 땅콩처럼 붙었다 — 연쇄 탐색 관계도와 forceCenter · 여러 D3 그래프에 흩어진 줌 로직을 d3ZoomControls.js로 외부화한 회고