QR 코드를 읽은 값을 그대로 쓰면 안 됩니다 ── 오류 정정이 통과해도 값은 보장되지 않습니다

· 업데이트: · · QR 코드, 바코드, 오류 정정, 입력 검증, 데이터 품질, 업무 시스템, C#, 설계, 현장 운영

수정 이력(4건, 최종 수정 2026년 09월 03일)

이 글에 적용한 변경 사항의 기록입니다. 보관해 둔 수정 전 버전은 DOI가 부여된 고정 URL에서 읽을 수 있습니다.

permalink·저자 표기·지식 맵 래퍼·깨진 내부 링크 등 CI가 지적한 표시용 수정을 반영했습니다. 본문의 기술적인 주장은 바꾸지 않았습니다.
기사 맨 앞에 「이 기사의 지식 맵」 절을 추가했습니다. 본문에서 다루는 개념과 그 관계를 요약·그림·상세 페이지 링크로 정리한 것입니다. 본문의 주장은 바꾸지 않았습니다.
외부 리뷰(1283건) 대응으로 본문을 갱신했습니다. 개별 변경 내용은 아래 이력을 참조하십시오.
검증 조건(대상 심볼, 생성 도구, decoder 2종, 손상 부여 방식, 시행 횟수, 판정 기준)을 표로 명시했습니다. 실패 패턴 4가지 각각에 업무에서 어떻게 나타나는지 구체 예를 추가하고, 문자 코드 전제를 먼저 정하는 주의와 검사 순서를 독립시켰습니다. 말미에 자신의 QR로 시험하는 3단계를 추가했습니다.
최초 공개
이 글을 인용하기(DOI(등록된 아카이브): 10.5281/zenodo.22175111)

아래 DOI는 이전에 등록된 아카이브를 가리키며 현재 본문과 다를 수 있습니다. 현재 본문을 참조할 때는 이 페이지의 URL을 사용하세요.

Go Komura (2026). 「QR 코드를 읽은 값을 그대로 쓰면 안 됩니다 ── 오류 정정이 통과해도 값은 보장되지 않습니다」. 합동회사 코무라소프트. https://comcomponent.com/ko/blog/qr-decoded-value-validation/

DOI(등록된 아카이브)
10.5281/zenodo.22175111
DOI(마지막 등록 버전)
10.5281/zenodo.22175112

창고 검품 단말이 「삐」 하고 울립니다. 전표의 QR 코드를 읽었다는 신호입니다. 읽힌 문자열은 그대로 재고 시스템으로 넘어가고, 전표가 할당되며, 출하 지시가 확정됩니다. QR 코드에는 오류 정정이 있으니, 조금 더러워져 있어도 올바른 값이 돌아온다 ── 이 전제로 만든 시스템은 결코 드물지 않습니다.

앞부분은 맞는 이야기입니다. QR 코드는 오염이나 파손이 있어도 데이터를 복원하도록 설계되어 있으며, 오류 정정 레벨 L부터 H까지 codeword의 약 7%부터 약 30%를 복원할 수 있습니다.1 문제는 뒷부분, 「그러니 돌아온 값은 맞다」는 추론입니다.

이 기사에서는 실제로 생성한 QR 코드 샘플 이미지와 decoder 2종으로 측정한 결과를 바탕으로, 오류 정정이 통과해도 값이 보장되지 않는 이유와 업무 시스템 쪽에서 무엇을 검증해야 하는지를 정리합니다. 기사에 실은 QR 코드는 모두 실물입니다. 읽을 수 있는 것은 손안의 QR 리더로 그대로 확인할 수 있습니다(「읽을 수 없음」을 보이기 위한 견본도 포함되어 있으며, 그 부분에서는 명시합니다). 브라우저에서 jsQR과 OpenCV.js를 바꿔 가며 시험할 수 있는 QR 코드 읽기 비교 도구도 준비했습니다. 이 기사의 실측은 그곳에서 재현할 수 있습니다.

1. 먼저 결론

  • 오류 정정은 「복원」이지 「검증」이 아닙니다. 규격 자신이, 더러워진 모듈은 「분명히 타당하지만 다른 codeword로 오복호한다」고 적고 있습니다.2
  • 무작위 오염이라면 거의 「읽을 수 없음」이 됩니다. 실측 9,700회에서 잘못된 값이 반환된 사례는 0건이었습니다.
  • 그러나 손상이 한곳에 치우치면 반드시 다른 값이 됩니다. 26 codeword 중 7개가 바뀌는 것만으로 004873104873으로 읽히고, 서로 독립된 decoder 2개가 나란히 같은 오류를 반환했습니다.
  • 오류 정정 바깥에는 더 단순한 함정이 있습니다. 분할 QR, 문자 코드, 화면 안의 다른 코드. 모두 오류가 나오지 않은 채로 일어날 수 있습니다. decoder에 따라 경고나 예외가 나오기도 하지만, 나오는 방식은 구현에 달리고, 나오지 않는 쪽으로 기울어지는 조합이 실제로 있습니다.
  • 그러므로 읽힌 값은 검증되지 않은 입력으로 다룹니다. 형식 검사 → check digit → 업무 검증의 3단계로 받는 것이 기본형입니다.

그림의 실선은 항상 성립하는 관계, 점선은 조건이 붙는 관계입니다(성립 조건은 상세 페이지의 관계별 설명에 적혀 있습니다). 관계 전체 목록(총 16건, 근거와 확신도 포함)과 주요 개념의 정의는 지식 맵 상세 페이지에 정리되어 있습니다(일본어). 데이터: JSON-LD / Turtle

2. 샘플 ── 거의 같아 보이는 QR 코드 두 장

먼저 실물을 보십시오. 다음 두 장은 모두 오류 없이 읽힙니다.

위가 정상 QR 코드, 아래는 세로 한 줄의 띠 모양 손상이 들어간 QR 코드. 겉모습은 거의 같다

시험하기 전에 한 마디: iOS 표준 카메라에서는 반응하지 않을 수 있습니다. 읽히지 않으면 QR 리더 앱으로 시험하십시오(이유는 아래 주석에 적었습니다).

  • A(위)NO:20260725-004873
  • B(아래)NO:20260725-104873

실물이므로 손안의 리더로 그대로 시험할 수 있습니다. 이 전표 번호는 「수주일 8자리 + 연번 5자리 + check digit 1자리」라는 체계이므로, 바뀐 것은 연번 부분 ── 0048710487이 되어 1만 건 어긋난 다른 전표를 가리킵니다.

