Deno의 Fresh 및 Postgres를 사용하여 전체 스택 CRUD 앱 구축

Fresh은 TypeScript로 앱을 쉽게 개발할 수 있게 해주는 Deno용 풀스택 프레임워크입니다. 이를 사용하여 전체 CRUD 기능을 갖춘 간단한 Todo 앱을 빌드해 보겠습니다.

이 (아주) 짧은 튜토리얼의 결과 코드는 in this GitHub repository에서 찾을 수 있습니다.

시작하려면 Deno CLI 버전 1.23.0 이상이 설치되어 있는지 확인하십시오.

1단계 - 새 새 프로젝트 만들기




deno run -A -r https://fresh.deno.dev todos
cd todos
deno task start


이제 브라우저에서 http://localhost:8000을 열어 페이지를 볼 수 있습니다.

2단계 - 전체 스택 CRUD 프레임워크로 작업 속도 향상



CRUD 작업을 빠르고 효율적으로 구축하기 위해 Node.js용으로 구축되었지만 Deno에서도 잘 작동하는 CRUD 프레임워크인 Remult을 사용하겠습니다.

(참고: 저는 Remult의 제작자 중 한 명입니다.)

Remult를 프로젝트import_map.json에 추가해 보겠습니다.

{
  "imports": {
    // ...
    "remult": "https://cdn.skypack.dev/[email protected]?dts",
    "remult/remult-fresh": "https://cdn.skypack.dev/[email protected]/remult-fresh?dts"
  }
}


Remult는 Fresh 미들웨어로 부트스트랩됩니다. 다음 코드를 사용하여 _middleware.ts 아래에 routes/ 파일을 생성해 보겠습니다.

// routes/_middleware.ts

import { remultFresh } from "remult/remult-fresh";

export const remultServer = remultFresh({ }, Response);

export const handler = remultServer.handle;


3단계 - 태스크 모델 생성



루트 아래에 model 폴더를 만들고 다음 코드를 사용하여 task.ts 파일을 만듭니다.

// model/task.ts

import { Entity, Fields } from "remult";

@Entity("tasks", {
    allowApiCrud: true
})
export class Task {
    @Fields.uuid()
    id!: string;

    @Fields.string()
    title = '';

    @Fields.boolean()
    completed = false;
}

remultTask 모델에 대한 CRUD API를 제공하려면 entities 서버의 remult 배열에 모델을 등록해야 합니다.

// routes/_middleware.ts

import { remultFresh } from "remult/remult-fresh";
import { Task } from "../model/task.ts";

export const remultServer = remultFresh({
  entities: [Task]
}, Response);

//...


