[우아한테크코스 #8] API 서버를 배포해봅시다

rat8397·2022년 4월 3일
9

우아한테크코스

목록 보기
12/15
post-thumbnail

인증서버 만들기 (with json-auth-server)

아주 간단합니다. 백엔드 API서버를 하나 만든다고 생각하시면 될 것 같아요

// index.js
const jsonServer = require("json-server");

// 그들이 만들어 둔 auth server library
const auth = require("json-server-auth");

const app = jsonServer.create();
const router = jsonServer.router("db.json");


app.db = router.db;


app.use(auth);
app.use(router);

app.listen(process.env.PORT || 3000);
// db.json

{
  "users": [
    
  ]
}
// package.json
{
  "name": "json-auth-server",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1",

  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "json-server": "^0.17.0",
    "json-server-auth": "^2.1.0"
  }
}

터미널에서 node index.js 를 실행하면 백엔드 서버가 '로컬'에서 실행됩니다. 로컬에서 실행되는 경우 3000포트로 서버를 열어 놨으므로 localhost:3000 주소로 요청을 보내시면 됩니다.

인증서버 배포하기

아래 헤로쿠 문서만 보신다면 배포 할 수 있습니다 !!! 간단하죠잉 배포 따라히기! 그럼 저희는 총 두개의 백엔드 API 서버를 갖게되었어요. (로컬 백엔드 서버와 배포 백엔드 서버)

  • 로컬 클라이언트 -> 로컬 백엔드 서버

  • 배포 클라이언트 -> 배포 백엔드 서버로 요청이 가게끔해야겠죠? 그전에 cors에러가 발생하게 될 테니 이를 해결해주고 위 작업을 해봅시다.

클라이언트 - 인증 API서버 간 CORS에러 해결하기

개발단, 배포단 모두 클라이언트와 API서버는 서로 다른 출처(개발단 : 포트가 다름, 배포단 : 주소 자체가 다름)이기 때문에 CORS 정책을 위반하여 에러가 발생하게 됩니다. 여기서 기억해야하는 사실은 CORS 정책은 브라우저 정책이기 때문에 서버간의 소통에선 서로 다른 출처여도 에러가 발생하지 않습니다. -> CORS란

서버간 소통에는 에러가 발생하지 않는다구?! 그럼 프록시 서버를 이용하면 개발단에서 CORS 에러를 해결할 수 있겠군요..?! (프록싱이 하기 싫다면..? API서버에서 모든 응답 헤더의 Access-Control-Allow-Origin을 바꾸는 처리를 해주면 되겠군요..!)

1. 서버에서 cors 미드웨어를 사용하여 교차 출처를 허용하도록 한다.

이 방법은 배포단, 개발단에서의 모든 CORS에러를 해결해줍니다.

const jsonServer = require("json-server");
const auth = require("json-server-auth");
const cors = require("cors");

const app = jsonServer.create();
const router = jsonServer.router("db.json");
app.db = router.db;

app.use(
  cors({
    // 모든 클라이언트에서의 요청을 거부하지 않는다.
    // origin: "*",
    // 특정 클라이언트라면? 9000포트로 열린 클라이언트에서의 요청은 거부하지 않습니다.
    origin: "http://localhost:9000"
    
    credentials: true,
  })
);
app.use(auth);
app.use(router);

app.listen(process.env.PORT || 3000);

2. 클라이언트 단에서의 요청을 프록싱 한다.

이 방법은 프로덕션에서는 사용할 수 없어요... devServer에서의 옵션이기 때문에.. 프로덕션에서 사용하고자 한다면 프록시 서버를 개발하거나 서드파티 프록시 서버를 통해 우회하면 될 것 같아요!

    devServer: {
      port: 9000,
      historyApiFallback: true,
        // api
        // localhost:3000으로 에서 보내는 요청으로 변경합니다.
      proxy: {
        '/api/*': {
          target: 'http://localhost:3000',
          pathRewrite: { '^/api': '' },
        },
      },
    },

개발 API 주소와 배포 API 주소가 다른데?

이를 처리하는 방법은 간단합니다. production일 때 사용하는 env파일, development일 때 사용하는 env 파일을 구분하여 관리하시면 됩니다.

클라이언트 프로젝트 폴더에 .env.production - .env.development 를 생성합니다.

// .env.development
API_URL="로컬 백엔드 API주소"
// .env.production
API_URL="배포 백엔드 API주소"

이 둘을 웹팩에서 인식하게 하고 클라이언트로 주입해준다면 해결되겠죠?

webpack -> 클라이언트 빌드 파일로 주입하기

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const dotenv = require('dotenv');

module.exports = (env, options) => {
  // production일때와 devlopment 일 때 사용하게 되는 env파일이 다르므로 이를 처리해야합니다.
  // 이거 하나님이 해주는게 아니에요 스크립트에서 webpack 옵션으로 처리해야합니다.
  dotenv.config({ path: `.env.${options.mode}` });
  
  return {
    mode: options.mode,
    entry: './src/index.ts',
    resolve: {
      extensions: ['.js', '.css', '.ts'],
    },
    devServer: {
      port: 9000,
      historyApiFallback: true,
      proxy: {
        '/api/*': {
          target: 'http://localhost:3000',
          pathRewrite: { '^/api': '' },
        },
      },
    },
   
	// ...
    
    plugins: [
      new webpack.DefinePlugin({
        // 클라이언트에 전역변수로 주입합니다.
        API_URL: JSON.stringify(process.env.API_URL),
      }),
      new CleanWebpackPlugin(),
      new HtmlWebpackPlugin({
        template: './index.html',
      }),
      new CopyPlugin({
        patterns: [{ from: 'public', to: '' }],
      }),
    ],
  };
};
// package.json
"scripts":{
  // --mode development 를 주게되면 웹팩 설정 파일에서 options.mode로 조회할 수 있습니다.
    "start": "webpack serve --open --mode development",
    "build": "webpack --mode production",
}

사용처에선 다음과 같이 전역 변수처럼 사용할 수 있어요

export const fetcher = async ({ path, option }) => {
  // 전역변수처럼 사용한다.
  const response = await fetch(`${API_URL}${path}`, option);
  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`api 요청 중 에러 발생: ${API_ERROR_MSG[errorText]}`);
  }
  const data = await response.json();
  return data;
};

Ref

profile
Frontend Ninja

0개의 댓글