티스토리 뷰

[8주차 추가과제 - 프롬프트 수정]
# 항상 고정된 JSON 형태로 응답
   - 사용자 질문에 대한 답변
   - 답변과 관련된 추가 질문 추천 리스트


하려는 방식 (responseSchema로 강제)

→ Gemini가 토큰을 생성하는 단계에서부터 이 스키마를 벗어나지 못하게 막음

// 인사말, 마크다운, 설명 같은 거 절대 못 붙임. 무조건 깔끔한 JSON만 나오게

 

  • 기존 방식: 친구한테 "JSON으로 답해줘~" 하고 부탁 → 친구가 까먹고 인사말 붙일 수도 있음
  • 새 방식: 친구한테 빈칸 뚫린 양식지를 주고 "여기에만 채워" → 양식 벗어날 방법이 없음

[순서]

STEP 1. 백엔드 수정 — route.js 코드 분석

  • 시스템 프롬프트에 추천 질문 규칙 추가
  • JSON 응답 스키마 정의 (responseSchema)
  • generationConfig에 스키마 적용
  • 응답 처리 로직 변경 (reply → answer + suggestedQuestions)

STEP 2. 프론트엔드 수정 — 응답 구조 맞추기

  • API 응답 키 변경 반영 (data.reply → data.answer)
  • 답변을 화면에 렌더링하는 부분 수정

STEP 3. 프론트엔드 추가 — 추천 질문 UI

  • 추천 질문을 버튼 형태로 표시
  • 버튼 클릭 시 자동으로 질문 전송
  • 로딩/에러 상태 처리

STEP 4. 동작 테스트

  • 한국어 질문 → 한국어 답변 + 한국어 추천 질문 확인
  • 영어 질문 → 영어 답변 + 영어 추천 질문 확인
  • 추천 질문 클릭 → 자동 전송 동작 확인
  • 이력서 외 질문 → "모른다" 답변 + 적절한 추천 질문 확인

 

 

generationConfig

-> Gemini 모델한테 "답변을 어떻게 생성할지"에 대한 설정을 넘기는 객체

 

예시)

generationConfig

 

[STEP 1]

 

1. import에 SchemaType 추가(스키마 타입 지정)

import { GoogleGenerativeAI, SchemaType } from '@google/generative-ai'

 

2. 시스템 프롬프트에 "추천 질문 규칙" 추가

JSON 출력은 스키마가 강제하지만, 추천 질문의 품질(말투, 길이, 시점)은 프롬프트에서 잡아줘야 함

 

스키마 = "구조"를 잡는다 (필드 이름, 타입, 개수)
프롬프트 = "내용 품질"을 잡는다 (말투, 시점, 구체성)

 

 

3. responseSchema + responseMimeType 추가

 

generationConfig: {
  responseMimeType: 'application/json',
  responseSchema: responseSchema,
}

 

-> JSON 안 깨지도록(핵심)

 

4. 응답 처리 변경

 

text()로 받은 문자열을 JSON.parse() → 안전하게 파싱(읽고 해석할 수 있게 만드는 작업)

응답 구조: { reply } → { answer, suggestedQuestions }


[STEP 2] 기존 동작 유지 + 추천 질문 데이터 받기

 

현재 프론트엔드에서 API를 호출하고 답변을 화면에 뿌리는 부분의 코드 필요
-> 나의 경우엔 ChatWidget.jsx 파일

 

const res = await fetch('/api/chat' 

-> 수정할 부분

 

const data = await res.json()
setMessages(prev => [...prev, { role: 'assistant', content: data.reply }])

응답 키 맞추기

data.replydata.answer

// 추천 질문은 받아서 state에 저장만 함 (UI에는 아직 표시 X)

// 목표: 기존 동작이 안 깨지게 유지하기

 

1. 추천 질문용 state 추가

const [suggestedQuestions, setSuggestedQuestions] = useState([])

 

2. 새 질문 보낼 때 이전 추천 질문 비우기

setSuggestedQuestions([])

답변 생성 중인데 옛날 추천 질문이 떠있으면 사용자가 헷갈림

로딩 = 추천 질문도 사라지게

 

3. 응답 처리

// Before
setMessages(prev => [...prev, { role: 'assistant', content: data.reply }])

// After
setMessages(prev => [...prev, { role: 'assistant', content: data.answer }])
setSuggestedQuestions(data.suggestedQuestions || [])


[STEP 3] 

  • 추천 질문을 버튼 형태로 표시 → map()으로 렌더링
  • 버튼 클릭 시 자동으로 질문 전송 → handleSuggestedClick
  • 로딩/에러 상태 처리 → !isLoading && length > 0 조건

 

1. send 함수를 sendMessage(text)로 분리

 

기존 send는 input state에서만 텍스트를 가져옴.

그런데 추천 질문 버튼을 클릭했을 때는 사용자가 input에 타이핑한 게 아니라 버튼에 적힌 텍스트를 보내야함.

→ 텍스트의 출처가 2개(직접 입력 / 버튼 클릭)가 됐으므로,

어디서 오든 받아서 처리할 수 있게 인자로 받는 형태로 분리

(=> 함수의 재사용성 :  같은 로직을 여러 곳에서 호출할 수 있게 만드는 패턴)

 

const sendMessage = async (text) => {   // ← text를 인자로 받음
  if (!text || isLoading) return  // ...fetch 로직
}

const send = () => sendMessage(input.trim())   // 직접 입력용

 

2. handleSuggestedClick 함수 추가

 

const handleSuggestedClick = (question) => sendMessage(question)

 

// 추천 질문 버튼을 누르면 그 질문 텍스트를 그대로 sendMessage에 넘겨서 전송하게 하는 함수

(확장성, 가독성, 관심사 분리: 서로 다른 역할 하는 코드를 분리해서, 각자 자기일만 하게 만들기)

 

3. 추가 질문 버튼 렌더링 영역 추가

 

{!isLoading && suggestedQuestions.length > 0 && (
  <div className='flex flex-col gap-1.5 mt-1'>
    {suggestedQuestions.map((q, i) => (
      <button key={i}
        onClick={() => handleSuggestedClick(q)}
        className={`...`}>
        {q}
      </button>
    ))}
  </div>
)}

 

key는 React가 리스트의 각 요소를 구별하기 위해 필요. 없으면 콘솔 경고.

 

&& 연결로 둘 다 true일 때만 표시

- !isLoading : 로딩 중이 아닐 때

- suggestedQuestions.length > 0 : 추천 질문이 1개 이상 있을 때

 

*MAP의 역할

추천 질문 배열을 → 클릭 가능한 버튼들로 화면에 그리기

(추천 질문 3개를 사용자가 클릭만 하면 자동 전송되게)

 

{suggestedQuestions.map((q, i) => (
  <button key={i} onClick={() => handleSuggestedClick(q)}>
    {q}
  </button>
))}


[STEP 4] 동작테스트

한국어질문, 영어질문, 추천 질문 클릭, 이력서 외 질문

이렇게 4가지 테스트

 

 

이런 식으로 잘 대답하고 있음. 모르는 내용은 모른다고 함.

 

근데 하나 문제가 있다면 추천 질문을 클릭했을 때

내가 입력한 프롬포트의 양이 많지 않다보니 답변을 제대로 못하는 경우가 발생

이는 추후 수정할 예정이고 git허브에 push 해두고 끝.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/05   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함