๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Frontend Dev/๊ธฐํƒ€ ์‹ค์Šต

Proxy ์„ค์ •ํ•ด HTTP ํ†ต์‹ ํ•˜๊ธฐ ( webpack dev server์˜ proxy์™€ React Proxy ์‚ฌ์šฉ๋ฒ• )

๋ฐ˜์‘ํ˜•

๐Ÿ’ฌ ์ง€๋‚œ๊ธ€๋“ค๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ด๋ฒˆ์—๋„ ์—ญ์‹œ CodeStates ๋ถ€ํŠธ์บ ํ”„ ์ค‘ ์‹ค์Šตํ–ˆ๋˜ ๋‚ด์šฉ์œผ๋กœ Proxy ์„ค์ •์— ๊ด€ํ•œ ๋ถ€๋ถ„์ด๋‹ค.

 

Proxy ์„œ๋ฒ„, ์–ธ์ œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„๊นŒ?

 ๋ธŒ๋ผ์šฐ์ €์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ API๋ฅผ ์š”์ฒญ ํ•  ๋•Œ์—๋Š” ๋ธŒ๋ผ์šฐ์ €์˜ ํ˜„์žฌ ์ฃผ์†Œ์™€ API ์ฃผ์†Œ์˜ ๋„๋ฉ”์ธ์ด ์ผ์น˜ํ•ด์•ผ๋งŒ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋งŒ์•ฝ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์—์„œ API๋ฅผ ์š”์ฒญํ•ด์„œ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋ ค๋ฉด CORS ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค.

 

 ๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป ์‹ค์ œ ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•˜๋ฉฐ ์„œ๋ฒ„์˜ API ๋ฅผ ์š”์ฒญํ•ด์•ผ ํ•  ๋•Œ, ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” localhost:3000 ์—์„œ ๋“ค์–ด์˜ค๋Š” ๊ฒƒ์ด ์ฐจ๋‹จ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ฒ„ ์ชฝ์— ํ•ด๋‹น ๋„๋ฉ”์ธ์„ ํ—ˆ์šฉํ•˜๋„๋ก ๊ตฌํ˜„์„ ํ•ด์•ผํ•œ๋‹ค. ํ•˜์ง€๋งŒ CORS ์„ค์ •์„ ํ•˜๋Š” ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ฐ”์˜๊ฑฐ๋‚˜, ๊ธฐ๋Šฅ ๊ตฌํ˜„์ด ๋‹น์žฅ์€ ์•ˆ๋œ๋‹ค๋ฉด? ์„œ๋ฒ„์—์„œ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํด๋ผ์ด์–ธํŠธ์˜ ์›นํŒฉ ๊ฐœ๋ฐœ์„œ๋ฒ„(๋˜๋Š” React ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋“ฑ)์—์„œ ์ œ๊ณตํ•˜๋Š” Proxy๋ผ๋Š” ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด์„œ๋„ CORS ์ •์ฑ…์„ ์šฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.

 ์ด๋Š” ๋ณ„๋„์˜ ์‘๋‹ต ํ—ค๋”๋ฅผ ๋ฐ›์„ ํ•„์š” ์—†์ด ๋ธŒ๋ผ์šฐ์ €๋Š” React ์•ฑ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๊ณ , ํ•ด๋‹น ์š”์ฒญ์„ ๋ฐฑ์—”๋“œ๋กœ ์ „๋‹ฌํ•˜๊ฒŒ ๋œ๋‹ค. ์—ฌ๊ธฐ์„œ React ์•ฑ์ด ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ ์‘๋‹ต ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๋ธŒ๋ผ์šฐ์ €๋กœ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์“ฐ๊ธฐ ๋•Œ๋ฌธ์— ๋ธŒ๋ผ์šฐ์ €๋Š” CORS ์ •์ฑ…์„ ์œ„๋ฐ˜ํ–ˆ๋Š”์ง€ ๋ชจ๋ฅด๊ฒŒ ๋œ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๋ฅผ proxy ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ์†์ด๋Š” ๊ฒƒ์ด๋‹ค.

 

1. webpack dev server์˜ proxy

๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป webpack dev server์˜ proxy ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด ์šฐํšŒํ•˜์—ฌ ์‘๋‹ต ๋ฐ›๊ธฐ

 webpack dev server์˜ proxy๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด, ๋ธŒ๋ผ์šฐ์ € API๋ฅผ ์š”์ฒญํ•  ๋•Œ ๋ฐฑ์—”๋“œ ์„œ๋ฒ„์— ์ง์ ‘์ ์œผ๋กœ ์š”์ฒญ์„ ํ•˜์ง€ ์•Š๊ณ , ํ˜„์žฌ ๊ฐœ๋ฐœ์„œ๋ฒ„์˜ ์ฃผ์†Œ๋กœ ์šฐํšŒ ์š”์ฒญ์„ ํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์›นํŒฉ ๊ฐœ๋ฐœ ์„œ๋ฒ„์—์„œ ํ•ด๋‹น ์š”์ฒญ์„ ๋ฐ›์•„ ๊ทธ๋Œ€๋กœ ๋ฐฑ์—”๋“œ ์„œ๋ฒ„๋กœ ์ „๋‹ฌํ•˜๊ณ , ๋ฐฑ์—”๋“œ ์„œ๋ฒ„์—์„œ ์‘๋‹ตํ•œ ๋‚ด์šฉ์„ ๋‹ค์‹œ ๋ธŒ๋ผ์šฐ์ € ์ชฝ์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

