본문 바로가기
  • 삽질하는 자의 블로그
메인-프로젝트/Next.js - 심리검사와 강아지 프로젝트

5. POST 를 이용한 데이터 가져오기, 로컬스토리지에 저장하기

by 이게뭐당가 2023. 2. 3.

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

 

댓글