이미지 로딩 중...
AI Generated
2025. 11. 19. · 2 Views
개발 서버 심화 설정 완벽 가이드
Vite 개발 서버의 고급 설정을 마스터하여 더 효율적이고 안전한 개발 환경을 구축하는 방법을 배웁니다. 포트 설정부터 HTTPS, Proxy, CORS 해결, 환경 변수 관리, 미들웨어 커스터마이징까지 실무에서 바로 활용할 수 있는 모든 것을 다룹니다.
목차
1. 포트_및_호스트_설정
시작하며
여러분이 팀 프로젝트를 진행하는데 같은 포트 3000을 사용하는 여러 프로젝트를 동시에 실행해야 하는 상황을 겪어본 적 있나요? 또는 모바일 기기에서 개발 중인 웹사이트를 테스트하고 싶은데 localhost로는 접근이 안 되는 경우가 있죠.
이런 문제는 실제 개발 현장에서 정말 자주 발생합니다. 기본 포트가 이미 사용 중이면 서버가 시작되지 않거나, 네트워크상의 다른 기기에서 접근이 필요한데 호스트 설정이 잘못되어 있으면 테스트가 불가능합니다.
바로 이럴 때 필요한 것이 포트와 호스트 설정입니다. Vite에서는 이러한 설정을 매우 간단하게 조정할 수 있어서, 여러분의 개발 환경을 자유자재로 구성할 수 있습니다.
개요
간단히 말해서, 포트(Port)는 컴퓨터에서 서버가 사용하는 '문 번호'이고, 호스트(Host)는 '주소'를 의미합니다. 포트 설정이 필요한 이유는 한 컴퓨터에서 여러 개의 서버를 동시에 실행할 때 각각 다른 문을 사용해야 하기 때문입니다.
예를 들어, 프론트엔드는 5173번 포트, 백엔드는 3000번 포트를 사용하는 식이죠. 호스트 설정은 누가 이 서버에 접근할 수 있는지를 결정합니다.
기존에는 서버 실행 시마다 포트 충돌 에러를 보고 수동으로 변경했다면, 이제는 설정 파일에서 한 번만 지정하면 됩니다. 핵심 특징은 첫째, 원하는 포트 번호를 자유롭게 지정할 수 있고, 둘째, 네트워크상의 다른 기기(스마트폰, 태블릿)에서도 접근 가능하게 설정할 수 있으며, 셋째, 포트가 이미 사용 중일 때 자동으로 다른 포트를 찾는 옵션도 제공합니다.
이러한 특징들이 팀 협업과 다양한 기기 테스트를 훨씬 수월하게 만들어줍니다.
코드 예제
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
server: {
// 원하는 포트 번호 지정
port: 3000,
// 포트가 사용 중이면 자동으로 다음 포트 사용
strictPort: false,
// 네트워크상의 모든 기기에서 접근 가능
host: '0.0.0.0',
// 서버 시작 시 자동으로 브라우저 열기
open: true
}
})
설명
이것이 하는 일: 위 설정은 Vite 개발 서버가 어떤 포트 번호에서, 어떤 네트워크 범위로 서비스될지를 결정합니다. 첫 번째로, port: 3000은 개발 서버를 3000번 포트에서 실행하도록 지정합니다.
기본값인 5173 대신 여러분이 선호하는 포트를 사용할 수 있죠. 만약 백엔드 개발자와 협업한다면, 프론트엔드는 3000번, 백엔드는 8080번처럼 명확하게 구분하면 혼란을 줄일 수 있습니다.
두 번째로, strictPort: false는 포트 충돌 시 자동으로 다음 사용 가능한 포트를 찾아줍니다. 만약 이 값을 true로 설정하면, 지정한 포트가 이미 사용 중일 때 서버 시작이 실패하고 에러가 발생합니다.
개발 단계에서는 false로 두는 것이 편리합니다. 세 번째로, host: '0.0.0.0'이 가장 중요한 부분입니다.
기본값인 'localhost'는 자신의 컴퓨터에서만 접근 가능하지만, '0.0.0.0'으로 설정하면 같은 Wi-Fi 네트워크에 연결된 스마트폰이나 태블릿에서도 접근할 수 있습니다. 예를 들어, 여러분의 컴퓨터 IP가 192.168.0.10이라면, 스마트폰 브라우저에서 http://192.168.0.10:3000으로 접속하여 실시간으로 테스트할 수 있습니다.
마지막으로, open: true는 서버가 시작되면 자동으로 기본 브라우저를 열어주는 편의 기능입니다. 매번 서버 실행 후 수동으로 브라우저를 열고 주소를 입력할 필요가 없어집니다.
여러분이 이 설정을 사용하면 포트 충돌 걱정 없이 여러 프로젝트를 동시에 실행할 수 있고, 모바일 반응형 디자인을 실제 기기에서 즉시 확인할 수 있으며, 같은 네트워크의 동료에게 작업 중인 화면을 바로 공유할 수 있습니다.
실전 팁
💡 모바일 테스트를 자주 한다면 host를 '0.0.0.0'으로 설정하고, 터미널에 표시되는 Network 주소를 QR 코드로 만들어두면 스마트폰에서 빠르게 접속할 수 있습니다.
💡 여러 프로젝트를 동시에 실행할 때는 각 프로젝트마다 고유한 포트를 설정 파일에 미리 지정해두세요. 예를 들어 메인 프로젝트는 3000, 관리자 페이지는 3001, 랜딩 페이지는 3002처럼 규칙을 정하면 혼동이 없습니다.
💡 production 환경과 동일한 포트를 사용하고 싶다면 strictPort를 true로 설정하여 포트 충돌을 강제로 체크하세요. 이렇게 하면 배포 전 포트 관련 문제를 미리 발견할 수 있습니다.
💡 방화벽이나 회사 네트워크 정책 때문에 특정 포트만 열려있다면, 해당 포트 번호를 사용하도록 설정하면 네트워크 관리자에게 별도 요청할 필요가 없습니다.
💡 개발 서버 주소를 팀원들과 공유할 때는 터미널에 표시되는 'Local'이 아닌 'Network' 주소를 알려주세요. Local 주소(localhost)는 자신의 컴퓨터에서만 작동합니다.
2. HTTPS_개발_서버_구성
시작하며
여러분이 카메라나 마이크 접근 기능을 개발하는데, HTTP 환경에서는 브라우저가 보안상의 이유로 접근을 차단하는 경험을 해보셨나요? 또는 실제 배포 환경은 HTTPS인데 개발 환경은 HTTP라서 쿠키나 보안 관련 기능이 다르게 동작하는 경우도 있죠.
이런 문제는 현대 웹 개발에서 점점 더 빈번해지고 있습니다. 브라우저들이 보안을 강화하면서 많은 웹 API들이 HTTPS 환경에서만 작동하도록 제한되고 있고, Service Worker, Geolocation, getUserMedia 같은 기능들이 대표적인 예입니다.
바로 이럴 때 필요한 것이 HTTPS 개발 서버 구성입니다. Vite에서는 간단한 설정만으로 로컬 개발 환경에서도 HTTPS를 사용할 수 있어서, 실제 배포 환경과 동일한 조건에서 테스트할 수 있습니다.
개요
간단히 말해서, HTTPS는 HTTP에 보안 계층을 추가한 프로토콜로, 데이터를 암호화하여 전송하는 방식입니다. HTTPS 개발 서버가 필요한 이유는 점점 더 많은 브라우저 API들이 보안상의 이유로 HTTPS 환경에서만 동작하기 때문입니다.
예를 들어, 화상 통화 앱을 개발한다면 카메라와 마이크 접근이 필수인데, HTTP 환경에서는 브라우저가 이를 차단합니다. 또한 PWA(Progressive Web App) 개발 시 Service Worker는 HTTPS에서만 작동합니다.
기존에는 복잡한 인증서 생성과 서버 설정을 직접 해야 했다면, 이제는 Vite가 자동으로 자체 서명 인증서를 생성하고 HTTPS 서버를 구성해줍니다. 핵심 특징은 첫째, 한 줄의 설정만으로 HTTPS를 활성화할 수 있고, 둘째, 자동으로 생성된 인증서를 사용하거나 직접 인증서를 지정할 수 있으며, 셋째, 실제 배포 환경과 동일한 보안 조건에서 개발할 수 있다는 점입니다.
이러한 특징들이 보안 관련 기능 개발과 테스트를 훨씬 수월하게 만들어줍니다.
코드 예제
// vite.config.js
import { defineConfig } from 'vite'
import basicSsl from '@vitejs/plugin-basic-ssl'
export default defineConfig({
// 방법 1: 플러그인 사용 (가장 간단)
plugins: [basicSsl()],
// 방법 2: 직접 설정
server: {
https: true,
// 또는 인증서 직접 지정
// https: {
// key: fs.readFileSync('path/to/key.pem'),
// cert: fs.readFileSync('path/to/cert.pem')
// }
}
})
설명
이것이 하는 일: 위 설정은 개발 서버를 HTTP 대신 HTTPS 프로토콜로 실행하여, 실제 배포 환경과 동일한 보안 조건을 만들어줍니다. 첫 번째 방법인 @vitejs/plugin-basic-ssl 플러그인은 가장 간단하고 권장되는 방법입니다.
먼저 pnpm add -D @vitejs/plugin-basic-ssl 명령으로 플러그인을 설치하고, plugins 배열에 basicSsl()만 추가하면 됩니다. 이 플러그인은 자동으로 자체 서명 인증서를 생성하고 HTTPS 서버를 구성해줍니다.
브라우저에서 처음 접속하면 "안전하지 않은 사이트" 경고가 나타날 수 있는데, 이는 자체 서명 인증서이기 때문에 정상이며 "계속 진행" 버튼을 클릭하면 됩니다. 두 번째 방법은 server.https: true로 직접 설정하는 방식입니다.
이 방법도 Vite가 자동으로 인증서를 생성해주지만, 플러그인 방식보다는 설정 옵션이 제한적입니다. 기본적인 HTTPS만 필요하다면 이 방법으로도 충분합니다.
세 번째 방법은 주석 처리된 부분처럼 실제 인증서 파일을 직접 지정하는 방식입니다. 회사에서 발급받은 인증서나 Let's Encrypt 같은 무료 인증서를 사용하고 싶을 때 유용합니다.
fs.readFileSync()로 key와 cert 파일을 읽어서 전달하면 됩니다. 이 경우 브라우저 경고 없이 "안전한 사이트"로 표시됩니다.
HTTPS 개발 서버가 실행되면 주소가 http://localhost:3000이 아닌 https://localhost:3000으로 변경됩니다. 이제 navigator.mediaDevices.getUserMedia(), Geolocation API, Service Worker 등 HTTPS가 필요한 모든 기능을 로컬 환경에서도 테스트할 수 있습니다.
여러분이 이 설정을 사용하면 카메라/마이크 기능을 로컬에서 바로 테스트할 수 있고, PWA 개발 시 Service Worker를 제대로 동작시킬 수 있으며, 쿠키의 Secure 속성이나 CORS 정책을 실제 배포 환경과 동일하게 테스트할 수 있습니다.
실전 팁
💡 Chrome에서 자체 서명 인증서 경고를 매번 보기 싫다면, chrome://flags/#allow-insecure-localhost를 활성화하면 localhost에 대한 HTTPS 경고가 사라집니다.
💡 모바일 기기에서 HTTPS 개발 서버를 테스트할 때는 실제 인증서를 사용하거나, mkcert 같은 도구로 로컬 CA를 만들어 기기에 설치하면 경고 없이 테스트할 수 있습니다.
💡 Service Worker를 개발한다면 반드시 HTTPS 환경이 필요하므로, 프로젝트 초반부터 HTTPS를 활성화하는 것이 좋습니다. 나중에 전환하면 기존 코드에서 예상치 못한 문제가 발생할 수 있습니다.
💡 팀원들과 공유할 때는 자체 서명 인증서 경고가 나타날 수 있다는 점을 미리 알려주고, "고급" → "계속 진행" 버튼을 클릭하는 방법을 공유하세요.
💡 실제 배포 환경의 인증서를 개발에 사용하는 것은 보안상 위험하므로, 개발용과 배포용 인증서를 반드시 분리하여 관리하세요.
3. Proxy_설정으로_API_연동
시작하며
여러분이 프론트엔드는 localhost:3000에서 개발하는데, 백엔드 API는 localhost:8080에서 실행 중인 상황을 생각해보세요. API를 호출하면 "CORS 정책에 의해 차단되었습니다"라는 에러를 마주하게 되죠.
이런 문제는 프론트엔드와 백엔드를 분리해서 개발하는 거의 모든 프로젝트에서 발생합니다. 브라우저는 보안상의 이유로 다른 출처(포트, 도메인, 프로토콜이 다른 경우)로의 요청을 기본적으로 차단합니다.
백엔드에서 CORS 설정을 추가할 수도 있지만, 개발 단계에서는 번거롭고 실수하기 쉽습니다. 바로 이럴 때 필요한 것이 Proxy 설정입니다.
Vite의 프록시 기능을 사용하면 마치 같은 서버에서 응답하는 것처럼 API 요청을 전달할 수 있어서, CORS 문제를 근본적으로 해결할 수 있습니다.
개요
간단히 말해서, 프록시(Proxy)는 '중간 대리인' 역할을 하는 서버입니다. 여러분의 브라우저가 직접 백엔드에 요청하지 않고, Vite 개발 서버를 거쳐서 백엔드로 요청을 전달하는 방식이죠.
프록시 설정이 필요한 이유는 개발 환경에서 프론트엔드와 백엔드가 다른 포트나 서버에서 실행되기 때문입니다. 예를 들어, /api/users 요청을 할 때 브라우저는 localhost:3000/api/users로 요청하지만, Vite 프록시가 이를 가로채서 localhost:8080/api/users로 전달합니다.
브라우저 입장에서는 같은 서버에 요청한 것이므로 CORS 에러가 발생하지 않습니다. 기존에는 백엔드에서 CORS 헤더를 설정하거나, 별도의 프록시 서버를 구축해야 했다면, 이제는 Vite 설정 파일에 몇 줄만 추가하면 됩니다.
핵심 특징은 첫째, 특정 경로 패턴을 감지하여 자동으로 다른 서버로 요청을 전달하고, 둘째, 요청 경로를 변환(rewrite)하여 백엔드 API 구조에 맞게 조정할 수 있으며, 셋째, 쿠키나 인증 헤더도 함께 전달할 수 있다는 점입니다. 이러한 특징들이 실제 배포 환경과 유사한 개발 환경을 만들어줍니다.
코드 예제
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
server: {
proxy: {
// /api로 시작하는 요청을 백엔드로 전달
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
// 경로에서 /api 제거하기: /api/users → /users
rewrite: (path) => path.replace(/^\/api/, '')
},
// WebSocket 프록시 (채팅, 실시간 알림 등)
'/socket.io': {
target: 'ws://localhost:8080',
ws: true
}
}
}
})
설명
이것이 하는 일: 위 설정은 특정 경로 패턴의 요청을 감지하여, Vite 개발 서버가 중간에서 가로채 백엔드 서버로 전달하는 프록시 역할을 수행합니다. 첫 번째로, '/api' 키는 프록시할 경로 패턴을 지정합니다.
브라우저에서 /api/users, /api/products 같은 요청을 하면 이 규칙에 매칭됩니다. target: 'http://localhost:8080'은 실제 백엔드 서버의 주소를 의미하며, 매칭된 요청은 모두 이 주소로 전달됩니다.
두 번째로, changeOrigin: true는 매우 중요한 옵션입니다. 이 옵션은 요청의 Origin 헤더를 타겟 서버에 맞게 변경합니다.
백엔드에서 가상 호스팅을 사용하거나 Origin을 체크하는 경우 반드시 true로 설정해야 합니다. 대부분의 경우 true로 설정하는 것이 안전합니다.
세 번째로, rewrite 함수는 경로를 변환합니다. 예를 들어, 브라우저에서 /api/users로 요청하면, 실제로는 백엔드에 /users로 전달됩니다.
만약 백엔드 API가 /api 접두사 없이 설계되어 있다면 이 변환이 필수입니다. 정규표현식 /^\/api/는 경로 시작 부분의 /api를 찾아서 빈 문자열로 교체합니다.
네 번째로, WebSocket 프록시 설정도 포함되어 있습니다. 실시간 채팅이나 알림 기능처럼 WebSocket을 사용하는 경우, ws: true 옵션을 추가하고 target을 ws:// 프로토콜로 지정하면 WebSocket 연결도 프록시할 수 있습니다.
여러분이 이 설정을 사용하면 프론트엔드 코드에서 fetch('/api/users')처럼 간단하게 API를 호출할 수 있고, CORS 에러 걱정 없이 백엔드와 통신할 수 있으며, 배포 시에도 같은 코드를 사용할 수 있어 환경별 코드 변경이 필요 없습니다.
실전 팁
💡 여러 백엔드 서버와 통신해야 한다면 프록시 규칙을 여러 개 추가하세요. 예를 들어 /api는 메인 서버로, /auth는 인증 서버로, /upload는 파일 서버로 각각 다른 target을 지정할 수 있습니다.
💡 백엔드가 쿠키 기반 인증을 사용한다면 secure: false와 cookieDomainRewrite 옵션을 추가하여 개발 환경에서 쿠키가 제대로 전달되도록 설정하세요.
💡 프록시가 제대로 작동하는지 확인하려면 브라우저 개발자 도구의 Network 탭에서 요청 URL과 응답을 확인하세요. 프록시된 요청은 여전히 /api/users로 표시되지만, 실제로는 백엔드에서 응답이 옵니다.
💡 외부 API(예: 다른 회사의 API 서버)를 프록시할 때는 target을 전체 도메인으로 지정하고, changeOrigin: true를 반드시 설정하세요. 예: target: 'https://api.example.com'
💡 프록시 설정을 변경했다면 반드시 개발 서버를 재시작해야 합니다. vite.config.js 파일 변경은 hot reload로 반영되지 않습니다.
4. CORS_문제_해결_방법
시작하며
여러분이 API를 호출했는데 콘솔에 빨간 글씨로 "Access to fetch at '...' from origin '...' has been blocked by CORS policy"라는 에러 메시지가 나타난 경험이 있으신가요? 분명 백엔드는 정상 작동하는데 프론트엔드에서만 막히는 답답한 상황이죠.
이런 문제는 웹 개발을 시작하는 초보자부터 경험 많은 개발자까지 모두가 겪는 가장 흔한 문제 중 하나입니다. CORS(Cross-Origin Resource Sharing)는 브라우저의 보안 메커니즘으로, 악의적인 웹사이트가 사용자의 정보를 몰래 가져가는 것을 막기 위해 존재합니다.
하지만 정상적인 개발 과정에서도 자주 걸림돌이 됩니다. 바로 이럴 때 필요한 것이 CORS 문제 해결 방법입니다.
Vite에서는 프록시 설정과 CORS 헤더 설정을 통해 이 문제를 깔끔하게 해결할 수 있으며, 개발 환경과 배포 환경에 맞는 적절한 전략을 선택할 수 있습니다.
개요
간단히 말해서, CORS는 브라우저가 보안을 위해 다른 출처(도메인, 포트, 프로토콜)의 리소스 접근을 제한하는 정책입니다. CORS 문제가 발생하는 이유를 이해하려면 '출처(Origin)'의 개념을 알아야 합니다.
http://localhost:3000과 http://localhost:8080은 포트가 다르므로 서로 다른 출처입니다. 브라우저는 기본적으로 스크립트가 다른 출처의 리소스에 접근하는 것을 차단합니다.
예를 들어, 프론트엔드가 3000번 포트에서 실행 중인데 8080번 포트의 API를 호출하면 CORS 에러가 발생합니다. 기존에는 백엔드 코드를 수정하여 Access-Control-Allow-Origin 헤더를 추가하거나, 브라우저 확장 프로그램으로 CORS를 우회했다면, 이제는 Vite의 개발 서버 설정으로 깔끔하게 해결할 수 있습니다.
핵심 해결 방법은 세 가지입니다. 첫째, 프록시를 사용하여 모든 요청이 같은 출처에서 오는 것처럼 만들기, 둘째, 개발 서버에 CORS 헤더를 직접 설정하기, 셋째, 백엔드와 협업하여 적절한 CORS 정책을 구성하기입니다.
각 방법마다 장단점이 있으므로 상황에 맞게 선택해야 합니다.
코드 예제
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
server: {
// 방법 1: 프록시로 CORS 우회 (가장 권장)
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
secure: false // HTTPS 인증서 검증 생략
}
},
// 방법 2: CORS 헤더 직접 설정
cors: true, // 모든 출처 허용
// 또는 세밀한 제어
// cors: {
// origin: ['http://localhost:8080', 'https://example.com'],
// credentials: true // 쿠키 포함
// }
}
})
설명
이것이 하는 일: 위 설정은 개발 서버에서 발생하는 CORS 문제를 해결하여, 프론트엔드가 백엔드 API와 원활하게 통신할 수 있도록 합니다. 첫 번째 방법인 프록시는 가장 권장되는 해결책입니다.
위에서 설명한 프록시 설정을 사용하면, 브라우저는 Vite 개발 서버(같은 출처)에 요청하고, Vite가 이를 백엔드로 전달합니다. 브라우저 입장에서는 모든 요청이 같은 출처이므로 CORS 문제가 발생하지 않습니다.
secure: false 옵션은 백엔드가 자체 서명 HTTPS 인증서를 사용할 때 검증을 생략하도록 합니다. 두 번째 방법인 cors: true는 Vite 개발 서버 자체가 CORS 헤더를 응답에 추가합니다.
이 방법은 Vite가 직접 제공하는 정적 파일에는 유효하지만, 백엔드 API 요청에는 효과가 없습니다. 따라서 주로 첫 번째 프록시 방법과 함께 사용됩니다.
세밀한 CORS 제어가 필요하다면 객체 형태로 설정할 수 있습니다. origin 배열에는 허용할 출처들을 명시하고, credentials: true는 쿠키나 인증 헤더를 포함한 요청을 허용합니다.
예를 들어, JWT 토큰을 쿠키로 관리한다면 이 옵션이 필수입니다. 실무에서 가장 좋은 전략은 개발 환경에서는 프록시로 CORS를 우회하고, 배포 환경에서는 백엔드에서 적절한 CORS 헤더를 설정하는 것입니다.
이렇게 하면 개발은 편리하면서도 보안은 유지할 수 있습니다. 여러분이 이 설정을 사용하면 더 이상 CORS 에러로 개발이 막히지 않고, 쿠키 기반 인증을 사용하는 API도 문제없이 테스트할 수 있으며, 백엔드 개발자와의 협업이 훨씬 수월해집니다.
실전 팁
💡 CORS 에러가 발생하면 먼저 브라우저 콘솔의 정확한 에러 메시지를 확인하세요. "preflight request" 관련 에러라면 백엔드가 OPTIONS 메서드를 처리하지 못하는 것이고, "credentials" 관련 에러라면 쿠키 설정 문제입니다.
💡 Chrome 확장 프로그램으로 CORS를 우회하는 방법은 절대 사용하지 마세요. 이는 임시방편일 뿐이고, 다른 팀원이나 실제 사용자 환경에서는 작동하지 않습니다.
💡 배포 환경에서는 프록시를 사용할 수 없으므로, 배포 전에 반드시 백엔드에서 적절한 CORS 헤더를 설정해야 합니다. Nginx나 Express 같은 서버에서 설정하는 방법을 미리 확인하세요.
💡 외부 API(예: Google Maps API)를 사용할 때 CORS 에러가 발생한다면, 해당 API 제공자의 문서를 확인하세요. 많은 API들이 특정 도메인만 허용하거나 API 키 설정이 필요합니다.
💡 개발 중에 자주 사용하는 백엔드 주소는 환경 변수로 관리하면 편리합니다. .env.development 파일에 VITE_API_URL=http://localhost:8080처럼 저장하고 프록시 target에서 사용하세요.
5. 환경_변수_관리
시작하며
여러분이 API 키나 백엔드 서버 주소를 코드에 직접 적어넣었다가, 실수로 GitHub에 올려서 보안 경고를 받은 경험이 있으신가요? 또는 개발 환경과 배포 환경의 설정이 달라서, 배포할 때마다 코드를 수정해야 하는 번거로움을 겪어보셨나요?
이런 문제는 거의 모든 프로젝트에서 발생하는 매우 흔한 문제입니다. 민감한 정보를 코드에 직접 넣으면 보안 위험이 있고, 환경별로 다른 설정값을 하드코딩하면 유지보수가 어려워집니다.
특히 팀 프로젝트에서는 각 개발자마다 다른 설정을 사용해야 할 때도 있죠. 바로 이럴 때 필요한 것이 환경 변수 관리입니다.
Vite는 .env 파일을 사용하여 환경별 설정을 쉽게 관리할 수 있고, 보안이 필요한 정보는 코드에서 분리하여 안전하게 보관할 수 있습니다.
개요
간단히 말해서, 환경 변수는 코드 바깥에 저장되는 설정값입니다. 마치 옷장에 옷을 넣어두듯이, 변경 가능한 설정값들을 별도의 파일에 보관하는 방식이죠.
환경 변수가 필요한 이유는 크게 세 가지입니다. 첫째, API 키나 비밀번호 같은 민감한 정보를 코드에서 분리하여 보안을 강화할 수 있습니다.
둘째, 개발/스테이징/프로덕션 환경마다 다른 설정값(예: 서버 주소)을 쉽게 관리할 수 있습니다. 셋째, 팀원마다 다른 로컬 설정을 사용하면서도 같은 코드를 공유할 수 있습니다.
기존에는 설정값을 코드에 직접 작성하고 환경별로 주석 처리하거나, 복잡한 빌드 스크립트를 사용했다면, 이제는 .env 파일에 KEY=VALUE 형식으로 저장하고 코드에서 import.meta.env.KEY로 간단하게 접근할 수 있습니다. 핵심 특징은 첫째, VITE_ 접두사가 붙은 변수만 클라이언트 코드에 노출되어 보안을 지킬 수 있고, 둘째, .env.local, .env.development, .env.production 같은 파일로 환경별 설정을 분리할 수 있으며, 셋째, TypeScript 타입 지원으로 자동완성과 타입 체크를 받을 수 있다는 점입니다.
이러한 특징들이 안전하고 편리한 설정 관리를 가능하게 합니다.
코드 예제
# .env.development (개발 환경)
VITE_API_URL=http://localhost:8080
VITE_API_KEY=dev-api-key-12345
VITE_ENABLE_MOCK=true
# .env.production (배포 환경)
VITE_API_URL=https://api.example.com
VITE_API_KEY=prod-api-key-67890
VITE_ENABLE_MOCK=false
# .env.local (개인 로컬 설정 - Git 제외)
VITE_DEBUG=true
// src/config.js
export const config = {
apiUrl: import.meta.env.VITE_API_URL,
apiKey: import.meta.env.VITE_API_KEY,
enableMock: import.meta.env.VITE_ENABLE_MOCK === 'true',
isDev: import.meta.env.DEV, // Vite 내장 변수
isProd: import.meta.env.PROD // Vite 내장 변수
}
설명
이것이 하는 일: 위 설정은 환경별로 다른 설정값을 별도 파일에 저장하고, 코드에서 안전하게 불러와 사용할 수 있도록 합니다. 첫 번째로, .env.development 파일은 pnpm dev 명령으로 개발 서버를 실행할 때 자동으로 로드됩니다.
여기에는 로컬 백엔드 주소(http://localhost:8080)와 개발용 API 키, 목 데이터 활성화 플래그 등을 저장합니다. VITE_ 접두사는 필수입니다.
이 접두사가 없는 변수는 보안상의 이유로 클라이언트 코드에서 접근할 수 없습니다. 두 번째로, .env.production 파일은 pnpm build 명령으로 배포 빌드를 할 때 로드됩니다.
여기에는 실제 배포 서버 주소(https://api.example.com)와 프로덕션 API 키를 저장합니다. 같은 코드이지만 빌드 모드에 따라 자동으로 다른 환경 변수가 적용되는 것이죠.
세 번째로, .env.local 파일은 모든 환경에서 로드되지만 Git에는 커밋하지 않습니다. .gitignore에 *.local을 추가하여 개인적인 설정이나 민감한 정보를 저장합니다.
팀원마다 다른 데이터베이스를 사용하거나, 디버그 모드를 개별적으로 설정할 때 유용합니다. 네 번째로, JavaScript 코드에서는 import.meta.env.VITE_API_URL 형식으로 환경 변수에 접근합니다.
모든 환경 변수는 문자열로 전달되므로, boolean 값이 필요하면 === 'true'로 비교해야 합니다. Vite는 DEV, PROD, MODE 같은 내장 변수도 제공하여 현재 환경을 쉽게 확인할 수 있습니다.
우선순위는 .env.local > .env.[mode].local > .env.[mode] > .env 순서입니다. 같은 변수가 여러 파일에 있으면 우선순위가 높은 파일의 값이 사용됩니다.
여러분이 이 설정을 사용하면 코드를 수정하지 않고도 환경별로 다른 설정을 적용할 수 있고, API 키를 안전하게 관리할 수 있으며, 팀원과 코드를 공유하면서도 각자의 로컬 설정을 유지할 수 있습니다.
실전 팁
💡 절대로 .env.local 파일을 Git에 커밋하지 마세요. 대신 .env.example 파일을 만들어서 필요한 변수 목록을 공유하고, 각 개발자가 복사하여 .env.local로 사용하도록 안내하세요.
💡 TypeScript 자동완성을 받고 싶다면 src/vite-env.d.ts 파일에 환경 변수 타입을 정의하세요: interface ImportMetaEnv { readonly VITE_API_URL: string }
💡 환경 변수는 빌드 타임에 문자열로 치환됩니다. 따라서 import.meta.env.VITE_API_URL은 빌드 후 실제 값으로 변경되어, 런타임 성능에 영향을 주지 않습니다.
💡 민감한 정보는 절대 VITE_ 접두사를 붙이지 마세요. 클라이언트 코드에 노출되면 브라우저에서 누구나 볼 수 있습니다. 서버 사이드에서만 사용할 정보는 접두사 없이 저장하세요.
💡 환경 변수를 추가하거나 수정했다면 반드시 개발 서버를 재시작해야 합니다. 핫 리로드로는 환경 변수 변경이 반영되지 않습니다.
6. 개발_서버_미들웨어_커스터마이징
시작하며
여러분이 개발 중에 모든 API 요청을 로깅하고 싶거나, 특정 조건에서 목(Mock) 데이터를 반환하고 싶거나, 인증 토큰을 자동으로 추가하고 싶은 상황을 생각해보세요. Vite의 기본 기능만으로는 이런 세밀한 제어가 어렵습니다.
이런 문제는 복잡한 프로젝트나 특별한 개발 환경이 필요한 경우 자주 발생합니다. 백엔드가 아직 준비되지 않았을 때 프론트엔드를 먼저 개발해야 하거나, 특정 에러 상황을 시뮬레이션하고 싶거나, 요청/응답을 가로채서 디버깅 정보를 추가해야 할 때가 있죠.
바로 이럴 때 필요한 것이 개발 서버 미들웨어 커스터마이징입니다. Vite는 내부적으로 Connect(Express와 유사한 프레임워크)를 사용하므로, 여러분만의 미들웨어를 추가하여 개발 서버의 동작을 자유롭게 확장할 수 있습니다.
개요
간단히 말해서, 미들웨어는 요청과 응답 사이에서 작동하는 '중간 처리 함수'입니다. 마치 택배가 여러분에게 도착하기 전에 거치는 분류 센터처럼, HTTP 요청이 최종 목적지에 도달하기 전에 거치는 중간 단계입니다.
미들웨어 커스터마이징이 필요한 이유는 개발 환경에서 특별한 로직을 추가해야 하는 경우가 많기 때문입니다. 예를 들어, 백엔드 API가 완성되기 전에 가짜 데이터를 반환하거나, 모든 요청에 인증 헤더를 자동으로 추가하거나, 느린 네트워크 환경을 시뮬레이션하기 위해 지연을 추가하는 등의 작업을 할 수 있습니다.
기존에는 별도의 Mock 서버를 구축하거나, 브라우저 확장 프로그램을 사용하거나, 코드에 조건문을 잔뜩 추가했다면, 이제는 Vite 설정에 미들웨어를 추가하여 깔끔하게 처리할 수 있습니다. 핵심 특징은 첫째, configureServer 훅을 사용하여 개발 서버 시작 시 미들웨어를 등록하고, 둘째, 요청 경로나 메서드에 따라 다른 로직을 적용할 수 있으며, 셋째, Express 스타일의 미들웨어 패턴을 그대로 사용할 수 있다는 점입니다.
이러한 특징들이 개발 환경을 원하는 대로 커스터마이징할 수 있게 해줍니다.
코드 예제
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
server: {
// 미들웨어 추가
async configureServer(server) {
// 모든 요청 로깅 미들웨어
server.middlewares.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`)
next()
})
// Mock API 미들웨어
server.middlewares.use('/api/mock', (req, res) => {
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({
success: true,
data: { id: 1, name: 'Mock User' },
message: 'Mock data from middleware'
}))
})
// 느린 네트워크 시뮬레이션 (2초 지연)
server.middlewares.use('/api/slow', async (req, res, next) => {
await new Promise(resolve => setTimeout(resolve, 2000))
next()
})
}
}
})
설명
이것이 하는 일: 위 설정은 Vite 개발 서버에 커스텀 미들웨어를 추가하여, HTTP 요청이 처리되는 과정에 여러분만의 로직을 삽입합니다. 첫 번째로, configureServer 함수는 개발 서버가 시작될 때 자동으로 호출됩니다.
이 함수 안에서 server.middlewares.use()를 사용하여 미들웨어를 등록합니다. 미들웨어는 등록된 순서대로 실행되므로, 순서가 중요합니다.
두 번째로, 로깅 미들웨어는 모든 요청에 대해 실행됩니다. (req, res, next) 패턴은 Express 미들웨어와 동일합니다.
req는 요청 객체, res는 응답 객체, next는 다음 미들웨어로 넘어가는 함수입니다. 콘솔에 요청 정보를 출력한 후 next()를 호출하여 다음 미들웨어로 진행합니다.
만약 next()를 호출하지 않으면 요청 처리가 멈춰버리므로 주의하세요. 세 번째로, Mock API 미들웨어는 /api/mock 경로에 대해서만 작동합니다.
res.setHeader()로 응답 헤더를 설정하고, res.end()로 JSON 데이터를 반환합니다. 이 미들웨어는 next()를 호출하지 않고 직접 응답을 종료하므로, 백엔드로 요청이 전달되지 않습니다.
백엔드가 준비되지 않았을 때 프론트엔드 개발을 먼저 진행할 수 있게 해주는 강력한 도구입니다. 네 번째로, 느린 네트워크 시뮬레이션 미들웨어는 /api/slow로 시작하는 요청에 2초 지연을 추가합니다.
async/await와 setTimeout을 사용하여 비동기 지연을 구현하고, next()를 호출하여 실제 API 요청이 계속 진행되도록 합니다. 로딩 스피너나 타임아웃 처리를 테스트할 때 매우 유용합니다.
실무에서는 이러한 미들웨어들을 조합하여 인증 토큰 자동 추가, 에러 응답 시뮬레이션, API 버전별 라우팅 등 다양한 개발 도구를 만들 수 있습니다. 여러분이 이 설정을 사용하면 백엔드 없이도 프론트엔드 개발을 진행할 수 있고, 다양한 네트워크 상황과 에러 케이스를 쉽게 테스트할 수 있으며, 개발 중 디버깅에 필요한 정보를 자동으로 수집할 수 있습니다.
실전 팁
💡 Mock 데이터가 많아지면 별도의 파일로 분리하세요. mocks/users.json 같은 파일을 만들고 미들웨어에서 fs.readFileSync()로 읽어 반환하면 관리가 편리합니다.
💡 미들웨어 순서가 중요합니다. 로깅 미들웨어는 가장 먼저, 에러 처리 미들웨어는 가장 나중에 등록하세요. 특정 경로 미들웨어는 일반 미들웨어보다 먼저 등록해야 우선 처리됩니다.
💡 프로덕션 빌드에는 미들웨어가 포함되지 않으므로, 개발 환경에서만 필요한 로직을 자유롭게 추가해도 됩니다. 단, 코드에서 Mock API를 호출하는 부분은 환경 변수로 제어하세요.
💡 복잡한 Mock 서버가 필요하다면 MSW(Mock Service Worker) 라이브러리를 사용하는 것도 좋은 방법입니다. Vite 미들웨어보다 더 강력하고 테스트에도 재사용할 수 있습니다.
💡 에러 응답을 시뮬레이션할 때는 실제 백엔드가 반환하는 에러 형식과 동일하게 맞추세요. 상태 코드(404, 500 등)와 에러 메시지 구조를 일치시켜야 프론트엔드 에러 핸들링을 제대로 테스트할 수 있습니다.