16. next js _post에 .md파일 작성해서 저장하고 깃 커밋 푸시

2024-07-05
Author: 포리

pages/api/create-post.js

import fs from "fs";
import path from "path";
import { simpleGit } from "simple-git";

export default async function handler(req, res) {
  if (req.method === "POST") {
    const { title, author, keywords, content } = req.body;
    const date = new Date().toISOString().split("T")[0];

    // _posts 디렉터리의 파일 목록을 가져옴
    const postsDir = path.join(process.cwd(), "_posts");
    const files = fs.readdirSync(postsDir);
    const postNumbers = files
      .map((file) => parseInt(file.split(".")[0], 10))
      .filter((num) => !isNaN(num));

    const nextPostNumber = Math.max(0, ...postNumbers) + 1;
    const nextFileName = `${nextPostNumber}. ${title.replace(/\s+/g, "-")}.md`;

    const filePath = path.join(postsDir, nextFileName);

    const fileContent = `---
title: "${nextPostNumber}. ${title}"
date: "${date}"
author: "${author}"
keywords: ${JSON.stringify(keywords)}
---

${content}
`;

    try {
      // 파일 작성
      await fs.promises.writeFile(filePath, fileContent);

      // Git 커밋 및 푸시
      const git = simpleGit();
      await git.add(filePath);
      await git.commit(`Add new post: ${nextPostNumber}. ${title}`);
      await git.push("origin", "main"); // 'main' 브랜치에 푸시

      // 성공적으로 응답 전송
      res
        .status(200)
        .json({
          message: "Post created, committed, and pushed",
          fileName: nextFileName,
        });
    } catch (err) {
      // 오류 발생 시 응답 전송
      res.status(500).json({ error: "Failed to create post" });
    }
  } else {
    res.status(405).json({ error: "Method not allowed" });
  }
}

pages/create-post.js

import { useState } from "react";
import styles from "../styles/CreatePost.module.css";

export default function CreatePost() {
  const [title, setTitle] = useState("");
  const [author, setAuthor] = useState("");
  const [keywords, setKeywords] = useState("");
  const [content, setContent] = useState("");
  const [message, setMessage] = useState("");

  const handleSubmit = async (e) => {
    e.preventDefault();

    const response = await fetch("/api/create-post", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        title,
        author,
        keywords: keywords.split(","),
        content,
      }),
    });

    const data = await response.json();
    if (response.ok) {
      setMessage(`포스트가 생성되었습니다: ${data.fileName}`);
      setTitle("");
      setAuthor("");
      setKeywords("");
      setContent("");
    } else {
      setMessage(`오류: ${data.error}`);
    }
  };

  return (
    <div className={styles.container}>
      <h1>새 글 작성하기</h1>
      <form onSubmit={handleSubmit} className={styles.form}>
        <div>
          <label className={styles.label}>제목:</label>
          <input
            type="text"
            value={title}
            onChange={(e) => setTitle(e.target.value)}
            className={styles.input}
            required
          />
        </div>
        <div>
          <label className={styles.label}>작성자:</label>
          <input
            type="text"
            value={author}
            onChange={(e) => setAuthor(e.target.value)}
            className={styles.input}
            required
          />
        </div>
        <div>
          <label className={styles.label}>키워드 (쉼표로 구분):</label>
          <input
            type="text"
            value={keywords}
            onChange={(e) => setKeywords(e.target.value)}
            className={styles.input}
          />
        </div>
        <div>
          <label className={styles.label}>내용:</label>
          <textarea
            value={content}
            onChange={(e) => setContent(e.target.value)}
            className={styles.textarea}
            required
            rows="10"
            cols="50"
          />
        </div>
        <button type="submit" className={styles.button}>
          글 작성
        </button>
      </form>
      {message && <p>{message}</p>}
    </div>
  );
}