- 발행일
연결할 표준데이터가 목록에 안 보인 이유 — 관계에서 거꾸로 뽑던 후보를 전체 조회로
연결할 표준데이터가 목록에 안 보인 이유
이 글은 2026년 9월에 당시 커밋 이력을 다시 보며 정리한 것입니다. 날짜는 작업한 날 기준입니다.
OID 관리 화면에는 "연결된 표준데이터" 구획이 있습니다. 노드를 고르고 편집을 누르면 왼쪽에 후보 목록, 오른쪽에 선택된 목록이 나오는 이중 선택기예요. 여기서 방금 만든 표준데이터를 연결하려는데 후보에 없었습니다. 검색해도 안 나와요.
후보를 관계 테이블에서 거꾸로 뽑고 있었다
후보 목록을 채우는 함수의 주석이 이유를 그대로 말하고 있었습니다.
// 현재 제공되는 Dataset 전체 리스트 API가 없어서
// OID-Dataset 관계 목록을 통해 Dataset 정보를 역으로 수집한다.
const res = await $.ajax({
url: "/std-data/rest-api/oid-dataset/",
oid-dataset은 OID와 표준데이터를 잇는 관계 테이블입니다. 거기서 dataset 필드를 모아 중복을 걸러내면 표준데이터 목록처럼 보이긴 해요. 하지만 그 목록에 들어가려면 이미 어떤 OID에든 한 번은 연결돼 있어야 합니다. 처음 연결하려는 표준데이터는 정의상 나올 수 없었어요.
이 주석을 쓴 것도 저였습니다. 며칠 전 연결 구획을 만들면서 "전체 목록 API가 없다"고 적고 우회했는데, 그 우회가 어떤 데이터를 못 보여 주는지는 그때 생각하지 않았습니다.
처음 만든 엔드포인트
없으면 만들면 됩니다. 이때까지 REST 쪽은 동료가 맡아서 뷰셋 기반 클래스, 역할 기반 권한, 라우터 등록까지 다 갖춰 놓은 상태였어요. 저는 그 위에 표준데이터 하나를 얹었습니다.
class DatasetViewSet(LoggingModelViewSet):
queryset = Dataset.objects.select_related('owner_organization', 'creator').all()
serializer_class = DatasetSerializer
permission_classes = [RestApiRolePermission]
def get_queryset(self):
queryset = super().get_queryset()
# 상태 필터링
state = self.request.query_params.get('state', None)
if state is not None:
queryset = queryset.filter(state=state)
뷰셋 57줄, 시리얼라이저 25줄, 라우터 두 줄. 화면 쪽은 주소를 바꾸고 역추출 코드를 지우는 것으로 끝났습니다.
- const seen = new Set();
const datasets = [];
const list = Array.isArray(res) ? res : res.results || [];
list.forEach((item) => {
- const id = item.dataset;
- const title = item.dataset_title || item.dataset_name || "";
- if (id && !seen.has(id)) {
- seen.add(id);
+ const id = item.id;
+ const title = item.title || item.name || "";
+ if (id) {
state·private·owner_organization 필터 셋을 뷰셋에 넣었는데, 화면은 그중 하나도 쓰지 않았습니다. 후보 목록은 전체를 그대로 받아요. "있으면 좋겠다"로 만든 필터였고, 쓰는 쪽을 같이 만들지 않았습니다.
이 커밋의 메시지는 "상황 / 해결 / 효과"를 이모지 번호로 나눠 쓴 긴 글입니다. 그 시절 제 커밋 메시지가 그랬어요. 지금 보면 diff를 그대로 옮겨 적은 부분이 절반입니다.
같은 날 오후, 설정을 통째로 덮어써서 주소를 잃었다
그날 16:30에 화면 스크립트에 박혀 있던 API 주소들을 템플릿의 window.config.api로 옮겼습니다. 공용 OID 매크로가 이 객체를 만들어요. 그런데 관리자 목록·상세 템플릿은 자기 몫의 설정을 이렇게 쓰고 있었습니다.
window.config = {
initialTreeData: `{{ (data.core if data.core else []) | safe }}`,
targetUrl: "{{ 'std-data:oid-list' | url }}",
baseUrl: "{{ 'web-admin:std-data:oid_list' | url }}",
detailUrl: "{{ 'web-admin:std-data:oid_detail' | url(args=[0]) }}",
isAdmin: true
};
=로 대입하니 공용 매크로가 넣어 둔 api가 사라집니다. 스크립트는 window.config.api.oid.list를 읽는데 api가 없으니 거기서 멈춰요. 9분 뒤 병합으로 바꿨습니다(14e690f5).
window.config = Object.assign(window.config || {}, {
initialTreeData: `{{ (data.core if data.core else []) | safe }}`,
...
isAdmin: true,
api: Object.assign({}, (window.config && window.config.api) || {}, {
oid: {
list: "{{ 'std-data:oid-list' | url }}",
전역 객체 하나를 템플릿 두 곳이 나눠 채우는 구조라, 어느 쪽이 먼저 실행되든 상대 것을 지우지 않아야 했습니다. Object.assign으로 얕게 합치고, api는 한 겹 더 합쳤어요.
검증
- 아무 OID에도 연결되지 않은 표준데이터를 하나 만들고, 관리 화면 후보 목록에 나오는지 확인했습니다. 고치기 전에는 안 나왔어요.
- 관리자 목록·상세 두 화면에서 트리와 연결 구획이 다시 그려지는지 확인했습니다. 덮어쓰기 상태에서
api가 없어 스크립트가 멈춘다는 건 코드로 확인한 것이고, 당시 콘솔을 본 기록은 없습니다. - 서버 테스트는 쓰지 않았습니다. 브라우저에서
/std-data/rest-api/dataset/을 열어 JSON이 오는지 봤어요.
남은 것 · 한계
- 필터 셋을 만들고 하나도 안 썼습니다. 후보에 등록 상태가 아닌 표준데이터도 섞여 나옵니다.
?state=ACT한 줄이면 됐는데 그때는 붙이지 않았어요. - 페이지네이션을 안 봤습니다. 응답이
results로 올 수도 있다고 방어는 했는데, 다음 쪽을 읽는 코드는 없어요. 표준데이터가 많아지면 후보가 잘립니다. - 전역
window.config를 여러 템플릿이 채우는 구조 자체는 그대로입니다. 병합으로 덮어쓰기는 막았지만, 같은 키를 두 곳이 다른 값으로 넣으면 나중 것이 이깁니다. - 이 엔드포인트가 다루는
Dataset은 뒤에 표준데이터 모델이 인스턴스 기반으로 바뀌면서 레거시가 됐습니다. 2026년 7월에 동료가 OID 연결을 인스턴스 쪽으로 옮기면서 이 후보 목록도 다른 API를 보게 됐어요.
관련 글: 4단계 Creation Wizard UX 구현 회고2026-01-054단계 Creation Wizard UX 구현 회고 — 상태·모드 분리와 완전 종료 문제다단계 입력이 필요한 등록 폼에 Creation Wizard 모드와 일반 편집 모드를 분리해서 구현한 과정을 정리합니다. 상태 관리, 트리 연동, "완전 종료" 버그까지. · OID 트리를 jsTree에서 조직도로 갈아탔다2026-08-12OID 트리를 jsTree에서 조직도로 갈아탔다 — 네 화면 중 두 곳만OID는 번호로 갈라지는 계층인데 세로 목록 트리로는 형제 순서도 계층 폭도 안 읽혔습니다. d3.hierarchy로 가로 조직도를 새로 그렸는데, 네 화면 중 두 곳만 바꿨어요 — 경로 바가 있느냐로 갈랐습니다. 그리고 나서 트리 전체를 다 그리다 SVG가 12,691px가 됐습니다. · OID 하나에 표기법 넷2025-12-12OID 하나에 표기법 넷 — 같은 계산이 세 곳에 있었고, 네 번째 표기를 세 곳에 더했다객체 식별자 하나를 점 표기법·ASN.1·IRI·URN 네 가지로 보여 줘야 했습니다. 동료가 만든 세 표기법에 IRI를 넷째로 더했는데, 조상 경로를 이어 붙이는 같은 계산이 모델·매니저·화면 스크립트 세 곳에 있었고 저는 세 곳에 한 줄씩 더 얹었어요. 왜 세 곳인지는 나중에 이해했고, 그때는 걷어내지 않았습니다. · 트리에서 뺀 신청 상태 표시를 일주일 뒤 되살렸다2025-12-29트리에서 뺀 신청 상태 표시를 일주일 뒤 되살렸다 — 지운 커밋 해시를 커밋 메시지에 적었다OID 트리 노드 이름 뒤에 [신청중] 같은 상태를 붙이던 코드를 76줄 지웠다가 일주일 뒤 되살렸습니다. 되살린 커밋 메시지에 지운 커밋의 해시를 "깃 참고"로 적어 뒀어요. 그런데 되살린 건 같은 것이 아니었습니다. 워크플로 상태 대신 모델 필드, 모두에게 대신 관리자에게만. 35분 뒤엔 공용 파서에서 빼서 화면 전용 후처리로 옮겼습니다.