From c5f16795ef9fc55be5b2c897470635defb9fd8cc Mon Sep 17 00:00:00 2001 From: sb Date: Sun, 5 Jul 2026 14:46:14 +0900 Subject: [PATCH] =?UTF-8?q?fix(ai):=20=EC=98=81=EC=88=98=EC=A6=9D=20AI=20?= =?UTF-8?q?=EB=A9=80=ED=8B=B0=ED=8C=8C=ED=8A=B8=20=ED=8C=8C=ED=8A=B8?= =?UTF-8?q?=EC=88=98=20=EC=B4=88=EA=B3=BC(500)=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tomcat 기본 파트 수 제한(10)에 categoryHints/walletHints를 이름마다 파트로 보내 초과 → 500. 힌트를 줄바꿈으로 합친 단일 필드로 받아 파트 3개로 축소. Co-Authored-By: Claude Opus 4.8 --- .../web/account/controller/OcrController.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/sb/web/account/controller/OcrController.java b/src/main/java/com/sb/web/account/controller/OcrController.java index 8ba9642..a093b68 100644 --- a/src/main/java/com/sb/web/account/controller/OcrController.java +++ b/src/main/java/com/sb/web/account/controller/OcrController.java @@ -17,6 +17,7 @@ import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.time.LocalDate; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -44,18 +45,33 @@ public class OcrController { return Map.of("text", text); } - /** 영수증 이미지 → AI 구조화. AI 미설정/실패/미인식이면 recognized=false 로 반환(프론트가 /receipt 폴백). */ + /** + * 영수증 이미지 → AI 구조화. AI 미설정/실패/미인식이면 recognized=false 로 반환(프론트가 /receipt 폴백). + * 분류·계좌 후보는 멀티파트 파트 수 제한(Tomcat 기본 10개)을 피하려 '줄바꿈'으로 합친 단일 필드로 받는다. + */ @PostMapping("/receipt-ai") public ParsedEntryResponse receiptAi( @RequestParam("image") MultipartFile image, - @RequestParam(value = "categoryHints", required = false) List categoryHints, - @RequestParam(value = "walletHints", required = false) List walletHints, + @RequestParam(value = "categoryHints", required = false) String categoryHints, + @RequestParam(value = "walletHints", required = false) String walletHints, @RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) throws IOException { if (image == null || image.isEmpty()) { throw new ApiException(HttpStatus.BAD_REQUEST, "영수증 이미지를 첨부하세요."); } return aiEntryParser - .parseReceipt(image.getBytes(), image.getContentType(), LocalDate.now(), categoryHints, walletHints) + .parseReceipt(image.getBytes(), image.getContentType(), LocalDate.now(), + splitLines(categoryHints), splitLines(walletHints)) .orElseGet(() -> ParsedEntryResponse.builder().recognized(false).build()); } + + /** 줄바꿈으로 합쳐 온 후보 문자열 → 리스트(공백 항목 제거). null/빈 값이면 빈 리스트. */ + private static List splitLines(String joined) { + if (joined == null || joined.isBlank()) return List.of(); + List out = new ArrayList<>(); + for (String s : joined.split("\n")) { + String t = s.trim(); + if (!t.isEmpty()) out.add(t); + } + return out; + } }