개발새발 로그

React - 공공 API가 http만 지원하면 어떻게 해야 할까? [serverless, proxy] 본문

React

React - 공공 API가 http만 지원하면 어떻게 해야 할까? [serverless, proxy]

이즈흐 2024. 4. 26. 03:13

개인 프로젝트를 진행하면서 배포 후 테스트를 점검하기 위해 Vercel에 배포를 해야했다.

진행하면서 생긴 문제를 해결하는 과정을 정리해보려고 한다.

 

 

😯공공 데이터에서 http만 지원한다고?

이 부분을 전혀 생각하지 않고 배포를 했는데 아래와 같이 오류가 발생했다.

Mixed Content: The page at ' [배포 주소] ' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint '[요청 주소'. This request has been blocked; the content must be served over HTTPS

검색해보니 아래와 같은 오류를 뜻한다

이는 웹 페이지가 HTTPS로 로드되었지만, 페이지 내에서 HTTP로 리소스(이 경우에는 XMLHttpRequest 엔드포인트)를 요청하려고 할 때 발생합니다.
HTTPS 페이지에서는 모든 요청이 HTTPS를 통해 이루어져야 보안이 유지됩니다.
HTTP로 요청하려고 하면, 사용자의 데이터가 도청될 위험이 있기 때문에 브라우저는 이러한 요청을 차단합니다.

 

 

🤔어떻게 해결해야 할까?

그럼 서버측에서 https를 지원해줘야하는데 공공 데이터니까 당연히 안될 것이다.

그럼 내가 직접 어떤 해결책을 만들어야 했다.

 

나는 해결책 중 하나인 Vercel의 서버리스 함수를 사용하고자 했다.

 

사실 개인프로젝트를 과거에 진행할 때 CORS에러를 해결하려고 서버를 직접 express로 하나 만들어서 proxy서버로 이용했다.

근데 Vercel에서는 이걸 서버리스로 사용할 수 있도록 해주고 있었다.

 

그럼 먼저 이전에 사용했던 proxy서버를 사용하지 않고, Vercel의 서버리스 함수를 사용해야 했다.

 

📖Vercel 서버리스 기본 사용방법!

🛠️일단 먼저 vercel을 설치해야한다!

npm i -D vercel

 

🛠️ 그리고 package.json에 아래와 같이 Vercel로 열 수 있도록 명령어를 추가해줘야한다.

 

🛠️ 그리고 api라는 이름의 폴더를 최상위 경로에 만들어야한다.

api라는 이름의 폴더를 만들면 Vercel이 해당 폴더안에서 서버리스 함수를 구성하게 된다. (api라는 이름으로 지정해야 함)

 

🛠️ 이제 그럼 서버리스 함수 기본 구성을 보자

위 코드가 하나의 요청을 처리해서 응답을 해줄 수 있는 서버가 된다.

이 함수는 두개의 매개변수를 가질 수 있는 req와 res다.

 

- 이때 req와 res에 오류가 뜨는데 타입스크립트라 그렇다

- vercel에서 지원해주는 타입을 가져와서 지정해주면된다.

 

🛠️ 그럼 이제 실제로 요청을 해보자

Vue 코드입니다.

경로는 이미지와 같이 우리가 만든 파일 경로로 접근하면 된다.

앞쪽 별도의 도메인은 작성하지 않는다.

어디까지나 같은 도메인을 활용해서 내부서버에 접근을 한다라고 이해하면 된다.

 

👨‍💻그럼 우리는 실제로 적용해봐야지!

나는 아래와 같이 서버리스 함수를 구성했다.

import axios from 'axios'
import converter from 'xml-js'
import type { VercelRequest, VercelResponse } from '@vercel/node'

//env 활용
const { VITE_API_BASE_URL, VITE_API_KEY } = process.env

export default async function (req: VercelRequest, res: VercelResponse) {
  const sText = req.query.searchData
  const pageNo = req.query.pageNo
  const numOfRows = req.query.numOfRows
 
  //확정 할당 단언 사용
  //실제 요청 주소로 요청
  const { data } = await axios.get(apiUrl!, {
    params: {
      apiKey,
      sType: 'sCntntsSj',
      sText,
      pageNo,
      numOfRows
    }
  })
  //xml데이터로 오기 때문에 변환해야 함
  const xmlToJson = converter.xml2json(data)

  //이미 json으로 변환했으므로 send()로 보내줌
  //이때 클라이언트는 응답을 받았을 때, 이것이 JSON 문자열이라는 것을 알고 적절히 처리할 수 있도록 setHeader수행
  res.setHeader('Content-Type', 'application/json')
  res.send(xmlToJson)
}

실제로 데이터 요청은 아래와 같이 하면 된다.

export const getHerbList = async ({
  searchData = '',
  pageNo,
  numOfRows
}: HerbListPageParams) => {
  const response = await axios.get('/api/proxy', {
    params: {
      searchData,
      pageNo,
      numOfRows
    }
  })
  return response.data.elements[0].elements[1].elements[0].elements
}

 

이렇게 되면 기존의 proxy서버를 직접 만들었던 것은 필요없어진다.

그리고 vercel의 서버리스를 사용할 수 있도록 npm run vercel로 개발서버를 열면 잘 작동하게 된다.

 

여기까지 간단하게 정리하면

서버리스 함수를 사용해서 클라이언트는 해당 서버에 요청을 하고, 서버리스 함수는 실제 서버에 데이터를 요청하는 것이다.

이때 중간 역할을 하는 서버리스 함수가 CORS에러나 https<-->http 에러를 해결할 수 있게 되는 것이다.

 

 

