Next.js 15와 OpenAI를 사용한 ‘스마트’ Markdown 노트 테이커 구축

발행: (2025년 12월 23일 오후 11:45 GMT+9)
4 분 소요
원문: Dev.to

Source: Dev.to

Intro

2025년에는 표준 CRUD 앱만으로는 충분하지 않습니다. 사용자는 애플리케이션이 생각하기를 기대합니다. 오늘은 마크다운을 저장할 뿐만 아니라 AI를 활용해 자동으로 태그와 요약을 생성하는 스마트 노트‑테이킹 앱을 만들겠습니다.

The Stack

  • Frontend/Backend: Next.js 15 (App Router)
  • Styling: Tailwind CSS
  • Database: Prisma + PostgreSQL (via Supabase)
  • AI: OpenAI API

1. 프로젝트 설정

먼저 최신 Next.js 기능을 사용하여 프로젝트를 초기화하세요:

npx create-next-app@latest smart-notes --typescript --tailwind --eslint
cd smart-notes
npm install @prisma/client lucide-react openai
npx prisma init

2. 데이터베이스 스키마

우리는 간단하지만 효과적인 모델이 필요합니다. prisma/schema.prisma 파일에서:

model Note {
  id        String   @id @default(cuid())
  title     String
  content   String   @db.Text
  tags      String[]
  summary   String?  @db.Text
  createdAt DateTime @default(now())
}

3. “스마트” 로직 (서버 액션)

별도의 API 라우트를 사용하는 대신, Next.js 서버 액션을 활용합니다. 이렇게 하면 로직이 서버에 묶여 보안이 강화됩니다.

app/actions/notes.ts 파일을 생성합니다:

"use server";
import { PrismaClient } from "@prisma/client";
import OpenAI from "openai";

const prisma = new PrismaClient();
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

export async function createSmartNote(formData: FormData) {
  const title = formData.get("title") as string;
  const content = formData.get("content") as string;

  // AI Magic: Generate Summary and Tags
  const aiResponse = await openai.chat.completions.create({
    model: "gpt-4o-mini",
    messages: [
      {
        role: "system",
        content:
          "Analyze the following note. Return a JSON object with a 'summary' (1 sentence) and 'tags' (array of strings).",
      },
      { role: "user", content: content },
    ],
    response_format: { type: "json_object" },
  });

  const { summary, tags } = JSON.parse(
    aiResponse.choices[0].message.content || "{}"
  );

  const note = await prisma.note.create({
    data: { title, content, summary, tags },
  });

  return note;
}

4. UI: 깔끔하고 기능적

app/page.tsx에서 입력을 처리하는 간단한 폼을 만듭니다. AI가 “생각”하고 있는 동안 로딩 상태를 표시하기 위해 useFormStatus를 사용할 것입니다.

import { createSmartNote } from "./actions/notes";

export default function Home() {
  return (
    <main className="p-8">
      <h1 className="text-2xl font-bold mb-4">Smart Markdown Notes 📝</h1>

      <form action={createSmartNote} className="space-y-4">
        <div>
          <label htmlFor="title" className="block font-medium">
            Title
          </label>
          <input
            id="title"
            name="title"
            type="text"
            required
            className="mt-1 block w-full rounded border p-2"
          />
        </div>

        <div>
          <label htmlFor="content" className="block font-medium">
            Content (Markdown)
          </label>
          <textarea
            id="content"
            name="content"
            rows={6}
            required
            className="mt-1 block w-full rounded border p-2"
          />
        </div>

        <button
          type="submit"
          className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
        >
          Save Smart Note
        </button>
      </form>

      {/* Logic to fetch and map notes would go here */}
    </main>
  );
}

5. 왜 이것이 2025년에 중요한가

  • Server Actions: 복잡한 useEffect와 fetch 호출의 필요성을 없앱니다.
  • AI Integration: “Full Stack”에서 “AI‑Native”로 전환하여 인텔리전스를 데이터 흐름에 직접 삽입합니다.
  • Type Safety: TypeScript는 AI 응답이 데이터베이스 스키마와 일치하도록 보장합니다.

Conclusion

오늘날 풀스택 앱을 구축하는 것은 서비스들을 조율하는 것입니다. Next.js의 강력함과 AI를 결합하면 단순히 데이터 저장을 넘어 실제적인 유용성을 제공하는 도구를 만들 수 있습니다.

Back to Blog

관련 글

더 보기 »