문제 1
. 내부 서버에서 json-server의 db를 가져오고, 내부 서버에서 클라이언트로 데이터를 반환해서 전달해주는 로직을 짰는데, db를 수정했는데도 자꾸 수정 전 데이터가 들어왔다.
원인
.next
라는 폴더에 캐싱된 값이 들어있으므로 해당 폴더를 지워주자Why?!
- 빌드될 때 내부 서버, 즉 route.ts도 page.tsx와 같은 시점에 렌더링되기 때문에 page.tsx에서 짜놓은 내부 서버 통신 로직에서 해당 서버가 없는 상태가 발생한다. (걔도 아직 렌더링되고 있는 상태니까)
- 내부서버(ex : localhost:3000/api) 에 접근할 때의 시점에 해당 api가 준비되지 않아 발생하는 문제!
- 따라서, SSG로 구현하고 싶을때는 기존에 리액트 코드 짤때처럼 json-server에 곧장 통신하는 방식을 써야 한다.
문제 2
. 구조분해할당해서 name, description, image를 가져왔는데 console에 찍을때는 잘 들어오면서 막상 브라우저에서는 description은 undefined에, image도 db수정전 데이터가 들어왔다.
원인
key: {객체}
형식이어야 companyInfo라는 key를 갖고 구조분해할당을 할텐데, 없는 companyInfo를 찾고있는거였음.// ❌ 기존
import React from "react";
const AboutPage = async () => {
const response = await fetch(`http://localhost:4005/companyInfo`);
// 여기서 구조분해할당을 잘못하고있다!
const { companyInfo } = await response.json();
console.log(companyInfo);
const { name, description, image } = companyInfo;
return (
<div>
<li>{name}</li>
<li>{description}</li>
<img src={image} alt="" width={400} height={400} />
</div>
);
};
export default AboutPage;
// ✅ 해결
import React from "react";
const AboutPage = async () => {
const response = await fetch(`http://localhost:4005/companyInfo`);
// 이렇게 통으로 가져와서
const data = await response.json();
console.log(data);
// 여기서 구조분해할당 하는게 맞다.
const { name, description, image } = data;
return (
<div>
<li>{name}</li>
<li>{description}</li>
<img src={image} alt="" width={400} height={400} />
</div>
);
};
export default AboutPage;
void[] 형식은 ReactNode에 할당할 수 없습니다
라는 오류메시지 발생!원인 : map의 return문에 아무것도 없어서... 생긴 오류였음....... todo도 넣어보고 값 넣어보긴 했는데 태그를 넣어보진 않았다 ㅠㅠ
해결 : <></>
만 넣었더니 바로 해결됨
오류 메시지 : Unhandled Runtime Error Error: NextRouter was not mounted. https://nextjs.org/docs/messages/next-router-not-mounted
문제 : 버튼 클릭시 페이지 이동시키고싶어서 useRouter를 사용했는데 계속 runtime error 발생
원인 : useRouter
는 next/router
에서 import❌, next/navigation
에서 import✅
🧡
Route Handler
를 통해 특정 id의 todo를delete
,toggle
하고싶어서 찾던 중 알게된 방식!
로직 : 내부 서버(route)로 request 보낼때 body
에 id
를 담아서 보내기
http://localhost:3000/api/todos/${id}
도 시도해보았지만, 404 Not Found가 뜸http://localhost:3000/api/todos
)로 request를 보내려다보니, 그럼 id는 어떻게 보내야하지..? 생각하던 중 착안한 방식코드
// ✅ Csr.tsx - todoList(next.js)
"use client";
//...생략
const { mutate: deleteTodo } = useMutation({
mutationFn: async (id) => {
// url로 id를 전달하지 않음
await fetch(`http://localhost:3000/api/todos`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
// body로 id를 전달함
body: JSON.stringify({ id }),
});
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["todos"],
});
},
});
const onDeleteHandler = (id: string) => {
deleteTodo(id);
};
// ✅ route.ts (app > api > todos)
export async function DELETE(request: Request) {
// request body로 가져온 id 뽑아쓰기
const { id } = await request.json();
const response = await
// 가져온 id를 url 뒤에 넣어 해당 todo를 delete!
fetch(`http://localhost:4005/todos/${id}`, {
method: "DELETE",
});
return Response.json({
id,
});
}
Dynamic Route Segments
를 활용하여, 굳이 body에 id를 담아 보내지 않더라도 쉽게 id를 보낼 수 있다.Dynamic Route Segements
로 delete 구현하기params
를 활용하여 id를 가져올 수 있다!👍🏻 공식문서 : Next.js - Dynamic Route Segments
// ✅ Csr.tsx - TodoList(next.js)
// ...생략
const { mutate: deleteTodo } = useMutation({
mutationFn: async (id: string) => {
await fetch(`http://localhost:3000/api/todos/${id}`, {
method: "DELETE",
});
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["todos"],
});
},
});
const onDeleteHandler = (id: string) => {
deleteTodo(id);
};
// ✅ route.ts (app > todos > ⭐️ [id])
export async function DELETE(
request: Request,
// ⭐️ params도 가져오기!
{ params }: { params: { id: string } }
) {
// ⭐️ params에서 id를 빼서 쓴다!
const id = params.id;
const response = await fetch(`http://localhost:4005/todos/${id}`, {
method: "DELETE",
});
return Response.json({
id,
});
}
toggle
)// ✅ Csr.tsx - todoList(next.js)
// ...생략
const { mutate: toggleTodo } = useMutation({
// 가져온 id는 url뒤에 넣어주고, isDone은 body통해 route.ts에 보낼 예정
mutationFn: async ({ id, isDone }: { id: string; isDone: boolean }) => {
await fetch(`http://localhost:3000/api/todos/${id}`, {
// isDone의 상태를 수정할거라 PATCH
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
// isDone을 body통해 내부서버(route.ts)로 보내주기
body: JSON.stringify({ isDone }),
});
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["todos"],
});
},
});
// '완료'버튼 클릭시 mutaion함수로 id, isDone을 전달함
const onToggleHandler = (id: string, isDone: boolean) => {
toggleTodo({ id, isDone });
};
// route.ts (app > todos > ⭐️ [id])
export async function PATCH(
request: Request,
// delete와 마찬가지로 params를 가져옴
{ params }: { params: { id: string } }
) {
// body통해 받아온 isDone을 뽑아주기
const { isDone } = await request.json();
// url뒤에 넣을 id도 선언
const id = params.id;
// 해당 id의 url로, body통해 isDone 상태를 !isDone으로 수정하겠다는 요청 보내기
const response = await fetch(`http://localhost:4005/todos/${id}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ isDone: !isDone }),
});
// return값이 없으면 500 내부서버 오류가 발생한다.
return Response.json({
id,
isDone,
});
}
세상 간단함... 이미지를 Public폴더에 넣은다음, img src="" 여기에 그냥 파일명 넣어주면 끝..!!