심볼로지·정규식 필터링 — SCANDIT SDK 5계층 필터링 가이드
최근 업데이트: 2026-06-10
한줄 요약
SCANDIT SDK의 필터링은 단일 스위치가 아니라 계층 구조입니다. 심볼로지 활성화(1단), Active Symbol Counts(2단), 복합 코드 설정(3단)은 SDK 설정 레벨의 사전 필터이고, 앱 콜백 정규식(4단)은 모든 플랫폼과 제품에서 쓸 수 있는 범용 필터입니다. BarcodeFilterSettings는 BarcodeCapture가 아니라 BarcodeCount(MatrixScan Count) 전용 설정(5단)이며, MatrixScan AR에는 setBarcodeFilter()(8.1+)가 따로 있습니다. 한국 물류·의약품 현장에서 자주 쓰는 정규식 패턴과 각 계층의 선택 기준을 정리합니다.
TL;DR
- SCANDIT SDK 필터링은 5계층 구조다: 심볼로지 활성화(1단) → Active Symbol Counts(2단) → 복합 코드 설정(3단) → 앱 콜백 정규식(4단) → 제품별 전용 필터(5단).
- 파이프라인 앞단(1·2단)에서 차단할수록 배터리·CPU 비용이 낮다. 설정 변경은
applySettings()로 세션 재시작 없이 런타임에 반영된다. BarcodeFilterSettings는 BarcodeCapture 기능이 아니다. BarcodeCount(MatrixScan Count) 전용이며BarcodeCountSettings.filterSettings로 적용하고, 뷰 표시는BarcodeCountView+BarcodeFilterHighlightSettings가 담당한다.- 한국 물류·의약품 현장에서 자주 쓰는 패턴: 880 접두사 EAN-13, 12자리 송장번호, GS1 DataMatrix AI(01) 포맷(원시 데이터에는 괄호 없음).
Scandit 필터링 5계층 — 의사결정 트리
SCANDIT SDK는 바코드 인식 파이프라인 전반에 걸쳐 다섯 가지 필터링 지점을 제공합니다. 이 계층 구조를 이해하면 어디서 차단하는 것이 가장 효율적인지, 어떤 상황에서 유연성이 필요한지 빠르게 판단할 수 있습니다.
1단: Symbology Enable/Disable — 인식 시작 전 심볼로지 선택
SymbologySettings.isEnabled를 통해 특정 심볼로지를 활성화하거나 비활성화합니다. SCANDIT SDK는 여러 심볼로지를 동시에 인식할 수 있지만, 현장에서 필요하지 않은 심볼로지가 켜져 있으면 불필요한 연산이 생깁니다. EAN-13만 처리하는 리테일 POS라면 EAN-13만 켜는 것이 기본입니다.
1단은 프레임 처리 파이프라인 최상단에서 동작합니다. 비활성화된 심볼로지에 대해서는 디코딩 시도 자체가 발생하지 않으므로 배터리와 CPU 부담이 전 계층 중 가장 작습니다.
SDK가 지원하는 심볼로지는 수십 가지에 달하지만, 모든 심볼로지는 기본 비활성화 상태입니다. QR코드든 EAN-13이든 필요한 심볼로지는 반드시 명시적으로 켜야 합니다. 기본값이 전부 꺼져 있으므로 화이트리스트 방식이 자연스럽게 강제됩니다 — 프로젝트 초기에 필요한 심볼로지 목록을 정리하고 그것만 활성화하면 됩니다. 심볼로지를 추가로 켤수록 처리 시간이 늘어나므로, 필요한 최소 집합만 유지하는 것이 인식 속도와 정확도 모두에 유리합니다.
iOS Swift에서는 settings.set(symbology: .ean13UPCA, enabled: true), Android Kotlin에서는 settings.enableSymbology(Symbology.EAN13_UPCA, true) 형태입니다. 여러 개를 한 번에 켜려면 enableSymbologies()를 씁니다. Web TypeScript와 React Native도 같은 패턴이며, 플랫폼별 구현 예시는 이 페이지 상단의 코드 샘플 탭에서 확인할 수 있습니다.
2단: Active Symbol Counts — 자릿수 범위 사전 제한
SymbologySettings.activeSymbolCounts는 특정 심볼로지 내에서 처리할 데이터 길이(자릿수)를 지정합니다. Code 39를 활성화했더라도 현장에서 6자리 코드만 유효하다면 activeSymbolCounts = Set([6])으로 설정해 그 외 길이의 코드는 인식 도중에 폐기합니다.
EAN-13은 항상 13자리로 고정이라 이 설정이 의미 없지만, Code 128·Code 39·Code 93처럼 가변 길이를 허용하는 심볼로지에서 특히 유용합니다. Active Symbol Counts는 SymbologySettings 객체 안에 있으므로 심볼로지를 활성화하는 시점에 함께 설정합니다.
iOS Swift — Code 39, 8자리만 허용
let symSettings = settings.settings(for: .code39)
symSettings.activeSymbolCounts = Set([8])
Android Kotlin — Code 39, 8자리만 허용
val symSettings = settings.getSymbologySettings(Symbology.CODE39)
symSettings.activeSymbolCounts = setOf(8)
자릿수 범위를 넓게 설정할수록 오인식 가능성이 높아집니다. 현장에서 사용하는 코드 길이가 명확하다면 최소 범위만 허용하는 것을 권장합니다. 현장 테스트 단계에서 실제 코드 샘플을 수집해 자릿수 분포를 확인한 뒤 범위를 결정하면 이후 운영 중 예외 발생을 크게 줄일 수 있습니다.
3단: Composite Types — 1D+2D 합성 코드 처리
GS1 복합 코드(Composite Code)는 1D 선형 심볼로지(EAN-13, Code 128 등)와 2D 컴포넌트(CC-A, CC-B)가 수직으로 결합된 형태입니다. 국내 의약품 라벨과 GS1-128 의약품 표준코드 일부가 이 포맷을 사용합니다.
복합 코드를 인식하려면 BarcodeCaptureSettings의 enabledCompositeTypes 속성을 통해 CompositeType.a, CompositeType.b, CompositeType.c 중 필요한 유형을 명시적으로 활성화해야 합니다. 단순히 EAN-13을 켠 것만으로는 복합 컴포넌트를 처리하지 않습니다. SDK 7부터는 복합 유형을 구성하는 1D·2D 심볼로지도 함께 활성화되어야 하며, enableSymbologiesForCompositeTypes()를 호출하면 해당 복합 유형에 필요한 구성 심볼로지를 한 번에 켤 수 있습니다. CC-A는 최대 56자리, CC-B는 최대 338자리 데이터를 담을 수 있으며, 국내 의약품 표준코드에서는 주로 CC-A와 CC-B가 사용됩니다.
복합 코드를 불필요하게 활성화하면 인식 속도가 떨어집니다. GS1 의약품 라인처럼 복합 코드가 명확히 필요한 환경에서만 해당 CompositeType을 켜는 것이 원칙입니다. 복합 코드 필요 여부가 불확실하다면 PoC 단계에서 실제 라벨 샘플로 테스트하고, 비활성화 상태에서도 필요한 데이터가 정상 인식되는지 먼저 확인하는 것이 좋습니다.
4단: 앱 콜백 필터링 — 정규식·비즈니스 로직 검증
네 번째 계층은 SDK 설정이 아니라 앱 코드입니다. BarcodeCaptureListener 콜백(iOS: barcodeCapture(_:didScanIn:frameData:), Android: onBarcodeScanned, Web·React Native: didScan)에서 전달받은 BarcodeCaptureSession의 인식 결과를 검증하고, 비즈니스 규칙에 맞지 않는 결과를 버리는 단계입니다. 이 방식은 모든 플랫폼, 모든 제품(BarcodeCapture·SparkScan·MatrixScan 계열)에서 동일하게 동작하는 범용 필터입니다.
인식 결과는 session.newlyRecognizedBarcode로 읽습니다. 단수형이며 nullable입니다 — 복수형 newlyRecognizedBarcodes는 v6 시절 API로, v7부터 단수형으로 바뀌었습니다. 마이그레이션 코드에서 가장 자주 걸리는 지점이니 주의해야 합니다.
Android에서 한 가지 더 주의할 점: BarcodeCaptureListener는 onBarcodeScanned 외에 onSessionUpdated, onObservationStarted, onObservationStopped까지 포함한 메서드 4개짜리 인터페이스입니다. 메서드가 하나가 아니므로 SAM 변환 람다로 만들 수 없고, 클래스(또는 object 식)로 구현해야 합니다.
Android Kotlin — 콜백 내 비즈니스 로직 정규식 검증 (4단) 예시
출처: Scandit Docs — Barcode Capture Get Started (Android)
// Android Kotlin — BarcodeCaptureListener 콜백에서 4단 정규식 검증
class BadgeScanListener : BarcodeCaptureListener {
// 사번 형식: 영문 2자리 + 숫자 6자리 (예: DC123456)
private val employeeIdRegex = Regex("^[A-Z]{2}\\d{6}$")
override fun onBarcodeScanned(
barcodeCapture: BarcodeCapture,
session: BarcodeCaptureSession,
data: FrameData
) {
// newlyRecognizedBarcode는 단수·nullable — 복수형은 v6 API
val value = session.newlyRecognizedBarcode?.data ?: return
if (!employeeIdRegex.matches(value)) {
// 형식 불일치 — UI 알림 또는 재스캔 안내 (콜백은 백그라운드 스레드)
return
}
// 형식 일치 — 비즈니스 로직 처리
processEmployeeBadge(value)
}
override fun onSessionUpdated(barcodeCapture: BarcodeCapture, session: BarcodeCaptureSession, data: FrameData) {}
override fun onObservationStarted(barcodeCapture: BarcodeCapture) {}
override fun onObservationStopped(barcodeCapture: BarcodeCapture) {}
}
4단은 SDK의 인식 비용을 모두 지불한 뒤 남은 결과에 대한 관문입니다. 이 단계에서 걸러내는 비율이 높다면 1·2단으로 로직을 올리는 리팩토링을 검토해야 합니다. 콜백까지 도달하는 코드가 줄어들수록 앱 응답성이 올라갑니다.
5단: 제품별 전용 필터 — BarcodeCount·MatrixScan AR
마지막 계층은 특정 제품에만 존재하는 전용 필터입니다. 가장 자주 혼동되는 부분이므로 명확히 해 두겠습니다. BarcodeFilterSettings는 BarcodeCapture의 기능이 아닙니다. BarcodeCaptureSettings에는 filterSettings 속성이 없으며, 이 클래스는 BarcodeCount(MatrixScan Count)에서 BarcodeCountSettings.filterSettings를 통해 사용합니다.
BarcodeFilterSettings가 제공하는 제외 조건은 다음과 같습니다.
excludedSymbologies— 지정한 심볼로지의 인식 결과를 카운트에서 제외합니다.excludedCodesRegex— 정규식 패턴 문자열에 일치하는 데이터를 가진 코드를 제외합니다. 문자열 패턴이며, Web에서도 JavaScriptRegExp객체가 아니라 문자열을 지정합니다.excludedSymbolCounts— 심볼로지별로 제외할 자릿수를 지정하는 딕셔너리(맵) 구조입니다.excludeEan13/excludeUpca— EAN-13·UPC-A를 통째로 제외하는 전용 플래그입니다.
필터에 걸린 바코드의 효과는 뷰 레벨에서 나타납니다. BarcodeCountView에 BarcodeFilterHighlightSettings(구현체: BarcodeFilterHighlightSettingsBrush)를 지정하면 필터된 바코드 위에 지정한 브러시 색상의 커버가 표시되어, 작업자가 "이 코드는 카운트되지 않음"을 화면에서 바로 확인할 수 있습니다. 사용자가 필터된 바코드를 탭하면 BarcodeCountViewListener.didTapFilteredBarcode 콜백으로 알 수 있습니다.
Web TypeScript — BarcodeCount(MatrixScan Count) 필터 설정
출처: BarcodeFilterSettings API (Web)
// BarcodeCount(MatrixScan Count) 전용 — BarcodeCapture에는 없는 설정
const countSettings = new BarcodeCountSettings();
countSettings.enableSymbologies([Symbology.EAN13UPCA, Symbology.Code128]);
// 880 접두사 코드를 카운트에서 제외 — 문자열 패턴 (JS RegExp 아님)
countSettings.filterSettings.excludedCodesRegex = "^880\\d{10}$";
// 필터된 바코드의 화면 표시는 BarcodeCountView에
// BarcodeFilterHighlightSettings(Brush)를 지정해 제어합니다
MatrixScan AR에는 별도의 필터 API인 setBarcodeFilter()(SDK 8.1+)가 있습니다. 세션에 표시할 바코드를 필터 객체로 제한하고, null을 전달하면 전체 표시로 돌아갑니다. 이 API는 iOS·Android 네이티브에서 제공되며 Capacitor·Flutter 등 일부 크로스플랫폼 프레임워크에서는 지원되지 않으므로, 해당 환경에서는 리스너 콜백 또는 highlight provider에서 필터링합니다.
사전 필터 vs 앱 콜백 필터 — 언제 무엇을 쓰는가
필터링 계층을 사전(1~3단, SDK 설정)과 콜백(4단, 앱 코드)으로 나누면 의사결정이 명확해집니다.
사전 필터 (1단·2단) 는 카메라 프레임 처리 파이프라인 초입에서 동작합니다. 심볼로지 비활성화는 해당 심볼로지의 디코딩 시도 자체를 차단하므로, 처리하지 않을 코드에 대한 연산이 전혀 발생하지 않습니다. 배터리 민감도가 높은 모바일 현장 기기나 저사양 Android 단말기에서 인식 속도와 배터리 수명에 직접적인 영향을 미칩니다. 특히 물류 창고처럼 하루 수천 건 이상 스캔하는 환경에서는 사전 필터만 제대로 설계해도 배터리 소모를 눈에 띄게 줄일 수 있습니다.
사전 필터가 적합한 상황은 다음과 같습니다.
- 스캔 대상 심볼로지가 처음부터 명확한 경우 (예: 리테일 POS에서 EAN-13만 처리)
- 자릿수처럼 디코딩 단계에서 판정 가능한 조건인 경우
- 배터리·CPU 최적화가 최우선인 경우
앱 콜백 필터 (4단) 는 디코딩이 완료된 이후에 동작합니다. 인식 비용은 발생하지만, 정규식·접두사·체크섬·DB 조회까지 어떤 조건이든 코드로 표현할 수 있다는 장점이 있습니다.
콜백 필터가 적합한 상황은 다음과 같습니다.
- 데이터 내용(패턴·접두사·형식)으로 판정해야 하는 경우
- 형식 검증 실패 시 재스캔 안내 등 UI 분기가 필요한 경우
- 외부 시스템 조회 결과에 따라 수용 여부가 달라지는 경우
한 가지 흔한 오해를 짚고 가겠습니다. "사전 필터는 세션 중 고정"이 아닙니다. 심볼로지 구성을 포함한 모든 BarcodeCaptureSettings 변경은 새 설정 객체를 만들어 barcodeCapture.applySettings(newSettings)를 호출하면 세션 재시작 없이 런타임에 반영됩니다. 작업 모드에 따라 허용 심볼로지를 바꿔야 하는 멀티 모드 앱도 이 패턴으로 충분합니다.
실무 원칙은 간단합니다. 사전 필터로 최대한 좁히고, 데이터 내용 기반 판정만 콜백으로 보완합니다. 두 계층은 배타적이지 않으며 함께 쓰는 것이 일반적입니다.
Regex 필터링 — 한국 도메인 패턴
현장에서 반복적으로 등장하는 한국 도메인 정규식 패턴을 정리합니다. 이 패턴들은 4단(앱 콜백)에서 허용 검증으로 쓰거나, BarcodeCount 환경이라면 5단(excludedCodesRegex)에서 제외 조건으로 씁니다. 주의할 점: excludedCodesRegex는 일치하는 코드를 제외하는 설정입니다. "일치하는 것만 남기기"가 목적이라면 콜백에서 허용 검증을 하거나, 부정형 선행 탐색(negative lookahead)으로 패턴을 뒤집어야 합니다.
한국 EAN-13 국산 제품 접두사 (880)
GS1 한국(GS1 Korea)이 발급하는 국가 코드는 880입니다. 국내에서 생산된 제품의 EAN-13은 이 접두사로 시작합니다.
^880\d{10}$
총 13자리이며 880 이후 10자리는 GS1 멤버사 코드(5~7자리)·상품 코드·체크 디지트로 구성됩니다. 국산 제품만 처리해야 하는 유통 시스템이라면 콜백 단에서 이 정규식으로 검증합니다.
iOS Swift + Android Kotlin — 사전 필터(1단) + 콜백 필터(4단) 조합 예시
출처: Scandit Docs — Barcode Capture Get Started (iOS) · SymbologySettings API (iOS)
// iOS Swift — 1단: EAN-13만 활성화
let settings = BarcodeCaptureSettings()
settings.set(symbology: .ean13UPCA, enabled: true)
// 4단: didScanIn 콜백에서 880 접두사만 허용
extension ScanViewController: BarcodeCaptureListener {
func barcodeCapture(_ barcodeCapture: BarcodeCapture,
didScanIn session: BarcodeCaptureSession,
frameData: FrameData) {
guard let data = session.newlyRecognizedBarcode?.data,
data.range(of: "^880\\d{10}$", options: .regularExpression) != nil
else { return }
// 국산 EAN-13 — 처리
}
}
// Android Kotlin — 1단: EAN-13만 활성화
val settings = BarcodeCaptureSettings().apply {
enableSymbology(Symbology.EAN13_UPCA, true)
}
// 4단: onBarcodeScanned 콜백에서 880 접두사만 허용
class KrOnlyListener : BarcodeCaptureListener {
private val krRegex = Regex("^880\\d{10}$")
override fun onBarcodeScanned(
barcodeCapture: BarcodeCapture,
session: BarcodeCaptureSession,
data: FrameData
) {
val value = session.newlyRecognizedBarcode?.data ?: return
if (krRegex.matches(value)) {
// 국산 EAN-13 — 처리
}
}
override fun onSessionUpdated(barcodeCapture: BarcodeCapture, session: BarcodeCaptureSession, data: FrameData) {}
override fun onObservationStarted(barcodeCapture: BarcodeCapture) {}
override fun onObservationStopped(barcodeCapture: BarcodeCapture) {}
}
국내 대형 택배사 송장번호
국내 대형 택배사의 송장번호는 12자리 숫자로 구성됩니다. 심볼로지는 Code 128 또는 GS1-128 형태로 인쇄되는 것이 일반적입니다.
^\d{12}$
물류 창고 현장에서 송장 바코드만 처리하고 박스에 부착된 다른 상품 코드를 무시하려면 Code 128을 활성화한 뒤 이 정규식을 콜백 검증에 적용합니다. 더 정교하게는 특정 물류사 접두사를 추가해 ^63\d{10}$ 형태로 좁히는 것도 가능합니다. 물류사마다 접두사 규칙이 다르므로 현장 샘플을 바탕으로 패턴을 조정하는 것이 중요합니다.
사번·직원 ID 형식
제조 현장이나 입출고 관리 앱에서 사원 카드 QR코드 또는 바코드를 스캔하는 경우, 사번 형식이 고정되어 있다면 정규식으로 검증합니다.
^[A-Z]{2}\d{6}$
영문 2자리 + 숫자 6자리(예: DC123456) 형식의 사번을 검증하는 예시입니다. 실제 형식은 조직마다 다르므로 현장 규칙을 반영해 조정합니다. 사번 형식이 복수인 경우(예: 정규직과 협력사 구분)에는 ^([A-Z]{2}\d{6}|[A-Z]{3}\d{5})$ 처럼 교대 패턴으로 묶을 수 있습니다.
의약품 GS1 DataMatrix — AI(01) GTIN
식약처 의약품 표준코드에서 GS1 DataMatrix를 사용하는 경우, 애플리케이션 식별자(AI) 01로 시작하는 GTIN-14 구조를 포함합니다. 정규식으로 기초 검증할 때는 다음 패턴을 참고합니다.
^01\d{14}
주의할 점이 있습니다. 라벨에 사람이 읽도록 인쇄된 (01)0880… 같은 괄호 표기는 HRI(Human Readable Interpretation)일 뿐, 스캔된 원시 데이터에는 괄호가 없습니다. 원시 데이터는 01 두 자리 AI 뒤에 14자리 GTIN이 바로 이어집니다. ^\(01\) 같은 패턴으로 검증하면 정상 코드도 전부 불일치 처리되므로 반드시 괄호 없는 패턴을 써야 합니다. 실제 데이터에는 AI(01) 뒤에 AI(17)(유효기간), AI(10)(배치번호), AI(21)(일련번호) 등이 이어질 수 있으며, 가변 길이 AI의 FNC1 구분자 처리까지 포함한 전체 파싱은 GS1 파서 라이브러리에 맡기는 분업 구조를 권장합니다. 정규식은 기초 포맷 확인 용도로만 씁니다.
한국 KAN 코드
KAN(Korean Article Number)은 EAN-13의 한국 로컬 명칭입니다. 구조는 EAN-13과 동일하며 880 접두사 패턴이 그대로 적용됩니다. 내부 시스템에서 KAN 용어를 사용하는 경우에도 SDK 레벨 심볼로지는 EAN-13으로 처리합니다. 명칭 혼용으로 인한 혼선을 피하려면 팀 내 문서에 "KAN = EAN-13 심볼로지, 880 접두사 검증"으로 명시해 두는 것이 좋습니다.
Smart Duplicate Filter vs 패턴 필터
같은 결과를 "무시"하는 것처럼 보이지만 전혀 다른 두 기능을 구분해 보겠습니다.
Smart Duplicate Filter는 동일한 바코드가 짧은 시간 내에 반복 인식될 때 중복 콜백을 억제하는 기능입니다. 카메라 프레임은 초당 여러 번 처리되므로, 사용자가 바코드 하나를 스캔하면 같은 코드가 수십 프레임에 걸쳐 인식됩니다. SDK 7.1에서 도입되었고 8.1부터는 기본 동작이며, codeDuplicateFilter 값을 -2로 지정해 명시적으로 활성화할 수 있습니다. 고정 시간 창 대신 SDK가 사용자의 스캔 행동을 바탕으로 억제 기간을 동적으로 조정합니다. 참고로 codeDuplicateFilter는 밀리초 단위 시간 창(500 등), 0(모든 인식 보고), -1(스캔 중지 전까지 코드당 1회)도 지원합니다.
Web TypeScript — Smart Duplicate Filter와 scanIntention 설정
출처: Scandit Docs — Barcode Capture Advanced (Web) · AI-Powered Barcode Scanning
import { BarcodeCaptureSettings, ScanIntention, Symbology } from "@scandit/web-datacapture-barcode";
const settings = new BarcodeCaptureSettings();
settings.enableSymbologies([Symbology.EAN13UPCA]);
// Smart Duplicate Filter — 같은 코드의 반복 콜백을 SDK가 지능적으로 억제
// SDK 7.1 도입, 8.1부터 기본 동작. -2로 명시 활성화
settings.codeDuplicateFilter = -2;
// scanIntention은 별개 기능 — 사용자가 어떤 바코드를 의도했는지 추론
// 7.x 기본값은 Smart, 8.1+ 기본값은 SmartSelection
settings.scanIntention = ScanIntention.SmartSelection;
여기서 scanIntention을 함께 언급한 이유는, 이 둘이 자주 혼동되기 때문입니다. scanIntention은 의도 추론 기능입니다. 화면에 바코드가 여러 개 보일 때 사용자가 어느 것을 노리는지 판단해 의도하지 않은 스캔을 줄입니다. 7.x에서는 .smart가 기본값이었고 8.1부터는 후보 여러 개 중 사용자가 가리키는 것을 선택하는 .smartSelection이 기본값입니다. 중복 콜백 억제는 codeDuplicateFilter의 영역이고, 의도 추론은 scanIntention의 영역입니다 — 두 기능을 묶어서 이해하면 디버깅이 어려워집니다.
패턴 필터 (콜백 정규식, BarcodeCount의 excludedCodesRegex)는 시간 개념이 없습니다. 패턴에 일치하는 코드는 세션 내내, 몇 번을 인식하더라도 항상 같은 판정을 받습니다. 특정 코드 패턴 자체를 시스템에서 배제하는 정책적 설정입니다.
| 기준 | Smart Duplicate Filter | 패턴 필터 (콜백 정규식 / excludedCodesRegex) |
|---|---|---|
| 동작 시점 | 동일 코드 반복 인식 시 | 패턴 일치 시 항상 |
| 시간 감수성 | 있음 (동적 억제 후 재인식 허용) | 없음 (항상 동일 판정) |
| 적합한 상황 | 연속 스캔 중 중복 방지 | 특정 패턴 코드 배제 |
| 적용 범위 | BarcodeCapture 등 (7.1+, 8.1부터 기본) | 콜백: 전 제품 / excludedCodesRegex: BarcodeCount 전용 |
결론적으로 두 기능은 서로 보완적입니다. 일반적인 스캔 앱에서는 Smart Duplicate Filter로 중복을 막고, 콜백 정규식으로 도메인에 맞지 않는 코드 패턴을 걸러내는 방식으로 함께 사용합니다.
OCR fallback 정규식
SCANDIT SDK v7.4부터 제공되는 OCR fallback 기능(SymbologySettings.ocrFallbackRegex)은 바코드 인식에 실패했을 때 해당 프레임 영역의 텍스트를 OCR로 읽어 정규식으로 검증하는 방식입니다.
바코드가 물리적으로 손상되었거나 인쇄 품질이 낮아 디코딩에 실패한 경우, OCR로 동일 위치의 숫자나 문자를 읽은 뒤 형식 검증을 통해 "이것이 우리가 찾는 코드일 가능성이 높다"고 판단하면 해당 텍스트를 결과로 반환합니다.
국내 현장에서 이 기능이 도움이 되는 대표적인 상황 두 가지를 들면 다음과 같습니다.
의약품 일련번호 스캔: 약품 포장재에 인쇄된 GS1 DataMatrix나 식약처 표준코드가 유통·보관 과정에서 오염되거나 긁힌 경우, OCR fallback으로 숫자 일련번호를 읽어 약품 관리 시스템에 입력합니다. 바코드 판독 실패로 작업이 중단되는 현장 병목을 줄이는 데 효과적입니다.
노후 단말기 환경: 카메라 해상도가 낮은 구형 Android 단말기에서 미세한 바코드 인식이 불안정한 경우, OCR fallback이 보완책이 됩니다. 하드웨어 교체 전 과도기에 현장 업무 연속성을 유지하는 수단으로 활용합니다.
ocrFallbackRegex에 지정하는 정규식은 기대하는 텍스트 형식을 정밀하게 정의해야 합니다. 너무 느슨하면 오탐이 늘고, 너무 엄격하면 정상 케이스도 놓칩니다. 의약품 일련번호처럼 형식이 표준화된 경우에 OCR fallback을 활용하는 것이 가장 효과적입니다.
ocrFallbackRegex는 SymbologySettings 객체에 심볼로지별로 설정하므로, 특정 심볼로지(예: Code 128)에서만 OCR fallback을 활성화하고 나머지에서는 비활성화하는 세밀한 제어가 가능합니다.
OCR fallback을 활성화할 때는 성능 트레이드오프를 반드시 고려해야 합니다. OCR 처리는 바코드 디코딩보다 연산량이 크므로, 바코드 인식 실패가 자주 발생하는 환경에서 OCR fallback을 전면 활성화하면 전체 처리 속도가 저하될 수 있습니다. 손상된 코드가 산발적으로 등장하는 환경에서 선택적으로 활용하거나, OCR fallback 결과를 별도로 로깅해 실제 활용 빈도를 모니터링한 뒤 유지 여부를 결정하는 것을 권장합니다. 도입 전후의 스캔 성공률과 처리 지연을 비교 측정하는 A/B 테스트 방식이 효과적입니다.
한국 도메인 사례
물류 창고 — 택배 송장 단일 인식
국내 대형 물류 창고 현장에서 입고 처리 앱을 구축한 프로젝트에서, 작업자가 스캔해야 할 코드는 택배 송장(12자리 Code 128)뿐이었지만 박스 외면에는 제조사 EAN-13, 내용물 QR코드, 물류사 내부 코드 등 다양한 바코드가 혼재했습니다. 1단에서 Code 128만 활성화하고, 4단 콜백에서 ^\d{12}$ 정규식으로 추가 검증해 오인식 없는 단일 코드 처리 환경을 구성했습니다. SDK 설정과 콜백 검증 두 계층만으로 목표를 달성할 수 있었던 사례입니다.
이 접근 방식의 핵심은 "필요한 것만 켠다"는 화이트리스트 원칙입니다. Code 128 이외 모든 심볼로지를 꺼 두는 것만으로 잘못된 코드가 콜백에 도달하는 경우가 사실상 사라집니다. 정규식 검증은 송장번호 형식이 유효한지 확인하는 안전망 역할이며, 이 두 계층의 조합이 가장 단순하면서도 효율적인 구성입니다. 결과적으로 불필요한 스캔 이벤트가 줄어들면서 작업자 UX도 개선되었습니다.
의약품 라인 — GS1 DataMatrix 단독 인식
의약품 제조 라인의 품질 관리 시스템에서는 GS1 DataMatrix 코드만 유효하며, 같은 라벨에 EAN-13이 병기되는 경우가 많습니다. 1단에서 DataMatrix만 활성화하고, 4단 콜백에서 ^01\d{14} 패턴(원시 데이터 — 괄호 없음)으로 GS1 AI(01) 구조를 검증한 뒤 GS1 파서로 전체 AI를 파싱하는 구성을 적용했습니다. 인식 정확도와 처리 속도 모두 현장 요건을 충족했습니다.
같은 요건을 MatrixScan Count 기반 입고 검수 화면에서 구현한다면 5단의 BarcodeCountSettings.filterSettings를 활용할 수 있습니다. 이때 excludedCodesRegex는 일치 항목을 제외하는 설정이므로, "AI(01) 형식만 남기기"가 목적이라면 부정형 선행 탐색으로 패턴을 뒤집어 ^(?!01\d{14}).* 형태로 지정하거나, 필터 없이 콜백에서 허용 검증하는 쪽이 읽기 쉽습니다.
의약품 라인의 경우 식약처 컴플라이언스 요건이 있으므로 오인식은 단순한 UX 문제가 아니라 규제 위반으로 이어질 수 있습니다. 따라서 콜백에서 GS1 애플리케이션 식별자(AI) 파싱 결과를 검증하는 추가 레이어를 두어 포맷 확인과 데이터 무결성 검증을 이중으로 수행했습니다. 필터링 설계가 단순한 편의 기능이 아닌 규제 대응 수단이 되는 전형적인 사례입니다.
자주 묻는 질문
이 섹션은 아래 FAQ 항목의 상세 답변입니다. 요약 답변은 페이지 하단 FAQ 아코디언에서 확인할 수 있습니다.
사전 필터와 콜백 필터의 선택 기준에 대해 — 핵심은 "판정 기준이 어디에 있는가"입니다. 심볼로지·자릿수처럼 디코딩 단계에서 판정 가능한 조건은 사전 필터(1단·2단)가 효율적입니다. 데이터 내용(패턴·접두사·형식)으로 판정해야 한다면 콜백(4단)에서 처리합니다. 설정을 바꿔야 할 때는 새 BarcodeCaptureSettings를 만들어 applySettings()를 호출하면 세션 재시작 없이 런타임에 반영되므로, "사전 필터는 고정"이라는 걱정 때문에 콜백으로 미룰 필요는 없습니다.
Active Symbol Counts와 콜백 정규식의 관계 — 이 두 설정은 서로 다른 파이프라인 위치에서 동작하며 역할도 다릅니다. Active Symbol Counts는 심볼로지 인식 단계에서 자릿수 기준으로 조기 폐기하고, 콜백 정규식은 인식이 완료된 결과의 데이터 문자열을 검증합니다. 자릿수는 앞단에서, 패턴은 콜백에서 — 두 설정을 함께 쓰면 더 효율적인 파이프라인을 구성할 수 있습니다.
한국 송장 정규식 관련 — EAN-13 880 접두사 검증(국산 제품)과 12자리 숫자 송장번호(물류 송장)는 심볼로지 자체가 다를 수 있으므로 정규식 전에 심볼로지 분기를 먼저 확인합니다. 콜백에서 barcode.symbology 값을 체크한 뒤 해당 심볼로지에 맞는 정규식을 적용하는 패턴이 오류를 줄입니다.
BarcodeFilterSettings의 소속과 효과 — BarcodeFilterSettings는 BarcodeCount(MatrixScan Count) 전용이며 BarcodeCountSettings.filterSettings로 적용합니다. BarcodeCaptureSettings에는 filterSettings 속성이 없습니다 — 이 코드를 BarcodeCapture에 붙이면 컴파일되지 않습니다. 필터된 바코드는 카운트에서 제외되고, BarcodeCountView에 BarcodeFilterHighlightSettings를 지정하면 별도 색상 커버로 표시됩니다. MatrixScan AR은 setBarcodeFilter()(8.1+)라는 별도 API를 씁니다.
Smart Duplicate Filter와 패턴 필터 선택 — 실시간 스캔 피드에서 같은 코드가 반복 콜백되는 문제라면 Smart Duplicate Filter(codeDuplicateFilter = -2, 7.1+, 8.1부터 기본)가 정답입니다. 특정 포맷의 코드를 시스템적으로 배제해야 한다면 콜백 정규식 또는 (BarcodeCount 한정) excludedCodesRegex를 선택합니다. 두 기능은 해결하는 문제가 달라 함께 써도 충돌하지 않습니다.
최근 업데이트
최근 업데이트: 2026-06-10
이 페이지의 내용은 SCANDIT SDK 8.x 기준으로 작성되었습니다. SDK 버전 업데이트에 따라 API 시그니처나 설정 방법이 변경될 수 있으며, 데이터커넥트는 분기별로 내용을 검토하고 업데이트합니다. SCANDIT SDK 도입 상담 및 현장 PoC 설계가 필요하시면 데이터커넥트 기술팀에 문의하거나 데모를 신청해 주시기 바랍니다.
참조 문서: BarcodeFilterSettings API (Web) · SymbologySettings API (iOS) · Configure Barcode Symbologies · Barcode Capture Get Started (Android) · AI-Powered Barcode Scanning
코드 샘플
EAN-13만 활성화 + didScanIn 콜백에서 880 접두사만 처리 (1단 + 4단 조합)
let settings = BarcodeCaptureSettings()
// 1단: EAN-13만 활성화 — 모든 심볼로지는 기본 비활성화
settings.set(symbology: .ean13UPCA, enabled: true)
// 4단: 콜백에서 880 접두사(국산 제품)만 처리
extension ScanViewController: BarcodeCaptureListener {
func barcodeCapture(_ barcodeCapture: BarcodeCapture,
didScanIn session: BarcodeCaptureSession,
frameData: FrameData) {
guard let barcode = session.newlyRecognizedBarcode,
let data = barcode.data,
data.range(of: "^880\\d{10}$", options: .regularExpression) != nil
else { return }
// 국산 EAN-13 — 비즈니스 로직 처리
}
}
EAN-13만 활성화 + onBarcodeScanned 콜백에서 880 접두사만 처리 (1단 + 4단 조합)
val settings = BarcodeCaptureSettings().apply {
// 1단: EAN-13만 활성화 — 모든 심볼로지는 기본 비활성화
enableSymbology(Symbology.EAN13_UPCA, true)
}
// 4단: 콜백에서 880 접두사(국산 제품)만 처리
// BarcodeCaptureListener는 메서드 4개짜리 인터페이스 — SAM 람다 아님
class ScanListener : BarcodeCaptureListener {
private val krRegex = Regex("^880\\d{10}$")
override fun onBarcodeScanned(
barcodeCapture: BarcodeCapture,
session: BarcodeCaptureSession,
data: FrameData
) {
val value = session.newlyRecognizedBarcode?.data ?: return
if (krRegex.matches(value)) {
// 국산 EAN-13 — 비즈니스 로직 처리 (백그라운드 스레드 — UI 작업은 디스패치)
}
}
override fun onSessionUpdated(barcodeCapture: BarcodeCapture, session: BarcodeCaptureSession, data: FrameData) {}
override fun onObservationStarted(barcodeCapture: BarcodeCapture) {}
override fun onObservationStopped(barcodeCapture: BarcodeCapture) {}
}
EAN-13만 활성화 + didScan 콜백에서 880 접두사만 처리 (1단 + 4단 조합)
import {
BarcodeCaptureSettings,
type BarcodeCaptureListener,
type BarcodeCaptureSession,
Symbology,
} from "@scandit/web-datacapture-barcode";
const settings = new BarcodeCaptureSettings();
// 1단: EAN-13만 활성화 — 모든 심볼로지는 기본 비활성화
settings.enableSymbologies([Symbology.EAN13UPCA]);
// 4단: didScan 콜백에서 880 접두사(국산 제품)만 처리
const krRegex = /^880\d{10}$/;
const listener: BarcodeCaptureListener = {
didScan: async (barcodeCapture, session: BarcodeCaptureSession) => {
const barcode = session.newlyRecognizedBarcode;
if (!barcode?.data || !krRegex.test(barcode.data)) return;
// 국산 EAN-13 — 비즈니스 로직 처리
},
};
barcodeCapture.addListener(listener);
EAN-13만 활성화 + didScan 콜백에서 880 접두사만 처리 (1단 + 4단 조합)
const settings = new BarcodeCaptureSettings();
// 1단: EAN-13만 활성화 — 모든 심볼로지는 기본 비활성화
settings.enableSymbologies([Symbology.EAN13UPCA]);
// 4단: didScan 콜백에서 880 접두사(국산 제품)만 처리
const krRegex = /^880\d{10}$/;
const listener = {
didScan: (_: BarcodeCapture, session: BarcodeCaptureSession) => {
const barcode = session.newlyRecognizedBarcode;
if (!barcode?.data || !krRegex.test(barcode.data)) return;
// 국산 EAN-13 — 비즈니스 로직 처리
},
};
barcodeCapture.addListener(listener);

