EPC를 FQDN으로 바꾸는 절차 — ONS NAPTR 질의 구현

EPC를 FQDN으로 정규화해 ONS에 NAPTR 질의하는 변환 절차를 단계별로 따라가고 Python 구현 예제로 확인한다

2026-08-13 · 최초 발행 2025-11-26

EPC "epc:sgtin:0614141.112345.400"를 손으로 한 단계씩 따라가 보면 FQDN이 어떻게 만들어지는지 가장 빨리 감이 온다. EPC(Electronic Product Code)는 스킴별(SGTIN, SSCC 등)로 구성이 조금씩 다르지만 공통적으로 관리주체, 객체 클래스, 일련번호를 담는다. 이 값을 DNS가 질의할 수 있는 FQDN으로 정규화하고 ONS(Object Name Service)에 NAPTR 레코드로 물어보면, EPCIS 같은 정보서버의 위치가 돌아온다. Root ONS 도메인은 표준·운영 정책에 따라 달라질 수 있어(예: onsepc.com) 실제 배포 전에는 최신 값을 확인해야 한다.

입력을 받으면 가장 먼저 하는 일

EPC URI(epc:sgtin:...) 형태든 점(.) 구분 숫자열이든, 먼저 스킴별 필드 개수와 범위를 검증한다. 허용 문자인지, 필드 수가 맞는지, 음수나 비숫자가 섞여 있지 않은지를 이 단계에서 걸러낸다. 서비스 디스커버리는 대개 시리얼(개별 아이템) 단위가 아니라 관리주체/클래스 수준으로 집계되기 때문에, 검증을 통과한 값은 각 필드를 10진수 라벨로 바꾼다 — 16진이나 이진 입력이 들어와도 10진 문자열로 통일하고 선행 0은 제거한다.

변환 절차

전제조건은 입력 예시 "0614141.112345.400"(또는 "epc:sgtin:0614141.112345.400") 형태, Root ONS는 "onsepc.com"(운영 환경별로 최신 도메인을 확인해야 한다), 스킴 레이블은 미포함 버전을 기준으로 한다(필요하면 "sgtin.id" 같은 스킴 레이블을 붙일 수 있다).

  1. 점(.) 구분 — 입력을 "관리주체.객체클래스.일련번호" 형태로 분리한다.
  2. 일련번호 삭제 — 마지막 필드(Serial)를 제거한다. 서비스 위치는 개별 아이템이 아니라 클래스 레벨을 참조하기 때문이다.
  3. 10진수 변환 — 각 필드를 10진수 문자열로 정규화한다. 0x 접두 16진/이진 입력이면 10진수로 바꾸고 선행 0을 제거한다.
  4. 역순 배열 — 라벨을 역순으로 재배열한다.
  5. EPC Root ONS 주소 삽입 — FQDN = "역순라벨.join('.') + '.' + Root ONS".

예시로 따라가면 입력 "0614141.112345.400"은 시리얼 삭제 후 "0614141.112345"가 되고, 역순 배열로 "112345.0614141"이 된 뒤 최종 FQDN "112345.0614141.onsepc.com"이 만들어진다. 이후 NAPTR 질의로 EPCIS나 정보서버 위치를 받으며, 우선순위·가중치 기반 선택과 정규식 재작성(rule)이 적용된다.

유효무효NAPTR 존재NOANSWER/NXDOMAIN캐시 적중실패입력 EPC 수신예:epc:sgtin:0614141.112345.400파싱·검증필드 분리관리주체.객체클래스.일련번호에러 반환INVALID_EPC, 로그·모니터링일련번호 제거 필드 10진수 정규화선행 0 제거역순 배열Root ONS 결합예: onsepc.comDNS NAPTR 질의응답 유무Answer 섹션 저장서비스 타입·우선순위·재작성규칙재시도/백오프/캐시 확인에러 반환RESOLUTION_FAILED선택된 엔드포인트 반환예:https://epcis.example/api

입력 EPC "epc:sgtin:0614141.112345.400"을 그대로 예로 들면, 분리 결과는 0614141.112345.400, 일련번호 삭제 후 0614141.112345, 10진수 정규화 후 614141.112345(선행 0 제거 예시), 역순 배열로 112345.614141, Root ONS 결합으로 112345.614141.onsepc.com이 된다. 이 FQDN에 대한 NAPTR 질의 결과는 예를 들어 다음과 같은 형태다.

IN NAPTR 100 10 "U" "EPC+epcis" "!^.\*$!https://epcis.example/api!" .
IN NAPTR 100 20 "U" "EPC+ws" "!^.\*$!mqtts://broker.example/epc!" .

선택 로직은 서비스 타입(EPC+epcis)을 우선 사용해 https 엔드포인트를 반환하는 식이다.

공개 Root ONS와 사설 ONS, 무엇을 포기하는가

