실명확인 API 연동 가이드: 일치·불일치·조회 오류를 안전하게 구분하기

실명확인 API의 일치, 불일치, 조회 오류를 서로 다른 상태로 구분하는 개념도
입력 정보의 일치 여부와 조회 오류를 구분하는 개념 이미지입니다. 신분증 소지자 확인이나 법적 자격 판정을 의미하지 않습니다.

실명확인 API를 연결할 때 가장 먼저 설계할 것은 성공 화면보다 실패 화면입니다. 이름과 주민등록번호가 맞지 않는 상황, 외부 조회가 완료되지 않은 상황을 같은 “인증 실패”로 묶으면 이용자에게 잘못된 수정을 요구하고 상담도 길어집니다. APICK의 성명/주민등록번호 실명확인 API를 활용하려면 일치·불일치·조회 오류를 별도 상태로 다루는 것이 출발점입니다.

이 글은 적법한 처리 근거와 이용 조건을 검토한 사업자의 서버 개발자를 위한 연동 안내입니다. 이름과 번호의 일치 확인이 필요한 업무에서 요청 규격을 연결하고, 확인 결과를 서비스의 다음 단계에 전달하는 방법에 집중합니다. 공개 문서의 규격을 바탕으로 작성했으며 실제 개인정보나 운영 인증키를 사용해 호출하지 않았습니다.

도입 전에 정할 것: 어떤 업무의 어떤 확인인가

예를 들어 계약 관련 정보를 접수하는 과정에 실명 일치 확인 단계가 필요하다면, 접수 상태와 확인 상태를 나누어 보관하는 편이 좋습니다. 접수 완료가 곧 실명 일치를 뜻하지 않고, 조회 오류가 계약 당사자의 정보 오류를 뜻하지도 않기 때문입니다. 확인을 끝내기 전까지 후속 절차를 보류할지, 별도 검토로 넘길지는 서비스 정책으로 정합니다.

이 API가 반환하는 정보는 제출한 성명과 주민등록번호의 일치 여부입니다. 요청한 사람이 그 정보의 주인인지, 현재 화면 앞에 있는지, 실물 신분증이 위조되었는지까지 증명한다고 해석하면 안 됩니다. 그런 확인이 필요한 업무는 별도의 적합한 절차를 함께 검토해야 합니다.

주민등록번호는 처리 제한을 별도로 확인해야 하는 정보입니다. 동의 화면을 마련했다는 이유만으로 수집·조회가 가능하다고 판단하지 마세요. 처리 근거, 제공받는 사업자의 자격과 허용 목적, 공급자 이용 조건, 보안 조치와 보유·파기 기준을 각각 점검한 뒤 연결해야 합니다. 관련 제한은 국가법령정보센터의 개인정보 보호법 제24조의2에서 확인할 수 있으며, 이 기술 예시는 개별 사업자의 법적 이용 자격을 보장하지 않습니다.

요청은 세 필드, 판단은 세 가지 결과

요청 주소는 POST https://apick.app/rest/name_rrn_auth입니다. 헤더에는 Authorization: Bearer 방식의 인증키를 넣고, 본문은 multipart/form-data로 전송합니다. 필수 값은 한글 성명인 name, 주민등록번호 앞 6자리 문자열 rrn1, 뒤 7자리 문자열 rrn2입니다. 숫자 모양의 값도 문자열로 유지하세요.

  • data.result === 1: 성명과 주민등록번호 일치. 해당 확인 단계를 통과시킬 수 있습니다.
  • data.result === 0: 불일치. 이용자가 입력을 확인할 수 있도록 안내합니다.
  • data.result === -1: 조회 오류. 입력 불일치로 단정하지 않고 확인 대기 상태로 둡니다.

api.success는 API 서버의 정상 응답 여부이고, data.success는 문서에서 과금 여부를 설명하는 별도 필드입니다. 어느 하나를 실명 일치 판정의 대용으로 쓰지 마세요. 또한 자바스크립트에서 -1은 참으로 평가되므로 if (data.result)는 오류를 통과시키는 버그가 됩니다. 반드시 숫자를 엄격하게 비교해야 합니다.

curl로 요청 구조 확인하기

아래는 전송 형식을 보여주는 정적 예시입니다. 모든 개인정보 위치를 자리표시자로 남겼습니다. 실제 값을 셸 명령이나 공유 문서에 붙여 넣으면 명령 기록과 프로세스 정보에 남을 수 있으므로, 운영에서는 보호된 서버 요청을 통해 처리하세요. 응답 원문도 터미널에 출력하지 않도록 구성했습니다.

# 요청 구조 설명용입니다. 자리표시자를 실제 개인정보로 바꿔 실행하지 마세요.
# 실서비스는 아래 서버 함수처럼 보호된 요청에서 값을 받아 처리합니다.
curl --fail-with-body --silent --show-error \
  --request POST 'https://apick.app/rest/name_rrn_auth' \
  --header "Authorization: Bearer $APICK_API_KEY" \
  --form-string 'name=<보안_서버_요청의_성명>' \
  --form-string 'rrn1=<보안_서버_요청의_주민번호_앞부분>' \
  --form-string 'rrn2=<보안_서버_요청의_주민번호_뒷부분>' \
  --output /dev/null --write-out 'HTTP %{http_code}\n'

