발행일

탭의 '전체'는 조건 없음이 아니라 합집합이었다 — 한 낱말의 세 가지 뜻과 11분 만에 뒤집은 결정

탭의 '전체'는 조건 없음이 아니라 합집합이었다

표준데이터 검색 화면에는 탭이 셋 있습니다. 통합 검색, 카탈로그, 개념체계. 이 글은 뒤의 두 탭 첫 칸에 서 있는 "전체 카탈로그", **"전체 개념체계"**라는 선택지가 무엇을 뜻하는지를 놓고 9월 4일 저녁에 한 번 정하고 11분 뒤 다시 정한 이야기입니다.

낱말 하나가 왜 문제였는지부터 말해야 해요. 그 전날 두 탭의 정체가 바뀌었고, 다음 날 또 바뀌었거든요.

하루 사이에 두 번 바뀐 탭의 정체

9월 3일 저녁, 두 탭을 트리에서 카드 목록으로 바꿨습니다(c61135ca). 카탈로그 탭에는 dcat:Catalog 카드가, 개념체계 탭에는 skos:ConceptScheme 카드가 서는 구성이에요. 트리 뷰 두 개와 노드 정보 엔드포인트, 트리 전용 e2e 5건을 지웠고 2,466줄이 빠졌습니다.

다음 날 오후 2시 37분, 되돌렸습니다(db1a819c). 기획 결정이었어요. 세 탭은 같은 모집단을 보고, 좁히는 축만 다르다. 필터를 안 걸면 통합 검색과 같은 1,489건이 서고, 걸면 그 축으로 좁혀집니다. 같은 조건이면 탭이 달라도 건수가 같아야 해요. /catalog/?catalog_id=3/dataset/?catalog_id=3이 둘 다 99건.

두 뷰에 남은 건 scope_axis 한 줄입니다. 모집단·카드 링크·유형 칩을 통합 검색과 같게 맞췄고, 전날 "두 탭의 목록은 표준데이터가 아니라 칩이 전부 0"이라는 이유로 뺐던 유형 칩이 세 탭에 다시 섰습니다.

여기까지는 되돌림입니다. 문제는 그다음이에요. 세 탭이 같은 모집단이라면, 카탈로그 탭에서 아무것도 안 골랐을 때 통합 검색과 뭐가 다른가?

'전체'의 세 가지 뜻

첫 화면에 나오는 것문제
① 조건 없음모집단 1,489건 전부카탈로그에 안 담긴 표준데이터까지 카탈로그 탭에 선다. 통합 검색과 구분이 안 됨
② 첫 뿌리 하나이름순 첫 뿌리 카탈로그의 것뿌리가 여럿이면 나머지는 골라야 보임. 칸이 "고른 채"로 뜸
③ 모든 뿌리의 합집합어느 카탈로그에든 담긴 것분류 안 된 데이터는 이 탭에 없음. 칸은 "전체"로 서 있음

되돌린 직후의 상태가 ①이었습니다. 저녁 8시 50분에 이걸 버그로 잡았어요(8cf85395). 커밋 제목이 "카탈로그 탭 첫 화면이 카탈로그 밖 표준데이터까지 세던 것"입니다. 탭의 취지가 카탈로그로 분류된 데이터를 보는 것이니 조건 없음은 없다는 데까지 기획과 합의했고, 그 자리에 ②를 넣었습니다.

# 카탈로그 탭에 '조건 없음' 은 없다 — 탭이 보겠다는 것이 카탈로그로 분류된 표준데이터라,
# 카탈로그를 안 고른 채 오면 첫 뿌리 카탈로그가 걸린다(기획 결정 2026-09-04). 뿌리
# 하나를 잠가 두는 대신 1차 칸에서 고르게 둔다 — 최상위가 여럿이 될 수 있어서다.
if self.scope_axis == "catalog" and not catalog_ids:
    catalog_ids = list(
        MetaClassInstanceManager.get_root_catalog_queryset(public_only=True)
        .order_by("name", "id")
        .values_list("pk", flat=True)[:1]
    )

1차 칸에서 "전체" 선택지를 지우고, 뿌리끼리 고를 수 있게 잠금을 풀었습니다. 테스트 이름도 test_catalog_tab_defaults_to_first_root_but_roots_are_selectable이었어요.

