반쯤 찢어진 QR 코드도 읽히는 이유: QR 코드의 구조와 오류 정정

QR 코드의 모서리 사각형, 타이밍 패턴, 형식 정보가 하는 일과 버전·오류 정정 수준(L·M·Q·H)이 크기를 정하는 방식, 가운데 로고를 넣어도 읽히는 이유와 여백 규칙, 스캔 전 주소 확인까지 정리했습니다.

도구QR 코드

단골 식당 테이블에 붙은 주문용 QR 스티커는 모서리가 한참 전부터 떨어져 나가 있었습니다. 커피 얼룩도 번져 있었습니다. 그런데 휴대폰을 대면 여전히 메뉴가 열립니다. 바코드였다면 줄 몇 개만 지워져도 읽히지 않았을 텐데, QR 코드는 어떻게 버티는 걸까요.

답은 QR 코드 안에 같은 정보를 복구할 수 있는 여분이 처음부터 들어 있다는 데 있습니다. 그 여분이 얼마나 되는지, 어디가 망가지면 안 되는지를 알면 QR 코드를 만들 때 선택지가 분명해집니다.

QR 코드 버전 2(25×25 모듈)의 구조도. 1은 왼쪽 위·오른쪽 위·왼쪽 아래 세 모서리의 위치 찾기 패턴, 2는 위치 찾기 패턴 사이를 잇는 한 줄과 한 열의 흑백 교대 타이밍 패턴, 3은 오른쪽 아래 가까이의 정렬 패턴, 4는 위치 찾기 패턴 옆에 두 번 저장된 형식 정보(오류 정정 수준과 마스크), 5는 나머지를 채우는 데이터와 오류 정정 영역, 6은 모든 변에 최소 4모듈 이상 비워 두는 여백입니다. 데이터 영역은 임의로 채운 그림이라 스캔되지 않습니다.

스캐너가 가장 먼저 찾는 것

QR 코드의 세 모서리에 있는 큰 사각형은 위치 찾기 패턴(finder pattern) 입니다. 검정·흰색·검정의 폭이 1:1:3:1:1인 이 무늬는 어느 방향으로 가로질러도 같은 비율로 보이기 때문에, 카메라가 비스듬하거나 코드가 거꾸로 붙어 있어도 스캐너는 세 모서리의 위치와 방향을 먼저 잡아냅니다. 네 번째 모서리에 무늬가 없는 것이 오히려 방향을 알려 주는 단서입니다.

위치 찾기 패턴 사이에는 흑백이 번갈아 나오는 타이밍 패턴이 한 줄, 한 열씩 지나갑니다. 자처럼 칸 간격을 알려 주는 역할입니다. 버전 2 이상에는 오른쪽 아래쪽에 작은 정렬 패턴이 들어가 있어서, 휘어진 병이나 기울어진 화면에서 생긴 왜곡을 바로잡습니다. 가장 큰 버전 40에는 정렬 패턴이 46개나 들어갑니다.

위치 찾기 패턴 바로 옆에는 15비트짜리 형식 정보가 두 군데에 같은 내용으로 적혀 있습니다. 오류 정정 수준과 마스크 번호가 여기에 담기고, 이 정보 자체에도 오류 정정 부호가 붙어 있습니다. 스캐너는 이것부터 읽어야 나머지를 해석할 수 있습니다.

망가져도 읽히는 이유

나머지 칸에는 데이터와 함께 리드-솔로몬(Reed–Solomon) 오류 정정 부호가 들어 있습니다. CD나 위성 통신에도 쓰이는 방식으로, 데이터 일부가 지워지거나 잘못 읽혀도 여분의 부호로 원래 값을 계산해 냅니다. QR 코드를 만들 때는 이 여분을 얼마나 둘지 네 단계 중에서 고릅니다.

수준 복구할 수 있는 양(코드워드 기준, 대략) 어울리는 곳
L 7% 화면에 띄우는 코드처럼 깨끗한 환경
M 15% 일반적인 인쇄물. 많은 생성기의 기본값
Q 25% 공장, 야외처럼 더러워지기 쉬운 곳
H 30% 가운데 로고를 넣을 때, 손상이 잦은 곳

식당의 스티커가 버틴 것은 찢어지고 얼룩진 부분이 이 한도 안에 있었기 때문입니다. 다만 한도는 데이터 영역의 이야기입니다. 위치 찾기 패턴 세 개 중 하나가 통째로 사라지거나 형식 정보가 두 곳 모두 가려지면, 복구할 기회도 없이 읽기에 실패합니다.

가운데 로고는 오류로 취급된다

가게 로고가 가운데 들어간 QR 코드가 읽히는 것도 같은 원리입니다. 스캐너 입장에서 로고는 “망가진 칸”이고, 오류 정정이 그 부분을 메웁니다. 그래서 로고를 넣을 때는 수준을 H로 올리고, 로고를 30%에 한참 못 미치게 작게 두는 것이 안전합니다. 인쇄 번짐이나 긁힘도 같은 여분을 나눠 쓰기 때문입니다. 위치 찾기 패턴, 타이밍 패턴, 형식 정보 위에는 로고를 올리지 않습니다.

크기는 담는 내용과 수준이 함께 정한다