Vercel에 환경변수 저장하는 부분은 생략하겠다! ( 다들 아니까!)

 

✨ 배포하고 새로고침 하니까 404 에러가 뜬다..? - SPA Fallback 설정하기

Vercel에 리액트 프로젝트를 배포하고 다른 경로에서 새로고침을 누르면 아래와 같이 뜰 것이다.

이게 왜그러냐면

로컬 프로젝트에서는 vite와 vercel을 통해 개발서버를 오픈하는 건데

실제 Vercel에서는 vite는 동작이 되지 않는다.

그래서 하위 주소에서 새로고침을 했을 때 하위 주소에 대한 연결이 따로 설정이 되어있지 않아서 에러가 발생하는 것이다.

 

우리가 index.html파일로 하나만 구성되어있는 SPA에서 만약 지금처럼 하위주소를 사용하고,

새로고침을 했을 때 잘 동작이 되는 것을 원한다면 별도로 Vercel서버에서 구성을 해줘야 한다.

 

vercel 공식문서 Legacy SPA Fallback

//최상위 경로에 vercel.json으로 생성
{
  "routes": [
    { "handle": "filesystem" },
    { "src": "/(.*)", "dest": "/index.html" }
  ]
}

위처럼 파일을 하나만들어서 구성해줘야한다.

 

간단하게 위 코드를 설명하자면

src 부분에 정규식으로 모든 하위 주소를 일치시키고 있다.

dest 부분에는 /index.html이 있다.

이 구성을 실제 Vercel 서비스에 넣어줘야지

Vercel의 호스팅 서버에서 하위 경로를 전부 다 index.html 파일로 연결을 해서

현재 우리 프로젝트가 하위 경로에서 새로고침을 해도 잘 보여질 수 있게 된다.

 

🤔그럼 끝인가?

아마 Vercel로 배포한 환경에서는 잘 작동할 것이다

하지만 다시 로컬 개발 서버를 열면 아래와 같이 오류가 생기고 페이지가 아예 열리지 않게 된다.

해당 이미지는 다른 곳에서 갖고온 것입니다. 개인프로젝트와 비슷한 오류입니다!

npm run vercel을 통해서 서버를 열었을 때와 vite의 구성과 충돌이 발생했기 때문에 문제가 생긴 것이다.

그니까 vercel 통해서 서버를 열었을 때vite 내부에서 동작하는 서버가 충돌이 발생한 것이다.

 

그래서 두개의 개발 서버를 따로 오픈해서 서로 연결을 해줘야한다.

 

아래 파일은 vite.confing.ts다.

위 이미지에서 표시한 부분을 추가해줘야한다.

이 코드는 vite에서 vercel의 서버리스 함수를 사용할 수 있도록 하는 것이다. 

 

target에 있는 주소가 vercel 서버의 주소다.

vercel 서버의 주소는 기본적으로 3000 포트로 열린다.

그래서 vercel 서버가 3000번 포트로 열리면서 서버리스 함수 사용하기 위한 /api 를 연결할 수 있게된다.

proxy는 무엇인가를 가로챈다는 의미를 지니고 있다.
그래서 vite의 서버를 오픈할 때 vercel 서버에 /api처럼 등록된 주소를 가로채서
자신이 사용하도록 만들어 주는 것이다.

이제 두 개의 터미널을 열고 npm run devnpm run vercel을 같이 동작시킨다.

 

중심이 되는 것은 아까 위에서 실행했던 vercel이 아니고 vite의 서버다.(vite에 해당하는 주소로 들어가야한다)

서버리스 함수를 사용하기 위해 vercel서버를 여는 것이다.

 

결론적으로 

실제 배포한 호스팅 서버 옵션 관리하기 위해서 vercel.json을 추가했는데

그 구성이 우리 프로젝트의 로컬 서버에 영향을 주게 됐다.

이를 해결하기위해 위처럼 vite 로컬 서버와 vercel  로컬 서버를 따로 열어

vite의 로컬 서버를 기준으로 vercel의 서버리스 함수를 쓸 수 있도록 proxy라는 옵션을 통해 연결을 한 것이다.

 

 

💢주의사항

 

위와 같이 npm run vercel을 통해 vercel을 이용해 개발서버를 오픈하다가 오류가 떴다.

이는 vercel에서 지원하는 typescript와 내 프로젝트의 타입스크립트 버전이 충돌이 된 것이었다.

 

내 프로젝트의 pakage.json에서 타입스크립트 버전이 옛날 버전일 수 있으니 확인해야한다.

왜냐하면 vercel에서는 무조건 최신 버전의 typescript를 사용하기 때문이다.

 

나도 확인해 봤더니 일주일 전에는 5.2.2 였는데 지금 npm i -D typescript@latest로 설치하니 5.3.2가 나왔다.

그래서 pakage.json에 overrides로 버전을 맞춰주니 위 같은 오류가 뜨지 않게 되었다.

 

이렇게 수정해도 해결되지않는다면 캐시 문제일 수 있으므로 node_modules와 package.lock.json도 지우고 다시 설치해서 실행해보면 될 것이다!

 

📘마무리 하며..

개인프로젝트 처음에 CORS에러가 생겨서 proxy서버를 express를 활용해 직접 만들어서 해결했었다.

근데 Vercel의 서버리스를 처음부터 사용할 걸.. 이라는 생각이 들었다.

 

Vercel의 서버리스함수를 알고 있었음에도 생각을 못해서 너무 아쉬웠다.

그리고 위처럼 vercel을 사용하고 서버리스를 사용하는 경우가 아주 많은 것 같아서

위 내용은 반복적으로 읽으면 좋을 것 같다!

728x90
반응형
LIST