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

3. DB의 데이터를 fetch - "API" 를 통한 GET 과 "사전 데이터페칭"을 통한 GET

by 이게뭐당가 2023. 1. 28.

Next.js 는 ServerSide 에서 사용 가능한 Code 를 제공한다.

 

클라이언트 측에서는 할 수 없는 DB 와 소통하는 코드가 아주 대표적이다.

 

서버측코드는 일반 클라이언트측 자바스크립트 코드와는 다르게 유저들에게 노출되어있지 않다.

기본적으로 유저들에게  숨겨져있으며 따로 볼 수 없다.

 

사전 데이터 페칭을 제공하는

getStaticProps 나 getServerSideProps 를 통해, 코드 내에서 직접적으로 서버측 코드를 사용 할 수 있고,

 

pages 의 api 폴더 내에, 자신이 직접 서버측 코드를 만들어 클라이언트측에서 fetch 를 통해 서버에 요청 할 수도 있다.

 

기본적으로 REST API 를 하기 위해, 독립적으로 node(서버) 와 react(클라이언트) 를 운영하지만

 

Nextjs 는 이 모든걸 한방에! CORS Error 를 처리할 필요도 없이 사용가능하다!

 

 

그렇다면 전에 만든 DB 에서 값을 가져와보자.

 

 

0. mongodb 를 사용하기 위해 mongodb 를 설치한다.

https://www.npmjs.com/package/mongodb

 

mongodb

The official MongoDB driver for Node.js. Latest version: 4.13.0, last published: a month ago. Start using mongodb in your project by running `npm i mongodb`. There are 11424 other projects in the npm registry using mongodb.

www.npmjs.com

 

 

 

1. helper 함수로 만든 DB 에 접근하자.

 

< src / helper / sample.ts >

    import { MongoClient } from "mongodb";

    export const client = new MongoClient(String(process.env.MONGODB_URL));

    export const getSample = async (client: MongoClient) => {
      const dbName = "personality-and-dog";
      const collectionName = "user-personality-result";
      
      const result = await client
        .db(dbName)
        .collection(collectionName)
        .find({})
        .toArray();

      return result;
    };

 

client 의 Type 을 정해주고, DB 에서 만든 DB Name 과 collection 을 가져온다.

mongodb 는 데이터를 가져올때 find({조건}) 를 사용하거나, findOne({조건}) 을 사용하여 data 를 get 할 수 있다.

 

 

 

2. [ 방법 1 ] getStaticProps (or getServerSideProps ) 를 통해 바로 가져오기

 

< pages / index.tsx >

    import Head from "next/head";
    import Link from "next/link";
    import { getSample, client } from "../helper/sample";
    import { useEffect } from "react";

    export default function Home(props: { responseData: { _id: string; message: string }}) {
      const { responseData } = props;
      console.log(responseData);

      return (
        ...
      )
    }

    export async function getStaticProps() {
      const response = await getSample(client);
      const responseData = JSON.stringify(response);

      return {
        props: { responseData },
      };
    }

 

서버측 코드인 getStaticProps 를 통해, api 를 통하지 않고 직접 DB의 값에 접근하여 가져왔다.

데이터 교환은 json 데이터 교환으로 이루어지므로, JSON.stringify 를 통해 javascript Codeparsing 해준다.

 

3. [ 방법 2 ] api 를 통해 가져오기 - api 의 정의

 

next.js 를 사용하는 가장 큰 장점 여러가지중 하나이다.

서버측 코드를 직접 만들어서, REST API 를 구현가능하다는 것.

 

위치는 pages / api / 안에 서버 코드를 만들어준다.

 

< pages / api / getSample.ts >

    import { getSample, client } from "../../helper/sample";
    import type { NextApiRequest, NextApiResponse } from "next";

    const sampleHandler = async (req: NextApiRequest, res: NextApiResponse) => {
      if (req.method === "GET") {
        const response = await getSample(client);

        res.status(200).json(response);
      }
    };

    export default sampleHandler;

 

next.js 의 official DOCS 를 보면 req 와 res 에 대한 Type 이 정의되어 있다. 가져오도록 하자.

https://nextjs.org/docs/api-routes/response-helpers

 

API Routes: Response Helpers | Next.js

API Routes include a set of Express.js-like methods for the response to help you creating new API endpoints. Learn how it works here.

nextjs.org

 

req.method 를 통해 어떤 요청이 들어왔는지 확인하고,

