useChat의 Stale body 값
useChat의 Stale body 값
useChat 훅 레벨의 body 파라미터로 동적 정보를 전달할 때 데이터가 초기 컴포넌트 렌더링 시점의 값에 머물러 있는 문제가 있어요. 원인과 해결 방법을 정리해 드릴게요.
출처: 문서
본문
문제 (Issue)
useChat을 사용하고 훅 레벨에서 body 파라미터로 동적 정보를 전달할 때, 데이터가 stale 상태로 남아 초기 컴포넌트 렌더링 시점의 값만 반영합니다. 이는 body 구성이 훅 초기화 시 한 번만 캡처되고 이후 컴포넌트가 다시 렌더링될 때 갱신되지 않기 때문입니다.
// Problematic code - body data will be stale
export default function Chat() {
const [temperature, setTemperature] = useState(0.7);
const [userId, setUserId] = useState('user123');
// This body configuration is captured once and won't update
const { messages, sendMessage } = useChat({
transport: new DefaultChatTransport({
api: '/api/chat',
body: {
temperature, // Always the initial value (0.7)
userId, // Always the initial value ('user123')
},
}),
});
// Even if temperature or userId change, the body in requests will still use initial values
return (
<div>
<input
type="range"
value={temperature}
onChange={e => setTemperature(parseFloat(e.target.value))}
/>
{/* Chat UI */}
</div>
);
}
배경 (Background)
훅 레벨의 body 구성은 초기 렌더링 중에 한 번만 평가되며 컴포넌트 상태가 바뀌어도 다시 평가되지 않습니다.
해결책 (Solution)
동적 변수는 훅 레벨 대신 sendMessage 함수의 두 번째 인자로 전달하세요. 요청 레벨 옵션은 호출할 때마다 평가되며 훅 레벨 옵션보다 우선합니다.
export default function Chat() {
const [temperature, setTemperature] = useState(0.7);
const [userId, setUserId] = useState('user123');
const [input, setInput] = useState('');
const { messages, sendMessage } = useChat({
// Static configuration only
transport: new DefaultChatTransport({
api: '/api/chat',
}),
});
return (
<div>
<input
type="range"
value={temperature}
onChange={e => setTemperature(parseFloat(e.target.value))}
/>
<form
onSubmit={event => {
event.preventDefault();
if (input.trim()) {
// Pass dynamic values as request-level options
sendMessage(
{ text: input },
{
body: {
temperature, // Current value at request time
userId, // Current value at request time
},
},
);
setInput('');
}
}}
>
<input value={input} onChange={e => setInput(e.target.value)} />
</form>
</div>
);
}
대안: 동적 훅 레벨 구성
변화에 대응하는 훅 레벨 구성이 필요하다면 설정 값을 반환하는 함수를 사용할 수 있습니다. 다만 컴포넌트 상태의 경우 useRef를 사용해 현재 값에 접근해야 합니다.
export default function Chat() {
const temperatureRef = useRef(0.7);
const { messages, sendMessage } = useChat({
transport: new DefaultChatTransport({
api: '/api/chat',
body: () => ({
temperature: temperatureRef.current, // Access via ref.current
sessionId: getCurrentSessionId(), // Function calls work directly
}),
}),
});
// ...
}
권장사항: 요청 레벨 구성이 컴포넌트 상태에 더 간단하고 신뢰할 수 있습니다. 컴포넌트 수명 동안 변하는 동적 값을 전달해야 한다면 이것을 사용하세요.
서버 측 처리
서버 측에서는 요청 본문을 구조 분해(destructure)해서 커스텀 필드를 가져옵니다.
// app/api/chat/route.ts
export async function POST(req: Request) {
const { messages, temperature, userId } = await req.json();
const result = streamText({
model: 'openai/gpt-4.1-mini', // Supports dynamic temperature settings
messages: await convertToModelMessages(messages),
temperature, // Use the dynamic temperature from the request
// ... other configuration
});
return createUIMessageStreamResponse({
stream: toUIMessageStream({ stream: result.stream }),
});
}
자세한 내용은 chatbot 요청 구성 문서를 참고하세요.
더 알아보기 (Learn more)
- AI SDK 공식 문서에서 다른 트러블슈팅 항목도 확인해 보세요.