iOS 표준 카메라에서는 반응하지 않을 수 있습니다. 표준 카메라는 URL처럼 「열기」 조작이 가능한 내용을 우선하는 구조라, 이 기사처럼 문자열만 있는 QR에서는 아무것도 나오지 않을 수 있습니다. QR 리더 앱을 쓰면 읽힙니다. 같은 이미지인데 읽는 쪽 구현에 따라 동작이 바뀐다 ── 이 기사의 주제 그 자체를 첫 샘플에서 체험하게 됩니다.

B가 A와 다른 점은 왼쪽에서 10~14열, 세로 한 줄의 띠 안에 있는 31모듈뿐입니다. codeword 영역 208모듈 중 15%에 해당합니다.

B 가운데 A에서 바뀐 31모듈을 빨간 테두리로 나타낸 그림. 세로 방향으로 가늘고 길게 분포한다

그리고 핵심은 decoder가 어느 쪽에 대해서도 오류를 반환하지 않는다는 점입니다. 「정정했습니다」라는 알림조차 없습니다. 앱에서 보면 둘 다 같은 성공 읽기입니다.

한 가지 밝혀 두면, 이 손상은 노리고 구성한 것이며, 우연히 이렇게 될 확률은 높지 않습니다. 만드는 방법과 현실 오염과의 거리는 제4절에서 다룹니다.

3. 왜 일어나는가 ── 오류 정정의 내용

QR 코드의 오류 정정은 Reed-Solomon 부호이며, codeword(8비트 단위)마다 동작합니다. 이번에 쓴 버전 1·오류 정정 레벨 M(21×21 모듈)의 내역은 이렇습니다.2

총 codeword 수 데이터 codeword 오류 정정 codeword 규격상 정정 능력
26 16 10 4 codeword

눈에 띄는 것은 마지막 열입니다. 오류 정정 codeword가 10개 있으면 Reed-Solomon 부호로서는 5개까지 정정할 수 있습니다. 그런데 규격이 정한 정정 능력은 4입니다. 차이의 1개분은 오독 방지용 codeword p 로 의도적으로 남겨 둡니다(버전 1-M에서는 p = 2). 규격 표 13에도 각주로 「오독 확률을 낮추기 위해 정정 능력을 오류 정정 codeword 수의 절반 미만으로 한다」고 명시되어 있습니다.2

규격 자체가 「오류 정정은 잘못된 값을 낼 수 있다」는 전제로 설계되어 있다는 뜻입니다. 같은 절에는 이렇게도 적혀 있습니다.

QR 코드는 매트릭스 심볼이므로, 모듈을 어두운 쪽에서 밝은 쪽으로(또는 그 반대로) 바꾸는 결함은, 해당 심볼 문자가 분명히 타당하지만 다른 codeword로 오복호하는 결과를 초래한다.2

이유는 정정 원리 그 자체에 있습니다. Reed-Solomon 복호가 하는 일은 받은 패턴에서 일정 거리(정정 능력) 안쪽에 codeword가 있는지 찾는 것입니다. 찾으면 그것을 답으로 반환하고, 찾지 못하면 「읽을 수 없음」으로 끝납니다. 아무리 깨져 있어도 가장 가까운 codeword를 찾아내는 동작이 아닙니다.

이 성질 때문에 결과는 둘로 갈립니다. 무작위 손상은 어느 codeword에서도 먼 곳에 흩어지므로, 대개는 「찾지 못함=읽을 수 없음」이 됩니다. 위험한 것은 손상이 우연히 다른 codeword의 근방에 들어갔을 때입니다. 그때 decoder는 그 codeword야말로 정답이라고 판단해 반환합니다. 돌아온 codeword는 그 자체로 완전히 정합하므로, 오류라고는 알 수 없습니다.

4. 어디까지 정정되고, 어디서부터 위험한가

여기부터가 실측입니다. 결론을 먼저 말하면, 위험한 것은 오염의 「양」이 아니라 「걸리는 곳」이었습니다.

숫자를 읽기 전에 이 장의 실험 조건을 정리합니다. 직접 재현할 때의 전제입니다.

항목 내용
대상 심볼 버전 1-M / 21×21(26 codeword = 데이터 16 + 오류 정정 10)
생성 segno 1.6.6 / Python 3.11
decoder 1 OpenCV 5.0.0 cv2.QRCodeDetector(Python 3.11)
decoder 2 jsQR 1.4.0(Node.js 22)
손상 부여 방식(4.1절) codeword 영역 208모듈에서 무작위로 골라 반전/codeword 단위로 무작위 파괴/심볼 전체에 균일한 흐림·노이즈·대비 저하
손상 부여 방식(4.2절) 목표값 B로 모으는 codeword 조합을 전수 시행
시행 횟수 모듈 반전 3,900회(각 수준 300회), codeword 파괴 1,800회(각 수준 200회)+정정 능력 초과의 추가 시험 4,000회, 화질 열화 1,000회(각 조건 200회). 4.2절은 792가지·495가지 전수
판정 decoder 반환값을, 기댓값과 일치하면 「올바르게 읽힘」, 값을 얻지 못하면 「읽을 수 없음」, 비어 있지 않은 다른 값이 오면 「잘못된 값」으로 분류

제5절 샘플을 포함한 기사 전체 환경은 기사 말미의 「검증 환경」에 정리되어 있습니다.

4.1. 무작위 오염은 「읽을 수 없음」이 된다

버전 1-M 심볼에 대해 codeword 영역 208모듈 중 몇 개를 무작위로 반전하고 결과를 분류했습니다(각 수준 300회, 합계 3,900회).

6모듈 반전과 9모듈 반전 QR 코드 비교. 위는 읽히고 아래는 읽히지 않는다

위는 6개, 아래는 9개를 반전한 것입니다. 위는 올바르게 읽고, 아래는 전혀 읽히지 않습니다. 3모듈 차이는 사람 눈에는 거의 보이지 않습니다. 경계는 겉모습에 나타나지 않는 곳에 있습니다.

반전한 모듈 수 올바르게 읽힘 읽을 수 없음 잘못된 값
0〜5 1,799 1 0
6 131 169 0
7 32 268 0
8 11 289 0
9〜12 0 1,200 0

읽을 수 있는지는 5개에서 6개 사이에서 무너지고, 9개 이상은 전멸합니다. 그리고 잘못된 값은 1건도 나오지 않았습니다. 정정하지 못하는 손상은 「읽을 수 없음」이 된다는 것이 솔직한 좋은 소식입니다. jsQR에서도 숫자는 거의 같았습니다(0〜5는 1,800건 모두 정답, 6 이후는 OpenCV와 1건 차 이내).