🚀 이제 CRUD API가 준비되었습니다. (http://localhost:8000/api/tasks에서 테스트하십시오.)

4단계 - Todo를 표시하고 편집하기 위한 섬 만들기



할 일 목록은 대화식이므로 목록을 표시하고 편집하기 위해 Freshisland를 사용하겠습니다. 그러나 페이지가 로드될 때 초기 작업 목록을 가져오기 위해 Fresh의 서버 측 렌더링을 사용하겠습니다. 💪

다음 코드를 사용하여 islands/todos.tsx 파일을 만듭니다.

// islands/todos.tsx

/** @jsx h */
import { h } from "preact";
import { Remult } from "remult";
import { useState } from "preact/hooks";
import { Task } from "../model/task.ts";

const remult = new Remult();
const taskRepo = remult.repo(Task);

export default function Todos({ data }: { data: Task[] }) {
  const [tasks, setTasks] = useState<Task[]>(data);

  const addTask = () => {
    setTasks([...tasks, new Task()]);
  };

  return (
    <div>
      {tasks.map((task) => {
        const handleChange = (values: Partial<Task>) => {
          setTasks(tasks.map((t) => t === task ? { ...task, ...values } : t));
        };

        const saveTask = async () => {
          const savedTask = await taskRepo.save(task);
          setTasks(tasks.map((t) => t === task ? savedTask : t));
        };

        const deleteTask = async () => {
          await taskRepo.delete(task);
          setTasks(tasks.filter((t) => t !== task));
        };

        return (
          <div key={task.id}>
            <input
              type="checkbox"
              checked={task.completed}
              onClick={(e) => handleChange({ completed: !task.completed })}
            />
            <input
              value={task.title}
              onInput={(e) => handleChange({ title: e.currentTarget.value })}
            />
            <button onClick={saveTask}>Save</button>
            <button onClick={deleteTask}>Delete</button>
          </div>
        );
      })}
      <button onClick={addTask}>Add Task</button>
    </div>
  );
}


기본routes/index.ts 파일을 수정하여 SSR 핸들러의 작업 목록에 대한 데이터베이스를 쿼리하고 페이지를 렌더링할 때 Todos 아일랜드에 전달합니다.

// routes/index.ts

/** @jsx h */
import { h } from "preact";
import { Handlers, PageProps } from "$fresh/server.ts";
import Todos from "../islands/todos.tsx";
import { Task } from "../model/task.ts";
import { remultServer } from "./_middleware.ts";

export const handler: Handlers<Task[]> = {
  async GET(req, ctx) {
    const remult = await remultServer.getRemult(req);
    return ctx.render(await remult.repo(Task).find());
  },
};

export default function Home({ data }: PageProps<Task[]>) {
  return (
    <div>
      <Todos data={data} />
    </div>
  );
}


어려운 부분은 끝났습니다 😌



이 시점에서 할 일 목록이 있고 작업을 생성, 업데이트 및 삭제할 수 있습니다.

Go ahead and create a few tasks, when you save them you'll notice a db folder with a tasks.json file has been created in the project folder. This JSON database is remult's default database which is nice for quick prototyping.



5단계 - Postgres에 연결 및 배포


  • 연결할 새 Postgres 인스턴스가 필요합니다. Deno Deploy 문서에서 these instructions을 사용하여 Supabase에 인스턴스를 설정했습니다.

  • Deno Deploy에 앱을 배포하는 방법에는 여러 가지가 있습니다. 내 코드를 GitHub에 푸시하고 Deno Deploy 프로젝트를 Deno Deploy dashboard을 사용하여 GitHub 리포지토리에 연결하도록 선택했습니다.

    Don't forget to add a DATABASE_URL environment variable in the Deno Deploy project settings, with the value of the connection string provided by Supabase.


  • 이제 remult 환경 변수를 사용하여 Postgres에 연결하도록 지시해 보겠습니다.

  • // routes/_middleware.ts
    
    import { remultFresh } from "remult/remult-fresh";
    import { Task } from "../model/task.ts";
    import { createPostgresConnection } from "https://deno.land/x/remult/postgres.ts";
    
    export const remultServer = remultFresh({
      entities: [Task],
      dataProvider: async () => {
        const dbUrl = Deno.env.get("DATABASE_URL");
        if (dbUrl) {
          return createPostgresConnection({ connectionString: dbUrl });
        }
        return await undefined;
      },
    }, Response);
    
    export const handler = remultServer.handle;
    


    You can test the Postgres connection locally by creating a .env file in the project's root folder with the content: DATABASE_URL='<YOUR_CONNECTION_STRING>'.



    이 마지막 변경 사항을 커밋하고 GitHub 리포지토리에 푸시하면 Todo 앱이 배포되고 Postgres 데이터베이스에 연결됩니다. ✨



    이 게시물은 가능한 짧고 따라하기 쉽게 만들려고 노력했습니다. 중요한 내용을 놓친 경우 수정할 수 있도록 댓글을 남겨주세요.
  • Deno의 Fresh 프레임워크에 대해 자세히 알아보려면 https://fresh.deno.dev으로 이동하십시오.
  • Remult에 대해 자세히 알아보려면 https://remult.dev으로 이동하십시오.
  • 좋은 웹페이지 즐겨찾기