Skip to content

반환 로직 수정#355

Merged
unifolio0 merged 3 commits into
developfrom
feature/bedrock
May 17, 2026
Merged

반환 로직 수정#355
unifolio0 merged 3 commits into
developfrom
feature/bedrock

Conversation

@unifolio0

Copy link
Copy Markdown
Contributor

closed #

작업 내용

스크린샷

참고 사항

@unifolio0 unifolio0 self-assigned this May 17, 2026
@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Rate limit exceeded

@unifolio0 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 35 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9c0510a7-fe12-4c6d-bc1a-63b7093c51cb

📥 Commits

Reviewing files that changed from the base of the PR and between 2e0ad6d and a8dc560.

📒 Files selected for processing (7)
  • src/main/java/com/samhap/kokomen/global/external/bedrock/BedrockConverseClient.java
  • src/main/java/com/samhap/kokomen/global/external/bedrock/DocumentJsonConverter.java
  • src/main/java/com/samhap/kokomen/interview/external/ResumeBasedQuestionBedrockService.java
  • src/main/java/com/samhap/kokomen/interview/external/dto/response/BedrockConverseResponse.java
  • src/main/java/com/samhap/kokomen/interview/service/infra/InterviewProceedBedrockFlowAsyncService.java
  • src/main/java/com/samhap/kokomen/interview/tool/InterviewBedrockSystemMessageConstant.java
  • src/main/java/com/samhap/kokomen/resume/service/ResumeEvaluationAsyncService.java

워크스루

AWS Bedrock AgentRuntime의 invokeFlow 스트리밍 기반 호출 방식을 Bedrock Converse API의 동기 요청-응답 패턴으로 전환. Converse 클라이언트 및 요청/응답 처리 인프라 추가, 인터뷰/이력서 도메인별 클라이언트 추가, 기존 invokeFlow 로직 제거 및 서비스 통합.

변경 사항

Bedrock Converse API 마이그레이션

