본문 바로가기

프론트엔드/Next.js

Next.js API routes CORS 대응하기 + hook 이중 호출 방지하기

  • 프로젝트: tracking map
  • 키워드: Next.js, api routes, CORS, next.config.js, rewrites, strict mode, double-invoking
  • 상황 1
    1. Next.js에서 Naver Maps Directions 5 서비스의 driving(길찾기) API를 호출해야한다.
    2. API를 Next.js의 API routes를 이용하여 분리하고, CORS에도 대응하고자 한다.
  • 해결 방법 1
    1. 먼저 api/driving.ts 파일을 만들어 API를 분리한다.
      (참고: start와 goal parameter를 입력값으로 하면,  네이버 길찾기 API를 이용하여 출발지와 목적지에 대한 paths(좌표들의 배열)를 return 하는 로직이다.)
        // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
      import type { NextApiRequest, NextApiResponse } from 'next';
      import { Coordinates } from '../../types/map';
      
      type Data = {
        paths: Coordinates[];
      };
      
      export default async function handler(req: NextApiRequest, res: NextApiResponse<Data>) {
        const { start, goal } = req.query;
      
        let paths = [];
        try {
          const response = await fetch(
            `https://naveropenapi.apigw.ntruss.com/map-direction/v1/driving?start=${start}&goal=${goal}`,
            {
              headers: {
                'X-NCP-APIGW-API-KEY-ID': process.env.NEXT_NCP_CLIENT_ID,
                'X-NCP-APIGW-API-KEY': process.env.NEXT_NCP_CLIENT_SECRET,
              },
            },
          );
          paths = (await response.json()).route.traoptimal[0].path.map((path: Coordinates) => path.reverse());
        } catch (e) {
          paths = [];
        }
      
        res.status(200).json({ paths });
      }

      위의 로직을 next.js client code에서 호출하는 코드는 아래와 같을 것이다.
      (driving.ts 파일이 api 폴더 아래에 있으므로 '/api/driving'이 end point가 된다.

        const response = await fetch(`/api/driving?start=${start}&goal=${goal}`);


    2. 하지만 클라이언트 코드를 실행해보면, CORS 에러가 발생하며 응답값이 제대로 내려오지 않는 것을 확인할 수 있다.
      이 에러를 피하기 위해 naver api URL에 직접 요청을 날리지 않고, next에서 지원해주는 proxy를 이용하여 우회해야한다.

      next.config.js에서 'rewrites'를 이용하여 api 요청을 우회한다.
        /** @type {import('next').NextConfig} */
      
      module.exports = {
        ...
        async rewrites() {
          return [
            {
              destination: 'https://naveropenapi.apigw.ntruss.com/:path*',
              source: '/example/:path*',
            },
          ];
        },
      };


    3. api/driving.ts의 fetch url을 proxy 주소로 변경한다.
        const response = await fetch(
        `${process.env.NEXT_PUBLIC_URL}/example/map-direction/v1/driving?start=${start}&goal=${goal}`,
        {
          headers: {
            'X-NCP-APIGW-API-KEY-ID': process.env.NEXT_NCP_CLIENT_ID,
            'X-NCP-APIGW-API-KEY': process.env.NEXT_NCP_CLIENT_SECRET,
          },
        },
       위 코드의 'NEXT_PUBLIC_URL' 환경 변수 값에는 사이트의 URL을 적으면 된다.

    4. 이제 클라이언트 코드를 실행해도 CORS 에러가 발생하지 않는다! 

  • 상황 2
    1. Next.js 개발 도중 useEffect hook이 두 번 실행되는 현상을 발견했다.
    2. 자기 자신과 부모 컴포넌트를 모두 확인해보니 컴포넌트는 1번씩만 렌더링되고 있었고, hook의 dependency array도 이상이 없었기 때문에 코드 상으로는 문제가 없었다.
    3. 또한, 배포했을 때 프로덕션 환경에서는 이렇다 할 버그가 없었기 때문에 개발 환경에 문제가 있을 것이다.
  • 해결 방법 2
    1. 구글링 결과, 정답은 next.config.js 파일에 있었다.
      https://ko.reactjs.org/docs/strict-mode.html
      react strict mode가 설정되어 있을 때, React가 side effects를 찾아내기 위해 의도적으로 hook을 이중 호출할 수 있다는 것이다.

      Strict 모드가 자동으로 부작용을 찾아주는 것은 불가능합니다. 하지만, 조금 더 예측할 수 있게끔 만들어서 문제가 되는 부분을 발견할 수 있게 도와줍니다. 이는 아래의 함수를 의도적으로 이중으로 호출하여 찾을 수 있습니다.

    2. next.config.js에서 strict mode를 끄면 해당 현상이 사라지는 것을 확인할 수 있다. 다만 이유가 없다면 켜두는 것이 좋고, strict mode를 켜두어도 프로덕션 환경에선 자연스럽게 사라지는 현상이라 문제가 없을 것이다.
        /** @type {import('next').NextConfig} */
      
      module.exports = {
        reactStrictMode: false,
        ...
      };