발행일

등록된 적 없는 ASN.1을 파싱하고 있었다 — 표 셋의 출처를 등록값 하나로

등록된 적 없는 ASN.1을 파싱하고 있었다

표준데이터 상세에는 구성 요소를 보여주는 표가 둘 있고, 구조 시각화 트리까지 하면 셋입니다. 셋 다 같은 값을 보여줍니다 — 필드 이름, 의무 수준(M/O), 최대 반복 횟수.

셋 다 - 였습니다.

한두 행이 아니라 통째로요. 화면상으로는 "아직 등록이 덜 됐나 보다"로 읽히는 모양이었습니다.

파서는 맞았고, 읽을 자리가 없었다

값은 상위 자원의 ASN.1 원문에서 뽑고 있었습니다. kadif:asn1Code 슬롯의 텍스트를 파싱해서 멤버 한 겹을 뽑고, 그 멤버와 실제 자식 인스턴스를 타입 이름으로 짝짓는 코드예요. 파서에는 테스트가 34건 붙어 있고 전부 초록이었습니다.

문제는 그 위였습니다.

- `kadif:asn1Code`**어느 클래스에도 등록돼 있지 않다**(dev 실측 슬롯 0건·값 0).
  ASN.1 원문은 표현 자원(`kadif:Representation`)`kadif:definitionText` 로 옮겼고, 상세
  §8 `데이터 표현` 이 그 자원을 편다. 그래서 파서 경로는 **늘 빈손**이었다.

슬롯 자체가 없습니다. 이 시스템은 클래스에 어떤 속성을 둘지를 관리 화면에서 등록하는 구조인데, kadif:asn1Code는 등록된 적이 없어요. 원문은 그 사이 표현 자원 쪽으로 옮겨 가 있었습니다.

파서는 한 번도 틀린 답을 낸 적이 없습니다. 아무 답도 안 냈어요. 빈 문자열을 받아 빈 목록을 돌려주고, 화면은 그 빈 목록을 -로 그렸습니다. 어느 층에서도 오류가 아닙니다.

테스트 34건이 이걸 못 잡은 이유도 같습니다. 파서 테스트는 ASN.1 문자열을 직접 넣어 파싱 결과를 단언해요. 그 문자열이 실제로 어디서 오는지는 테스트의 관심 밖입니다. 단위 테스트가 초록인 것과 화면에 값이 뜨는 것 사이에는 "그 입력이 실제로 도착하는가"라는 층이 하나 더 있었습니다.

값은 이미 필드가 들고 있었다

찾아보니 그 사이 데이터 모델이 움직여 있었습니다. 필드가 클래스로 등록됐어요. kadif:MessageField·kadif:DataFrameField로 dev에 109건이 있고, 표가 묻는 값을 필드가 자기 슬롯에 들고 있었습니다.

방법장점포기하는 것판단
kadif:asn1Code 슬롯을 클래스에 등록하고 원문을 채움코드 변경 0원문이 이미 표현 자원으로 옮겨 갔는데 같은 값을 두 곳에 둠기각
② 표현 자원의 kadif:definitionText를 파서에 물림원문 위치를 따라감원문을 파싱해 얻는 값이 이미 필드에 등록값으로 있는데 굳이 다시 계산기각
③ 필드의 등록 슬롯을 읽는 field_facts 하나로 세 곳의 출처를 통일등록한 값이 곧 보이는 값. 관리 화면과 사용자 화면이 안 갈림필드를 거치지 않고 자원이 바로 매달린 옛 데이터는 답이 없음채택
④ ③ + 원문 파서를 옛 데이터의 폴백으로 남김옛 데이터도 값이 남경로가 둘이라 어느 쪽이 답했는지 화면이 말 안 함채택(③에 포함)
|| 슬롯 | dev 채움 |
|-|-|-|
| `순서` | `kadif:fieldNumber` (필드 순서) | 109/109 |
| `필드 명` | `kadif:fieldName` (데이터 필드 이름) | 109/109 |
| `M/O` | `kadif:requirement` → 그 인스턴스의 `kadif:notation` | 109/109 |
| `최대 반복` | `kadif:maxOccurs` (최대 반복 횟수) | 0/109 — 관리 화면에서 채운다 |

