← 에디토리얼

금융 · 인증 · 2026.08.17 · 5분

eKYC 신분증 인식, 인식률부터 보면 늦습니다

시험실에서 95% 나온 엔진이 현장에서 70%대로 떨어집니다. 찍기 전에 거르는 법, 확신도로 사람에게 넘기는 구조, 그리고 개인정보를 얼마나 들고 있을지.

해외송금이나 간편결제를 만들면 본인확인을 피할 수 없습니다. 규제가 요구하고, 규제가 없더라도 사고가 나면 결국 여기서 막았어야 했다는 얘기가 나옵니다.

요즘은 신분증을 찍으면 이름과 생년월일, 발급일자를 알아서 읽어냅니다. 그래서 발주 문서에 "OCR 인식률 95% 이상" 같은 숫자가 적혀 옵니다. 그 숫자만 보고 만들면 대개 다시 만듭니다.

95%가 뭐의 95%인지를 안 정했습니다

같은 "95%"가 이만큼 다른 얘기일 수 있습니다.

기준 사용자가 겪는 것
글자 단위 전체 글자 중 95%가 맞음 이름 세 글자 중 하나 틀림
필드 단위 필드 하나가 통째로 맞을 확률 95% 5개 필드면 다 맞을 확률 77%
건 단위 한 장에서 모든 필드가 맞을 확률 95% 이게 진짜 체감값

0.95^5 = 0.77입니다. 필드별 95%짜리 엔진으로 5개를 읽으면 넷 중 한 명은 뭔가를 고쳐야 합니다.

계약서에 쓸 숫자는 건 단위여야 합니다. 그리고 그 숫자는 시험실이 아니라 실제 찍는 환경에서 재야 합니다.

인식률은 찍는 환경이 정합니다

같은 엔진인데 결과가 이렇게 갈립니다.

  • 형광등 아래, 어두운 책상, 손으로 든 신분증 → 반사와 그림자
  • 오래된 신분증은 인쇄가 닳아 있음
  • 저가 안드로이드는 초점이 안 잡힘
  • 화면 속 신분증을 다시 찍는 경우

시험실에서 95% 나온 엔진이 현장에서 70%대로 떨어지는 건 흔합니다. 엔진을 바꿔서 될 일이 아니라 찍는 단계에서 걸러야 할 일입니다.

그래서 저희는 인식보다 촬영 화면에 시간을 더 씁니다.

// 찍기 전에 막는다 — 흐리거나 반사가 심하면 셔터를 잠근다
function canShoot(frame: ImageData) {
  const blur = laplacianVariance(frame);      // 낮으면 초점이 안 맞음
  const glare = brightPixelRatio(frame, 250); // 높으면 반사
  const fill = cardAreaRatio(frame);          // 가이드를 채웠는지

  if (blur < 80) return { ok: false, hint: "초점이 맞지 않습니다" };
  if (glare > 0.04) return { ok: false, hint: "빛 반사를 피해 주세요" };
  if (fill < 0.55) return { ok: false, hint: "테두리에 맞춰 주세요" };
  return { ok: true };
}

한 번 더 맞춰야 하니 귀찮긴 합니다. 그래도 다시 찍는 횟수는 줄어듭니다. 실패하고 나서 재촬영하는 것보다 찍기 전에 안내받는 쪽이 훨씬 덜 짜증납니다.

틀린 다음이 진짜 설계입니다

인식은 언제든 틀립니다. 중요한 건 틀린 다음입니다.

자동으로 통과시키면 안 되는 값이 있습니다

이름 한 글자가 잘못 읽혀도 송금은 그대로 진행됩니다. 받는 사람이 다른 사람이 됩니다.

읽어낸 값은 반드시 사용자가 눈으로 보고 고칠 수 있어야 합니다. "자동 입력"이지 "자동 확정"이 아닙니다. 자동으로 채운 칸은 직접 친 칸과 눈에 띄게 달라야 하고요.

확신이 약하면 사람에게 넘깁니다

대부분의 인식 엔진은 항목마다 확신도를 같이 돌려줍니다. 이 값으로 세 갈래를 만듭니다.