Content-Type을 수동으로 지정하지 않은 이유는 multipart 경계 문자열까지 클라이언트가 설정해야 하기 때문입니다. API 키는 서버의 비밀 저장소와 실행 환경에서 관리하고, 브라우저 자바스크립트·모바일 앱 배포 파일·공개 저장소에 넣지 않습니다.

Node.js에서 결과를 안전한 상태로 좁히기

다음 서버 ESM 함수는 기본 fetch와 FormData를 지원하는 Node.js 환경을 전제로 합니다. 입력은 이미 인증된 보호 경로에서 받은 값이어야 하며, 요청 권한과 적법한 처리 조건 확인은 함수 바깥에서 끝내야 합니다. 형식 검사만으로 입력의 실제 유효성을 증명하지는 않습니다.

// Node.js 서버 ESM. 브라우저 번들에 포함하지 않습니다.
/** @typedef {{name: string, rrn1: string, rrn2: string}} SecureInput */
/** @typedef {'match'|'mismatch'|'lookup_error'|'http_error'|
 * 'invalid_response'|'transport_unknown'|'input_error'|'config_error'} State */
/** @typedef {{state: State, httpStatus: number|null,
 * costPoints: number|null}} Outcome */
const isObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);

/** @param {SecureInput} secureInput
 * 보호된 서버 요청에서 받은 값만 전달합니다. 예시 개인정보는 사용하지 않습니다.
 * 처리 근거, 호출 권한, 요청자 인증, 속도 제한은 호출 전에 확인해야 합니다.
 * @returns {Promise<Outcome>} */
export async function verifyRealName(secureInput) {
  const done = (state, httpStatus = null, costPoints = null) =>
    ({ state, httpStatus, costPoints });
  const key = process.env.APICK_API_KEY;
  if (!key) return done("config_error");
  if (!isObject(secureInput) ||
      typeof secureInput.name !== "string" || !secureInput.name.trim() ||
      typeof secureInput.rrn1 !== "string" || !/^\d{6}$/.test(secureInput.rrn1) ||
      typeof secureInput.rrn2 !== "string" || !/^\d{7}$/.test(secureInput.rrn2)) {
    return done("input_error");
  }
  const form = new FormData();
  for (const field of ["name", "rrn1", "rrn2"]) {
    form.append(field, secureInput[field]);
  }
  let response;
  try {
    response = await fetch("https://apick.app/rest/name_rrn_auth", {
      method: "POST",
      headers: { Authorization: `Bearer ${key}` },
      body: form,
      signal: AbortSignal.timeout(120_000),
    });
  } catch {
    // 통신 실패만으로 공급자 처리 완료·과금 여부를 확정하지 않습니다.
    return done("transport_unknown");
  }
  // HTTP 오류도 본문은 읽되 원문이나 예외를 로그에 남기지 않습니다.
  const body = await response.json().catch(() => null);
  const data = isObject(body?.data) ? body.data : null;
  const api = isObject(body?.api) ? body.api : null;
  const cost = Number.isSafeInteger(api?.cost) && api.cost >= 0 ? api.cost : null;
  if (data?.result === -1) return done("lookup_error", response.status, cost);
  if (!response.ok) return done("http_error", response.status, cost);
  if (api?.success !== true || !Number.isInteger(data?.result)) {
    return done("invalid_response", response.status, cost);
  }
  if (data.result === 1) return done("match", response.status, cost);
  if (data.result === 0) return done("mismatch", response.status, cost);
  return done("invalid_response", response.status, cost);
}

이 함수는 원문 응답 대신 내부 상태, HTTP 상태, 숫자로 검증한 과금 포인트만 반환합니다. 정상 HTTP 응답과 api.success === true를 확인한 뒤 일치 또는 불일치를 확정합니다. 응답이 JSON이 아니거나 결과의 타입이 다르거나 알 수 없는 숫자이면 자동 통과시키지 않습니다. 결과가 문자열 “1”로 바뀌었을 때도 임의로 숫자로 바꾸지 않고 명세 변경 여부를 확인합니다.

120초는 예시 코드의 클라이언트 대기 제한입니다. 공급자의 처리 완료 시간이나 무과금 조건을 보장하는 수치가 아닙니다. 연결이 끊기거나 로컬 시간 제한에 걸린 경우에는 상대 서버의 처리·과금 여부가 불명확할 수 있으므로 곧바로 같은 요청을 반복하지 않도록 합니다.