같은 일을 codeword 단위로 보면 경계가 더 뚜렷합니다(각 수준 200회, 합계 1,800회).

파괴한 codeword 수 올바르게 읽힘 읽을 수 없음 잘못된 값
0〜5 1,194 6 0
6〜8 0 600 0

5 codeword까지 정정되고 있습니다. 앞 절에서 본 대로 규격상 정정 능력은 4이고, 나머지는 「정정하지 않고 검출만 하는」 칸이었습니다. jsQR은 0〜5의 1,200건을 모두 올바르게 읽었고, 구현 2개가 모두 그 칸을 정정에 써 버리고 있습니다. 규격이 오독 대책으로 둔 마진은 구현 수준에서는 믿을 수 없습니다.

정정 능력을 분명히 넘는 파괴(6 codeword와 8 codeword)도 각 2,000회씩 추가 시험했지만, 잘못된 값은 역시 0건, 모두 「읽을 수 없음」으로 끝났습니다.

카메라에서 오는 열화도 같은 경향입니다. 심볼 전체에 균일한 흐림·노이즈·대비 저하를 준 결과(각 200회, 합계 1,000회).

흐림 σ 노이즈 σ 대비 올바르게 읽힘 읽을 수 없음 잘못된 값
0 0 1.00 200 0 0
1.5 10 0.90 183 17 0
3.0 20 0.70 1 199 0
4.5 30 0.50 0 200 0
6.0 40 0.35 0 200 0

결과는 「올바르게 읽힘」이거나 「읽을 수 없음」 중 하나이며, 중간은 없습니다. 열화가 진행되면 정답률이 떨어지지만, 줄어든 분은 모두 읽기 실패로 바뀝니다.

다만 이것은 균일한 열화에서의 이야기입니다. 실제 손떨림에는 방향이 있고, 비스듬한 촬영이나 조명 얼룩은 상의 일부만 무너뜨립니다. 다음에 보듯 위험한 것은 손상이 치우치는 일이므로, 「화질 문제라면 오독하지 않는다」고 일반화하지 마십시오.

4.2. 걸리는 곳이 나쁘면 반드시 다른 값이 된다

제2절의 샘플 B는 그 「치우친 손상」을 노리고 구성한 것입니다.

NO:20260725-004873(A)와 NO:20260725-104873(B)의 26 codeword를 나란히 놓으면, 차이가 있는 곳은 12곳이었습니다. 데이터 codeword 2개와, 그에 끌려간 오류 정정 codeword 10개입니다.

이 12곳 중 7곳을 B 값으로 모으면, 완성된 패턴은 A에서 7 codeword만큼 떨어지고, B에서는 5 codeword만큼의 거리에 옵니다. 정정 능력이 5라면 decoder는 이를 「B가 5곳 더러워진 것」으로 해석하고 B로 정정합니다.

조건 시험한 조합 B로 오독한 수
7 codeword를 B로 모음(B와의 거리 5) 792가지 792가지(100%)
8 codeword를 B로 모음(B와의 거리 4) 495가지 495가지(100%)

어느 조합에서도 예외 없이 오독했습니다. 아래 행은 B와의 거리가 4, 즉 규격이 정한 정정 능력 안쪽입니다. 오독 방지용 codeword p를 존중하는 구현이더라도, 손상이 1 codeword만큼 늘면 같은 결과가 됩니다. p는 확률을 낮출 뿐이며, 막지는 않습니다.

제2절에 실은 B는 이 가운데 손상이 세로 한 줄로 모이는 조합(31모듈)입니다. 최소화하면 23모듈까지 줄일 수 있었습니다. OpenCV 5.0.0과 jsQR 1.4.0이라는 무관한 구현 2개가 모두 NO:20260725-104873을 반환합니다.

4.3. 이 차이를 어떻게 받아들일까

솔직히 쓰면, 이 손상은 무작위로 일어나기 어렵습니다. 아무렇게나 더럽힌 경우에는 9,700회 시험해 오독 0건이었습니다. 「내일 당장 일어난다」는 이야기가 아닙니다.

그래도 무시할 수 없는 이유가 세 가지 있습니다.

  • 현실의 손상은 무작위가 아닙니다. 접힌 자국은 직선으로 달리고, 운반 마찰은 같은 변에 모이며, 인쇄 헤드 막힘은 세로 줄이 됩니다. 이번 띠 모양 손상은 이런 「위치에 치우치는 손상」의 한 예입니다. 다만 B는 흰→검 17모듈·검→흰 14모듈로 양방향 변화를 포함하므로, 잉크만 줄어드는 불량으로는 재현할 수 없습니다. 양방향이 동시에 일어나는 것은 접힌 자국의 그림자로 이진화 판정이 어긋나는 경우, 오염과 흐려짐이 겹치는 경우, 위에서 다른 스티커가 부분적으로 붙는 경우입니다.
  • 스캔 횟수의 규모가 다릅니다. 1회당은 무시할 확률이라도, 하루에 수만 회 읽는 현장에서는 이야기가 달라집니다. 게다가 오독은 오류를 내지 않아 기록에 남지 않고, 원인 불명의 재고 조사 차이로 처리되고 끝납니다.
  • 의도적으로 만들 수 있다는 것은 다른 사람도 만들 수 있다는 뜻입니다. 이번 패턴은 노린 값을 정한 뒤 기계적으로 구성했습니다. 가격표나 쿠폰처럼 바꿔 쓸 동기가 있는 QR에서는 이것이 공격 수법이 됩니다.

5. 오류 정정과 관계없는 「읽혔는데 다르다」

실무에서 더 자주 밟는 것은 이쪽입니다. 오류 정정이 완벽하게 동작해도 일어나고, 확률의 문제조차 아닙니다.

5.1. 분할 QR의 첫 장만 읽는다

QR 코드에는 긴 데이터를 여러 심볼로 나눠 읽는 쪽에서 이어 붙이는 장치(Structured Append)가 있습니다. 다음은 NO:20260725-004873/LOT:AB-77/QTY:120/EXP:20270131을 3장으로 나눈, 그 첫 장입니다.

분할 QR 3장 중 첫 장. 단독으로 읽으면 끝이 잘린 전표 번호가 반환된다

겉모습은 평범한 QR 코드이며, 3장 중 1장이라는 단서는 없습니다. 이것을 단독으로 읽으면 OpenCV는 이렇게 반환합니다.

NO:20260725-00487

