https 접속 허용
This commit is contained in:
+5
-6
@@ -2,12 +2,9 @@
|
||||
FROM node:20-alpine AS builder
|
||||
WORKDIR /app
|
||||
|
||||
# 의존성 레이어 캐시 (package*.json 변경 시만 재설치)
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
# 소스 복사 후 프로덕션 빌드
|
||||
# Docker 환경에서 /api 는 nginx 가 backend:8080 으로 프록시하므로 그대로 사용
|
||||
COPY . .
|
||||
ARG VITE_API_BASE_URL=/api
|
||||
ARG VITE_APP_VERSION=
|
||||
@@ -18,11 +15,13 @@ RUN npm run build
|
||||
# ── 2단계: 서비스 ─────────────────────────────────────────────────────────────
|
||||
FROM nginx:1.27-alpine AS production
|
||||
|
||||
# 빌드 결과물 복사
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
# SPA 라우팅 + 정적 파일 캐시 설정
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY nginx-locations.inc /etc/nginx/gc-locations.inc
|
||||
COPY nginx-https.conf /etc/nginx/nginx-https.conf
|
||||
COPY docker-entrypoint.d/40-enable-https.sh /docker-entrypoint.d/40-enable-https.sh
|
||||
RUN chmod +x /docker-entrypoint.d/40-enable-https.sh
|
||||
|
||||
EXPOSE 80
|
||||
EXPOSE 80 443
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
HTTPS_CONF="/etc/nginx/conf.d/https.conf"
|
||||
HTTPS_TEMPLATE="/etc/nginx/nginx-https.conf"
|
||||
|
||||
if [ -f /etc/nginx/ssl/fullchain.pem ] && [ -f /etc/nginx/ssl/privkey.pem ]; then
|
||||
cp "$HTTPS_TEMPLATE" "$HTTPS_CONF"
|
||||
echo "[gc-frontend] HTTPS enabled — listening on 443"
|
||||
else
|
||||
rm -f "$HTTPS_CONF" 2>/dev/null || true
|
||||
echo "[gc-frontend] HTTPS skipped — place fullchain.pem + privkey.pem in /etc/nginx/ssl"
|
||||
fi
|
||||
@@ -0,0 +1,22 @@
|
||||
# SSL 인증서가 /etc/nginx/ssl/ 에 있을 때 entrypoint 가 conf.d 에 복사한다.
|
||||
server {
|
||||
listen 443 ssl;
|
||||
http2 on;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 1d;
|
||||
|
||||
client_max_body_size 32m;
|
||||
|
||||
resolver 127.0.0.11 8.8.8.8 8.8.4.4 valid=30s;
|
||||
resolver_timeout 5s;
|
||||
|
||||
include /etc/nginx/gc-locations.inc;
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
# Let's Encrypt HTTP-01 (certbot webroot)
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
allow all;
|
||||
}
|
||||
|
||||
# ── STOMP / SockJS (Spring context-path /api → /api/ws/trading) ───
|
||||
location /api/ws/ {
|
||||
proxy_pass http://backend:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_connect_timeout 7d;
|
||||
proxy_send_timeout 7d;
|
||||
proxy_read_timeout 7d;
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
gzip off;
|
||||
chunked_transfer_encoding on;
|
||||
}
|
||||
|
||||
# ── GoldenChart Spring Boot Backend 프록시 ────────────────────────
|
||||
location /api/ {
|
||||
proxy_pass http://backend:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_connect_timeout 10s;
|
||||
proxy_read_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_buffering off;
|
||||
}
|
||||
|
||||
# ── Upbit REST API 역방향 프록시 ──────────────────────────────────────
|
||||
location /upbit-api/ {
|
||||
proxy_pass https://api.upbit.com/;
|
||||
proxy_ssl_server_name on;
|
||||
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
||||
proxy_set_header Host api.upbit.com;
|
||||
proxy_set_header Accept application/json;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_hide_header Access-Control-Allow-Origin;
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
|
||||
# ── Upbit WebSocket 역방향 프록시 ─────────────────────────────────────
|
||||
location /upbit-ws {
|
||||
proxy_pass https://api.upbit.com/websocket/v1;
|
||||
proxy_http_version 1.1;
|
||||
proxy_ssl_server_name on;
|
||||
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
||||
proxy_ssl_verify off;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_set_header Host api.upbit.com;
|
||||
proxy_set_header Origin "https://upbit.com";
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Authorization "";
|
||||
proxy_read_timeout 3600s;
|
||||
proxy_send_timeout 3600s;
|
||||
proxy_buffering off;
|
||||
}
|
||||
|
||||
# ── STOMP / SockJS WebSocket 프록시 (구버전 /ws/**) ─────────────────
|
||||
location /ws/ {
|
||||
proxy_pass http://backend:8080/api/ws/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_connect_timeout 7d;
|
||||
proxy_send_timeout 7d;
|
||||
proxy_read_timeout 7d;
|
||||
proxy_buffering off;
|
||||
}
|
||||
|
||||
# ── 모바일 APK 다운로드 (대용량) ─────────────────────────────────────
|
||||
location /api/mobile-app/download/ {
|
||||
proxy_pass http://backend:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_buffering off;
|
||||
client_max_body_size 200m;
|
||||
}
|
||||
|
||||
# ── React SPA 폴백 ────────────────────────────────────────────────────
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location ~* \.(js|css|woff2?|ttf|eot|svg|ico|png|jpg|jpeg|gif|webp)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location ~* \.html$ {
|
||||
expires -1;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||
}
|
||||
|
||||
# GoldenChart Desktop updater manifest + bundles
|
||||
location /desktop/updates/ {
|
||||
alias /usr/share/nginx/html/desktop/updates/;
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
location = /desktop/updates/latest.json {
|
||||
alias /usr/share/nginx/html/desktop/updates/latest.json;
|
||||
default_type application/json;
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css text/javascript application/javascript
|
||||
application/json image/svg+xml;
|
||||
+1
-143
@@ -10,152 +10,10 @@ server {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# multipart 이미지 업로드 (Spring max-request-size 30MB와 맞춤)
|
||||
client_max_body_size 32m;
|
||||
|
||||
# 외부 호스트명 DNS 조회용 resolver (Upbit + Backend 프록시에 필요)
|
||||
resolver 127.0.0.11 8.8.8.8 8.8.4.4 valid=30s;
|
||||
resolver_timeout 5s;
|
||||
|
||||
# ── STOMP / SockJS (Spring context-path /api → /api/ws/trading) ───
|
||||
# SockJS 가 /api/ws/trading/{id}/{session}/websocket 으로 업그레이드하므로
|
||||
# 일반 REST 프록시와 분리해 Upgrade 헤더·장시간 타임아웃을 반드시 설정한다.
|
||||
location /api/ws/ {
|
||||
proxy_pass http://backend:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_connect_timeout 7d;
|
||||
proxy_send_timeout 7d;
|
||||
proxy_read_timeout 7d;
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
gzip off;
|
||||
chunked_transfer_encoding on;
|
||||
}
|
||||
|
||||
# ── GoldenChart Spring Boot Backend 프록시 ────────────────────────
|
||||
# 브라우저 → /api/... → http://backend:8080/api/...
|
||||
location /api/ {
|
||||
proxy_pass http://backend:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_connect_timeout 10s;
|
||||
proxy_read_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_buffering off;
|
||||
}
|
||||
|
||||
# ── Upbit REST API 역방향 프록시 ──────────────────────────────────────
|
||||
# 브라우저 → /upbit-api/v1/... → https://api.upbit.com/v1/...
|
||||
# CORS 없이 동일 오리진 요청으로 처리됨 (dev: Vite proxy, prod: nginx proxy)
|
||||
location /upbit-api/ {
|
||||
proxy_pass https://api.upbit.com/;
|
||||
proxy_ssl_server_name on;
|
||||
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
||||
proxy_set_header Host api.upbit.com;
|
||||
proxy_set_header Accept application/json;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_hide_header Access-Control-Allow-Origin;
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
|
||||
# ── Upbit WebSocket 역방향 프록시 ─────────────────────────────────────
|
||||
# 브라우저 → ws://HOST/upbit-ws → wss://api.upbit.com/websocket/v1
|
||||
location /upbit-ws {
|
||||
proxy_pass https://api.upbit.com/websocket/v1;
|
||||
proxy_http_version 1.1;
|
||||
proxy_ssl_server_name on;
|
||||
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
||||
proxy_ssl_verify off;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_set_header Host api.upbit.com;
|
||||
proxy_set_header Origin "https://upbit.com";
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
# 쿠키/Authorization 헤더 제거 (업비트 WebSocket 인증 불필요)
|
||||
proxy_set_header Authorization "";
|
||||
proxy_read_timeout 3600s;
|
||||
proxy_send_timeout 3600s;
|
||||
proxy_buffering off;
|
||||
}
|
||||
|
||||
# ── STOMP / SockJS WebSocket 프록시 ──────────────────────────────────
|
||||
# Spring context-path /api → 실제 엔드포인트는 /api/ws/trading
|
||||
# (구버전 클라이언트 /ws/** 요청도 /api/ws/** 로 전달)
|
||||
location /ws/ {
|
||||
proxy_pass http://backend:8080/api/ws/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_connect_timeout 7d;
|
||||
proxy_send_timeout 7d;
|
||||
proxy_read_timeout 7d;
|
||||
proxy_buffering off;
|
||||
}
|
||||
|
||||
# ── 모바일 APK 다운로드 (대용량) ─────────────────────────────────────
|
||||
location /api/mobile-app/download/ {
|
||||
proxy_pass http://backend:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_buffering off;
|
||||
client_max_body_size 200m;
|
||||
}
|
||||
|
||||
# ── React SPA 폴백 ────────────────────────────────────────────────────
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# JS/CSS/이미지 등 정적 자산 — 1년 장기 캐시 (Vite 콘텐츠 해시 포함 파일명)
|
||||
location ~* \.(js|css|woff2?|ttf|eot|svg|ico|png|jpg|jpeg|gif|webp)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# HTML 은 캐시 안 함 (항상 최신 index.html 로드)
|
||||
location ~* \.html$ {
|
||||
expires -1;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||
}
|
||||
|
||||
# GoldenChart Desktop updater manifest + bundles
|
||||
location /desktop/updates/ {
|
||||
alias /usr/share/nginx/html/desktop/updates/;
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
location = /desktop/updates/latest.json {
|
||||
alias /usr/share/nginx/html/desktop/updates/latest.json;
|
||||
default_type application/json;
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
|
||||
# gzip 압축
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css text/javascript application/javascript
|
||||
application/json image/svg+xml;
|
||||
include /etc/nginx/gc-locations.inc;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user