그럼에도 불구하고

👨‍💻

[Vite] Vite에서 Proxy 설정하기 with Cross origin & CORS Error 본문

이모저모/Bundler

[Vite] Vite에서 Proxy 설정하기 with Cross origin & CORS Error

zenghyun 2023. 9. 30. 16:19

Start!

 

🧑🏻‍💻 Cross origin이란

크로스 오리진 (cross origin) 문제란 "브라우저는 자신의 오리진과 다른 오리진의 API 서버와 통신할 때 문제가 발생한다"는 개념입니다. 크로스 오리진 문제를 발생시킴으로써 잠재적인 위험을 가진 문서의 로딩을 제한해 브라우저 공격의 가능성을 줄일 수 있습니다.

 

크로스 오리진 문제는 웹 브라우저에 내장된 SOP(Same Origin Policy: 동일 근원 정책)라는 보안 정책 때문에 발생합니다. 크로스 오리진과 SOP를 이해하려면 브라우저의 오리진이라는 개념부터 이해해야 합니다.

 

브라우저가 웹 서버에 요청(request)을 전송하면 이에 대해 웹 서버는 응답(response)을 합니다. 일반적인 경우라면 웹 서버는 HTML 문서 형태를 응답할 것입니다. 

 

👉 오리진 (Origin)

URI 경로 중 "앞에서 포트 번호까지의 문자열"이 바로 오리진입니다.

https://velog.io/@jellyjw/Same-Origin-Policy-%EC%99%80-CORS

 

웹 브라우저는 HTML 문서를 제공한 웹 서버의 정보 (가장 앞에서부터 포트 번호까지의 문자열 정보)를 오리진으로 저장합니다. 즉, 오리진은 "HTML 문서를 내려받은 원천지는 이곳입니다"라는 뜻입니다. 

 

👉  동일 근원 정책 (Sam Origin Policy, SOP)

SOP는 "브라우저의 오리진과 동일한 오리진을 가진 서버일 때만 통신을 가능하게 한다"라는 의미를 가진 브라우저 내부의 보안 정책입니다. 반대로 말하면 동일 오리진이 아닌 다른 오리진, 즉 크로스 오리진일 때는 통신에 뭔가 문제가 발생한다는 것입니다.

 

🧑🏻‍💻  크로스 오리진 발생 예시

이제 크로스 오리진이 발생하는 예시를 같이 보겠습니다.

 

1. 웹 브라우저는 프론트엔드 앱을 호스팅 하는 웹 서버 (http://localhost:3000)으로 index.html을 요청합니다.

2. 웹 서버로 응답받은 브라우저의 오리진은 "http://localhost:3000"의 문자열로 지정됩니다.

3. 이 오리진을 가진 브라우저가 자바스크립트 코드로 http://localhost:8000 오리진을 가진 백엔드 API 서버로 요청합니다.

4. 백엔드 API는 요청을 수신합니다. 

5. 백엔드 API에서는 웹 브라우저로 보낼 데이터를 생성합니다.

6. 백엔드 API는 응답을 전송합니다.

 

7. 웹 서버가 백엔드 API 서버가 보낸 응답을 수신하고 로딩해야 하는데 에러가 발생합니다. 

 

👉  크로스 오리진 문제 해결 방법 

크로스 오리진 문제는 크게 두 가지 해결 방법이 있습니다. 하나는 백엔드 API 서버 측에서 CORS(Cross Origin Resource Sharing)라는 기능을 제공해주는 방법이고, 또 한 가지는 프론트엔드 애플리케이션을 호스팅하는 웹 서버에 프록시(proxy)를 설정하는 방법입니다.

 

👉 CORS

CORS는 백엔드 API 측에서 제공해주어야 하는 기능입니다. 간단하게 알아보면, CORS는 "크로스 오리진의 브라우저가 백엔드 API 서버로 요청했을 때 서버에서 Access-Control-Allow-Origin HTTP 헤더로 브라우저의 오리진을 응답하여 브라우저가 통신 및 데이터 로딩을 할 수 있도록 허용하는 방법"입니다. 

 

아래 예시는 프론트엔드 애플리케이션을 호스팅 하는 서버 (이하 프론트 서버)에서 로딩한 HTML 문서의 자바스크립트 코드로 백엔드 API 서버와 직접 통신하는 것을 볼 수 있습니다. 요청 흐름을 간략하게 단계적으로 살펴보면 다음과 같습니다. 

 

1. 브라우저는 프론트 서버에서 HTML 문서를 받아와 자신의 오리진을 설정합니다.

2. 자바스크립트 코드로 백엔드 API 서버에 요청합니다. 이때 자신의 오리진을 Origin HTTP 헤더에 추가합니다.

3. 백엔드 API 서버는 전송된 Origin 헤더를 읽어내어 등록된 리스트에 일치하는 것이 있는지 확인합니다.

4. 백엔드 API 서버는 Access-Control-Allow-Origin 응답 헤더를 추가하고, * 또는 브라우저의 오리진을 값으로 지정하여 응답합니다.

5. 브라우저는 자신의 오리진과 백엔드 API 서버로부터 전송받은 Access-Control-Allow-Origin 헤더가 일치하거나 *라면 응답이 허가된 것으로 간주하고 데이터를 로딩합니다. 

 

만약 백엔드 API 서버에 대한 변경 권한을 가지고 있다면 이 방법을 사용하는 것이 좋습니다. :) 