레이어 / 파일(들) 요약
Bedrock Converse 기초 인프라
src/main/java/com/samhap/kokomen/global/config/AwsConfig.java, src/main/java/com/samhap/kokomen/global/external/bedrock/BedrockConverseClient.java, src/main/java/com/samhap/kokomen/global/external/bedrock/BedrockConverseProperties.java, src/main/java/com/samhap/kokomen/global/external/bedrock/DocumentJsonConverter.java, build.gradle, gradle/gradle-daemon-jvm.properties
BedrockConverseClient가 Bedrock Converse API 호출, 응답에서 toolUse 블록 추출, Document를 자바 객체로 변환 처리 제공. DocumentJsonConverter는 AWS Document를 재귀적으로 자바 자료구조로 변환. BedrockConverseProperties는 aws.bedrock 설정값 바인딩. AwsConfig에서 socketTimeout을 15초에서 60초로 조정 및 설정 프로퍼티 활성화.
인터뷰 Bedrock 클라이언트 및 요청 구조
src/main/java/com/samhap/kokomen/interview/external/InterviewProceedBedrockClient.java, src/main/java/com/samhap/kokomen/interview/external/AnswerFeedbackBedrockClient.java, src/main/java/com/samhap/kokomen/interview/external/dto/request/InterviewBedrockRequestFactory.java, src/main/java/com/samhap/kokomen/interview/external/dto/response/BedrockConverseResponse.java, src/main/java/com/samhap/kokomen/interview/external/dto/response/AnswerFeedbackOnlyResponse.java
InterviewProceedBedrockClient는 진행/종료 분기로 시스템/메시지/툴 설정을 적용해 Bedrock 호출 후 BedrockConverseResponse 반환. AnswerFeedbackBedrockClient는 답변 피드백 요청 처리. InterviewBedrockRequestFactory가 3가지 시나리오별 시스템 프롬프트, 메시지, 툴 설정 생성. BedrockConverseResponse는 Document toolInput을 저장하고 LlmResponse 추출 메서드 구현.
인터뷰 서비스 Converse 통합
src/main/java/com/samhap/kokomen/interview/service/infra/InterviewProceedBedrockFlowAsyncService.java, src/main/java/com/samhap/kokomen/interview/external/ResumeBasedQuestionBedrockService.java
InterviewProceedBedrockFlowAsyncService가 invokeFlow 스트림/콜백 구조 제거하고 Bedrock 클라이언트 직접 호출로 변경. processBedrockProceed 메서드로 Bedrock 응답 처리, TTS 생성, 피드백 요청을 순차 처리하며 TTS_FAILED 상태 확인으로 완료 덮어쓰기 방지. ResumeBasedQuestionBedrockService는 Bedrock 호출을 ResumeBasedQuestionBedrockClient 위임으로 단순화.
이력서 평가 Bedrock 클라이언트 및 요청 구조
src/main/java/com/samhap/kokomen/resume/external/ResumeBasedQuestionBedrockClient.java, src/main/java/com/samhap/kokomen/resume/external/ResumeEvaluationBedrockClient.java, src/main/java/com/samhap/kokomen/resume/external/dto/ResumeBedrockRequestFactory.java, src/main/java/com/samhap/kokomen/resume/external/dto/ResumeBedrockSystemMessageConstant.java
ResumeBasedQuestionBedrockClient는 이력서/포트폴리오/직무경력 입력받아 질문 생성 반환. ResumeEvaluationBedrockClient는 평가 요청 처리. ResumeBedrockRequestFactory가 질문생성/평가 시나리오별 시스템 프롬프트(XML 형식 메시지 포함), 툴 설정 생성. 프롬프트 상수 제공.
이력서 평가 서비스 통합 및 InvokeFlow 제거
src/main/java/com/samhap/kokomen/resume/service/ResumeEvaluationAsyncService.java
ResumeEvaluationAsyncService가 invokeFlow 스트림 구조 제거하고 ResumeEvaluationBedrockClient 단일 호출로 변경. 멤버/비회원 평가 모두 executor에서 Bedrock 직접 호출, 성공 시 저장, 예외 시 GPT 폴백 실행. parseGptResponse로 GPT 문맥 로그 처리.
인터뷰 시스템 메시지 및 팩토리 정리
src/main/java/com/samhap/kokomen/interview/tool/InterviewBedrockSystemMessageConstant.java, src/main/java/com/samhap/kokomen/interview/tool/InterviewMessagesFactory.java, src/main/java/com/samhap/kokomen/interview/external/dto/request/InterviewInvokeFlowRequestFactory.java(삭제)
InterviewBedrockSystemMessageConstant가 3개 시스템 프롬프트 상수 제공. InterviewMessagesFactory에서 Bedrock 메서드 제거 후 GPT 경로만 유지. 기존 InvokeFlowRequestFactory 완전 제거.
설정 및 테스트 인프라
src/main/resources/application.yml, src/test/java/com/samhap/kokomen/global/BaseTest.java, src/test/java/com/samhap/kokomen/global/fixture/interview/BedrockResponseFixtureBuilder.java
application.yml에 aws.bedrock 설정 블록 추가. BaseTest에서 BedrockAgentRuntimeAsyncClient mock 제거 후 3개 새로운 Bedrock 클라이언트 mock 추가. BedrockResponseFixtureBuilder에서 문자열 JSON을 Document 기반 BedrockConverseResponse로 변경.

시퀀스 다이어그램