1) proxy ์ถ”๊ฐ€

 ์›นํŒฉ ๊ฐœ๋ฐœ์„œ๋ฒ„์˜ proxy ์„ค์ •์€ ์›๋ž˜ ์›นํŒฉ ์„ค์ •์„ ํ†ตํ•ด์„œ ์ ์šฉ์„ ํ•˜์ง€๋งŒ, CRA๋ฅผ ํ†ตํ•ด ๋งŒ๋“  ๋ฆฌ์•กํŠธ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” package.json ์—์„œ "proxy" ๊ฐ’์„ ์„ค์ •ํ•˜์—ฌ ์‰ฝ๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑ์ด ๋˜์–ด ์žˆ๋‹ค.

...
"browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "proxy" : "<http://localhost:3080>" // ์šฐํšŒํ•  IP ์ฃผ์†Œ
}

๐Ÿ’ฌ proxy๋Š” ๋ณดํ†ต ๋งจ ๋ฐ‘์— ์ž‘์„ฑ์„ ํ•ด ๊ธˆ๋ฐฉ ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

 

2) ์š”์ฒญ url์˜ ๋„๋ฉ”์ธ ๋ถ€๋ถ„ ์ œ๊ฑฐ

fetch, ํ˜น์€ axios๋ฅผ ํ†ตํ•ด ์š”์ฒญํ•˜๋˜ ๋ถ€๋ถ„์—์„œ ๋„๋ฉ”์ธ ๋ถ€๋ถ„์„ ์ œ๊ฑฐํ•œ๋‹ค.

export async function getAllfetch() {
  const response = await fetch('์šฐํšŒํ•  api์ฃผ์†Œ/params');
  .then(() => {
     ...
   })
}

โฌ‡๏ธŽ

export async function getAllfetch() {
  const response = await fetch('/params');
  .then(() => {
     ...
   })
}

 

 


example

โœ๏ธ CodeStates SEB ๊ณผ์ œ ์ค‘ ์˜ˆ์‹œ ์ฝ”๋“œ

// BookService.js
/* ๊ธฐ์กด fetch url์˜ ๋„๋ฉ”์ธ ๋ถ€๋ถ„์„ ์ œ๊ฑฐํ•œ๋‹ค.
const response = await fetch('<http://localhost:3080/api/books>');
*/ 

export const getAllBooks = async () => {
  const response = await **fetch('/api/books')**; 
  return await response.json();
}

export const createBook = async (data) => {
  const response = await **fetch('/api/book'**, {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({book: data})
    })
  return await response.json();
}

๋„๋ฉ”์ธ ๋ถ€๋ถ„์„ ์ œ๊ฑฐํ•˜๋ฉด ํ˜„์žฌ ๊ฐœ๋ฐœ์„œ๋ฒ„์ธ http://localhost:3000์œผ๋กœ ๋ณด๋‚ด๊ฒŒ ๋œ๋‹ค.

 

โ—๏ธ proxy ์„ค์ •์„ ๊ฑด๋“œ๋ฆฌ๋ฉด ํด๋ผ์ด์–ธํŠธ๋ฅผ ๋‹ค์‹œ ์‹œ์ž‘ํ•ด์ค˜์•ผ ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค.

Uncaught (in promise) SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON

โœ๏ธ CodeStates SEB ๊ณผ์ œ ์ค‘ , ์ฒ˜์Œ proxy ์„ค์ • ํ›„์— ์œ„์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๊ณ„์†ํ•ด์„œ ๋‚ฌ๋‹ค. npm start๋กœ ํด๋ผ์ด์–ธํŠธ๋ฅผ ๋‹ค์‹œ ์‹œ์ž‘ํ•˜๋‹ˆ ์ ์šฉ์ด ๋˜์—ˆ๋‹ค. ๋‚˜์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋‚œ ์‚ฌ๋žŒ์ด ๋ช‡๋ช‡ ์žˆ๋Š”๊ฒƒ ๊ฐ™์•˜๋Š”๋ฐ, ํ•ด๊ฒฐ์ด ์•ˆ๋œ ๋ถ„๋“ค๋„ ์žˆ๋Š” ๊ฒƒ ๊ฐ™๋‹ค. ์ด๋Š” ๋„คํŠธ์›Œํฌ ๋ฌธ์ œ์ผ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค.

 

 

 


 