세 표가 같은 함수(field_facts)를 부릅니다. 한 화면의 두 그림이 같은 멤버를 다르게 말하는 일을 구조로 막는 자리예요.

화면이 만들어 내는 글자를 없앴다

값의 출처를 옮기면서 함께 정리한 게 있습니다. 화면이 스스로 만들어 내던 글자를 전부 등록값으로 바꿨어요.

M·O·C 표기부터 그렇습니다.

⚠️ **`M`·`O`·`C` 글자도 등록값이다.** `필수`·`선택`·`조건부`·`권장` 인스턴스가 든
`kadif:notation` 을 그대로 쓴다 — 코드가 한글 이름을 글자로 옮기면 등급이 늘 때 여기만 옛
표로 남는다(구획 제목·행 이름을 코드에 두지 않는 원칙과 같다).

{"필수": "M", "선택": "O"} 같은 표를 코드에 두면, 등급이 하나 늘 때 이 표만 조용히 옛날에 남습니다. 이 시스템은 의무 수준을 3단계로 늘린 적이 이미 있어서, 늘어나는 축이라는 걸 알고 있었어요.

마지막 열은 이름까지 등록값입니다. 값이 관리 화면의 참조한 데이터 칸에 넣은 바로 그 자원인데 화면만 구성 자원이라고 부르면, 등록한 사람과 보는 사람이 다른 이름으로 같은 것을 말하게 되거든요.

그리고 빈칸을 안 채웁니다.

값이 비면 그 칸은 `-` 다 — **등록이 덜 됐다는 신호**라 화면이 채워 넣지 않는다.

이 글의 처음이 정확히 그 반대 상황이었습니다. -가 "등록이 덜 됐다"가 아니라 "코드가 엉뚱한 데를 보고 있다"였고, 그걸 구별할 방법이 화면에 없었어요. 이제는 -가 한 가지 뜻만 갖습니다.

seq는 줄 세우는 값이 못 된다

같이 나온 두 번째 결함은 순서였습니다. 데이터 프레임 상세의 구조 표는 필드 차례대로 서는데, 같은 필드를 필드 상세에서 상위 자원 카드로 펼쳐 보면 알파벳 순으로 섰어요. 한 화면의 두 그림이 다른 차례를 그렸습니다.

- `seq` 는 줄 세우는 값이 못 된다. 관리 화면 저장(`_sync_object_links`)`seq` 를 폼 후보
  쿼리셋 순서(`order_by("name", "id")`)**다시 매기므로**, 필드처럼 자기 차례를 값으로 든
  자원은 저장 한 번에 알파벳으로 흩어진다. 데이터를 손으로 맞춰도 다음 저장에 되돌아간다.

링크의 seq는 저장할 때마다 폼 후보 순서로 다시 매겨집니다. 데이터를 손으로 맞춰 놔도 다음 저장에 되돌아가요. 그래서 쓰는 쪽이 아니라 읽는 쪽을 고쳤습니다 — 링크 목록도 인덱스 슬롯(kadif:fieldNumber)으로 다시 세우게 하고, 정렬 규칙의 정본을 함수 하나로 모았습니다.

여기 함정이 하나 더 있었습니다. 표가 따로 다시 세우면 안 됩니다. 슬롯 단위로 세운 결과를 표 전체에서 한 번 더 정렬하면, 순서값이 없는 슬롯이 통째로 뒤로 밀려요. 정렬은 한 곳이 정하고 나머지는 받기만 해야 합니다.

원문과 등록값이 어긋난 자리 셋 — 안 고쳤다

출처를 바꾸고 나서 표현 자원의 ASN.1 원문과 등록값을 전수 대조했습니다(원문이 있는 필드 93건). 어긋난 자리가 셋 나왔어요.

  • MSG_MapforAutonomousDrivingOffer-Request·-Response의 M/O 6건 — 원문은 ::= CHOICE인데 등록값은 선택. 셋 중 하나만 고르는 자리라 조건부(C)가 맞습니다.
  • DF_DDateTime의 필드 0건 — 원문에 yearoffset 7개가 있는데 필드가 하나도 등록돼 있지 않아 구조 표가 통째로 빕니다.
  • 테스트 데이터 2건 — 원문에 없는 필드입니다.

