Computer >> 컴퓨터 >  >> 프로그램 작성 >> Redis

Blitz.js와 Redis로 할 일 목록 만들기

Blitz.js는 원래 Next.js에서 분기된 React 프레임워크입니다. 오늘 우리는 Upstash에 작업을 저장하는 Blitz.js To-Do 애플리케이션을 만들 것입니다. 더 이상 고민하지 않고 시작하겠습니다!

설정

시작하려면 컴퓨터에 Blitz.js를 설치해야 합니다.

NPM:

npm install -g blitz --legacy-peer-deps

원사:

yarn global add blitz

새 Blitz.js 앱을 만들려면 blitz new를 사용하세요. 디렉토리로 이동합니다.

blitz new blitzjs-todo && cd blitzjs-todo

좋습니다. 이제 TailwindCSS를 설치하여 웹사이트의 스타일을 지정해 보겠습니다.

blitz install tailwind

마지막으로 Upstash API를 더 쉽게 호출할 수 있도록 Upstash JS SDK를 설치해 보겠습니다.

NPM:

npm i @upstash/redis

원사:

yarn i @upstash/redis

이 시점에서 blitz dev를 실행해야 합니다. 모든 것이 올바르게 작동하는지 확인합니다. 계정도 만들고 로그인도 해보세요. 지금까지 모든 작업을 올바르게 수행했다면 다음과 같이 표시되어야 합니다.

Blitz.js와 Redis로 할 일 목록 만들기

또한 파일 구조는 다음과 같아야 합니다.

Blitz.js와 Redis로 할 일 목록 만들기

UPSTASH_REDIS_REST_URL 복사 및 UPSTASH_REDIS_REST_TOKEN Upstash 콘솔에서 .env라는 파일로 지금은. 다음과 같이 표시되어야 합니다.

# This env file should be checked into source control
# This is the place for default values for all environments
# Values in `.env.local` and `.env.production` will override these values

UPSTASH_REDIS_REST_URL=YOUR_URL_HERE
UPSTASH_REDIS_REST_TOKEN=YOUR_TOKEN_HERE

이제 Blitz.js 애플리케이션을 완전히 설정했습니다! 할 일 목록을 구현해 보겠습니다.

구현

Blitz.js에는 사용자 인증이 내장되어 있습니다! 이를 활용하여 각 사용자에 대한 비공개 할 일 목록을 만들어 보겠습니다.

먼저 /lib/redis.ts에서 Upstash JS SDK를 초기화하겠습니다.

import { Redis } from "@upstash/redis";
const redis = Redis.fromEnv();
export default redis;

3을 만들어야 합니다. 할 일 목록에 액세스하기 위한 다양한 API 경로

app/api로 이동합니다. getall.ts라는 파일을 만듭니다. . 완료했으면 다음 코드를 붙여넣으세요.

import { BlitzApiRequest, BlitzApiResponse, getSession } from "blitz";
import redis from "../../lib/redis";

export const handler = async (req: BlitzApiRequest, res: BlitzApiResponse) => {
  const session = await getSession(req, res);
  if (!session.userId) {
    res.status(401).json({ error: `Do not tamper with this route!` });
  } else {
    await redis
      .lrange(String(session.userId), 0, 100)
      .then((data) => res.status(200).json({ data: data, success: true }))
      .catch((error) => res.status(500).json({ error: error }));
  }
};
export default handler;

이 API 경로가 어떻게 작동하는지 단계별로 살펴보겠습니다. 먼저 경로를 요청합니다. 경로 자체에서 사용자가 로그인했는지 확인합니다. 사용자가 없으면 "승인되지 않음" 응답을 반환합니다. 있다 그런 다음 Upstash Redis 데이터베이스를 가져와 현재 목록에 있는 모든 할 일을 찾습니다. 이것은 약 100개의 할 일을 가져올 것입니다.

<블록 인용>

Q:잠깐, 처음에 할 일을 어떻게 추가해야 하나요?A:좋은 질문입니다! 다음에 합시다!

다시 한 번 다음 코드를 add.ts라는 새 파일에 붙여넣습니다. app/api에서 .

import { BlitzApiRequest, BlitzApiResponse, getSession } from "blitz";
import redis from "../../lib/redis";
const handler = async (req: BlitzApiRequest, res: BlitzApiResponse) => {
  const session = await getSession(req, res);
  if (req.method !== "POST" || !req.body.data || !session.userId) {
    res.status(401).json({ error: `Do not tamper with this route!` });
  } else {
    let todo = encodeURI(req.body.data);
    await redis
      .lpush(String(session.userId), todo)
      .then(() => res.status(200).json({ success: true }))
      .catch(() => res.status(500).json({ error: "Error adding data." }));
  }
};
export default handler;

