연령 확인은 체크박스에서 엔지니어링 과제로 바뀌었습니다. 영국에서는 Online Safety Act가 일부 서비스에 "매우 효과적인" 연령 확인을 요구합니다. EU에서는 유럽연합 집행위원회가 다른 정보는 하나도 넘기지 않고 18세 이상임을 증명하는 청사진(blueprint)을 공개했습니다. 두 곳 모두 신분증을 하나의 경로로 꼽습니다.

이 글은 "연령 확인을 추가해 달라"는 요청을 받고, 신분증 스캔이 그 안에서 어디에 들어가는지 알고 싶은 개발자를 위한 글입니다. 영국과 EU 당국이 꼽는 방법, 여권이나 신분증 스캔이 증명하는 것과 증명하지 못하는 것, 생년월일을 "기준 나이 이상인지 미만인지"라는 답으로 바꾸는 방법, 그리고 확인 뒤에 무엇을 남길지를 다룹니다. 아래의 인용은 모두 해당 당국의 페이지에서 가져왔고, 인용한 자리에 링크를 달았으며, 한국어로 옮겼습니다.

이곳은 여권 및 신분증 OCR API doc.cheap의 블로그입니다. 제품을 다루는 부분은 이 점을 감안하고 읽어 주세요.

영국: 기준은 "매우 효과적"

의무는 Online Safety Act 2023의 section 12에 있습니다. 가장 유해한 유형의 콘텐츠에 대해 사업자는 "모든 연령의 아동이 아동에게 유해한 최우선 콘텐츠를 접하지 못하도록 연령 확인 또는 연령 추정(또는 둘 다)을 사용"해야 합니다. 그리고 그 확인은 "특정 이용자가 아동인지 아닌지를 올바르게 판별하는 데 매우 효과적인 종류의 것이어야 하며, 그러한 방식으로 사용되어야" 합니다.

section 230은 두 용어를 정의합니다. 연령 확인은 적용 대상 서비스 "이용자의 정확한 나이를 확인하도록 설계된 모든 조치를 의미"합니다. 연령 추정은 "이용자의 나이 또는 연령대를 추정하도록 설계된 모든 조치를 의미"합니다. 또 가장 손쉬운 지름길도 막습니다. "이용자에게 자신의 나이를 스스로 신고하게 하는 조치(그 이상의 것이 없는 경우)는 연령 확인 또는 연령 추정으로 보지 않는다."

영국의 통신 감독 기관인 Ofcom은 Part 3 서비스를 위한 매우 효과적인 연령 확인에 관한 지침(개정판 날짜는 2025년 4월 24일)에서 이 기준을 설명합니다. 섹션 3은 매우 효과적일 수 있다고 보는 방법의 "망라적이지 않은 목록"을 제시합니다.

Ofcom 목록의 방법 신분증이 관여하는가
오픈 뱅킹 아니요
사진 신분증 대조(photo-ID matching) 예, 문서 자체가 증거
얼굴 기반 연령 추정 아니요
이동통신사 연령 확인 아니요
신용카드 확인 아니요
이메일 기반 연령 추정 아니요
디지털 신원 서비스 신원을 처음 확인하는 시점에 관여하는 경우가 많음

Ofcom은 사진 신분증 대조를 이렇게 설명합니다. 이 방법은 "업로드된 사진 신분증에서 관련 정보를 수집하고, 이를 신분증 업로드 시점의 이용자 이미지와 비교하여 동일인임을 확인하는 방식으로 작동"합니다.

같은 섹션은 기준에 미치지 못하는 것도 나열하는데, 그중에는 "이 정보를 뒷받침하는 추가 증거 없이 이용자에게 생년월일을 입력하게 하는 것"이 있습니다. 직접 입력한 생년월일은 자기 신고입니다. 문서에서 읽어 낸 생년월일은 증거이며, Ofcom 지침의 나머지 부분은 그 증거가 얼마나 좋아야 하는지에 관한 내용입니다.

EU: 연령 증명을 바탕으로 한 청사진

