# Tasteby 환경 관리 가이드 ## 환경 구성 개요 ``` ┌─────────────────────────────────────────────────────────────┐ │ 개발 (Local) │ │ │ │ backend/.env ← 백엔드 환경변수 (DB, OCI, Google) │ │ frontend/.env.local ← 프론트엔드 환경변수 (NEXT_PUBLIC_*) │ │ ~/.oci/config ← OCI API 인증 (로컬 경로) │ │ Oracle Wallet 디렉토리 ← DB mTLS 인증 (로컬 경로) │ │ │ │ PM2 → start.sh → source backend/.env → gradlew bootRun │ │ PM2 → npm run dev (Next.js, .env.local 자동 로드) │ │ Redis → 192.168.0.147:6379 (로컬 네트워크) │ └─────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────┐ │ 운영 (OKE) │ │ │ │ k8s/configmap.yaml ← 비밀이 아닌 설정 (Redis host 등) │ │ k8s/secrets.yaml ← 민감 정보 (DB, JWT, API keys) │ │ Secret: oracle-wallet ← DB mTLS 인증 (Volume 마운트) │ │ Secret: oci-config ← OCI API 인증 (Volume 마운트) │ │ │ │ Pod → envFrom: ConfigMap + Secret → application.yml 참조 │ │ Redis → redis:6379 (K8s Service DNS, 클러스터 내부) │ └─────────────────────────────────────────────────────────────┘ ``` ## 환경변수 전체 목록 ### 백엔드 (application.yml에서 참조) | 변수명 | 설명 | 민감 | 개발 | 운영 | |--------|------|:----:|------|------| | `ORACLE_USER` | DB 사용자명 | ✅ | backend/.env | Secret | | `ORACLE_PASSWORD` | DB 비밀번호 | ✅ | backend/.env | Secret | | `ORACLE_DSN` | TNS 연결 문자열 | ✅ | backend/.env | Secret | | `ORACLE_WALLET` | Wallet 디렉토리 경로 | | backend/.env | ConfigMap → `/etc/oracle/wallet` | | `JWT_SECRET` | JWT 서명 키 | ✅ | 기본값 사용 | Secret | | `OCI_COMPARTMENT_ID` | OCI 컴파트먼트 ID | ✅ | backend/.env | Secret | | `OCI_CHAT_ENDPOINT` | GenAI Chat 엔드포인트 | | backend/.env | ConfigMap | | `OCI_GENAI_ENDPOINT` | GenAI Embed 엔드포인트 | | backend/.env | ConfigMap | | `OCI_CHAT_MODEL_ID` | Chat 모델 ID | ✅ | backend/.env | Secret | | `OCI_EMBED_MODEL_ID` | Embed 모델 ID | | backend/.env | ConfigMap | | `GOOGLE_MAPS_API_KEY` | Google Maps API 키 | ✅ | backend/.env | Secret | | `YOUTUBE_DATA_API_KEY` | YouTube Data API 키 | ✅ | backend/.env | Secret | | `GOOGLE_CLIENT_ID` | Google OAuth 클라이언트 ID | | 기본값 사용 | ConfigMap | | `REDIS_HOST` | Redis 호스트 | | 기본값 `192.168.0.147` | ConfigMap → `redis` | | `REDIS_PORT` | Redis 포트 | | 기본값 `6379` | ConfigMap | | `REDIS_DB` | Redis DB 번호 | | 기본값 `0` | ConfigMap | ### 프론트엔드 (빌드 시 주입) | 변수명 | 설명 | 개발 | 운영 | |--------|------|------|------| | `NEXT_PUBLIC_API_URL` | API 기본 URL | `.env.local` → 빈값 (상대경로) | Docker build arg → 빈값 | | `NEXT_PUBLIC_GOOGLE_MAPS_API_KEY` | Maps API 키 | `.env.local` | Docker build arg | | `NEXT_PUBLIC_GOOGLE_CLIENT_ID` | OAuth 클라이언트 ID | `.env.local` | Docker build arg | > **참고**: `NEXT_PUBLIC_*` 변수는 Next.js 빌드 시 코드에 인라인되므로, > 런타임이 아닌 Docker 빌드 시점에 주입해야 합니다. ## 개발 vs 운영 차이점 ### Redis 연결 ``` 개발: REDIS_HOST=192.168.0.147 (로컬 네트워크 IP, 기본값) 운영: REDIS_HOST=redis (K8s Service DNS, ConfigMap) ``` K8s에서 Service 이름 `redis`는 클러스터 내부 DNS로 자동 해석됩니다. Pod IP가 변경되어도 Service가 항상 올바른 Pod를 가리킵니다. ``` redis (Service, ClusterIP) └─ selector: app=redis └─ redis Pod (IP 자동 할당, 변경되어도 Service가 추적) ``` ### OCI 인증 ``` 개발: ~/.oci/config → 로컬 PEM 파일 경로 운영: /root/.oci/config → Secret Volume 마운트 /root/.oci/oci_api_key.pem → Secret Volume 마운트 ``` 운영용 OCI config 파일은 `key_file` 경로를 컨테이너 내부로 변경해야 합니다: ```ini [DEFAULT] user=ocid1.user.oc1..xxx fingerprint=xx:xx:xx key_file=/root/.oci/oci_api_key.pem ← 컨테이너 내부 경로 tenancy=ocid1.tenancy.oc1..xxx region=ap-seoul-1 ``` ### Oracle DB 연결 ``` 개발: ORACLE_WALLET=/Users/joungmin/.../Wallet_xxx (로컬 경로) ORACLE_DSN=tasteby_high?TNS_ADMIN=/Users/joungmin/.../Wallet_xxx 운영: ORACLE_WALLET=/etc/oracle/wallet (Secret Volume 마운트 경로) ORACLE_DSN=tasteby_high?TNS_ADMIN=/etc/oracle/wallet ``` ## 환경별 설정 파일 매핑 ``` ┌──────────────────┬──────────────────────┬──────────────────────────┐ │ 설정 항목 │ 개발 (Local) │ 운영 (OKE) │ ├──────────────────┼──────────────────────┼──────────────────────────┤ │ 백엔드 환경변수 │ backend/.env │ k8s/configmap.yaml │ │ │ (start.sh에서 source) │ k8s/secrets.yaml │ ├──────────────────┼──────────────────────┼──────────────────────────┤ │ 프론트엔드 환경변수│ frontend/.env.local │ Dockerfile build args │ │ │ (Next.js 자동 로드) │ (deploy.sh에서 주입) │ ├──────────────────┼──────────────────────┼──────────────────────────┤ │ OCI 인증 │ ~/.oci/config │ Secret: oci-config │ │ │ (로컬 PEM 경로) │ (Volume → /root/.oci/) │ ├──────────────────┼──────────────────────┼──────────────────────────┤ │ Oracle Wallet │ 로컬 Wallet 디렉토리 │ Secret: oracle-wallet │ │ │ │ (Volume → /etc/oracle/ │ │ │ │ wallet/) │ ├──────────────────┼──────────────────────┼──────────────────────────┤ │ Redis │ 192.168.0.147:6379 │ redis:6379 │ │ │ (로컬 네트워크) │ (K8s Service DNS) │ ├──────────────────┼──────────────────────┼──────────────────────────┤ │ 웹서버/SSL │ nginx + Let's Encrypt │ Ingress + cert-manager │ │ │ (로컬 certbot) │ (자동 발급/갱신) │ └──────────────────┴──────────────────────┴──────────────────────────┘ ``` ## 시크릿 관리 원칙 1. **secrets.yaml은 git에 올리지 않음** — `.gitignore`에 포함 2. **secrets.yaml.template만 git에 올림** — 키 이름 + 구조만 공유 3. **운영 시크릿 변경 시**: ```bash # secrets.yaml 수정 후 kubectl apply -f k8s/secrets.yaml kubectl rollout restart deployment/backend -n tasteby ``` 4. **파일 기반 시크릿(Wallet, OCI config)은 kubectl로 직접 생성**: ```bash kubectl create secret generic oracle-wallet --from-file=... -n tasteby kubectl create secret generic oci-config --from-file=... -n tasteby ``` ## application.yml의 환경변수 바인딩 Spring Boot의 `application.yml`이 환경변수를 자동으로 읽습니다: ```yaml spring: datasource: url: jdbc:oracle:thin:@${ORACLE_DSN} # ← Secret에서 주입 data: redis: host: ${REDIS_HOST:192.168.0.147} # ← ConfigMap에서 주입, 개발은 기본값 app: oci: compartment-id: ${OCI_COMPARTMENT_ID} # ← Secret에서 주입 ``` K8s에서는 `envFrom`으로 ConfigMap과 Secret의 모든 키-값이 환경변수로 들어갑니다: ```yaml # backend-deployment.yaml envFrom: - configMapRef: name: tasteby-config # REDIS_HOST, REDIS_PORT, ... - secretRef: name: tasteby-secrets # ORACLE_USER, ORACLE_PASSWORD, ... ``` 개발 환경에서는 동일한 변수명이 `backend/.env`에서 `source`로 로드됩니다. **변수명이 동일하므로 코드 변경 없이 환경만 바꿀 수 있습니다.**