오류 없음, 경고 없음. 끝의 check digit 3만 떨어진, 완전히 그럴듯한 전표 번호입니다. 2장·3장은 각각 3/LOT:AB-77/QTY: 120/EXP:20270131이 됩니다. 같은 이미지를 jsQR에 넘기면 빈 문자열이 반환되었습니다. 분할 QR을 가정하지 않은 앱이 우연히 첫 장을 스캔했을 때 무엇이 일어나는지는 decoder에 따라 달라집니다.

업무에서는 이렇게 나타납니다. 전표 번호를 앞부분 일치나 LIKE로 검색하는 화면이면, 끝 1자리가 떨어진 NO:20260725-00487이 그대로 원래 전표에 걸려 「읽혔고, 전표도 나왔으니」 아무도 이상을 눈치채지 못합니다. 자릿수를 고정으로 검사하지 않는 한, 이 조각은 끝까지 정상적인 읽기로 흘러갑니다.

5.2. 문자 코드와 ECI

다음은 部品番号 東-004873을 Shift_JIS로, ECI 지정 없이 넣은 QR 코드입니다.

Shift_JIS로 ECI 지정 없이 생성한 QR 코드. 읽으면 문자가 깨진다

이것도 실물입니다. 손안의 리더로 읽으면 部品番号 東-004873이 나오거나, 깨진 문자열이 나오거나, 아무것도 반환되지 않거나 ── 사용 중인 리더가 어느 해석을 하는지 알 수 있습니다.

QR 코드 읽기 비교 도구에서 이 이미지를 읽히면, jsQR이 꺼낸 원시 바이트열과 그것을 UTF-8 / Shift_JIS / EUC-JP 등으로 다시 해석한 결과가 나란히 나옵니다. 같은 바이트열이 문자 코드에 따라 다른 것이 되는 모습을 그 자리에서 확인할 수 있습니다.

같은 내용을 문자 코드와 ECI 지정을 바꿔 생성해 decoder 2개에 읽힌 결과가 이쪽입니다. 이 4조건의 심볼은 모두 도구 샘플에 들어 있습니다. 다만 표의 값은 Python의 cv2로 측정한 것이며, 3행만 브라우저 판과 결과가 다릅니다(이 표 직후에 자세히 말합니다). 도구에서 확인할 수 있는 것은 브라우저 판의 동작이므로, 3행의 「예외로 죽는다」를 재현하려면 Python의 cv2가 필요합니다.

생성 조건 OpenCV 5.0.0 jsQR 1.4.0
Shift_JIS / ECI 없음 깨진 문자열을 성공으로 반환 빈 문자열
UTF-8 / ECI 없음 部品番号 東-004873 部品番号 東-004873
Shift_JIS / ECI 있음 경고를 내고 복호 실패 빈 문자열
UTF-8 / ECI 있음 部品番号 東-004873 部品番号 東-004873

1행이 최악입니다. OpenCV는 바이트열을 Latin-1로 해석해 \x95\x94\x95i...라는 깨진 문자열을 오류 없이 반환했습니다. 앱에서 보면 정상 읽기이고, 그대로 데이터베이스에 들어가면 문자가 깨진 레코드가 1건 만들어집니다.

업무에서는 이렇게 나타납니다. 입고 실적의 품명 칸에 깨진 문자열이 들어가 등록되고, 다음날 「그 품번으로 검색해도 실적이 나오지 않는다」는 문의가 됩니다. 같은 라벨을 다시 읽어도 같은 값이 들어가므로 현장은 「시스템 검색이 이상하다」고 보고하고, 원인이 읽기 쪽 문자 코드라고 알기까지 왕복이 이어집니다.

이것은 OpenCV의 버그가 아니라 현행 규격이 정한 기본 해석(ISO/IEC 8859-1)대로의 동작입니다.3 규격에서 벗어난 것은 Shift_JIS를 ECI 없이 넣은 쪽입니다.

3행도 놓치면 안 됩니다. ECI(문자 코드를 명시하는 장치)를 올바르게 지정했는데 OpenCV는 QR: ECI is not supported properly라는 경고를 내고, 반환값을 UTF-8로 해석하지 못해 예외로 죽었습니다. 규격에 충실하게 만든 QR 쪽이 읽히지 않는 역전이 실제로 일어납니다.

게다가 이 3행은 같은 OpenCV라도 언어 바인딩에 따라 결과가 바뀝니다. 위 표는 Python의 cv2 결과이지만, 브라우저 판(opencv.js 5.0.0)에 같은 이미지를 넘기면 예외는 나오지 않고 ���i��� ��-004873이라는 치환 문자투성이 문자열을 성공으로 반환합니다. Emscripten이 std::string을 UTF-8로 변환할 때 잘못된 바이트를 예외가 아니라 U+FFFD로 떨어뜨리기 때문입니다. 버전도 이미지도 같고, 바뀐 것은 호출 쪽 언어뿐입니다. 예외로 눈치챌 수 있는 Python 쪽이 아직 낫고, 브라우저 판은 「읽혔다」고 하면서 깨진 값을 반환합니다. 도구 샘플에서 실제로 확인할 수 있습니다.

5.3. 화면 안에 QR이 여러 개 있다

전표에 QR이 여러 장 인쇄되어 있다, 옆 상자 라벨이 시야에 들어온다 ── 흔한 상황입니다. QR 3개를 가로로 나란히 두고 시험했습니다.

QR 코드 3개를 가로로 나란히 둔 이미지. 왼쪽부터 NO:, ITEM:, LOT: 순

먼저 이 이미지에서는 OpenCV의 단일 읽기 API는 아무것도 반환하지 않았습니다. 다만 이것은 「여러 장이면 걸러 준다」는 보장이 아닙니다. 단일 읽기 API는 하나의 QR을 검출해 복호한다고 문서화되어 있을 뿐, 여러 장을 거부한다고는 적혀 있지 않으며, 배치에 따라서는 그중 하나를 반환할 수도 있습니다. 여러 코드 검출을 단일 읽기 API 반환값의 유무로 대신하지 마십시오.

그렇다면 다중 읽기 API라면 어떨까 ── 여기가 까다로웠습니다.

위 그림(왼쪽부터 NO: / ITEM: / LOT:)을 그대로 쓰고 픽셀 수만 바꿔 읽힌 결과입니다. 실제 스캐너에서 대상과의 거리나 카메라 해상도가 바뀌는 상황에 해당합니다.

이미지 너비 반환 순서
1,001px NO: / LOT: / ITEM:
1,502px 아무것도 반환되지 않음
2,002px NO: / LOT: / ITEM:
3,003px NO: / ITEM: / LOT:
4,004px LOT: / NO: / ITEM:

