[nextjs]앱라우트,페이지라우트/App Route,Page Route

👉🏻App Route와 Page Route를 이해하기 위해서 데이터베이스 없는 간단한 방명록을 만들어 봅니다.
To understand App Routes and Page Routes, we will build a simple guestbook without a database.

👉🏻 입력되는 값은 배열에 저장됩니다.
The input values ​​are stored in an array.

📁 프로젝트 생성
Create Project

npx create-next-app@latest

📁 API 만들기 / Creating an API

▷ Next.js는 확장자가 .js, .ts, .jsx, .tsx인 것들을 모두 동일하게 route 파일로 인식합니다.
Next.js treats files with the extensions .js, .ts, .jsx, and .tsx equally as route files.

▷ tsx나 jsx를 사용해도 문제가 없지만 HTML이나 JSX를 사용할때 X가 붙습니다.
There is no issue with using TSX or JSX, but the “X” is included when using HTML or JSX.

▷ JSX는 자바스크립트 객체를 태그형으로 만든 것입니다.
JSX is a tag-like representation of JavaScript objects.

▷ 전체 디렉토리 구조
Overall directory structure

myapp3/
├── app/                        # 1.앱라우트 / App Router
│   ├── api/                    # 1)백엔드 API / BackEnd API
│   │   └── guestbookse/        
│   │       └── route.js        # App Router API : /api/guestbookse
│   ├── guestbookse/            # 2)프론트 앤드 / FrontEnd
│   │   └── page.tsx            # App Router Front: /guestbookse
│   ├── favicon.ico             
│   ├── globals.css             
│   ├── layout.tsx              
│   └── page.tsx                # 메인 홈 / Main Home: /
│
└── pages/                      # 1.페이지라우터 / Pages Router
    ├── api/                    
    │   └── guestbook.js        # 1)백엔드 API / BackEnd API: /api/guestbook
    └── guestbook-page.js       # 2)프론트 앤드 / FrontEnd: /guestbook-page
 

✔️App Route방식(최신방식)
App Router approach (latest method)

— project/app 디렉토리 안에 다음 처럼 디렉토리와 파일을 만듭니다.
Create the following directories and files inside the project/app directory.

myapp3/
└── app/                        # 1.앱라우트 / App Router
    ├── api/                    # 1)백엔드 API / BackEnd API
    │   └── guestbookapp/        
    │       └── route.js        # App Router API : /api/guestbookse
    └── guestbookapp/            # 2)프론트 앤드 / FrontEnd
        └── page.tsx            # App Router Front: /guestbookse

— route.js파일에 다음 코드를 작성합니다.
Write the following code in the route.js file.

import { NextResponse } from 'next/server';

const guestbookData = [
  { id: 1, name: '2B', content: '니어오토마타 / Nier:Automata' },
  { id: 2, name: '9S', content: 'Next.js API App Routes.' },
];

// GET 요청 처리 (데이터 조회)
// Handle GET requests (data retrieval)
export async function GET() {
  return NextResponse.json(guestbookData);
}

// POST 요청 처리 (데이터 등록)
// Handle POST requests (data registration)
export async function POST(request) {
  const body = await request.json();
  const { name, content } = body;

  if (!name || !content) {
    return NextResponse.json({ message: '이름과 내용을 입력해주세요. / Please enter both name and content.' }, { status: 400 });
  }

  const newPost = {
    id: Date.now(),
    name,
    content,
  };

  guestbookData.push(newPost);
  return NextResponse.json(newPost, { status: 201 });
}

— 서버를 실행하고 API를 테스트합니다.
Run the server and test the API.

# 터미널에서 서버 실행 / Run the server in the terminal
npm run dev

# 브라우저에서 아래 주소로 접속하기 / Access the address below in your browser.
http://localhost:3000/api/guestbookapp
guestabookapp-App Router

— page.tsx파일에 다음 코드를 작성합니다.
Write the following code in the page.tsx file.

"use client";

import { useEffect, useState } from "react";

// 방명록 데이터의 타입 정의
// Define the type for guestbook data
interface GuestbookItem {
  id: number;
  name: string;
  content: string;
}

