diff --git a/docs/fewshot-reviewed-loader.md b/docs/fewshot-reviewed-loader.md index 6b6d4b45..55ab265f 100644 --- a/docs/fewshot-reviewed-loader.md +++ b/docs/fewshot-reviewed-loader.md @@ -10,7 +10,7 @@ - jobTitle을 보존하고, 없는 기존 CSV만 jobCategorySmall로 대체합니다. - 우대사항을 예시 프롬프트에 포함합니다. - approvedAnalysisJson은 단일 JSON 객체여야 합니다. 잘못된 행만 제외하고 다음 행을 읽습니다. - 이 검사는 JSON 문법과 최상위 객체 검사이며 분석 내용의 의미 검증은 아닙니다. + 검수 CSV와 JSON 리소스 모두 아래 부분 분석 계약을 검증합니다. 분석 의미의 최종 승인은 PM에게 있습니다. - 활성·승인·caseId·문항·비식별 답변 검증을 유지합니다. ## 로컬 평가 설정 @@ -40,3 +40,50 @@ analysis.few-shot.reviewed-evaluation-csv-path=/absolute/path/fewshot_candidates `./gradlew test --tests '*fewshot.*'`: 27개 성공. 선택 서비스의 기존 캐시·입력 제외 회귀 테스트와 CSV 로더의 선택 JD 필드, 직무명 호환, 우대사항 보존, 잘못된 JSON 행 이후 정상 행 적재를 확인했습니다. + +## 2026-09-15 승인 데이터 연결 및 검증 보강 + +- 기존 변환본 `fewshot-pm-reviewed-20260914-v2`의 FS-02·03·05·08·09, 총 5개를 + `analysis/fewshot/reviewed-fewshot-cases-pm-20260914-v2.json`에 반영했습니다. + 새 승인을 만들어낸 것이 아니라 기존 승인·비식별 통과 데이터만 연결했습니다. + 동일한 데이터이므로 버전은 v2를 유지합니다. 후속 데이터 변경 시 새 버전을 사용합니다. +- PM 우선순위 1이 최상위라는 확인은 변환본의 `10-rank` 값으로 이미 반영되어 있습니다. + 검색기는 큰 priority를 우선하므로 추가로 역전하지 않습니다. +- 기존 점수는 예시에 넣지 않았으며 보류 사례·문장도 다시 활성화하지 않았습니다. +- 데이터 및 프롬프트의 이메일·국내 휴대전화·HTTP URL 패턴 검사에서 일치 항목이 없었습니다. + 이는 이름·소속·경험 조합을 통한 재식별 위험까지 보장하는 개인정보 정책 검증은 아닙니다. + 원본 승인 범위는 유지하며 5번 이슈에서 정책을 별도로 확정해야 합니다. + +### 부분 분석 계약 + +- `keyStrengths`, `missingKeywords`, `questionAnalyses`: 필수 배열. 빈 배열 허용. +- 강점 항목: 비어 있지 않은 문자열 `title`, `quote`. +- 누락 항목: 비어 있지 않은 `keyword`, 출처는 `mainTask|qualification|preference`. +- 문장 항목: 양의 정수 `questionId`, 비어 있지 않은 `sentence`, `reason`, + `status=proven|mentioned|fabricated`, `improvement`는 null 또는 비어 있지 않은 문자열. +- 점수·feedback은 부분 Few-shot 계약의 필수 필드가 아닙니다. +- CSV의 필수 헤더, 중복 헤더, 열 개수, 닫히지 않은 따옴표도 검사합니다. + 구조상 행 경계를 신뢰할 수 없는 CSV는 파일 단위로 제외합니다. +- JSON 리소스 내부 source로 승인 데이터 검증을 우회할 수 없도록 로딩 경로의 source를 적용합니다. +- 검증 통과 후 ID와 정규화 입력을 등록합니다. 같은 입력의 첫 유효 후보만 유지합니다. + 기존 검색 단계와 동일한 NFKC·공백·대소문자 정규화 및 SHA-256을 재사용합니다. + 입력 지문은 기존 호환성을 위해 주요 업무·자격요건·문항·답변을 기준으로 하며 + 우대사항만 다른 입력은 같은 입력으로 보수적으로 취급합니다. +- 파싱 오류 로그에는 답변이 포함될 수 있는 예외 메시지를 남기지 않습니다. + +### 평가 프로필 + +`analysis-eval,fewshot-pm-eval`을 명시적으로 함께 선택하면 검수 후보 5개만 사용합니다. +이 전용 프로필은 `analysis.mode=single-pass`로 지정합니다. two-pass에는 Few-shot이 적용되지 않습니다. +운영 기본 프로필이나 기본 feature flag는 변경하지 않았습니다. + +STATIC 비교 시 동일 프로필에서 `analysis.few-shot.dynamic-selection-enabled=false`만 덮어씁니다. +STATIC 또는 동적 실패 시 기존 정적 전체 블록이 사용되므로, 사용 후보 추적과 fallback 구분은 +후속 2번 메타데이터 기록 작업이 완료된 뒤 품질 평가에서 확인해야 합니다. +holdout은 후보 원본 및 단순 비식별 변형과 분리하세요. 해시 검사는 유사문·비식별 전후 변형까지 +탐지하지 못하므로 데이터 분할 검토를 대체하지 않습니다. + +이번 작업에서는 외부 AI 호출, 운영 활성화, 실제 품질 비교 평가를 수행하지 않았습니다. + +검증: `./gradlew test --tests '*fewshot.*' --tests '*AnalysisPromptBuilderTest' --tests '*FewShotPromptProviderTest' --tests '*Evaluation*'` +149개 통과(실패·건너뜀 0). `git diff --check` 통과. diff --git a/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/DefaultFewShotSearchService.java b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/DefaultFewShotSearchService.java index 2d548864..13a5a441 100644 --- a/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/DefaultFewShotSearchService.java +++ b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/DefaultFewShotSearchService.java @@ -8,7 +8,6 @@ import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.text.Normalizer; import java.time.Instant; import java.util.ArrayList; import java.util.Comparator; @@ -33,7 +32,6 @@ public class DefaultFewShotSearchService implements FewShotSearchService { private static final long QUERY_EMBEDDING_CACHE_CLEANUP_INTERVAL_MILLIS = 60_000L; private static final long DEFAULT_QUERY_EMBEDDING_IN_FLIGHT_WAIT_TIMEOUT_MILLIS = 20_000L; private static final Pattern TOKEN_SPLIT_PATTERN = Pattern.compile("[^\\p{IsAlphabetic}\\p{IsDigit}가-힣]+"); - private static final Pattern NORMALIZED_INPUT_WHITESPACE_PATTERN = Pattern.compile("[\\p{Z}\\s]+"); private final FewShotCaseStore caseStore; private final FewShotSearchTextBuilder textBuilder; @@ -383,7 +381,7 @@ private void initializeMissingDocumentEmbeddings(List private List localPrefilter(List activeCases, FewShotSearchQuery query) { int limit = Math.max(1, properties.getSearch().getCandidateLimit()); - String queryInputHash = normalizedInputHash( + String queryInputHash = FewShotInputHash.of( query.mainTasks(), query.qualifications(), query.question(), @@ -509,7 +507,7 @@ private static boolean sameNormalizedInput(String queryInputHash, FewShotCase fe if (!StringUtils.hasText(queryInputHash)) { return false; } - String candidateInputHash = normalizedInputHash( + String candidateInputHash = FewShotInputHash.of( fewShotCase.mainTasks(), fewShotCase.qualifications(), fewShotCase.question(), @@ -518,42 +516,6 @@ private static boolean sameNormalizedInput(String queryInputHash, FewShotCase fe return queryInputHash.equals(candidateInputHash); } - private static String normalizedInputHash( - List mainTasks, - List qualifications, - String question, - String answer - ) { - String normalizedMainTasks = normalizeInputSection(mainTasks == null ? "" : String.join("\n", mainTasks)); - String normalizedQualifications = normalizeInputSection( - qualifications == null ? "" : String.join("\n", qualifications) - ); - String normalizedQuestion = normalizeInputSection(question); - String normalizedAnswer = normalizeInputSection(answer); - if (normalizedMainTasks.isEmpty() - && normalizedQualifications.isEmpty() - && normalizedQuestion.isEmpty() - && normalizedAnswer.isEmpty()) { - return ""; - } - return sha256( - normalizedMainTasks + '\u001f' - + normalizedQualifications + '\u001f' - + normalizedQuestion + '\u001f' - + normalizedAnswer - ); - } - - private static String normalizeInputSection(String value) { - if (!StringUtils.hasText(value)) { - return ""; - } - String unicodeNormalized = Normalizer.normalize(value, Normalizer.Form.NFKC); - return NORMALIZED_INPUT_WHITESPACE_PATTERN.matcher(unicodeNormalized) - .replaceAll(" ") - .trim() - .toLowerCase(Locale.ROOT); - } private static String defaultString(String value) { return value == null ? "" : value; diff --git a/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/FewShotCaseStore.java b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/FewShotCaseStore.java index 31d5f351..9c9da1d8 100644 --- a/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/FewShotCaseStore.java +++ b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/FewShotCaseStore.java @@ -103,14 +103,15 @@ private List loadJsonCases(String resourcePath, FewShotSource expec List cases = objectMapper.readValue(json, new TypeReference<>() { }); return cases.stream() + .filter(java.util.Objects::nonNull) .map(fewShotCase -> normalizeSource(fewShotCase, expectedSource)) .toList(); } catch (IOException | RuntimeException e) { log.warn( - "few-shot resource parse failed. source={}, resource={}, message={}", + "few-shot resource parse failed. source={}, resource={}, errorType={}", expectedSource, resourcePath, - e.getMessage() + e.getClass().getSimpleName() ); return List.of(); } @@ -119,14 +120,11 @@ private List loadJsonCases(String resourcePath, FewShotSource expec private List filterSearchable(List cases) { List result = new ArrayList<>(); Set ids = new HashSet<>(); + Set inputHashes = new HashSet<>(); for (FewShotCase fewShotCase : cases) { if (fewShotCase == null || !StringUtils.hasText(fewShotCase.id())) { continue; } - if (!ids.add(fewShotCase.id())) { - log.warn("few-shot case skipped. reason=duplicate_case_id, id={}, source={}", fewShotCase.id(), fewShotCase.source()); - continue; - } if (!fewShotCase.searchable()) { log.debug("few-shot case skipped. reason=not_searchable, id={}, source={}", fewShotCase.id(), fewShotCase.source()); continue; @@ -135,6 +133,25 @@ private List filterSearchable(List cases) { log.debug("few-shot case skipped. reason=blank_prompt_block, id={}, source={}", fewShotCase.id(), fewShotCase.source()); continue; } + if (fewShotCase.source() == FewShotSource.REVIEWED_EVALUATION + && (!StringUtils.hasText(fewShotCase.question()) + || !StringUtils.hasText(fewShotCase.sanitizedAnswer()) + || !validReviewedAnalysis(fewShotCase.approvedAnalysisJson(), fewShotCase.id()))) { + log.warn("few-shot case skipped. reason=invalid_reviewed_case, id={}", fewShotCase.id()); + continue; + } + if (ids.contains(fewShotCase.id())) { + log.warn("few-shot case skipped. reason=duplicate_case_id, id={}, source={}", fewShotCase.id(), fewShotCase.source()); + continue; + } + String inputHash = FewShotInputHash.of(fewShotCase.mainTasks(), fewShotCase.qualifications(), + fewShotCase.question(), fewShotCase.sanitizedAnswer()); + if (!inputHash.isEmpty() && !inputHashes.add(inputHash)) { + log.warn("few-shot case skipped. reason=duplicate_normalized_input, id={}, source={}", + fewShotCase.id(), fewShotCase.source()); + continue; + } + ids.add(fewShotCase.id()); result.add(fewShotCase); } return List.copyOf(result); @@ -152,7 +169,6 @@ private List loadReviewedEvaluationCsvCases(String csvPath) { try { List> rows = readCsv(path); List result = new ArrayList<>(); - Set ids = new HashSet<>(); for (Map row : rows) { String id = value(row, "caseId"); if (!"true".equalsIgnoreCase(value(row, "fewShotEnabled"))) { @@ -182,14 +198,14 @@ private List loadReviewedEvaluationCsvCases(String csvPath) { log.warn("reviewed evaluation few-shot row skipped. reason=invalid_analysis_object, caseId={}", id); continue; } + if (!ReviewedFewShotAnalysisValidator.isValid(analysis)) { + log.warn("reviewed evaluation few-shot row skipped. reason=invalid_analysis_schema, caseId={}", id); + continue; + } } catch (IOException e) { log.warn("reviewed evaluation few-shot row skipped. reason=invalid_analysis_json, caseId={}", id); continue; } - if (!ids.add(id)) { - log.warn("reviewed evaluation few-shot row skipped. reason=duplicate_case_id, caseId={}", id); - continue; - } result.add(new FewShotCase( id, FewShotSource.REVIEWED_EVALUATION, @@ -209,18 +225,33 @@ private List loadReviewedEvaluationCsvCases(String csvPath) { buildReviewedPromptBlock(id, row, sanitizedAnswer, approvedAnalysisJson) )); } - log.info("reviewed evaluation few-shot CSV loaded. rows={}, accepted={}, path={}", rows.size(), result.size(), csvPath); + log.info("reviewed evaluation few-shot CSV validated before deduplication. rows={}, validRows={}, path={}", rows.size(), result.size(), csvPath); return result; } catch (IOException | RuntimeException e) { - log.warn("reviewed evaluation few-shot CSV parse failed. path={}, message={}", csvPath, e.getMessage()); + log.warn("reviewed evaluation few-shot CSV parse failed. path={}, errorType={}", csvPath, e.getClass().getSimpleName()); return List.of(); } } + private boolean validReviewedAnalysis(String json, String id) { + try { + var analysis = objectMapper.reader() + .with(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_TRAILING_TOKENS) + .readTree(json); + if (ReviewedFewShotAnalysisValidator.isValid(analysis)) { + return true; + } + log.warn("reviewed few-shot case skipped. reason=invalid_analysis_schema, caseId={}", id); + } catch (IOException e) { + log.warn("reviewed few-shot case skipped. reason=invalid_analysis_json, caseId={}", id); + } + return false; + } + private FewShotCase normalizeSource(FewShotCase fewShotCase, FewShotSource expectedSource) { return new FewShotCase( fewShotCase.id(), - fewShotCase.source() == null ? expectedSource : fewShotCase.source(), + expectedSource, fewShotCase.reviewStatus(), fewShotCase.enabled(), fewShotCase.priority(), @@ -329,11 +360,23 @@ private static List> readCsv(Path path) throws IOException { if (!headers.isEmpty() && !headers.getFirst().isEmpty() && headers.getFirst().charAt(0) == '\uFEFF') { headers.set(0, headers.getFirst().substring(1)); } + if (new HashSet<>(headers).size() != headers.size() + || !headers.containsAll(List.of("caseId", "question", "sanitizedAnswer", + "approvedAnalysisJson", "fewShotEnabled", "reviewStatus")) + || headers.stream().noneMatch(Set.of("mainTasks", "qualifications", "preferences")::contains)) { + log.warn("reviewed evaluation few-shot CSV skipped. reason=invalid_csv_headers"); + return List.of(); + } List> result = new ArrayList<>(); for (List row : rows.subList(1, rows.size())) { if (row.stream().allMatch(String::isBlank)) { continue; } + if (row.size() != headers.size()) { + log.warn("reviewed evaluation few-shot row skipped. reason=invalid_column_count, expected={}, actual={}", + headers.size(), row.size()); + continue; + } Map values = new LinkedHashMap<>(); for (int i = 0; i < headers.size(); i++) { values.put(headers.get(i), i < row.size() ? row.get(i) : ""); @@ -375,6 +418,9 @@ private static List> parseRows(String content) { field.append(current); } } + if (inQuotes) { + throw new IllegalArgumentException("Unclosed quoted CSV field"); + } row.add(field.toString()); if (!row.isEmpty() && row.stream().anyMatch(value -> !value.isBlank())) { rows.add(row); diff --git a/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/FewShotInputHash.java b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/FewShotInputHash.java new file mode 100644 index 00000000..0aaa1e8f --- /dev/null +++ b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/FewShotInputHash.java @@ -0,0 +1,70 @@ +package com.jobdri.jobdri_api.domain.analysis.service.ai.fewshot; + +import org.springframework.util.StringUtils; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.Normalizer; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; + +final class FewShotInputHash { + private static final Pattern NORMALIZED_INPUT_WHITESPACE_PATTERN = Pattern.compile("[\\p{Z}\\s]+"); + + private FewShotInputHash() { + } + + static String of( + List mainTasks, + List qualifications, + String question, + String answer + ) { + String normalizedMainTasks = normalizeInputSection(mainTasks == null ? "" : String.join("\n", mainTasks)); + String normalizedQualifications = normalizeInputSection( + qualifications == null ? "" : String.join("\n", qualifications) + ); + String normalizedQuestion = normalizeInputSection(question); + String normalizedAnswer = normalizeInputSection(answer); + if (normalizedMainTasks.isEmpty() + && normalizedQualifications.isEmpty() + && normalizedQuestion.isEmpty() + && normalizedAnswer.isEmpty()) { + return ""; + } + return sha256( + normalizedMainTasks + '\u001f' + + normalizedQualifications + '\u001f' + + normalizedQuestion + '\u001f' + + normalizedAnswer + ); + } + + private static String normalizeInputSection(String value) { + if (!StringUtils.hasText(value)) { + return ""; + } + String unicodeNormalized = Normalizer.normalize(value, Normalizer.Form.NFKC); + return NORMALIZED_INPUT_WHITESPACE_PATTERN.matcher(unicodeNormalized) + .replaceAll(" ") + .trim() + .toLowerCase(Locale.ROOT); + } + + private static String sha256(String value) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(value.getBytes(StandardCharsets.UTF_8)); + StringBuilder result = new StringBuilder(); + for (byte b : hash) { + result.append("%02x".formatted(b)); + } + return result.toString(); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("SHA-256 algorithm is not available", e); + } + } + +} diff --git a/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/ReviewedFewShotAnalysisValidator.java b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/ReviewedFewShotAnalysisValidator.java new file mode 100644 index 00000000..9cfdc49c --- /dev/null +++ b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/ReviewedFewShotAnalysisValidator.java @@ -0,0 +1,52 @@ +package com.jobdri.jobdri_api.domain.analysis.service.ai.fewshot; + +import com.fasterxml.jackson.databind.JsonNode; + +import java.util.Set; + +/** + * Validates the partial analysis contract used by reviewed prompt examples. + * Scores and feedback are deliberately not required; semantic approval remains a PM task. + */ +final class ReviewedFewShotAnalysisValidator { + private static final Set STATUSES = Set.of("proven", "mentioned", "fabricated"); + private static final Set SOURCES = Set.of("mainTask", "qualification", "preference"); + + private ReviewedFewShotAnalysisValidator() { + } + + static boolean isValid(JsonNode analysis) { + if (analysis == null || !analysis.isObject() + || !analysis.path("keyStrengths").isArray() + || !analysis.path("missingKeywords").isArray() + || !analysis.path("questionAnalyses").isArray()) { + return false; + } + for (JsonNode strength : analysis.path("keyStrengths")) { + if (!text(strength, "title") || !text(strength, "quote")) { + return false; + } + } + for (JsonNode missing : analysis.path("missingKeywords")) { + if (!text(missing, "keyword") || !text(missing, "source") + || !SOURCES.contains(missing.path("source").textValue())) { + return false; + } + } + for (JsonNode sentence : analysis.path("questionAnalyses")) { + JsonNode questionId = sentence.path("questionId"); + if (!questionId.isIntegralNumber() || !questionId.canConvertToLong() || questionId.longValue() <= 0 + || !text(sentence, "sentence") || !text(sentence, "reason") + || !text(sentence, "status") || !STATUSES.contains(sentence.path("status").textValue()) + || !sentence.has("improvement") + || !(sentence.path("improvement").isNull() || text(sentence, "improvement"))) { + return false; + } + } + return true; + } + + private static boolean text(JsonNode node, String field) { + return node.path(field).isTextual() && !node.path(field).textValue().isBlank(); + } +} diff --git a/src/main/resources/analysis/fewshot/reviewed-fewshot-cases-pm-20260914-v2.json b/src/main/resources/analysis/fewshot/reviewed-fewshot-cases-pm-20260914-v2.json new file mode 100644 index 00000000..7c8a0aa4 --- /dev/null +++ b/src/main/resources/analysis/fewshot/reviewed-fewshot-cases-pm-20260914-v2.json @@ -0,0 +1,173 @@ +[ + { + "id": "FS-02", + "source": "REVIEWED_EVALUATION", + "reviewStatus": "APPROVED", + "enabled": true, + "priority": 6, + "jobCategory": "회계 관리자", + "jobTitle": "재무/회계 Sr.", + "mainTasks": [ + "- 회계 정보 " + ], + "qualifications": [ + "- 학사 이상", + "- 회계 관련 전공자", + "- (최소 5년) 10년 미만 관련 업무 경험자", + "- 연결재무제표 작성 경력 최소 2년 이상", + "- SAP FI 활용 경험이 있는 분", + "- 외부감사 수검 2회이상 경험을 보유한 분", + "- 결산 조정 가능한 분", + "- 팀 내/간 구성원 커뮤니케이션이 원활한 분", + "- 성실하고 기민하여 꼼꼼한 업무수행이 가능한 분" + ], + "question": "지원 동기와 입사 후 포부를 기술하시오", + "sanitizedAnswer": "자기소개\n\n영화 인터스텔라의 ‘밀러 행성’ 장면에서 1시간의 체류 시간이 지구에서는 7년의 시간이 흐른다는 설정을 보며 겉으로는 같은 숫자라도, 어떤 기준에서 바라보느냐에 따라 전혀 다른 결과로 해석될 수 있음을 느꼈습니다.\n\n회계는 기업의 성과를 기준에 따라 기록하고 이를 통해 사업의 실질을 정확하게 전달하는 역할을 합니다. 기준이 명확하지 않으면 동일한 거래도 왜곡된 결과로 나타날 수 있으며 이는 재무정보의 신뢰성을 저해할 수 있습니다.\n\n저는 ○○○에서 11년간 근무하며 별도 결산, 세무, 자산 관리 및 내부회계 운영 등 재무 전반의 실무 경험을 쌓아왔습니다. 결산 과정에서 손익 변동의 원인을 분석하고 회계 기준과 거래의 실질이 일치하는지 지속적으로 점검해 왔습니다. 또한 재무 결과를 사업 활동과 연결하여 이해하고 설명하는 데 집중해 왔으며 이를 통해 재무정보의 정확성과 신뢰성을 유지해 왔습니다.\n\n이러한 경험을 바탕으로 재무 데이터를 통해 사업의 현재를 왜곡 없이 투영하고 엄격한 기준과 원칙 위에서 재무정보의 정확성을 책임지는 재무 담당자가 되겠습니다.\n\n지원동기\n\n콘텐츠 산업은 하나의 IP가 음악, 공연, 영상, 플랫폼 등 다양한 형태로 확장되며 글로벌 시장에서 지속적으로 성장하는 산업이라고 생각합니다. 이러한 산업 구조에서는 다양한 국가와 사업 영역에서 발생하는 거래를 정확하게 반영하고 사업의 재무 흐름을 안정적으로 관리하는 재무 역할이 중요하다고 생각합니다.\n\n하이브는 아티스트 IP를 기반으로 음악, 공연, 플랫폼 등 다양한 콘텐츠 사업을 글로벌 시장으로 확장하며 새로운 비즈니스 구조를 만들어가고 있는 기업이라고 생각합니다. 특히 하나의 IP가 다양한 사업으로 확장되는 구조에서는 각 사업 활동에서 발생하는 거래를 재무적으로 정확하게 반영하고 사업의 성과를 일관된 기준으로 관리하는 재무 기능이 중요하다고 생각했습니다.\n\n저는 ○○○에서 결산과 재무 분석을 수행하며 사업 활동과 재무 결과를 연결해 설명하는 경험을 쌓아왔습니다. 이러한 경험을 바탕으로 다양한 사업 구조 속에서 발생하는 거래를 정확하게 반영하고 글로벌 사업의 재무 흐름을 안정적으로 관리하는 역할을 수행하고자 지원했습니다.\n\n직무적합성 및 강점\n\n○○○에서 11년간 근무하며 결산을 중심으로 회계, 세무, 내부회계 운영 등 재무 전반의 실무 경험을 쌓아왔으며 특히 주요 거래와 회계 이슈에 대한 기준을 검토하고 재무제표의 신뢰성을 확보하는 역할을 수행해 왔습니다. 이러한 경험은 다음과 같은 성과로 이어졌습니다.\n\n첫째, 자산 거버넌스 및 자본 구조 개선 경험입니다. 전사 토지 재평가 프로젝트를 수행하며 약 7.8조 원 규모의 자산 재평가를 진행하였고 부채비율을 약 35.4%p 개선하여 재무 안정성을 확보했습니다. 공정가치 산정 기준 검토와 재무적 영향 분석을 수행하였으며 이후 손상평가 등 후속 검토까지 관리하며 자산 회계의 전 과정을 경험했습니다.\n\n둘째, 세무 리스크 관리 및 조세 실익 확보 경험입니다. 부가가치세 조세불복을 수행하여 약 320억 원 규모의 세액 환급을 이끌어냈으며 약 800억 원 규모의 장기 미수금을 정비하여 재무 건전성 개선에 기여했습니다. 회계와 세무를 분리하지 않고 재무 전반의 관점에서 리스크를 관리한 경험입니다.\n\n셋째, 시스템 고도화를 통한 재무 프로세스 개선 경험입니다. SAP S/4 HANA 고도화 프로젝트에 참여하여 현금흐름 추출 로직을 설계하고 결산 프로세스를 개선하였습니다. 수기 업무를 시스템 통제로 전환하며 데이터 정합성을 강화하였고 결산 리드타임을 약 30% 단축하였습니다. 이는 디지털 기반으로 재무 프로세스를 고도화하고 재무보고의 효율성을 높인 경험입니다.\n\n입사 후 포부\n\n입사 초기에는 하이브의 결산 프로세스와 재무보고 체계를 빠르게 이해하고 맡은 업무를 정확하고 안정적으로 수행하는 데 집중하겠습니다. 특히 다양한 국가와 사업 구조에서 발생하는 거래를 정확하게 반영하고 글로벌 사업의 재무 흐름을 이해하며 결산 업무의 전문성을 넓혀 나가겠습니다.\n\n중장기적으로는 복잡한 사업 구조 속에서 발생하는 회계 이슈에 대해 기준을 검토하고 합리적인 처리 방향을 제시할 수 있는 재무 담당자로 성장하고 싶습니다. 또한 글로벌 사업 확장 과정에서 재무 데이터의 정확성과 일관성을 유지함으로써 하이브의 안정적인 재무 운영을 뒷받침하겠습니다.", + "approvedAnalysisJson": "{\"keyStrengths\": [], \"missingKeywords\": [{\"keyword\": \"연결 재무제표 작성\", \"source\": \"qualification\"}], \"questionAnalyses\": [{\"questionId\": 1, \"sentence\": \"SAP S/4 HANA 고도화 프로젝트에 참여하여 현금흐름 추출 로직을 설계하고 결산 프로세스를 개선하였습니다.\", \"status\": \"proven\", \"reason\": \"채용공고의 SAP FI 활용 경험과 결산 프로세스 개선 역량에 직접 맞닿는 문장입니다. 단순 사용이 아니라 시스템 로직 설계와 프로세스 개선까지 수행한 점이 드러나 직무 적합성을 뒷받침합니다.\", \"improvement\": null}, {\"questionId\": 1, \"sentence\": \"수기 업무를 시스템 통제로 전환하며 데이터 정합성을 강화하였고 결산 리드타임을 약 30% 단축하였습니다.\", \"status\": \"proven\", \"reason\": \"프로세스 개선의 결과가 수치로 제시되어 있어 성과의 검증 가능성이 높습니다. 효율성과 정확성 두 측면의 개선 효과가 함께 드러나 재무/회계 운영 기여도를 보여줍니다.\", \"improvement\": null}]}", + "tags": [ + "회계·세무", + "회계 관리자", + "mentioned", + "proven", + "missing", + "completeness", + "jobFit", + "impact" + ], + "datasetVersion": "fewshot-pm-reviewed-20260914-v2", + "promptBlock": "## 예시 FS-02: PM 검수 사례\n관련 JD 요구사항:\n- 회계 정보 \n- 학사 이상\n- 회계 관련 전공자\n- (최소 5년) 10년 미만 관련 업무 경험자\n- 연결재무제표 작성 경력 최소 2년 이상\n- SAP FI 활용 경험이 있는 분\n- 외부감사 수검 2회이상 경험을 보유한 분\n- 결산 조정 가능한 분\n- 팀 내/간 구성원 커뮤니케이션이 원활한 분\n- 성실하고 기민하여 꼼꼼한 업무수행이 가능한 분\n우대사항:\n- 엔터테인먼트/미디어 혹은 IT업계에서 회계 관련 경험을 보유한 분\n- 공인회계사 혹은 회계 관련 자격증을 보유한 분\n- 영어나 일본어 커뮤니케이션에 원활한 분\n평가 대상 답변:\n- questionId: 1\n- question: 지원 동기와 입사 후 포부를 기술하시오\n- answer: 자기소개\n\n영화 인터스텔라의 ‘밀러 행성’ 장면에서 1시간의 체류 시간이 지구에서는 7년의 시간이 흐른다는 설정을 보며 겉으로는 같은 숫자라도, 어떤 기준에서 바라보느냐에 따라 전혀 다른 결과로 해석될 수 있음을 느꼈습니다.\n\n회계는 기업의 성과를 기준에 따라 기록하고 이를 통해 사업의 실질을 정확하게 전달하는 역할을 합니다. 기준이 명확하지 않으면 동일한 거래도 왜곡된 결과로 나타날 수 있으며 이는 재무정보의 신뢰성을 저해할 수 있습니다.\n\n저는 ○○○에서 11년간 근무하며 별도 결산, 세무, 자산 관리 및 내부회계 운영 등 재무 전반의 실무 경험을 쌓아왔습니다. 결산 과정에서 손익 변동의 원인을 분석하고 회계 기준과 거래의 실질이 일치하는지 지속적으로 점검해 왔습니다. 또한 재무 결과를 사업 활동과 연결하여 이해하고 설명하는 데 집중해 왔으며 이를 통해 재무정보의 정확성과 신뢰성을 유지해 왔습니다.\n\n이러한 경험을 바탕으로 재무 데이터를 통해 사업의 현재를 왜곡 없이 투영하고 엄격한 기준과 원칙 위에서 재무정보의 정확성을 책임지는 재무 담당자가 되겠습니다.\n\n지원동기\n\n콘텐츠 산업은 하나의 IP가 음악, 공연, 영상, 플랫폼 등 다양한 형태로 확장되며 글로벌 시장에서 지속적으로 성장하는 산업이라고 생각합니다. 이러한 산업 구조에서는 다양한 국가와 사업 영역에서 발생하는 거래를 정확하게 반영하고 사업의 재무 흐름을 안정적으로 관리하는 재무 역할이 중요하다고 생각합니다.\n\n하이브는 아티스트 IP를 기반으로 음악, 공연, 플랫폼 등 다양한 콘텐츠 사업을 글로벌 시장으로 확장하며 새로운 비즈니스 구조를 만들어가고 있는 기업이라고 생각합니다. 특히 하나의 IP가 다양한 사업으로 확장되는 구조에서는 각 사업 활동에서 발생하는 거래를 재무적으로 정확하게 반영하고 사업의 성과를 일관된 기준으로 관리하는 재무 기능이 중요하다고 생각했습니다.\n\n저는 ○○○에서 결산과 재무 분석을 수행하며 사업 활동과 재무 결과를 연결해 설명하는 경험을 쌓아왔습니다. 이러한 경험을 바탕으로 다양한 사업 구조 속에서 발생하는 거래를 정확하게 반영하고 글로벌 사업의 재무 흐름을 안정적으로 관리하는 역할을 수행하고자 지원했습니다.\n\n직무적합성 및 강점\n\n○○○에서 11년간 근무하며 결산을 중심으로 회계, 세무, 내부회계 운영 등 재무 전반의 실무 경험을 쌓아왔으며 특히 주요 거래와 회계 이슈에 대한 기준을 검토하고 재무제표의 신뢰성을 확보하는 역할을 수행해 왔습니다. 이러한 경험은 다음과 같은 성과로 이어졌습니다.\n\n첫째, 자산 거버넌스 및 자본 구조 개선 경험입니다. 전사 토지 재평가 프로젝트를 수행하며 약 7.8조 원 규모의 자산 재평가를 진행하였고 부채비율을 약 35.4%p 개선하여 재무 안정성을 확보했습니다. 공정가치 산정 기준 검토와 재무적 영향 분석을 수행하였으며 이후 손상평가 등 후속 검토까지 관리하며 자산 회계의 전 과정을 경험했습니다.\n\n둘째, 세무 리스크 관리 및 조세 실익 확보 경험입니다. 부가가치세 조세불복을 수행하여 약 320억 원 규모의 세액 환급을 이끌어냈으며 약 800억 원 규모의 장기 미수금을 정비하여 재무 건전성 개선에 기여했습니다. 회계와 세무를 분리하지 않고 재무 전반의 관점에서 리스크를 관리한 경험입니다.\n\n셋째, 시스템 고도화를 통한 재무 프로세스 개선 경험입니다. SAP S/4 HANA 고도화 프로젝트에 참여하여 현금흐름 추출 로직을 설계하고 결산 프로세스를 개선하였습니다. 수기 업무를 시스템 통제로 전환하며 데이터 정합성을 강화하였고 결산 리드타임을 약 30% 단축하였습니다. 이는 디지털 기반으로 재무 프로세스를 고도화하고 재무보고의 효율성을 높인 경험입니다.\n\n입사 후 포부\n\n입사 초기에는 하이브의 결산 프로세스와 재무보고 체계를 빠르게 이해하고 맡은 업무를 정확하고 안정적으로 수행하는 데 집중하겠습니다. 특히 다양한 국가와 사업 구조에서 발생하는 거래를 정확하게 반영하고 글로벌 사업의 재무 흐름을 이해하며 결산 업무의 전문성을 넓혀 나가겠습니다.\n\n중장기적으로는 복잡한 사업 구조 속에서 발생하는 회계 이슈에 대해 기준을 검토하고 합리적인 처리 방향을 제시할 수 있는 재무 담당자로 성장하고 싶습니다. 또한 글로벌 사업 확장 과정에서 재무 데이터의 정확성과 일관성을 유지함으로써 하이브의 안정적인 재무 운영을 뒷받침하겠습니다.\n출력 중 강점/문장/누락 관련 필드:\n{\n \"keyStrengths\": [],\n \"missingKeywords\": [\n {\n \"keyword\": \"연결 재무제표 작성\",\n \"source\": \"qualification\"\n }\n ],\n \"questionAnalyses\": [\n {\n \"questionId\": 1,\n \"sentence\": \"SAP S/4 HANA 고도화 프로젝트에 참여하여 현금흐름 추출 로직을 설계하고 결산 프로세스를 개선하였습니다.\",\n \"status\": \"proven\",\n \"reason\": \"채용공고의 SAP FI 활용 경험과 결산 프로세스 개선 역량에 직접 맞닿는 문장입니다. 단순 사용이 아니라 시스템 로직 설계와 프로세스 개선까지 수행한 점이 드러나 직무 적합성을 뒷받침합니다.\",\n \"improvement\": null\n },\n {\n \"questionId\": 1,\n \"sentence\": \"수기 업무를 시스템 통제로 전환하며 데이터 정합성을 강화하였고 결산 리드타임을 약 30% 단축하였습니다.\",\n \"status\": \"proven\",\n \"reason\": \"프로세스 개선의 결과가 수치로 제시되어 있어 성과의 검증 가능성이 높습니다. 효율성과 정확성 두 측면의 개선 효과가 함께 드러나 재무/회계 운영 기여도를 보여줍니다.\",\n \"improvement\": null\n }\n ]\n}" + }, + { + "id": "FS-03", + "source": "REVIEWED_EVALUATION", + "reviewStatus": "APPROVED", + "enabled": true, + "priority": 1, + "jobCategory": "공공기관 인턴", + "jobTitle": "청년인턴(체험형)", + "mainTasks": [ + "- 해외시장 개척, 해외 투자진출 지원 및 시장정보 조사, 경영관리 보조 등", + "- 행정업무 보조" + ], + "qualifications": [ + "- 가. 국적 : 대한민국 국적 소지자", + "- 나. 응시연령 : 만 34세 이하 청년", + "- 다. 학력 : 고등학교 졸업 이상(‘26. 6. 17. 공고일 기준)", + "- 라. 국가공무원법 제33조에 의한 결격사유가 없는 자", + "- 마. 최종 합격 후 근로계약 체결일(채용일)부터 근무 가능한 자", + "- 바. KOTRA 청년인턴으로 근무했던 자 또는 근무 중인 자는 채용 제외", + "- 유효한 영어 TOEIC 성적 보유자" + ], + "question": "지원 동기와 입사 후 포부를 기술하시오", + "sanitizedAnswer": "기업의 해외 진출을 지원하는 KOTRA에서 기업지원 실무를 직접 경험하고 싶어 지원했습니다. 무역학개론을 수강하며 기업의 해외 진출은 제품의 경쟁력뿐 아니라 국가별 시장환경과 고객을 이해하고 그에 맞는 전략을 수립하는 과정이 중요하다는 점을 배웠습니다. 이를 배우며 해외시장 분석과 전략 수립을 지원하는 업무에 관심을 갖게 되었고, 교환학생 기간 중 창업 프로젝트를 통해 미국 시장에서 이를 직접 검증해 볼 수 있었습니다.\n한국에서 구상한 아이디어를 미국 시장에 적용하는 과정에서 한국에서 효과적이라고 생각했던 전략이 그대로 통하지 않는다는 점을 몸소 경험했습니다. 고객 특성과 경쟁 환경을 다시 분석하며 운영 전략을 수정했고 시장을 이해하는 과정이 전략을 바꾸기도 한다는 점을 직접 확인할 수 있었습니다. 이 경험을 통해 기업이 해외시장에 진출하기 위해서는 시장을 이해하고 전략을 세우는 과정이 중요하다는 점을 느꼈습니다. 그래서 이러한 과정을 지원하는 KOTRA의 기업지원 업무에 관심을 갖게 되었습니다.\n체험형 인턴으로 근무하게 된다면 자료조사와 행정지원 등 맡은 업무를 책임감 있게 수행하겠습니다. 실무를 통해 기업이 해외시장을 분석하고 진출을 준비하는 과정을 배우며 기업지원 업무에 대한 이해를 넓히고 싶습니다.\n\n직무와 관련해 가장 자신 있는 역량은 고객과 시장을 분석하고 이를 바탕으로 전략을 보완하는 능력입니다. 이를 키우기 위해 미국 교환학생 기간 동안 온라인 영어 교육 플랫폼 창업 프로젝트를 수행했습니다.\n영어학원에서 근무하며 학부모들은 성적 뿐만 아니라 자녀가 실제로 얼마나 이해했는지 확인하는 것 또한 중요하게 생각한다는 점을 알게 되었습니다. 그러나 기존 온라인 교육 플랫폼은 강의 시청 여부만 확인할 수 있어 학생의 실제 이해 수준을 확인하기에는 한계가 있었습니다. 이를 해결하기 위해 강의 수강 후 피드백 세션에서 학생이 배운 내용을 직접 설명하고 이해가 확인되어야 다음 단계로 학습할 수 있는 구조를 기획했습니다.\n초기에는 영어학원에서 학부모들이 교사의 밀착 관리와 개별 피드백을 선호한다는 점을 고려해 1:1 피드백 중심으로 서비스를 설계했습니다. 그러나 미국에서 토론과 협업 중심의 수업을 직접 경험하며 학생 간 상호작용을 통해 학습 효과를 높이는 교육 방식을 직접 경험했고 현지 교육 전문가의 조언을 통해 이러한 방식이 미국 시장에 더 적합하다는 점을 확인했습니다. 이에 그룹 피드백 중심으로 운영 방식을 전환해 1:1 강사 운영 비용을 줄이면서도 교육 효과와 서비스 확장성을 확보했습니다.\n이 경험을 통해 시장이 달라지면 전략도 달라져야 한다는 점을 직접 경험했고 고객과 시장에 맞게 전략을 보완하는 역량을 기를 수 있었습니다.", + "approvedAnalysisJson": "{\"keyStrengths\": [], \"missingKeywords\": [], \"questionAnalyses\": [{\"questionId\": 1, \"sentence\": \"고객 특성과 경쟁 환경을 다시 분석하며 운영 전략을 수정했고 시장을 이해하는 과정이 전략을 바꾸기도 한다는 점을 직접 확인할 수 있었습니다.\", \"status\": \"proven\", \"reason\": \"해외시장 진출 지원 직무의 핵심인 시장정보 조사와 전략 보완 역량을 보여주는 문장입니다. 단순 관심 표명이 아니라 시장과 경쟁 환경을 재분석하고 전략을 조정한 행동이 드러나 직무 연관성이 높습니다.\", \"improvement\": null}, {\"questionId\": 1, \"sentence\": \"체험형 인턴으로 근무하게 된다면 자료조사와 행정지원 등 맡은 업무를 책임감 있게 수행하겠습니다.\", \"status\": \"mentioned\", \"reason\": \"입사 후 포부가 공고상 업무와 직접 연결된 점은 좋지만, 책임감 있게 수행하겠다는 표현이 다소 일반적입니다. 어떤 방식으로 자료조사 정확도, 보고 정리, 행정업무 지원에 기여할지 실행 계획이 보이면 더 설득력 있습니다.\", \"improvement\": \"체험형 인턴으로 근무하게 된다면 해외시장 관련 자료를 목적에 맞게 빠르게 정리하고, 행정업무 보조 시에도 일정·문서·데이터를 정확하게 관리해 실무자들이 바로 활용할 수 있는 지원을 하겠습니다.\"}]}", + "tags": [ + "공공·복지", + "공공기관 인턴", + "mentioned", + "proven", + "impact", + "jobFit", + "completeness" + ], + "datasetVersion": "fewshot-pm-reviewed-20260914-v2", + "promptBlock": "## 예시 FS-03: PM 검수 사례\n관련 JD 요구사항:\n- 해외시장 개척, 해외 투자진출 지원 및 시장정보 조사, 경영관리 보조 등\n- 행정업무 보조\n- 가. 국적 : 대한민국 국적 소지자\n- 나. 응시연령 : 만 34세 이하 청년\n- 다. 학력 : 고등학교 졸업 이상(‘26. 6. 17. 공고일 기준)\n- 라. 국가공무원법 제33조에 의한 결격사유가 없는 자\n- 마. 최종 합격 후 근로계약 체결일(채용일)부터 근무 가능한 자\n- 바. KOTRA 청년인턴으로 근무했던 자 또는 근무 중인 자는 채용 제외\n- 유효한 영어 TOEIC 성적 보유자\n우대사항:\n\n평가 대상 답변:\n- questionId: 1\n- question: 지원 동기와 입사 후 포부를 기술하시오\n- answer: 기업의 해외 진출을 지원하는 KOTRA에서 기업지원 실무를 직접 경험하고 싶어 지원했습니다. 무역학개론을 수강하며 기업의 해외 진출은 제품의 경쟁력뿐 아니라 국가별 시장환경과 고객을 이해하고 그에 맞는 전략을 수립하는 과정이 중요하다는 점을 배웠습니다. 이를 배우며 해외시장 분석과 전략 수립을 지원하는 업무에 관심을 갖게 되었고, 교환학생 기간 중 창업 프로젝트를 통해 미국 시장에서 이를 직접 검증해 볼 수 있었습니다.\n한국에서 구상한 아이디어를 미국 시장에 적용하는 과정에서 한국에서 효과적이라고 생각했던 전략이 그대로 통하지 않는다는 점을 몸소 경험했습니다. 고객 특성과 경쟁 환경을 다시 분석하며 운영 전략을 수정했고 시장을 이해하는 과정이 전략을 바꾸기도 한다는 점을 직접 확인할 수 있었습니다. 이 경험을 통해 기업이 해외시장에 진출하기 위해서는 시장을 이해하고 전략을 세우는 과정이 중요하다는 점을 느꼈습니다. 그래서 이러한 과정을 지원하는 KOTRA의 기업지원 업무에 관심을 갖게 되었습니다.\n체험형 인턴으로 근무하게 된다면 자료조사와 행정지원 등 맡은 업무를 책임감 있게 수행하겠습니다. 실무를 통해 기업이 해외시장을 분석하고 진출을 준비하는 과정을 배우며 기업지원 업무에 대한 이해를 넓히고 싶습니다.\n\n직무와 관련해 가장 자신 있는 역량은 고객과 시장을 분석하고 이를 바탕으로 전략을 보완하는 능력입니다. 이를 키우기 위해 미국 교환학생 기간 동안 온라인 영어 교육 플랫폼 창업 프로젝트를 수행했습니다.\n영어학원에서 근무하며 학부모들은 성적 뿐만 아니라 자녀가 실제로 얼마나 이해했는지 확인하는 것 또한 중요하게 생각한다는 점을 알게 되었습니다. 그러나 기존 온라인 교육 플랫폼은 강의 시청 여부만 확인할 수 있어 학생의 실제 이해 수준을 확인하기에는 한계가 있었습니다. 이를 해결하기 위해 강의 수강 후 피드백 세션에서 학생이 배운 내용을 직접 설명하고 이해가 확인되어야 다음 단계로 학습할 수 있는 구조를 기획했습니다.\n초기에는 영어학원에서 학부모들이 교사의 밀착 관리와 개별 피드백을 선호한다는 점을 고려해 1:1 피드백 중심으로 서비스를 설계했습니다. 그러나 미국에서 토론과 협업 중심의 수업을 직접 경험하며 학생 간 상호작용을 통해 학습 효과를 높이는 교육 방식을 직접 경험했고 현지 교육 전문가의 조언을 통해 이러한 방식이 미국 시장에 더 적합하다는 점을 확인했습니다. 이에 그룹 피드백 중심으로 운영 방식을 전환해 1:1 강사 운영 비용을 줄이면서도 교육 효과와 서비스 확장성을 확보했습니다.\n이 경험을 통해 시장이 달라지면 전략도 달라져야 한다는 점을 직접 경험했고 고객과 시장에 맞게 전략을 보완하는 역량을 기를 수 있었습니다.\n출력 중 강점/문장/누락 관련 필드:\n{\n \"keyStrengths\": [],\n \"missingKeywords\": [],\n \"questionAnalyses\": [\n {\n \"questionId\": 1,\n \"sentence\": \"고객 특성과 경쟁 환경을 다시 분석하며 운영 전략을 수정했고 시장을 이해하는 과정이 전략을 바꾸기도 한다는 점을 직접 확인할 수 있었습니다.\",\n \"status\": \"proven\",\n \"reason\": \"해외시장 진출 지원 직무의 핵심인 시장정보 조사와 전략 보완 역량을 보여주는 문장입니다. 단순 관심 표명이 아니라 시장과 경쟁 환경을 재분석하고 전략을 조정한 행동이 드러나 직무 연관성이 높습니다.\",\n \"improvement\": null\n },\n {\n \"questionId\": 1,\n \"sentence\": \"체험형 인턴으로 근무하게 된다면 자료조사와 행정지원 등 맡은 업무를 책임감 있게 수행하겠습니다.\",\n \"status\": \"mentioned\",\n \"reason\": \"입사 후 포부가 공고상 업무와 직접 연결된 점은 좋지만, 책임감 있게 수행하겠다는 표현이 다소 일반적입니다. 어떤 방식으로 자료조사 정확도, 보고 정리, 행정업무 지원에 기여할지 실행 계획이 보이면 더 설득력 있습니다.\",\n \"improvement\": \"체험형 인턴으로 근무하게 된다면 해외시장 관련 자료를 목적에 맞게 빠르게 정리하고, 행정업무 보조 시에도 일정·문서·데이터를 정확하게 관리해 실무자들이 바로 활용할 수 있는 지원을 하겠습니다.\"\n }\n ]\n}" + }, + { + "id": "FS-05", + "source": "REVIEWED_EVALUATION", + "reviewStatus": "APPROVED", + "enabled": true, + "priority": 8, + "jobCategory": "프로젝트 어시스턴트", + "jobTitle": "Project Assistant", + "mainTasks": [], + "qualifications": [ + "- 프로젝트 전체를 보고 판단 및 결정할 수 있는 능력", + "- 부드럽고 정확한 커뮤니케이션 능력", + "- VFX 작업 프로세스를 알고 있거나 영화 제작 프로세스를 이해하고 있는 분", + "- 해외 여행 결격사유 없는 분", + "- 배려하며 긍정적인 분위기로 협업이 가능한 분" + ], + "question": "1. 자기소개 및 진로계획", + "sanitizedAnswer": "1. 자기소개 및 진로계획\n\n여러 사람과 협업하여 하나의 결과물을 만들어낼 때 가장 큰 즐거움을 느낍니다. 학부 시절 영화를 전공하며 전공하며 연출, 조연출, PD, 편집 등 다양한 역할을 맡아 영화 프로세스 전 과정을 경험했습니다. 여러 역할을 넘나들며 깨달은 건, 제가 가장 뿌듯함을 느끼는 순간은 혼자 무언가를 완성했을 때가 아니라, 여럿이 모여 각자의 역할을 다해 하나의 결과물로 완성되는 그 과정이었습니다.\n\n이 확신은 3D 포트폴리오를 제작하며 컴프 아티스트와 협업할 때 더욱 굳어졌습니다. 혼자서만 작업하던 영상을 처음으로 타인에게 설명하려니, 기획 의도와 추상적인 이미지를 전달하는 데 어려움을 느꼈습니다. 이를 해결하기 위해 레퍼런스 보드를 제작해 수조의 탁도, 포그 농도, 글레어 강도 등 톤앤매너를 시각적으로 수치화했습니다. 또한 렌더링 소요 시간과 작업 스케줄을 함께 조율하고, 단계마다 결과물을 체크하며 방향성을 맞춰나갔습니다. 마침내 의도했던 이미지가 실제결과물로 구현되었을 때, 단순한 작업 이상의 협업이 만들어낸 시너지에 깊은 성취감을 느꼈습니다. 이와 같은 경험을 바탕으로, 저는 단일 작업자에 머무르기보다 협업의 중심에 서고자 합니다. 한국 VFX 1세대로서 한국영화를 이끌어온 디지털아이디어는, 영화에 그치지 않고 VR/AR, 전시 영상 등 영역을 확장해나가고 있습니다. 새로운 도전을 두려워하지 않고 계속해서 뻗어나가는 모습이 새로운 도전을 즐기는 제 지향점과 꼭 닮아 있습니다. 이곳에서 프리프로덕션부터 현장, 후반 작업을 두루 경험하며 쌓은 높은 이해도와 원활한 소통 능력을 기반으로 프로젝트를 완성도 높은 결과물로 끝까지 만들고 싶습니다.\n\n2. 커뮤니케이션 역량\n\n학부 시절 단편영화를 찍고 상업 영상 현장에서 조연출과 PD로 일하며, 예측 불가능한 돌발 변수 속에서도 우선순위를 재설정하고 스케줄을 효율적으로 관리하는 능력을 얻었습니다. 촬영 하루 전 매니지먼트 측의 갑작스런 일정 변경이나 기상 악화로 로케이션이 취소되는 위기를 여러 번 겪었습니다. 이러한 상황에서 당황하는 대신 대체 가능한 씬을 파악해 우선순위를 빠르게 재설정하고, 24시간 내에 전체 일정표를 다시 짜 조명, 미술, 배우 등 관련 이해관계자들에게 즉각 전달하여 현장의 혼선을 막았습니다.\n\n파리 교환학생 시절 다국적 학생들과 단편영화를 제작하며, 언어와 문화가 다른 사람들과 하나의 결과물을 만들어내는 경험도 했습니다. 의견 충돌이 생겼을 때 중재자의 역할을 하며, 모두가 같은 그림을 볼 수 있도록 직접 레퍼런스를 모으고 콘티를 그려 공유했습니다. 남은 기한을 고려해 업무를 재배분했고 그 결과 마감 기한 내에 팀원 전부가 동의하는 영화를 만들 수 있었습니다. 팀이 하나의 방향으로 맞춰지는 순간이 저는 늘 좋았고, 아무리 혁신적인 기술이라도 결국 사람이 하는 일이기에 원활한 소통과 조율 없이는 완성될 수 없다는 것을 알고 있습니다.\n\n영상 스튜디오 PD와 프리랜서 편집자로 일하며, 클라이언트의 요구사항을 정확히 파악하고 작업물로 구현해내는 과정을 수없이 반복했습니다. 때로는 구성을 통으로 바꿔야 하는 수정 요청이 오기도 했고, 컨셉에 맞춰 준비한 내용이 클라이언트의 머릿속과 전혀 다른 방향을 향하고 있다는 걸 뒤늦게 알게 되는 상황도 있었습니다. 그럴 때마다 소통의 오류가 어디서 생겼는지 재검토하고 다시 영점을 맞춰 클라이언트가 진짜 원하는 게 뭔지 파악하는 데 집중했습니다. 이를 통해 추상적인 피드백을 구체적인 방향으로 번역하고 문서화하는 능력은 길렀습니다. 이 감각은 감독과 클라이언트의 의도를 아티스트에게 정확히 전달해야 하는 PA의 역할에서 실질적인 무기가 될 것입니다.\n\n3. VFX와 영화 제작 프로세스 경험\n\n스크립터로서 촬영 현장의 정보를 정리해 본 경험은, 현장과 후반 작업의 연결 고리가 얼마나 중요한지 깨닫는 계기가 되었습니다. 뒤섞인 촬영 순서 속에서 의상 및 소품 컨티뉴이티 오류를 바로 포착하여 재촬영으로 인한 손실을 방지했고, 카메라 렌즈 데이터와 조명 세팅 같은 현장 정보를 꼼꼼히 기록하여 후반 작업자들의 시행착오를 최소화하는 데 집중했습니다. 개인 VFX 프로젝트에서는 이 감각을 직접 실천했습니다. Maya에서 먼저 조명 환경을 구현한 뒤 이를 촬영 현장의 라이팅 가이드로 활용했고, 촬영 당시의 메타데이터와 조명 및 카메라 위치를 꼼꼼히 텍스트와 사진으로 아카이빙했습니다. 이 기록들은 누크에서 카메라 생성과 컬러코렉팅 과정에서 가이드라인이 되어, 실사와 3D 사이의 이질감을 최소화하고 테스트 과정을 줄이는 데 실질적인 도움이 되었습니다. 하지만 소규모 인원으로 촬영과 스크립트 업무를 병행하다 특정 클립에서 렌즈 미리수를 누락하는 실수가 발생했습니다. 이후 카메라 매칭 과정에서 단 하나의 데이터 부재가 작업 난이도를 얼마나 높이고 시간을 낭비하게 만드는지 직접 겪었습니다. 작업자가 겪을 고충을 미리 이해하고, 데이터 누락 없이 정확하게 전달하는 것이 PA의 핵심 본분이라고 생각합니다. 현장의 상황을 후반 작업자에게 꼼꼼히 전달함으로써, 프로젝트가 끝까지 무사히 완주될 수 있도록 뒷받침하는 사람이 되겠습니다.", + "approvedAnalysisJson": "{\"keyStrengths\": [], \"missingKeywords\": [{\"keyword\": \"adobe, microsoft\", \"source\": \"preference\"}], \"questionAnalyses\": [{\"questionId\": 1, \"sentence\": \"파리 교환학생 시절 다국적 학생들과 단편영화를 제작하며, 언어와 문화가 다른 사람들과 하나의 결과물을 만들어내는 경험도 했습니다.\", \"status\": \"mentioned\", \"reason\": \"채용공고의 '해외 여행 결격사유 없는 분' 요건에 대해 이 문장은 해외 체류·협업 경험이라는 간접 근거만 제공합니다. 결격사유가 없다는 명시적 확인은 아니므로 proven이 아닌 mentioned에 머뭅니다. 다만 답변 내 다른 서술이나 JD와 모순되는 정황이 전혀 없어, 주장 자체의 신뢰가 깨지는 fabricated와는 구분됩니다 — 근거의 '부족'과 '모순'의 경계를 보여주는 판정입니다.\", \"improvement\": \"파리 교환학생 시절 다국적 팀과 단편영화를 제작하며 의견 충돌이 생겼을 때 중재 역할을 맡아 레퍼런스와 콘티를 직접 공유해 기준을 맞췄고, 남은 기한에 맞춰 업무를 재배분해 팀원 전원이 동의하는 결과물을 기한 내에 완성했습니다. 이러한 해외 현장 협업 경험을 바탕으로 해외 로케이션과 출장이 포함된 프로젝트에도 안정적으로 참여할 수 있습니다.\"}, {\"questionId\": 1, \"sentence\": \"이를 해결하기 위해 레퍼런스 보드를 제작해 수조의 탁도, 포그 농도, 글레어 강도 등 톤앤매너를 시각적으로 수치화했습니다.\", \"status\": \"proven\", \"reason\": \"추상적인 톤앤매너를 탁도·농도·강도 값으로 수치화해 협업자와 공유한 구체 행동이 드러납니다. '부드럽고 정확한 커뮤니케이션' 요건을 실행 방식 수준에서 입증하며, 감독·클라이언트의 의도를 아티스트에게 번역해 전달해야 하는 PA 직무와 직결되는 강점 근거입니다.\", \"improvement\": null}, {\"questionId\": 1, \"sentence\": \"이러한 상황에서 당황하는 대신 대체 가능한 씬을 파악해 우선순위를 빠르게 재설정하고, 24시간 내에 전체 일정표를 다시 짜 조명, 미술, 배우 등 관련 이해관계자들에게 즉각 전달하여 현장의 혼선을 막았습니다.\", \"status\": \"proven\", \"reason\": \"돌발 상황에서 대체안 파악 → 24시간 내 일정 재편 → 이해관계자 즉각 전파 → 현장 혼선 방지까지 상황-행동-결과가 완결적으로 서술됩니다. 수치는 없지만 행동과 결과가 구체적이어서 감점 요인이 되지 않으며, 프로젝트 전체를 보고 판단·결정하는 능력 요건과도 연결됩니다.\", \"improvement\": null}]}", + "tags": [ + "미디어·문화·스포츠", + "프로젝트 어시스턴트", + "missing", + "mentioned", + "proven", + "completeness", + "jobFit", + "impact" + ], + "datasetVersion": "fewshot-pm-reviewed-20260914-v2", + "promptBlock": "## 예시 FS-05: PM 검수 사례\n관련 JD 요구사항:\n- 프로젝트 전체를 보고 판단 및 결정할 수 있는 능력\n- 부드럽고 정확한 커뮤니케이션 능력\n- VFX 작업 프로세스를 알고 있거나 영화 제작 프로세스를 이해하고 있는 분\n- 해외 여행 결격사유 없는 분\n- 배려하며 긍정적인 분위기로 협업이 가능한 분\n우대사항:\n- adobe, microsoft 프로그램 사용 가능 자 우대\n평가 대상 답변:\n- questionId: 1\n- question: 1. 자기소개 및 진로계획\n- answer: 1. 자기소개 및 진로계획\n\n여러 사람과 협업하여 하나의 결과물을 만들어낼 때 가장 큰 즐거움을 느낍니다. 학부 시절 영화를 전공하며 전공하며 연출, 조연출, PD, 편집 등 다양한 역할을 맡아 영화 프로세스 전 과정을 경험했습니다. 여러 역할을 넘나들며 깨달은 건, 제가 가장 뿌듯함을 느끼는 순간은 혼자 무언가를 완성했을 때가 아니라, 여럿이 모여 각자의 역할을 다해 하나의 결과물로 완성되는 그 과정이었습니다.\n\n이 확신은 3D 포트폴리오를 제작하며 컴프 아티스트와 협업할 때 더욱 굳어졌습니다. 혼자서만 작업하던 영상을 처음으로 타인에게 설명하려니, 기획 의도와 추상적인 이미지를 전달하는 데 어려움을 느꼈습니다. 이를 해결하기 위해 레퍼런스 보드를 제작해 수조의 탁도, 포그 농도, 글레어 강도 등 톤앤매너를 시각적으로 수치화했습니다. 또한 렌더링 소요 시간과 작업 스케줄을 함께 조율하고, 단계마다 결과물을 체크하며 방향성을 맞춰나갔습니다. 마침내 의도했던 이미지가 실제결과물로 구현되었을 때, 단순한 작업 이상의 협업이 만들어낸 시너지에 깊은 성취감을 느꼈습니다. 이와 같은 경험을 바탕으로, 저는 단일 작업자에 머무르기보다 협업의 중심에 서고자 합니다. 한국 VFX 1세대로서 한국영화를 이끌어온 디지털아이디어는, 영화에 그치지 않고 VR/AR, 전시 영상 등 영역을 확장해나가고 있습니다. 새로운 도전을 두려워하지 않고 계속해서 뻗어나가는 모습이 새로운 도전을 즐기는 제 지향점과 꼭 닮아 있습니다. 이곳에서 프리프로덕션부터 현장, 후반 작업을 두루 경험하며 쌓은 높은 이해도와 원활한 소통 능력을 기반으로 프로젝트를 완성도 높은 결과물로 끝까지 만들고 싶습니다.\n\n2. 커뮤니케이션 역량\n\n학부 시절 단편영화를 찍고 상업 영상 현장에서 조연출과 PD로 일하며, 예측 불가능한 돌발 변수 속에서도 우선순위를 재설정하고 스케줄을 효율적으로 관리하는 능력을 얻었습니다. 촬영 하루 전 매니지먼트 측의 갑작스런 일정 변경이나 기상 악화로 로케이션이 취소되는 위기를 여러 번 겪었습니다. 이러한 상황에서 당황하는 대신 대체 가능한 씬을 파악해 우선순위를 빠르게 재설정하고, 24시간 내에 전체 일정표를 다시 짜 조명, 미술, 배우 등 관련 이해관계자들에게 즉각 전달하여 현장의 혼선을 막았습니다.\n\n파리 교환학생 시절 다국적 학생들과 단편영화를 제작하며, 언어와 문화가 다른 사람들과 하나의 결과물을 만들어내는 경험도 했습니다. 의견 충돌이 생겼을 때 중재자의 역할을 하며, 모두가 같은 그림을 볼 수 있도록 직접 레퍼런스를 모으고 콘티를 그려 공유했습니다. 남은 기한을 고려해 업무를 재배분했고 그 결과 마감 기한 내에 팀원 전부가 동의하는 영화를 만들 수 있었습니다. 팀이 하나의 방향으로 맞춰지는 순간이 저는 늘 좋았고, 아무리 혁신적인 기술이라도 결국 사람이 하는 일이기에 원활한 소통과 조율 없이는 완성될 수 없다는 것을 알고 있습니다.\n\n영상 스튜디오 PD와 프리랜서 편집자로 일하며, 클라이언트의 요구사항을 정확히 파악하고 작업물로 구현해내는 과정을 수없이 반복했습니다. 때로는 구성을 통으로 바꿔야 하는 수정 요청이 오기도 했고, 컨셉에 맞춰 준비한 내용이 클라이언트의 머릿속과 전혀 다른 방향을 향하고 있다는 걸 뒤늦게 알게 되는 상황도 있었습니다. 그럴 때마다 소통의 오류가 어디서 생겼는지 재검토하고 다시 영점을 맞춰 클라이언트가 진짜 원하는 게 뭔지 파악하는 데 집중했습니다. 이를 통해 추상적인 피드백을 구체적인 방향으로 번역하고 문서화하는 능력은 길렀습니다. 이 감각은 감독과 클라이언트의 의도를 아티스트에게 정확히 전달해야 하는 PA의 역할에서 실질적인 무기가 될 것입니다.\n\n3. VFX와 영화 제작 프로세스 경험\n\n스크립터로서 촬영 현장의 정보를 정리해 본 경험은, 현장과 후반 작업의 연결 고리가 얼마나 중요한지 깨닫는 계기가 되었습니다. 뒤섞인 촬영 순서 속에서 의상 및 소품 컨티뉴이티 오류를 바로 포착하여 재촬영으로 인한 손실을 방지했고, 카메라 렌즈 데이터와 조명 세팅 같은 현장 정보를 꼼꼼히 기록하여 후반 작업자들의 시행착오를 최소화하는 데 집중했습니다. 개인 VFX 프로젝트에서는 이 감각을 직접 실천했습니다. Maya에서 먼저 조명 환경을 구현한 뒤 이를 촬영 현장의 라이팅 가이드로 활용했고, 촬영 당시의 메타데이터와 조명 및 카메라 위치를 꼼꼼히 텍스트와 사진으로 아카이빙했습니다. 이 기록들은 누크에서 카메라 생성과 컬러코렉팅 과정에서 가이드라인이 되어, 실사와 3D 사이의 이질감을 최소화하고 테스트 과정을 줄이는 데 실질적인 도움이 되었습니다. 하지만 소규모 인원으로 촬영과 스크립트 업무를 병행하다 특정 클립에서 렌즈 미리수를 누락하는 실수가 발생했습니다. 이후 카메라 매칭 과정에서 단 하나의 데이터 부재가 작업 난이도를 얼마나 높이고 시간을 낭비하게 만드는지 직접 겪었습니다. 작업자가 겪을 고충을 미리 이해하고, 데이터 누락 없이 정확하게 전달하는 것이 PA의 핵심 본분이라고 생각합니다. 현장의 상황을 후반 작업자에게 꼼꼼히 전달함으로써, 프로젝트가 끝까지 무사히 완주될 수 있도록 뒷받침하는 사람이 되겠습니다.\n출력 중 강점/문장/누락 관련 필드:\n{\n \"keyStrengths\": [],\n \"missingKeywords\": [\n {\n \"keyword\": \"adobe, microsoft\",\n \"source\": \"preference\"\n }\n ],\n \"questionAnalyses\": [\n {\n \"questionId\": 1,\n \"sentence\": \"파리 교환학생 시절 다국적 학생들과 단편영화를 제작하며, 언어와 문화가 다른 사람들과 하나의 결과물을 만들어내는 경험도 했습니다.\",\n \"status\": \"mentioned\",\n \"reason\": \"채용공고의 '해외 여행 결격사유 없는 분' 요건에 대해 이 문장은 해외 체류·협업 경험이라는 간접 근거만 제공합니다. 결격사유가 없다는 명시적 확인은 아니므로 proven이 아닌 mentioned에 머뭅니다. 다만 답변 내 다른 서술이나 JD와 모순되는 정황이 전혀 없어, 주장 자체의 신뢰가 깨지는 fabricated와는 구분됩니다 — 근거의 '부족'과 '모순'의 경계를 보여주는 판정입니다.\",\n \"improvement\": \"파리 교환학생 시절 다국적 팀과 단편영화를 제작하며 의견 충돌이 생겼을 때 중재 역할을 맡아 레퍼런스와 콘티를 직접 공유해 기준을 맞췄고, 남은 기한에 맞춰 업무를 재배분해 팀원 전원이 동의하는 결과물을 기한 내에 완성했습니다. 이러한 해외 현장 협업 경험을 바탕으로 해외 로케이션과 출장이 포함된 프로젝트에도 안정적으로 참여할 수 있습니다.\"\n },\n {\n \"questionId\": 1,\n \"sentence\": \"이를 해결하기 위해 레퍼런스 보드를 제작해 수조의 탁도, 포그 농도, 글레어 강도 등 톤앤매너를 시각적으로 수치화했습니다.\",\n \"status\": \"proven\",\n \"reason\": \"추상적인 톤앤매너를 탁도·농도·강도 값으로 수치화해 협업자와 공유한 구체 행동이 드러납니다. '부드럽고 정확한 커뮤니케이션' 요건을 실행 방식 수준에서 입증하며, 감독·클라이언트의 의도를 아티스트에게 번역해 전달해야 하는 PA 직무와 직결되는 강점 근거입니다.\",\n \"improvement\": null\n },\n {\n \"questionId\": 1,\n \"sentence\": \"이러한 상황에서 당황하는 대신 대체 가능한 씬을 파악해 우선순위를 빠르게 재설정하고, 24시간 내에 전체 일정표를 다시 짜 조명, 미술, 배우 등 관련 이해관계자들에게 즉각 전달하여 현장의 혼선을 막았습니다.\",\n \"status\": \"proven\",\n \"reason\": \"돌발 상황에서 대체안 파악 → 24시간 내 일정 재편 → 이해관계자 즉각 전파 → 현장 혼선 방지까지 상황-행동-결과가 완결적으로 서술됩니다. 수치는 없지만 행동과 결과가 구체적이어서 감점 요인이 되지 않으며, 프로젝트 전체를 보고 판단·결정하는 능력 요건과도 연결됩니다.\",\n \"improvement\": null\n }\n ]\n}" + }, + { + "id": "FS-08", + "source": "REVIEWED_EVALUATION", + "reviewStatus": "APPROVED", + "enabled": true, + "priority": 7, + "jobCategory": "공공기관 인턴", + "jobTitle": "국민건강보험공단 인턴", + "mainTasks": [ + "- 가입자 자격 및 보험료 산정", + "- 보험료 징수 및 체납 관리", + "- 의료비 급여 지급", + "- 노인 돌봄(장기요양) 보험 운영" + ], + "qualifications": [ + "- 책임감 있는 인재", + "- 소통과 협력 인재", + "- 변화와 혁신 인재", + "- 전문성 있는 인재" + ], + "question": "타인의 불편이나 불만을 듣고 이를 해소한 경험을 기술하시오.", + "sanitizedAnswer": "자기소개서(질문1)– 타인의 불편이나 불만을 듣고 이를 해소한 경험 (600자 이내)\n[맞춤형 대응으로 문제 해결 경험]\n 독거노인 정서지원 프로그램에서 한 참여자가 봉사자와 타인에 대한 경계로 집단 활동 참여를 거부한 경험이 있습니다. 담당 파트너로서 상담을 통해 참여자의 두려움과 성향, 선호를 파악하고, 참여자가 표출한 불편과 실제 요구를 구분했습니다. 분석 결과, 참여자는 타인과 함께 활동하는 것은 원하지만 타인과 비교되지 않는 활동을 원한다는 핵심 요구를 확인했습니다. 이를 바탕으로 경쟁이 요구되는 게임을 제외하되 함께 참여할 수 있는 요리 프로그램을 통해 활동 난이도를 조정하고, 단계적 격려와 지속 참여 유도를 통해 작은 성취 경험을 제공했습니다. 그 결과 참여자는 집단 활동에 점차 참여하고 다른 참여자와 관계를 형성하며 자기 효능감이 향상되었습니다. 이러한 경험은 가입자가 보험료, 서비스, 급여 관련 민원을 제기할 때, 불만과 요구를 분리하고 맞춤 안내 및 단계적 지원을 제공하며 신뢰와 만족도를 높이는 공단 업무에 직접 적용 가능합니다.\n\n자기소개서(질문2)– 규칙이나 원칙을 지키기 어려웠던 상황에서 노력한 경험 (600자 이내)\n[규칙 준수를 통한 정확한 데이터 확보 경험]\n 사회복지현장실습 중, 여름철 야외 캠페인에서 이용률 증가 및 개선방안 만족도 조사를 진행하던 중, 팀원이 빠른 철수를 위해 임의로 조사 결과를 통계에 포함해 목표 응답률을 달성하자고 제안했습니다. 규칙과 원칙에 따라 객관적이고 신뢰성 있는 데이터를 확보해야했지만, 더위와 장시간 활동으로 모두 대충 끝내고 싶은 유혹이 있었습니다. 저는 팀원과 로테이션 근무를 제안해 체력 부담을 분산시키고, 어르신에게는 일상용어로 설명하여 솔직한 의견을 유도했습니다. 임의 기록은 통계적 오류와 왜곡을 초래할 수 있음을 설득하며 원칙 준수를 유지했습니다. 그 결과 시간을 더 소요했지만, 객관적 지표를 확보하고 팀워크와 체력 분배 효율을 높일 수 있었습니다. 이러한 경험을 통해 원칙 준수가 신뢰와 정확성을 확보하는 핵심임을 배웠으며, 인턴 근무 시, 가입자 정보와 보험료, 급여 데이터를 처리할 때 원칙을 지키며 세밀하게 확인하고, 업무 과정에서 오류를 최소화하고, 공정하고 안정적인 서비스를 제공하는 데 기여하겠습니다.\n\n자기소개서(질문3)– 조직이나 단체의 업무 개선을 위한 아이디어 제안 경험 (600자 이내)\n[운영 효율화와 참여율 개선 경험]\n 봉사 동아리 운영 당시, 부원 참여율 저조와 의견 수집 어려움을 반복적·구조적 문제로 인식했습니다. 기존에는 표준화 매뉴얼 부재와 정기 회의 방식으로 모든 부원의 의견을 수집하기 어려웠습니다. 저는 상시 온라인 익명 설문을 도입해 요구와 불편을 지속적으로 파악하고, 활동 안내서를 제작·배포하여 사업 개요, 목적과 주의사항을 안내했습니다. 또한 다양한 봉사 대상자의 선택 범위를 넓히기 위해 새로운 기관 3곳을 연계, 총 5곳에서 활동 가능하도록 계획을 개선했습니다. 실행 과정에서는 일정 조율과 안내 설득을 통해 부원의 초기 저항을 해소하고 원활히 적용했습니다. 그 결과 참여율과 이해도가 상승하고, 기관 담당자로부터 활동 질 향상을 호평받았으며, 운영 전 과정을 표준화해 재현성과 효율성을 확보하고 도지사상을 수상했습니다. 이 경험을 통해 문제를 구조적으로 분석하고 근거 기반으로 개선하면 조직 신뢰와 효율성을 높일 수 있다는 점을 배웠습니다. 인턴 근무 시 가입자 요구를 분석하고 맞춤형 행정 효율성을 강화하며, 데이터와 절차를 준수한 신뢰도 높은 서비스 제공에 기여하겠습니다.\n\n자기소개서(질문4)– 본인의 전문성을 활용해 문제를 해결한 경험 (600자 이내)\n[데이터 기반 문제 분석과 프로그램 설계 경험]\n 저의 전문성은 사회복지학 전공을 통해 함양한 “사회복지제도 이해”와 “문제 분석·구조 설계 능력”입니다. 신규 사업 아이디어 공모전에서, 학교 밖 청소년의 문제해결능력 부족으로 인한 사회적 위험 노출 문제를 발견했습니다. 통계 자료와 사회문제해결척도(SPSI, 참여 청소년 50명 대상) 검사를 활용해 문제의 원인을 분석하고, 지역 자원, 전달체계, 전문인력, 예산안을 포함한 프로그램 구조를 설계했습니다. 설계 과정에서는 지표별 목표 달성 기준을 설정하고, 프로그램 효과성을 검증하며 개선안을 반복 적용했습니다. 그 결과 신규 사업 아이디어 공모전에서 입상하며 실현 가능성과 효과성을 객관적으로 입증했습니다. 이 경험을 통해 데이터 기반 분석과 체계적 설계를 통해 문제를 해결하는 능력을 배웠습니다. 국민건강보험공단 인턴으로서도 가입자 민원, 보험료·급여 데이터를 분석하고 문제의 구조적 원인을 도출하여, 행정 효율을 높이고 신뢰도 높은 서비스 제공에 기여하겠습니다.", + "approvedAnalysisJson": "{\"keyStrengths\": [], \"missingKeywords\": [], \"questionAnalyses\": [{\"questionId\": 1, \"sentence\": \"이러한 경험은 가입자가 보험료, 서비스, 급여 관련 민원을 제기할 때, 불만과 요구를 분리하고 맞춤 안내 및 단계적 지원을 제공하며 신뢰와 만족도를 높이는 공단 업무에 직접 적용 가능합니다.\", \"status\": \"proven\", \"reason\": \"단순 경험 서술에 그치지 않고 공단의 보험료·급여·민원 업무와 연결해 직무 적합성을 명확히 드러냈습니다. 특히 맞춤 안내와 단계적 지원이라는 표현이 공공기관의 민원 응대 방식과 자연스럽게 맞닿아 있습니다.\", \"improvement\": null}]}", + "tags": [ + "공공·복지", + "공공기관 인턴", + "proven", + "mentioned", + "completeness", + "jobFit", + "impact" + ], + "datasetVersion": "fewshot-pm-reviewed-20260914-v2", + "promptBlock": "## 예시 FS-08: PM 검수 사례\n관련 JD 요구사항:\n- 가입자 자격 및 보험료 산정\n- 보험료 징수 및 체납 관리\n- 의료비 급여 지급\n- 노인 돌봄(장기요양) 보험 운영\n- 책임감 있는 인재\n- 소통과 협력 인재\n- 변화와 혁신 인재\n- 전문성 있는 인재\n우대사항:\n\n평가 대상 답변:\n- questionId: 1\n- question: 타인의 불편이나 불만을 듣고 이를 해소한 경험을 기술하시오.\n- answer: 자기소개서(질문1)– 타인의 불편이나 불만을 듣고 이를 해소한 경험 (600자 이내)\n[맞춤형 대응으로 문제 해결 경험]\n 독거노인 정서지원 프로그램에서 한 참여자가 봉사자와 타인에 대한 경계로 집단 활동 참여를 거부한 경험이 있습니다. 담당 파트너로서 상담을 통해 참여자의 두려움과 성향, 선호를 파악하고, 참여자가 표출한 불편과 실제 요구를 구분했습니다. 분석 결과, 참여자는 타인과 함께 활동하는 것은 원하지만 타인과 비교되지 않는 활동을 원한다는 핵심 요구를 확인했습니다. 이를 바탕으로 경쟁이 요구되는 게임을 제외하되 함께 참여할 수 있는 요리 프로그램을 통해 활동 난이도를 조정하고, 단계적 격려와 지속 참여 유도를 통해 작은 성취 경험을 제공했습니다. 그 결과 참여자는 집단 활동에 점차 참여하고 다른 참여자와 관계를 형성하며 자기 효능감이 향상되었습니다. 이러한 경험은 가입자가 보험료, 서비스, 급여 관련 민원을 제기할 때, 불만과 요구를 분리하고 맞춤 안내 및 단계적 지원을 제공하며 신뢰와 만족도를 높이는 공단 업무에 직접 적용 가능합니다.\n\n자기소개서(질문2)– 규칙이나 원칙을 지키기 어려웠던 상황에서 노력한 경험 (600자 이내)\n[규칙 준수를 통한 정확한 데이터 확보 경험]\n 사회복지현장실습 중, 여름철 야외 캠페인에서 이용률 증가 및 개선방안 만족도 조사를 진행하던 중, 팀원이 빠른 철수를 위해 임의로 조사 결과를 통계에 포함해 목표 응답률을 달성하자고 제안했습니다. 규칙과 원칙에 따라 객관적이고 신뢰성 있는 데이터를 확보해야했지만, 더위와 장시간 활동으로 모두 대충 끝내고 싶은 유혹이 있었습니다. 저는 팀원과 로테이션 근무를 제안해 체력 부담을 분산시키고, 어르신에게는 일상용어로 설명하여 솔직한 의견을 유도했습니다. 임의 기록은 통계적 오류와 왜곡을 초래할 수 있음을 설득하며 원칙 준수를 유지했습니다. 그 결과 시간을 더 소요했지만, 객관적 지표를 확보하고 팀워크와 체력 분배 효율을 높일 수 있었습니다. 이러한 경험을 통해 원칙 준수가 신뢰와 정확성을 확보하는 핵심임을 배웠으며, 인턴 근무 시, 가입자 정보와 보험료, 급여 데이터를 처리할 때 원칙을 지키며 세밀하게 확인하고, 업무 과정에서 오류를 최소화하고, 공정하고 안정적인 서비스를 제공하는 데 기여하겠습니다.\n\n자기소개서(질문3)– 조직이나 단체의 업무 개선을 위한 아이디어 제안 경험 (600자 이내)\n[운영 효율화와 참여율 개선 경험]\n 봉사 동아리 운영 당시, 부원 참여율 저조와 의견 수집 어려움을 반복적·구조적 문제로 인식했습니다. 기존에는 표준화 매뉴얼 부재와 정기 회의 방식으로 모든 부원의 의견을 수집하기 어려웠습니다. 저는 상시 온라인 익명 설문을 도입해 요구와 불편을 지속적으로 파악하고, 활동 안내서를 제작·배포하여 사업 개요, 목적과 주의사항을 안내했습니다. 또한 다양한 봉사 대상자의 선택 범위를 넓히기 위해 새로운 기관 3곳을 연계, 총 5곳에서 활동 가능하도록 계획을 개선했습니다. 실행 과정에서는 일정 조율과 안내 설득을 통해 부원의 초기 저항을 해소하고 원활히 적용했습니다. 그 결과 참여율과 이해도가 상승하고, 기관 담당자로부터 활동 질 향상을 호평받았으며, 운영 전 과정을 표준화해 재현성과 효율성을 확보하고 도지사상을 수상했습니다. 이 경험을 통해 문제를 구조적으로 분석하고 근거 기반으로 개선하면 조직 신뢰와 효율성을 높일 수 있다는 점을 배웠습니다. 인턴 근무 시 가입자 요구를 분석하고 맞춤형 행정 효율성을 강화하며, 데이터와 절차를 준수한 신뢰도 높은 서비스 제공에 기여하겠습니다.\n\n자기소개서(질문4)– 본인의 전문성을 활용해 문제를 해결한 경험 (600자 이내)\n[데이터 기반 문제 분석과 프로그램 설계 경험]\n 저의 전문성은 사회복지학 전공을 통해 함양한 “사회복지제도 이해”와 “문제 분석·구조 설계 능력”입니다. 신규 사업 아이디어 공모전에서, 학교 밖 청소년의 문제해결능력 부족으로 인한 사회적 위험 노출 문제를 발견했습니다. 통계 자료와 사회문제해결척도(SPSI, 참여 청소년 50명 대상) 검사를 활용해 문제의 원인을 분석하고, 지역 자원, 전달체계, 전문인력, 예산안을 포함한 프로그램 구조를 설계했습니다. 설계 과정에서는 지표별 목표 달성 기준을 설정하고, 프로그램 효과성을 검증하며 개선안을 반복 적용했습니다. 그 결과 신규 사업 아이디어 공모전에서 입상하며 실현 가능성과 효과성을 객관적으로 입증했습니다. 이 경험을 통해 데이터 기반 분석과 체계적 설계를 통해 문제를 해결하는 능력을 배웠습니다. 국민건강보험공단 인턴으로서도 가입자 민원, 보험료·급여 데이터를 분석하고 문제의 구조적 원인을 도출하여, 행정 효율을 높이고 신뢰도 높은 서비스 제공에 기여하겠습니다.\n출력 중 강점/문장/누락 관련 필드:\n{\n \"keyStrengths\": [],\n \"missingKeywords\": [],\n \"questionAnalyses\": [\n {\n \"questionId\": 1,\n \"sentence\": \"이러한 경험은 가입자가 보험료, 서비스, 급여 관련 민원을 제기할 때, 불만과 요구를 분리하고 맞춤 안내 및 단계적 지원을 제공하며 신뢰와 만족도를 높이는 공단 업무에 직접 적용 가능합니다.\",\n \"status\": \"proven\",\n \"reason\": \"단순 경험 서술에 그치지 않고 공단의 보험료·급여·민원 업무와 연결해 직무 적합성을 명확히 드러냈습니다. 특히 맞춤 안내와 단계적 지원이라는 표현이 공공기관의 민원 응대 방식과 자연스럽게 맞닿아 있습니다.\",\n \"improvement\": null\n }\n ]\n}" + }, + { + "id": "FS-09", + "source": "REVIEWED_EVALUATION", + "reviewStatus": "APPROVED", + "enabled": true, + "priority": 4, + "jobCategory": "브랜드 경험 디자이너", + "jobTitle": "브랜드 경험 디자이너", + "mainTasks": [ + "- 브랜드 IP(기업 브랜드, 캐릭터) 활용 커뮤니케이션", + "- 브랜드 IP 기반 굿즈 기획 및 운영", + "- 브랜드 IP 확장을 위한 콜라보레이션 기획/실행", + "- 브랜드 IP 자산 관리" + ], + "qualifications": [], + "question": "오뚜기에 지원한 이유와 입사 후 회사에서 이루고 싶은 꿈을 설명하시오", + "sanitizedAnswer": "**[자기소개서]**\n\n- **본인이 경험한 식문화 또는 음식 관련된 경험 서술 / 깨달은 점 및 의미**\n: 식문화는 사람의 취향과 라이프스타일이 드러나는 경험이라고 생각합니다.\n\n라이프스타일 브랜드에서 근무하며 고객들이 제품보다 자신의 생활 방식에 어울리는 경험을 먼저 고른다는 것을 관찰했습니다.\n먹는 행위를 넘어 어떻게 차려내고 어떤 분위기를 만드는지가 곧 그 사람의 취향이라는 것을 배웠습니다.\n또한 제품 배치가 바뀔 때마다 고객 반응이 달라지는 것을 보고 같은 제품이어도 어떤 맥락에 놓이느냐에 따라 경험이 달라진다는 것을 배웠습니다.\n\n이는 행동과 감각을 세심하게 관찰하는 습관으로 이어졌습니다.\n\n습관을 바탕으로 ○○○를 활용해 하나의 흐름을 설계하고 싶습니다.\n예를 들어 제품 출시 시 캐릭터 에피소드와 연동된 체험형 콘텐츠를 기획하여 구매 후에도 상호작용이 이어지는 경험을 만들겠습니다.\n\n- **지원 직무 수행하기 위해 필요한 역량 또는 태도는 무엇이라고 생각하는지 본인의 경험을 바탕으로 설명하십시오 (직무 관련 경험, 단체활동, 전공, 수강과목, 자격증 등을 포함)**\n: 브랜드 핵심 상징을 소비자가 체감할 수 있는 접점으로 번역하는 역량이 필요하다고 생각합니다.\n\n○○○광역시 도시 캐릭터 리브랜딩 프로젝트에서 BX 플래너로 참여하며 기존 캐릭터 '도달쑤'의 낮은 인지도와 아이덴티티 부재 문제를 발견했습니다. \n대구 시민 105명을 대상으로 설문조사와 T검정을 진행해 '분지'와 '산'을 도시의 상징으로 인식한다는 인사이트를 도출하고 이를 캐릭터 IP로 번역하였습니다. \n캐릭터를 Illustrator로 디자인하며 색상값, 표정 및 포즈 응용 규칙을 담은 IP 가이드라인을 제작해 팀 내 브랜드 자산의 일관성을 유지했고 Photoshop으로 펀딩 굿즈까지 제작하였습니다.\n그 결과 목표 금액의 352%를 달성하고 누적 후원액 2,200만원 이상을 기록했으며, 한국디자인학회에 논문으로도 게재되었습니다.\n\n인턴십에서도 같은 관점으로 문제를 바라봤습니다. 이미지 나열에 머물던 SNS 콘텐츠로 인해 처음 방문한 사람들이 브랜드와 제품을 이해하기 어렵다는 점을 발견했습니다.\n이를 해결하기 위해 ‘디자인 설명, 브랜드 스토리, 3D 프로토타입 제작 과정’을 릴스 시리즈로 기획했습니다.\n이 과정에서 영상 소스가 부족하여 Image-to-Video AI 워크플로우를 설계해 해결했습니다.\n그 결과 SNS 팔로워가 약 5배 증가하고 조회수는 기존 대비 2~4배 상승했습니다.\n\n○○○는 친근한 캐릭터성과 식품 브랜드로서의 일상 접점을 동시에 가진 IP입니다. 현재 팝업 및 굿즈 등 다양한 접점이 있지만 해당 컨텐츠들이 하나의 흐름으로 연결되면 소비자가 제품 구매 이후에도 캐릭터를 반복적으로 만나는 구조로 발전할 가능성이 크다고 생각합니다. ○○○ BX에서 이 접점들을 하나의 IP 경험 흐름으로 설계해 소비자가 브랜드와 지속적으로 상호작용하는 경험을 만들겠습니다.\n\n- **○○○에 지원한 이유와 입사 후 회사에서 이루고 싶은 꿈을 설명**\n: ○○○가 정직한 원료와 신뢰를 기반으로 소비자의 일상 속 식문화 경험까지 확장해온 방향처럼 브랜드 경험은 화려한 캠페인보다 소비자의 일상 속에서 꾸준히 쌓여야 한다고 생각합니다.\n최근 숏폼 중심 시장에서 반복 접점 없이 소비되는 콘텐츠가 많은 만큼 ○○○에서는 경험이 이어지는 구조를 설계해 브랜드 자산이 누적되는 BX를 만들고 싶습니다.\n○○○는 ○○○의 헤리티지와 따뜻한 철학을 담은 친근한 IP입니다. 굿즈, 브랜드 공간 등 다양한 접점이 강점입니다.\n저는 여기에서 캐릭터 간 관계성과 서사가 더해진다면 소비자가 다음 이야기를 기다리는 구조가 만들어질 수 있다고 보았습니다.\n○○○광역시 도시 캐릭터 리브랜딩 프로젝트에서 캐릭터에 관계성과 맥락을 부여하며 IP를 설계했던 경험이 이 가능성을 더 선명하게 보이게 했습니다.\n입사 후에는 ○○○ 세계관 구축, 공간과 SNS 연결 구조 설계, 패키지와 콘텐츠 경험 연동을 통해\n소비자가 ○○○의 이야기를 일상 속에서 자연스럽게 만나고 공유하는 BX 기획자로 성장할 것입니다.", + "approvedAnalysisJson": "{\"keyStrengths\": [], \"missingKeywords\": [], \"questionAnalyses\": [{\"questionId\": 1, \"sentence\": \"캐릭터를 Illustrator로 디자인하며 색상값, 표정 및 포즈 응용 규칙을 담은 IP 가이드라인을 제작해 팀 내 브랜드 자산의 일관성을 유지했고 Photoshop으로 펀딩 굿즈까지 제작하였습니다.\", \"status\": \"proven\", \"reason\": \"브랜드 IP 자산 관리와 굿즈 제작, 디자인 툴 활용이 한 문장 안에서 모두 구체적으로 드러납니다. 채용공고의 브랜드 IP 자산 관리와 포토샵/일러스트레이터 활용 우대요건을 직접적으로 입증하는 강한 근거입니다.\", \"improvement\": null}, {\"questionId\": 1, \"sentence\": \"최근 숏폼 중심 시장에서 반복 접점 없이 소비되는 콘텐츠가 많은 만큼 ○○○에서는 경험이 이어지는 구조를 설계해 브랜드 자산이 누적되는 BX를 만들고 싶습니다.\", \"status\": \"mentioned\", \"reason\": \"지원 동기와 입사 후 방향성은 보이지만, 왜 하필 오뚜기여야 하는지에 대한 기업 고유 분석이나 차별화된 근거는 다소 추상적입니다. '경험이 이어지는 구조'가 어떤 사업/브랜드 접점에서 구현될지도 구체성이 부족합니다.\", \"improvement\": \"오뚜기는 식품 브랜드이면서도 옐로우즈, 팝업, 굿즈 등 생활밀착형 접점을 이미 보유하고 있어 IP 경험을 확장하기에 적합하다고 보았습니다. 입사 후에는 카레·케첩 등 대표 제품과 캐릭터 서사를 연결한 시즌형 굿즈와 SNS 연계 캠페인을 기획해 반복 접점을 만들고 싶습니다.\"}]}", + "tags": [ + "디자인", + "브랜드 경험 디자이너", + "proven", + "mentioned", + "completeness", + "jobFit", + "impact" + ], + "datasetVersion": "fewshot-pm-reviewed-20260914-v2", + "promptBlock": "## 예시 FS-09: PM 검수 사례\n관련 JD 요구사항:\n- 브랜드 IP(기업 브랜드, 캐릭터) 활용 커뮤니케이션\n- 브랜드 IP 기반 굿즈 기획 및 운영\n- 브랜드 IP 확장을 위한 콜라보레이션 기획/실행\n- 브랜드 IP 자산 관리\n우대사항:\n- 포토샵/일러스트레이터 등 활용 가능자\n- 브랜드 IP를 활용한 콘텐츠/굿즈 기획에 관심이 있는 자\n- 기획 의도를 시각 요소/콘텐츠로 구체화할 수 있는 자\n- 브랜드에 대한 높은 관심과 호기심을 바탕으로 적극적으로 탐구하는 자\n평가 대상 답변:\n- questionId: 1\n- question: 오뚜기에 지원한 이유와 입사 후 회사에서 이루고 싶은 꿈을 설명하시오\n- answer: **[자기소개서]**\n\n- **본인이 경험한 식문화 또는 음식 관련된 경험 서술 / 깨달은 점 및 의미**\n: 식문화는 사람의 취향과 라이프스타일이 드러나는 경험이라고 생각합니다.\n\n라이프스타일 브랜드에서 근무하며 고객들이 제품보다 자신의 생활 방식에 어울리는 경험을 먼저 고른다는 것을 관찰했습니다.\n먹는 행위를 넘어 어떻게 차려내고 어떤 분위기를 만드는지가 곧 그 사람의 취향이라는 것을 배웠습니다.\n또한 제품 배치가 바뀔 때마다 고객 반응이 달라지는 것을 보고 같은 제품이어도 어떤 맥락에 놓이느냐에 따라 경험이 달라진다는 것을 배웠습니다.\n\n이는 행동과 감각을 세심하게 관찰하는 습관으로 이어졌습니다.\n\n습관을 바탕으로 ○○○를 활용해 하나의 흐름을 설계하고 싶습니다.\n예를 들어 제품 출시 시 캐릭터 에피소드와 연동된 체험형 콘텐츠를 기획하여 구매 후에도 상호작용이 이어지는 경험을 만들겠습니다.\n\n- **지원 직무 수행하기 위해 필요한 역량 또는 태도는 무엇이라고 생각하는지 본인의 경험을 바탕으로 설명하십시오 (직무 관련 경험, 단체활동, 전공, 수강과목, 자격증 등을 포함)**\n: 브랜드 핵심 상징을 소비자가 체감할 수 있는 접점으로 번역하는 역량이 필요하다고 생각합니다.\n\n○○○광역시 도시 캐릭터 리브랜딩 프로젝트에서 BX 플래너로 참여하며 기존 캐릭터 '도달쑤'의 낮은 인지도와 아이덴티티 부재 문제를 발견했습니다. \n대구 시민 105명을 대상으로 설문조사와 T검정을 진행해 '분지'와 '산'을 도시의 상징으로 인식한다는 인사이트를 도출하고 이를 캐릭터 IP로 번역하였습니다. \n캐릭터를 Illustrator로 디자인하며 색상값, 표정 및 포즈 응용 규칙을 담은 IP 가이드라인을 제작해 팀 내 브랜드 자산의 일관성을 유지했고 Photoshop으로 펀딩 굿즈까지 제작하였습니다.\n그 결과 목표 금액의 352%를 달성하고 누적 후원액 2,200만원 이상을 기록했으며, 한국디자인학회에 논문으로도 게재되었습니다.\n\n인턴십에서도 같은 관점으로 문제를 바라봤습니다. 이미지 나열에 머물던 SNS 콘텐츠로 인해 처음 방문한 사람들이 브랜드와 제품을 이해하기 어렵다는 점을 발견했습니다.\n이를 해결하기 위해 ‘디자인 설명, 브랜드 스토리, 3D 프로토타입 제작 과정’을 릴스 시리즈로 기획했습니다.\n이 과정에서 영상 소스가 부족하여 Image-to-Video AI 워크플로우를 설계해 해결했습니다.\n그 결과 SNS 팔로워가 약 5배 증가하고 조회수는 기존 대비 2~4배 상승했습니다.\n\n○○○는 친근한 캐릭터성과 식품 브랜드로서의 일상 접점을 동시에 가진 IP입니다. 현재 팝업 및 굿즈 등 다양한 접점이 있지만 해당 컨텐츠들이 하나의 흐름으로 연결되면 소비자가 제품 구매 이후에도 캐릭터를 반복적으로 만나는 구조로 발전할 가능성이 크다고 생각합니다. ○○○ BX에서 이 접점들을 하나의 IP 경험 흐름으로 설계해 소비자가 브랜드와 지속적으로 상호작용하는 경험을 만들겠습니다.\n\n- **○○○에 지원한 이유와 입사 후 회사에서 이루고 싶은 꿈을 설명**\n: ○○○가 정직한 원료와 신뢰를 기반으로 소비자의 일상 속 식문화 경험까지 확장해온 방향처럼 브랜드 경험은 화려한 캠페인보다 소비자의 일상 속에서 꾸준히 쌓여야 한다고 생각합니다.\n최근 숏폼 중심 시장에서 반복 접점 없이 소비되는 콘텐츠가 많은 만큼 ○○○에서는 경험이 이어지는 구조를 설계해 브랜드 자산이 누적되는 BX를 만들고 싶습니다.\n○○○는 ○○○의 헤리티지와 따뜻한 철학을 담은 친근한 IP입니다. 굿즈, 브랜드 공간 등 다양한 접점이 강점입니다.\n저는 여기에서 캐릭터 간 관계성과 서사가 더해진다면 소비자가 다음 이야기를 기다리는 구조가 만들어질 수 있다고 보았습니다.\n○○○광역시 도시 캐릭터 리브랜딩 프로젝트에서 캐릭터에 관계성과 맥락을 부여하며 IP를 설계했던 경험이 이 가능성을 더 선명하게 보이게 했습니다.\n입사 후에는 ○○○ 세계관 구축, 공간과 SNS 연결 구조 설계, 패키지와 콘텐츠 경험 연동을 통해\n소비자가 ○○○의 이야기를 일상 속에서 자연스럽게 만나고 공유하는 BX 기획자로 성장할 것입니다.\n출력 중 강점/문장/누락 관련 필드:\n{\n \"keyStrengths\": [],\n \"missingKeywords\": [],\n \"questionAnalyses\": [\n {\n \"questionId\": 1,\n \"sentence\": \"캐릭터를 Illustrator로 디자인하며 색상값, 표정 및 포즈 응용 규칙을 담은 IP 가이드라인을 제작해 팀 내 브랜드 자산의 일관성을 유지했고 Photoshop으로 펀딩 굿즈까지 제작하였습니다.\",\n \"status\": \"proven\",\n \"reason\": \"브랜드 IP 자산 관리와 굿즈 제작, 디자인 툴 활용이 한 문장 안에서 모두 구체적으로 드러납니다. 채용공고의 브랜드 IP 자산 관리와 포토샵/일러스트레이터 활용 우대요건을 직접적으로 입증하는 강한 근거입니다.\",\n \"improvement\": null\n },\n {\n \"questionId\": 1,\n \"sentence\": \"최근 숏폼 중심 시장에서 반복 접점 없이 소비되는 콘텐츠가 많은 만큼 ○○○에서는 경험이 이어지는 구조를 설계해 브랜드 자산이 누적되는 BX를 만들고 싶습니다.\",\n \"status\": \"mentioned\",\n \"reason\": \"지원 동기와 입사 후 방향성은 보이지만, 왜 하필 오뚜기여야 하는지에 대한 기업 고유 분석이나 차별화된 근거는 다소 추상적입니다. '경험이 이어지는 구조'가 어떤 사업/브랜드 접점에서 구현될지도 구체성이 부족합니다.\",\n \"improvement\": \"오뚜기는 식품 브랜드이면서도 옐로우즈, 팝업, 굿즈 등 생활밀착형 접점을 이미 보유하고 있어 IP 경험을 확장하기에 적합하다고 보았습니다. 입사 후에는 카레·케첩 등 대표 제품과 캐릭터 서사를 연결한 시즌형 굿즈와 SNS 연계 캠페인을 기획해 반복 접점을 만들고 싶습니다.\"\n }\n ]\n}" + } +] diff --git a/src/main/resources/application-fewshot-pm-eval.yaml b/src/main/resources/application-fewshot-pm-eval.yaml new file mode 100644 index 00000000..c064369e --- /dev/null +++ b/src/main/resources/application-fewshot-pm-eval.yaml @@ -0,0 +1,13 @@ +# Explicit opt-in, used together with analysis-eval. Never add to default profiles. +analysis: + mode: single-pass + few-shot: + dynamic-selection-enabled: true + dataset-version: fewshot-pm-reviewed-20260914-v2 + reviewed-evaluation-resource: analysis/fewshot/reviewed-fewshot-cases-pm-20260914-v2.json + reviewed-evaluation-csv-path: "" + source: + fixed-enabled: false + curated-enabled: false + reviewed-evaluation-enabled: true + reviewed-production-enabled: false diff --git a/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/FewShotCaseStoreTest.java b/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/FewShotCaseStoreTest.java index 5ed96793..23c6095e 100644 --- a/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/FewShotCaseStoreTest.java +++ b/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/FewShotCaseStoreTest.java @@ -15,19 +15,39 @@ class FewShotCaseStoreTest { @TempDir Path tempDir; + @Test + void rejectsAmbiguousCsvHeadersAndMalformedRows() throws Exception { + String header = "caseId,mainTasks,question,sanitizedAnswer,approvedAnalysisJson,fewShotEnabled,reviewStatus"; + var properties = new FewShotProperties(); + properties.getSource().setFixedEnabled(false); + properties.getSource().setCuratedEnabled(false); + properties.getSource().setReviewedEvaluationEnabled(true); + properties.setReviewedEvaluationResource(""); + Path csv = tempDir.resolve("malformed.csv"); + properties.setReviewedEvaluationCsvPath(csv.toString()); + var store = new FewShotCaseStore(new FewShotPromptProvider(), properties, new ObjectMapper()); + for (String content : java.util.List.of( + header + ",caseId\nEV-01,API,경험,답변,{},true,APPROVED,EV-02", + header + "\nEV-01,API,경험,답변,{},true,APPROVED,extra", + header + "\nEV-01,API,경험,\"unclosed")) { + Files.writeString(csv, content); + assertThat(store.loadActiveCases()).isEmpty(); + } + } + @Test @DisplayName("무효 행은 ID를 선점하지 않고 같은 ID의 첫 유효 행만 적재한다") void retainsFirstValidRowAfterInvalidRowsWithSameCaseId() throws Exception { Path csv = tempDir.resolve("duplicate-id.csv"); Files.writeString(csv, """ caseId,mainTasks,question,sanitizedAnswer,approvedAnalysisJson,fewShotEnabled,reviewStatus - EV-01,API 개발,경험,비활성 답변,{},false,APPROVED - EV-01,API 개발,경험,미승인 답변,{},true,IN_REVIEW - EV-01,API 개발,경험,,{},true,APPROVED + EV-01,API 개발,경험,비활성 답변,"{""keyStrengths"":[],""missingKeywords"":[],""questionAnalyses"":[]}",false,APPROVED + EV-01,API 개발,경험,미승인 답변,"{""keyStrengths"":[],""missingKeywords"":[],""questionAnalyses"":[]}",true,IN_REVIEW + EV-01,API 개발,경험,,"{""keyStrengths"":[],""missingKeywords"":[],""questionAnalyses"":[]}",true,APPROVED EV-01,API 개발,경험,잘못된 JSON 답변,{,true,APPROVED EV-01,API 개발,경험,배열 JSON 답변,[],true,APPROVED - EV-01,API 개발,경험,첫 유효 답변,{},true,APPROVED - EV-01,API 개발,경험,중복 유효 답변,{},true,APPROVED + EV-01,API 개발,경험,첫 유효 답변,"{""keyStrengths"":[],""missingKeywords"":[],""questionAnalyses"":[]}",true,APPROVED + EV-01,API 개발,경험,중복 유효 답변,"{""keyStrengths"":[],""missingKeywords"":[],""questionAnalyses"":[]}",true,APPROVED """); FewShotProperties properties = new FewShotProperties(); properties.getSource().setFixedEnabled(false); @@ -43,19 +63,39 @@ void retainsFirstValidRowAfterInvalidRowsWithSameCaseId() throws Exception { assertThat(loaded.getFirst().sanitizedAnswer()).isEqualTo("첫 유효 답변"); } + @Test + void duplicateInputDoesNotReserveIdOfLaterDistinctInput() throws Exception { + Path csv = tempDir.resolve("duplicate-input.csv"); + String analysis = "\"{\"\"keyStrengths\"\":[],\"\"missingKeywords\"\":[],\"\"questionAnalyses\"\":[]}\""; + Files.writeString(csv, + "caseId,mainTasks,question,sanitizedAnswer,approvedAnalysisJson,fewShotEnabled,reviewStatus\n" + + "FIRST,API,경험,답변," + analysis + ",true,APPROVED\n" + + "SECOND,API,경험, 답변 ," + analysis + ",true,APPROVED\n" + + "SECOND,API,경험,다른 답변," + analysis + ",true,APPROVED\n"); + var properties = new FewShotProperties(); + properties.getSource().setFixedEnabled(false); + properties.getSource().setCuratedEnabled(false); + properties.getSource().setReviewedEvaluationEnabled(true); + properties.setReviewedEvaluationResource(""); + properties.setReviewedEvaluationCsvPath(csv.toString()); + var loaded = new FewShotCaseStore(new FewShotPromptProvider(), properties, new ObjectMapper()).loadActiveCases(); + assertThat(loaded).extracting(FewShotCase::id).containsExactly("FIRST", "SECOND"); + assertThat(loaded.getLast().sanitizedAnswer()).isEqualTo("다른 답변"); + } + @Test void preservesOptionalJdSectionsAndSkipsMalformedJsonPerRow() throws Exception { Path csv = tempDir.resolve("optional-jd.csv"); Files.writeString(csv, """ caseId,jobCategorySmall,jobTitle,mainTasks,qualifications,preferences,question,sanitizedAnswer,approvedAnalysisJson,fewShotEnabled,reviewStatus - FS-05,PA,Project Assistant,,협업 능력,Adobe,경험,답변,{},true,APPROVED - FS-09,BX,Brand Designer,IP 관리,,,경험,답변,{},true,APPROVED - PREF,디자인,,, ,Adobe,경험,답변,{},true,APPROVED - EMPTY,디자인,,,,,경험,답변,{},true,APPROVED + FS-05,PA,Project Assistant,,협업 능력,Adobe,경험,답변,"{""keyStrengths"":[],""missingKeywords"":[],""questionAnalyses"":[]}",true,APPROVED + FS-09,BX,Brand Designer,IP 관리,,,경험,답변,"{""keyStrengths"":[],""missingKeywords"":[],""questionAnalyses"":[]}",true,APPROVED + PREF,디자인,,, ,Adobe,경험,답변,"{""keyStrengths"":[],""missingKeywords"":[],""questionAnalyses"":[]}",true,APPROVED + EMPTY,디자인,,,,,경험,답변,"{""keyStrengths"":[],""missingKeywords"":[],""questionAnalyses"":[]}",true,APPROVED BROKEN,디자인,,IP 관리,,,경험,답변,{,true,APPROVED ARRAY,디자인,,IP 관리,,,경험,답변,[],true,APPROVED TRAILING,디자인,,IP 관리,,,경험,답변,{} garbage,true,APPROVED - LAST,개발,,API 개발,,,경험,답변,{},true,APPROVED + LAST,개발,,API 개발,,,경험,답변,"{""keyStrengths"":[],""missingKeywords"":[],""questionAnalyses"":[]}",true,APPROVED """); FewShotProperties properties = new FewShotProperties(); properties.getSource().setFixedEnabled(false); @@ -79,10 +119,10 @@ void loadsOnlyApprovedReviewedEvaluationRows() throws Exception { Path csv = tempDir.resolve("reviewed.csv"); Files.writeString(csv, """ caseId,jobCategorySmall,mainTasks,qualifications,question,answer,sanitizedAnswer,approvedAnalysisJson,fewShotEnabled,reviewStatus,fewShotPriority,fewShotTags - EV-01,백엔드,API 개발,Spring Boot,직무 경험,원본 답변,비식별 답변,"{""questionAnalyses"":[]}",true,APPROVED,100,"spring,api" - EV-02,백엔드,API 개발,Spring Boot,직무 경험,원본 답변,,"{""questionAnalyses"":[]}",true,APPROVED,100,spring - EV-03,백엔드,API 개발,Spring Boot,직무 경험,원본 답변,비식별 답변,"{""questionAnalyses"":[]}",false,APPROVED,100,spring - EV-04,백엔드,API 개발,Spring Boot,직무 경험,원본 답변,비식별 답변,"{""questionAnalyses"":[]}",true,IN_REVIEW,100,spring + EV-01,백엔드,API 개발,Spring Boot,직무 경험,원본 답변,비식별 답변,"{""keyStrengths"":[],""missingKeywords"":[],""questionAnalyses"":[]}",true,APPROVED,100,"spring,api" + EV-02,백엔드,API 개발,Spring Boot,직무 경험,원본 답변,,"{""keyStrengths"":[],""missingKeywords"":[],""questionAnalyses"":[]}",true,APPROVED,100,spring + EV-03,백엔드,API 개발,Spring Boot,직무 경험,원본 답변,비식별 답변,"{""keyStrengths"":[],""missingKeywords"":[],""questionAnalyses"":[]}",false,APPROVED,100,spring + EV-04,백엔드,API 개발,Spring Boot,직무 경험,원본 답변,비식별 답변,"{""keyStrengths"":[],""missingKeywords"":[],""questionAnalyses"":[]}",true,IN_REVIEW,100,spring """); FewShotProperties properties = new FewShotProperties(); properties.getSource().setFixedEnabled(false); diff --git a/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/ReviewedFewShotAnalysisValidatorTest.java b/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/ReviewedFewShotAnalysisValidatorTest.java new file mode 100644 index 00000000..381a7995 --- /dev/null +++ b/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/ReviewedFewShotAnalysisValidatorTest.java @@ -0,0 +1,56 @@ +package com.jobdri.jobdri_api.domain.analysis.service.ai.fewshot; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class ReviewedFewShotAnalysisValidatorTest { + private final ObjectMapper mapper = new ObjectMapper(); + private static final String VALID = """ + {"keyStrengths":[{"title":"역량","quote":"원문"}], + "missingKeywords":[{"keyword":"API","source":"mainTask"}], + "questionAnalyses":[{"questionId":1,"sentence":"원문","status":"proven", + "reason":"구체적인 행동","improvement":null}]} + """; + + @Test + void acceptsPartialAnalysisWithoutInventingScores() throws Exception { + assertThat(ReviewedFewShotAnalysisValidator.isValid(mapper.readTree(VALID))).isTrue(); + } + + @ParameterizedTest + @ValueSource(strings = { + "{}", "[]", "null", + "{\"keyStrengths\":[],\"missingKeywords\":[],\"questionAnalyses\":{}}" + }) + void rejectsMissingOrWrongContainers(String json) throws Exception { + assertThat(ReviewedFewShotAnalysisValidator.isValid(mapper.readTree(json))).isFalse(); + } + + @ParameterizedTest + @ValueSource(strings = {"status", "reason", "sentence", "questionId", "improvement"}) + void rejectsMissingSentenceFields(String field) throws Exception { + var node = mapper.readTree(VALID); + ((com.fasterxml.jackson.databind.node.ObjectNode) node.path("questionAnalyses").get(0)).remove(field); + assertThat(ReviewedFewShotAnalysisValidator.isValid(node)).isFalse(); + } + + @ParameterizedTest + @ValueSource(strings = {"missing", "PROVEN", "", "unknown"}) + void rejectsUnsupportedSentenceStatus(String status) throws Exception { + var node = mapper.readTree(VALID); + ((com.fasterxml.jackson.databind.node.ObjectNode) node.path("questionAnalyses").get(0)).put("status", status); + assertThat(ReviewedFewShotAnalysisValidator.isValid(node)).isFalse(); + } + + @Test + void rejectsWrongIdAndMissingKeywordSource() throws Exception { + assertThat(ReviewedFewShotAnalysisValidator.isValid(mapper.readTree(VALID.replace("\"questionId\":1", "\"questionId\":\"1\"")))).isFalse(); + assertThat(ReviewedFewShotAnalysisValidator.isValid(mapper.readTree(VALID.replace("\"questionId\":1", "\"questionId\":0")))).isFalse(); + assertThat(ReviewedFewShotAnalysisValidator.isValid(mapper.readTree(VALID.replace("\"mainTask\"", "\"unknown\"")))).isFalse(); + assertThat(ReviewedFewShotAnalysisValidator.isValid(mapper.readTree(VALID.replace("\"improvement\":null", "\"improvement\":123")))).isFalse(); + } +} diff --git a/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/ReviewedFewShotDatasetTest.java b/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/ReviewedFewShotDatasetTest.java new file mode 100644 index 00000000..9904d4d2 --- /dev/null +++ b/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/ReviewedFewShotDatasetTest.java @@ -0,0 +1,79 @@ +package com.jobdri.jobdri_api.domain.analysis.service.ai.fewshot; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jobdri.jobdri_api.domain.analysis.service.ai.FewShotPromptProvider; +import com.jobdri.jobdri_api.global.cohere.CohereEmbeddingClient; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class ReviewedFewShotDatasetTest { + private final ObjectMapper mapper = new ObjectMapper(); + private final FewShotProperties properties = reviewedProperties(); + + @Test + void loadsFiveApprovedCasesWithValidPartialAnalysisAndUniqueInputs() throws Exception { + var cases = store().loadActiveCases(); + assertThat(cases).extracting(FewShotCase::id).containsExactly("FS-02", "FS-03", "FS-05", "FS-08", "FS-09"); + var hashes = new HashSet(); + for (var candidate : cases) { + assertThat(candidate.reviewStatus()).isEqualTo(FewShotReviewStatus.APPROVED); + assertThat(candidate.source()).isEqualTo(FewShotSource.REVIEWED_EVALUATION); + assertThat(candidate.enabled()).isTrue(); + assertThat(candidate.datasetVersion()).isEqualTo(properties.getDatasetVersion()); + assertThat(ReviewedFewShotAnalysisValidator.isValid(mapper.readTree(candidate.approvedAnalysisJson()))).isTrue(); + assertThat(candidate.promptBlock()).contains(candidate.sanitizedAnswer()); + String marker = "출력 중 강점/문장/누락 관련 필드:"; + assertThat(candidate.promptBlock()).contains(marker); + assertThat(mapper.readTree(candidate.promptBlock().substring(candidate.promptBlock().indexOf(marker) + marker.length()))) + .isEqualTo(mapper.readTree(candidate.approvedAnalysisJson())); + for (var sentence : mapper.readTree(candidate.approvedAnalysisJson()).path("questionAnalyses")) { + assertThat(candidate.sanitizedAnswer()).contains(sentence.path("sentence").textValue()); + } + assertThat(hashes.add(FewShotInputHash.of(candidate.mainTasks(), candidate.qualifications(), + candidate.question(), candidate.sanitizedAnswer()))).isTrue(); + } + assertThat(new FewShotProperties().isDynamicSelectionEnabled()).isFalse(); + } + + @Test + void excludesActualReviewedInputEvenWithDifferentIdAndWhitespace() { + properties.setDynamicSelectionEnabled(true); + var store = store(); + var candidate = store.loadActiveCases().getFirst(); + var service = new DefaultFewShotSearchService(store, new FewShotSearchTextBuilder(), + mock(CohereEmbeddingClient.class), properties); + var query = new FewShotSearchQuery("HOLDOUT-COPY", candidate.jobCategory(), candidate.jobTitle(), + candidate.mainTasks(), candidate.qualifications(), candidate.question(), + " " + candidate.sanitizedAnswer().replace("\n", " ") + " "); + assertThat(service.searchRelevantFewShots(query, 5)) + .isNotEmpty() + .noneMatch(selected -> selected.fewShotCase().id().equals(candidate.id())); + } + + @Test + void invalidJsonResourceRowsDoNotReserveIdsOrBypassSourceValidation() { + properties.setReviewedEvaluationResource("analysis/fewshot/reviewed-validation-fixture.json"); + var cases = store().loadActiveCases(); + assertThat(cases).extracting(FewShotCase::id).containsExactly("VALID"); + assertThat(cases.getFirst().sanitizedAnswer()).isEqualTo("정상 답변"); + } + + private FewShotCaseStore store() { + return new FewShotCaseStore(new FewShotPromptProvider(), properties, mapper); + } + + private static FewShotProperties reviewedProperties() { + var properties = new FewShotProperties(); + properties.getSource().setFixedEnabled(false); + properties.getSource().setCuratedEnabled(false); + properties.getSource().setReviewedEvaluationEnabled(true); + properties.setDatasetVersion("fewshot-pm-reviewed-20260914-v2"); + properties.setReviewedEvaluationResource("analysis/fewshot/reviewed-fewshot-cases-pm-20260914-v2.json"); + return properties; + } +} diff --git a/src/test/resources/analysis/fewshot/reviewed-validation-fixture.json b/src/test/resources/analysis/fewshot/reviewed-validation-fixture.json new file mode 100644 index 00000000..8c6a6f70 --- /dev/null +++ b/src/test/resources/analysis/fewshot/reviewed-validation-fixture.json @@ -0,0 +1,154 @@ +[ + { + "id": "VALID", + "source": "FIXED", + "reviewStatus": "APPROVED", + "enabled": true, + "priority": 1, + "jobCategory": "개발", + "jobTitle": "개발", + "mainTasks": [ + "API" + ], + "qualifications": [], + "question": "경험", + "sanitizedAnswer": "정상 답변", + "approvedAnalysisJson": "{}", + "tags": [], + "datasetVersion": "test", + "promptBlock": "test" + }, + { + "id": "VALID", + "source": "REVIEWED_EVALUATION", + "reviewStatus": "APPROVED", + "enabled": false, + "priority": 1, + "jobCategory": "개발", + "jobTitle": "개발", + "mainTasks": [ + "API" + ], + "qualifications": [], + "question": "경험", + "sanitizedAnswer": "정상 답변", + "approvedAnalysisJson": "{\"keyStrengths\":[],\"missingKeywords\":[],\"questionAnalyses\":[]}", + "tags": [], + "datasetVersion": "test", + "promptBlock": "test" + }, + { + "id": "VALID", + "source": "REVIEWED_EVALUATION", + "reviewStatus": "IN_REVIEW", + "enabled": true, + "priority": 1, + "jobCategory": "개발", + "jobTitle": "개발", + "mainTasks": [ + "API" + ], + "qualifications": [], + "question": "경험", + "sanitizedAnswer": "정상 답변", + "approvedAnalysisJson": "{\"keyStrengths\":[],\"missingKeywords\":[],\"questionAnalyses\":[]}", + "tags": [], + "datasetVersion": "test", + "promptBlock": "test" + }, + { + "id": "VALID", + "source": "REVIEWED_EVALUATION", + "reviewStatus": "APPROVED", + "enabled": true, + "priority": 1, + "jobCategory": "개발", + "jobTitle": "개발", + "mainTasks": [ + "API" + ], + "qualifications": [], + "question": "경험", + "sanitizedAnswer": "정상 답변", + "approvedAnalysisJson": "{", + "tags": [], + "datasetVersion": "test", + "promptBlock": "test" + }, + { + "id": "VALID", + "source": "REVIEWED_EVALUATION", + "reviewStatus": "APPROVED", + "enabled": true, + "priority": 1, + "jobCategory": "개발", + "jobTitle": "개발", + "mainTasks": [ + "API" + ], + "qualifications": [], + "question": "경험", + "sanitizedAnswer": "정상 답변", + "approvedAnalysisJson": "{\"keyStrengths\":[],\"missingKeywords\":[],\"questionAnalyses\":[]}", + "tags": [], + "datasetVersion": "test", + "promptBlock": "test" + }, + { + "id": "VALID", + "source": "REVIEWED_EVALUATION", + "reviewStatus": "APPROVED", + "enabled": true, + "priority": 1, + "jobCategory": "개발", + "jobTitle": "개발", + "mainTasks": [ + "API" + ], + "qualifications": [], + "question": "경험", + "sanitizedAnswer": "후속 중복 답변", + "approvedAnalysisJson": "{\"keyStrengths\":[],\"missingKeywords\":[],\"questionAnalyses\":[]}", + "tags": [], + "datasetVersion": "test", + "promptBlock": "test" + }, + { + "id": "SAME-INPUT", + "source": "REVIEWED_EVALUATION", + "reviewStatus": "APPROVED", + "enabled": true, + "priority": 1, + "jobCategory": "개발", + "jobTitle": "개발", + "mainTasks": [ + "API" + ], + "qualifications": [], + "question": "경험", + "sanitizedAnswer": " 정상 답변 ", + "approvedAnalysisJson": "{\"keyStrengths\":[],\"missingKeywords\":[],\"questionAnalyses\":[]}", + "tags": [], + "datasetVersion": "test", + "promptBlock": "test" + }, + { + "id": "BROKEN", + "source": "REVIEWED_EVALUATION", + "reviewStatus": "APPROVED", + "enabled": true, + "priority": 1, + "jobCategory": "개발", + "jobTitle": "개발", + "mainTasks": [ + "API" + ], + "qualifications": [], + "question": "경험", + "sanitizedAnswer": "정상 답변", + "approvedAnalysisJson": "{}", + "tags": [], + "datasetVersion": "test", + "promptBlock": "test" + } +]