QR 코드의 내부: 버전, 모듈, 인코딩 모드
QR 코드(ISO/IEC 18004)는 버전이라 불리는 40가지 크기로 나옵니다. 버전 1은 21×21 모듈(흑백 사각형) 그리드이고, 버전이 하나 오를 때마다 한 변에 4모듈씩 늘어나 버전 40에서는 177×177입니다. 데이터가 많을수록 버전이 높아지고, 같은 인쇄 크기에서 모듈이 작아지며, 스캔이 어려워집니다. 큰 사각형 3개는 파인더 패턴, 작은 것들은 정렬 패턴, 파인더 사이의 교대 무늬 줄은 타이밍 패턴입니다 — 이들은 데이터를 담지 않으며, 어느 것도 가려져선 안 됩니다.
용량은 세그먼트별로 자동 선택되는 인코딩 모드에 달려 있습니다. 숫자 모드는 3자리를 10비트에, 영숫자 모드는 2문자를 11비트에 담지만 45문자 집합(숫자, 대문자 A~Z, 공백, $ % * + - . / :)만 지원합니다. 바이트 모드는 바이트당 8비트를 온전히 쓰며 텍스트 인코딩은 사실상 UTF-8입니다. 한자 모드는 Shift-JIS 문자를 13비트에 담습니다. 버전 40, 최저 오류 정정 기준 상한은 숫자 7,089자리, 영숫자 4,296자, 바이트 2,953개입니다.
실용적 결론 두 가지. 첫째, 소문자는 바이트 모드를 강제합니다 — HTTPS://EXAMPLE.COM/ABC처럼 대문자로 쓴 URL은 영숫자 모드로 인코딩되어 소문자 쌍둥이보다 눈에 띄게 성긴 코드가 됩니다(스킴과 호스트는 대소문자 구분이 없지만 경로는 대개 구분하니, 통제 가능한 부분만 대문자화하세요). 둘째, 30~50자의 전형적인 짧은 URL은 버전 2~4(25×25 ~ 33×33)에 해당하며, 포스터에서 안정적으로 스캔되는 최적 지점입니다.
Alphanumeric capacity by version and EC level:
Version 1 (21×21): L 25 / M 20 / Q 16 / H 10
Version 5 (37×37): L 154 / M 122 / Q 87 / H 64
Version 10 (57×57): L 395 / M 311 / Q 221 / H 174
Version 40 (177×177): L 4296 / M 3391 / Q 2420 / H 1852
Same 60-char URL: EC L -> version 3, EC H -> version 5
(more correction = physically denser code)
HTTPS://SDK.IS/QR -> alphanumeric mode (sparser)
https://sdk.is/qr -> byte mode (denser, same length)오류 정정: L, M, Q, H가 실제로 맞바꾸는 것
QR 코드는 Reed-Solomon 오류 정정으로 페이로드를 보호합니다 — CD와 심우주 통신에 쓰이는 것과 같은 계열의 부호입니다. 네 레벨은 코드워드가 얼마나 파괴돼도 복구되는지를 말합니다: L 약 7%, M 약 15%, Q 약 25%, H 약 30%. 대가는 용량입니다. 정정 바이트가 같은 그리드에 살기 때문에 같은 URL이라도 H 레벨은 L 레벨의 대략 두 배 모듈이 필요해 버전이 한두 단계 올라갑니다.
이 퍼센트는 인쇄 면적이 아니라 코드워드 기준이며, 복구 가능성은 손상 위치에도 달려 있습니다. Reed-Solomon은 흩어진 오류에 강하지만, 코드를 가로지르는 반사광 줄무늬나 접힌 자국은 인터리브된 블록들을 표면 숫자보다 빨리 무너뜨릴 수 있습니다. 그리고 기능 패턴 — 파인더, 타이밍, 포맷 정보 — 은 오류 정정의 보호를 아예 받지 않습니다. 모서리 파인더 하나를 덮은 스티커는 레벨과 무관하게 스캔을 죽입니다.
로고 오버레이는 오류 예산을 의도적으로 소비하는 방식입니다 — 디코더가 가려진 모듈을 손상으로 간주하고 복원합니다. 로고를 얹으려면 H 레벨을 쓰고, 로고를 코드 면적의 약 10% 이하로, 중앙에, 세 파인더에서 떨어뜨려 배치하세요. 그래야 로고 비용 위에 현실의 마모와 반사에 대한 여유가 남습니다. 실용적 기본값: 화면과 깨끗한 인쇄물엔 M, 메뉴판처럼 손을 타는 물건엔 Q, 브랜딩 오버레이나 마모될 표면 인쇄에만 H — 그리고 한 단계 올릴 때마다 코드가 물리적으로 촘촘해진다는 걸 기억하세요.
Level Recoverable codewords Overhead Typical use
L ~7% lowest controlled screens
M ~15% default posters, docs
Q ~25% higher menus, packaging
H ~30% ~2x vs L logo overlay, harsh env
NOT protected by any level:
- the 3 corner finder patterns
- timing patterns / format info
- a missing quiet zone around the code인쇄의 물리학: 콰이어트 존, 대비, 거리 법칙
현장에서 일어나는 QR 실패는 대부분 디지털이 아니라 물리적 문제입니다. 스펙은 콰이어트 존 — 사방으로 최소 4모듈 폭의 빈 여백 — 을 요구합니다. 디자이너들은 낭비처럼 보인다며 이를 끊임없이 잘라내는데, 그러면 스캐너가 복잡한 배경에서 심볼을 분리하지 못해 코드가 간헐적으로 실패합니다 — 하필 책상 위에선 절대 재현되지 않는 방식으로요.
크기는 간단한 현장 법칙을 따릅니다: 최소 코드 폭 ≈ 스캔 거리 ÷ 10. 10cm에서 스캔하는 명함 코드는 1cm면 되고, 3m에서 읽는 포스터는 30cm 코드가 필요합니다. 여기에 모듈 수를 결합하세요. 같은 인쇄 폭에서 버전 10 코드는 한 변이 57모듈이라 각 모듈이 버전 2 모듈의 3분의 1 크기입니다 — 고버전 코드는 인쇄 면적이 불균형하게 더 필요합니다. 인쇄 해상도는 모듈당 프린터 도트 4개 이상을 유지하세요. 사무용 프린터에서 모듈이 약 0.4mm 아래로 내려가면 잉크 번짐이 모듈들을 뭉개기 시작합니다.
대비는 또 하나의 조용한 살인자입니다. 디코더는 이미지를 이진화하므로 밝은 배경 위의 어두운 모듈, 강한 휘도 차이가 필요합니다 — 흰 바탕에 짙은 남색은 괜찮지만 흰 바탕에 노랑은 안 됩니다. 반전 코드(어두운 바탕에 밝은 모듈)는 스펙 위반입니다. iOS와 많은 Android 스캐너가 일부를 읽어주긴 하지만 상당수 스캐너 앱과 임베디드 리더는 못 읽습니다 — 테스트 없이 배포하지 마세요. 유광 코팅은 정반사 눈부심으로 영역 전체를 날려버릴 수 있고, 곡면(병, 머그컵)은 기하를 왜곡합니다. 가능한 한 가장 평평한 면에 배치하고, 천 장을 인쇄하기 전에 반드시 iPhone과 Android 카메라 양쪽으로 테스트하세요.
Field rules of thumb:
min code width ≈ scan distance / 10
business card (10 cm) -> 1 cm
shelf label (50 cm) -> 5 cm
poster (3 m) -> 30 cm
quiet zone >= 4 modules, all four sides
module size >= 0.4 mm on office printers (>= 4 dots)
contrast dark-on-light; inverted codes fail many readers
surfaces flat > gently curved; avoid gloss + direct light알아둘 가치가 있는 페이로드 형식: Wi-Fi, vCard, otpauth, 동적 QR
QR 코드는 바이트를 인코딩할 뿐이고, 휴대폰이 그걸로 무엇을 하는지는 접두사에 따른 관례입니다. URL은 브라우저를 엽니다. WIFI: 문자열은 네트워크에 접속시킵니다: WIFI:T:WPA;S:MyNetwork;P:secret;; — T는 인증 방식(WPA가 WPA2/WPA3 퍼스널을 포괄, 개방 네트워크는 nopass), S는 SSID, P는 비밀번호이고 H:true를 붙이면 숨김 SSID입니다. 값 안의 백슬래시, 세미콜론, 콤마, 콜론, 큰따옴표는 백슬래시로 이스케이프해야 합니다 — Wi-Fi 비밀번호 속 이스케이프 안 된 세미콜론이 Wi-Fi QR이 '가끔' 실패하는 고전적 이유입니다.
연락처 데이터에는 관례가 둘 있습니다. vCard(BEGIN:VCARD ... END:VCARD)는 장황하지만 어디서나 이해되고 여러 줄 주소도 살아남습니다. MeCard(MECARD:N:...;TEL:...;;)는 NTT 도코모의 압축 형식으로 코드가 훨씬 작아지지만 지원 필드가 적습니다. 2단계 인증 설정용 TOTP 시크릿은 otpauth://totp/Issuer:account?secret=BASE32&issuer=Issuer로 이동합니다 — 그 QR 자체를 시크릿으로 취급하세요. 스캔한 사람은 누구든 영원히 당신의 코드를 생성할 수 있습니다. 그 밖의 유용한 접두사: tel:, mailto:, sms:, geo:37.5665,126.9780.
정적 vs 동적 구분은 형식이 아니라 제품 결정입니다. 정적 코드는 최종 URL을 직접 담습니다 — 유지보수가 없고 영원히 작동하지만 인쇄 후엔 바꿀 수 없습니다. '동적' 코드는 그저 당신이 통제하는 짧은 리다이렉트 URL을 인코딩한 것으로, 인쇄 후 수정 가능성, 스캔 분석, 더 짧은 페이로드(따라서 더 성긴 코드)를 얻는 대신 의존성이 생깁니다 — 리다이렉트 서비스가 죽거나 구독이 끊기면 인쇄된 모든 코드가 함께 죽습니다. 종이 위에 오래 살아남아야 하는 것이라면 서드파티 단축 서비스보다 본인 소유의 리다이렉트 도메인을 쓰세요.
WIFI:T:WPA;S:Cafe Guest;P:s3cret;;
WIFI:T:WPA;S:My\;Cafe;P:pa\:ss\,word;; <- ; : , escaped
WIFI:T:WPA;S:BackOffice;P:secret;H:true;; <- hidden SSID
otpauth://totp/GitHub:octocat?secret=JBSWY3DPEHPK3PXP&issuer=GitHub
MECARD:N:Doe,Jane;TEL:+821012345678;EMAIL:[email protected];;
BEGIN:VCARD
VERSION:3.0
N:Doe;Jane
TEL;TYPE=CELL:+82-10-1234-5678
EMAIL:[email protected]
END:VCARD보안과 프라이버시: 큐싱, 시크릿, 그리고 데이터가 가는 곳
QR 코드는 암호화되지 않고, 서명되지 않으며, 눈으로 검증할 수 없습니다. 이 세 가지 부재가 QR의 위협 모델을 정의합니다.
큐싱(quishing) — QR을 통한 피싱 — 은 사람이 스캔 전에 페이로드를 읽을 수 없다는 사실을 악용합니다. 표준 공격은 주차 미터기, 식당 테이블, 청구서의 정상 코드 위에 악성 코드 스티커를 덧붙이는 것입니다. 결제 사기 변종은 짝퉁 결제 페이지로 리다이렉트합니다. 방어책은 평범하지만 효과적입니다: 코드 옆에 사람이 읽을 수 있는 URL을 인쇄해 스캐너 미리보기와 비교할 수 있게 하고, 범용 단축 서비스 대신 브랜드 도메인을 쓰고(미리보기에 pay.yourbrand.com이 보이면 검증 가능하지만 무작위 단축 코드는 불가능), 오래 노출되는 공공 코드는 물리적 변조 여부를 점검하세요. 요즘 휴대폰 카메라는 열기 전에 목적지 도메인을 보여줍니다 — 사용자가 그걸 실제로 읽도록 훈련시키세요.
카메라가 닿는 곳에 표시되거나 인쇄되는 QR에 시크릿을 넣지 마세요. Wi-Fi QR에는 비밀번호가 평문으로 들어 있습니다 — 포스터를 사진 찍은 사람은 누구나 갖게 됩니다. otpauth QR은 2FA 시드 그 자체입니다. 행사 티켓과 탑승권에는 예약을 복제하거나 취소하기에 충분한 정보가 담겨 있습니다 — SNS에 올리는 사람들이 비싼 수업료를 치르고 배우는 사실입니다.
마지막으로, 생성이 어디서 일어나는지 아세요. 일부 웹 생성기(이 페이지 포함)는 서버 측 API를 호출해 이미지를 만들며, 이는 페이로드가 당신의 기기를 떠난다는 뜻입니다. 다른 것들은 JavaScript 라이브러리로 브라우저 안에서만 렌더링하거나 qrencode CLI 같은 오프라인 도구를 씁니다. 공개 웹사이트 URL이라면 차이가 없습니다. Wi-Fi 자격 증명, TOTP 시드, 내부용 정보라면 오프라인에서 생성하세요 — qrencode 명령이나 로컬 라이브러리가 네트워크 왕복 없이 같은 PNG를 만들어 줍니다.
# offline generation — payload never leaves the machine
qrencode -o wifi.png -l H 'WIFI:T:WPA;S:BackOffice;P:secret;;'
# checklist for public, long-lived codes
[ ] human-readable URL printed next to the code
[ ] branded domain (pay.yourbrand.com), not a generic shortener
[ ] HTTPS destination — camera previews show the domain
[ ] periodic physical check for sticker-over-sticker tampering
[ ] no secrets in the payload (Wi-Fi passwords ARE plaintext)