이 API 경로는 마지막 경로와 매우 유사하지만 다섯 번째 줄에 더 많은 검사를 추가했습니다. 이 요청이 GET가 아니기 때문입니다. 요청이 아니라 POST입니다. 요구. 항목을 확인하는 방법에 유의하세요. 먼저 요청이 실제로 POST인지 확인합니다. 요구. 다음으로 req.body.data에 JSON 또는 텍스트가 있는지 확인합니다. . 마지막으로 사용자가 로그인했는지 확인합니다. 이러한 작은 검사가 모두 통과되면 할 일을 Upstash의 Redis 목록에 푸시할 수 있습니다. 가져오는 동안 오류가 발생하면 .catch를 사용하여 500을 반환할 수 있습니다. .

추가해야 하는 마지막 경로는 할 일을 제거하는 경로입니다. 일단 무언가를 마치면 당연히 건너야합니다! app/api/remove.ts에 마지막 API 경로를 추가해 보겠습니다. . 다음 코드를 파일에 복사합니다.

import { BlitzApiRequest, BlitzApiResponse, getSession } from "blitz";
import redis from "../../lib/redis";
const handler = async (req: BlitzApiRequest, res: BlitzApiResponse) => {
  const session = await getSession(req, res);
  if (req.method !== "POST" || !req.body.data || !session.userId) {
    res.status(401).json({ error: `Do not tamper with this route!` });
  } else {
    let todo = encodeURI(req.body.data);
    await redis
      .lrem(String(session.userId), 1, todo)
      .then(() => res.status(200).json({ success: true }))
      .catch(() => res.status(500).json({ error: "Error removing data." }));
  }
};
export default handler;

비슷한 점이 있습니까? 이 경로가 add와 거의 동일하기 때문입니다. API 경로. 여기서 가장 큰 차이점은 LREM을 사용한다는 것입니다. , LPUSH가 아닙니다. , Redis에서 항목을 제거합니다.

프론트엔드 구축

시작하려면 app/pages/index.js의 모든 항목을 삭제해 보겠습니다. 할 일 목록을 단계별로 작성해 보세요.

파일 상단에 이러한 가져오기를 붙여넣습니다.

import { Link, BlitzPage, useMutation, Routes, getAntiCSRFToken } from "blitz";
import { useRef, useEffect, useState, Suspense } from "react";
import Layout from "app/core/layouts/Layout";
import { useCurrentUser } from "app/core/hooks/useCurrentUser";
import logout from "app/auth/mutations/logout";

우리는 할 일 목록의 핵심 기능을 구축하기 위해 React Hooks를 사용할 것입니다. 목록의 핵심 기능 중 일부를 구현해 보겠습니다.

const Main = () => {
  const todoRef = useRef<HTMLInputElement>(null)
  const [todos, setTodos] = useState([])
  const currentUser = useCurrentUser()
  const [logoutMutation] = useMutation(logout)
  const handleAddTodo = async (e) => {
    e.preventDefault()
    const antiCSRFToken = await getAntiCSRFToken()
    const response = await fetch("/api/add", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "anti-csrf": antiCSRFToken,
      },
      body: JSON.stringify({ data: todoRef.current?.value }),
    })
    const data = await response.json()
    if (data.success) {
      todoRef.current!.value = ""
      fetchTodos()
    }
  }
  const handleRemoveTodo = async (id) => {
    const antiCSRFToken = await getAntiCSRFToken()
    const response = await fetch("/api/remove", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "anti-csrf": antiCSRFToken,
      },
      body: JSON.stringify({ data: id }),
    })
    const data = await response.json()
    if (data.success) {
      fetchTodos()
    }
  }
  const fetchTodos = async () => {
    const antiCSRFToken = await getAntiCSRFToken()
    const response = await fetch("/api/getall", {
      method: "GET",
      headers: {
        "anti-csrf": antiCSRFToken,
      },
    })
    const res = await response.json()
    setTodos(res.data)
  }
  useEffect(() => {
    fetchTodos()
  }, [])

  if (currentUser) {
    return (
      <>
        <button
          className="mt-4 px-2 py-1 border-2 border-black hover:bg-gray-400 mb-3"
          onClick={async () => {
            await logoutMutation()
          }}
        >
          Logout
        </button>
        <div>
          User id: <code>{currentUser.id}</code>
          <br />
          User email: <code>{currentUser.email}</code>
        </div>
        <form className="mt-2" onSubmit={handleAddTodo}>
          <p>add a todo:</p>
          <input
            ref={todoRef}
            className="w-full border-black border-2 focus:outline-none text-center"
          />
        </form>
        <div className="flex flex-col gap-2 mt-4 bg-gray-300 rounded-md">
          {(todos as string[]).map((todo: string, index: number) => (
            <div className="flex items-center p-3 rounded-md bg-gray-300" key={index}>
              <button
                onClick={() => handleRemoveTodo(todo)}
                className="flex items-center mr-4 justify-center w-5 h-5 rounded-[0.25rem] border border-solid border-gray-500 shadow-sm hover:bg-gray-700"
              ></button>

              <span>{todo}</span>
            </div>
          ))}
        </div>
      </>
    )
  } else {
    return (
      <div className="flex flex-col gap-4 text-center">
        <Link href={Routes.SignupPage()}>
          <a className="mt-4 px-2 py-1 border-2 border-black hover:bg-gray-400">
            <strong>Sign Up</strong>
          </a>
        </Link>
        <Link href={Routes.LoginPage()}>
          <a className="mt-4 px-2 py-1 border-2 border-black hover:bg-gray-400">
            <strong>Login</strong>
          </a>
        </Link>
      </div>
    )
  }
}