2. React Proxy ์‚ฌ์šฉ๋ฒ•

๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป  http-proxy-middleware์˜ proxy ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ 2๊ฐœ ์ด์ƒ์˜ ๋„๋ฉ”์ธ์—์„œ ์‘๋‹ต ๋ฐ›๊ธฐ

 webpack dev server์—์„œ ์ œ๊ณตํ•˜๋Š” proxy๋Š” ์ „์—ญ์ ์ธ ์„ค์ •์ด๊ธฐ ๋•Œ๋ฌธ์—, ์ข…์ข… ํ•ด๋‹น ๋ฐฉ๋ฒ•์ด ์ถฉ๋ถ„ํžˆ ์ ์šฉ๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ƒ๊ธฐ๊ธฐ๋„ ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ์ˆ˜๋™์œผ๋กœ proxy๋ฅผ ์ ์šฉํ•ด์ค˜์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ด๋•Œ๋Š” http-proxy-middleware ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

 

1) http-proxy-middleware ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜

npm install http-proxy-middleware --save

 

2) src ํด๋” ๋‚ด setupProxy.js ํŒŒ์ผ ์ƒ์„ฑ

โœจ ๊ธฐ๋ณธ์˜ˆ์‹œ

// setupProxy.js 

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    '/api', // proxy๊ฐ€ ํ•„์š”ํ•œ path prameter๋ฅผ ์ž…๋ ฅ
    createProxyMiddleware({
      target: '<http://localhost:5000>', // ํƒ€๊ฒŸ์ด ๋˜๋Š” api url๋ฅผ ์ž…๋ ฅ
      changeOrigin: true, // ๋Œ€์ƒ ์„œ๋ฒ„ ๊ตฌ์„ฑ์— ๋”ฐ๋ผ ํ˜ธ์ŠคํŠธ ํ—ค๋”๊ฐ€ ๋ณ€๊ฒฝ๋˜๋„๋ก ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„
    })
  );
};

 

โœจ ๋‘ ๊ฐœ ์ด์ƒ์˜ ๋„๋ฉ”์ธ ์„ค์ •

// setupProxy.js 

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    '/api', 
    createProxyMiddleware({
      target: '<http://localhost:3080>',
      changeOrigin: true,
    })
  );
  app.use(
    '/api2', 
    createProxyMiddleware({
      target: '<http://localhost:3070>',
      changeOrigin: true,
    })
  );
};

// ์œ„์ฒ˜๋Ÿผ ๋”ฐ๋กœ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ , ํ•œ๋ฒˆ์— ์จ์ค„์ˆ˜๋„ ์žˆ๋‹ค.
module.exports = function(app) {
  app.use(
    ['/api', '/api2'], 
    createProxyMiddleware({
      target: '<http://localhost:3080>', // ๊ธฐ๋ณธ url (๋ฐฐ์—ด ๋งจ ์•ž api์˜ ์ฃผ์†Œ)
      changeOrigin: true,
      router: {
        '/api2' : '<http://localhost:3070>'
      }
    })
  );
};

 

3) ์š”์ฒญ url์˜ ๋„๋ฉ”์ธ ๋ถ€๋ถ„ ์ œ๊ฑฐ

 fetch, ํ˜น์€ axios๋ฅผ ํ†ตํ•ด ์š”์ฒญํ•˜๋˜ ๋ถ€๋ถ„์—์„œ ๋„๋ฉ”์ธ ๋ถ€๋ถ„์„ ์ œ๊ฑฐํ•œ๋‹ค.

 (webpack dev server์—์„œ ์ œ๊ณตํ•˜๋Š” proxy ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ๋•Œ์™€ ๋™์ผ)

export async function getAllfetch() {
  const response = await fetch('์šฐํšŒํ•  api์ฃผ์†Œ/params');
  .then(() => {
    ...
  })
}

export async function getAllfetch() {
  const response = await fetch('/params');
  .then(() => {
    ... 
  })
}

 

 


example

โœ๏ธ CodeStates SEB ๊ณผ์ œ ์ค‘ ์˜ˆ์‹œ ์ฝ”๋“œ

// setupProxy.js 

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    '/api', 
    createProxyMiddleware({
      target: '<http://localhost:3080>',
      changeOrigin: true,
    })
  );
  app.use(
    '/api2', 
    createProxyMiddleware({
      target: '<http://localhost:3070>',
      changeOrigin: true,
    })
  );
};
export const getAllTodos = async () => {
  const response = await **fetch('/api2/todos')**;
  return await response.json(); 
}

export const createTodo = async (data) => {
  const response = await **fetch('/api2/todo'**, {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({todo: data})
    })
  return await response.json();
}

 

โœ๏ธ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์„œ๋กœ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์—์„œ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

 


 

๋ฐ˜์‘ํ˜•