같은 이미지인데 해상도가 바뀌기만 해도 순서가 바뀝니다. 왼쪽부터 순서도, 큰 순서도 아닙니다. 읽히지 않는 해상도조차 있습니다. 순서는 검출 알고리즘의 내부 사정으로 정해지는 것이며, 규정되어 있지 않은 이상 이렇게 됩니다.

즉 「첫 번째가 전표 번호일 것」이라며 인덱스 0을 쓰는 코드는, 오늘은 우연히 동작하더라도 내일 카메라를 가까이 대기만 해도 다른 코드를 잡습니다. 재현하기 어렵고 원인도 쫓기 어려운 종류의 장애입니다.

업무에서는 이렇게 나타납니다. 검품대 위에서 목적 전표와 옆 상자 라벨이 동시에 시야에 들어온다. 인덱스 0을 채택하는 앱은 옆 전표를 할당하고 출하 지시가 그쪽에서 확정됩니다. 작업자는 올바른 라벨에 카메라를 향한 셈이므로, 출하 뒤에 「다른 상품이 도착했다」는 말을 듣기까지 아무도 모릅니다.

받는 쪽은 순서가 아니라 내용으로 고를 필요가 있습니다. 다중 읽기 API로 전부 집어 접두사와 형식이 일치하는 것만 채택하고, 해당이 0개 또는 2개 이상이면 오류로 한다 ── 이것이 안전한 작성법입니다.

5.4. 애초에 내용이 맞다는 보장은 없다

이미지 처리로는 원리적으로 검출할 수 없는 계층이 있습니다. 인쇄 원본 데이터가 틀렸다, 바꿔 붙이기 전 옛 라벨이 상자에 남아 있다, 다른 거래처 라벨이 섞였다, 라벨이 복사되어 있다.

QR 코드는 「거기에 무엇이 쓰여 있는지」만 알려 줍니다. 「그것이 맞는지」「자사가 발행한 것인지」는 받은 쪽이 확인할 수밖에 없습니다.

업무에서는 이렇게 나타납니다. 재사용한 상자 측면에 지난번 라벨이 남아 있어, 윗면의 새 라벨이 아니라 그쪽을 읽는다. 값으로서는 완전히 올바른 QR이므로 형식 검사도 check digit도 master 조회도 전부 통과하고, 지난번 출하처로 짐이 향합니다. 이 계층만은 읽기 값 검사로는 전혀 잡히지 않습니다.

6. 받은 값을 어떻게 다룰까

대책은 오류 정정 바깥에 자체 검증을 쌓는 것으로 귀결됩니다.

단계 검증 내용 잡을 수 있는 것
1. 형식 검사 길이·문자 종류·구분자·접두사의 완전 일치 다른 코드 읽기, 분할 QR 조각, 문자 깨짐
2. 자기 검증 check digit 1글자 변화는 확실히 검출. 여러 글자는 놓침 있음
3. 업무 검증 master 조회와, 지금 처리해야 할 대상과 일치하는지 옛 라벨, 타사 라벨, 다른 전표 뒤바꿈

샘플 전표 번호는 NO: + 수주일 8자리 + 연번 5자리 + check digit 1자리이며, 끝은 GS1과 같은 modulus 10·weight 3 방식입니다. 제2절의 오독 NO:20260725-104873은 여기서 멈춥니다. 2026072510487의 올바른 check digit은 0이고, 라벨 위의 3과 일치하지 않기 때문입니다.

코드를 쓰기 전에 문자 코드 전제를 먼저 정하십시오. 아래 구현은 전표 번호가 ASCII 숫자만으로 구성된다는 전제로, check digit을 「문자 − '0'」으로 계산합니다. 전각 숫자나 5.2절에서 본 깨진 바이트열이 이 계산까지 도착하면 결과는 의미가 없습니다. 그래서 형식 검사의 정규식은 \d가 아니라 [0-9]로 쓰고, ASCII 이외의 숫자를 check digit 계산에 도달시키지 않는 순서로 되어 있습니다. 전제를 정하는 것이 먼저, 그것을 형식 검사로 보장하는 것이 다음, 계산은 그다음입니다. 이 순서가 무너지면 뒷단 검사는 모두 헛돌게 됩니다.

using System.Linq;
using System.Text.RegularExpressions;

public sealed record ScanOutcome(bool Accepted, string? SlipNo, string Reason);

public static class SlipScanValidator
{
    // NO: + 수주일 8자리 + '-' + 연번 5자리 + check digit 1자리
    // 끝은 $가 아니라 \z. .NET의 $는 끝 개행 직전에도 매칭하므로,
    // "NO:20260725-004873\n"을 통과시켜 버린다.
    // 숫자는 \d가 아니라 [0-9]. .NET의 \d는 전각 숫자 등 Unicode 숫자 전반에
    // 매칭하지만, 뒷단 check digit 계산은 ASCII를 전제로 한다
    private static readonly Regex Format =
        new(@"\ANO:(?<date>[0-9]{8})-(?<seq>[0-9]{5})(?<cd>[0-9])\z", RegexOptions.Compiled);

    public static ScanOutcome Validate(string? raw, ISlipRepository repo)
    {
        // 1. 「읽지 못함」을 「빈 성공」으로 만들지 않는다.
        //    decoder는 실패를 null / 빈 문자열 / 예외 중 무엇으로 반환하는지 구현마다 다르다
        if (string.IsNullOrEmpty(raw))
            return new(false, null, "읽지 못했습니다. 다시 스캔해 주십시오");

        // 2. 형식 검사. 앞부분 일치가 아니라 길이까지 포함한 완전 일치로 본다.
        //    분할 QR 조각 "NO:20260725-00487"은 여기서 떨어진다
        var m = Format.Match(raw);
        if (!m.Success)
            return new(false, null, $"전표 QR 형식이 아닙니다({Describe(raw)})");

        // 3. 자기 검증. 오정정으로 숫자가 바뀌었으면 여기서 잡힌다
        var body = m.Groups["date"].Value + m.Groups["seq"].Value;
        if (Modulus10Weight3(body) != m.Groups["cd"].Value[0] - '0')
            return new(false, null, "check digit이 일치하지 않습니다. 라벨 오염을 확인해 주십시오");

        // 4. 업무 검증. 실재하는지, 그리고 지금 처리해도 되는 상태인지
        var slip = repo.Find(raw);
        if (slip is null)
            return new(false, null, "해당하는 전표가 없습니다");
        if (slip.Status != SlipStatus.WaitingForShipment)
            return new(false, null, $"이 전표는 「{slip.Status}」입니다. 출하 대상이 아닙니다");

        return new(true, raw, "OK");
    }