helper 에서 정의한 DB 에서의 값을 가져오는 함수를 호출하고

res 를 통해, status 코드를 만들어 json 데이터로 응답하도록 만든다.

 

 

3. [ 방법 2 ]api 를 통해 가져오기 - 클라이언트에서 서버로 요청(fetch)

 

< pages / index.tsx >

    import Head from "next/head";
    import Link from "next/link";
    import { useEffect } from "react";

    export default function Home() {

      useEffect(() => {
        const getSampleData = async (): Promise<void> => {
          const result = await fetch("/api/getSample");
          const resultData = await result.json();
          console.log(resultData);
        };
        getSampleData();

      }, []);

      return (
          ...
      );
    }

 

fetch 를 통해 만든 api (서버) 로 값을 req(요청)한다.

fetch 는 기본적으로 req.method 가 GET 이기 때문에 조건에 부합할 것이다.

 

req.method 가 GET 인 요청에 서버(API) 에 전달되면 API 는 response 로 JSON Data 를 응답(res) 할 것이고

응답된 데이터는 result 에 들어갈 것이다.

 

해당 값을 json() 을 통해 자바스크립트 코드로 parsing 하고 log 하는 비동기 함수를 만들고 바로 사용한다. 

 

  useEffect(() => {
    (async (): Promise<void> => {
      const result = await fetch("/api/getSample");

      const resultData = await result.json();

      console.log(resultData);
    })();
  }, []);

 

이렇게 즉시 실행 함수로 사용해도 괜찮다. 잘 작동한다.

 

그래서 DB의 값을 가져오기 위해 어떤 것을 사용해야 하는가

 

지금 생각하기에는 

 

getStaticProps 의 경우 사전 데이터 페칭을 통해 HTML 을 생성할때 필요한 데이터를 미리 정의한다는 것에 초점을 둔다.

데이터를 미리 정의하면 SEO 에 굉장히 유리하다. HTML 을 생성할때 이미 그 데이터가 들어가있기 때문이다.

props 로 가져온 데이터는 공개적으로 캐시(저장) 되므로 민감한 정보는 getStaticProps 로 가져오지 않는 것이 타당하다.

 

getStaticProps 는 여러가지 옵션을 통해, 조작이 가능하다.

 

서버측 코드를 이용하는 REST API 의 장점을 생각해본다면

유니폼 인터페이스를 가지고 있어 어느 기기, 어느 환경 에서도 HTTP 표준을 따른다면 데이터의 교환을 할 수 있다.

 

또한 Validation 은 기본적으로 서버측에서 담당하는 것이다.

클라이언트 측에서 아무리 열심히 Validation 을 한다고 한들 서버에서 보내줄때 하는 것 만 못하다.

 

가령 일반적인 Validation 이나

 

if (req.body.name === "" ||
    !req.body.email.includes("@")
){
    throw new Error(...)}

 

heper 함수로 복잡한 Validation 을 따로 정의하여 조건에 맞지 않는다면 다른 res 를 주고 error를 발생시킨다거나

 

if (signupValidation(email, password, name)) {
      res.status(400).json({ message: "정보를 다시 확인해주세요" });
      throw new Error("정보를 다시 확인해주세요");
    }

 

서로 다른 method 에 따라 다른 res 를 준다던가

 

if(req.method === "GET"){
	...
    res.status(200).json({...})
}

if(req.method === "POST"){
	...
    res.status(200).json({...})
}

 

이런 것들을 사용하기 위해, 중요하거나 사전데이터페칭이 필요 없는 데이터들은 

api 를 사용해 직접 정의한 서버로부터 값을 받는 것이 더 낫다는 생각을 한다.

 

추가적으로

API 를 이용하여야 DB에 값을 CRUD 할 수 있다.

그냥 api 에 더 익숙해지는 것이 낫지 않을까 생각한다.

 

결론

사전 데이터페칭이 필요하다면 (SEO 를 위해) getStaticProps 를 사용하자.
숨겨야 하는 정보나, 민감한 정보들은 공개적으로 캐시되는 getStaticProps 를 사용하면 안된다.

따로, 그것들이 필요하지 않는 한 api 를 이용하여 값을 가져오자.
어차피 값을 가져오는 것 이외에 DB 에 CRUD 를 하기 위해서는 api 를 이용해야 하니까 말이다.

 

더 찾아보고 더 생각해보고 나중에 이 글도 수정해야겠다.

댓글