CORS에 대한 자세한 내용은 다음 모질라의 공식 문서를 확인하길 바랍니다.

 

교차 출처 리소스 공유 (CORS) - HTTP | MDN

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라

developer.mozilla.org

👉 프록시를 이용한 우회

크로스 오리진 문제를 해결하는 방법 중 가장 쉬운 방법은 프록시를 이용한 방법입니다. 브라우저가 백엔드 API 서버와 직접 통신하는 것이 아니라 프론트엔드 애플리케이션을 호스팅하는 서버 (이하 프론트 서버)에 프록시를 설치하여 프론트 서버의 프록시를 거쳐서 백엔드 API와 통신하도록 하여 브라우저 측에서는 동일한 오리진과 통신하도록 하는 방법입니다.

 

프론트엔드 애플리케이션을 백엔드 API 서버에 호스팅하면 크로스 오리진 문제는 발생하지 않을 거야"라고 생각할 수도 있겠지만,

실제 운영 환경에서는 백엔드 API 서버와 프론트엔드 서버가 분리된 경우가 많다고 합니다. :) 

적어도 개발 중에 npm run dev로 실행한 개발 서버는 백엔드 API 서버와 분리될 수밖에 없습니다.

 

따라서 프록시를 개발 서버와 다양한 운영 환경의 프론트엔드 호스팅 서버에 설정하는 방법을 알고 있어야 합니다.

🧑🏻‍💻 Vite에서 Proxy 설정하는 법 (vite.config.ts)

npm run dev 또는 npm run start로 실행되는 개발 서버에 프록시를 추가하도록 설정해보겠습니다.

 

Vite로 프로젝트를 생성한 경우에는 vite.config.ts에서 설정하면 됩니다. 

(webpack 사용하면 webpack.config.ts에서 하면 됩니다!)

 

📌 vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
	plugins: [react()],
    server: {
    	proxy: {
         "/api": {
         target: "http://localhost:8000",
         changeOrigin: true,
         rewrite: (path) => path.replace(/^\/api/, ""),
       },
     },  
   },
});

"/api"는 요청 경로가 /api로 시작하는 경우 target 경로로 전달하겠다는 것을 의미합니다.

/api/todolist/zenghyun과 같이 요청하면 http://localhost:8000 뒤에 요청 경로를 붙여서 

http://localhost:8000/api/todolist/zenghyun으로 요청을 전달하는 것입니다. 

 

만일 요청 경로를 바꾸고 싶으면 rewrite 속성을 추가해서 정규식을 이용해 경로를 변경하는 패턴을 등록하면 됩니다.

위에서는 최초 요청 경로에서 "/api" 부분을 찾아 빈 문자열로 변경, 즉 /api 문자열을 제거하고 나머지 부분만 경로로 덧붙여 요청을 전달한다는 뜻입니다.

 

📌 최초 요청 경로: /api/todolist/zenghyun

📌 타깃: http://localhost:8000

📌 최종 전달 경로: http//localhost:8000/todolist/zenghyun

 

🏷️ Ref

https://developer.mozilla.org/ko/docs/Web/HTTP/Access_control_CORS

https://velog.io/@jellyjw/Same-Origin-Policy-%EC%99%80-CORS

 

 

 

Comments