고치지 않았습니다.

**고치지 않았다** — 화면이 원문을 근거로 등록값을 덮으면 관리 화면에서 본 값과 사용자
화면의 값이 갈린다(사용자 결정). 고칠 자리는 관리 화면이다.

화면이 "원문을 보니 이게 맞는 것 같은데" 하고 값을 고쳐 주기 시작하면, 관리자가 등록한 값과 사용자가 보는 값이 달라집니다. 등록한 사람은 자기가 넣은 값이 왜 화면에 안 나오는지 알 수 없어요. 틀린 값을 그대로 보여주는 게, 몰래 고쳐 주는 것보다 낫습니다. 고칠 자리는 관리 화면입니다.

검증

  • 구조 표·활용 표 전수에서 빈칸 0건 확인. 고치기 전에는 세 열이 통째로 -였습니다.
  • 최대 반복 109건을 관리 화면에서 채우고(반복 14건 N, 나머지 95건 1) 화면에 그대로 뜨는 것 확인 — 코드 수정 없이 데이터만으로 채워지는지가 이번 설계의 확인 항목입니다.
  • 참조한 데이터 링크 3건을 새로 걸고, 원시 타입 2건은 타입 표현식으로 남는 것 확인. DF_BridgeTunnelInformationbTLat은 참조가 자기 상위 자원을 가리키고 있어 바로잡았습니다.
  • 필드를 거치지 않고 자원이 바로 매달린 옛 데이터에서 원문 파서 폴백이 도는 것 확인.
  • 구조 표와 필드 상세의 상위 자원 카드가 같은 차례로 서는 것 확인. 고치기 전에는 하나는 필드 순, 하나는 알파벳 순이었습니다.
  • 서버 규칙은 test_instance_detail_sections.py로 잠금(이번 커밋에서 380줄 추가). e2e 시나리오도 함께 갱신했습니다.

남은 것 · 한계

  • 어긋난 자리 셋은 그대로입니다. M/O 6건이 선택으로 남아 있고, 그게 틀렸다는 걸 알면서 뒀어요. 관리 화면에서 고칠 일이라고 정했지만 언제 누가 고치는지는 안 정했습니다. 지금은 이 문서에만 적혀 있습니다.
  • DF_DDateTime은 구조 표가 통째로 빕니다. 원문에는 7개 필드가 있는데 등록이 하나도 안 돼 있어요. 참조할 자원(DE_DYear 류)도 dev에 없습니다. 이건 데이터 등록이 안 끝난 것이고, 화면은 정직하게 비어 있습니다.
  • 채운 값들이 전부 dev DB에만 있습니다. 시드에 안 들어가서 load.sh dev d로 초기화하면 사라져요. 빈칸 0건이라는 검증 결과도 그 순간의 dev 상태입니다.
  • 경로가 둘인데 화면이 어느 쪽인지 말하지 않습니다. 등록 슬롯이 답했는지 원문 파서 폴백이 답했는지 보는 사람은 모릅니다. 값이 이상할 때 어디를 고쳐야 하는지도 그래서 안 보여요.
  • 테스트가 못 잡은 층은 그대로입니다. 파서 단위 테스트는 여전히 문자열을 직접 넣습니다. "그 슬롯이 실제로 등록돼 있는가"를 확인하는 테스트는 안 만들었어요. 같은 방식으로 다른 슬롯이 사라지면 또 조용히 -가 됩니다.
  • 최대 반복의 95건을 1로 채운 건 사용자 결정인데, 원문 대조로 확인한 값이 아니라 기본값을 일괄 적용한 것입니다. 반복이 있는데 1로 적힌 자리가 있는지는 모릅니다.

관련 글: 표준데이터 구조를 깊이마다 한 번씩만 읽었다 · 필수 여부 체크박스를 3단계 의무 수준으로