    // GS1과 같은 modulus 10·weight 3. 오른쪽 끝부터 3,1,3,1... 가중치를 곱한다
    private static int Modulus10Weight3(string body)
    {
        var sum = 0;
        for (var i = 0; i < body.Length; i++)
        {
            var weight = (body.Length - i) % 2 == 1 ? 3 : 1;
            sum += (body[i] - '0') * weight;
        }
        return (10 - sum % 10) % 10;
    }

    // 읽기 값은 외부 입력. 화면이나 로그에 내기 전에 인쇄 가능한 ASCII만 남긴다.
    // char.IsControl은 Unicode Control(Cc)만 떨어뜨리고, U+202E(오른쪽 가로쓰기
    // 덮어쓰기) 등의 Format(Cf)를 통과시킨다. 제외 목록이 아니라 허용 목록으로 쓴다
    private static string Describe(string raw)
    {
        var kept = raw.Where(c => c >= ' ' && c <= '~').Take(40).ToArray();
        if (kept.Length == 0) return "(표시할 수 없는 문자열)";
        var safe = new string(kept);
        return kept.Length < raw.Length ? safe + "…(일부 문자를 제거)" : safe;
    }
}

check digit 설계 자체는 업무 시스템의 코드 설계와 check digit에서 산식과 고르는 법을 정리하고 있습니다. 이 기사 맥락에서의 가치는, 그 검사가 사람의 입력 실수뿐 아니라 기계의 오독에도 효과가 있다는 점에 있습니다.

이 3단계가 막지 못하는 것

3단계를 갖춰도 빠져나가는 구멍이 세 가지 있습니다. 모두 「검증 설계를 한 단계 더 깊게」 하면 막을 수 있으니 함께 잡아 두십시오.

check digit은 여러 글자 변화를 놓칩니다. modulus 10·weight 3이 확실히 잡는 것은 1글자 오류입니다. 실제로 20260725004872026072517487은 둘 다 check digit이 3이 되므로, NO:20260725-174873은 2단계를 그냥 통과합니다. 오정정이 바꾸는 것이 1글자라고 할 수 없으므로 3단계를 뺄 수 없습니다.

master 조회를 「존재 확인」으로 끝내지 마십시오. 위 코드의 repo.Find()와 상태 검사는 「쓸 수 있는 전표가 어딘가에 존재한다」는 것만 확인합니다. 작업자가 옆 상자 라벨을 읽은 경우, 그 라벨도 형식·check digit·출하 대기 상태를 모두 충족하므로 그대로 통과합니다. 읽은 값은 지금 처리 중이어야 할 대상과 맞춰 봐야 합니다 ── picking list의 다음 1건과 일치하는지, 이미 스캔한 컨테이너 ID에 묶여 있는지, 출하처가 작업 중인 편과 같은지. 무엇과 맞출지는 업무마다 다르므로, 여기만은 범용 코드가 되지 않습니다.

이중 처리는 검증으로 막지 못합니다. 단말 2대가 같은 라벨을 거의 동시에 읽으면, 둘 다 「출하 대기」를 확인한 뒤 상태를 갱신하므로 양쪽이 통과합니다. 막는 것은 실행 쪽 일이며, UPDATE ... WHERE status = '出荷待ち'처럼 조건부 상태 전이를 한 번의 원자적 조작으로 만들거나, 멱등 키로 재실행을 흡수합니다. 검증은 입구 판정이며, 배타 제어를 대신하지 않습니다.

「사고」와 「공격」은 별개 문제

여기까지의 3단계가 막는 것은 사고입니다. 오염에 의한 오독, 분할 QR 읽기 누락, 문자 깨짐, 옛 라벨 혼입 ── 악의 없는 오류는 이것으로 멈춥니다.

한편 바꿔 쓸 동기가 있는 용도(가격표, 쿠폰, 입장권, 결제)에서는 방어가 되지 않습니다. 공격자는 형식을 충족하고, check digit을 다시 계산하고, 실재하는 다른 번호를 가리키는 QR을 자유롭게 만들 수 있습니다. master 조회는 존재만 보므로 그냥 통과합니다.

QR 소지가 가치나 권한을 뜻한다면, 값 자체에 authenticity를 갖게 할 필요가 있습니다. 서버가 발행하는 추측 불가능한 토큰(충분한 길이의 난수)으로 만들어 번호에서 번호를 추측하지 못하게 하거나, payload에 키가 있는 MAC이나 전자 서명을 붙여 받는 쪽이 키로 검증하게 합니다. 어느 쪽이든 사용 완료 상태를 서버 쪽에서 관리해 복제의 이중 사용을 막습니다.

다만 authenticity만으로는 「바꿔 붙이기」는 멈추지 않습니다. 싼 상품에서 정규 QR을 떼어 비싼 상품에 붙이면, 토큰도 서명도 진짜인 채로입니다. 바꿔 붙일 수 있는 가격표처럼 매체가 재사용되는 장면에서는 사용 완료 판정도 듣지 않습니다. 여기에서도 통하는 것은 3단계와 같은 발상으로, 그 QR이 눈앞 대상의 것인지를 값과는 다른 경로로 확인하는 것입니다 ── 상품 쪽 식별을 다른 수단으로 취해 맞추기, 거래 맥락(POS 명세, 입장 시간대)과 조회하기, 떼면 찢어지는 라벨로 물리적으로 묶기 같은 방법이 있습니다.

check digit도 master 조회도 authenticity에 대해서는 아무것도 보장하지 않습니다. 그리고 authenticity 자체도 그 값이 눈앞 대상의 것임까지는 보장하지 않습니다. 「오독 대책」「위조 대책」「바꿔 붙이기 대책」을 같은 장치로 메우려고 하지 않는 것이 핵심입니다.

7. 운영 쪽에서 정해 둘 것

