회사 프로젝트에서 네이버 OCR 기능을 활용해
사업자등록증의 정보를 추출하는 기능을 구현해야 했습니다.
구글 OCR도 있었지만 한글이기 때문에 인식률 측면에서 네이버 OCR을 쓰는 게 낫다고 판단했습니다.
또한 네이버 OCR은 특정 도메인(명함/사업자등록증/영수증 등)에 대한 템플릿을 제공해 더 정확하게 추츨합니다.
사용하면서 주의해야할 점은 요금입니다.
기본적으로 유료 서비스이기 때문에 무료 구간을 잘 확인하여 개발해야합니다.
잘못하면 개발하면서 많은 요청을 보내 과금이 될 수 있습니다.
네이버 OCR은 기본적으로 특성에 따라 3가지 서비스로 나뉘어져 있습니다.
General OCR, Template OCR, Document OCR입니다.
범용적으로 OCR을 활용할 때 사용됩니다.
사진이나 불특정 문서들의 글자를 추출합니다.
템플릿을 사용해 특정 파일이나 문서에 적함한 OCR 적용 방법입니다.
인식 템플릿을 만들어 적용하므로 구체적인 문서의 레이아웃에 맞다면 더 정확한 분석이 가능합니다.
특정한 문서를 대상으로 할 때 유용합니다.
많이 사용하는 파일들을 네이버에서 이미 학습 시켜서 인식율을 높인 모델입니다.
영수증, 사업자등록증, 신용카드, 명함, 신분증 추가로 법인등기부등론, 병원비 영수증, 진단서, 병원비세부내역서 가능합니다.
저는 사업자등록증 모델을 사용했습니다.
네이버 OCR 기능은 다른 API 와는 다르게 API Gateway를 통해 사용해야 해서 절차가 더 많습니다.
자세한 사항은 CLOVA API 문서를 참고해주세요.
네이버 클라우드 플랫폼에 접속하여 로그인
서비스 -> AI Service -> CLOVA OCR -> 이용 신청하기
CLOVA OCR -> Domain
상품 이용신청 해줍니다.
API Gateway 연동을 클릭합니다.
Secret Key 생성하고 자동연동을 클릭합니다.
여기서 API Gateway 생서하신 적 없는 처음 사용하시는 분들은 API Gateway 신청합니다.
자동연동 하셨다면 API Gateway에 자동으로 프로덕트가 생긴 것을 확인할 수 있습니다.(수동 연동도 가능지만 자동추천)
이제 요청해봅시다. 자동연동 했던 창에서 APIGW Invoke URL을 복사하여 endpoint로 설정합니다.
POSTMAN에서 다음과 같이 Header와 Body를 추가합니다.
POSTMAN 기본 헤더에 추가로 X-OCR-SECRET을 추가해야 합니다.
실제 프로젝트 환경에서 적용 시 POSTMAN에서는 잘 됐지만 host가 바뀌는 환경에서는 CORS 에러가 나는 걸 확인할 수 있습니다.
API Gateway에 추가적인 설정이 필요한데 이건 문서에 안 나와있어 직접 CLOUD Platform에 문의하여 적용했습니다.
API Gateway -> My Products로 이동합니다.
Product의 APIs를 클릭합니다.
Resources -> POST 메서드 클릭 -> 리소스 생성 -> CORS 활성화
Access-Control-Allow-Headers에 "x-ocr-secret"을 추가하고 수정합니다.
API 배포 버튼 클릭
이제 실제 프로젝트에 적용 해봅시다.
저는 Next.js 환경에서 requestOCR 함수를 만들어 적용했습니다.
const requestOCR = async (
imageFile: FileWithPath[] | File[]
): Promise<OCRResponse | null | undefined> => {
try {
if (!imageFile[0]) {
window.alert('사업자등록증을 등록해주세요.');
return null;
}
const formData = new FormData();
const message = {
version: 'V2',
requestId: Math.random().toString(),
timestamp: dayjs().format('YYYYMMDDHHmmss'),
images: [{ format: 'jpeg', name: imageFile[0].name }],
};
formData.append('message', JSON.stringify(message));
formData.append('file', imageFile[0]);
const data = await axios.post(
process.env.NEXT_PUBLIC_NAVER_OCR_INVOKE_URL || '',
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
'X-OCR-SECRET': process.env.NEXT_PUBLIC_NAVER_X_OCR_SECRET,
},
}
);
return data.data;
} catch (error) {
console.log(error);
}
};
export { requestOCR };
저는 사업자등록증 Document API를 사용했고
각각 상태는 다음과 같았습니다.
OCR 분석에 성공한 경우
사업자등록증 이미지가 아닌 경우
OCR 분석에 실패한 경우
세 가지 상태이다 보니 각각의 state 만들기 보다는 하나의 state를 사용했습니다.
interface ocrState {
isPending: boolean; // OCR 분석 중
isFileRejected: boolean; // OCR 이미지가 아닌 경우
error: Error | null; // OCR 분석 실패
data: OCRResponse | null; // OCR 분석 성공 시 데이터
}
Dropzone에 이미지 업로드 시 자동으로 requestOCR 함수가 실행되도록 하였습니다.
적절한 사업자등록증 이미지가 아닌 경우 판단은 ocrData?.images[0].bizLicense.result가 undefined인 것을 활용했습니다.
useEffect(() => {
if (ocrState.data) {
setOcrState((prevState) => ({
...prevState,
isPending: false,
error: null,
data: null,
}));
}
if (imageFile.length > 0) {
setOcrState((prevState) => ({
...prevState,
data: null,
isFileRejected: false,
isPending: true,
error: null,
}));
requestOCR(imageFile)
.then((ocrData) => {
// 사업자등록증 형식이 아닌 경우
if (ocrData?.images[0].bizLicense.result == undefined) {
setOcrState((prevState) => ({
...prevState,
isPending: false,
isFileRejected: true,
error: {
message: '사업자등록증 이미지가 아닙니다.',
description: '올바른 사업자등록증 이미지를 업로드해주세요.',
},
}));
} else {
// OCR 분석 성공
setOcrState((prevState) => ({
...prevState,
isPending: false,
data: ocrData,
}));
}
})
.catch((err) => {
// OCR 분석 실패
setOcrState((prevState) => ({
...prevState,
isPending: false,
error: {
message: '사업자등록증 분석에 실패했습니다.',
description: '잠시 후 다시 시도해 주세요.',
},
}));
})
.finally(() => {
setOcrState((prevState) => ({
...prevState,
isPending: false,
}));
});
}
}, [imageFile]);
lottie를 이용해서 추가적인 애니메이션 및 스크롤 이동도 구현해봤습니다.
감사합니다!