-
[API] API Queue 처리일하며 늘어가는 지식 2025. 3. 4. 09:28
시작
이번에 작성할 글은 API 관련된 게시물이다.
문제
이번에 발생한 문제는 클라이언트 단에서 파일을 업로드 할 때, 생기는 문제였다.
주로, 여러개의 PDF를 동시 업로드 할 때 생겼는데, 내가 구성해둔 API의 상태가 병렬로 N개로 제한시켜 진행되도록 했는데, API 호출이 길어질때의 예외처리를 빼먹은 것,
React 단에서 각 API의 의존성을 깊게 둔 것이 문제의 원인이라고 파악하였다.
이를 해결하기 위해서,
1. 의존성을 줄인다.
2. API 호출의 방식을 변경한다.
여기서 의존성을 줄이기 위해서 코드 리팩토링을 진행하였다.
이 때, 리팩토링을 진행하면서 코드가 되게 깔끔해졌다!?
API 호출은 구글을 통하여 Queue를 도입해서 진행했다.
문제 해결
기존에 발생한 문제는 OCR 요청을 비동기(Promise.all)로 처리하였는데, 한 번에 많은 요청을 보내면 백엔드가 과부화 되면서 데이터 누락이 되거나 OCR 호출이 실패하는 경험을 겪게되었다.
또한, OCR 처리후 데이터 저장하는 과정에서 오랜 시간이 걸리기도 하였다.
기존에 for await으로 구현한 코드를 Queue 방식의 형태를 이용하여 변경하기로 하였다.
Queue의 방식일 이용한 프로세스는 다음과 같았다.
1. 파일을 여러개 업로드 하면, 큐(배열)에 저장된다.
2. 동시에 최대 3~5개까지 OCR 이 호출되도록 제한 걸어두어 동작 되도록 한다.
3. 요청이 하나 끝나게 되면 큐(배열)에서 다음 파일 실행한다(즉, 지속적으로 파일마다의 OCR 호출 진행)
4. 모든 파일이 처리가 될 때 까지 진행한다.
이런 형태로 API 처리 형태를 수정했다.
1차적으론 아래와 같이 구현을 했다.
const processAllUploadedFiles = async (uploadedFiles) => { if (!uploadedFiles || uploadedFiles.length === 0) { setUploadStatus("처리할 파일이 없습니다."); return; } const maxConcurrentRequests = 3; const queue = [...uploadedFiles]; const updatedFiles = []; let activeRequests = 0; const processNext = async () => { if (queue.length === 0) return; if (activeRequests < maxConcurrentRequests) { const file = queue.shift(); activeRequests++; try { console.log(`파일 OCR 처리 시작: ${file.filename}`); let ocrResult = await handleOcrProcess(file); if (!ocrResult) { updatedFiles.push({ ...file, status: "OCR 실패", ocrResult: null, previewFiles: null }); return; } console.log(`OCR 결과 저장 시작 (${file.filename})`); let previewUrl = await saveOcrResult(file, ocrResult.encryptedusername, ocrResult.originalOcrResult, ocrResult.decryptedocrResult); if (!previewUrl) { updatedFiles.push({ ...file, status: "OCR 실패", ocrResult: null, previewFiles: null }); return; } updatedFiles.push({ ...file, status: "OCR 완료", ocrResult: ocrResult.decryptedocrResult, previewFiles: previewUrl }); } catch (error) { updatedFiles.push({ ...file, status: "OCR 실패", ocrResult: null, previewFiles: null }); } finally { activeRequests--; processNext(); } } }; for (let i = 0; i < maxConcurrentRequests; i++) { processNext(); } await new Promise((resolve) => { const checkComplete = setInterval(() => { if (queue.length === 0 && activeRequests === 0) { clearInterval(checkComplete); resolve(); } }, 500); }); console.log(`전체 OCR 처리 완료`); onUploadSuccess(updatedFiles); };
위와 같은 형태로 API 호출 되는걸 수정했다.
1. 파일을 업로드
2. 업로드된 파일 큐에 추가 및 동시에 돌아갈 최대 개수 제한
3. 실제 돌아가는 개수 추가하면서 각각의 프로세스 진행
4. 모든 큐가 끝나게 되면, 변경 내용 전체 동시 반영
위와 같은 프로세스로 진행했는데, 단점이 UI 반영도 모든 큐가 끝나야 반영된다는 단점이 있었다.
그래서 UI적 개선이 필요해 저기에서 조금 더 수정을 가했다.
const processAllUploadedFiles = async (uploadedFiles) => { if (!uploadedFiles || uploadedFiles.length === 0) { setUploadStatus("처리할 파일이 없습니다."); return; } const maxConcurrentRequests = 3; const queue = [...uploadedFiles]; const updatedFiles = []; let activeRequests = 0; const processNext = async () => { if (queue.length === 0) return; if (activeRequests < maxConcurrentRequests) { const file = queue.shift(); activeRequests++; try { console.log(`파일 OCR 처리 시작: ${file.filename}`); let ocrResult = await handleOcrProcess(file); if (!ocrResult) { updatedFiles.push({ ...file, status: "OCR 실패", ocrResult: null, previewFiles: null }); return; } onUploadSuccess([...updatedFiles]); console.log(`OCR 결과 저장 시작 (${file.filename})`); let previewUrl = await saveOcrResult(file, ocrResult.encryptedusername, ocrResult.originalOcrResult, ocrResult.decryptedocrResult); if (!previewUrl) { updatedFiles.push({ ...file, status: "OCR 실패", ocrResult: null, previewFiles: null }); return; } onUploadSuccess([...updatedFiles]); updatedFiles.push({ ...file, status: "OCR 완료", ocrResult: ocrResult.decryptedocrResult, previewFiles: previewUrl }); } catch (error) { updatedFiles.push({ ...file, status: "OCR 실패", ocrResult: null, previewFiles: null }); onUploadSuccess([...updatedFiles]); } finally { activeRequests--; processNext(); } } }; for (let i = 0; i < maxConcurrentRequests; i++) { processNext(); } console.log(`전체 OCR 처리 완료`); onUploadSuccess(updatedFiles); };
각 프로세스에서 결과 반환하도록 수정하여, 프로세스의 각 과정을 UI로 확인 가능하도록 수정했다.
기존에 구성했던 OCR 처리 프로세스에서 발생한 에러로 인해,
API 를 동시에 처리하는 여러 방법에 대해서 찾게 되었고,
실제로 적용해서 테스트를 진행해보면서 쉬운게 하나 없구나...를 알았다??
그래도 신기했던것은 큐 형태로 호출하는게 참 참신했다.
아직 배울게 더 많구나(?)
'일하며 늘어가는 지식' 카테고리의 다른 글
[AWS] amplify (0) 2025.01.16 [암호화] AES, RSA (0) 2025.01.06 [애플리케이션] React + Django + Nginx를 EC2에 배포! 거기에 Docker를 곁들인.. (2) (1) 2024.11.25 [애플리케이션] React + Django + Nginx를 EC2에 배포! 거기에 Docker를 곁들인.. (1) (2) 2024.11.17 [모니터링] 수집된 메트릭데이터 S3로 적재하기 (2) 2024.11.09