코드만으로는 막지 못하는 부분이 있습니다.

  • QR 아래에 사람이 읽을 수 있는 문자열을 반드시 병기한다. GS1이 정하는 HRI(Human Readable Interpretation) 생각과 같습니다.4 오독이 의심될 때 사람이 맞출 수단이 남습니다. 실제 운영에서는 이것이 유일한 발견 수단이 되는 일도 드물지 않습니다. 바코드 전반의 현장 운영은 GS1 바코드 규격의 기본과 현장 운영 주의점에 정리되어 있습니다.
  • 「읽을 수 없음」일 때의 절차를 정해 둔다. 재스캔 횟수 상한, 수기 입력으로의 fallback, 그 승인자. 여기가 모호하면 현장은 「읽을 때까지 각도를 바꿔 여러 번 시험한다」는, 오독 확률을 올리는 쪽으로 움직입니다.
  • 걸러 낸 값을 로그에 남긴다. 특정 라벨이나 단말에 치우쳐 있으면 인쇄기나 스캐너 이상을 일찍 발견할 수 있습니다. 다만 생 문자열을 행 지향 로그에 그대로 쓰지 마십시오. 개행이나 제어 문자를 포함한 값은 로그 행을 위장하거나 표시를 깨뜨립니다. 원본은 길이를 잘라 이스케이프해 구조화 로그 필드나 데이터베이스 열에 저장하고, 사람이 읽는 행에는 무해화한 표현만 낸다 ── 이 분리를 처음부터 넣어둡니다. 가능하면 읽기 이미지도 남깁니다.
  • 분할 QR을 쓰지 않는다. 데이터가 다 들어가지 않으면 버전을 올리거나, QR에는 식별자만 넣고 나머지는 master에서 가져옵니다. 후자는 라벨을 작게 할 수 있고, 내용 정정을 라벨 재발행 없이 할 수 있는 이점도 있습니다.
  • 문자 코드는 「넣지 않는 것」으로 해결한다. 업무용 QR은 ASCII 범위에 넣습니다. 바이트 모드는 ECI 지정이 없으면 문자 코드를 선언하지 않은 것이며, 기본 해석은 규격 판에 따라 바뀌어 왔습니다.3 UTF-8로 쓰기만 해서는 상호운용성이 나오지 않고, 다른 해석을 하는 스캐너에서는 문자가 깨집니다. 꼭 비ASCII를 넣으려면 UTF-8을 ECI 지정과 함께 선언하는 것이 규격상 정답이지만, 5.2절처럼 ECI 취급이 불확실한 구현도 현실에 있으므로, 대상 기종에서 실제 기기로 확인하는 일은 피할 수 없습니다.
  • 되돌릴 수 없는 조작 앞에 확인을 끼운다. 출하 확정·재고 차감·입금 반제처럼 취소가 무거운 조작에서는, 읽은 값에서 끌어 온 품명이나 금액을 화면에 내 사람에게 보여 준다. 오독은 값으로서는 타당해 보여도 업무 맥락에서는 부자연스럽게 보이는 경우가 많기 때문입니다.

검증을 어디까지 할지는 틀렸을 때의 손해로 정해집니다.

용도 형식 검사 check digit master 조회 사람에 의한 확인
사내 장소·선반 번호 필수 임의 권장 불필요
입출고·재고 조사 필수 권장 필수 불필요
출하 확정·재고 차감 필수 필수 필수 권장
청구·입금 반제 필수 필수 필수 필수
의약품·위험물 뒤바꿈 방지 필수 필수 필수 필수

8. 정리

QR 코드의 오류 정정은 인쇄된 패턴에서 원래 codeword를 복원하기 위한 장치입니다. 이 일은 제대로 해냅니다. 실측에서도 무작위 오염이나 균일한 화질 열화에 대해서는, 정정할 수 있는 범위에서는 올바르게 읽고, 할 수 없는 범위에서는 그대로 읽기를 포기했습니다.

그러나 그것은 앱이 받은 문자열이 업무적으로 맞다는 것과는 다른 이야기입니다. 손상이 걸리는 곳에 따라서는 정정이 동작한 결과로 다른 타당한 값이 나옵니다. 분할 QR이나 문자 코드, 화면 안의 다른 코드에 이르러서는 오류 정정 바깥의 문제이며, 확률조차 아닙니다.

규격 자신이 「분명히 타당하지만 다른 codeword로 오복호한다」고 쓰고, 오독 방지용 codeword를 일부러 확보하고 있는 것이 이 구조를 단적으로 보여 줍니다. 그렇게까지 해도 확률을 낮출 수 있을 뿐 ── 그것이 규격의 도달점입니다. 나머지를 메울 수 있는 것은 값을 받은 애플리케이션뿐입니다.

읽힌 값은 외부에서 온 검증되지 않은 입력입니다. 키보드에서 친 문자열과 같게 다룬다 ── 그것이 QR 코드와의 올바른 관계라고 생각합니다.

자신의 QR을 시험하는 3단계

여기까지의 이야기는 자사 라벨로 그대로 확인할 수 있습니다.

  1. 생성한다. 실제로 운영 중인 서식의 값을 하나, 손안의 QR 생성 도구로 QR로 만듭니다(이 기사의 샘플은 segno 1.6.6으로 생성했습니다). 우선 상처 없는 상태에서 읽히는지를 확인합니다.
  2. 상처를 낸다. 이미지 편집 소프트웨어로 접힌 자국이나 인쇄 헤드 막힘에 빗댄 세로 한 줄의 띠를 긋습니다. 중요한 것은 양이 아니라 치우침이므로, 전체에 얇게 노이즈를 뿌리지 말고 좁은 범위에 모으십시오.
  3. decoder 2개로 비교한다. QR 코드 읽기 비교 도구에 읽혀 jsQR과 OpenCV.js 결과를 비교합니다. 한쪽만 값을 반환한다, 둘 다 같은 값을 반환하는데 원래 값과 다르다, 같은 종류의 동작을 그 자리에서 확인할 수 있습니다.

결과가 decoder 2개에서 갈리면, 그 조건은 자사 검증 설계에서 반드시 돌봐야 할 조건입니다. 「우리 현장 스캐너는 괜찮은가」를 책상 위에서 한 번 시험해 둘 가치가 있습니다.


검증 환경

오류 정정 동작을 보는 제2〜4절은 버전 1-M(21×21 모듈)으로 통일합니다. 제5절 샘플은 데이터양에 따라 버전이 바뀌므로 절마다 적습니다.