type Field = { value: string; confidence: number };

function route(fields: Record<string, Field>) {
  const min = Math.min(...Object.values(fields).map((f) => f.confidence));

  if (min >= 0.95) return "auto";    // 그대로 진행, 사용자 확인만
  if (min >= 0.70) return "review";  // 해당 칸을 강조해 확인 요청
  return "manual";                   // 관리자 검토 대기열로
}

이 숫자 두 개(0.95, 0.70)가 시스템의 성격을 정합니다. 어떻게 정했는지 꼭 남겨두세요. 사람이 바뀌면 아무도 못 건드립니다.

실패한 시도도 남겨야 합니다

같은 사람이 열 번 실패했다면 기기 문제일 수도 있고 남의 신분증일 수도 있습니다. 성공한 것만 저장하면 이 신호를 놓칩니다.

남길 것: userId, 시각, 결과, 최저 확신도, 실패 사유, 기기 정보
(이미지 자체는 저장하지 않는다 — 아래 참고)

규제는 나라마다 다릅니다

일정이 가장 크게 어긋나는 지점입니다.

호주로 보내는 송금이면 그쪽 기준을 따라야 하고, 확인 항목과 보관 기간이 국내와 다릅니다. 어떤 서류를 몇 년간 어떤 형태로 보관하느냐에 따라 저장 구조 자체가 달라집니다.

  • 보관이 5년이냐 7년이냐 → 파티셔닝과 만료 삭제 설계
  • 원본 이미지를 보관해야 하나 → 별도 암호화 저장소
  • 제3자 제공 이력을 남겨야 하나 → 접근 로그 테이블

개발 시작 전에 확정돼야 합니다. 만들고 나서 "보관이 5년이었네요"가 나오면 데이터 구조를 다시 짭니다.

개인정보는 적게 들고 있을수록 낫습니다

신분증 이미지는 새어나가면 제일 곤란한 자료입니다. 기본은 이렇습니다.

자료 어떻게
원본 이미지 인식·확인 끝나면 지운다
규제상 보관해야 하는 것 암호화해서 서비스 DB와 분리된 곳
관리자 화면 전체 노출 금지, 필요한 항목만 마스킹 해제
기간 지난 자료 자동 삭제를 처음에 같이 만든다

마지막이 중요합니다. 만료 삭제를 나중에 하려고 미루면 결국 안 합니다. 그리고 그 자료는 계속 쌓입니다.

마스킹을 푸는 동작도 로그로 남기세요. 누가 언제 누구 걸 봤는지가 없으면 사고 났을 때 범위를 특정할 수 없습니다.

얼굴 대조를 넣을지

신분증 사진과 셀피를 맞춰보는 기능은 요청이 자주 옵니다. 기술은 어렵지 않은데 법이 무겁습니다.

얼굴은 생체정보입니다. 수집하려면 따로 동의를 받아야 하고, 뭘 어떻게 보관하는지 알려야 합니다.

대안이 있으면 대안이 낫습니다. 계좌 1원 인증이나 통신사 본인확인처럼 이미 제도로 자리 잡은 수단으로 되는 일이면 훨씬 간단하고 분쟁 소지도 없습니다. 얼굴이 아니면 안 되는 이유가 분명할 때만 가세요.

정리

eKYC에서 인식률은 여러 조건 중 하나일 뿐입니다. 순서는 이렇습니다.

  1. 95%가 뭐의 95%인지 정한다 (건 단위로)
  2. 찍는 화면에서 얼마나 거를지 설계한다
  3. 확신도로 자동·확인·수동 세 갈래를 나눈다
  4. 규제 요건을 개발 전에 확정한다
  5. 뭘 언제까지 들고 있을지 정하고 자동 삭제를 같이 만든다

이 다섯이 정해지면 인식률은 따라옵니다. 반대로 이걸 안 정하고 엔진부터 고르면, 몇 번을 바꿔도 같은 자리입니다.

관련 프로젝트

다음 글AI 기능 견적은 왜 자꾸 어긋날까