
원래 없던 계획이긴 한데, 날씨데이터를 이용해서 openai로 날씨에 얘기해주는 text를 생성해보기로 함.
각각의 캐스터 색깔에 맞는 말투로 텍스트를 정적으로 만들어 놓고, 동적으로 날씨데이터를 받아서 텍스트 사이사이에 넣어주려고 했는데, 그러자니 늘 똑같은 말투로 날씨를 알려주면 재미가 없을 것 같아서 openai를 써보면 좋겠다는 생각이 들었음.
한번도 써보지 않았지만 별로 어려울 것 없을거라 생각하고 시작..!
사용방법은 그렇게 어렵지 않음. docs 보고 그대로 하면 된다.
opneai docs(Node.js)
npm install --save openai
# or
yarn add openai
export OPENAI_API_KEY='your-api-key-here'
response는 아래와 같은 형태로 받는다.
{
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": "gpt-3.5-turbo-0125",
"system_fingerprint": "fp_44709d6fcb",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "\n\nHello there, how may I assist you today?",
},
"logprobs": null,
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 12,
"total_tokens": 21
}
}
답변을 추출하려면 다음을 사용해서 추출하면됨.
completion.choices[0].message.content
// app>api>weather-message>route.ts
import { generateWeatherMessage } from "@/app/service/openai";
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
const { caster, weather } = await req.json();
if (!caster || !weather) {
return NextResponse.json(
{ error: "Caster and weatherData are required" },
{ status: 400 }
);
}
try {
const res = await generateWeatherMessage(caster, weather);
return NextResponse.json(res);
} catch (error) {
console.error("Error generating text:", error);
return NextResponse.json(
{ error: "Error generating text" },
{ status: 500 }
);
}
}
onst response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gpt-4o",
messages: [systemMessage, userMessage],
max_tokens: 150,
}),
});
그래서 전체 코드는 아래와 같이 구성
//src>app>service>openai.ts
export type WeatherData = {
description: string;
temp: number;
temp_max: number;
temp_min: number;
precipitation: number | null;
wind: number;
snow: number | null;
humidity: number;
sunrise: number;
sunset: number;
icon: string;
};
export type Caster =
| "이장님"
| "할머니"
| "엄마"
| "여자캐스터"
| "남자캐스터"
| "KPOP매니아"
| "먹방유튜버";
const casterDescriptions: Record<Caster, string> = {
이장님:
"날씨를 비유적으로 돌려서 설명하는 불만이 많고 유머러스한 충청도 시골 이장님",
할머니:
"나이 많고 무심한듯한 전라도 사투리를 사용하며 잔소리하는 욕쟁이 할머니",
엄마: "따뜻하게 돌봐주는 엄마",
여자캐스터: "밝고 활기찬 기상캐스터",
남자캐스터: "자신감있는 기상캐스터",
KPOP매니아: "한국 노래를 좋아하고 모르는 노래가 없는 한국 노래 매니아",
먹방유튜버: "유머러스하고 날씨에 딱 어울리는 음식 추천을 잘하는 먹방유튜버",
};
type Message = {
caster: Caster;
message: string;
};
export async function generateWeatherMessage(
caster: Caster,
weatherData: WeatherData
): Promise<Message> {
const systemMessage = {
role: "system",
content: `너는 ${casterDescriptions[caster]}(으)로 행동할거야. 캐릭터 성격에 부합하는 말만 해줘. `,
};
const userMessage = {
role: "user",
content: `
날씨 설명: ${weatherData.description}
현재 기온: ${Math.floor(weatherData.temp)}도
최고 기온: ${Math.floor(weatherData.temp_max)}도
최저 기온: ${Math.floor(weatherData.temp_min)}도
강수량: ${weatherData.precipitation}mm
바람: ${weatherData.wind}m/s
습도: ${weatherData.humidity}%
캐릭터가 오늘 날씨에 대해 얘기해줘. 답변은 3-4문장으로 해줘. 날씨정보에 따라 캐릭터의 성격에 맞는 날씨 설명을 해줘. 단, '바람', '습도'라는 직접적인 단어사용은 하지마. `,
};
const apiKey = process.env.OPENAI_API_KEY;
if (!apiKey) {
throw new Error("API key is missing");
}
try {
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gpt-4o",
messages: [systemMessage, userMessage],
max_tokens: 150,
}),
});
if (!response.ok) {
throw new Error(`Error ${response.status}`);
}
const data = await response.json();
const message = {
caster,
message: data.choices[0].message.content,
};
return message;
} catch (error) {
console.error("Error generating text:", error);
throw error;
}
}
이유는 모르겠지만 4o버전이 대답을 좀더 내가 원하는 방향으로 해주는 것 같아서 4o를 사용하고 있지만 3.5 버전으로도 충분히 좋았음.
근데

서버에러가 계속 나서 이틀을 고민함..
처음에는 환경변수에서 api를 못 읽어오는 문제가 있었음. 그래서 dotenv 패키지도 설치해봤는데, 아무 소용없어서 다시 돌려놓고, openai 관련된 로직을 다 살펴봤는데도 문제가 없었음.
그래서 oepnai 대시보드를 확인해보니 usage:cost라는게 있음.
아무래도 유료인가하여 Billing에 들어가서 5달러만 내고 credit을 추가함.

그래도 안되는 것 같았는데, 어느 순간 갑자기 response가 찍히기 시작함.ㅋㅎㅋㅎ.. 페이를 하고 약간의 시간이 지나야 하나봄.
크레딧이 없어서 에러가 났던것 같은데 확인해보고자, 다른 구글 계정으로 로그인 후 프로젝트 apiKey 생성하고, 크레딧 없이 다시 api 요청 해봄.
안 됨!
크레딧이 필요함.
어쨌든.. response받아서 UI에서 보여주면,
import React, { useEffect, useState } from "react";
import { useCaster } from "../context/CasterContext";
import { useWeather } from "../context/WeatherContext";
export default function WeatherMessage() {
const [message, setMessage] = useState("");
const [isLoading, setIsLoading] = useState(false);
const { weather } = useWeather();
const { caster } = useCaster();
useEffect(() => {
async function fetchWeatherMessage() {
if (caster && weather) {
try {
setIsLoading(true);
const response = await fetch("/api/weather-message", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ caster, weather }),
});
const data = await response.json();
setMessage(data.message);
setIsLoading(false);
} catch (error) {
console.error("Error fetching weather message:", error);
}
}
}
fetchWeatherMessage();
}, [caster, weather]);
return (
<>
<div className="flex w-96 items-center justify-center bg-indigo-100 rounded-3xl p-6 h-72">
{isLoading && <p>Generating message...</p>}
{!isLoading && message && <p>{message}</p>}
</div>
</>
);
}
짠!
