next.config.js에서 rewrites 옵션을 사용하면 next 서버가 일종의 proxy 역할을 하도록 구현할 수 있다.

요청 url을 숨기고 싶을 때 따로 API를 구현하지 않아도 되고 머리 아픈 CORS 에러도 간단하게 해결할 수 있어서 종종 사용하는데, url에 query parameter를 적용하다가 삽질했던 기록을 남겨본다.

path parameter 제대로 이해하기

/aladin-api?QueryType=Bestseller와 같은 요청을 처리하고 싶을 때, 해당 요청의 path는 /aladin-api이다.

module.exports = {
  async rewrites() {
    return [
      {
        source: '/aladin-api',
        destination: `${process.env.ALADIN_OPEN_API}/api`,
      },
    ];
  },
};

source 필드에 정규표현식으로 query parameter key를 매치해보려고 시도하면 안된다.. 404 만남!😅

has 사용하기

대신 특정 query parameter가 포함되어 있는지 확인하기 위해 사용할 수 있는 필드가 있는데, 바로 has이다.

module.exports = {
  async rewrites() {
    return [
      {
        source: '/aladin-api',
        /** /aladin-api?QueryType=Bestseller 요청에 매치된다. */
        has: [
          {
            type: 'query',
            key: 'QueryType',
            value: 'Bestseller',
          },
        ],
        destination: `${process.env.ALADIN_OPEN_API}/api?QueryType=Bestseller`,
      },
    ];
  },
};

query parameter의 value 값을 그대로 destination에 넣고 싶다면, named capture group을 사용해야 한다. (query key를 변수처럼 사용할 수는 없다.)

module.exports = {
  async rewrites() {
    return [
      {
        source: '/aladin-api',
        /** /aladin-api?QueryType=* 요청에 매치된다. */
        has: [
          {
            type: 'query',
            key: 'QueryType',
            value: '(?<QueryType>.*)',
          },
        ],
        destination: `${process.env.ALADIN_OPEN_API}/api?QueryType=:QueryType`,
      },
    ];
  },
};

has는 query parameter 뿐만 아니라 header나 cookie에 특정 값이 포함되어 있는지 확인할 때 사용할 수도 있다.

주의할 점

has 필드로 전달되는 모든 query key가 존재해야지만 매칭된다는 점에 주의해야 한다. (아주.. 섬세한 친구이다..🫠)

module.exports = {
  async rewrites() {
    return [
      {
        source: '/aladin-api',
        /**
         * /aladin-api?QueryType=*&Cover=* 요청에 매치된다.
         * /aladin-api?Cover=*&QueryType=* (O)
         * /aladin-api?QueryType=* (X)
         * /aladin-api?Cover=* (X)
         */
        has: [
          {
            type: 'query',
            key: 'QueryType',
            value: '(?<QueryType>.*)',
          },
          {
            type: 'query',
            key: 'Cover',
            value: '(?<Cover>.*)',
          },
        ],
        destination: `${process.env.ALADIN_OPEN_API}/api?QueryType=:QueryType&Cover=:Cover`,
      },
    ];
  },
};

마무리

간단한 API에서 활용하기에는 문제가 없어 보이지만, 더 범용적으로 사용되는 API라면 rewrites 옵션만으로는 분기 처리가 쉽지 않아 별도의 API를 구축하는게 더 효율적일 것 같다.

참고