항목 내용
decoder 1 OpenCV 5.0.0 cv2.QRCodeDetector
decoder 2 jsQR 1.4.0(Node.js 22)
생성 segno 1.6.6 / Python 3.11
제2〜4절 심볼 버전 1-M / 21×21(26 codeword = 데이터 16 + 오류 정정 10)
5.1절(분할 QR) 3장 모두 버전 1-M / 21×21
5.2절(문자 코드와 ECI) Shift_JIS는 버전 2-Q, UTF-8은 버전 2-M(모두 25×25). 일본어 18〜23바이트가 버전 1-M의 데이터 16 codeword에 들어가지 않기 때문
5.3절(여러 코드) NO:ITEM:는 버전 1-M, LOT:AB-77은 데이터가 짧아 segno가 오류 정정 레벨을 올리므로 버전 1-H(모두 21×21)
  1. 株式会社デンソーウェーブ, 誤り訂正機能について|QRコードドットコム 

  2. ISO/IEC 18004 Information technology — Automatic identification and data capture techniques — QR code bar code symbology specification. 오류 정정 능력 식 e + 2t ≦ d - p, 오독 방지용 codeword p의 값, 그리고 「분명히 타당하지만 다른 codeword로 오복호한다」는 기술은 8.5.1 Error correction capacity에, 버전 1-M의 (26,16,4)와 각주 「오독 확률을 낮추기 위해 정정 능력을 오류 정정 codeword 수의 절반 미만으로 한다」는 Table 13에 있습니다(인용은 2000년판에 근거). 현행판은 ISO/IEC 18004:2024 2 3 4

  3. 바이트 모드에서 ECI 지정이 없을 때의 기본 해석은 규격 판에 따라 바뀌어 왔습니다. ISO/IEC 18004:2000의 8.3.1은 「The default interpretation for QR Code is ECI 000020 representing the JIS8 and Shift JIS character sets」로 정하고 있었지만, 2006년판(QR Code 2005) 이후는 ECI 000003, 즉 ISO/IEC 8859-1이 기본입니다. 선언되지 않은 기본에 의존하면 규격 판이 바뀌는 것만으로 해석이 바뀔 수 있다는 뜻이기도 합니다.  2

  4. GS1, GS1 General Specifications 

같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.

이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.

이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.

자주 묻는 질문

이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.

오류 정정 레벨을 H로 올리면 오독을 막을 수 있습니까?
막을 수 없습니다. 레벨을 올리면 정정할 수 있는 오염의 양은 늘지만, 「정정이 동작해 다른 값이 되는」 현상 자체는 없어지지 않습니다. Reed-Solomon 복호는 「받은 패턴에서 정정 능력 안쪽에 codeword가 있는지 찾는」 동작이므로, 손상이 다른 codeword의 근방에 들어가면 그쪽을 정답으로 반환합니다(멀리 벗어난 손상은 「읽을 수 없음」으로 끝납니다). ISO/IEC 18004는 이를 「분명히 타당하지만 다른 codeword로 오복호한다」고 명시하고, 오독 방지용 codeword를 별도로 확보하고 있지만, 이것도 확률을 낮추는 조치이지 보장은 아닙니다. 오독을 잡을 수 있는 것은 값을 받은 앱 쪽뿐입니다.
실제로 QR 오독은 어느 정도 일어납니까?
무작위 오염이라면 거의 일어나지 않습니다. 이 기사의 실측에서는 모듈을 무작위로 반전한 3,900회와 codeword를 무작위로 깨뜨린 5,800회 모두에서, 잘못된 값이 반환된 사례는 0건이었습니다. 정정하지 못하는 손상은 「읽을 수 없음」이 됩니다. 다만 현실의 손상은 무작위가 아니라, 접힌 자국·마찰·인쇄 헤드 막힘처럼 위치에 치우칩니다. 게다가 분할 QR의 읽기 누락이나 여러 코드를 뒤바꾸는 일은 확률의 문제조차 아니며, 조건이 맞으면 매번 일어납니다.
QR에는 오류 정정이 있는데, check digit도 필요합니까?
필요합니다. 지키는 계층이 다릅니다. 오류 정정이 다루는 것은 심볼 내부의 일관성으로, 복원을 보장하는 범위도 정정 능력 안쪽까지입니다. 그 범위를 넘는 손상에서는 다른 타당한 codeword로 「복원」해 버릴 수 있습니다. check digit 쪽은 「앱이 받은 문자열이 코드 체계로서 성립하는지」를 검사합니다. 1글자 변화는 확실히 잡히지만, 여러 글자가 바뀐 경우에는 놓치는 경우가 있습니다. modulus 10 검사값은 10가지뿐이며, 이 기사에서도 2자리가 바뀌었는데 check digit이 일치하는 예를 듭니다. 그래서 check digit은 오독의 대부분을 막는 계층으로 보고, 최종 판단은 master 조회와 업무적 대조에 맡기십시오. 수기 입력·전사·다른 경로에서의 반입도 같은 검사로 지킬 수 있는 점은 이점입니다.
스마트폰 카메라 앱에서 읽혔으니 값은 맞는 것 아닙니까?
「읽혔다」가 의미하는 것은 「decoder가 비어 있지 않은 문자열을 반환했다」는 것뿐이며, 그 내용이 맞는지는 아무것도 말하지 않습니다. 실패를 반환하는 방식도 구현마다 달라 예외·빈 문자열·null이 모두 가능하므로, 예외가 나오지 않았다는 것을 성공 판정에 쓰는 것도 위험합니다. 이 기사의 실측에서는 같은 이미지에 대해 OpenCV와 jsQR이 다른 결과를 반환한 예가 여러 건 있었습니다. Shift_JIS로 일본어를 넣은 QR을, 한쪽은 깨진 문자열을 성공으로 반환하고 다른 한쪽은 빈 문자열을 반환합니다. 분할 QR의 첫 장에서도 결과가 갈렸습니다. 읽혔는지 여부는 값의 정확성에 대해 아무것도 말하지 않습니다.
분할 QR(Structured Append)은 업무에서 쓰지 않는 편이 낫습니까?
특별한 이유가 없다면 피하는 편이 무난합니다. 분할 QR은 여러 심볼로 나눈 데이터를 읽는 쪽이 모아 이어 붙이는 장치이지만, 대응하지 않는 decoder에 첫 장만 읽혔을 때의 동작은 구현에 달립니다. 이 기사의 실측에서는 OpenCV가 끝이 잘린 전표 번호를 오류 없이 반환한 반면, jsQR은 빈 문자열을 반환했습니다. 조각을 그럴듯한 값으로 통과시키는 구현이 있는 이상, 분할 QR을 가정하지 않는다면 불완전한 결과를 걸러 내는 검증이 필요합니다. 데이터가 다 들어가지 않으면 QR 버전을 올리거나, 코드를 짧게 해 master 참조로 모으는 편이 안전합니다.

저자 프로필

기사 저자의 프로필 페이지입니다.

Go Komura

합동회사 코무라소프트 대표

Windows 소프트웨어 개발, 기술 상담, 장애 조사를 중심으로 재현이 어려운 장애 조사와 기존 자산이 남아 있는 프로젝트에 강점이 있습니다.

블로그 목록으로 돌아가기