Skip to Content
WebReactReact 실무19. 빌드·환경 변수·배포(Vercel/GitHub Pages)

이번 편의 결과물: bookshelf가 실제 URL로 배포되어 브라우저에서 접속됩니다. · 다루는 개념: 프로덕션 빌드, .env 환경 변수 분리, Vercel 배포, GitHub Pages 대안 배포

이 편에서 만드는 파일

bookshelf/ ├── .env.development + 개발용 환경 변수 ├── .env.production + 배포용 환경 변수 ├── vercel.json + Vercel 리라이트 설정 ├── vite.config.js ~ GitHub Pages용 base 경로 분기 ├── package.json ~ deploy:gh-pages 스크립트, gh-pages 의존성 추가 └── src/api/ ├── client.js + 08편의 apiFetch를 books.js에서 분리한 공용 모듈 ├── books.js ~ client.js의 apiFetch 사용 └── auth.js ~ client.js의 apiFetch 사용

개념 정리

환경 변수는 빌드 시점에 굳는다

Vite는 import.meta.env.VITE_* 형태의 값을 빌드하는 순간 실제 문자열로 치환합니다. 실행 중에 읽는 것이 아니라 번들 파일 안에 값이 그대로 박힙니다. 배포 후 값을 바꾸면 다음 빌드부터 반영됩니다. VITE_ 접두사가 없는 변수는 브라우저 번들에 포함되지 않습니다.

파일적용 시점용도
.env.developmentnpm run dev로컬 개발용 값
.env.productionnpm run build배포용 값
.env.local항상(git 무시 대상)개인별 비공개 값

이 과목의 배포가 갖는 한계