<Main/> component는 우리 애플리케이션의 핵심입니다. 코드를 자세히 보면 어떻게 사용하는지 알 수 있습니다. 구성 요소의 맨 위에서 애플리케이션의 상태를 초기화합니다. 또한 ref 나중에 "New To-Do" 입력에 사용할 수 있습니다. antiCSRFToken의 사용을 확인할 수도 있습니다. ! Blitz.js는 API 경로를 가져올 때 이러한 토큰을 사용하여 모든 종류의 악의적인 행위자가 사이트에 해를 끼치는 것을 방지해야 합니다. 제 생각에는 가지고 있는 것이 좋습니다!

우리는 웹사이트에서 데이터를 처리하기 위해 세 가지 주요 기능을 사용합니다. 이 세 가지는 다음과 같습니다.

  • handleAddTodo
  • handleRemoveTodo
  • fetchTodos

fetchTodos를 호출합니다. 페이지가 로드되는 즉시 사용자가 완료해야 하는 모든 할일을 로드합니다. 사용자가 할 일을 제거하거나 추가할 때 fetchTodos를 호출합니다. 웹사이트에 해당 변경 사항을 다시 반영합니다!

<블록 인용>

사용자가 로그인하지 않은 경우 이 페이지를 보기 전에 웹사이트에 로그인하라는 메시지가 표시됩니다.

웹사이트에 아직 세션이 없는 경우 가입하거나 로그인할 수 있습니다. 계정 없이 할 일을 저장할 수 없으며 모든 API 경로를 사용하려면 AntiCSRFToken으로 인증해야 합니다. !

그러나 한 가지 더 중요한 단계가 있습니다. 페이지를 내보내야 합니다!

const Home: BlitzPage = () => {
  return (
    <div className="flex flex-col min-h-screen items-center justify-center">
      <main>
        <div className="my-4">
          <Suspense fallback="Loading...">
            <Main />
          </Suspense>
        </div>
      </main>
    </div>
  );
};

Home.suppressFirstRenderFlicker = true;
Home.getLayout = (page) => <Layout title="Home">{page}</Layout>;

export default Home;

위에서 볼 수 있듯이 Blitz.js는 Next와 약간 다른 접근 방식을 사용하지만 현재로서는 접근 방식이 핵심입니다. Suspense를 사용합니다. 사용자에게 앱이 로드 중임을 보여주기 위해 이전에 가져온 다음 </Main> 로드가 완료된 후 구성 요소!

변경 사항이 적용된 것을 확인하려면 콘솔에서 한 번 더 실행하고 브라우저에서 앱으로 이동하세요.

blitz dev

지침을 따랐다면 로그인하고 몇 가지 할 일을 추가하면 애플리케이션이 다음과 같이 표시되어야 합니다.

Blitz.js와 Redis로 할 일 목록 만들기

할 일 옆에 있는 상자를 클릭하여 할 일을 제거할 수 있습니다. 바로 removeTodo입니다. 기능은 😉를 위한 것입니다.

축하합니다!

이 블로그 게시물을 읽고 새로운 것을 배웠기를 바랍니다. 그렇지 않은 경우 기술을 연마하는 것이 나쁘지 않다는 것을 기억하십시오! Blitz.js는 Next.js에서 초점을 돌리고 있으므로 미래에는 완전히 다른 프레임워크가 될 수 있지만 여기에서 그들의 웹사이트를 계속 지켜봐 주십시오!

프로젝트 소스 :GitHub 링크

작업 데모: 데모 링크

피드백이 있습니까? Twitter에서 @upstash를 팔로우하고 Discord 서버에 가입하세요!