웹 기능으로 바꾸는 순간
Node.js에서 AI API를 호출할 수 있다면, 다음 단계는 사용자가 쓸 수 있는 화면을 만드는 일입니다. AI 기능의 기본 흐름은 단순합니다.
사용자가 폼에 입력합니다. 클라이언트는 그 값을 서버로 보냅니다. 서버는 입력값을 검사하고 프롬프트를 만듭니다. 그다음 OpenAI API를 호출하고, 결과를 다시 화면에 보여줍니다.
중요한 점은 모델 호출은 반드시 서버에서 해야 한다는 것입니다. 브라우저에서 직접 호출하면 API Key가 노출됩니다.

API Route와 Server Action
Next.js에서 서버 코드를 실행하는 대표적인 방법은 두 가지입니다.
API Route 또는 Route Handler는 /api/generate 같은 HTTP 엔드포인트를 만듭니다. 클라이언트에서 fetch()로 호출합니다. 외부 앱, 모바일 앱, 다른 서비스에서도 같은 API를 호출해야 한다면 이 방식이 좋습니다. 스트리밍, 상태 코드, 헤더, 로그를 세밀하게 다루기도 쉽습니다.
Server Action은 React 컴포넌트에서 서버 함수를 직접 호출하는 방식입니다. 폼 제출과 잘 어울립니다. 별도 API 주소를 만들지 않아도 되므로 간단한 AI 글쓰기, 요약, 변환 기능을 빠르게 만들 때 편합니다.
처음에는 Server Action으로 시작해도 좋습니다. 기능이 커지고, 여러 클라이언트에서 재사용해야 한다면 API Route로 분리합니다.
클라이언트 입력 받기
AI 기능은 입력 설계가 절반입니다. 단순히 "글을 써줘"만 받으면 결과가 흔들립니다. 글감, 목적, 톤, 길이처럼 결과를 결정하는 값을 나누어 받는 편이 좋습니다.
<form action={generatePost}>
<input name="topic" placeholder="글감" />
<select name="purpose">
<option value="blog">블로그 글</option>
<option value="summary">요약</option>
</select>
<select name="tone">
<option value="friendly">친근하게</option>
<option value="professional">전문적으로</option>
</select>
<input name="length" placeholder="짧게 / 보통 / 길게" />
<button type="submit">생성하기</button>
</form>입력값을 쪼개면 프롬프트도 안정됩니다. 사용자가 어떤 결과를 원하는지 서버가 더 정확히 이해할 수 있기 때문입니다.
서버에서 검증하고 호출하기
서버에서는 먼저 입력값을 검증합니다. 빈 글감, 너무 긴 입력, 허용하지 않는 옵션은 모델에 보내기 전에 막아야 합니다.
'use server';
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
export async function generatePost(formData: FormData) {
const topic = String(formData.get('topic') || '').trim();
const purpose = String(formData.get('purpose') || 'blog');
const tone = String(formData.get('tone') || 'friendly');
const length = String(formData.get('length') || '보통');
if (!topic) return { ok: false, message: '글감을 입력하세요.' };
if (topic.length > 500) return { ok: false, message: '입력이 너무 깁니다.' };
const prompt = `
글감: ${topic}
목적: ${purpose}
톤: ${tone}
길이: ${length}
위 조건에 맞는 초안을 한국어로 작성하세요.
`;
try {
const response = await openai.responses.create({
model: 'gpt-4.1-mini',
input: prompt,
max_output_tokens: 800,
});
return { ok: true, text: response.output_text };
} catch {
return { ok: false, message: 'AI 응답을 생성하지 못했습니다.' };
}
}프롬프트 템플릿은 서버에 두는 것이 좋습니다. 그래야 사용자가 중요한 지시문을 마음대로 바꾸기 어렵고, 서비스 품질도 일정하게 유지됩니다.
결과를 화면에 보여주기
AI 응답은 항상 성공한다고 가정하면 안 됩니다. 빈 결과, 실패 결과, 재요청을 모두 생각해야 합니다.
화면에서는 세 가지 상태를 분리합니다. 생성 중이면 버튼을 비활성화하고 "작성 중"을 보여줍니다. 실패하면 짧고 이해하기 쉬운 오류 메시지를 보여줍니다. 결과가 비어 있으면 "다시 생성해 보세요" 같은 다음 행동을 제안합니다.
재요청 버튼도 중요합니다. AI 결과는 한 번에 완벽하지 않을 수 있습니다. 사용자가 같은 입력으로 다시 생성하거나, 톤과 길이를 바꿔 재생성할 수 있어야 합니다.
API Key를 클라이언트에 두면 안 되는 이유
NEXT_PUBLIC_이 붙은 환경변수는 브라우저 번들에 포함될 수 있습니다. 여기에 OpenAI API Key를 넣으면 사용자가 개발자 도구에서 키를 찾아낼 수 있습니다.
공격자는 그 키로 자신의 서버에서 대량 요청을 보낼 수 있습니다. 그러면 비용은 서비스 운영자가 부담합니다. 더 나쁜 경우, 악성 요청 때문에 계정 제한이 걸려 정상 사용자까지 기능을 쓰지 못할 수 있습니다.
따라서 API Key는 항상 서버 전용 환경변수에 둡니다. 클라이언트는 서버에 요청만 보내고, 실제 모델 호출은 서버가 담당합니다. 이것이 AI 기능을 웹 서비스에 붙일 때 지켜야 할 가장 기본적인 보안 원칙입니다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.