저번에 docker로 띄운 mysql을 next.js 프로젝트에 연결해서 테이블을 만드는 것까지 진행해 봤다.
그렇다면 간단하게 next.js에서 입력폼을 만들어보자
컴포넌트 폴더를 따로 만들어서 제작했다.
컴포넌트 세팅
먼저 app
폴더 바로 아래 있는 page.tsx
파일에 기본적으로 되어 있는 세팅들을 삭제하고 시작하자.global.css
에 있는 css 들도 다 삭제 했다. tailwind를 사용할 예정이라 @tailwind ~
부분은 삭제하지 말자
// app/page.tsx
export default function Home() {
return (
<main className="min-h-screen flex flex-col justify-center items-center space-y-10"></main>
);
}
다 삭제하고 입력 폼과 띄울 투두 리스트만 가운데로 오도록 세팅했다.
그럼 투두를 만들 입력 폼과 데이터를 받을 TodoForm.tsx
과 PostList.tsx
를 만들어보자
여기서
components
폴더는 어디에 만드나? 할 수 있는데app
디렉토리 안에 만들어도 되고 app외부로 나와서 루트에서 만들든 상관은 없다.
다만 주의할 점으론app
폴더에 만들경우 현재 Next.js는 라우팅을 자동으로 해주는데 우리가 만든components
폴더 또한 라우팅 될 수 있다(물론page.tsx
를 만들지 않을 경우 엑세스 하진 않는다)
그래서app
폴더 안에components
폴더를 만들고 싶은 경우 앞에_
를 붙여서 라우팅 되지 않도록 만들어주자_components
라고 폴더를 만들어줄 경우 Next.js에서 이를 인식하고 라우팅을 하지 않는다
// components/TodoForm.tsx
export default function TodoForm() {
return (
<form
className="flex flex-col justify-center items-center rounded shadow-md bg-sky-200 p-5 space-y-3"
>
<input type="text" name="title" className="p-2 rounded" />
<input type="text" name="content" className="p-2 rounded" />
<button className="p-2 bg-slate-300 w-24 rounded ">submit</button>
</form>
);
}
간단한 모양으로 만들었고 만들어진 모양을 보면
어떤 효과도 입히지 않은 정말 간단한 form 입력창이 하나 만들어졌고 다음으로 PostList
또한 간단하게 만들어보자
// components/PostList.tsx
export default function PostList() {
return (
<div className="w-3/5 flex flex-col items-center space-y-5">
<div className="p-5 bg-slate-100 w-full rounded shadow-lg flex flex-col space-y-2 ">
<h1 className="font-bold text-xl">Title : title부분</h1>
<p>내용 부분</p>
<span className="text-slate-500 text-sm">Create : 만든날짜</span>
</div>
<div className="p-5 bg-slate-100 w-full rounded shadow-lg flex flex-col space-y-2 ">
<h1 className="font-bold text-xl">Title : title부분</h1>
<p>내용 부분</p>
<span className="text-slate-500 text-sm">Create : 만든날짜</span>
</div>
</div>
);
}
일단은 목록이기에 2개를 붙였고 list가 불러질 땐 하나로 돌릴 예정이다.
일단 완성된 화면을 확인하면
이렇게 아주 간단한 입력창과 todo 창이 완성된 걸 확인해 볼 수 있다
Server
그럼 이제 Next.js 의 server action을 통해 prisma에 접근해야 하는데 이는 공식문서를 참고하자
https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations
LIST불러오기(getTodos)
일단 Next.js를 사용하며 "use client"
를 통해 클라이언트 컴포넌트를 나누듯이 "use server"
를 통해 서버에서 동작하도록 만들 수 있다.
각 함수 내부에서 사용해도 되지만 나는 루트 폴더에 server
폴더를 만들어 server에 관련된 부분들을 해당 폴더에 넣을 예정이다server
폴더 안에 actions
폴더를 만들어 post.ts
란 파일을 만들어 투두리스트를 불러오는 함수와 추가하는 함수를 구현해보자
그 전에 이전에 만들었던
/lib/prisma.ts
에 작성한 쿼리 client 부분을 지우고 새로clinet
라는 폴더를 만들어서clinet.ts
란 파일에 새롭게 적자.
이전에production
상태등을 고려해서 작성했고 정상적으로 작동하지만 typescript가 제대로 동작하지 않는 경우가 있어 일단 수정했다
// @client/client.ts
import { PrismaClient } from "@prisma/client";
export const prisma = new PrismaClient();
혹시 import
가 안된다면 npm install @prisma/client
로 직접 설치해주면 해결된다!
그리고 이를 이용해 todolist를 불러오는 server action 함수를 만들어보자
//@server/actions/post.ts
"use server";
import { prisma } from "@/client/client";
export const getTodos = async () => {
const todos = await prisma.post.findMany();
return todos;
};
여기서 prisma.post.findMany()
가 무엇인가? 궁금한데.. prisma는 우리가 이전 client/client.ts
에서 만들었던 new PrismaClient
객체로 우리가 schema로 만들었던 모델들이 모두 포함 되어 있다
그리고 자체적으로 주어지는 쿼리들이 존재하며 우리가 만들었던 post
로 접근할시 prisma에서 만들어놓은 많은 데이터 쿼리들이 존재하는데 상황에 맞춰 공식문서를 찾아보며 사용해보면 된다.
https://www.prisma.io/docs/orm/prisma-client/queries
나는 이곳에서 전부 찾아와주는 메서드인 findMany()
를 사용했다. 이는 SELECT * FROM post
와 동일하게 동작한다.
그리고 docker에 올라가 있는 mysql에서 받은 리스트들을 return
해주고 이를 화면에 뿌려주면 된다.
// @components/PostList.tsx
import { getTodos } from "@/server/actions/post";
export default async function PostList() {
const todos = await getTodos();
return (
<div className="w-3/5 flex flex-col items-center space-y-5">
{todos.map(({ id, title, content, createdAt }) => (
<div
key={id}
className="p-5 bg-slate-100 w-full rounded shadow-lg flex flex-col space-y-2 "
>
<h1 className="font-bold text-xl">Title : {title}</h1>
<p>{content}</p>
<span className="text-slate-500 text-sm">
Create : {createdAt.toLocaleString()}
</span>
</div>
))}
</div>
);
}
코드 부분은 끝났고 mysql에 대강 todolist 몇개를 강제로 insert
해서 띄워보자
MySQL Workbench에 들어가서 입력 시키고 db를 확인해보니 잘 들어간 걸 확인할 수 있다
insert into Post (title,content)
values('test2','Todo테스트2입니다');
insert into Post (title,content)
values('test3','Todo테스트3입니다');
-- ....some code
select * from Post;
잘 들어간 걸 확인했으니 그럼 화면에도 정상적으로 뜨는지 확인해보자.
우리가 만든 컴포넌트들을 page.tsx
에도 잘 넣었다면 무사히 뜰것이다.
import PostList from "@/components/PostList";
import TodoForm from "@/components/TodoForm";
export default function Home() {
return (
<main className="min-h-screen flex flex-col justify-center items-center space-y-10">
<TodoForm />
<PostList />
</main>
);
}
코드도 잘 들어갔고? 화면도 잘 뜬다.
굉장히 쉽게 완성됐고 이제 todo를 작성할 수만 있다면 완성이다.
Todo 추가하기(addTodo)
아까 작성하던 @server/actions/post.ts
로 돌아가서 addTodo
함수를 완성해보자
// @server/actions/post.ts
"use server"
// ...
export const addTodo = async (formData: FormData) => {
const title = formData.get("title");
const content = formData.get("content");
if (title instanceof File || content instanceof File || !title || !content)
return console.error("Please enter the title and content");
await prisma.post.create({ data: { title, content } });
};
이후 form에 가서
// @components/TodoForm.tsx
import { addTodo } from "@/server/actions/post";
export default function TodoForm() {
return (
<form
action={addTodo}
className="flex flex-col justify-center items-center rounded shadow-md bg-sky-200 p-5 space-y-3"
>
<input type="text" name="title" className=" p-2 rounded" />
<input type="text" name="content" className=" p-2 rounded" />
<button className="p-2 bg-slate-300 w-24 rounded ">submit</button>
</form>
);
}
form
태그의 action
부분에 우리가 작성한 addTodo
함수를 집어 넣으면 완성된다.addTodo
함수를 잠깐 살펴보자면 이는 form
태그에서 action
을 사용한다면 매개변수 부분에서 FormData
가 들어오게 되고 이는 get
을 이용해서 데이터를 꺼내야 한다.
이때 데이터를 꺼낼때 이름은 각 input
의 name
값으로 정해지니 name
을 잘 맞춰주자
데이터를 꺼낸 후 바로 타입을 살펴보면 FormDataEntryValue | null
라는 타입이 각각 title
과 contetn
에 붙어있을 것이다.
이를 바로 prisma.post.create
를 사용해 생성시킨다면 타입 관련한 에러가 나오게 되는데
이는 우리가 schema.prisma
에서 선언한 타입들을 그대로 가져오기 때문에 string
과 충돌나는 것이다.
이를 타입 네로잉 즉 타입을 string
으로 좁히기 위해 각각 title
과 conetent
는 File
타입이고 null
인지 체크해서 맞다면 함수를 더이상 진행하지 않고 return
하도록 했다
그리고 무사히 if
문 쪽을 통과한다면 title
과 content
는 string
타입만 남게되고 이를 통해 우리가 설정한 타입 그대로 prisma.post.create
의 data
로 각각 넘겨줄 수 있게 된다.
실행을 해보면
새로운 값들을 넣은다음 제출버튼을 누른 후 데이터가 db에 들어갔는지 워크벤치로 가서 확인해보자
select * from Post;
로 쿼리를 적어 확인해보니
무사히 db에 도착한 것 같다!
화면도 새로고침을 해보니
변경된 데이터를 받아 띄워주는 걸 확인할 수 있다.
지금 페이지들은 서버 컴포넌트들로 즉각적인 변화가 일어나지 않기 때문에 새로고침을 해줘야 하는데 이부분은 Next.js를 공부하면서 더 수정해보자.
일단 글의 목표인 Next.js 기반으로 Prisma, ServerAction, docker, My SQL을 활용해서 로컬에서 돌아갈 간단한 웹 페이지와 서버, db를 연결시키는 테스트를 해봤다.
추가적으로 어떤 걸 더 해볼지는 개인 적으로 하나씩 해보면 좋을 것 같은데 나는 tRPC를 활용해서 server action과 함께 묶어보려고 한다.
'업무' 카테고리의 다른 글
docker로 mysql띄우고 Next.js로 연결해보기(prisma)-2 (0) | 2024.05.13 |
---|---|
docker로 mysql띄우고 Next.js로 연결해보기(prisma)-1 (0) | 2024.05.13 |
전역 상태관리 툴 없이 Toast만들기(custom hook으로) (0) | 2024.04.17 |
모노레포 -Monorepo (0) | 2024.04.15 |