Scandit SDK 핵심 개념 — DataCaptureContext와 5요소 아키텍처 완전 가이드
최근 업데이트: 2026-05-01
한줄 요약
Scandit SDK는 DataCaptureContext(오케스트레이터)·DataCaptureView(렌더링)·FrameSource(카메라)·DataCaptureMode(스캔 능력)·Overlays(AR 증강) 5요소로 구성된다. 각 요소는 명확한 책임 경계를 가지며, 라이프사이클 연동과 메모리 관리가 안정적인 프로덕션 운영의 핵심이다. 라이선스 키는 빌드별로 분리하고, 오프라인 환경에서도 최대 7일간 오프라인 검증이 동작한다.
TL;DR
- Scandit SDK는 5요소로 구성됩니다:
DataCaptureContext(오케스트레이터),FrameSource(카메라),DataCaptureMode(인식 엔진),DataCaptureView(렌더링),Overlays(AR 증강). - 초기화 순서와 플랫폼별 라이프사이클 연동이 잘못되면 콜백이 호출되지 않는 침묵 장애가 발생합니다.
- 라이선스 키는 개발·스테이징·프로덕션을 반드시 분리하고, 소스 코드에 하드코딩하지 않습니다.
- 오프라인 환경에서는 온라인 초기 검증 후 최대 7일간 캐시된 토큰으로 동작합니다.
Scandit SDK 5요소 — Architecture Map
Scandit SDK를 처음 접하는 개발자가 가장 자주 혼란을 겪는 지점은 "어떤 객체가 무엇을 책임지는가"입니다. SDK는 관심사 분리(Separation of Concerns) 원칙에 따라 다섯 가지 핵심 빌딩 블록을 명확하게 분리해 놓았습니다.
DataCaptureContext — 모든 것의 시작점
DataCaptureContext는 SDK 전체의 진입점이자 라이프사이클 관리자입니다. 라이선스 키 검증, FrameSource 연결, DataCaptureMode 등록, DataCaptureView 바인딩을 모두 이 객체가 중재합니다. iOS에서는 DataCaptureContext(licenseKey:)로, Android에서는 DataCaptureContext.forLicenseKey()로, React Native에서는 DataCaptureContext.initialize()로 생성하며, Web에서는 DataCaptureContext.forLicenseKey(licenseKey, { libraryLocation, moduleLoaders })를 비동기 호출합니다.
초기화 시점에 라이선스 키 검증이 즉시 이루어집니다. 키가 유효하지 않거나 번들 ID가 일치하지 않으면 Context 생성 자체가 실패하므로, 앱 런치 플로에서 에러 핸들링을 반드시 구현해야 합니다. 특히 Trial 키에서 Production 키로 전환할 때 번들 ID 바인딩 오류가 자주 발생하므로, 전환 전 스테이징 환경에서 충분히 검증하는 것이 중요합니다.
Context 하나에 여러 Mode를 등록할 수 있지만, 활성화된 Mode 수가 늘어날수록 프레임당 처리 부하가 증가합니다. 이 점은 다중 Mode 사용 섹션에서 상세히 다룹니다.
FrameSource — 카메라와 영상 스트림
FrameSource는 SDK에 영상 프레임을 공급하는 계층입니다. 실제 디바이스 카메라를 추상화한 Camera 객체가 가장 일반적인 구현체입니다. Camera.default 또는 Camera.atPosition(.worldFacing)처럼 카메라 포지션을 지정해 후면·전면 카메라를 선택합니다.
FrameSource를 DataCaptureContext.setFrameSource()로 연결하면 Context가 카메라를 제어하기 시작합니다. 카메라 권한 획득은 플랫폼 OS가 담당하며, SDK는 권한이 없을 경우 FrameSourceState.unavailable 상태를 반환합니다. 이 상태를 리스너로 감지해 사용자에게 권한 요청 UI를 보여주는 흐름이 실무 패턴입니다.
창고 자동화나 고정 카메라 시나리오에서는 실시간 디바이스 카메라 대신 외부 영상 피드를 FrameSource로 주입하는 방식도 지원합니다. 이에 대해서는 "Capture View 없이 off-screen 처리" 섹션에서 별도로 다룹니다.
DataCaptureMode — 실제 인식 능력
DataCaptureMode는 프레임에서 무엇을 인식할 것인지를 정의합니다. Scandit SDK가 지원하는 Mode는 다음과 같습니다.
| Mode | 용도 | 주요 설정 클래스 |
|---|---|---|
| BarcodeCapture | 단일 바코드 스캔 | BarcodeCaptureSettings |
| SparkScan | 고속 연속 스캔 (피킹·물류) | SparkScanSettings |
| IDCapture | 신분증·여권 인식 | IDCaptureSettings |
| LabelCapture | 복합 라벨 인식 | LabelCaptureSettings |
| MatrixScan | 다중 바코드 동시 인식 | BarcodeBatchSettings |
각 Mode는 DataCaptureContext.addMode()로 등록하고, mode.isEnabled = true로 활성화합니다. Mode 객체를 생성하는 시점에는 context를 파라미터로 전달해 연결 관계를 수립합니다.
iOS Swift — BarcodeCapture 리스너 콜백 구현
import ScanditBarcodeCapture
class ScanViewController: UIViewController, BarcodeCaptureListener {
func barcodeCapture(_ barcodeCapture: BarcodeCapture,
didScanIn session: BarcodeCaptureSession,
frameData: FrameData) {
// 인식된 바코드 처리 (newlyRecognizedBarcode: 단수)
guard let barcode = session.newlyRecognizedBarcode else { return }
let symbology = barcode.symbology.description
let data = barcode.data ?? "(데이터 없음)"
print("인식: [\(symbology)] \(data)")
// 중복 스캔 방지: 리스너 내부에서 즉시 비활성화
DispatchQueue.main.async {
barcodeCapture.isEnabled = false
self.handleScanResult(data: data)
}
}
}
// BarcodeCapture 생성 후 리스너 등록
barcodeCapture.addListener(self)
DataCaptureView — UI 렌더링 레이어
DataCaptureView는 카메라 피드를 화면에 그리고, 인식 영역 UI(뷰파인더)와 Overlay를 표시하는 역할을 합니다. 이 객체는 필수가 아닙니다. 화면이 없는 백엔드 처리나 헤드리스 환경에서는 생략 가능합니다.
iOS에서는 DataCaptureView(context: context, frame: view.bounds)로 생성하고 UIView 계층에 추가합니다. Android에서는 DataCaptureView.newInstance(context, dataCaptureContext)로 생성 후 레이아웃에 배치합니다. Web SDK에서는 DataCaptureView.forContext(context)로 생성한 뒤 DOM 엘리먼트에 연결합니다.
실무에서 중요한 점은 DataCaptureView의 크기와 레이아웃이 카메라 미리보기 해상도와 일치해야 한다는 것입니다. View 크기가 카메라 출력 비율과 맞지 않으면 뷰파인더가 왜곡되거나, 스캔 영역(Viewfinder)이 실제 인식 가능 영역과 시각적으로 어긋나는 현상이 발생합니다. 국내 Android 단말의 경우 제조사별 화면 비율 차이가 크기 때문에, DataCaptureView를 전체 화면에 맞게 배치하고 ScanAreaMargins로 인식 범위를 명시적으로 지정하는 방어 설정을 권장합니다.
Overlays — AR 증강과 스캔 박스 UI
Overlay는 DataCaptureView 위에 인식 결과를 시각적으로 표시하는 AR 레이어입니다. BarcodeCaptureOverlay는 인식된 바코드 위에 브러시(색상·테두리)를 그려줍니다. SparkScanBarcodeFeedback은 SparkScan 모드의 피드백 UI를 제어합니다.
Overlay는 DataCaptureView.addOverlay()로 View에 연결합니다. 커스텀 Overlay를 구현하면 인식된 바코드 위치에 재고 수량, 가격 정보 같은 커스텀 데이터를 AR로 오버레이할 수 있습니다. 이 패턴은 MatrixScan을 활용한 실시간 재고 피킹 UI에서 실무 적용 빈도가 높습니다.
라이선스 모델 — 발급·검증·만료 처리
라이선스 키 종류
Scandit 라이선스 키는 세 가지 유형으로 발급됩니다.
Trial 키: Scandit 포털에서 자가 발급이 가능합니다. 기능 제한 없이 일정 기간 또는 스캔 횟수 제한으로 동작합니다. PoC나 개발 초기 단계에 적합하지만, 프로덕션 배포에는 사용할 수 없습니다. 번들 ID나 패키지 이름이 특정 값으로 고정되지 않아 개발 환경 유연성이 높습니다.
Development 키: Scandit과 계약 체결 후 발급되는 개발용 키입니다. 특정 번들 ID(iOS) 또는 Application ID(Android)에 바인딩되며, 디버그 빌드에만 동작하도록 제한할 수 있습니다. 팀 내 여러 개발자가 공유하는 키로 운용하되, 프로덕션 서버에 배포해서는 안 됩니다.
Production 키: 앱스토어·플레이스토어 배포용 키입니다. 번들 ID, 서명 인증서, 라이선스된 기능 범위(활성화된 Mode, 심볼로지 종류)가 모두 고정됩니다. 키 유출 시 Scandit 측에 즉시 폐기 요청이 가능합니다.
국내 구축 사례에서 DC가 권장하는 패턴은 Development 키와 Production 키를 환경 변수(.env.local, Bitrise/Jenkins CI 시크릿)로 분리 관리하는 것입니다. 소스 코드에 키를 하드코딩하면 GitHub 등 공개 저장소에 노출될 위험이 있으며, 특히 한국 공공기관 발주 프로젝트에서 보안 감사 시 지적 사항이 되는 경우가 있습니다.
검증 메커니즘과 오프라인 동작
앱이 처음 실행되어 DataCaptureContext를 생성하면, SDK는 Scandit 인증 서버에 연결해 라이선스 키의 유효성·만료일·바인딩된 번들 ID·활성화된 기능을 확인합니다. 온라인 검증 성공 시 서명된 오프라인 토큰이 기기 내부 저장소에 캐시됩니다.
이후 최대 7일간은 인터넷 연결 없이 캐시된 토큰으로 동작합니다. 7일이 지나면 온라인 재검증이 필요합니다. 재검증에 성공하면 토큰이 갱신되어 7일이 리셋되고, 실패하면 스캔 기능이 비활성화됩니다. 이 동작은 병원 지하층, 냉동 창고, 지하철 물류 터미널처럼 Wi-Fi가 불안정한 환경에서 설계 시 반드시 고려해야 하는 제약입니다.
만료 시 동작
라이선스가 만료되면 DataCaptureContext 생성 자체가 즉시 실패하고 LicenseException이 발생합니다. 이미 실행 중인 앱의 경우, 만료 직전 오프라인 토큰이 소진되면 스캔 기능이 중단됩니다. 만료 예정 날짜가 다가올 때 SDK는 DataCaptureContextListener의 경고 콜백을 통해 만료 임박을 알려주므로, 이 리스너를 구현해 갱신 알림을 사용자에게 표시하는 것이 권장 패턴입니다.
iOS — 라이선스 만료 모니터링
DataCaptureContextListener를 구현하면 context(_:didChange:) 콜백으로 Context 상태 변화를 감지할 수 있습니다. 라이선스 만료 처리를 위해서는 DataCaptureContext에 리스너를 등록하고, 해당 콜백에서 애플리케이션 레벨의 경고 알림 로직을 구현하는 것이 권장 패턴입니다. 구체적인 ContextStatus API는 Scandit Docs — DataCaptureContextListener (iOS)를 참조하세요.
라이선스 만료 예방의 실무 패턴으로는 만료 60일 전 내부 모니터링 알림을 설정하고, 30일 전에 갱신을 시작하며, 만료 7일 전에 프로덕션 키 교체를 완료하는 것이 권장됩니다.
iOS / Android / Web / React Native — 라이프사이클 매핑
플랫폼마다 컴포넌트 라이프사이클이 다르기 때문에, SDK 객체를 언제 초기화하고 언제 해제할지가 통합의 핵심입니다. 이 타이밍이 어긋나면 카메라가 해제되지 않아 다른 앱이 카메라를 사용하지 못하거나, 백그라운드에서 복귀할 때 카메라가 재시작에 실패하는 문제가 발생합니다.
iOS — UIViewController 라이프사이클
iOS에서 DataCaptureContext는 일반적으로 UIViewController의 viewDidLoad에서 초기화합니다. 카메라(FrameSource)는 viewWillAppear에서 시작하고, viewWillDisappear에서 정지합니다. context.removeAllModes()는 ViewController가 pop되거나 dismiss될 때 호출해 Mode가 메모리에 남지 않도록 합니다.
SwiftUI를 사용하는 경우 onAppear / onDisappear에서 동일한 패턴을 적용하되, @StateObject로 Context를 보관해 View 재렌더링 시 불필요한 재초기화가 일어나지 않도록 합니다.
국내 iOS 앱 프로젝트에서 자주 발생하는 실수는 viewDidDisappear에서 카메라를 정지하는 것입니다. viewWillDisappear와 viewDidDisappear의 차이는 애니메이션 타이밍인데, viewDidDisappear에서 정지하면 화면 전환 애니메이션 중에도 카메라가 동작하여 불필요한 전력 소모와 간혹 ARKit 충돌이 발생합니다.
Android — Activity / Fragment 라이프사이클
Android에서는 Activity.onCreate()에서 DataCaptureContext를 생성하고, onResume()에서 카메라를 시작하며, onPause()에서 카메라를 정지합니다. onDestroy()에서 context.release()를 호출해 네이티브 리소스를 해제합니다.
Fragment를 사용하는 경우 onViewCreated / onDestroyView의 쌍으로 View 관련 리소스를 관리하되, Context 자체의 생명주기는 Fragment보다 긴 경우가 있으므로 ViewModel에 Context를 보관하는 패턴이 적합합니다.
국내 Android 환경에서 특히 주의할 점은 삼성 OneUI의 멀티윈도우·DeX 모드입니다. 앱이 분할 화면으로 전환될 때 onPause→onResume 사이클이 즉시 발생하는데, 이 시점에 카메라 재초기화 지연이 발생하면 사용자에게 카메라 피드가 잠시 검게 보이는 증상이 나타납니다. FrameSource 상태를 onResume에서 즉시 on으로 전환하기보다 FrameSourceStateChangeListener를 등록해 상태가 실제로 on이 된 후에 스캔을 활성화하는 방어 코드가 필요합니다.
Web — Page Lifecycle API와 SPA 라우팅
Web SDK는 비동기 초기화가 필요합니다. SDCCore.configure()는 WASM 모듈과 Worker를 로드하는 비동기 작업이므로, 이 완료를 await한 후에 DataCaptureContext.create()를 호출해야 합니다.
Web TypeScript — SDK 초기화 및 libraryLocation 설정
import { DataCaptureContext, Camera } from "@scandit/web-datacapture-core";
import { barcodeCaptureLoader } from "@scandit/web-datacapture-barcode";
// WASM·Worker 파일은 /sdc-lib/ 경로에서 서빙해야 합니다
// 404가 발생하면 libraryLocation 경로를 먼저 확인하세요
const context = await DataCaptureContext.forLicenseKey(
process.env.NEXT_PUBLIC_SCANDIT_KEY!,
{
libraryLocation: new URL("sdc-lib/", document.baseURI).toString(),
moduleLoaders: [barcodeCaptureLoader()],
}
);
const camera = Camera.default;
await context.setFrameSource(camera);
// 탭 닫힘 또는 컴포넌트 언마운트 시 정리
window.addEventListener("beforeunload", () => context.dispose());
SPA(React, Vue, Angular)에서의 핵심 패턴은 컴포넌트 언마운트 시 context.dispose()를 호출하는 것입니다. beforeunload 이벤트에서도 동일하게 처리해 탭을 닫을 때 카메라 스트림이 정리되도록 합니다.
Next.js나 Nuxt처럼 SSR 환경에서는 DataCaptureContext를 클라이언트 사이드에서만 초기화해야 합니다. typeof window === 'undefined' 가드나 Next.js의 dynamic(() => import(...), { ssr: false }) 패턴으로 서버 사이드 렌더링 시 SDK 초기화를 방지합니다.
React Native — useEffect와 useMemo
React Native에서는 useMemo로 DataCaptureContext를 컴포넌트 마운트 시 한 번만 생성하고, useEffect cleanup 함수에서 context.dispose()를 호출해 언마운트 시 정리합니다. 이 패턴은 React Strict Mode에서 두 번 렌더링이 발생해도 안전하게 동작합니다.
// React Native: initialize()는 모듈 레벨에서 한 번만 호출합니다
// import { DataCaptureContext } from "scandit-react-native-datacapture-core";
DataCaptureContext.initialize("-- ENTER YOUR SCANDIT LICENSE KEY HERE --");
const context = DataCaptureContext.sharedInstance;
useEffect(() => {
return () => { context.dispose(); };
}, []);
useEffect의 dependency array에 context를 포함하는 이유는 linter가 exhaustive deps 경고를 발생시키기 때문이기도 하지만, 실제로 Context가 재생성될 경우 이전 Context를 정리하는 안전망 역할도 합니다. context를 useState로 관리하면 상태 업데이트마다 불필요한 재초기화가 발생하므로 useMemo 사용이 권장됩니다.
Capture View 없이 off-screen 처리
모든 스캔 작업에 화면 UI가 필요한 것은 아닙니다. 한 국내 제약물류 자동화 업체의 경우, 컨베이어 벨트 위에 고정 카메라를 설치하고 DataCaptureView 없이 순수 백엔드 이미지 처리 파이프라인에 Scandit SDK를 통합했습니다.
이 패턴의 핵심은 DataCaptureView를 생성하지 않고 FrameSource를 직접 커스텀 구현으로 교체하는 것입니다. SDK는 FrameSource 인터페이스를 구현한 커스텀 객체를 받아들이므로, IP 카메라 스트림이나 영상 파일을 FrameSource로 주입할 수 있습니다.
구체적인 시나리오는 다음과 같습니다.
고정 카메라 + 서버 처리: 산업용 카메라가 RTSP 스트림을 생성하고, 서버 측에서 OpenCV 등으로 프레임을 추출해 SDK에 주입합니다. 이 경우 DataCaptureView는 불필요하며, DataCaptureMode의 리스너만 구현해 인식 결과를 수신합니다.
Robot + embedded: 로봇 팔에 장착된 카메라의 영상을 Raspberry Pi나 Jetson Nano 같은 임베디드 디바이스에서 처리하는 경우, 디스플레이 없이 DataCaptureContext + FrameSource + BarcodeCapture만으로 스캔 파이프라인을 구성합니다.
이 패턴에서 주의할 점은 FrameSource 커스텀 구현 시 프레임 타임스탬프와 해상도 메타데이터를 올바르게 전달해야 SDK의 모션 블러 보정과 자동 노출 알고리즘이 정상 동작한다는 것입니다. 프레임 메타데이터가 부정확하면 인식률이 예상보다 낮게 나타나는 경우가 있습니다.
off-screen 패턴은 특히 GS1 DataMatrix 직렬화 검증이 필요한 의약품 유통 창고에서 자주 사용됩니다. 컨베이어 속도가 분당 수백 개 수준인 환경에서는 화면 렌더링에 드는 GPU 리소스를 절약하고 CPU를 인식 파이프라인에만 집중시키는 것이 실질적인 처리량 향상으로 이어집니다. 국내 한 자동화 물류센터 사례에서는 DataCaptureView를 제거한 후 동일 하드웨어에서 처리량이 약 15% 향상된 것으로 측정되었습니다.
다중 Mode 동시 활성
Scandit SDK는 하나의 DataCaptureContext에 여러 Mode를 동시에 등록할 수 있습니다. 예를 들어 한 국내 의료기기 유통사는 BarcodeCapture와 IDCapture를 동시에 활성화해, 바코드 스캔과 환자 신분증 인식을 같은 화면에서 처리합니다.
동시 활성화 가능 여부
현재 SDK 8.x 기준으로 동시 활성화가 지원되는 조합과 제한 사항은 다음과 같습니다.
| 조합 | 동시 활성 | 비고 |
|---|---|---|
| BarcodeCapture + BarcodeCapture | 불가 | 동일 Mode 중복 등록 불가 |
| BarcodeCapture + IDCapture | 가능 | 프레임마다 두 Mode가 순차 처리 |
| BarcodeCapture + MatrixScan | 불가 | MatrixScan이 BarcodeCapture의 상위 집합 |
| SparkScan + IDCapture | 가능 | SparkScan 트리거 → IDCapture 분기 패턴 |
프레임 처리 순서와 성능
여러 Mode가 활성화되면 SDK는 각 프레임에 대해 등록된 Mode를 순차적으로 실행합니다. Mode가 추가될수록 프레임당 처리 시간이 증가하고, 60fps 카메라에서 처리 여력이 부족하면 프레임 드롭이 발생해 인식 반응성이 저하됩니다.
DC 권장 패턴은 동시 활성보다 상황 분기입니다. 특정 화면 상태(바코드 모드 vs. 신분증 모드)에 따라 mode.isEnabled를 토글하는 방식이 불필요한 처리 부하를 줄이면서 기능 전환을 구현하는 더 효율적인 방법입니다. isEnabled를 토글하는 것은 Mode를 추가·제거하는 것보다 가볍습니다.
성능 예산 관점에서 실제 수치를 공유하면, 국내 중급 Android 단말(Snapdragon 680 기준)에서 BarcodeCapture 단독 활성 시 프레임당 처리 시간은 약 8–12ms로 60fps 유지가 가능합니다. IDCapture를 동시 활성화하면 프레임당 처리 시간이 25–35ms 수준으로 늘어나 30fps 이하로 떨어지는 경우가 있습니다. 두 Mode를 실시간으로 동시에 써야 하는 요건이 아니라면 isEnabled 토글 패턴을 우선 검토하기를 권장합니다.
Android Kotlin — onDestroy 메모리 정리 패턴
// Activity.onDestroy()에서 SDK 리소스를 명시적으로 해제합니다
override fun onDestroy() {
super.onDestroy()
// 1. 스캔 Mode 비활성화 후 Context에서 제거
barcodeCapture.isEnabled = false
dataCaptureContext.removeAllModes()
// 2. 카메라(FrameSource) 정지
camera?.switchToDesiredState(FrameSourceState.OFF)
// 3. Context 자체 해제 — 네이티브 리소스 반환
dataCaptureContext.release()
}
트러블슈팅 — 자주 마주치는 5가지 패턴
현장 통합에서 DC가 반복적으로 마주치는 문제와 원인·해결 방법을 정리합니다.
패턴 1: 카메라 권한 거부
증상: 앱을 처음 실행하면 카메라가 표시되지 않고, FrameSource의 상태가 unavailable에서 벗어나지 않는다.
원인: OS 레벨 카메라 권한이 없거나, 사용자가 권한 요청을 거부한 상태다.
해결: FrameSourceStateChangeListener를 구현해 unavailable 상태 진입 시 OS 설정 화면으로 안내하는 Alert를 표시합니다. Android에서는 ActivityCompat.requestPermissions()로 명시적 권한 요청 후 onRequestPermissionsResult에서 결과를 처리합니다. iOS는 AVCaptureDevice.authorizationStatus(for: .video) 확인 후 Info.plist의 NSCameraUsageDescription이 반드시 설정되어 있어야 합니다.
패턴 2: Web SDK library file 404
증상: Web 앱 로드 시 브라우저 콘솔에 WASM 파일 또는 Worker 스크립트 404 에러가 표시되고 스캔이 동작하지 않는다.
원인: SDCCore.configure()의 libraryLocation 경로가 실제 파일 위치와 일치하지 않는다.
해결: 빌드 스크립트에서 node_modules/@scandit/web-datacapture-core/build/의 .wasm, .js, Worker 파일들을 /public/scandit/으로 복사하고 libraryLocation: '/scandit/'로 설정합니다. Vite나 webpack을 사용하는 경우 CopyPlugin으로 자동화할 수 있습니다. CDN 경유 시에는 CORS 헤더 설정이 필요합니다.
패턴 3: Mode가 View에 attach되지 않은 채 활성화
증상: 스캔을 시작해도 DataCaptureListener의 콜백이 호출되지 않는다.
원인: Mode가 DataCaptureContext에 등록되었지만, DataCaptureView에 Overlay가 추가되지 않았거나, Mode 자체가 isEnabled = false 상태인 경우가 많다.
해결: 초기화 순서를 점검합니다. 올바른 순서는 다음과 같습니다.
DataCaptureContext생성BarcodeCaptureSettings설정 및BarcodeCapture생성 (Context 연결)DataCaptureView생성 (Context 연결)BarcodeCaptureOverlay생성 및 View에 추가barcodeCapture.isEnabled = truecamera.switchToDesiredState(.on)
Overlay가 View에 추가되지 않아도 콜백은 동작하지만, 뷰파인더 UI가 없으면 사용자는 스캔 영역을 인지하지 못합니다. Step 4를 빠뜨리면 기능은 동작해도 UX가 불완전합니다.
패턴 4: 라이선스 키 만료
증상: 배포 후 특정 날짜 이후로 모든 기기에서 스캔이 중단됩니다. 로그에 LicenseException 또는 라이선스 관련 에러 메시지가 표시됩니다.
원인: 라이선스 계약 갱신이 누락되거나, Trial 키를 프로덕션에 사용한 경우다.
해결: Scandit 포털에서 라이선스 만료일을 주기적으로 확인합니다. Production 키의 만료일이 계약 기간과 일치하는지 점검하고, 갱신 후 새 키를 앱 업데이트로 배포합니다. 만료 30일 전 알림을 CI/CD 파이프라인에 연동해 놓으면 배포 지연 위험을 줄일 수 있습니다. DC는 국내 고객사의 라이선스 갱신 일정을 대신 관리하는 서비스를 제공하므로, 갱신 누락이 우려된다면 담당 엔지니어에게 문의하시면 됩니다.
패턴 5: 백그라운드 복귀 시 카메라 freeze
증상: 앱을 백그라운드로 보냈다가 돌아오면 카메라 피드가 검거나 멈춰 있습니다.
원인: onPause에서 카메라를 정지했지만 onResume에서 재시작 로직이 누락되거나, 재시작 타이밍이 너무 이르다.
해결: Android에서는 onResume에서 camera.switchToDesiredState(.on)을 호출하고, FrameSourceStateChangeListener.onFrameSourceChanged()에서 상태가 실제로 on이 된 것을 확인한 후에 barcodeCapture.isEnabled = true를 설정합니다. iOS에서는 viewWillAppear에서 카메라를 재시작하고 CameraState.on이 됨을 CameraListener로 확인합니다. 상태 확인 없이 즉시 활성화하면 일부 단말에서 첫 몇 프레임이 누락되거나 검은 화면이 잠시 표시됩니다.
DC 한국 도입 패턴
혼합 플랫폼 환경에서의 라이선스 키 관리
한 글로벌 물류사가 국내 DC 지원으로 SDK를 도입할 때 직면한 과제는 iOS 단말과 Android 단말이 혼재하는 환경에서 라이선스 키를 어떻게 배포·관리하는가였습니다. 플랫폼별로 키를 분리 발급하되, CI/CD 시스템의 시크릿 스토어에 SCANDIT_KEY_IOS_PROD와 SCANDIT_KEY_ANDROID_PROD를 별도로 저장하고 빌드 시 각 플랫폼 바이너리에 주입하는 방식으로 해결했습니다.
이 방식의 장점은 iOS 키가 유출되더라도 Android 스캔에 영향이 없고, 키 폐기·재발급 시 해당 플랫폼만 업데이트하면 된다는 점입니다. 국내 공공기관 발주 프로젝트의 경우 보안 감사에서 시크릿 관리 방식이 확인 항목으로 포함되는 경우가 있어, 이런 분리 관리 패턴이 실질적인 컴플라이언스 이점도 제공합니다.
의약품 유통 — 한 국내 대형 의약품 유통사 사례
한 국내 대형 의약품 유통사는 식약처 의약품 표준코드 컴플라이언스를 위해 GS1 DataMatrix 스캔 시스템을 구축했습니다. 핵심 요구사항은 창고 내 고정 스캐너 스테이션과 이동형 Android 단말이 동일한 DataCaptureContext 설정을 공유해야 한다는 것이었습니다.
DataCaptureContext 초기화 코드를 공통 라이브러리로 추출하고, 단말 유형(고정/이동)에 따라 FrameSource 구현만 교체하는 아키텍처를 채택했습니다. 고정 스테이션은 커스텀 FrameSource로 IP 카메라 스트림을 주입하고 DataCaptureView를 생략했으며, 이동형 단말은 표준 Camera 객체와 DataCaptureView를 사용했습니다. Mode 설정과 리스너 코드는 두 환경 모두 동일하게 재사용되어 유지보수 부담이 크게 줄었습니다.
노후 Android 단말 호환성
국내 물류·리테일 현장에서 여전히 운용 중인 Android 7.0(API 24) 이하 단말에서 SDK를 안정적으로 운용하는 방법에 대해 질문이 자주 들어옵니다. 택배·편의점·마트 현장처럼 단말 교체 주기가 긴 환경에서는 최신 SDK가 구형 기기에서 어떻게 동작하는지 사전 검증이 필수입니다. Scandit SDK 8.x는 Android 5.0(API 21)부터 공식 지원하지만, 구형 단말일수록 카메라 HAL 구현의 품질 차이가 큽니다.
DC 현장 경험상 API 21–23 단말에서는 Camera2 API 지원이 불완전한 경우가 있어, SDK 내부적으로 Camera1 폴백이 동작합니다. 이 상황에서 자동 초점(AF)과 자동 노출(AE) 응답 속도가 현저히 느려지므로, BarcodeCapture.barcodeCaptureSettings.codeDuplicateFilter를 넉넉하게 설정해 동일 바코드가 반복 인식되는 문제를 줄여야 합니다. 또한 화면 해상도가 제한된 구형 단말에서는 Active Symbol Counts 범위를 좁혀 처리 부하를 줄이는 것이 인식 반응성에 도움이 됩니다.
자주 묻는 질문 (FAQ)
DataCaptureContext와 DataCaptureView 둘 다 만들어야 하나요?
용도에 따라 다릅니다. 카메라 피드를 화면에 보여줘야 한다면 DataCaptureView가 반드시 필요합니다. 반면 창고 자동화나 서버 사이드 이미지 처리처럼 화면 렌더링이 없는 환경이라면 DataCaptureContext만으로 스캔을 구동할 수 있습니다. DataCaptureContext는 항상 필수이며, DataCaptureView는 선택 사항입니다.
라이선스 키는 어디서 발급받고 어떻게 검증하나요?
Scandit 공식 포털(ssl.scandit.com)에서 개발·프로덕션·트라이얼 키를 각각 발급받을 수 있습니다. 앱 실행 시 DataCaptureContext 생성 시점에 키가 서버에서 온라인 검증됩니다. 검증에 성공하면 오프라인 토큰이 캐시되어 이후 최대 7일간 서버 없이 동작합니다.
Activity/ViewController가 백그라운드로 가면 카메라 세션은 어떻게 처리해야 하나요?
iOS에서는 viewWillDisappear 또는 viewDidDisappear에서 카메라를 정지시키고 Mode를 제거해야 합니다. Android에서는 onPause에서 FrameSource를 끄고 onDestroy에서 context.release()를 호출하는 패턴을 권장합니다.
Web SDK는 라이브러리 파일을 어디에 두어야 하나요?
Web SDK는 WASM 및 Worker 파일을 정적 경로에서 서빙해야 합니다. 빌드 시 node_modules의 scandit 패키지 파일을 /public/scandit/ 같은 정적 디렉터리로 복사하고 libraryLocation: '/scandit/'로 설정하는 것이 권장 방법입니다.
오프라인 환경에서 라이선스 검증이 작동하나요?
온라인 초기 검증 후 오프라인 토큰이 기기에 캐시되어 최대 7일간 인터넷 없이 동작합니다. 7일 이후에는 온라인 재검증이 필요하며, 재검증 실패 시 스캔 기능이 비활성화됩니다. 상시 오프라인 환경에서는 Scandit 측과 별도 오프라인 라이선스 옵션을 협의해야 합니다.
최근 업데이트
최근 업데이트: 2026-05-01
이 페이지는 Scandit SDK 8.x 기준으로 작성되었습니다. SDK 버전이 업데이트되면 API 시그니처와 라이프사이클 동작이 변경될 수 있으므로, Scandit Docs — Core Concepts 및 DataCaptureContext API (Android)를 병행 참조하시기 바랍니다. Scandit SDK 도입 설계, PoC, 라이선스 상담은 데이터커넥트 기술팀으로 문의해 주시기 바랍니다.
코드 샘플
DataCaptureContext 초기화 및 ViewController 라이프사이클 연동
import ScanditCaptureCore
let context = DataCaptureContext(licenseKey: "-- ENTER YOUR SCANDIT LICENSE KEY HERE --")
// ViewController 라이프사이클에 맞춰 release
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
context.removeAllModes()
}
DataCaptureContext 초기화 및 Activity 라이프사이클 연동
val context = DataCaptureContext.forLicenseKey("YOUR_LICENSE_KEY")
override fun onPause() {
super.onPause()
context.removeAllModes()
}
override fun onDestroy() {
super.onDestroy()
context.release()
}
DataCaptureContext 비동기 초기화 및 페이지 언마운트 정리
import { DataCaptureContext } from "@scandit/web-datacapture-core";
import { barcodeCaptureLoader } from "@scandit/web-datacapture-barcode";
const context = await DataCaptureContext.forLicenseKey(
"-- ENTER YOUR SCANDIT LICENSE KEY HERE --",
{ libraryLocation: "/sdc-lib/", moduleLoaders: [barcodeCaptureLoader()] }
);
// 페이지 unmount 시 정리
window.addEventListener("beforeunload", () => context.dispose());
DataCaptureContext 초기화 및 useEffect cleanup 패턴
import { DataCaptureContext } from "scandit-react-native-datacapture-core";
// initialize()는 컴포넌트 외부(모듈 레벨)에서 한 번만 호출합니다
DataCaptureContext.initialize("-- ENTER YOUR SCANDIT LICENSE KEY HERE --");
const context = DataCaptureContext.sharedInstance;
useEffect(() => {
return () => { context.dispose(); };
}, []);