서비스 화면과 재시도는 어떻게 연결할까

  1. 서버에서 요청자 인증, 허용된 업무, 필수 값의 형식을 확인합니다. 브라우저 검사만 통과했다는 이유로 호출하지 않습니다.
  2. 접수 건에 개인정보를 포함하지 않는 무작위 작업 ID를 부여하고 처리 중 상태를 저장합니다. 더블 클릭으로 같은 작업이 동시에 실행되지 않게 제어합니다.
  3. match는 해당 확인 단계 완료로, mismatch는 입력 확인으로 연결합니다. 불일치만으로 도용이나 부정행위라고 단정하지 않습니다.
  4. 조회 오류·통신 불명·잘못된 응답은 확인 대기로 남깁니다. “지금은 확인을 완료하지 못했습니다”처럼 입력 오류와 구분되는 문구를 사용합니다.
  5. 재시도가 필요하면 운영 정책에 따라 간격과 최대 횟수를 제한합니다. 앱의 중복 제출 방지는 APICK이 제공하는 멱등성 보장과 별개입니다.

연속 조회로 정보를 맞혀보는 시도를 줄이기 위해 계정·업무 단위 호출 제한을 두고, 이용자 화면에 번호의 어느 부분이 맞는지 세분화해서 알려주지 않는 편이 안전합니다. 로그는 작업 ID, 결과 상태, 시간, 허용된 과금 메타데이터 중심으로 남기세요. 성명·주민등록번호·인증키·원문 요청과 응답은 애플리케이션 로그뿐 아니라 APM, 오류 수집기, 리버스 프록시의 본문 기록에서도 제외해야 합니다.

50P 요금으로 예산을 잡는 방법

2026년 10월 11일 공개 요금표 기준 실명확인 API는 건당 50P입니다. 설명용으로 과금 대상 호출이 월 2,000건이면 100,000P, 과금 대상 재조회 100건이 더 생기면 합계 105,000P입니다. 이는 계산 예시이며 실제 이용량이나 판매 수치가 아닙니다. 가입자 수 대신 과금 대상 호출 수로 계산해야 중복 요청의 영향을 볼 수 있습니다.

운영에서는 data.success, api.cost, 결제 로그를 함께 대조해 비용을 확인하세요. 화면의 불일치 비율과 미과금 비율은 같은 지표가 아닙니다. 이 글에서는 원화 환산, 부가세, 모든 오류 유형의 과금 조건을 확정하지 않았으므로 도입 시 최신 요금과 이용 조건을 확인해야 합니다.

출시 전 확인할 테스트

개인정보가 없는 모의 응답으로 1·0·-1, 누락된 필드, 문자열 결과값, 비정상 JSON, HTTP 오류, 통신 단절을 각각 시험하세요. 특히 -1에서 다음 단계가 열리지 않는지, 중복 제출이 두 번의 호출을 만들지 않는지, 요청 취소 후 화면이 계속 처리 중으로 남지 않는지 점검합니다. 실제 검증은 공급자와 허용된 방법 및 데이터를 확인한 뒤 별도 환경에서 진행해야 합니다.

자주 묻는 질문

실명확인에 성공하면 본인 인증을 모두 마친 건가요?

이 API의 일치 결과는 제출한 이름과 번호의 일치 확인입니다. 제출자의 소유·현존성이나 서비스가 요구하는 전체 본인 확인 절차를 충족하는지는 따로 판단해야 합니다.

조회 오류가 나면 번호를 다시 입력받아야 하나요?

먼저 입력 오류와 시스템 오류를 구분하세요. data.result === -1은 불일치가 아니므로 무조건 재입력을 요구하지 말고, 장애 상태와 재시도 정책을 확인하는 편이 좋습니다.

주민등록번호를 저장하지 않으면 처리 근거 검토를 생략할 수 있나요?

저장 여부만으로 판단하면 안 됩니다. 수집과 외부 조회에 필요한 처리 근거와 이용 조건을 먼저 검토하고, 저장을 최소화하더라도 전송·접근 통제와 보유·파기 기준을 함께 설계하세요.

APICK 공식 문서에서 연동 조건 확인하기

세 가지 결과를 명확히 분리하면 실명확인 단계를 업무 흐름에 연결하고 운영 문제를 추적하기 쉬워집니다. 필요한 확인 범위를 정한 뒤 실명확인 API 개발 가이드에서 요청·응답을 확인하고, APICK 공식 요금표에서 도입 예산을 검토해 보세요.

기준일: 2026년 10월 11일. 공개 개발 문서와 요금표 기준이며, 코드 예시는 실제 API 호출 없이 작성했습니다. 계약 조건, 실제 응답 동작, 개별 사업자의 처리 자격은 별도 확인이 필요합니다.

#APICK #실명확인API #개인정보보호 #API연동

댓글

이 블로그의 인기 게시물

[들려줄밤] 페가수스 프로젝트 | 7살 아이를 과거 전쟁터로 보낸 정부의 시간여행 실험?

도로명주소 조회 API로 주문서 주소 검색 만들기: 페이지 처리와 우편번호 보존

오늘 볼 영상 6편: 소비가 멈춘 하루부터 영상 제작 과정까지 (10월 22일)