2023년 02월 15일 수정
나는 정말로 멍청이다.
Dynamic API Routes (동적 API 라우트) 가 있는데
굳이 클라이언트에서 POST 요청을 해서 값을 받으려했다.
https://nextjs.org/docs/api-routes/dynamic-api-routes
API Routes: Dynamic API Routes | Next.js
You can add the dynamic routes used for pages to API Routes too. Learn how it works here.
nextjs.org
API 라우트를 동적으로 만들어서
해당되는 값을
export default function handler(req, res) {
const { pid } = req.query
res.end(`Post: ${pid}`)
}
이렇게 가져온 후에, GET 요쳥을 했으면 됐다.
수정전 POST 로 값 요청하기
숫자 성격 테스트 페이지를 작성중에,
동적페이지에서 받아온 쿼리를 바탕으로 DB의 값을 가져오는 작업이 필요했다.
문제는 method GET 으로는 body를 넣을 수 없어, api 에 req.body 를 넣을 수 없었다.
결과적으로 DB 에서 find 의 조건을 적을 수 없어, POST 를 사용해 DB의 값을 가져오게 되었다.
클라이언트에서 요청할때, req.body를 넣을 필요가 없었다. 동적 API 라우트를 이용하여, query 로 값을 가져왔음 됐다.
숫자 테스트 페이지로직
1) 원하는 "번호" 선택
2) "번호" 를 선택하면, 해당 "동적페이지" 로 이동
3) "동적페이지" 에서 "쿼리"를 통해, DB 에서 결과에 해당하는 "결과값을 Get 해온다"
* fetch 의 method 는 "POST" 를 사용하여, "findOne" 을 사용하도록 한다.
method 가 "GET" 이라면 "body" 를 쓸 수 없고, body 를 쓸 수 없다면,
"findOne" 을 하기위한 "조건"을 "보낼 수 없으므로"
4) 결과를 확인하고 나와 맞는다면, DB에 "내 결과값을 저장", 하고 "나와 맞는 Dog" 페이지를 가져온다.
1. 우선 페이지에서 쿼리를 사용한다.
< pages / test-number / [result] >
import { GetStaticPropsContext } from "next";
import TestNumberResult from "../../components/test-number/test-number-result";
const FavoriteNumber = (props: { result: string }) => {
const { result } = props;
return <div> <TestNumberResult result={result}/> </div>;
};
export default FavoriteNumber;
export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
const result = params!.result?.toString();
return {
props: { result },
revalidate: 1000,
};
};
export async function getStaticPaths() {
const total = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
return {
paths: total.map((item) => ({ params: { result: item.toString() } })),
fallback: true,
};
}
2. DB 에서 받아올 API 를 작성하고
< / api / test-result.ts >
import type { NextApiRequest, NextApiResponse } from "next";
import { MongoClient } from "mongodb";
export const client = new MongoClient(String(process.env.MONGODB_URL));
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const dbName = "personality-and-dog";
const collectionName = "test-result";
if (req.method === "POST") {
const { testType, testId } = req.body;
let result;
try {
result = await client
.db(dbName)
.collection(collectionName)
.findOne({ testType, testId });
} catch (error) {
throw new Error("Something got ERROR.");
}
res.status(200).json({ message: "got test data", data: result });
}
};
export default handler;
3. page 에서 받은 쿼리를 이용하여, POST method 를 이용해 데이터를 요청해 가져온다.
< components / test-number / test-number-result.tsx >
import { useEffect, useState } from "react";
interface TestResultType {
_id: string;
testType: string;
testResult: string;
testDescription: string;
}
let initial = true; // 불필요한 re-render 방지를 위한 initial
const TestNumberResult = (props: { result: string }) => {
const [testResult, setTestResult] = useState<TestResultType>();
const { result } = props;
useEffect(() => {
if (initial) {
initial = false; // 불필요한 re-render 방지를 위한 initial
return;
}
(async () => {
const response = await fetch("/api/test-result", { // POST 로 데이터가져오기
method: "POST",
body: JSON.stringify({ testType: "number-test", testId: result }),
headers: {
"Content-Type": "application/json",
},
});
const responseData = await response.json();
setTestResult(responseData.data);
})();
}, []);
return (
<div>
<p>테스트아이디 {testResult?.testId}</p>
<p>테스트타입 {testResult?.testType}</p>
<p>결과 {testResult?.testResult}</p>
<p>설명 {testResult?.testDescription}</p>
</div>
)
};
export default TestNumberResult;
불필요한 초기 render 시 fetch 를 방지하기 위한 initial 을 사용했다.
4. 최종적으로, 유저가 누른 test의 결과값이 컴포넌트에 오면, 자동으로 그 값을 로컬스토리지에 저장하고,
버튼을 누르면 DB에 save 하면서, dogs 페이지로 넘어가는 넣는 로직을 작성한다.
< components / test-number / test-number-result.tsx >
const TestNumberResult = (props: { result: string }) => {
...
const [testResultData, setTestResultData] = useState<TestResultType>();
useEffect(() => {
if (initial) {
initial = false;
return;
}
window.localStorage.setItem("test-result", JSON.stringify(testResultData)); // 로컬스토리지에 저장
}, [testResultData]);
const submitHandler = async () => { // 버튼을 누르면
if (data === null) {
signIn("google", { callbackUrl: "/" }); // 로그인안했다면 하게 만들고
return;
}
const userId = data?.user?.email;
const testId = testResultData?.testId;
const testType = testResultData?.testType;
const testResult = testResultData?.testResult;
const date = new Date().toLocaleString("ko-KR");
const response = await fetch("/api/user-test-data", { // DB에 결과값을 넣는다.
method: "POST",
body: JSON.stringify({ userId, testId, testType, testResult, date }),
headers: { "Content-Type": "application/json" },
});
const responseData = await response.json();
if (!response.ok) {
console.log(responseData.message);
} else {
router.push("/dogs");
}
};
...
로컬스토리지에 저장하는 이유는 Redux-toolkit 이나 State 는 새로고침을 하면 값이 사라지기 때문이다.
유저는 테스트 결과를 다시볼수도, 뒤로와서 다시 저장할 수도 있다.
dogs 페이지로 넘어갔을때, 만약 저장된 State 값이 사라지게 된다면, dogs 페이지에서는 정상적인 값을 출력할 수 없다.
그러므로, 로컬스토리지에 저장하고, dogs 페이지로 이동했을때 그 값을 받아 동적으로 페이지를 작성할것이다.
로컬스토리지에 대하여 : https://dive-into-frontend.tistory.com/172
5. localStorage 에 대하여. [ WEB API ]
로컬스토리지를 사용해보자. 로컬스토리지는 저장가능 용량이 무한하고, 온갖 종류의 데이터를 전부 저장할 수 있으며 도메인 별로 저장한다는 특징을 가지고 있다. 또한 로컬스토리지에는 "문
dive-into-frontend.tistory.com
'메인-프로젝트 > Next.js - 심리검사와 강아지 프로젝트' 카테고리의 다른 글
7. useCallback 을 이용한 fetch ( 비동기함수 ) 사용하기, useEffect 초기 렌더시 내부함수 사용 금지하기 (0) | 2023.02.03 |
---|---|
6. SASS, @for 과 @if 를 활용한, 스타일링 지정 (0) | 2023.02.03 |
4. Redux-toolkit을 사용해 "비동기함수(fetch)" 를 이용한 DB 의 값 받아와서 넣기 + Thunk 생성자 + action.ts (0) | 2023.01.29 |
3. DB의 데이터를 fetch - "API" 를 통한 GET 과 "사전 데이터페칭"을 통한 GET (0) | 2023.01.28 |
2. MongoDB 연결과 API 를 통한 fetch(1) - MongoDB 연결 (0) | 2023.01.28 |
댓글