11분 뒤에 ③으로 바꿨습니다(0eaa1b9d, 밤 9시 1분).

왜 11분 만에 뒤집었나

②의 결함은 데이터가 아니라 화면에 있었습니다. 첫 뿌리가 골라진 채로 뜨면, 사용자는 자기가 고른 적 없는 조건이 걸려 있는 화면을 봅니다. 칸에 뿌리 이름이 서 있고, 목록은 그 뿌리 것만이에요. 뿌리가 둘이면 두 번째 뿌리의 데이터는 화면 어디에도 없습니다. "전체"라는 선택지가 없으니 둘을 한꺼번에 볼 방법도 없고요.

③은 그 자리에 "전체 카탈로그"를 되살리되, 그 뜻을 조건 없음이 아니라 뿌리 전부의 합집합으로 둡니다. 어느 카탈로그에도 안 담긴 표준데이터는 여전히 이 탭에 없어요. ①과 다른 점입니다.

# 축 탭에 '조건 없음' 은 없다 — 탭이 보겠다는 것이 그 축으로 분류된 표준데이터라,
# 축을 안 고른 채 오면 첫 칸의 '전체 …' 가 곧 조건이다. 카탈로그 탭은 **뿌리 카탈로그
# 전부**, 개념체계 탭은 **모든 체계**의 합집합(기획 결정 2026-09-04). 어디에도 안
# 분류된 표준데이터는 축 탭에 없다. 최상위가 여럿이 돼도 한꺼번에 보거나 하나만 고를
# 수 있다. 고른 것으로는 보고하지 않는다 — 칸은 '전체' 로 서 있어야 하고, 검색 폼
# hidden 에 id 가 실리면 다음 제출부터 첫 항목이 골라진 것처럼 보인다.

마지막 두 줄이 ②에서 배운 겁니다. 서버가 기본 조건으로 뿌리들을 걸더라도 화면에는 "고른 것"으로 보고하지 않습니다. selected_catalog_ids가 빈 목록이에요. 걸린 조건을 검색 폼 hidden에 실어 보내면 다음 제출부터 첫 항목이 골라진 것처럼 보이는 문제가 ②에 있었거든요.

"전체" 선택지 옆 건수도 합집합으로 맞췄습니다. 모집단 크기를 두면 고른 뒤 목록과 어긋나요.

# 축 탭의 '전체 …' 는 뿌리(체계) 전부의 합집합이라 그 건수를 말해야 한다
# (`scope_selection` 의 기본 조건과 같은 집합). 모집단 크기를 두면 고른 뒤 목록과 어긋난다.
if groups and self.scope_axis in ("catalog", "concept"):
    ...
    classified = set().union(*groups[axis].values())
    data["total"] = len(classified & population)

개념체계 탭도 같은 규칙입니다. 체계 목록을 필터 칸과 기본 조건이 같은 집합으로 보도록 get_scheme_queryset을 한 곳으로 뺐어요.

같은 저녁에 걷어낸 것 둘

이 결정 앞뒤로 두 가지를 더 지웠습니다.

하나는 필터 선택 2차 드롭다운입니다(c835e226). 검색박 왼쪽에 조건을 걸어야만 나타나던 드롭다운인데, 레거시 카탈로그 검색에서 체크박스로 여러 개를 고른 뒤 그중 하나만 남겨 보던 장치였어요. 지금 화면에서는 카탈로그·개념체계 탭의 칸이 단일 선택이라 걸리는 항목이 늘 하나고, 통합 검색에는 조건 칩이 그 역할을 이미 합니다. 최신 선택 하나만 들어 있는 드롭다운이었어요. 198줄이 빠졌고, ?filter_selected=는 더 이상 읽지 않습니다.

다른 하나는 dev 데이터입니다(3e23a4c6). 운영에는 카탈로그에 담긴 표준데이터가 0건이에요. dev는 전날 적재기가 표준을 다리 삼아 메시지 12건을 매달아 99건이 보였는데, 운영처럼 0건이어야 한다는 결정으로 그 단계와 링크를 지웠습니다. 그래서 카탈로그 탭 첫 화면은 지금 "전체 카탈로그"가 걸린 채 0건입니다. e2e는 0건이면 건너뛰도록 해 뒀고, 관리 화면에서 소속이 걸리기 시작하면 저절로 풀립니다.

