생성 기능을 구현하기 위해서는 onSubmit과 같은 사용자와 상호작용하는 코드가 필요합니다. 이런 코드는 서버 쪽에서 실행할 수 없기 때문에 클라이언트 쪽으로 전송되어서 실행되야 합니다. 여기서는 클라이언트 컴포넌트를 만드는 방법을 살펴봅니다.
소스코드
https://github.com/egoing/nextapp/commit/0a316f1e31a52729607bc190d5c5e65483a71e20
절차
1. app/create/layout.js 삭제
이 파일은 중첩된 layout을 보여주기 위해서 만든 임시 파일이기 때문에 삭제 합니다.
2. app/create/page.js 수정
'use client' import { useRouter } from "next/navigation"; export default function Create(){ const router = useRouter(); return <form onSubmit={async evt=>{ evt.preventDefault(); const title = evt.target.title.value; const body = evt.target.body.value; const resp = await fetch('http://localhost:9999/topics/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({title, body}) }); const topic = await resp.json(); console.log("file: page.js:19 ~ Create ~ topic:", topic) router.push(`/read/${topic.id}`); router.refresh(); }}> <h2>Create</h2> <p><input type="text" name="title" placeholder="title" /></p> <p><textarea name="body" placeholder="body"></textarea></p> <p><input type="submit" value="create" /></p> </form> }
3. server component -> client component
'use client'
위의 코드를 사용하면 client component로 전환됩니다. 클라이언트 컴포넌트가 되면 useEffect, useState, onSubmit과 같은 코드를 사용할 수 있게 됩니다.
4. 라우터
const router = useRouter();
useRouter를 사용하면 라우터 객체를 생성할 수 있습니다. useRouter는 client component에서만 사용 가능합니다.
5. 라우터 사용
router.push(`/read/${topic.id}`); router.refresh();
router.push를 사용하면 페이지 리로드 없이 사용자의 화면을 해당 페이지로 이동합니다. router.refresh를 사용하면 서버 컴포넌트를 서버 쪽에서 다시 랜더링해서 새로 고침할 수 있습니다. 여기서는 app/layout.js을 새로고침하기 위해서 사용된 코드입니다.
6. cache 업데이트
router.refresh를 했음에도 글 목록이 갱신되지 않을 것입니다. 그 이유는 서버쪽에서 fetch를 사용하면 응답 결과를 저장하기 때문입니다. 개발 서버를 다시 시작하고, 페이지를 리로드한 후에 터미널을 봅시다.
루트 페이지(/)로 접속했을 때 app/layout.js에서 fetch가 동작해서 http://localhost:3000/api/topics/로 접속이 발생했다는 뜻입니다. cache : MISS는 캐쉬가 없기 때문에 서버에 실제로 접속해서 데이터를 가져왔다는 뜻입니다.
다시 접속하면 아래와 같이 됩니다.
cache : HIT 입니다. 캐쉬를 사용했다는 뜻입니다.
캐쉬를 삭제한 후에 router.refresh를 하면 됩니다만, 그건 수업의 범위를 벗어나기 때문에 fetch를 하는 단계에서 캐쉬를 사용하지 않는 방법을 보여드리겠습니다. 자세한 내용은 revalidate를 확인해주세요.
7. app/layout.js 편집
import Link from 'next/link' import './globals.css' export const metadata = { title: 'WEB tutorial', description: 'Generated by egoing', } export default async function RootLayout({ children }) { const resp = await fetch('http://localhost:9999/topics/', {cache:'no-cache'}) const topics = await resp.json(); console.log('page/layout.js/topics', topics) return ( <html> <body> <h1><Link href="/">WEB</Link></h1> <ol> {topics.map(topic=>{ return <li key={topic.id}><Link href={`/read/${topic.id}`}>{topic.title}</Link></li> })} </ol> {children} <ul> <li><Link href="/create">create</Link></li> <li><Link href="/update/id">update</Link></li> <li><button>delete</button></li> </ul> </body> </html> ) }
위의 코드는 아래 부분이 변경되었습니다.
const resp = await fetch('http://localhost:9999/topics/', {cache:'no-cache'})
{cache:'no-cache'}를 추가하면 캐쉬를 사용하지 않게 됩니다.
이제 layout.js의 fetch는 랜더링 될 때마다 캐쉬를 사용하지 않고 신선한 데이터를 가져오게 되었습니다.