- Published on
2025년도~현재 — ITS 표준데이터 관리 시스템(SDMS), 프런트에서 풀스택으로
- Authors

- Name
- Hyo814
ITS 표준데이터 관리 시스템(SDMS) — 2025.04 ~ 현재
한눈에 보기
| 항목 | 내용 |
|---|---|
| 도메인 | ITS(지능형 교통체계) 표준 — ISO 14817-2, V2X, ASN.1 |
| 기간 | 2025.04 ~ 현재 (진행 중) |
| 역할 | 프런트엔드 주력으로 합류 → 모델·마이그레이션·쿼리까지 범위 확장 중 |
| 기술 스택 | Django 4.2, PostgreSQL(운영)·SQLite(개발), django-treebeard, Jinja 템플릿, JavaScript, D3.js, jsTree, Tailwind CSS(v3→v4), Playwright |
| 담당 영역 | 표준데이터 모델링·적재 파이프라인, 분류체계·메타클래스 트리, 표준 시각화, 설문조사 시스템, 통합 검색, Open API 포털 화면, QA 자동화 |
앞의 두 경력기술서와 다른 점을 먼저 적습니다. 앞의 두 회사에서는 프런트엔드였고, 여기서는 화면과 모델을 같이 만집니다. 다만 "풀스택으로 합류했다"가 아니라 "프런트 주력으로 합류해서 백엔드로 넓혀가는 중" 이 정확한 서술이에요. 모델·마이그레이션을 만지다 밟은 지뢰들(SQLite에선 통과하고 PostgreSQL에서 터진 CharField, 없는 관계를 prefetch하고 있었다)을 지우지 않고 기록해뒀습니다.
지표 표기 원칙 — 이 문서의 수치는 전부 측정 과정을 적어둔 개별 글에 근거가 있습니다. 각 절의 링크가 그 근거입니다. 측정하지 않은 것은 수치 없이 적었습니다.
핵심 세 가지를 먼저 꼽으면:
- 표준데이터 모델을 고정 컬럼에서 인스턴스 기반으로 전환 — 표준이 바뀔 때마다 스키마가 흔들리던 구조를 데이터로 흡수 (§1)
- 메타클래스 목록 쿼리 최적화 — 300건 기준 908쿼리·1.6s → 4쿼리·180ms (§3)
- 엑셀 적재 로더 3종 통합 — 중복 로더를 워크북 리더 + 4패스 로더로 합쳐 1,476줄 삭제 (§2)
1. 표준 메타데이터 모델링 — 표준 문서를 스키마로 옮기는 일
문제. ISO 14817-2 기반의 데이터 개념(메타클래스·속성·분류체계)을 DB로 옮겨야 했습니다. 표준 문서는 사람이 읽는 물건이라, 그대로 테이블을 만들면 표준이 개정될 때마다 스키마가 따라 흔들립니다.
한 일.
- 표준데이터를 고정 컬럼에서 인스턴스 기반 모델로 전환했습니다. 컬럼 추가가 아니라 행 추가로 표준 변화를 흡수하는 구조입니다 — 전환 기록
- 속성의 값 제약을 클래스 참조(range)와 데이터 유형으로 분리했습니다 — range 분리 기록
- "필수" 플래그를 전역 속성에서 클래스별 관계로 옮겼습니다. 같은 속성이 클래스에 따라 필수/선택이 달라지는 게 표준의 실제 모습이었어요 — 필수 플래그 이동
- 네임스페이스 옵션을 JSON 상수에서 DB로 옮겨 관리 주체를 코드에서 데이터로 넘겼습니다 — 전환 이유
- 표준 등록 절차는 ISO 14817-2의 7단계 워크플로우로 설계했고, 그 기반이 된 워크플로우 엔진 DB 설계를 먼저 정리했습니다
판단의 기록. 이 절의 결정들은 대부분 "코드에 박을 것인가, 데이터로 뺄 것인가"의 반복이었습니다. 도메인 지식(ITS·V2X 표준 정리)을 먼저 글로 정리하고 모델을 만졌는데, 순서가 그 반대였으면 전환 비용이 훨씬 컸을 거예요.
2. 적재 파이프라인 — 엑셀에서 DB까지
문제. 표준 용어·목업 데이터가 전부 엑셀로 들어옵니다. 2단 병합 헤더, 시트별로 다른 형식, 사람 손을 탄 값들.
한 일.
- 같은 파싱 로직이 복제돼 조금씩 달라진 로더 3종을 공용 워크북 리더 + 4패스 로더로 통합 — 853행 적재 기준, 1,476줄 삭제 / 95줄 추가. 이 작업의 실제 이득은 기능이 아니라 삭제한 줄 수였습니다 — 통합 기록
- 목업 데이터 시드를 수작업 49개에서 엑셀 기반 478개 자동 생성으로 — 자동화 기록
- 정본 데이터가 바뀔 때 파생 캐시를 따라 갱신하는 signal 기반 동기화를 정리했습니다 — signal fanout
밟은 지뢰 두 개. 개발(SQLite)과 운영(PostgreSQL)의 제약 차이로 CharField(512)가 운영에서만 터진 사건 — 벌크 적재는 폼 검증을 타지 않으므로 DB 제약이 유일한 방어선이라는 걸 여기서 배웠습니다(전말). 그리고 존재하지 않는 관계명을 prefetch해 다운로드 기능 네 개를 죽인 사건 — QuerySet 지연 평가 때문에 오타가 런타임까지 살아남는 자리를 AST 대조로 전수 점검했습니다(전말).
3. 분류체계·메타클래스 트리 — 계층 데이터의 저장과 화면
문제. 분류체계와 메타클래스가 전부 트리입니다. 저장(treebeard MP_Node), 조회(N+1), 화면(jsTree) 세 층이 각각 함정을 갖고 있었어요.
한 일.
- 메타클래스 목록의 쿼리를 300건 기준 908쿼리·1.6s → 4쿼리·180ms로 줄였습니다. 속성 개수만 필요한 자리에 prefetch 대신
annotate(Count(...))를 쓴 게 핵심이었어요 — 최적화 기록 - 도메인별로 따로 구현돼 있던 트리 JS 3개를 공통 유틸리티 1개(333줄)로 통합하고, 분류체계·메타클래스 두 트리에 중복돼 있던 서버 로직도 공통 헬퍼 4종으로 추출 — 추출 회고
- jsTree 컨텍스트 메뉴와 모달 편집 흐름을 정리했고(기록), MP_Node의 사용 조건들 — "N+1을 없앴다와 빨라졌다는 다르다", 이동 연산의 영향 범위 — 을 레퍼런스 글로 남겼습니다
- 속성 편집을 모달에서 인라인 폼으로 전환해 편집 왕복을 줄였습니다 — 전환 회고
4. 표준 시각화 — Graphviz에서 D3로
문제. ASN.1 구조와 데이터 개념 관계를 화면에서 탐색할 수 있어야 했습니다. 서버에서 이미지를 굽는 방식(Graphviz)은 확대·탐색·상호작용이 안 됩니다.
한 일.
- ASN.1 트리 렌더링을 Graphviz 정적 이미지에서 D3 인터랙티브 트리로 전환 — 전환 기록
- 관계도의 연쇄 탐색을 붙이고, 노드가 겹치는 레이아웃 문제를 해결 — 관계도 기록
- 화면마다 복제되던 줌 컨트롤을 공용 모듈로 외부화 — 줌 컨트롤 회고
- 통계 대시보드를 "개수 나열"에서 "인사이트 중심"으로 개편했고(대시보드 회고), 워드클라우드를 실제 검색어 빈도 기반으로 바꿨습니다(개편 기록)
5. 설문조사 시스템 재설계 (2025.10 ~ 2026.02)
문제. 설문 로직이 공통 앱(common) 안에 189줄로 섞여 있었습니다. "모델 두어 개니까 공통에 두자"로 시작한 게 요구사항이 늘면서 공통 앱을 비대하게 만들었어요.
한 일. 독립 앱으로 분리하고 3-tier 계층 모델(Template → Section → Question) 기반 7개 모델로 재설계했습니다. Soft delete, 점수 자동 계산, 낮은 점수일 때 사유 필수 입력 같은 실운영 요구사항을 모델 단에 반영했고, 대상자 임시 계정 발급과 이메일 발송은 management command로 자동화했습니다. 관리자 CRUD와 PDF 다운로드(참여자 목록·개별 응답)까지 붙였어요 — 설계 기록
그 글 말미에 직접 찾은 결함 네 가지(PDF 뷰 접근제어 부재로 인한 IDOR, 점수 계산의 N+1, get() 500, 검증 폼이 동적 필드를 빠뜨려 재현 불가)를 적어뒀습니다.
6. 통합 검색 (2026.01 ~ 2026.03)
문제. 단어 검색·계층 검색·필터 검색이 각각 독립 구현돼 있었습니다. 사용자는 "어디서 찾아야 하는지"를 먼저 알아야 검색할 수 있는 상태였어요.
한 일. 7개 엔티티(Dataset·Concept·MetaClass·MetaClassProperty·OID·Catalog·Scheme)를 하나의 TotalSearchView로 통합하고, 결과를 4개 탭(전체·표준데이터·메타데이터·OID)으로 분류했습니다. 검색 범위도 이름 기반에서 메타데이터 값·분류체계·카탈로그 경로까지 넓혔고, 계층 검색에서는 자식 노드를 포함하는 재귀 탐색에 루트 노드 제외 처리를 넣었습니다.
탭별 건수는 Subquery로 집계해 DB 왕복 없이 표시했습니다. 카테고리 수만큼 카운트 쿼리를 날리는 구조였으면 탭 하나 추가할 때마다 쿼리가 늘었을 거예요.
되돌린 것. 처음엔 검색 전용 search 앱을 따로 만들었습니다. 그런데 만들고 보니 리다이렉트 중간 경유지 역할밖에 안 하는 앱이었어요. 앱 경계를 하나 늘린 대가로 얻는 게 없어서 앱을 제거하고 직접 연결로 단순화했습니다. 9절의 RVS 사례와 같은 종류의 판단입니다 — 만든 걸 되돌리는 쪽이 유지하는 것보다 싸다고 봤어요.
7. Open API 포털 — 외부 제공 화면
한 일.
- 목업 데이터로 먼저 만든 화면을 실데이터로 전환하며 provider 추상화로 출처를 갈아끼울 수 있게 했습니다 — 추상화 기록
- 정책 토글의 캐시와 만료 상태가 어긋나는 문제를 동기화 — 기록
- 토큰 승인 버튼의 오클릭을 가드하고 버튼 그룹을 재배치 — 기록
8. 품질 — 손 QA를 코드로
문제. 납품 문서의 기능목록과 실제 화면이 맞는지 사람이 눈으로 돌던 QA였습니다.
한 일.
- 기능목록 엑셀을 JSON으로 뽑아 메뉴 136종을 전부 열어보는 Playwright 스윕을 만들었습니다. 개발 DB가 SQLite라 워커를 1로 고정한 이유, 확인 불가 항목을 스킵으로 남긴 판단까지 기록 — QA 스윕
- 문서용 화면 캡처를 자동화 스크립트로 만들었고, 디자인 시안과 구현의 어긋남은 Figma 44프레임 대조로 정리했습니다
- 화면 개선은 근거를 남기며 진행 — 페이지네이션 리디자인, 이용안내 아코디언→캐러셀 전환, OID 생성 위저드 UX
9. 도입했다가 철회한 것 — 실패의 기록
성과만 적으면 경력기술서가 광고가 됩니다. 이 기간에 도입했다가 되돌린 것도 있습니다.
- 워크플로우에 RVS 상태 모델을 도입했다가 도메인과 맞지 않아 철회했습니다. 도입 이유와 철회 이유를 둘 다 남겼어요 — 도입과 철회 회고
- 확정 전 데이터를 미리 반영했다가 도메인 문제를 두 번 드러낸 사건 — 선반영 두 케이스
되돌린 결정이 있다는 건 되돌릴 수 있게 만들었다는 뜻이기도 합니다. 이 문서에서 이 절을 빼지 않는 이유예요.
돌아보면
- 화면만 만질 때는 안 보이던 것들이 모델을 만지면서 보이기 시작했습니다. 개발·운영 DB 차이, 지연 평가, 벌크 경로의 검증 부재 — 전부 프런트에서는 만날 일이 없던 실패들이에요.
- 아직 못 넘은 선도 압니다. 트랜잭션 경계, 락, 인덱스, 동시성 같은 운영 조건으로 시선이 늦게 갑니다. 위 글들 곳곳에 "확인 안 했다"고 적어둔 항목들이 그 증거이고, 다음 구간의 과제입니다.
- 이 문서는 진행 중인 경력의 중간 기록이라, 위 글들이 늘어나는 만큼 갱신할 예정입니다.