SelfCheck_AI Troubleshooting_2 (26.01.25)

Meustarยท2026๋…„ 1์›” 25์ผ

Project

๋ชฉ๋ก ๋ณด๊ธฐ
9/15

๐Ÿ› ๏ธ 2. Troubleshooting (ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…)

์ด๋ฒˆ ์Šคํ”„๋ฆฐํŠธ(1.22 ~ 1.25)์—์„œ OpenAI API๋ฅผ ์—ฐ๋™ํ•˜๋ฉฐ ๊ฒช์—ˆ๋˜ ์ฃผ์š” ์—๋Ÿฌ 3๊ฐ€์ง€์™€ ๊ทธ ํ•ด๊ฒฐ ๊ณผ์ •์„ ์ƒ์„ธํžˆ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.


Issue 1. IllegalArgumentException: The template string is not valid

๐Ÿšจ ๋ฌธ์ œ ์ƒํ™ฉ
Spring AI์˜ ChatClient๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ AI์—๊ฒŒ ์š”์ฒญ์„ ๋ณด๋ƒˆ์œผ๋‚˜, ์„œ๋ฒ„ ๋กœ๊ทธ์— IllegalArgumentException์ด ๋ฐœ์ƒํ•˜๋ฉฐ ์š”์ฒญ์ด ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.

java.lang.IllegalArgumentException: The template string is not valid.
    at org.springframework.ai.chat.prompt.PromptTemplate.<init>(PromptTemplate.java:85)
    ...

๐Ÿ” ์›์ธ ๋ถ„์„
Spring AI์˜ ๊ธฐ๋ณธ PromptTemplate ํด๋ž˜์Šค๋Š” ๋ฌธ์ž์—ด ๋‚ด๋ถ€์˜ ์ค‘๊ด„ํ˜ธ { }๋ฅผ ๋ณ€์ˆ˜ ์น˜ํ™˜์ž(Placeholder)๋กœ ์ธ์‹.
ํ•˜์ง€๋งŒ ์ œ๊ฐ€ ์ž‘์„ฑํ•œ ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ์—๋Š” AI์—๊ฒŒ JSON ์‘๋‹ต ํ˜•์‹์„ ์˜ˆ์‹œ๋กœ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด ์ค‘๊ด„ํ˜ธ๊ฐ€ ํฌํ•จ๋œ JSON ๋ฌธ์ž์—ด์ด ๋“ค์–ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.


// ๋ฌธ์ œ๊ฐ€ ๋œ ํ”„๋กฌํ”„ํŠธ ์˜ˆ์‹œ
String systemPrompt = """
    ...
    ๋ฐ˜๋“œ์‹œ ์•„๋ž˜ ํ˜•์‹์œผ๋กœ ์‘๋‹ตํ•˜์„ธ์š”:
    {  <-- Spring AI๋Š” ์ด๊ฑธ ๋ณ€์ˆ˜์˜ ์‹œ์ž‘์œผ๋กœ ์ฐฉ๊ฐํ•จ
        "score": ...
    }
""";

PromptTemplate์€ ์ด ์ค‘๊ด„ํ˜ธ๋ฅผ ๋ณด๊ณ  "์—ฌ๊ธฐ์— ์–ด๋–ค ๊ฐ’์„ ์ฑ„์›Œ๋„ฃ์–ด์•ผ ํ•˜๋Š”๋ฐ, ๋งค์นญ๋˜๋Š” ๋ณ€์ˆ˜๊ฐ€ ์—†๋‹ค"๊ณ  ํŒ๋‹จํ•˜์—ฌ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ ๊ฒƒ.

โœ… ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
PromptTemplate์„ ๊ฑฐ์น˜์ง€ ์•Š๊ณ , SystemMessage์™€ UserMessage ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜์—ฌ ๋ฉ”์‹œ์ง€ ๋ฆฌ์ŠคํŠธ๋กœ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ…œํ”Œ๋ฆฟ ํŒŒ์‹ฑ ๊ณผ์ •์„ ๊ฑด๋„ˆ๋›ฐ๊ณ  ๋ฌธ์ž์—ด ๊ทธ๋Œ€๋กœ AI์—๊ฒŒ ์ „์†ก๋ฉ๋‹ˆ๋‹ค.

  • ์ˆ˜์ • ์ „(์—๋Ÿฌ ๋ฐœ์ƒ ์ฝ”๋“œ)

// ํ…œํ”Œ๋ฆฟ ํŒŒ์‹ฑ์„ ์‹œ๋„ํ•˜๋‹ค๊ฐ€ JSON ์ค‘๊ด„ํ˜ธ ๋•Œ๋ฌธ์— ์‹คํŒจํ•จ
chatClient.prompt()
    .system(systemPrompt) 
    .user(userMessage)
    .call();
  • ์ˆ˜์ • ํ›„(ํ•ด๊ฒฐ๋œ ์ฝ”๋“œ - Aiclient.java)

// Message ๊ฐ์ฒด๋กœ ์ง์ ‘ ๊ฐ์‹ธ์„œ ์ „๋‹ฌ (ํ…œํ”Œ๋ฆฟ ํŒŒ์‹ฑ ์šฐํšŒ)
chatClient.prompt()
    .messages(new SystemMessage(systemPrompt), new UserMessage(userMessage))
    .call();

Issue 2. 429 insufficient_quota (OpenAI Quota Exceeded)

๐Ÿšจ ๋ฌธ์ œ ์ƒํ™ฉ
์ฝ”๋“œ๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ์ˆ˜์ •ํ–ˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ , AI ์š”์ฒญ ์‹œ 429 ์ƒํƒœ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ์•„๋ž˜์™€ ๊ฐ™์€ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๊ฐ€ ๋ฐ˜ํ™˜๋˜์—ˆ์Šต๋‹ˆ๋‹ค.


{
    "error": {
        "code": "insufficient_quota",
        "message": "You exceeded your current quota, please check your plan and billing details."
    }
}

๐Ÿ” ์›์ธ ๋ถ„์„
์ด ์—๋Ÿฌ๋Š” "API ์‚ฌ์šฉ ํ•œ๋„ ์ดˆ๊ณผ" ๋˜๋Š” "์ž”์•ก ๋ถ€์กฑ"์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

  • ์˜คํ•ด: ์ €๋Š” ChatGPT Plus(์›” $20)๋ฅผ ๊ตฌ๋… ์ค‘์ด์—ˆ๊ธฐ์— API๋„ ๋ฌด๋ฃŒ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋ผ ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ChatGPT(์›น ์ฑ„ํŒ…)์™€ OpenAI API(๊ฐœ๋ฐœ์šฉ)๋Š” ๊ณผ๊ธˆ ์ฒด๊ณ„๊ฐ€ ์™„์ „ํžˆ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. API๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋ณ„๋„์˜ Credit(์„ ๋ถˆ ์ถฉ์ „๊ธˆ)์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ณ„์ • ์ƒ์„ฑ ์‹œ ์ œ๊ณต๋˜๋Š” ๋ฌด๋ฃŒ ํฌ๋ ˆ๋”ง์ด ๋งŒ๋ฃŒ๋˜์—ˆ๊ฑฐ๋‚˜ ์†Œ์ง„๋˜์–ด ์ž”์•ก์ด $0 ์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•œ ๋ฌธ์ œ์˜€์Šต๋‹ˆ๋‹ค.

โœ… ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
OpenAI Platform์˜ Billing Settings ํŽ˜์ด์ง€์—์„œ Credit์„ ์ถฉ์ „ํ–ˆ์Šต๋‹ˆ๋‹ค.

  1. ์นด๋“œ ๋“ฑ๋ก: ๊ฒฐ์ œ ์ˆ˜๋‹จ์„ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.
  2. ํฌ๋ ˆ๋”ง ์ถฉ์ „: ์ตœ์†Œ ๊ธˆ์•ก์ธ $5 (์•ฝ 7,500์›)๋ฅผ ์ถฉ์ „ํ–ˆ์Šต๋‹ˆ๋‹ค.
  3. ์„ค์ • ํŒ: Automatic recharge ์˜ต์…˜์„ OFF๋กœ ์„ค์ •ํ•˜์—ฌ, ์˜๋„์น˜ ์•Š์€ ์ž๋™ ๊ฒฐ์ œ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ณ  ๋”ฑ ์ถฉ์ „ํ•œ ๊ธˆ์•ก๋งŒํผ๋งŒ ์•ˆ์ „ํ•˜๊ฒŒ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

Issue 3. JSON ํŒŒ์‹ฑ ์‹คํŒจ์™€ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ (AI_INVALID_RESPONSE)

๐Ÿšจ ๋ฌธ์ œ ์ƒํ™ฉ
AI๊ฐ€ ์‘๋‹ต์„ ์ฃผ๊ธด ํ•˜๋Š”๋ฐ, ๊ฐ„ํ˜น ObjectMapper๊ฐ€ ์ด๋ฅผ DTO๋กœ ๋ณ€ํ™˜ํ•˜์ง€ ๋ชปํ•˜๊ณ  ์—๋Ÿฌ๋ฅผ ๋ฑ‰๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒ

๐Ÿ” ์›์ธ ๋ถ„์„
LLM(๊ฑฐ๋Œ€์–ธ์–ด๋ชจ๋ธ)์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ํ…์ŠคํŠธ ์ƒ์„ฑ๊ธฐ์ด๊ธฐ ๋•Œ๋ฌธ์—, JSON ํ˜•์‹์œผ๋กœ ๋‹ฌ๋ผ๊ณ  ์š”์ฒญํ•ด๋„ ์นœ์ ˆํ•˜๊ฒŒ(?) ๋งˆํฌ๋‹ค์šด ์ฝ”๋“œ ๋ธ”๋ก์„ ์”Œ์›Œ์„œ ์ฃผ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.

// AI๊ฐ€ ์‹ค์ œ๋กœ ์ค€ ์‘๋‹ต (Raw String)
{
   "score": { ... },
   "feedback": [ ... ]
}
```  <-- ๋’ค์— ๋ฐฑํ‹ฑ(```)์ด ๋ถ™์–ด์žˆ์Œ

Java์˜ ObjectMapper๋Š” ์ˆœ์ˆ˜ํ•œ JSON ๋ฌธ์ž์—ด๋งŒ ํŒŒ์‹ฑํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์•ž๋’ค์— ๋ถ™์€ \```json ํƒœ๊ทธ ๋•Œ๋ฌธ์— ํŒŒ์‹ฑ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ.

โœ… ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
AI์˜ ์‘๋‹ต(Raw String)์„ ํŒŒ์‹ฑํ•˜๊ธฐ ์ „์— ์ „์ฒ˜๋ฆฌ(Pre-processing) ๊ณผ์ •์„ ์ถ”๊ฐ€ํ•˜๊ณ , ํŒŒ์‹ฑ ์‹คํŒจ ์‹œ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ช…ํ™•ํ•œ ์—๋Ÿฌ๋ฅผ ์ „๋‹ฌํ•˜๋„๋ก ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ๊ฐ•ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.

1. ๋ฌธ์ž์—ด ์ „์ฒ˜๋ฆฌ (replace)


// AiClient.java
String cleanJson = aiResponseRaw
        .replace("```json", "")  // ๋งˆํฌ๋‹ค์šด ์‹œ์ž‘ ํƒœ๊ทธ ์ œ๊ฑฐ
        .replace("```", "")      // ๋งˆํฌ๋‹ค์šด ๋ ํƒœ๊ทธ ์ œ๊ฑฐ
        .trim();                 // ๊ณต๋ฐฑ ์ œ๊ฑฐ
        

2. ์ปค์Šคํ…€ ์˜ˆ์™ธ ๋˜์ง€๊ธฐ
ํŒŒ์‹ฑ ์ค‘ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์‹œ์Šคํ…œ ๋‚ด๋ถ€ ์—๋Ÿฌ(500)๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๋Œ€์‹ , ์ •์˜ํ•ด๋‘” ๋น„์ฆˆ๋‹ˆ์Šค ์˜ˆ์™ธ๋ฅผ ๋˜์ง‘๋‹ˆ๋‹ค.


try {
    return objectMapper.readValue(cleanJson, ResumeResponse.class);
} catch (Exception e) {
    log.error("AI ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: ", e);
    // "AI ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"๋ผ๋Š” ์˜๋ฏธ์˜ ์ปค์Šคํ…€ ์˜ˆ์™ธ
    throw new BusinessException(ErrorCode.AI_INVALID_RESPONSE);
}

3. ์ „์—ญ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ(GlobalExceptionHandler)
BusinessException์„ ์žก์•„์„œ ํ”„๋ก ํŠธ์—”๋“œ๊ฐ€ ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šด ํ‘œ์ค€ ํฌ๋งท์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.


// GlobalExceptionHandler.java
@ExceptionHandler(BusinessException.class)
public ApiResponse<?> handleBusinessException(BusinessException e) {
    return ApiResponse.error(e.getErrorCode().name(), e.getMessage());
}
profile
์œ ํŠœ๋ธŒ ๊ธฐ์ˆ  ์˜์ƒ์„ ๋ณด๋ฉด์„œ ์ž˜ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด... Lilys AI๋ฅผ ํ™œ์šฉํ•ด ๋ฐฐ๊ฒฝ์ง€์‹, ์˜์ƒ ์ „์ฒด ์š”์•ฝ ๋ฐ ํ•ต์‹ฌ ๋‚ด์šฉ ์„ค๋ช…๋“ค์„ ๋ธ”๋กœ๊น… ํ•ฉ๋‹ˆ๋‹ค. ์ž‘์„ฑํ•œ ๋‚ด์šฉ๋“ค์— ๋Œ€ํ•ด์„œ ์–ธ์ œ๊ณ  ๋‹ค์‹œ "๋‚ด๊ฐ€" ์ฐพ์•„ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ๊ธฐ๋ก์œผ๋กœ ๋‚จ๊น๋‹ˆ๋‹ค!

0๊ฐœ์˜ ๋Œ“๊ธ€