사흘 뒤, 4분짜리 결정 하나 더

9월 7일에 같은 칸에서 비슷한 일이 한 번 더 있었습니다. 개념체계 칸(이날 "정보 주제"로 이름이 바뀌었어요)에서 0건 체계를 뿌리 목록에서 빼는 커밋을 올렸다가(b8bd291d, 18:43) 4분 뒤 되돌렸습니다(429ff6c2, 18:47). 골라도 빈 목록인 뿌리가 칸만 길게 하니까 뺐는데, 기존 체계 둘이 0건인 채로 보이는 게 맞다는 쪽으로 돌아갔어요.

그리고 15분 뒤 뿌리 자체를 바꿨습니다(8b774c1c). dcat:theme의 값은 skos:Concept이라 사용자가 고르는 것도 개념이고, 체계는 그 개념을 담은 그릇일 뿐입니다. 트리에서 체계 단을 없애고 모든 체계의 최상위 개념을 이어 붙여 뿌리로 세웠어요. 값이 달린 개념(count > 0)을 앞에 둡니다. 0건 체계를 숨길지 말지는 이러면 애초에 질문이 안 됩니다. 4분 만에 되돌린 결정이 15분 뒤 문제 자체를 없애는 결정으로 이어진 셈이에요.

검증

  • 단위 테스트 test_catalog_tab_defaults_to_all_roots_and_roots_are_selectable — 이름순으로 앞서는 뿌리를 하나 더 만들고, 기본 화면에 두 뿌리의 데이터가 다 나오는지, selected_catalog_ids가 빈 목록인지, "전체" 건수가 2인지 확인합니다. ②의 테스트(..._defaults_to_first_root_...)를 이름부터 갈아 끼웠습니다.
  • test_scheme_tab_defaults_to_all_schemes — 어느 체계에도 안 분류된 데이터가 개념체계 탭에는 없고 통합 검색에는 있는지, 두 체계에 걸친 데이터가 한 번만 세어지는지(합집합 4건).
  • e2e std-data-axis-tabs.spec.js 9개 — "같은 조건이면 탭이 달라도 건수가 같다"를 잠급니다. 카탈로그 탭은 0건이면 건너뜁니다.
  • dataset-list-scope-filter.spec.js — "전체 카탈로그" 선택지가 돌아왔는지.

남은 것 · 한계

  • ②를 커밋한 뒤 화면을 열어 보고 11분 만에 ③으로 바꿨습니다. 기획과 같은 날 두 번 정한 셈인데, 첫 번째는 화면을 안 보고 정했습니다. 첫 뿌리가 골라진 채 뜬 화면을 보는 순간 틀렸다는 걸 알았어요. 커밋 전에 봤어야 합니다.
  • 카탈로그 탭은 지금 0건입니다. 운영과 같은 상태라 의도된 것이지만, 이 탭의 첫 화면이 빈 상태인 채로 배포되는 겁니다. "전체 카탈로그 (0)"이 서 있는 화면이에요.
  • "전체"라는 낱말이 탭마다 뜻이 다릅니다. 통합 검색의 "전체 주제"는 조건 없음이고, 개념체계 탭의 "전체 개념체계"는 합집합이에요. 같은 글자인데 한쪽은 분류 안 된 데이터를 포함하고 다른 쪽은 뺍니다. 사용자에게 이 차이를 말해 주는 곳은 없습니다.
  • 9월 3일의 카드 목록 2,466줄 삭제는 되돌리지 않았습니다. 트리 뷰가 지워진 상태에서 표준데이터 목록으로 돌아갔으니, 카탈로그 자체를 둘러보는 화면은 이제 없어요. 카탈로그 카드에서 통합 검색 필터로 연결하던 링크도 같이 사라졌습니다.
  • 개념체계 칸의 뿌리가 체계에서 개념으로 바뀌면서 scheme_ids는 서버가 계속 받지만 화면이 새로 만들지는 않습니다. 옛 주소와 새 주소가 한동안 같이 살아 있는 상태예요.

관련 글: 같은 표준데이터를 두 화면이 198건과 134건으로 세고 있었다 · 분류 칩 하나를 네 번 갈아엎었다 · 필터 선택지 옆 건수를 어느 모집단으로 셀 것인가 · 상세 화면을 목업에 맞추며 하루에 세 번 뒤집었다