본문 바로가기

개발/Next

[Next14] To Do List 만들기 with Supabase (2) Next API Route 사용하여 할 일 목록 구현하기

반응형

이전 글

 

 

[Next14] To Do List 만들기 with Supabase (1) 프로젝트 세팅

요즘에 Next를 사용하는 회사가 많아짐에 따라 어떤 것인지 궁금해졌어요. Next는 SSR이라서SEO에 유리하고, 렌더링 속도가 빠릅니다.특히 API도 직접 만들 수 있다는 것이 흥미로웠어요! 실무에서

devshelly.com


이전 글에서 Next 프로젝트와 Supabase 프로젝트를 생성하고, 두 프로젝트를 연결해 보았어요.

그리고 Supabase의 데이터를 Next에서 러프하게 노출시켰습니다.

정말 데이터를 그대로 노출한 것이라서 아직 페이지라는 느낌이 안들죠. 😅

이번 글에서는 할 일 목록 UI 추가해보려고 합니다.


할 일 목록 구현하기

UI 구현

우선은 할 일을 확인할 수 있는 목록 UI를 구현해봅시다!

 

지난 글에서 생성했던 `src/app/todos/page.tsx` 파일을 아래와 같이 수정했습니다.

스타일링은 초기 설정할 때 함께 설치한 `tailwind`를 사용했어요.

"use client";

export default function Todos() {
  // 더미데이터!
  const todos = [
    "할 일 목록 구현하기",
    "할 일 추가 기능 구현하기",
    "할 일 완료 기능 구현하기",
  ];

  return (
    <div className="w-screen h-screen flex justify-center items-center">
      <section className="w-[400px] p-[30px] rounded-lg bg-gray-400 bg-opacity-10">
        <h2 className="text-2xl font-bold mb-4">TO DO</h2>
        <ul>
          {todos.map((todo) => (
            <li className="px-[10px] text-lg leading-8" key={todo}>
              {todo}
            </li>
          ))}
        </ul>
      </section>
    </div>
  );
}

간단하게 todos 배열을 노출시키는 UI를 완성했습니다.

(정말 간단한 UI죠? ㅎ)


타입 정의

이전 글에서 다음과 같이 테이블을 생성했었어요.

이름 설명 타입 기본값 빈 값 허용 여부
id 고유 id (primary) int8 자동생성 X
createdAt 생성한 날짜 timestamptz now() X
contents 할 일 내용 text - X
isDone 완료 여부 bool false X

 

이 테이블에 맞게 Next에서도 Todo 객체의 타입을 정의해줍시다!

`src/types/todo.ts` 파일을 생성하고 아래와 같이 타입정의를 해주었습니다. 

// src/types/todo.ts
export interface Todo {
  id: number;
  createdAt: string;
  contents: string;
  isDone: boolean;
}

 

다시 `src/app/todos/page.tsx` 파일로 돌아가서 todos 배열의 타입을 정의하고,

타입에 맞게 데이터를 수정해줍시다.

// src/app/todos/pages.tsx
"use client";

import { Todo } from "@/types/todo";

export default function Todos() {
  // 더미데이터!
  const todos: Todo[] = [
    {
      id: 1,
      createdAt: new Date().toString(),
      contents: "할 일 목록 구현하기",
      isDone: false,
    },
    {
      id: 2,
      createdAt: new Date().toString(),
      contents: "할 일 추가 기능 구현하기",
      isDone: false,
    },
    {
      id: 3,
      createdAt: new Date().toString(),
      contents: "할 일 완료 기능 구현하기",
      isDone: false,
    },
  ];

  return (
    <div className="w-screen h-screen flex justify-center items-center">
      <section className="w-[400px] p-[30px] rounded-lg bg-gray-400 bg-opacity-10">
        <h2 className="text-2xl font-bold mb-4">TO DO</h2>
        <ul>
          <!-- key값은 고유한 todo.id -->
          {todos.map((todo) => (
            <li className="px-[10px] text-lg leading-8" key={todo.id}>
              <!-- 할 일 콘텐츠 노출 -->
              {todo.contents}
            </li>
          ))}
        </ul>
      </section>
    </div>
  );
}

API 선언 및 호출

더미데이터를 사용했었으니,

이번에는 supabase에 저장된 실제 데이터를 가져와봅시다!

 

1. Next API route 선언하기

`src/app/api/todo/route.ts` 파일을 생성하고,

다음과 같이 코드를 작성해줍니다.

// src/app/api/todos/route.ts
import { createClient } from "@/utils/supabase/server";
import { NextResponse } from "next/server";

export async function GET() {
  const supabase = createClient();
  const { data, error } = await supabase.from("todos").select("*");

  return NextResponse.json({ data, error });
}

supabase의 todos 테이블의 데이터를 불러오고,

그 데이터를 Response 해주는 API입니다.

 

중요한 것은 폴더 구조입니다.

`api/todos/route.ts` 로 선언한 후 함수명을 `GET`으로 했다면,

`GET {도메인}/api/todos` 호출했을 때 위의 응답을 반환해줍니다.

 

2. API 호출하기

그러면 다시 UI 페이지로 돌아가서 생성한 API를 호출해봅시다!

 

// src/app/todos/page.tsx
"use client";

import { Todo } from "@/types/todo";
import { useEffect, useState } from "react";

export default function Todos() {
  const [todos, setTodos] = useState<Todo[]>([]);

  const loadTodos = async () => {
    // 위에서 선언한 GET /api/todos 호출!
    const response = await fetch("/api/todos", { method: 'GET' });
    const { data } = await response.json();
    setTodos(data);
  };

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

  return (
    <div className="w-screen h-screen flex justify-center items-center">
      <section className="w-[400px] p-[30px] rounded-lg bg-gray-400 bg-opacity-10">
        <h2 className="text-2xl font-bold mb-4">TO DO</h2>
        <ul>
          {todos.map((todo) => (
            <li className="px-[10px] text-lg leading-8" key={todo.id}>
              {todo.contents}
            </li>
          ))}
        </ul>
      </section>
    </div>
  );
}

 

supabase에 저장되어있던 데이터를 불러오는데 성공했습니다!

데이터가 너무 없으니 데이터 몇 개만 더 추가해볼게요!

 

 

3. 데이터 추가하기

이렇게 데이터를 추가하고 다시 새로고침 해보면

 

추가된 데이터도 잘 보입니다.

이제 데이터 할 일 목록 구현이 완료되었습니다! 🎉

 

 

반응형