refactor: all

This commit is contained in:
OhSeongRak 2025-06-18 13:48:10 +09:00
parent ca1e7dbdf0
commit e6f2c3a810
6 changed files with 266 additions and 50 deletions

View File

@ -4,7 +4,15 @@ server {
root /usr/share/nginx/html;
index index.html index.htm;
# Gzip compression
# 에러 페이지 설정
error_page 404 /index.html;
error_page 500 502 503 504 /50x.html;
# 로깅 설정
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
# Gzip compression 최적화
gzip on;
gzip_vary on;
gzip_min_length 1024;
@ -17,30 +25,118 @@ server {
text/javascript
application/javascript
application/xml+rss
application/json;
application/json
application/xml
image/svg+xml;
# Handle client routing (Vue Router)
location / {
try_files $uri $uri/ /index.html;
}
# Brotli compression (더 좋은 압축률)
# brotli on;
# brotli_comp_level 6;
# brotli_types text/xml image/svg+xml application/x-font-ttf image/vnd.microsoft.icon application/x-font-opentype application/json font/eot application/vnd.ms-fontobject application/javascript font/otf application/xml application/xhtml+xml text/javascript application/x-javascript text/plain application/x-font-truetype application/xml+rss image/x-icon font/opentype text/css image/x-win-bitmap;
# Security headers
# 보안 헤더 강화
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' http://smarketing.20.249.184.228.nip.io https://smarketing.20.249.184.228.nip.io" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
# CORS 설정 (필요시)
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization" always;
# SPA 라우팅 처리 (Vue Router)
location / {
try_files $uri $uri/ /index.html;
# HTML 파일은 캐시하지 않음 (런타임 설정 반영 위해)
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
}
}
# Health check endpoint
# 런타임 환경 설정 파일 - 캐시하지 않음
location /runtime-env.js {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
}
# 정적 자산 캐시 최적화
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
# 정적 파일에 대한 로깅 최소화
access_log off;
}
# manifest.json과 service worker 캐시 설정
location ~* \.(json|webmanifest)$ {
expires 1d;
add_header Cache-Control "public";
}
location /sw.js {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# 파비콘 캐시
location /favicon.ico {
expires 1M;
access_log off;
log_not_found off;
}
# 헬스체크 엔드포인트
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
# API 프록시 (필요시 백엔드로 직접 프록시)
# location /api/ {
# proxy_pass http://smarketing.20.249.184.228.nip.io/api/;
# 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 30s;
# proxy_send_timeout 30s;
# proxy_read_timeout 30s;
# }
# 숨겨진 파일 접근 차단
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# 불필요한 파일 접근 차단
location ~* \.(log|txt|md|yml|yaml|conf)$ {
deny all;
access_log off;
log_not_found off;
}
# 압축된 자산 우선 제공
location ~* \.(js|css)$ {
gzip_static on;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# 추가 서버 블록 (HTTPS 리다이렉트, 필요시)
# server {
# listen 8080;
# server_name smarketing.20.249.184.228.nip.io;
# return 301 https://$server_name$request_uri;
# }

View File

@ -140,7 +140,7 @@ metadata:
labels:
app: smarketing-frontend
spec:
type: LoadBalancer
type: ClusterIP
ports:
- port: 80
targetPort: ${export_port}
@ -157,6 +157,7 @@ metadata:
namespace: ${namespace}
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- host: ${ingress_host}

View File

@ -14,14 +14,14 @@ export_port=18080
# Gateway/Ingress 설정 (⭐ smarketing-backend와 동일한 IP 사용)
ingress_host=smarketing.20.249.184.228.nip.io
# 리소스 설정
resources_requests_cpu=256m
resources_requests_memory=256Mi
resources_limits_cpu=1024m
resources_limits_memory=1024Mi
# 리소스 설정 (프론트엔드에 맞게 조정)
resources_requests_cpu=128m # 프론트엔드는 CPU 사용량이 적음
resources_requests_memory=128Mi # 메모리도 적게 사용
resources_limits_cpu=512m # 제한도 낮게 설정
resources_limits_memory=512Mi
# API URLs (⭐ smarketing-backend ingress를 통해 라우팅)
# 현재 설정된 백엔드 API들과 일치
# 백엔드 서비스별 API 경로
auth_url=http://smarketing.20.249.184.228.nip.io/api/auth
member_url=http://smarketing.20.249.184.228.nip.io/api/member
store_url=http://smarketing.20.249.184.228.nip.io/api/store
@ -30,6 +30,39 @@ sales_url=http://smarketing.20.249.184.228.nip.io/api/sales
content_url=http://smarketing.20.249.184.228.nip.io/api/content
recommend_url=http://smarketing.20.249.184.228.nip.io/api/recommend
# Frontend 이미지 경로 설정
smarketing_frontend_image_path=${registry}/${image_org}/smarketing-frontend:latest
# GitHub 설정
github_org=won-ktds
teamid=smarketing
teamid=smarketing
# 환경 플래그
environment=production
debug_mode=false
# SSL/TLS 설정 (필요시)
ssl_enabled=false
ssl_redirect=false
# 로깅 레벨
log_level=info
# 헬스체크 설정
health_check_path=/health
health_check_interval=30s
health_check_timeout=5s
health_check_retries=3
# 보안 설정
security_headers_enabled=true
cors_enabled=true
allowed_origins=*
# 캐시 설정
static_cache_enabled=true
static_cache_duration=1y
# 압축 설정
gzip_enabled=true
gzip_compression_level=6

View File

@ -5,6 +5,7 @@ metadata:
namespace: ${namespace}
labels:
app: smarketing-frontend
version: v1
spec:
replicas: ${replicas}
selector:
@ -14,6 +15,7 @@ spec:
metadata:
labels:
app: smarketing-frontend
version: v1
spec:
imagePullSecrets:
- name: acr-secret
@ -23,6 +25,7 @@ spec:
imagePullPolicy: Always
ports:
- containerPort: ${export_port}
name: http
resources:
requests:
cpu: ${resources_requests_cpu}
@ -34,19 +37,53 @@ spec:
- name: runtime-config
mountPath: /usr/share/nginx/html/runtime-env.js
subPath: runtime-env.js
readOnly: true
env:
- name: NGINX_PORT
value: "${export_port}"
livenessProbe:
httpGet:
path: /health
port: ${export_port}
scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 1
readinessProbe:
httpGet:
path: /health
port: ${export_port}
scheme: HTTP
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
successThreshold: 1
# 시작 프로브 추가 (컨테이너 시작 시간이 오래 걸릴 수 있음)
startupProbe:
httpGet:
path: /health
port: ${export_port}
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 30
successThreshold: 1
volumes:
- name: runtime-config
configMap:
name: smarketing-frontend-config
name: smarketing-frontend-config
defaultMode: 0644
# 배포 전략 설정
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
# 재시작 정책
restartPolicy: Always
# DNS 정책
dnsPolicy: ClusterFirst

View File

@ -8,14 +8,14 @@ data:
console.log('=== RUNTIME-ENV.JS 로드됨 (배포 환경) ===');
window.__runtime_config__ = {
// 백엔드 API 구조에 맞게 URL 설정
// 백엔드 API 구조에 맞게 URL 설정 - ingress host 사용
AUTH_URL: 'http://${ingress_host}/api/auth',
MEMBER_URL: 'http://${ingress_host}/api/member',
STORE_URL: 'http://${ingress_host}/api/store',
MENU_URL: 'http://${ingress_host}/api/menu',
SALES_URL: 'http://${ingress_host}/api/sales',
CONTENT_URL: 'http://${ingress_host}/api/content',
RECOMMEND_URL: 'http://${ingress_host}/api/recommendations',
RECOMMEND_URL: 'http://${ingress_host}/api/recommend',
// Gateway URL (운영 환경)
GATEWAY_URL: 'http://${ingress_host}',

View File

@ -1,18 +1,62 @@
//* public/runtime-env.js - 백엔드 API 경로에 맞게 수
//* public/runtime-env.js - 배포 환경 우선 설
console.log('=== RUNTIME-ENV.JS 로드됨 ===');
// 배포 환경 감지 함수
const isProduction = () => {
// 프로덕션 환경 감지 로직
return window.location.hostname !== 'localhost' &&
window.location.hostname !== '127.0.0.1' &&
!window.location.hostname.includes('dev');
};
// 기본 ingress host 설정 (deploy_env_vars에서 설정된 값)
const DEFAULT_INGRESS_HOST = 'smarketing.20.249.184.228.nip.io';
// 환경별 API URL 설정
const getBaseUrl = () => {
if (isProduction()) {
// 프로덕션: ingress host 사용
return `http://${DEFAULT_INGRESS_HOST}`;
} else {
// 개발환경: localhost 사용
return '';
}
};
const baseUrl = getBaseUrl();
window.__runtime_config__ = {
// ⚠️ 수정: 백엔드 API 구조에 맞게 URL 설정
AUTH_URL: 'http://localhost:8081/api/auth',
MEMBER_URL: 'http://localhost:8081/api/member',
STORE_URL: 'http://localhost:8082/api/store',
MENU_URL: 'http://localhost:8082/api/menu',
SALES_URL: 'http://localhost:8082/api/sales', // store 서비스
CONTENT_URL: 'http://localhost:8083/api/content',
RECOMMEND_URL: 'http://localhost:8084/api/recommendations', // ⚠️ 수정: 올바른 경로
// 프로덕션 환경에서는 ingress host 사용, 개발환경에서는 localhost
AUTH_URL: isProduction() ?
`${baseUrl}/api/auth` :
'http://localhost:8081/api/auth',
MEMBER_URL: isProduction() ?
`${baseUrl}/api/member` :
'http://localhost:8081/api/member',
STORE_URL: isProduction() ?
`${baseUrl}/api/store` :
'http://localhost:8082/api/store',
MENU_URL: isProduction() ?
`${baseUrl}/api/menu` :
'http://localhost:8082/api/menu',
SALES_URL: isProduction() ?
`${baseUrl}/api/sales` :
'http://localhost:8082/api/sales',
CONTENT_URL: isProduction() ?
`${baseUrl}/api/content` :
'http://localhost:8083/api/content',
RECOMMEND_URL: isProduction() ?
`${baseUrl}/api/recommend` :
'http://localhost:8084/api/recommendations',
// Gateway URL (운영 환경용)
GATEWAY_URL: 'http://20.1.2.3',
// Gateway URL
GATEWAY_URL: isProduction() ? baseUrl : 'http://20.1.2.3',
// 기능 플래그
FEATURES: {
@ -20,17 +64,17 @@ window.__runtime_config__ = {
PUSH_NOTIFICATIONS: true,
SOCIAL_LOGIN: false,
MULTI_LANGUAGE: false,
API_HEALTH_CHECK: true, // ⚠️ 추가
API_HEALTH_CHECK: true,
},
// 환경 설정
ENV: 'development',
DEBUG: true,
ENV: isProduction() ? 'production' : 'development',
DEBUG: !isProduction(),
// ⚠️ 추가: API 타임아웃 설정
// API 타임아웃 설정
API_TIMEOUT: 30000,
// ⚠️ 추가: 재시도 설정
// 재시도 설정
RETRY_ATTEMPTS: 3,
RETRY_DELAY: 1000,
@ -38,7 +82,7 @@ window.__runtime_config__ = {
VERSION: '1.0.0',
BUILD_DATE: new Date().toISOString(),
// ⚠️ 추가: 백엔드 서비스 포트 정보 (디버깅용)
// 백엔드 서비스 포트 정보 (디버깅용)
BACKEND_PORTS: {
AUTH: 8081,
STORE: 8082,
@ -47,7 +91,7 @@ window.__runtime_config__ = {
}
};
// ⚠️ 추가: 설정 검증 함수
// 설정 검증 함수
const validateConfig = () => {
const config = window.__runtime_config__;
const requiredUrls = ['AUTH_URL', 'STORE_URL', 'SALES_URL', 'RECOMMEND_URL'];
@ -63,8 +107,13 @@ const validateConfig = () => {
return true;
};
// ⚠️ 추가: 개발 환경에서만 상세 로깅
// 환경별 상세 로깅
if (window.__runtime_config__.DEBUG) {
console.log('=== 현재 환경 정보 ===');
console.log('🌍 Environment:', window.__runtime_config__.ENV);
console.log('🏠 Hostname:', window.location.hostname);
console.log('🔧 Is Production:', isProduction());
console.log('=== 백엔드 API URLs ===');
console.log('🔐 AUTH_URL:', window.__runtime_config__.AUTH_URL);
console.log('🏪 STORE_URL:', window.__runtime_config__.STORE_URL);
@ -74,12 +123,9 @@ if (window.__runtime_config__.DEBUG) {
console.log('=== 설정 상세 정보 ===');
console.log('전체 설정:', window.__runtime_config__);
// 설정 검증 실행
validateConfig();
}
// ⚠️ 추가: 전역 설정 접근 함수
// 전역 설정 접근 함수
window.getApiConfig = () => window.__runtime_config__;
window.getApiUrl = (serviceName) => {
const config = window.__runtime_config__;
@ -87,4 +133,7 @@ window.getApiUrl = (serviceName) => {
return config[urlKey] || null;
};
console.log('✅ [RUNTIME] 런타임 설정 로드 완료');
// 설정 검증 실행
validateConfig();
console.log(`✅ [RUNTIME] 런타임 설정 로드 완료 (${window.__runtime_config__.ENV} 환경)`);