본문으로 건너뛰기

유닛 하나가 지키는 것

유닛은 기능이 달라도 쓰는 법이 같아야 합니다. 새 유닛을 처음 쓰는 사람이 README 를 다 읽지 않아도 실패를 어떻게 다룰지 알 수 있게, 순서와 실패의 말과 의존 방향을 고정했습니다.

순서: 정규화, 로컬 검증, 캐시, 외부 호출

  1. 1

    정규화

    하이픈·공백을 걷고 형식을 맞춘다

  2. 2

    로컬 검증

    검증 숫자처럼 밖에 묻지 않고 알 수 있는 것

  3. 3

    캐시

    같은 값을 짧은 시간에 다시 묻지 않는다

  4. 4

    외부 호출

    타임아웃·재시도·오류 분류가 붙은 한 번

공개 함수는 이 순서를 바꾸지 않습니다. 어댑터를 가짜로 바꿔도 순서는 같아서, 개발에서 본 동작이 운영에서도 같습니다. 각 단계는 onStep 으로 기록을 내보냅니다. 동작에는 영향이 없고, 데모 화면과 로그가 씁니다.

실패는 여섯 가지 말로만

나누는 기준은 원인이 아니라 호출한 쪽이 다음에 할 일입니다. 유닛이 던지지 않고 결과(Result)로 돌려주므로, 타입이 실패 분기를 빠뜨리지 못하게 합니다.

종류뜻다시 보내면
invalid-input입력을 고쳐야 한다같다
auth키·권한을 고쳐야 한다(운영자의 일)같다
quota한도에 걸렸다기다리면 풀린다
timeout응답이 늦었다달라질 수 있다
upstream-down상대 서버가 실패했다달라질 수 있다
unexpected응답을 해석하지 못했다(명세가 바뀌었을 수 있다)같다. 사람이 본다

「없음」은 실패가 아닙니다. 등록되지 않은 사업자번호는 조회에 성공한 결과이고, 상태로 말합니다.

의존 방향

소비 앱 ─→ @duolabs-deux/unit-<id> ─→ unit-runtime ─→ unit-contracts
패키지맡는 것
unit-contractsResult, 오류 여섯 가지, manifest 타입. 코드가 거의 없다
unit-runtime외부 호출(타임아웃 기본 5초, 상태 코드·본문 분류, 재시도 기본 0회, Retry-After), 캐시, 카운터, 차단기
unit-<id>기능 하나. 어댑터(실제·가짜)와 파이프라인

유닛끼리는 서로 import 하지 않습니다. 둘을 엮는 것은 소비 앱입니다. CI 의 계약 검사가 이것을 막습니다.

어댑터: 실제 하나, 가짜 하나

가짜 어댑터는 키 없이 결정적으로 답합니다. 같은 입력에는 언제나 같은 답이라 테스트 번호를 사람이 만들 수 있습니다. 가짜의 답에는 출처 표시가 붙어서 화면에서 실제 조회처럼 보이지 않습니다.

밖에 묻지 않는 유닛(요청자 IP 처럼 입력만으로 답하는 것)은 어댑터가 없습니다. 입력이 같으면 답이 같으니 가짜가 곧 실물입니다.

재시도는 다시 보내도 안전한 요청(조회)에만 켭니다. 발송과 결제는 0회입니다. 인증키는 어댑터 옵션으로 받고, 유닛이 환경변수를 직접 읽지 않습니다. 어느 이름으로 둘지는 소비 앱이 정합니다.

소비 앱에서 쓰기

DEUX 를 이미 쓰는 프로젝트라면 1번은 되어 있습니다.

# 1. .npmrc (GitHub Packages, read:packages 토큰)
@duolabs-deux:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}

# 2. 설치
npm install @duolabs-deux/unit-biz-registry

# 3. next.config.ts (TS 소스를 그대로 발행하므로 앱이 컴파일한다)
transpilePackages: ["@duolabs-deux/unit-contracts", "@duolabs-deux/unit-runtime", "@duolabs-deux/unit-biz-registry"]