01편 학습 방향에서 이미 밝혔듯, 이 과목은 실서버 구현을 다루지 않습니다. 04편부터 백엔드로 써온 json-server는 학습자의 로컬 컴퓨터에서만 실행되는 개발용 도구라, Vercel에 올린 정적 프런트엔드가 그 주소(http://localhost:3001)에 접근할 수 없습니다. 그래서 배포된 데모는 화면과 빌드 파이프라인까지 실제로 동작하는 것을 목표로 하고, “로그인 후 책 목록 조회”처럼 백엔드가 필요한 기능은 배포 URL을 연 사람이 자신의 컴퓨터에서 json-server를 함께 띄워야 확인할 수 있다는 점을 안내합니다. VITE_API_BASE_URL을 환경 변수로 분리해 두면, 이후 실제 서버를 마련했을 때 이 값만 바꿔 그대로 연결할 수 있습니다.

정적 호스팅과 SPA 라우팅

bookshelfdist/ 폴더는 index.html과 해시가 붙은 JS·CSS뿐인 정적 파일입니다. react-router가 만드는 /books/3 같은 경로는 실제 파일로 존재하지 않아, 이 경로에서 새로고침하면 정적 서버가 파일을 못 찾아 404를 돌려줍니다. 해법은 어떤 경로든 못 찾으면 index.html을 200으로 돌려주는 리라이트 규칙입니다.

실습

1. 환경 변수 파일 만들기

# .env.development VITE_API_BASE_URL=http://localhost:3001
# .env.production VITE_API_BASE_URL=http://localhost:3001

두 값을 같게 둔 이유는 이 과목이 실제 호스팅된 백엔드를 마련하지 않기 때문입니다. 실제 서버를 붙이면 .env.production의 값만 그 서버 도메인으로 바꾸면 됩니다.

2. api/client.js로 apiFetch 공용화

08편에서 api/books.js 안에 직접 두었던 apiFetch를 별도 모듈로 옮기고, 하드코딩된 URL을 환경 변수로 바꿉니다.

// src/api/client.js import { getToken, clearToken } from './auth.js' const API_BASE = import.meta.env.VITE_API_BASE_URL export async function apiFetch(path, options = {}) { const token = getToken() if (!token || token === 'expired-token') { clearToken() throw new Error('SESSION_EXPIRED') } const headers = { 'Content-Type': 'application/json', ...options.headers } const response = await fetch(`${API_BASE}${path}`, { ...options, headers }) if (!response.ok) { throw new Error(`요청이 실패했습니다 (상태 코드: ${response.status})`) } return response }
// src/api/books.js (발췌 — 함수 본문은 04~12편 코드 유지, import만 변경) import { apiFetch } from './client.js' export async function getBooks(params) { const response = await apiFetch(`/books?${new URLSearchParams(params)}`) return response.json() }

auth.jslogin도 같은 API_BASE를 쓰도록 import.meta.env.VITE_API_BASE_URL로 바꿉니다. 세션 만료 검사가 필요 없는 login 요청은 apiFetch를 거치지 않고 지금처럼 fetch를 직접 씁니다.

3. 프로덕션 빌드와 로컬 리허설

npm run build npm run preview

npm run preview가 알려주는 주소(기본 http://localhost:4173)에 접속하기 전에, 다른 터미널에서 npx json-server db.json --port 3001을 함께 켭니다. 개발 서버에서는 안 보이던 문제(정적 자산 경로 등)가 여기서 드러납니다.

4. Vercel에 배포하기

vercel.json으로 SPA 리라이트를 선언합니다.

// vercel.json { "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] }
  1. Vercel 대시보드에서 New Projectbookshelf 저장소 선택.
  2. Framework Preset이 Vite로 자동 인식되는지 확인(빌드 명령 npm run build, 출력 폴더 dist).
  3. Environment Variables에 VITE_API_BASE_URL을 등록.
  4. Deploy 클릭 → 완료 후 https://bookshelf-xxxx.vercel.app 형태의 URL 발급.
npx vercel --prod

이후 main 브랜치에 push할 때마다 자동으로 재배포되고, 다른 브랜치는 미리보기 URL을 별도로 받습니다.

5. GitHub Pages 대안 배포

npm install --save-dev gh-pages cross-env

GitHub Pages는 저장소 이름이 경로에 포함되므로(https://사용자명.github.io/bookshelf/) base 경로를 지정해야 합니다.

// vite.config.js import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], base: process.env.DEPLOY_TARGET === 'gh-pages' ? '/bookshelf/' : '/', })
// package.json (scripts 발췌) { "scripts": { "build": "vite build", "build:gh-pages": "cross-env DEPLOY_TARGET=gh-pages vite build", "deploy:gh-pages": "npm run build:gh-pages && gh-pages -d dist" } }
npm run deploy:gh-pages

gh-pages 브랜치에 dist/ 내용을 올리고, 저장소 설정의 Settings → Pages에서 그 브랜치를 배포 소스로 지정하면 https://사용자명.github.io/bookshelf/로 접속됩니다. GitHub Pages는 SPA 리라이트를 기본 지원하지 않으므로, public/404.htmlindex.html과 같은 내용을 복사해 두는 방식이 흔한 우회책입니다. 이 과목은 Vercel을 기본으로 삼으므로 GitHub Pages는 대안 절차로만 다룹니다.

직접 해보기

Vercel과 GitHub Pages에 배포된 두 URL 모두에서 /books/3처럼 하위 경로로 직접 접속해 새로고침해 보고, 결과를 비교해 보세요.

Vercel은 vercel.json의 리라이트 덕에 정상적으로 페이지가 나옵니다(로그인 화면으로 리다이렉트되더라도 404는 아닙니다). GitHub Pages는 public/404.html 우회책을 추가하지 않았다면 404가 뜨는 것을 확인할 수 있습니다.

자주 하는 실수

증상원인고치는 법
배포 후 화면이 하얗게 뜨고 콘솔에 자산 404GitHub Pages인데 base 경로를 안 맞춤vite.config.jsbase를 저장소 이름과 일치시킴
환경 변수를 바꿨는데 배포에 반영 안 됨값 변경 후 재배포 안 함값은 다음 빌드부터 적용, 재배포까지 한 세트
배포된 사이트에서 로그인이 항상 실패로컬에서 json-server를 안 띄움VITE_API_BASE_URL이 가리키는 서버를 직접 실행

확인 문제

문제 14지선다
Vite에서 VITE_ 접두사가 붙지 않은 환경 변수는 어떻게 됩니까
문제 24지선다
배포된 SPA에서 /books/3을 새로고침하면 404가 나는 근본 원인은 무엇입니까
문제 34지선다
이 과목의 배포된 데모에서 로그인·목록 조회 같은 기능을 확인하려면 무엇이 추가로 필요합니까
문제 44지선다
GitHub Pages 배포 시 vite.config.js에서 base 경로를 지정해야 하는 이유는 무엇입니까

참고 자료

Last updated on