구분 성능 확장성 일관성 안정성 운영 편의
공개 Root ONS 전역 분산, 지연 낮음(캐시 의존) 글로벌 네임스페이스 확장 용이 공용 정책 준수 필요 공용 인프라 가용성 의존 운영 단순, 정책 제약 존재
사설/로컬 ONS 로컬 캐시로 초저지연 조직 경계 내 유연 확장 사내 표준으로 강한 일관성 내부 장애 영향 범위 제한 설정·보안·유지관리 부담 증가

성능만 보면 로컬 ONS 캐시가 지연을 최소화하고 공용 Root는 광역 커버리지를 확보한다는 차이가 있다. 보안 측면에서는 사설 ONS로 데이터 주권을 강화하고 DNSSEC·접근통제를 적용하기가 더 쉽다. 운영은 공용 Root 쪽이 간편하지만 사설은 유연한 대신 운영 복잡도가 올라간다.

Python으로 그대로 옮기면

전제조건은 Python 3.10+와 dnspython(pip install dnspython)이다. 입력 형식은 "epc:sgtin:관리주체.객체클래스.일련번호" 또는 "관리주체.객체클래스.일련번호"이고, 루트 ONS는 "onsepc.com"(예시이며 최신 정보를 확인해야 한다)을 가정한다.

# Python 3.10+
# pip install dnspython

from __future__ import annotations
import re
from typing import List, Tuple, Optional
import dns.resolver

def epc_to_fqdn(epc: str, ons_root: str = "onsepc.com") -> str:
    # 1) prefix 제거
    m = re.match(r"^epc:[a-z0-9]+:(.+)$", epc)
    core = m.group(1) if m else epc

    # 2) 점 구분 파싱
    parts = core.strip().split(".")
    if len(parts) < 3:
        raise ValueError("INVALID_EPC: 최소 3개 필드 필요(관리주체.객체클래스.일련번호)")

    # 3) 일련번호 제거
    parts_wo_serial = parts[:-1]

    # 4) 10진수 정규화(0x.., 0b.. 등 허용)
    norm: List[str] = []
    for p in parts_wo_serial:
        p = p.strip().lower()
        # base 0: 0x.., 0b.., 0o.. 자동 인식, 그 외 10진
        try:
            val = int(p, 0)
        except ValueError:
            # 숫자만 허용
            if not re.fullmatch(r"\d+", p):
                raise ValueError(f"INVALID_FIELD: {p}")
            val = int(p, 10)
        norm.append(str(val))  # 선행 0 제거된 10진 문자열

    # 5) 역순 배열 + Root ONS 결합
    fqdn = ".".join(reversed(norm)) + "." + ons_root
    return fqdn

def query_ons_naptr(fqdn: str, timeout: float = 3.0):
    try:
        answers = dns.resolver.resolve(fqdn, "NAPTR", lifetime=timeout)
        return [rdata.to_text() for rdata in answers]
    except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer) as e:
        return []
    except Exception as e:
        raise

if __name__ == "__main__":
    epc_input = "epc:sgtin:0614141.112345.400"
    fqdn = epc_to_fqdn(epc_input, ons_root="onsepc.com")  # 최신 Root 확인 필요
    print("FQDN:", fqdn)
    naptrs = query_ons_naptr(fqdn)
    print("NAPTR:", naptrs)

캐시 DNS를 지정하고 TTL을 최적화하며, 타임아웃·재시도는 지수 백오프로 처리한다. DNSSEC 검증은 기본으로 켜두는 편이 낫고, 사설 ONS라면 내부 PKI·접근제어와 묶어서 운영한다. 스킴별(sgtin, sscc 등) 라벨을 추가하는 정책은 모듈을 분리해두면 나중에 바꾸기 쉽다.

실무에서 이 절차가 쓰이는 곳

제조·유통 추적에서는 EPC를 스캔한 뒤 FQDN 변환 → ONS NAPTR 조회 → EPCIS REST 엔드포인트 조회 → 이벤트 조회까지 자동화하고, 창고 WMS·TMS와 연계해 로트·배치 수준까지 추적한다. AIDC 미들웨어에서는 리더기 게이트웨이에서 실시간 변환·질의를 처리하고 로컬 캐시로 1차 응답을 가속하며, 네트워크가 분절된 구간에는 사설 ONS를 두어 오프라인 지속성을 확보한다. 애프터서비스·리콜 대응에서는 클래스 레벨 정보서버에서 매뉴얼·리콜 공지를 제공하고, 지역별로 ONS를 재지정해 지연을 줄이고 트래픽을 분산한다.

캐시 적중률을 80%로 가정하면 평균 조회 지연이 30~70% 줄어들 수 있고, NAPTR을 직접 조회하는 대신 캐시를 거치면 권한 서버 부하도 그만큼 줄어든다. 변환·질의 절차가 표준화돼 있으면 시스템 간 연동 비용이 줄고, NXDOMAIN·NOANSWER 같은 실패 케이스가 명확히 구분되는 만큼 장애 진단 시간도 짧아진다. DNSSEC과 접근통제를 적용하면 변조·스푸핑 위험이 줄고, 사설 ONS를 쓰면 데이터 경계 통제와 규제 준수가 쉬워진다.

EPCONSFQDN변환DNS NAPTRdnspython