sequenceDiagram
  participant InterviewService
  participant ProceedBedrockClient
  participant FeedbackBedrockClient
  participant ConverseClient
  participant Bedrock
  InterviewService->>ProceedBedrockClient: requestToBedrock(qa)
  ProceedBedrockClient->>ConverseClient: converse(system, messages, tools, maxTokens)
  ConverseClient->>Bedrock: ConverseRequest
  Bedrock-->>ConverseClient: ConverseResponse(stopReason=TOOL_USE, output=[toolUse])
  ConverseClient->>ConverseClient: extractToolUse(response, toolName)
  ConverseClient-->>ProceedBedrockClient: ConverseResponse with toolInput
  ProceedBedrockClient-->>InterviewService: BedrockConverseResponse
  InterviewService->>FeedbackBedrockClient: requestAnswerFeedback(qa, rank)
  FeedbackBedrockClient->>ConverseClient: converse(system, messages, tools, maxTokens)
  ConverseClient->>Bedrock: ConverseRequest
  Bedrock-->>ConverseClient: ConverseResponse
  ConverseClient-->>FeedbackBedrockClient: ConverseResponse with toolInput
  FeedbackBedrockClient-->>InterviewService: feedback String
Loading

예상 코드 리뷰 노력

🎯 4 (Complex) | ⏱️ ~60분

관련 PR

  • samhap-soft/kokomen-backend#325: BedrockConverseClient 및 관련 클라이언트들이 직접 지원하는 이력서 기반 질문 생성/평가 흐름에서 기존 Bedrock 레거시 클라이언트 제거 및 리팩터링.
  • samhap-soft/kokomen-backend#322: ResumeBasedQuestionBedrockService의 invokeFlow 호출 방식 변경 및 Flow ID/Alias 관련 동작 리팩터링과 연관.

추천 리뷰어

  • nak-honest
  • kargowild

🐰 Converse를 타고 흐르는 Bedrock의 물결,
문서는 자바로 변신하고, 스트림은 역사가 되네.
진행과 피드백, 이력서 평가까지
새로운 흐름에 담긴 설계의 아름다움.
스트리밍 무너지고 동기의 시대 온다! 🚀

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning PR 제목이 변경사항의 핵심 내용과 맞지 않습니다. '반환 로직 수정'은 실제 주요 변경인 'Bedrock Flow에서 Converse로의 교체' 및 광범위한 아키텍처 리팩토링을 제대로 반영하지 못합니다. PR 제목을 실제 변경사항을 더 정확히 반영하도록 수정하세요. 예: 'Bedrock Flow에서 Converse API로 리팩토링' 또는 'Bedrock 호출 방식 개선: Flow에서 Converse로 변경'
Description check ⚠️ Warning PR 설명이 기본 템플릿 형식만 포함되어 있으며 실질적인 내용이 없습니다. 작업 내용, 스크린샷, 참고 사항 섹션이 모두 비어 있어 변경사항을 파악할 수 없습니다. PR 설명에 변경의 주요 목적, 영향 범위, 테스트 결과 등 구체적인 정보를 작성하세요. 커밋 메시지와 코드 변경사항을 고려하여 설명을 작성해야 합니다.
Docstring Coverage ⚠️ Warning Docstring coverage is 1.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/bedrock

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 기존의 Bedrock Flow API를 사용하던 면접 및 이력서 평가 로직을 Bedrock Converse API로 마이그레이션하는 작업을 포함합니다. 이를 통해 모델과의 직접적인 통신을 최적화하고, 연동 로직을 서비스별로 모듈화하여 유지보수성을 크게 향상시켰습니다. 또한, 설정 파일과 유틸리티 클래스를 추가하여 향후 모델 변경이나 설정 조정에 유연하게 대응할 수 있도록 구조를 개선했습니다.

Highlights

  • Bedrock Converse API 마이그레이션: 기존의 Bedrock Flow API 기반 연동 방식을 Bedrock Converse API로 전면 교체하여 모델 상호작용의 안정성과 제어력을 높였습니다.
  • 인프라 및 로직 모듈화: BedrockConverseClient, InterviewProceedBedrockClient 등 신규 클라이언트와 팩토리 클래스를 도입하여 Bedrock 연동 로직을 체계적으로 분리했습니다.
  • 설정 및 유틸리티 개선: BedrockConverseProperties를 통해 설정을 중앙화하고, DocumentJsonConverter를 추가하여 AWS SDK Document 타입 처리를 간소화했습니다.
  • 레거시 코드 정리: 더 이상 사용하지 않는 Flow 기반의 팩토리 클래스, DTO, 그리고 관련 의존성을 제거하여 코드베이스를 최적화했습니다.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.