QR 코드의 크기는 버전으로 나타냅니다. 버전 1은 21×21칸이고, 버전이 1 오를 때마다 가로세로 4칸씩 늘어 버전 40은 177×177칸입니다. 한 변의 칸 수는 17 + 4 × 버전입니다.

같은 글자 수라도 어떤 문자냐에 따라 필요한 칸이 다릅니다. QR 코드에는 네 가지 모드가 있습니다.

모드 담을 수 있는 문자 효율 버전 40-L 최대
숫자 0–9 3자리에 10비트 7,089자
영숫자 0–9, A–Z(대문자), 공백, $ % * + - . / : 2자에 11비트 4,296자
바이트 모든 바이트(UTF-8 한글 포함) 1바이트에 8비트 2,953바이트
한자 Shift_JIS 한자·가나 1자에 13비트 1,817자

한글은 UTF-8로 한 글자가 3바이트라 바이트 모드로 들어갑니다. 버전 40-L에 한글만 넣으면 984자(2,953 ÷ 3)가 한계입니다.

실제 주소로 계산해 보면 수준의 차이가 크기로 이어지는 것이 보입니다. https://motitory.com/ko/tools/qr-code는 37바이트입니다. 기본값 M이면 버전 3(29×29)에 들어가지만, 로고를 넣으려고 H로 올리면 버전 5(37×37)가 필요합니다. 같은 인쇄 크기라면 칸이 작아지므로, 멀리서 찍어야 하는 포스터일수록 주소를 짧게 줄이는 편이 낫습니다.

대문자로 쓴 주소가 더 작아지는 경우

영숫자 모드에는 소문자가 없습니다. 그래서 https://motitory.com(20바이트)은 바이트 모드로 저장되어 M 수준에서 버전 2가 필요합니다. 같은 주소를 HTTPS://MOTITORY.COM으로 쓰면 영숫자 모드로 들어가 버전 1(21×21)에 딱 맞습니다. 주소의 앞부분(스킴과 도메인)은 대소문자를 구별하지 않으니 가능한 방법입니다. 다만 경로(/ko/tools/…)는 서버에 따라 대소문자를 구별하므로 함부로 바꾸면 안 됩니다.

마스크는 읽기 쉬운 무늬를 고르는 과정

데이터를 그대로 칸에 채우면 한쪽에 검은 칸이 몰리거나 위치 찾기 패턴과 비슷한 무늬가 우연히 생길 수 있습니다. 그러면 스캐너가 헷갈립니다. 그래서 생성기는 정해진 8가지 마스크 패턴으로 칸을 뒤집어 본 뒤, 벌점 규칙에 따라 가장 고르게 퍼진 결과를 고릅니다. 어떤 마스크를 썼는지는 형식 정보에 적혀 있어서, 스캐너가 다시 뒤집어 원래 데이터를 꺼냅니다.

여백을 잘라 내면 안 된다

QR 코드 둘레에는 최소 4칸 너비의 빈 여백(quiet zone) 이 필요합니다. 디자인을 정리하다 여백을 바짝 잘라 내거나, 무늬가 있는 배경 위에 코드를 붙이면 스캐너가 코드의 경계를 찾지 못합니다. 흰 여백을 충분히 두고, 코드는 밝은 바탕에 어두운 칸으로 인쇄하는 것이 가장 확실합니다. 색을 뒤집은 코드는 읽지 못하는 스캐너도 있습니다.

찍기 전에 주소부터 본다

QR 코드는 사람이 눈으로 내용을 읽을 수 없다는 점 때문에 악용되기도 합니다. 주차 요금 정산기나 공공장소의 안내문 위에 가짜 QR 스티커를 덧붙여 결제 정보를 가로채는 수법이 여러 나라에서 보고됐습니다. 이른바 “큐싱(quishing)“입니다. 스티커가 덧붙여진 흔적이 있으면 의심하고, 스캔한 뒤에는 열기 전에 주소의 도메인이 맞는지 확인합니다.

직접 만들고 읽어 보기

QR 코드 만들기에서 주소나 글을 넣으면 오류 복원(오류 정정) 수준을 골라 QR 코드를 만들고 PNG나 SVG로 내려받을 수 있습니다. 같은 주소를 L과 H로 바꿔 가며 만들어 보면 칸 수가 달라지는 것이 보입니다. 받은 QR 코드가 어디로 연결되는지는 QR 코드 읽기로 열기 전에 확인할 수 있습니다. 두 도구 모두 입력과 이미지를 기기 안에서만 처리합니다.

QR 코드는 1994년 일본의 덴소(현재의 덴소 웨이브, DENSO WAVE)가 자동차 부품 관리를 위해 개발했습니다. 특허를 행사하지 않겠다고 밝힌 덕분에 누구나 자유롭게 쓰게 되었고, 지금은 ISO/IEC 18004로 국제 표준이 되었습니다. “QR 코드”는 덴소 웨이브의 등록 상표입니다.

참고

  • ISO/IEC 18004:2015, Information technology — Automatic identification and data capture techniques — QR Code bar code symbology specification
  • DENSO WAVE, QR 코드 공식 사이트 qrcode.com (역사, 오류 정정 수준, 버전별 용량)

블로그로