EU 쪽에서는 집행위원회가 2025년 7월 14일에 연령 확인 청사진의 첫 번째 버전을 공개했습니다. 집행위원회의 표현으로는, 이 청사진은 "온라인 포르노그래피 같은 제한된 성인 콘텐츠에 접근할 때, 다른 개인 정보는 전혀 드러내지 않고 이용자가 18세 이상임을 증명할 수 있는 방법을 제공"합니다.

이 설계는 확인과 사용을 분리합니다. 이용자의 "나이는 발급 기관이 생년월일 같은 상세한 개인 데이터를 이용해 확인한다. 그러나 온라인 서비스는 다른 개인 정보 없이 이용자가 18세 이상이라는 증명만 받게 된다."

신분증은 첫 단계에서 등장합니다. 집행위원회의 청사진 설명 페이지는 연령 증명의 출처로 "국가 eID, 여권과 신분증, 은행 앱처럼 나이 정보를 가진 사전 설치 앱, 또는 우체국 같은 제3자를 통한 오프라인 활성화"를 꼽습니다. 그리고 "2025년 10월에는 여권과 신분증으로 연령 증명을 생성하는 방법을 담은 청사진 두 번째 버전이 공개되었다"고 덧붙입니다.

따라서 두 곳 모두에서 문서는 인정되는 입력입니다. 그리고 두 곳 모두에서 문서 사진 하나만으로 확인이 끝나지는 않습니다.

스캔이 증명하는 것과 증명하지 못하는 것

문서 스캔은 "이 문서에 무엇이 쓰여 있는가"라는 한 가지 질문에 잘 답합니다. 생년월일, 만료일, 문서 종류, 기계 판독 영역(MRZ)을 읽고, 그 영역의 검증 숫자가 맞는지도 알려 줍니다. 이것이 Ofcom 설명에서 "관련 정보를 수집하는" 부분입니다.

연령 확인이 다뤄야 하는 다른 질문에는 답하지 않습니다.

  • 그 사람이 소지자인가? Ofcom의 견고성 섹션은 뻔한 경로를 짚습니다. "아동 이용자가 자신의 것이 아닌 신분증 이미지를 업로드하는 것"입니다. 그 답은 "사진 신분증 대조에서 신분증 업로드 시점에 이용자의 사진을 요구하면, 그 사진 신분증이 해당 이용자의 것인지 확인하는 데 도움이 된다"는 것입니다.
  • 그 사람이 그 자리에 있는가? "라이브니스 감지는 연령 확인 절차를 거치는 이용자가 확인이 이루어지는 시점에 그 자리에 있음을 보장함으로써, 아동 이용자가 성인의 사진을 업로드하지 않았다는 확신을 한층 높여 준다."
  • 문서가 진짜인가? "견고한 사진 신분증 확인은 쉽게 우회될 수 없어야 하며, 따라서 사진 신분증 방식은 아동이 만들거나 구할 수 있는 위조 문서나 조작을 감지할 수 있어야 한다."

이 중 어느 것도 OCR이 아닙니다. 문서를 읽는 일과 문서를 검증하는 일은 서로 다른 일이고, 서로 다른 도구가 맡습니다. 문서에 기반한 연령 확인에는 둘 다 필요합니다. 읽기 단계는 문서에 대한 믿을 수 있는 데이터를 주고, 얼굴 대조, 라이브니스 확인, 진위 확인은 그 문서와 사람을 믿어도 되는지를 알려 줍니다.

생년월일에서 "이상인가 미만인가"로

생년월일을 읽고 나면 나이 계산은 짧지만 틀리기 쉽습니다. 연도만 빼서는 안 됩니다. 2008년 11월 20일에 태어난 사람은 2026년 11월 10일에 18세가 아니라 17세입니다. 월과 일도 비교하세요.

from datetime import date


def age_on(birth_iso: str, on: date) -> int:
    """Whole years completed on the day `on`, from a YYYY-MM-DD date of birth."""
    born = date.fromisoformat(birth_iso)
    years = on.year - born.year
    if (on.month, on.day) < (born.month, born.day):
        years -= 1  # the birthday has not come yet this year
    return years


def is_over(birth_iso: str, threshold: int, on: date) -> bool:
    return age_on(birth_iso, on) >= threshold


print(age_on("2008-11-20", date(2026, 11, 10)))     # 17
print(is_over("2008-11-20", 18, date(2026, 11, 20)))  # True

실무에서는 몇 가지 세부 사항이 중요합니다.

  • 확인한 날짜를, 하나의 시간대로 쓰세요. 서버의 "오늘"과 이용자의 "오늘"은 자정 무렵에 다를 수 있습니다. 규칙을 하나 정하고, 사용한 날짜를 로그에 남기세요.
  • 2월 29일. 위의 비교에서는 2월 29일생은 2월 29일이 없는 해에 3월 1일에 한 살을 더 먹습니다. 해당 관할에서 다르게 정의한다면 그 분기 하나만 바꾸면 됩니다.
  • 날짜가 없는 것은 답이 아닙니다. 스캔이 생년월일을 돌려주지 않으면 결과는 "확인 불가"이지, 결코 "18세 이상"이 아닙니다.
  • 만료는 별개의 질문입니다. 만료된 문서를 증거로 인정할지는 여러분의 정책입니다. 만료일을 읽고 명시적으로 결정하세요.

문서가 아니라 답을 남기세요

집행위원회의 설계는 서비스에 "이용자가 18세 이상이라는 증명만" 보냅니다. 같은 생각을 여러분의 시스템에도 적용할 수 있습니다. 확인이 끝나면 필요한 것은 불리언 값과 날짜이지, 여권 이미지가 아닙니다.

Ofcom의 개인정보 섹션도 같은 방향을 가리킵니다. 사업자에게 "모든 연령 확인 방법은 개인 데이터 처리를 수반하므로 설계 단계부터의 데이터 보호 접근 방식을 따라야 한다"고 상기시키고, 따라야 할 원칙으로 데이터 최소화와 보관 기간 제한을 꼽습니다.

최소한의 기록은 다음과 같습니다.

{
  "user_id": "u_3f9c",
  "check": "age_over_18",
  "result": true,
  "checked_on": "2026-11-10",
  "method": "photo_id_matching"
}

이미지도, 문서 번호도, 생년월일도 없습니다. 법이 더 많은 보관을 요구한다면 정확히 그만큼만, 자체 삭제일이 있는 하나의 저장소에 보관하세요.

doc.cheap의 역할

doc.cheap은 여권, 신분증, 운전면허증을 읽고 인쇄된 내용을 JSON으로 돌려줍니다. 연령 확인에 유용한 필드는 ISO YYYY-MM-DD 날짜 또는 null인 holder.birth_date, 그리고 document.expiry_date, document.is_expired, mrz.status입니다. 덕분에 age_on에 넣는 날짜는 타당성을 따져 볼 수 있는 판독 결과에서 나옵니다.

이것은 판독 서비스이지 검증 서비스가 아닙니다. 인식 전용 시나리오에서 응답의 authenticity.overall은 not_checked이며, 얼굴 대조와 라이브니스 단계는 그 일을 위해 만들어진 도구에 맡깁니다. 또한 요청하지 않은 것은 아무것도 보관하지 않습니다. 라이브 키에서 retain_hours: 0을 지정하면 행이 하나도 기록되지 않으며, 이는 기록 보관 기간 가이드에서 다룹니다. 이미지 자체는 절대 저장되지 않습니다.

나머지를 설계하기 전에 읽기 단계를 시험해 보려면 무료 여권 OCR API부터 시작하세요. 제공업체를 고르고 있다면 여권 OCR API 비교에서 공개된 가격을 나란히 볼 수 있습니다.

이 글은 기술적인 요약이며 법률 자문이 아닙니다.

질문 하나 드립니다. 여러분의 연령 확인에서 제대로 만드는 데 가장 오래 걸린 부분은 어디였나요 – 판독, 얼굴 대조, 아니면 남겨 두는 기록인가요? 아래 댓글로 알려 주세요.