Bedrock 흐름 떠나 Converse로 새 길 찾아 면접 더 밝게

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request migrates the application from using AWS Bedrock Agents (Flows) to the AWS Bedrock Converse API. It introduces several new client classes, such as BedrockConverseClient, InterviewProceedBedrockClient, and ResumeEvaluationBedrockClient, to handle interactions with the Converse API and tool-use features. The build configuration was updated to remove the bedrockagentruntime dependency, and configuration properties were added for model parameters. Review feedback highlighted potential NullPointerException risks in BedrockConverseClient and InterviewProceedBedrockFlowAsyncService when accessing nested response objects, as well as an opportunity to deduplicate JSON mapping logic by moving it to a shared utility.

"Bedrock 응답이 tool_use가 아닙니다. stopReason=" + response.stopReason()
+ ", expected=" + expectedToolName);
}
return response.output().message().content().stream()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

response.output().message().content()를 호출할 때 중간 객체(output, message)가 null일 경우 NullPointerException이 발생할 수 있습니다. stopReasonTOOL_USE이더라도 방어적인 코드를 위해 null 체크를 추가하는 것이 안전합니다.

Comment on lines +32 to +39
private static <T> T mapTo(Document document, Class<T> type, ObjectMapper objectMapper) {
try {
Object javaObject = DocumentJsonConverter.toJavaObject(document);
return objectMapper.convertValue(javaObject, type);
} catch (Exception e) {
throw new ExternalApiException("Bedrock toolUse 파싱 실패: input=" + document, e);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

mapTo 메서드의 로직은 BedrockConverseClient.parseToolInput에서도 동일하게 사용되고 있습니다. 이 로직을 DocumentJsonConverter와 같은 유틸리티 클래스로 추출하여 중복을 제거하고 유지보수성을 높이는 것을 권장합니다.

Comment on lines +112 to +115
if (result.isInProgress()) {
requestAndSaveAnswerFeedbackAsync(questionAndAnswers, mdcContext,
result.getCurAnswer().getAnswerRank(), result.getCurAnswer().getId());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

result.getCurAnswer()가 null일 경우 getAnswerRank() 호출 시 NullPointerException이 발생할 수 있습니다. result.isInProgress() 체크와 함께 result.getCurAnswer()에 대한 null 체크를 추가하는 것이 안전합니다.

Suggested change
if (result.isInProgress()) {
requestAndSaveAnswerFeedbackAsync(questionAndAnswers, mdcContext,
result.getCurAnswer().getAnswerRank(), result.getCurAnswer().getId());
}
if (result.isInProgress() && result.getCurAnswer() != null) {
requestAndSaveAnswerFeedbackAsync(questionAndAnswers, mdcContext,
result.getCurAnswer().getAnswerRank(), result.getCurAnswer().getId());
}

@github-actions

github-actions Bot commented May 17, 2026

Copy link
Copy Markdown

Test Results

 51 files   51 suites   1m 32s ⏱️
291 tests 290 ✅ 1 💤 0 ❌
293 runs  292 ✅ 1 💤 0 ❌

Results for commit a8dc560.

♻️ This comment has been updated with latest results.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/samhap/kokomen/global/external/bedrock/BedrockConverseClient.java`:
- Around line 71-77: The catch block in parseToolInput(ToolUseBlock toolUse,
Class<T> type) currently embeds the full toolUse.input() into the
ExternalApiException message which may expose sensitive data; change the thrown
ExternalApiException to omit the raw input and instead include only a
non-sensitive identifier (e.g., toolUse.name() or toolUse.id() if available) and
the original exception as the cause (keep throw new ExternalApiException(...,
e)); ensure DocumentJsonConverter and objectMapper usage is unchanged and only
the exception message is sanitized.

In
`@src/main/java/com/samhap/kokomen/global/external/bedrock/BedrockConverseProperties.java`:
- Around line 5-14: Add bean validation to the BedrockConverseProperties record
so invalid config fails at startup: annotate the record with `@Validated` and add
jakarta.validation constraints to fields — mark modelId as `@NotBlank`, mark
proceedMaxTokens, endMaxTokens, answerFeedbackMaxTokens,
resumeQuestionMaxTokens, resumeEvaluationMaxTokens as `@NotNull` and
`@PositiveOrZero` (or `@Min`(0)), and constrain temperature with `@NotNull` and
`@DecimalMin`("0.0") `@DecimalMax`("1.0"); ensure imports use jakarta.validation.*
and that the class remains annotated with `@ConfigurationProperties`(prefix =
"aws.bedrock") so Spring validates the bound values on startup.

In
`@src/main/java/com/samhap/kokomen/global/external/bedrock/DocumentJsonConverter.java`:
- Line 35: In DocumentJsonConverter replace the plain IllegalStateException
thrown for unknown Document types with the project's custom exception (e.g.,
ExternalApiException) so exception hierarchy stays consistent; update the throw
in the method inside class DocumentJsonConverter to construct and throw
ExternalApiException (preserving the current message "알 수 없는 Document 타입입니다: " +
document and, if available, include the original cause or document details)
instead of IllegalStateException.

In
`@src/main/java/com/samhap/kokomen/interview/external/dto/response/BedrockConverseResponse.java`:
- Line 37: The throw in BedrockConverseResponse currently includes the raw
`document` (toolInput) in the ExternalApiException message which may expose
sensitive interview content; change the code to avoid embedding the full
`document` string—either log a redacted/trimmed form, its length, or a hash/ID
for diagnostics and include that in the ExternalApiException message instead
(keep the original exception `e` as the cause) and ensure any debug logging that
records `document` is likewise redacted; update the throw site that constructs
ExternalApiException to use the sanitized token rather than the raw `document`.

In
`@src/main/java/com/samhap/kokomen/interview/external/ResumeBasedQuestionBedrockService.java`:
- Line 59: The error log currently writes the full LLM response variable
jsonResponse on parse failures in ResumeBasedQuestionBedrockService; remove
sensitive content from logs by not including jsonResponse (or any raw
resume/portfolio text) in log.error calls inside the parsing error handler
(e.g., the block where log.error("GPT 질문 응답 파싱 실패: {}", jsonResponse, e) is
called). Instead log a non-sensitive message plus the exception (and if needed a
safe identifier such as a truncated hash or boolean flag) so the code in the
method that parses the LLM response no longer emits raw jsonResponse to logs.

In
`@src/main/java/com/samhap/kokomen/interview/service/infra/InterviewProceedBedrockFlowAsyncService.java`:
- Around line 206-217: The executor.execute(...) call can throw (e.g.,
RejectedExecutionException) and propagate up to processBedrockProceed causing
the whole flow to fail; wrap the executor.execute submission in a try-catch
around the call itself (not just inside the runnable) to catch submission
failures, log the rejection with curAnswerId and mdcContext, and avoid
rethrowing so Bedrock success isn't treated as overall failure—keep the existing
inner runnable handling for runtime errors (setMdcContext,
answerFeedbackBedrockClient.requestAnswerFeedback,
interviewProceedService.saveAnswerFeedback) but ensure executor.execute errors
are handled locally to prevent LLM_FAILED/GPT fallback from being triggered by
task submission failures.
- Around line 72-74: Wrap the executor.execute(...) call in a try/catch for
java.util.concurrent.RejectedExecutionException and in the catch release the
interview lock using the existing lock release mechanism (invoke the same logic
that frees lockKey with lockValue and updates interviewProceedStateKey / related
state) so the lock is always cleared if the task cannot be scheduled; reference
symbols: executor.execute, processBedrockProceed, lockKey, lockValue,
interviewProceedStateKey, RejectedExecutionException.

In
`@src/main/java/com/samhap/kokomen/interview/tool/InterviewBedrockSystemMessageConstant.java`:
- Line 68: The end/termination prompt in InterviewBedrockSystemMessageConstant
currently uses a different rank mapping ("랭크 매핑 : A(4-6점) / B(3점) / C(2점) /
D(1점) / F(0점") than the 진행 프롬프트, causing inconsistent scoring; open the
InterviewBedrockSystemMessageConstant class, find the end/termination prompt
string literal (the constant holding the 종료/종료 프롬프트 text) and change its rank
mapping to match the 진행 프롬프트 mapping exactly (copy the same A/B/C/D/F ranges
from the 진행 프롬프트 constant or variable) so both prompts use identical
score-to-rank rules.

In
`@src/main/java/com/samhap/kokomen/resume/external/dto/ResumeBedrockRequestFactory.java`:
- Around line 31-47: In createQuestionGenerationMessages (and the other similar
methods around lines 88-114) you are inserting raw user input into an XML-like
template which can break tag boundaries; fix this by escaping XML special
characters before formatting: add or use a helper (e.g., escapeXml or xmlEscape)
and call it on resumeText, portfolioText, and jobCareer (instead of passing
nullToEmpty(...) directly), ensuring the formatted userText uses the escaped
values; update any other message-creation methods in ResumeBedrockRequestFactory
to apply the same escape function to their inputs.

In
`@src/main/java/com/samhap/kokomen/resume/service/ResumeEvaluationAsyncService.java`:
- Line 289: The current log in ResumeEvaluationAsyncService (log.error("GPT 이력서
평가 응답 파싱 실패: {}", jsonResponse, e)) must not print the full GPT response; change
it to log only non-sensitive metadata (e.g., response length via
jsonResponse.length(), a deterministic short fingerprint/hash of jsonResponse
such as SHA-256 truncated to 8-12 hex chars, and/or an internal correlation id)
along with the exception e and a clear message; remove or redact the raw
jsonResponse from the error message while keeping the exception stacktrace for
troubleshooting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 97956e12-800f-426f-b3b5-0c36c81cc991

📥 Commits

Reviewing files that changed from the base of the PR and between 20d3f56 and 2e0ad6d.

📒 Files selected for processing (26)
  • build.gradle
  • gradle/gradle-daemon-jvm.properties
  • src/main/java/com/samhap/kokomen/global/config/AwsConfig.java
  • src/main/java/com/samhap/kokomen/global/external/bedrock/BedrockConverseClient.java
  • src/main/java/com/samhap/kokomen/global/external/bedrock/BedrockConverseProperties.java
  • src/main/java/com/samhap/kokomen/global/external/bedrock/DocumentJsonConverter.java
  • src/main/java/com/samhap/kokomen/interview/external/AnswerFeedbackBedrockClient.java
  • src/main/java/com/samhap/kokomen/interview/external/InterviewProceedBedrockClient.java
  • src/main/java/com/samhap/kokomen/interview/external/ResumeBasedQuestionBedrockService.java
  • src/main/java/com/samhap/kokomen/interview/external/dto/request/InterviewBedrockRequestFactory.java
  • src/main/java/com/samhap/kokomen/interview/external/dto/request/InterviewInvokeFlowRequestFactory.java
  • src/main/java/com/samhap/kokomen/interview/external/dto/response/AnswerFeedbackOnlyResponse.java
  • src/main/java/com/samhap/kokomen/interview/external/dto/response/BedrockConverseResponse.java
  • src/main/java/com/samhap/kokomen/interview/external/dto/response/BedrockResponse.java
  • src/main/java/com/samhap/kokomen/interview/service/infra/InterviewProceedBedrockFlowAsyncService.java
  • src/main/java/com/samhap/kokomen/interview/tool/InterviewBedrockSystemMessageConstant.java
  • src/main/java/com/samhap/kokomen/interview/tool/InterviewMessagesFactory.java
  • src/main/java/com/samhap/kokomen/resume/external/ResumeBasedQuestionBedrockClient.java
  • src/main/java/com/samhap/kokomen/resume/external/ResumeEvaluationBedrockClient.java
  • src/main/java/com/samhap/kokomen/resume/external/ResumeInvokeFlowRequestFactory.java
  • src/main/java/com/samhap/kokomen/resume/external/dto/ResumeBedrockRequestFactory.java
  • src/main/java/com/samhap/kokomen/resume/external/dto/ResumeBedrockSystemMessageConstant.java
  • src/main/java/com/samhap/kokomen/resume/service/ResumeEvaluationAsyncService.java
  • src/main/resources/application.yml
  • src/test/java/com/samhap/kokomen/global/BaseTest.java
  • src/test/java/com/samhap/kokomen/global/fixture/interview/BedrockResponseFixtureBuilder.java
💤 Files with no reviewable changes (5)
  • build.gradle
  • src/main/java/com/samhap/kokomen/resume/external/ResumeInvokeFlowRequestFactory.java
  • src/main/java/com/samhap/kokomen/interview/external/dto/request/InterviewInvokeFlowRequestFactory.java
  • src/main/java/com/samhap/kokomen/interview/external/dto/response/BedrockResponse.java
  • src/main/java/com/samhap/kokomen/interview/tool/InterviewMessagesFactory.java

Comment on lines +5 to +14
@ConfigurationProperties(prefix = "aws.bedrock")
public record BedrockConverseProperties(
String modelId,
Integer proceedMaxTokens,
Integer endMaxTokens,
Integer answerFeedbackMaxTokens,
Integer resumeQuestionMaxTokens,
Integer resumeEvaluationMaxTokens,
Float temperature
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# validation 관련 의존성/사용 여부를 읽기 전용으로 확인
fd -i "build.gradle*" | xargs -I{} rg -n "spring-boot-starter-validation|jakarta.validation|hibernate-validator" {}
rg -n "`@ConfigurationProperties`|`@Validated`|jakarta.validation.constraints"

Repository: samhap-soft/kokomen-backend

Length of output: 3433


🏁 Script executed:

# Search for other `@ConfigurationProperties` usage to see if they use validation
rg -A 10 "`@ConfigurationProperties`" --type java | head -60

Repository: samhap-soft/kokomen-backend

Length of output: 1374


🏁 Script executed:

cat -n src/main/java/com/samhap/kokomen/global/external/bedrock/BedrockConverseProperties.java

Repository: samhap-soft/kokomen-backend

Length of output: 641


Bedrock 설정값에 바인딩 단계 검증을 추가하세요.

현재는 modelId 누락, 토큰 음수, temperature 범위 오류가 런타임 호출 시점까지 지연됩니다. 시작 단계에서 실패하도록 제약을 넣는 게 안전합니다. 프로젝트에 spring-boot-starter-validation 의존성이 있으며, 다른 DTO 클래스들에서도 jakarta.validation.constraints를 활발하게 사용하고 있습니다.

🔧 제안 수정안
 import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Positive;
 
+@Validated
 `@ConfigurationProperties`(prefix = "aws.bedrock")
 public record BedrockConverseProperties(
-        String modelId,
-        Integer proceedMaxTokens,
-        Integer endMaxTokens,
-        Integer answerFeedbackMaxTokens,
-        Integer resumeQuestionMaxTokens,
-        Integer resumeEvaluationMaxTokens,
-        Float temperature
+        `@NotBlank` String modelId,
+        `@NotNull` `@Positive` Integer proceedMaxTokens,
+        `@NotNull` `@Positive` Integer endMaxTokens,
+        `@NotNull` `@Positive` Integer answerFeedbackMaxTokens,
+        `@NotNull` `@Positive` Integer resumeQuestionMaxTokens,
+        `@NotNull` `@Positive` Integer resumeEvaluationMaxTokens,
+        `@NotNull` `@Min`(0) `@Max`(1) Float temperature
 ) {
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/samhap/kokomen/global/external/bedrock/BedrockConverseProperties.java`
around lines 5 - 14, Add bean validation to the BedrockConverseProperties record
so invalid config fails at startup: annotate the record with `@Validated` and add
jakarta.validation constraints to fields — mark modelId as `@NotBlank`, mark
proceedMaxTokens, endMaxTokens, answerFeedbackMaxTokens,
resumeQuestionMaxTokens, resumeEvaluationMaxTokens as `@NotNull` and
`@PositiveOrZero` (or `@Min`(0)), and constrain temperature with `@NotNull` and
`@DecimalMin`("0.0") `@DecimalMax`("1.0"); ensure imports use jakarta.validation.*
and that the class remains annotated with `@ConfigurationProperties`(prefix =
"aws.bedrock") so Spring validates the bound values on startup.

Comment on lines +31 to +47
public static List<Message> createQuestionGenerationMessages(String resumeText, String portfolioText, String jobCareer) {
String userText = """
<resume>
%s
</resume>

<portfolio>
%s
</portfolio>

<job_career>
%s
</job_career>
""".formatted(
nullToEmpty(resumeText),
nullToEmpty(portfolioText),
nullToEmpty(jobCareer));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

사용자 입력 XML 이스케이프 처리 필요

사용자 입력을 XML 유사 포맷에 그대로 삽입하고 있어 태그 경계가 깨지거나 프롬프트 구조가 오염될 수 있습니다. 입력값은 최소한 XML 특수문자를 이스케이프한 뒤 삽입해 주세요.

수정 예시
@@
-        String userText = """
+        String userText = """
                 <resume>
                 %s
                 </resume>
@@
-                nullToEmpty(resumeText),
-                nullToEmpty(portfolioText),
-                nullToEmpty(jobCareer));
+                escapeXml(nullToEmpty(resumeText)),
+                escapeXml(nullToEmpty(portfolioText)),
+                escapeXml(nullToEmpty(jobCareer)));
@@
-        String userText = """
+        String userText = """
                 <resume>
                 %s
                 </resume>
@@
-                nullToEmpty(request.resume()),
-                nullToEmpty(request.portfolio()),
-                nullToEmpty(request.jobPosition()),
-                nullToEmpty(request.jobDescription()),
-                nullToEmpty(request.jobCareer()));
+                escapeXml(nullToEmpty(request.resume())),
+                escapeXml(nullToEmpty(request.portfolio())),
+                escapeXml(nullToEmpty(request.jobPosition())),
+                escapeXml(nullToEmpty(request.jobDescription())),
+                escapeXml(nullToEmpty(request.jobCareer())));
@@
     private static String nullToEmpty(String value) {
         return value != null ? value : "";
     }