export default function GuestbookPage() {
  
  // 상태(State) 정의
  // Define state
  const [posts, setPosts] = useState<GuestbookItem[]>([]);
  const [name, setName] = useState("");
  const [content, setContent] = useState("");
  const [isLoading, setIsLoading] = useState(true);

  // API에서 방명록 목록 가져오는 함수 (GET)
  // Function to fetch guestbook entries from the API (GET)
  const fetchPosts = async () => {
    try {
      const response = await fetch("/api/guestbookapp");
      if (!response.ok) throw new Error("데이터를 가져오는데 실패했습니다. / Failed to fetch data.");
      const data = await response.json();
      setPosts(data);
    } catch (error) {
      console.error(error);
    } finally {
      setIsLoading(false);
    }
  };

  // 페이지가 처음 켜질 때 API 호출하기
  // Call the API when the page first loads
  useEffect(() => {
    fetchPosts();
  }, []);

  // 새 방명록 등록하는 함수 (POST)
  // Function to register a new guestbook entry (POST)
  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault(); // 새로고침 방지 / Prevent page refresh

    if (!name.trim() || !content.trim()) {
      alert("이름과 내용을 모두 입력해 주세요./Please enter both name and content.");
      return;
    }

    try {
      const response = await fetch("/api/guestbookapp", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ name, content }),
      });

      if (response.ok) {
        // 등록 성공 시 입력창을 비우고 목록 새로고침
        // If registration is successful, clear the input fields and refresh the list
        setName("");
        setContent("");
        fetchPosts();
      } else {
        alert("등록에 실패했습니다./Failed to register the entry.");
      }
    } catch (error) {
      console.error(error);
    }
  };

  return (
    <div className="p-10 max-w-[600px] mx-auto font-sans">
      <h1 className="text-3xl font-bold">📓 방명록 / Guestbook (App Router)</h1>

      {/* 방명록 입력 폼 / Guestbook Entry Form */}
      <form onSubmit={handleSubmit} style={{ display: "flex", flexDirection: "column", gap: "10px", margin: "20px 0" }}>
        <input
          type="text"
          placeholder="이름/Name"
          value={name}
          onChange={(e) => setName(e.target.value)}
          style={{ padding: "8px", border: "1px solid #ccc", borderRadius: "4px" }}
        />
        <textarea
          placeholder="내용을 입력하세요/Please enter your message"
          value={content}
          onChange={(e) => setContent(e.target.value)}
          style={{ padding: "8px", border: "1px solid #ccc", borderRadius: "4px", minHeight: "8px" }}
        />
        <button type="submit" style={{ padding: "10px", backgroundColor: "#dcd809", color: "white", border: "none", borderRadius: "4px", cursor: "pointer" }}>
          등록하기/Submit
        </button>
      </form>

      <hr style={{ margin: "30px 0", border: "0", borderTop: "1px solid #eee" }} />

      {/* 방명록 목록 출력 / Guestbook Entry List */}
      <h2 className="text-2xl font-bold text-gray-800">최근 방명록 목록 / Recent Guestbook Entries</h2>
      {isLoading ? (
        <p>로딩 중/Loading...</p>
      ) : posts.length === 0 ? (
        <p>작성된 방명록이 없습니다. 첫 글을 남겨보세요! / There are no guestbook entries yet. Be the first to leave a message!</p>
      ) : (
        <ul style={{ listStyle: "none", padding: 0 }}>
          {posts.map((post) => (
            <li key={post.id} style={{ borderBottom: "1px solid #eee", padding: "15px 0" }}>
              <strong>{post.name}</strong>
              <p style={{ margin: "5px 0 0 0", color: "#555" }}>{post.content}</p>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

— 브라우저에 접속해서 방명록을 테스트 해봅니다.
Access the browser and test the guestbook.

Screenshot

✔️ Page Route방식(기존방식)
Page Routing Method (Conventional Method)

— 프로젝트 루트에 pages와 api디렉토리를 만듭니다.
Create pages and api directories in the project root.

myapp3/
└── pages/                      # 1.페이지라우터 / Pages Router
    ├── api/                    
    │   └── guestbook.js        # 1)백엔드 API / BackEnd API: /api/guestbook
    └── guestbook-page.js       # 2)프론트 앤드 / FrontEnd: /guestbook-page

— guestbook.js파일에 다음 코드를 작성합니다.
Write the following code in the guestbook.js file.

// 서버가 켜져 있는 동안 유지되는 임시 데이터 저장소
// Temporary data storage that persists while the server is running
const guestbookData = [
  { id: 1, name: 'Alexios', content: '어쌔씬크리드오디세에 / Assassin\'s Creed Odyssey' },
  { id: 2, name: 'Kassandra', content: 'Next.js API Page Routes.' },
];

export default function handler(req, res) {
  // GET 요청 처리 (데이터 조회)
  // Handle GET requests (data retrieval)
  if (req.method === 'GET') {
    return res.status(200).json(guestbookData);
  }

  // POST 요청 처리 (데이터 등록)
  // Handle POST requests (data registration)
  if (req.method === 'POST') {
    const { name, content } = req.body;

    if (!name || !content) {
      return res.status(400).json({ message: '이름과 내용을 입력해주세요. / Please enter both name and content.' });
    }

    const newPost = {
      id: Date.now(),
      name,
      content,
    };

    guestbookData.push(newPost);
    return res.status(201).json(newPost);
  }

  // 3. 지원하지 않는 메서드 처리
  // Handle unsupported methods
  res.setHeader('Allow', ['GET', 'POST']);
  return res.status(405).end(`Method ${req.method} Not Allowed`);
}

— 서버를 실행하고 API를 테스트합니다.
Run the server and test the API.

# 터미널에서 서버 실행 / Run the server in the terminal
npm run dev

# 브라우저에서 아래 주소로 접속하기 / Access the address below in your browser.
http://localhost:3000/api/guestbookapp
BackEnd-API-Page Router

— guestbook-page.js파일에 다음 코드를 작성합니다.
Write the following code in the guestbook-page.js file.

import { useEffect, useState } from "react";

export default function GuestbookPage() {

  // 상태(State) 정의
  // Define state
  const [posts, setPosts] = useState([]);
  const [name, setName] = useState("");
  const [content, setContent] = useState("");
  const [isLoading, setIsLoading] = useState(true);

  // Pages Router API에서 데이터 가져오기 (GET)
  // Fetch data from the Pages Router API (GET)
  const fetchPosts = async () => {
    try {
      // 방금 만든 "pages/api/guestbook" 주소
      // The address of the newly created "pages/api/guestbook"
      const response = await fetch("/api/guestbook"); 
      if (!response.ok) throw new Error("데이터 수신 실패 / Failed to receive data.");
      const data = await response.json();
      setPosts(data);
    } catch (error) {
      console.error(error);
    } finally {
      setIsLoading(false);
    }
  };

  useEffect(() => {
    fetchPosts();
  }, []);

  // 새 방명록 등록하기 (POST) 
  // Register a new guestbook entry
  const handleSubmit = async (e) => {
    e.preventDefault();

    if (!name.trim() || !content.trim()) {
      alert("이름과 내용을 입력해 주세요. / Please enter both name and content.");
      return;
    }

    try {
      const response = await fetch("/api/guestbook", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ name, content }),
      });

      if (response.ok) {
        setName("");
        setContent("");
        fetchPosts(); // 등록 후 목록 새로고침 / Refresh the list after registration
      } else {
        alert("등록 실패");
      }
    } catch (error) {
      console.error(error);
    }
  };

  return (
    <div style={{ padding: "40px", maxWidth: "600px", margin: "0 auto", fontFamily: "sans-serif" }}>
      <h1>📓 방명록 / Guestbook (Page Router)</h1>

      <form onSubmit={handleSubmit} style={{ display: "flex", flexDirection: "column", gap: "10px", margin: "20px 0" }}>
        <input
          type="text"
          placeholder="이름/Name"
          value={name}
          onChange={(e) => setName(e.target.value)}
          style={{ padding: "8px", border: "1px solid #ccc", borderRadius: "4px" }}
        />
        <textarea
          placeholder="내용을 입력하세요/Please enter your message"
          value={content}
          onChange={(e) => setContent(e.target.value)}
          style={{ padding: "8px", border: "1px solid #ccc", borderRadius: "4px", minHeight: "60px" }}
        />
        <button type="submit" style={{ padding: "10px", backgroundColor: "#40d080", color: "white", border: "none", borderRadius: "4px", cursor: "pointer" }}>
          등록하기/Submit
        </button>
      </form>

      <hr style={{ margin: "30px 0", border: "0", borderTop: "1px solid #eee" }} />

      <h2 >최근 방명록 목록 / Recent Guestbook Entries</h2>
      {isLoading ? (
        <p>로딩 중 / Loading...</p>
      ) : (
        <ul style={{ listStyle: "none", padding: 0 }}>
          {posts.map((post) => (
            <li key={post.id} style={{ borderBottom: "1px solid #eee", padding: "15px 0" }}>
              <strong>{post.name}</strong>
              <p style={{ margin: "5px 0 0 0", color: "#555" }}>{post.content}</p>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

— 브라우저에 접속해서 방명록을 테스트 해봅니다.
Access the browser and test the guestbook.

FrontEnd – Page Router

Leave a Reply