+
+    private static String escapeXml(String value) {
+        return value
+                .replace("&", "&amp;")
+                .replace("<", "&lt;")
+                .replace(">", "&gt;")
+                .replace("\"", "&quot;")
+                .replace("'", "&apos;");
+    }
 }

Also applies to: 88-114

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/samhap/kokomen/resume/external/dto/ResumeBedrockRequestFactory.java`
around lines 31 - 47, In createQuestionGenerationMessages (and the other similar
methods around lines 88-114) you are inserting raw user input into an XML-like
template which can break tag boundaries; fix this by escaping XML special
characters before formatting: add or use a helper (e.g., escapeXml or xmlEscape)
and call it on resumeText, portfolioText, and jobCareer (instead of passing
nullToEmpty(...) directly), ensuring the formatted userText uses the escaped
values; update any other message-creation methods in ResumeBedrockRequestFactory
to apply the same escape function to their inputs.

Comment thread src/main/java/com/samhap/kokomen/resume/service/ResumeEvaluationAsyncService.java Outdated
@unifolio0 unifolio0 merged commit 013adee into develop May 17, 2026
4 checks passed
@unifolio0 unifolio0 deleted the feature/bedrock branch May 17, 2026 07:36
unifolio0 added a commit that referenced this pull request Jun 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant