mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 10:16:24 +00:00
Merge branch 'main' of https://github.com/hwanny1128/HGZero into chore/path
This commit is contained in:
commit
0be39486b4
500
.github/actions-pipeline-guide.md
vendored
Normal file
500
.github/actions-pipeline-guide.md
vendored
Normal file
@ -0,0 +1,500 @@
|
|||||||
|
# GitHub Actions CI/CD 파이프라인 구축 가이드
|
||||||
|
|
||||||
|
## 📋 목차
|
||||||
|
1. [개요](#개요)
|
||||||
|
2. [사전 준비사항](#사전-준비사항)
|
||||||
|
3. [GitHub 저장소 환경 구성](#github-저장소-환경-구성)
|
||||||
|
4. [디렉토리 구조](#디렉토리-구조)
|
||||||
|
5. [Kustomize 구조 설명](#kustomize-구조-설명)
|
||||||
|
6. [GitHub Actions 워크플로우](#github-actions-워크플로우)
|
||||||
|
7. [배포 방법](#배포-방법)
|
||||||
|
8. [롤백 방법](#롤백-방법)
|
||||||
|
9. [SonarQube 설정](#sonarqube-설정)
|
||||||
|
10. [트러블슈팅](#트러블슈팅)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
|
||||||
|
HGZero 프로젝트의 백엔드 서비스를 위한 GitHub Actions 기반 CI/CD 파이프라인입니다.
|
||||||
|
|
||||||
|
### 주요 기능
|
||||||
|
- ✅ Gradle 기반 빌드 및 테스트
|
||||||
|
- ✅ SonarQube 코드 품질 분석 (선택적)
|
||||||
|
- ✅ Azure Container Registry에 Docker 이미지 빌드 및 푸시
|
||||||
|
- ✅ Kustomize를 사용한 환경별(dev/staging/prod) 배포
|
||||||
|
- ✅ AKS 클러스터 자동 배포
|
||||||
|
|
||||||
|
### 지원 서비스
|
||||||
|
- **user**: 사용자 관리 서비스
|
||||||
|
- **meeting**: 회의 관리 서비스
|
||||||
|
- **stt**: 음성 인식 서비스
|
||||||
|
- **ai**: AI 처리 서비스
|
||||||
|
- **notification**: 알림 서비스
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 사전 준비사항
|
||||||
|
|
||||||
|
### 1. 프로젝트 정보
|
||||||
|
- **시스템명**: hgzero
|
||||||
|
- **ACR 이름**: acrdigitalgarage02
|
||||||
|
- **리소스 그룹**: rg-digitalgarage-02
|
||||||
|
- **AKS 클러스터**: aks-digitalgarage-02
|
||||||
|
- **네임스페이스**: hgzero
|
||||||
|
- **JDK 버전**: 21
|
||||||
|
|
||||||
|
### 2. 필수 도구
|
||||||
|
- Git
|
||||||
|
- kubectl
|
||||||
|
- Azure CLI
|
||||||
|
- Kustomize (자동 설치됨)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GitHub 저장소 환경 구성
|
||||||
|
|
||||||
|
### 1. Repository Secrets 설정
|
||||||
|
|
||||||
|
`Repository Settings > Secrets and variables > Actions > Repository secrets`에 다음 항목을 등록하세요:
|
||||||
|
|
||||||
|
#### Azure 인증 정보
|
||||||
|
```json
|
||||||
|
AZURE_CREDENTIALS:
|
||||||
|
{
|
||||||
|
"clientId": "{클라이언트ID}",
|
||||||
|
"clientSecret": "{클라이언트시크릿}",
|
||||||
|
"subscriptionId": "{구독ID}",
|
||||||
|
"tenantId": "{테넌트ID}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**예시:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"clientId": "5e4b5b41-7208-48b7-b821-d6d5acf50ecf",
|
||||||
|
"clientSecret": "ldu8Q~GQEzFYU.dJX7_QsahR7n7C2xqkIM6hqbV8",
|
||||||
|
"subscriptionId": "2513dd36-7978-48e3-9a7c-b221d4874f66",
|
||||||
|
"tenantId": "4f0a3bfd-1156-4cce-8dc2-a049a13dba23"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ACR Credentials
|
||||||
|
|
||||||
|
ACR Credential을 확인하려면:
|
||||||
|
```bash
|
||||||
|
az acr credential show --name acrdigitalgarage02
|
||||||
|
```
|
||||||
|
|
||||||
|
등록할 Secrets:
|
||||||
|
```
|
||||||
|
ACR_USERNAME: acrdigitalgarage02
|
||||||
|
ACR_PASSWORD: {ACR 패스워드}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### SonarQube 설정
|
||||||
|
|
||||||
|
**SONAR_HOST_URL 확인:**
|
||||||
|
```bash
|
||||||
|
kubectl get svc -n sonarqube
|
||||||
|
```
|
||||||
|
출력된 External IP를 사용하여 `http://{External IP}` 형식으로 설정
|
||||||
|
|
||||||
|
**SONAR_TOKEN 생성:**
|
||||||
|
1. SonarQube에 로그인 (기본: admin/admin)
|
||||||
|
2. 우측 상단 'Administrator' > My Account 클릭
|
||||||
|
3. Security 탭 선택 후 토큰 생성
|
||||||
|
|
||||||
|
등록할 Secrets:
|
||||||
|
```
|
||||||
|
SONAR_TOKEN: {SonarQube 토큰}
|
||||||
|
SONAR_HOST_URL: http://{External IP}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Docker Hub (Rate Limit 해결용)
|
||||||
|
|
||||||
|
**패스워드 생성:**
|
||||||
|
1. [Docker Hub](https://hub.docker.com) 로그인
|
||||||
|
2. 우측 상단 프로필 아이콘 > Account Settings
|
||||||
|
3. 좌측 메뉴 'Personal Access Tokens' 클릭하여 생성
|
||||||
|
|
||||||
|
등록할 Secrets:
|
||||||
|
```
|
||||||
|
DOCKERHUB_USERNAME: {Docker Hub 사용자명}
|
||||||
|
DOCKERHUB_PASSWORD: {Docker Hub 패스워드 또는 토큰}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Repository Variables 설정
|
||||||
|
|
||||||
|
`Repository Settings > Secrets and variables > Actions > Variables > Repository variables`에 등록:
|
||||||
|
|
||||||
|
```
|
||||||
|
ENVIRONMENT: dev
|
||||||
|
SKIP_SONARQUBE: true
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 디렉토리 구조
|
||||||
|
|
||||||
|
```
|
||||||
|
.github/
|
||||||
|
├── workflows/
|
||||||
|
│ └── backend-cicd.yaml # GitHub Actions 워크플로우
|
||||||
|
├── kustomize/
|
||||||
|
│ ├── base/ # 기본 Kubernetes 매니페스트
|
||||||
|
│ │ ├── common/ # 공통 리소스
|
||||||
|
│ │ │ ├── cm-common.yaml
|
||||||
|
│ │ │ ├── secret-common.yaml
|
||||||
|
│ │ │ ├── secret-imagepull.yaml
|
||||||
|
│ │ │ └── ingress.yaml
|
||||||
|
│ │ ├── user/ # User 서비스
|
||||||
|
│ │ │ ├── deployment.yaml
|
||||||
|
│ │ │ ├── service.yaml
|
||||||
|
│ │ │ └── secret-user.yaml
|
||||||
|
│ │ ├── meeting/ # Meeting 서비스
|
||||||
|
│ │ ├── stt/ # STT 서비스
|
||||||
|
│ │ ├── ai/ # AI 서비스
|
||||||
|
│ │ ├── notification/ # Notification 서비스
|
||||||
|
│ │ └── kustomization.yaml
|
||||||
|
│ └── overlays/ # 환경별 오버레이
|
||||||
|
│ ├── dev/ # 개발 환경
|
||||||
|
│ │ ├── kustomization.yaml
|
||||||
|
│ │ ├── cm-common-patch.yaml
|
||||||
|
│ │ ├── secret-common-patch.yaml
|
||||||
|
│ │ ├── ingress-patch.yaml
|
||||||
|
│ │ ├── deployment-{service}-patch.yaml
|
||||||
|
│ │ └── secret-{service}-patch.yaml
|
||||||
|
│ ├── staging/ # 스테이징 환경
|
||||||
|
│ └── prod/ # 프로덕션 환경
|
||||||
|
├── config/ # 환경별 설정
|
||||||
|
│ ├── deploy_env_vars_dev
|
||||||
|
│ ├── deploy_env_vars_staging
|
||||||
|
│ └── deploy_env_vars_prod
|
||||||
|
└── scripts/
|
||||||
|
└── deploy-actions.sh # 수동 배포 스크립트
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Kustomize 구조 설명
|
||||||
|
|
||||||
|
### Base 구조
|
||||||
|
|
||||||
|
Base는 모든 환경에서 공통으로 사용되는 기본 매니페스트입니다.
|
||||||
|
|
||||||
|
**주요 리소스:**
|
||||||
|
- **ConfigMap (cm-common)**: 환경 변수, 프로파일 설정
|
||||||
|
- **Secret (secret-common)**: JWT 시크릿, Redis 패스워드
|
||||||
|
- **Ingress**: API 라우팅 규칙
|
||||||
|
- **Deployment**: 각 서비스별 배포 설정
|
||||||
|
- **Service**: 각 서비스별 ClusterIP 서비스
|
||||||
|
- **Secret**: 각 서비스별 데이터베이스 연결 정보
|
||||||
|
|
||||||
|
### Overlay 구조
|
||||||
|
|
||||||
|
각 환경(dev/staging/prod)별로 Base를 오버라이드합니다.
|
||||||
|
|
||||||
|
#### DEV 환경
|
||||||
|
- **Replicas**: 1
|
||||||
|
- **Resources**: CPU 256m-1024m, Memory 256Mi-1024Mi
|
||||||
|
- **Profile**: dev
|
||||||
|
- **DDL**: update
|
||||||
|
- **Log Level**: DEBUG
|
||||||
|
- **Image Tag**: dev-{timestamp}
|
||||||
|
|
||||||
|
#### STAGING 환경
|
||||||
|
- **Replicas**: 2
|
||||||
|
- **Resources**: CPU 512m-2048m, Memory 512Mi-2048Mi
|
||||||
|
- **Profile**: staging
|
||||||
|
- **DDL**: validate
|
||||||
|
- **Log Level**: INFO
|
||||||
|
- **Image Tag**: staging-{timestamp}
|
||||||
|
- **SSL**: Enabled
|
||||||
|
|
||||||
|
#### PROD 환경
|
||||||
|
- **Replicas**: 3
|
||||||
|
- **Resources**: CPU 1024m-4096m, Memory 1024Mi-4096Mi
|
||||||
|
- **Profile**: prod
|
||||||
|
- **DDL**: validate
|
||||||
|
- **Log Level**: WARN
|
||||||
|
- **JWT Expiration**: 짧게 설정 (보안 강화)
|
||||||
|
- **Image Tag**: prod-{timestamp}
|
||||||
|
- **SSL**: Enabled
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GitHub Actions 워크플로우
|
||||||
|
|
||||||
|
### 트리거 조건
|
||||||
|
|
||||||
|
1. **Push 이벤트**:
|
||||||
|
- 브랜치: `main`, `develop`
|
||||||
|
- 경로: 서비스 코드 변경 시 (`user/**`, `meeting/**` 등)
|
||||||
|
|
||||||
|
2. **Pull Request**:
|
||||||
|
- 대상 브랜치: `main`
|
||||||
|
|
||||||
|
3. **수동 실행 (workflow_dispatch)**:
|
||||||
|
- Environment 선택: dev/staging/prod
|
||||||
|
- SonarQube 분석 스킵 선택: true/false
|
||||||
|
|
||||||
|
### 워크플로우 단계
|
||||||
|
|
||||||
|
#### 1. Build Job
|
||||||
|
1. 소스코드 체크아웃
|
||||||
|
2. JDK 21 설정
|
||||||
|
3. 환경 결정 (input 또는 기본값 dev)
|
||||||
|
4. Gradle 빌드 (테스트 제외)
|
||||||
|
5. SonarQube 분석 (선택적)
|
||||||
|
6. 빌드 아티팩트 업로드
|
||||||
|
7. 이미지 태그 생성 (타임스탬프 기반)
|
||||||
|
|
||||||
|
#### 2. Release Job
|
||||||
|
1. 빌드 아티팩트 다운로드
|
||||||
|
2. Docker Buildx 설정
|
||||||
|
3. Docker Hub 로그인 (Rate Limit 방지)
|
||||||
|
4. ACR 로그인
|
||||||
|
5. 각 서비스별 Docker 이미지 빌드 및 푸시
|
||||||
|
|
||||||
|
#### 3. Deploy Job
|
||||||
|
1. Azure CLI 설치 및 로그인
|
||||||
|
2. kubectl 설정
|
||||||
|
3. AKS Credentials 가져오기
|
||||||
|
4. 네임스페이스 생성
|
||||||
|
5. Kustomize 설치
|
||||||
|
6. 이미지 태그 업데이트
|
||||||
|
7. 매니페스트 적용
|
||||||
|
8. Deployment Ready 대기
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 배포 방법
|
||||||
|
|
||||||
|
### 1. 자동 배포 (Push/PR)
|
||||||
|
|
||||||
|
코드를 `main` 또는 `develop` 브랜치에 push하면 자동으로 dev 환경에 배포됩니다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add .
|
||||||
|
git commit -m "feat: 새로운 기능 추가"
|
||||||
|
git push origin develop
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 수동 배포 (GitHub Actions UI)
|
||||||
|
|
||||||
|
1. GitHub Repository > Actions 탭 이동
|
||||||
|
2. "Backend Services CI/CD" 워크플로우 선택
|
||||||
|
3. "Run workflow" 버튼 클릭
|
||||||
|
4. 환경 선택 (dev/staging/prod)
|
||||||
|
5. SonarQube 분석 스킵 여부 선택
|
||||||
|
6. "Run workflow" 실행
|
||||||
|
|
||||||
|
### 3. 로컬에서 수동 배포
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 스크립트 실행 권한 확인
|
||||||
|
chmod +x .github/scripts/deploy-actions.sh
|
||||||
|
|
||||||
|
# DEV 환경에 배포 (기본)
|
||||||
|
./.github/scripts/deploy-actions.sh dev latest
|
||||||
|
|
||||||
|
# STAGING 환경에 특정 이미지 태그로 배포
|
||||||
|
./.github/scripts/deploy-actions.sh staging 20250127120000
|
||||||
|
|
||||||
|
# PROD 환경에 배포
|
||||||
|
./.github/scripts/deploy-actions.sh prod 20250127120000
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 롤백 방법
|
||||||
|
|
||||||
|
### 1. GitHub Actions를 통한 롤백
|
||||||
|
|
||||||
|
1. GitHub > Actions > 성공한 이전 워크플로우 선택
|
||||||
|
2. "Re-run all jobs" 클릭
|
||||||
|
3. 이전 버전으로 재배포됨
|
||||||
|
|
||||||
|
### 2. kubectl을 이용한 롤백
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 특정 Revision으로 롤백
|
||||||
|
kubectl rollout undo deployment/user -n hgzero --to-revision=2
|
||||||
|
|
||||||
|
# 이전 버전으로 롤백
|
||||||
|
kubectl rollout undo deployment/user -n hgzero
|
||||||
|
|
||||||
|
# 롤백 상태 확인
|
||||||
|
kubectl rollout status deployment/user -n hgzero
|
||||||
|
|
||||||
|
# Rollout 히스토리 확인
|
||||||
|
kubectl rollout history deployment/user -n hgzero
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 수동 스크립트를 이용한 롤백
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 이전 안정 버전의 이미지 태그로 배포
|
||||||
|
./.github/scripts/deploy-actions.sh dev 20250126110000
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SonarQube 설정
|
||||||
|
|
||||||
|
### 프로젝트 생성
|
||||||
|
|
||||||
|
각 서비스별로 SonarQube 프로젝트를 생성하세요:
|
||||||
|
- hgzero-user-dev
|
||||||
|
- hgzero-meeting-dev
|
||||||
|
- hgzero-stt-dev
|
||||||
|
- hgzero-ai-dev
|
||||||
|
- hgzero-notification-dev
|
||||||
|
|
||||||
|
### Quality Gate 설정
|
||||||
|
|
||||||
|
기본 Quality Gate 설정:
|
||||||
|
- **Coverage**: >= 80%
|
||||||
|
- **Duplicated Lines**: <= 3%
|
||||||
|
- **Maintainability Rating**: <= A
|
||||||
|
- **Reliability Rating**: <= A
|
||||||
|
- **Security Rating**: <= A
|
||||||
|
|
||||||
|
### Gradle 설정 (이미 구성됨)
|
||||||
|
|
||||||
|
```gradle
|
||||||
|
// build.gradle
|
||||||
|
plugins {
|
||||||
|
id 'org.sonarqube' version '4.0.0.2929'
|
||||||
|
id 'jacoco'
|
||||||
|
}
|
||||||
|
|
||||||
|
sonarqube {
|
||||||
|
properties {
|
||||||
|
property "sonar.projectKey", "hgzero-${project.name}"
|
||||||
|
property "sonar.projectName", "hgzero-${project.name}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jacocoTestReport {
|
||||||
|
reports {
|
||||||
|
xml.enabled true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 트러블슈팅
|
||||||
|
|
||||||
|
### 1. 이미지 Pull 실패
|
||||||
|
|
||||||
|
**증상**: `ImagePullBackOff` 또는 `ErrImagePull`
|
||||||
|
|
||||||
|
**해결 방법**:
|
||||||
|
```bash
|
||||||
|
# ACR 로그인 테스트
|
||||||
|
az acr login --name acrdigitalgarage02
|
||||||
|
|
||||||
|
# Image Pull Secret 재생성
|
||||||
|
kubectl delete secret acr-secret -n hgzero
|
||||||
|
kubectl create secret docker-registry acr-secret \
|
||||||
|
--docker-server=acrdigitalgarage02.azurecr.io \
|
||||||
|
--docker-username=acrdigitalgarage02 \
|
||||||
|
--docker-password={ACR_PASSWORD} \
|
||||||
|
-n hgzero
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Deployment 타임아웃
|
||||||
|
|
||||||
|
**증상**: `deployment "user" exceeded its progress deadline`
|
||||||
|
|
||||||
|
**해결 방법**:
|
||||||
|
```bash
|
||||||
|
# Pod 상태 확인
|
||||||
|
kubectl get pods -n hgzero
|
||||||
|
|
||||||
|
# Pod 로그 확인
|
||||||
|
kubectl logs -n hgzero {pod-name}
|
||||||
|
|
||||||
|
# Events 확인
|
||||||
|
kubectl describe pod -n hgzero {pod-name}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Health Check 실패
|
||||||
|
|
||||||
|
**증상**: Deployment가 Ready 상태로 전환되지 않음
|
||||||
|
|
||||||
|
**해결 방법**:
|
||||||
|
```bash
|
||||||
|
# Actuator health 엔드포인트 확인
|
||||||
|
kubectl exec -n hgzero {pod-name} -- curl http://localhost:8080/actuator/health
|
||||||
|
|
||||||
|
# 애플리케이션 로그 확인
|
||||||
|
kubectl logs -n hgzero {pod-name} -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Kustomize 빌드 오류
|
||||||
|
|
||||||
|
**증상**: `kustomize build` 실패
|
||||||
|
|
||||||
|
**해결 방법**:
|
||||||
|
```bash
|
||||||
|
# 로컬에서 Kustomize 검증
|
||||||
|
kubectl kustomize .github/kustomize/overlays/dev
|
||||||
|
|
||||||
|
# YAML 문법 검증
|
||||||
|
yamllint .github/kustomize/overlays/dev/*.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. SonarQube 연결 실패
|
||||||
|
|
||||||
|
**증상**: SonarQube Analysis 단계에서 연결 오류
|
||||||
|
|
||||||
|
**해결 방법**:
|
||||||
|
1. SONAR_HOST_URL이 올바른지 확인
|
||||||
|
2. SONAR_TOKEN이 유효한지 확인
|
||||||
|
3. SonarQube 서비스 상태 확인:
|
||||||
|
```bash
|
||||||
|
kubectl get pods -n sonarqube
|
||||||
|
kubectl logs -n sonarqube {sonarqube-pod}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. 환경 변수 로드 실패
|
||||||
|
|
||||||
|
**증상**: 환경 설정 파일을 찾을 수 없음
|
||||||
|
|
||||||
|
**해결 방법**:
|
||||||
|
```bash
|
||||||
|
# 파일 존재 확인
|
||||||
|
ls -la .github/config/
|
||||||
|
|
||||||
|
# 파일 내용 확인
|
||||||
|
cat .github/config/deploy_env_vars_dev
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 참고 자료
|
||||||
|
|
||||||
|
- [Kustomize 공식 문서](https://kustomize.io/)
|
||||||
|
- [GitHub Actions 문서](https://docs.github.com/en/actions)
|
||||||
|
- [Azure Container Registry 문서](https://docs.microsoft.com/en-us/azure/container-registry/)
|
||||||
|
- [AKS 문서](https://docs.microsoft.com/en-us/azure/aks/)
|
||||||
|
- [SonarQube 문서](https://docs.sonarqube.org/)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 문의 및 지원
|
||||||
|
|
||||||
|
문제가 발생하거나 질문이 있으시면:
|
||||||
|
1. GitHub Issues에 등록
|
||||||
|
2. DevOps 팀에 문의 (송주영)
|
||||||
|
3. Slack #devops 채널
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**작성일**: 2025-01-27
|
||||||
|
**작성자**: DevOps Team (주영)
|
||||||
|
**버전**: 1.0.0
|
||||||
3
.github/config/deploy_env_vars_dev
vendored
Normal file
3
.github/config/deploy_env_vars_dev
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# dev Environment Configuration
|
||||||
|
resource_group=rg-digitalgarage-02
|
||||||
|
cluster_name=aks-digitalgarage-02
|
||||||
3
.github/config/deploy_env_vars_prod
vendored
Normal file
3
.github/config/deploy_env_vars_prod
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# prod Environment Configuration
|
||||||
|
resource_group=rg-digitalgarage-02
|
||||||
|
cluster_name=aks-digitalgarage-02
|
||||||
3
.github/config/deploy_env_vars_staging
vendored
Normal file
3
.github/config/deploy_env_vars_staging
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# staging Environment Configuration
|
||||||
|
resource_group=rg-digitalgarage-02
|
||||||
|
cluster_name=aks-digitalgarage-02
|
||||||
66
.github/kustomize/base/ai/deployment.yaml
vendored
Normal file
66
.github/kustomize/base/ai/deployment.yaml
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: ai
|
||||||
|
labels:
|
||||||
|
app: ai
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: ai
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: ai
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: ai
|
||||||
|
image: acrdigitalgarage02.azurecr.io/hgzero/ai:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: cm-common
|
||||||
|
- secretRef:
|
||||||
|
name: secret-common
|
||||||
|
env:
|
||||||
|
- name: DB_URL
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-ai
|
||||||
|
key: DB_URL
|
||||||
|
- name: DB_USERNAME
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-ai
|
||||||
|
key: DB_USERNAME
|
||||||
|
- name: DB_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-ai
|
||||||
|
key: DB_PASSWORD
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 256m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: 1024m
|
||||||
|
memory: 1024Mi
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /actuator/health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 60
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /actuator/health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
11
.github/kustomize/base/ai/secret-ai.yaml
vendored
Normal file
11
.github/kustomize/base/ai/secret-ai.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: secret-ai
|
||||||
|
labels:
|
||||||
|
app: ai
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_URL: "jdbc:postgresql://postgres-service:5432/aidb"
|
||||||
|
DB_USERNAME: "aiuser"
|
||||||
|
DB_PASSWORD: "aipass123"
|
||||||
15
.github/kustomize/base/ai/service.yaml
vendored
Normal file
15
.github/kustomize/base/ai/service.yaml
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: ai
|
||||||
|
labels:
|
||||||
|
app: ai
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
name: http
|
||||||
|
selector:
|
||||||
|
app: ai
|
||||||
25
.github/kustomize/base/common/cm-common.yaml
vendored
Normal file
25
.github/kustomize/base/common/cm-common.yaml
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: cm-common
|
||||||
|
data:
|
||||||
|
# Spring Profiles
|
||||||
|
SPRING_PROFILES_ACTIVE: "dev"
|
||||||
|
|
||||||
|
# Database Configuration
|
||||||
|
DDL_AUTO: "update"
|
||||||
|
SHOW_SQL: "true"
|
||||||
|
|
||||||
|
# JWT Configuration
|
||||||
|
JWT_ACCESS_TOKEN_EXPIRATION: "3600000" # 1 hour
|
||||||
|
JWT_REFRESH_TOKEN_EXPIRATION: "86400000" # 24 hours
|
||||||
|
|
||||||
|
# Logging Configuration
|
||||||
|
LOG_LEVEL: "INFO"
|
||||||
|
|
||||||
|
# Application Configuration
|
||||||
|
SERVER_PORT: "8080"
|
||||||
|
|
||||||
|
# Redis Configuration
|
||||||
|
REDIS_HOST: "redis-service"
|
||||||
|
REDIS_PORT: "6379"
|
||||||
48
.github/kustomize/base/common/ingress.yaml
vendored
Normal file
48
.github/kustomize/base/common/ingress.yaml
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: hgzero
|
||||||
|
annotations:
|
||||||
|
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||||
|
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
||||||
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
|
rules:
|
||||||
|
- host: hgzero-api.20.214.196.128.nip.io
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /api/users
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: user
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
- path: /api/meetings
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: meeting
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
- path: /api/stt
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: stt
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
- path: /api/ai
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: ai
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
- path: /api/notifications
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: notification
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
11
.github/kustomize/base/common/secret-common.yaml
vendored
Normal file
11
.github/kustomize/base/common/secret-common.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: secret-common
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
# JWT Secret Key (Base64 encoded in production)
|
||||||
|
JWT_SECRET_KEY: "hgzero-jwt-secret-key-change-in-production"
|
||||||
|
|
||||||
|
# Redis Password
|
||||||
|
REDIS_PASSWORD: "redis-password-change-in-production"
|
||||||
7
.github/kustomize/base/common/secret-imagepull.yaml
vendored
Normal file
7
.github/kustomize/base/common/secret-imagepull.yaml
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: acr-secret
|
||||||
|
type: kubernetes.io/dockerconfigjson
|
||||||
|
data:
|
||||||
|
.dockerconfigjson: e30K
|
||||||
49
.github/kustomize/base/kustomization.yaml
vendored
Normal file
49
.github/kustomize/base/kustomization.yaml
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
metadata:
|
||||||
|
name: hgzero-base
|
||||||
|
|
||||||
|
resources:
|
||||||
|
# Common resources
|
||||||
|
- common/cm-common.yaml
|
||||||
|
- common/secret-common.yaml
|
||||||
|
- common/secret-imagepull.yaml
|
||||||
|
- common/ingress.yaml
|
||||||
|
|
||||||
|
# User service
|
||||||
|
- user/deployment.yaml
|
||||||
|
- user/service.yaml
|
||||||
|
- user/secret-user.yaml
|
||||||
|
|
||||||
|
# Meeting service
|
||||||
|
- meeting/deployment.yaml
|
||||||
|
- meeting/service.yaml
|
||||||
|
- meeting/secret-meeting.yaml
|
||||||
|
|
||||||
|
# STT service
|
||||||
|
- stt/deployment.yaml
|
||||||
|
- stt/service.yaml
|
||||||
|
- stt/secret-stt.yaml
|
||||||
|
|
||||||
|
# AI service
|
||||||
|
- ai/deployment.yaml
|
||||||
|
- ai/service.yaml
|
||||||
|
- ai/secret-ai.yaml
|
||||||
|
|
||||||
|
# Notification service
|
||||||
|
- notification/deployment.yaml
|
||||||
|
- notification/service.yaml
|
||||||
|
- notification/secret-notification.yaml
|
||||||
|
|
||||||
|
images:
|
||||||
|
- name: acrdigitalgarage02.azurecr.io/hgzero/user
|
||||||
|
newTag: latest
|
||||||
|
- name: acrdigitalgarage02.azurecr.io/hgzero/meeting
|
||||||
|
newTag: latest
|
||||||
|
- name: acrdigitalgarage02.azurecr.io/hgzero/stt
|
||||||
|
newTag: latest
|
||||||
|
- name: acrdigitalgarage02.azurecr.io/hgzero/ai
|
||||||
|
newTag: latest
|
||||||
|
- name: acrdigitalgarage02.azurecr.io/hgzero/notification
|
||||||
|
newTag: latest
|
||||||
66
.github/kustomize/base/meeting/deployment.yaml
vendored
Normal file
66
.github/kustomize/base/meeting/deployment.yaml
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: meeting
|
||||||
|
labels:
|
||||||
|
app: meeting
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: meeting
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: meeting
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: meeting
|
||||||
|
image: acrdigitalgarage02.azurecr.io/hgzero/meeting:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: cm-common
|
||||||
|
- secretRef:
|
||||||
|
name: secret-common
|
||||||
|
env:
|
||||||
|
- name: DB_URL
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-meeting
|
||||||
|
key: DB_URL
|
||||||
|
- name: DB_USERNAME
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-meeting
|
||||||
|
key: DB_USERNAME
|
||||||
|
- name: DB_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-meeting
|
||||||
|
key: DB_PASSWORD
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "256m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "1024m"
|
||||||
|
memory: "1024Mi"
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /actuator/health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /actuator/health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 3
|
||||||
10
.github/kustomize/base/meeting/secret-meeting.yaml
vendored
Normal file
10
.github/kustomize/base/meeting/secret-meeting.yaml
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: secret-meeting
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
# Meeting Service Database Configuration (Development)
|
||||||
|
DB_URL: "jdbc:postgresql://postgres-meeting:5432/meeting"
|
||||||
|
DB_USERNAME: "meeting_user"
|
||||||
|
DB_PASSWORD: "meeting_password"
|
||||||
15
.github/kustomize/base/meeting/service.yaml
vendored
Normal file
15
.github/kustomize/base/meeting/service.yaml
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: meeting
|
||||||
|
labels:
|
||||||
|
app: meeting
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app: meeting
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
name: http
|
||||||
68
.github/kustomize/base/notification/deployment.yaml
vendored
Normal file
68
.github/kustomize/base/notification/deployment.yaml
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: notification
|
||||||
|
labels:
|
||||||
|
app: notification
|
||||||
|
system: hgzero
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: notification
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: notification
|
||||||
|
system: hgzero
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: notification
|
||||||
|
image: acrdigitalgarage02.azurecr.io/hgzero/notification:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: cm-common
|
||||||
|
- secretRef:
|
||||||
|
name: secret-common
|
||||||
|
env:
|
||||||
|
- name: DB_URL
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-notification
|
||||||
|
key: DB_URL
|
||||||
|
- name: DB_USERNAME
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-notification
|
||||||
|
key: DB_USERNAME
|
||||||
|
- name: DB_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-notification
|
||||||
|
key: DB_PASSWORD
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "256m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "1024m"
|
||||||
|
memory: "1024Mi"
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /actuator/health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 60
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /actuator/health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
12
.github/kustomize/base/notification/secret-notification.yaml
vendored
Normal file
12
.github/kustomize/base/notification/secret-notification.yaml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: secret-notification
|
||||||
|
labels:
|
||||||
|
app: notification
|
||||||
|
system: hgzero
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_URL: "jdbc:postgresql://postgres-service:5432/notification_db"
|
||||||
|
DB_USERNAME: "notification_user"
|
||||||
|
DB_PASSWORD: "notification_pass"
|
||||||
16
.github/kustomize/base/notification/service.yaml
vendored
Normal file
16
.github/kustomize/base/notification/service.yaml
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: notification
|
||||||
|
labels:
|
||||||
|
app: notification
|
||||||
|
system: hgzero
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app: notification
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
name: http
|
||||||
66
.github/kustomize/base/stt/deployment.yaml
vendored
Normal file
66
.github/kustomize/base/stt/deployment.yaml
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: stt
|
||||||
|
labels:
|
||||||
|
app: stt
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: stt
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: stt
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: stt
|
||||||
|
image: acrdigitalgarage02.azurecr.io/hgzero/stt:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: cm-common
|
||||||
|
- secretRef:
|
||||||
|
name: secret-common
|
||||||
|
env:
|
||||||
|
- name: DB_URL
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-stt
|
||||||
|
key: DB_URL
|
||||||
|
- name: DB_USERNAME
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-stt
|
||||||
|
key: DB_USERNAME
|
||||||
|
- name: DB_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-stt
|
||||||
|
key: DB_PASSWORD
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 256m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: 1024m
|
||||||
|
memory: 1024Mi
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /actuator/health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /actuator/health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 20
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
9
.github/kustomize/base/stt/secret-stt.yaml
vendored
Normal file
9
.github/kustomize/base/stt/secret-stt.yaml
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: secret-stt
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_URL: "jdbc:postgresql://postgres-service:5432/sttdb"
|
||||||
|
DB_USERNAME: "sttuser"
|
||||||
|
DB_PASSWORD: "sttpass"
|
||||||
15
.github/kustomize/base/stt/service.yaml
vendored
Normal file
15
.github/kustomize/base/stt/service.yaml
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: stt
|
||||||
|
labels:
|
||||||
|
app: stt
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app: stt
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
name: http
|
||||||
66
.github/kustomize/base/user/deployment.yaml
vendored
Normal file
66
.github/kustomize/base/user/deployment.yaml
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: user
|
||||||
|
labels:
|
||||||
|
app: user
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: user
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: user
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: user
|
||||||
|
image: acrdigitalgarage02.azurecr.io/hgzero/user:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: cm-common
|
||||||
|
- secretRef:
|
||||||
|
name: secret-common
|
||||||
|
env:
|
||||||
|
- name: DB_URL
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-user
|
||||||
|
key: DB_URL
|
||||||
|
- name: DB_USERNAME
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-user
|
||||||
|
key: DB_USERNAME
|
||||||
|
- name: DB_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-user
|
||||||
|
key: DB_PASSWORD
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "256m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "1024m"
|
||||||
|
memory: "1024Mi"
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /actuator/health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /actuator/health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 3
|
||||||
11
.github/kustomize/base/user/secret-user.yaml
vendored
Normal file
11
.github/kustomize/base/user/secret-user.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: secret-user
|
||||||
|
labels:
|
||||||
|
app: user
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_URL: "jdbc:mysql://mysql-user:3306/userdb?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true"
|
||||||
|
DB_USERNAME: "user"
|
||||||
|
DB_PASSWORD: "user1234"
|
||||||
15
.github/kustomize/base/user/service.yaml
vendored
Normal file
15
.github/kustomize/base/user/service.yaml
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: user
|
||||||
|
labels:
|
||||||
|
app: user
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app: user
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
name: http
|
||||||
18
.github/kustomize/overlays/dev/cm-common-patch.yaml
vendored
Normal file
18
.github/kustomize/overlays/dev/cm-common-patch.yaml
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: cm-common
|
||||||
|
data:
|
||||||
|
# Spring Profiles
|
||||||
|
SPRING_PROFILES_ACTIVE: "dev"
|
||||||
|
|
||||||
|
# Database Configuration
|
||||||
|
DDL_AUTO: "update"
|
||||||
|
SHOW_SQL: "true"
|
||||||
|
|
||||||
|
# JWT Configuration
|
||||||
|
JWT_ACCESS_TOKEN_EXPIRATION: "3600000" # 1 hour
|
||||||
|
JWT_REFRESH_TOKEN_EXPIRATION: "86400000" # 24 hours
|
||||||
|
|
||||||
|
# Logging Configuration
|
||||||
|
LOG_LEVEL: "DEBUG"
|
||||||
17
.github/kustomize/overlays/dev/deployment-ai-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/dev/deployment-ai-patch.yaml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: ai
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: ai
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "256m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "1024m"
|
||||||
|
memory: "1024Mi"
|
||||||
17
.github/kustomize/overlays/dev/deployment-meeting-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/dev/deployment-meeting-patch.yaml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: meeting
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: meeting
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "256m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "1024m"
|
||||||
|
memory: "1024Mi"
|
||||||
17
.github/kustomize/overlays/dev/deployment-notification-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/dev/deployment-notification-patch.yaml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: notification
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: notification
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "256m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "1024m"
|
||||||
|
memory: "1024Mi"
|
||||||
17
.github/kustomize/overlays/dev/deployment-stt-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/dev/deployment-stt-patch.yaml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: stt
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: stt
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "256m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "1024m"
|
||||||
|
memory: "1024Mi"
|
||||||
17
.github/kustomize/overlays/dev/deployment-user-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/dev/deployment-user-patch.yaml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: user
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: user
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "256m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "1024m"
|
||||||
|
memory: "1024Mi"
|
||||||
48
.github/kustomize/overlays/dev/ingress-patch.yaml
vendored
Normal file
48
.github/kustomize/overlays/dev/ingress-patch.yaml
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: hgzero
|
||||||
|
annotations:
|
||||||
|
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||||
|
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
||||||
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
|
rules:
|
||||||
|
- host: hgzero-api.20.214.196.128.nip.io
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /api/users
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: user
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
- path: /api/meetings
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: meeting
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
- path: /api/stt
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: stt
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
- path: /api/ai
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: ai
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
- path: /api/notifications
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: notification
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
84
.github/kustomize/overlays/dev/kustomization.yaml
vendored
Normal file
84
.github/kustomize/overlays/dev/kustomization.yaml
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namespace: hgzero
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- ../../base
|
||||||
|
|
||||||
|
patches:
|
||||||
|
# Common patches
|
||||||
|
- path: cm-common-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: ConfigMap
|
||||||
|
name: cm-common
|
||||||
|
- path: secret-common-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: secret-common
|
||||||
|
- path: ingress-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Ingress
|
||||||
|
name: hgzero
|
||||||
|
|
||||||
|
# User service patches
|
||||||
|
- path: deployment-user-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
name: user
|
||||||
|
- path: secret-user-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: secret-user
|
||||||
|
|
||||||
|
# Meeting service patches
|
||||||
|
- path: deployment-meeting-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
name: meeting
|
||||||
|
- path: secret-meeting-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: secret-meeting
|
||||||
|
|
||||||
|
# STT service patches
|
||||||
|
- path: deployment-stt-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
name: stt
|
||||||
|
- path: secret-stt-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: secret-stt
|
||||||
|
|
||||||
|
# AI service patches
|
||||||
|
- path: deployment-ai-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
name: ai
|
||||||
|
- path: secret-ai-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: secret-ai
|
||||||
|
|
||||||
|
# Notification service patches
|
||||||
|
- path: deployment-notification-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
name: notification
|
||||||
|
- path: secret-notification-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: secret-notification
|
||||||
|
|
||||||
|
images:
|
||||||
|
- name: acrdigitalgarage02.azurecr.io/hgzero/user
|
||||||
|
newTag: dev-latest
|
||||||
|
- name: acrdigitalgarage02.azurecr.io/hgzero/meeting
|
||||||
|
newTag: dev-latest
|
||||||
|
- name: acrdigitalgarage02.azurecr.io/hgzero/stt
|
||||||
|
newTag: dev-latest
|
||||||
|
- name: acrdigitalgarage02.azurecr.io/hgzero/ai
|
||||||
|
newTag: dev-latest
|
||||||
|
- name: acrdigitalgarage02.azurecr.io/hgzero/notification
|
||||||
|
newTag: dev-latest
|
||||||
11
.github/kustomize/overlays/dev/secret-ai-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/dev/secret-ai-patch.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: secret-ai
|
||||||
|
labels:
|
||||||
|
app: ai
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_URL: "jdbc:postgresql://postgres-service-dev:5432/aidb_dev"
|
||||||
|
DB_USERNAME: "aiuser_dev"
|
||||||
|
DB_PASSWORD: "aipass_dev123"
|
||||||
11
.github/kustomize/overlays/dev/secret-common-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/dev/secret-common-patch.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: secret-common
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
# JWT Secret Key (개발용)
|
||||||
|
JWT_SECRET_KEY: "hgzero-jwt-secret-key-change-in-production"
|
||||||
|
|
||||||
|
# Redis Password (개발용)
|
||||||
|
REDIS_PASSWORD: "redis-password-change-in-production"
|
||||||
10
.github/kustomize/overlays/dev/secret-meeting-patch.yaml
vendored
Normal file
10
.github/kustomize/overlays/dev/secret-meeting-patch.yaml
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: secret-meeting
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
# Meeting Service Database Configuration (Development)
|
||||||
|
DB_URL: "jdbc:postgresql://postgres-meeting-dev:5432/meeting_dev"
|
||||||
|
DB_USERNAME: "meeting_dev_user"
|
||||||
|
DB_PASSWORD: "meeting_dev_password"
|
||||||
12
.github/kustomize/overlays/dev/secret-notification-patch.yaml
vendored
Normal file
12
.github/kustomize/overlays/dev/secret-notification-patch.yaml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: secret-notification
|
||||||
|
labels:
|
||||||
|
app: notification
|
||||||
|
system: hgzero
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_URL: "jdbc:postgresql://postgres-service-dev:5432/notification_db_dev"
|
||||||
|
DB_USERNAME: "notification_dev_user"
|
||||||
|
DB_PASSWORD: "notification_dev_pass"
|
||||||
9
.github/kustomize/overlays/dev/secret-stt-patch.yaml
vendored
Normal file
9
.github/kustomize/overlays/dev/secret-stt-patch.yaml
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: secret-stt
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_URL: "jdbc:postgresql://postgres-service-dev:5432/sttdb_dev"
|
||||||
|
DB_USERNAME: "sttuser_dev"
|
||||||
|
DB_PASSWORD: "sttpass_dev"
|
||||||
11
.github/kustomize/overlays/dev/secret-user-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/dev/secret-user-patch.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: secret-user
|
||||||
|
labels:
|
||||||
|
app: user
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_URL: "jdbc:mysql://mysql-user-dev:3306/userdb_dev?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true"
|
||||||
|
DB_USERNAME: "user_dev"
|
||||||
|
DB_PASSWORD: "user_dev1234"
|
||||||
14
.github/kustomize/overlays/prod/cm-common-patch.yaml
vendored
Normal file
14
.github/kustomize/overlays/prod/cm-common-patch.yaml
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: common-config
|
||||||
|
namespace: hgzero
|
||||||
|
data:
|
||||||
|
SPRING_PROFILES_ACTIVE: "prod"
|
||||||
|
DDL_AUTO: "validate"
|
||||||
|
LOG_LEVEL: "WARN"
|
||||||
|
JWT_ACCESS_TOKEN_EXPIRATION: "1800000"
|
||||||
|
JWT_REFRESH_TOKEN_EXPIRATION: "43200000"
|
||||||
|
REDIS_HOST: "redis-svc.hgzero.svc.cluster.local"
|
||||||
|
REDIS_PORT: "6379"
|
||||||
|
KAFKA_BOOTSTRAP_SERVERS: "kafka-svc.hgzero.svc.cluster.local:9092"
|
||||||
18
.github/kustomize/overlays/prod/deployment-ai-patch.yaml
vendored
Normal file
18
.github/kustomize/overlays/prod/deployment-ai-patch.yaml
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: ai-deploy
|
||||||
|
namespace: hgzero
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: ai-container
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "1024m"
|
||||||
|
memory: "1024Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "4096m"
|
||||||
|
memory: "4096Mi"
|
||||||
18
.github/kustomize/overlays/prod/deployment-meeting-patch.yaml
vendored
Normal file
18
.github/kustomize/overlays/prod/deployment-meeting-patch.yaml
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: meeting-deploy
|
||||||
|
namespace: hgzero
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: meeting-container
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "1024m"
|
||||||
|
memory: "1024Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "4096m"
|
||||||
|
memory: "4096Mi"
|
||||||
18
.github/kustomize/overlays/prod/deployment-notification-patch.yaml
vendored
Normal file
18
.github/kustomize/overlays/prod/deployment-notification-patch.yaml
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: notification-deploy
|
||||||
|
namespace: hgzero
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: notification-container
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "1024m"
|
||||||
|
memory: "1024Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "4096m"
|
||||||
|
memory: "4096Mi"
|
||||||
18
.github/kustomize/overlays/prod/deployment-stt-patch.yaml
vendored
Normal file
18
.github/kustomize/overlays/prod/deployment-stt-patch.yaml
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: stt-deploy
|
||||||
|
namespace: hgzero
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: stt-container
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "1024m"
|
||||||
|
memory: "1024Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "4096m"
|
||||||
|
memory: "4096Mi"
|
||||||
18
.github/kustomize/overlays/prod/deployment-user-patch.yaml
vendored
Normal file
18
.github/kustomize/overlays/prod/deployment-user-patch.yaml
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: user-deploy
|
||||||
|
namespace: hgzero
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: user-container
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "1024m"
|
||||||
|
memory: "1024Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "4096m"
|
||||||
|
memory: "4096Mi"
|
||||||
49
.github/kustomize/overlays/prod/ingress-patch.yaml
vendored
Normal file
49
.github/kustomize/overlays/prod/ingress-patch.yaml
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: hgzero-ingress
|
||||||
|
namespace: hgzero
|
||||||
|
annotations:
|
||||||
|
nginx.ingress.kubernetes.io/rewrite-target: /$2
|
||||||
|
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||||
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
|
rules:
|
||||||
|
- host: hgzero-api.example.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /user(/|$)(.*)
|
||||||
|
pathType: ImplementationSpecific
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: user-svc
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
- path: /meeting(/|$)(.*)
|
||||||
|
pathType: ImplementationSpecific
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: meeting-svc
|
||||||
|
port:
|
||||||
|
number: 8081
|
||||||
|
- path: /stt(/|$)(.*)
|
||||||
|
pathType: ImplementationSpecific
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: stt-svc
|
||||||
|
port:
|
||||||
|
number: 8082
|
||||||
|
- path: /ai(/|$)(.*)
|
||||||
|
pathType: ImplementationSpecific
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: ai-svc
|
||||||
|
port:
|
||||||
|
number: 8083
|
||||||
|
- path: /notification(/|$)(.*)
|
||||||
|
pathType: ImplementationSpecific
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: notification-svc
|
||||||
|
port:
|
||||||
|
number: 8084
|
||||||
84
.github/kustomize/overlays/prod/kustomization.yaml
vendored
Normal file
84
.github/kustomize/overlays/prod/kustomization.yaml
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namespace: hgzero
|
||||||
|
|
||||||
|
bases:
|
||||||
|
- ../../base
|
||||||
|
|
||||||
|
patches:
|
||||||
|
# Common patches
|
||||||
|
- path: cm-common-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: ConfigMap
|
||||||
|
name: common-config
|
||||||
|
- path: secret-common-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: common-secret
|
||||||
|
- path: ingress-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Ingress
|
||||||
|
name: hgzero-ingress
|
||||||
|
|
||||||
|
# User service patches
|
||||||
|
- path: deployment-user-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
name: user-deploy
|
||||||
|
- path: secret-user-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: user-secret
|
||||||
|
|
||||||
|
# Meeting service patches
|
||||||
|
- path: deployment-meeting-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
name: meeting-deploy
|
||||||
|
- path: secret-meeting-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: meeting-secret
|
||||||
|
|
||||||
|
# STT service patches
|
||||||
|
- path: deployment-stt-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
name: stt-deploy
|
||||||
|
- path: secret-stt-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: stt-secret
|
||||||
|
|
||||||
|
# AI service patches
|
||||||
|
- path: deployment-ai-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
name: ai-deploy
|
||||||
|
- path: secret-ai-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: ai-secret
|
||||||
|
|
||||||
|
# Notification service patches
|
||||||
|
- path: deployment-notification-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
name: notification-deploy
|
||||||
|
- path: secret-notification-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: notification-secret
|
||||||
|
|
||||||
|
images:
|
||||||
|
- name: user-service
|
||||||
|
newTag: prod-latest
|
||||||
|
- name: meeting-service
|
||||||
|
newTag: prod-latest
|
||||||
|
- name: stt-service
|
||||||
|
newTag: prod-latest
|
||||||
|
- name: ai-service
|
||||||
|
newTag: prod-latest
|
||||||
|
- name: notification-service
|
||||||
|
newTag: prod-latest
|
||||||
9
.github/kustomize/overlays/prod/secret-ai-patch.yaml
vendored
Normal file
9
.github/kustomize/overlays/prod/secret-ai-patch.yaml
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: ai-secret
|
||||||
|
namespace: hgzero
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
OPENAI_API_KEY: "your-openai-api-key"
|
||||||
|
LANGCHAIN_API_KEY: "your-langchain-api-key"
|
||||||
11
.github/kustomize/overlays/prod/secret-common-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/prod/secret-common-patch.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: common-secret
|
||||||
|
namespace: hgzero
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
JWT_SECRET_KEY: "your-prod-secret-key-change-this-in-production"
|
||||||
|
REDIS_PASSWORD: "your-prod-redis-password"
|
||||||
|
KAFKA_USERNAME: "admin"
|
||||||
|
KAFKA_PASSWORD: "admin-secret"
|
||||||
13
.github/kustomize/overlays/prod/secret-meeting-patch.yaml
vendored
Normal file
13
.github/kustomize/overlays/prod/secret-meeting-patch.yaml
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: meeting-secret
|
||||||
|
namespace: hgzero
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_URL: "jdbc:postgresql://postgres-prod:5432/meeting_prod"
|
||||||
|
DB_USERNAME: "meeting_admin"
|
||||||
|
DB_PASSWORD: "meeting_prod_password"
|
||||||
|
OPENAI_API_KEY: "your-openai-api-key"
|
||||||
|
S3_ACCESS_KEY: "your-s3-access-key"
|
||||||
|
S3_SECRET_KEY: "your-s3-secret-key"
|
||||||
11
.github/kustomize/overlays/prod/secret-notification-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/prod/secret-notification-patch.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: notification-secret
|
||||||
|
namespace: hgzero
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_URL: "jdbc:postgresql://postgres-prod:5432/notification_prod"
|
||||||
|
DB_USERNAME: "notification_admin"
|
||||||
|
DB_PASSWORD: "notification_prod_password"
|
||||||
|
OPENAI_API_KEY: "your-openai-api-key"
|
||||||
10
.github/kustomize/overlays/prod/secret-stt-patch.yaml
vendored
Normal file
10
.github/kustomize/overlays/prod/secret-stt-patch.yaml
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: stt-secret
|
||||||
|
namespace: hgzero
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
OPENAI_API_KEY: "your-openai-api-key"
|
||||||
|
S3_ACCESS_KEY: "your-s3-access-key"
|
||||||
|
S3_SECRET_KEY: "your-s3-secret-key"
|
||||||
11
.github/kustomize/overlays/prod/secret-user-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/prod/secret-user-patch.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: user-secret
|
||||||
|
namespace: hgzero
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_URL: "jdbc:postgresql://postgres-prod:5432/user_prod"
|
||||||
|
DB_USERNAME: "user_admin"
|
||||||
|
DB_PASSWORD: "user_prod_password"
|
||||||
|
OPENAI_API_KEY: "your-openai-api-key"
|
||||||
99
.github/kustomize/overlays/staging/README.md
vendored
Normal file
99
.github/kustomize/overlays/staging/README.md
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# STAGING Environment Kustomize Overlay
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
STAGING 환경을 위한 Kustomize overlay 설정입니다.
|
||||||
|
|
||||||
|
## 환경 설정
|
||||||
|
|
||||||
|
### 네임스페이스
|
||||||
|
- `hgzero`
|
||||||
|
|
||||||
|
### 공통 설정
|
||||||
|
- **프로파일**: staging
|
||||||
|
- **DDL 모드**: validate
|
||||||
|
- **로그 레벨**: INFO
|
||||||
|
- **Ingress 호스트**: hgzero-staging-api.example.com
|
||||||
|
- **SSL 리다이렉트**: 활성화
|
||||||
|
|
||||||
|
### 리소스 설정
|
||||||
|
- **Replicas**: 2
|
||||||
|
- **Resource Requests**:
|
||||||
|
- CPU: 512m
|
||||||
|
- Memory: 512Mi
|
||||||
|
- **Resource Limits**:
|
||||||
|
- CPU: 2048m
|
||||||
|
- Memory: 2048Mi
|
||||||
|
|
||||||
|
## 생성된 파일 목록 (총 14개)
|
||||||
|
|
||||||
|
### Common Patches (3개)
|
||||||
|
1. `cm-common-patch.yaml` - 공통 ConfigMap 패치
|
||||||
|
2. `secret-common-patch.yaml` - 공통 Secret 패치 (JWT, Redis)
|
||||||
|
3. `ingress-patch.yaml` - Ingress 패치 (호스트, SSL)
|
||||||
|
|
||||||
|
### Service-specific Patches (10개)
|
||||||
|
각 서비스(user, meeting, stt, ai, notification)별 2개 파일:
|
||||||
|
- `deployment-{서비스명}-patch.yaml` - Deployment 리소스 패치
|
||||||
|
- `secret-{서비스명}-patch.yaml` - DB 연결 정보 패치
|
||||||
|
|
||||||
|
4. `deployment-user-patch.yaml`
|
||||||
|
5. `secret-user-patch.yaml`
|
||||||
|
6. `deployment-meeting-patch.yaml`
|
||||||
|
7. `secret-meeting-patch.yaml`
|
||||||
|
8. `deployment-stt-patch.yaml`
|
||||||
|
9. `secret-stt-patch.yaml`
|
||||||
|
10. `deployment-ai-patch.yaml`
|
||||||
|
11. `secret-ai-patch.yaml`
|
||||||
|
12. `deployment-notification-patch.yaml`
|
||||||
|
13. `secret-notification-patch.yaml`
|
||||||
|
|
||||||
|
### Kustomization (1개)
|
||||||
|
14. `kustomization.yaml` - Kustomize 설정 파일
|
||||||
|
|
||||||
|
## 데이터베이스 설정
|
||||||
|
|
||||||
|
각 서비스별 STAGING 환경 DB 정보:
|
||||||
|
- **호스트**: {서비스명}-db-staging
|
||||||
|
- **포트**: 5432
|
||||||
|
- **데이터베이스명**: {서비스명}_db_staging
|
||||||
|
- **사용자명**: {서비스명}_service
|
||||||
|
- **비밀번호**: stringData로 정의 (실제 환경에서 변경 필요)
|
||||||
|
|
||||||
|
## 이미지 태그
|
||||||
|
모든 서비스: `staging-latest`
|
||||||
|
|
||||||
|
## 사용 방법
|
||||||
|
|
||||||
|
### 1. Kustomize 빌드 확인
|
||||||
|
```bash
|
||||||
|
kubectl kustomize .github/kustomize/overlays/staging
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. STAGING 환경 배포
|
||||||
|
```bash
|
||||||
|
kubectl apply -k .github/kustomize/overlays/staging
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 배포 상태 확인
|
||||||
|
```bash
|
||||||
|
kubectl get all -n hgzero
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Secret 업데이트 (실제 배포 시)
|
||||||
|
```bash
|
||||||
|
# Secret 파일들의 stringData를 실제 STAGING 환경 값으로 변경
|
||||||
|
vi .github/kustomize/overlays/staging/secret-common-patch.yaml
|
||||||
|
vi .github/kustomize/overlays/staging/secret-user-patch.yaml
|
||||||
|
# ... (각 서비스별 secret 파일 수정)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 주의사항
|
||||||
|
1. Secret 파일들의 비밀번호는 반드시 실제 환경에 맞게 변경해야 합니다
|
||||||
|
2. Ingress 호스트명을 실제 STAGING 도메인으로 변경해야 합니다
|
||||||
|
3. DB 호스트명이 실제 STAGING 환경과 일치하는지 확인해야 합니다
|
||||||
|
4. 리소스 제한은 실제 부하 테스트 결과에 따라 조정이 필요할 수 있습니다
|
||||||
|
|
||||||
|
## 다음 단계
|
||||||
|
- PROD 환경 overlay 생성
|
||||||
|
- CI/CD 파이프라인과 통합
|
||||||
|
- Monitoring 및 Logging 설정 추가
|
||||||
8
.github/kustomize/overlays/staging/cm-common-patch.yaml
vendored
Normal file
8
.github/kustomize/overlays/staging/cm-common-patch.yaml
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: hgzero-common-config
|
||||||
|
data:
|
||||||
|
SPRING_PROFILES_ACTIVE: "staging"
|
||||||
|
DDL_AUTO: "validate"
|
||||||
|
LOG_LEVEL: "INFO"
|
||||||
17
.github/kustomize/overlays/staging/deployment-ai-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/staging/deployment-ai-patch.yaml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: ai-service
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: ai-service
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "512m"
|
||||||
|
limits:
|
||||||
|
memory: "2048Mi"
|
||||||
|
cpu: "2048m"
|
||||||
17
.github/kustomize/overlays/staging/deployment-meeting-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/staging/deployment-meeting-patch.yaml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: meeting-service
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: meeting-service
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "512m"
|
||||||
|
limits:
|
||||||
|
memory: "2048Mi"
|
||||||
|
cpu: "2048m"
|
||||||
17
.github/kustomize/overlays/staging/deployment-notification-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/staging/deployment-notification-patch.yaml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: notification-service
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: notification-service
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "512m"
|
||||||
|
limits:
|
||||||
|
memory: "2048Mi"
|
||||||
|
cpu: "2048m"
|
||||||
17
.github/kustomize/overlays/staging/deployment-stt-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/staging/deployment-stt-patch.yaml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: stt-service
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: stt-service
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "512m"
|
||||||
|
limits:
|
||||||
|
memory: "2048Mi"
|
||||||
|
cpu: "2048m"
|
||||||
17
.github/kustomize/overlays/staging/deployment-user-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/staging/deployment-user-patch.yaml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: user-service
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: user-service
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "512m"
|
||||||
|
limits:
|
||||||
|
memory: "2048Mi"
|
||||||
|
cpu: "2048m"
|
||||||
46
.github/kustomize/overlays/staging/ingress-patch.yaml
vendored
Normal file
46
.github/kustomize/overlays/staging/ingress-patch.yaml
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: hgzero-ingress
|
||||||
|
annotations:
|
||||||
|
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: hgzero-staging-api.example.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /api/users
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: user-service
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
- path: /api/meetings
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: meeting-service
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
- path: /api/stt
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: stt-service
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
- path: /api/ai
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: ai-service
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
- path: /api/notifications
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: notification-service
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
91
.github/kustomize/overlays/staging/kustomization.yaml
vendored
Normal file
91
.github/kustomize/overlays/staging/kustomization.yaml
vendored
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namespace: hgzero
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- ../../base
|
||||||
|
|
||||||
|
patches:
|
||||||
|
# Common patches
|
||||||
|
- path: cm-common-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: ConfigMap
|
||||||
|
name: hgzero-common-config
|
||||||
|
|
||||||
|
- path: secret-common-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: hgzero-common-secret
|
||||||
|
|
||||||
|
- path: ingress-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Ingress
|
||||||
|
name: hgzero-ingress
|
||||||
|
|
||||||
|
# User service patches
|
||||||
|
- path: deployment-user-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
name: user-service
|
||||||
|
|
||||||
|
- path: secret-user-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: user-service-secret
|
||||||
|
|
||||||
|
# Meeting service patches
|
||||||
|
- path: deployment-meeting-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
name: meeting-service
|
||||||
|
|
||||||
|
- path: secret-meeting-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: meeting-service-secret
|
||||||
|
|
||||||
|
# STT service patches
|
||||||
|
- path: deployment-stt-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
name: stt-service
|
||||||
|
|
||||||
|
- path: secret-stt-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: stt-service-secret
|
||||||
|
|
||||||
|
# AI service patches
|
||||||
|
- path: deployment-ai-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
name: ai-service
|
||||||
|
|
||||||
|
- path: secret-ai-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: ai-service-secret
|
||||||
|
|
||||||
|
# Notification service patches
|
||||||
|
- path: deployment-notification-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
name: notification-service
|
||||||
|
|
||||||
|
- path: secret-notification-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: notification-service-secret
|
||||||
|
|
||||||
|
images:
|
||||||
|
- name: user-service
|
||||||
|
newTag: staging-latest
|
||||||
|
- name: meeting-service
|
||||||
|
newTag: staging-latest
|
||||||
|
- name: stt-service
|
||||||
|
newTag: staging-latest
|
||||||
|
- name: ai-service
|
||||||
|
newTag: staging-latest
|
||||||
|
- name: notification-service
|
||||||
|
newTag: staging-latest
|
||||||
11
.github/kustomize/overlays/staging/secret-ai-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/staging/secret-ai-patch.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: ai-service-secret
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_HOST: "ai-db-staging"
|
||||||
|
DB_PORT: "5432"
|
||||||
|
DB_NAME: "ai_db_staging"
|
||||||
|
DB_USERNAME: "ai_service"
|
||||||
|
DB_PASSWORD: "your-staging-ai-db-password"
|
||||||
9
.github/kustomize/overlays/staging/secret-common-patch.yaml
vendored
Normal file
9
.github/kustomize/overlays/staging/secret-common-patch.yaml
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: hgzero-common-secret
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
JWT_SECRET: "your-staging-jwt-secret-key-here-change-in-production"
|
||||||
|
JWT_EXPIRATION: "3600000"
|
||||||
|
REDIS_PASSWORD: "your-staging-redis-password"
|
||||||
11
.github/kustomize/overlays/staging/secret-meeting-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/staging/secret-meeting-patch.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: meeting-service-secret
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_HOST: "meeting-db-staging"
|
||||||
|
DB_PORT: "5432"
|
||||||
|
DB_NAME: "meeting_db_staging"
|
||||||
|
DB_USERNAME: "meeting_service"
|
||||||
|
DB_PASSWORD: "your-staging-meeting-db-password"
|
||||||
11
.github/kustomize/overlays/staging/secret-notification-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/staging/secret-notification-patch.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: notification-service-secret
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_HOST: "notification-db-staging"
|
||||||
|
DB_PORT: "5432"
|
||||||
|
DB_NAME: "notification_db_staging"
|
||||||
|
DB_USERNAME: "notification_service"
|
||||||
|
DB_PASSWORD: "your-staging-notification-db-password"
|
||||||
11
.github/kustomize/overlays/staging/secret-stt-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/staging/secret-stt-patch.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: stt-service-secret
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_HOST: "stt-db-staging"
|
||||||
|
DB_PORT: "5432"
|
||||||
|
DB_NAME: "stt_db_staging"
|
||||||
|
DB_USERNAME: "stt_service"
|
||||||
|
DB_PASSWORD: "your-staging-stt-db-password"
|
||||||
11
.github/kustomize/overlays/staging/secret-user-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/staging/secret-user-patch.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: user-service-secret
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_HOST: "user-db-staging"
|
||||||
|
DB_PORT: "5432"
|
||||||
|
DB_NAME: "user_db_staging"
|
||||||
|
DB_USERNAME: "user_service"
|
||||||
|
DB_PASSWORD: "your-staging-user-db-password"
|
||||||
69
.github/scripts/deploy-actions.sh
vendored
Executable file
69
.github/scripts/deploy-actions.sh
vendored
Executable file
@ -0,0 +1,69 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
ENVIRONMENT=${1:-dev}
|
||||||
|
IMAGE_TAG=${2:-latest}
|
||||||
|
|
||||||
|
echo "🚀 Manual deployment starting..."
|
||||||
|
echo "Environment: $ENVIRONMENT"
|
||||||
|
echo "Image Tag: $IMAGE_TAG"
|
||||||
|
|
||||||
|
# Check if kustomize is installed
|
||||||
|
if ! command -v kustomize &> /dev/null; then
|
||||||
|
echo "Installing Kustomize..."
|
||||||
|
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
|
||||||
|
sudo mv kustomize /usr/local/bin/
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load environment variables from .github/config
|
||||||
|
if [[ -f ".github/config/deploy_env_vars_${ENVIRONMENT}" ]]; then
|
||||||
|
source ".github/config/deploy_env_vars_${ENVIRONMENT}"
|
||||||
|
echo "✅ Environment variables loaded for $ENVIRONMENT"
|
||||||
|
else
|
||||||
|
echo "❌ Environment configuration file not found: .github/config/deploy_env_vars_${ENVIRONMENT}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create namespace
|
||||||
|
echo "📝 Creating namespace hgzero..."
|
||||||
|
kubectl create namespace hgzero --dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
|
# 환경별 이미지 태그 업데이트 (.github/kustomize 사용)
|
||||||
|
cd .github/kustomize/overlays/${ENVIRONMENT}
|
||||||
|
|
||||||
|
echo "🔄 Updating image tags..."
|
||||||
|
# 서비스 배열 정의
|
||||||
|
services=(user meeting stt ai notification)
|
||||||
|
|
||||||
|
# 각 서비스별 이미지 태그 업데이트
|
||||||
|
for service in "${services[@]}"; do
|
||||||
|
kustomize edit set image acrdigitalgarage02.azurecr.io/hgzero/$service:${ENVIRONMENT}-${IMAGE_TAG}
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "🚀 Deploying to Kubernetes..."
|
||||||
|
# 배포 실행
|
||||||
|
kubectl apply -k .
|
||||||
|
|
||||||
|
echo "⏳ Waiting for deployments to be ready..."
|
||||||
|
# 서비스별 배포 상태 확인
|
||||||
|
for service in "${services[@]}"; do
|
||||||
|
kubectl rollout status deployment/$service -n hgzero --timeout=300s || echo "⚠️ $service deployment timeout"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "🔍 Health check..."
|
||||||
|
# 각 서비스의 Health Check
|
||||||
|
for service in "${services[@]}"; do
|
||||||
|
POD=$(kubectl get pod -n hgzero -l app.kubernetes.io/name=$service -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
|
||||||
|
if [[ -n "$POD" ]]; then
|
||||||
|
kubectl -n hgzero exec $POD -- curl -f http://localhost:8080/actuator/health 2>/dev/null || echo "⚠️ $service health check failed"
|
||||||
|
else
|
||||||
|
echo "⚠️ $service pod not found"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "📋 Service Information:"
|
||||||
|
kubectl get pods -n hgzero
|
||||||
|
kubectl get services -n hgzero
|
||||||
|
kubectl get ingress -n hgzero
|
||||||
|
|
||||||
|
echo "✅ GitHub Actions deployment completed successfully!"
|
||||||
276
.github/workflows/backend-cicd.yaml
vendored
Normal file
276
.github/workflows/backend-cicd.yaml
vendored
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
name: Backend Services CI/CD
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main, develop ]
|
||||||
|
paths:
|
||||||
|
- 'user/**'
|
||||||
|
- 'meeting/**'
|
||||||
|
- 'stt/**'
|
||||||
|
- 'ai/**'
|
||||||
|
- 'notification/**'
|
||||||
|
- 'common/**'
|
||||||
|
- '.github/**'
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
ENVIRONMENT:
|
||||||
|
description: 'Target environment'
|
||||||
|
required: true
|
||||||
|
default: 'dev'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- dev
|
||||||
|
- staging
|
||||||
|
- prod
|
||||||
|
SKIP_SONARQUBE:
|
||||||
|
description: 'Skip SonarQube Analysis'
|
||||||
|
required: false
|
||||||
|
default: 'true'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- 'true'
|
||||||
|
- 'false'
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: acrdigitalgarage02.azurecr.io
|
||||||
|
IMAGE_ORG: hgzero
|
||||||
|
RESOURCE_GROUP: rg-digitalgarage-02
|
||||||
|
AKS_CLUSTER: aks-digitalgarage-02
|
||||||
|
NAMESPACE: hgzero
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build and Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
image_tag: ${{ steps.set_outputs.outputs.image_tag }}
|
||||||
|
environment: ${{ steps.set_outputs.outputs.environment }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up JDK 21
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
java-version: '21'
|
||||||
|
distribution: 'temurin'
|
||||||
|
cache: 'gradle'
|
||||||
|
|
||||||
|
- name: Determine environment
|
||||||
|
id: determine_env
|
||||||
|
run: |
|
||||||
|
# Use input parameter or default to 'dev'
|
||||||
|
ENVIRONMENT="${{ github.event.inputs.ENVIRONMENT || 'dev' }}"
|
||||||
|
echo "environment=$ENVIRONMENT" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Load environment variables
|
||||||
|
id: env_vars
|
||||||
|
run: |
|
||||||
|
ENV=${{ steps.determine_env.outputs.environment }}
|
||||||
|
|
||||||
|
# Initialize variables with defaults
|
||||||
|
REGISTRY="acrdigitalgarage02.azurecr.io"
|
||||||
|
IMAGE_ORG="hgzero"
|
||||||
|
RESOURCE_GROUP="rg-digitalgarage-02"
|
||||||
|
AKS_CLUSTER="aks-digitalgarage-02"
|
||||||
|
NAMESPACE="hgzero"
|
||||||
|
|
||||||
|
# Read environment variables from .github/config file
|
||||||
|
if [[ -f ".github/config/deploy_env_vars_${ENV}" ]]; then
|
||||||
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||||
|
# Skip comments and empty lines
|
||||||
|
[[ "$line" =~ ^#.*$ ]] && continue
|
||||||
|
[[ -z "$line" ]] && continue
|
||||||
|
|
||||||
|
# Extract key-value pairs
|
||||||
|
key=$(echo "$line" | cut -d '=' -f1)
|
||||||
|
value=$(echo "$line" | cut -d '=' -f2-)
|
||||||
|
|
||||||
|
# Override defaults if found in config
|
||||||
|
case "$key" in
|
||||||
|
"resource_group") RESOURCE_GROUP="$value" ;;
|
||||||
|
"cluster_name") AKS_CLUSTER="$value" ;;
|
||||||
|
esac
|
||||||
|
done < ".github/config/deploy_env_vars_${ENV}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Export for other jobs
|
||||||
|
echo "REGISTRY=$REGISTRY" >> $GITHUB_ENV
|
||||||
|
echo "IMAGE_ORG=$IMAGE_ORG" >> $GITHUB_ENV
|
||||||
|
echo "RESOURCE_GROUP=$RESOURCE_GROUP" >> $GITHUB_ENV
|
||||||
|
echo "AKS_CLUSTER=$AKS_CLUSTER" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Grant execute permission for gradlew
|
||||||
|
run: chmod +x gradlew
|
||||||
|
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: |
|
||||||
|
./gradlew build -x test
|
||||||
|
|
||||||
|
- name: SonarQube Analysis & Quality Gate
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
|
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
|
||||||
|
run: |
|
||||||
|
# Check if SonarQube should be skipped
|
||||||
|
SKIP_SONARQUBE="${{ github.event.inputs.SKIP_SONARQUBE || 'true' }}"
|
||||||
|
|
||||||
|
if [[ "$SKIP_SONARQUBE" == "true" ]]; then
|
||||||
|
echo "⏭️ Skipping SonarQube Analysis (SKIP_SONARQUBE=$SKIP_SONARQUBE)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Define services array
|
||||||
|
services=(user meeting stt ai notification)
|
||||||
|
|
||||||
|
# Run tests, coverage reports, and SonarQube analysis for each service
|
||||||
|
for service in "${services[@]}"; do
|
||||||
|
./gradlew :$service:test :$service:jacocoTestReport :$service:sonar \
|
||||||
|
-Dsonar.projectKey=hgzero-$service-${{ steps.determine_env.outputs.environment }} \
|
||||||
|
-Dsonar.projectName=hgzero-$service-${{ steps.determine_env.outputs.environment }} \
|
||||||
|
-Dsonar.host.url=$SONAR_HOST_URL \
|
||||||
|
-Dsonar.token=$SONAR_TOKEN \
|
||||||
|
-Dsonar.java.binaries=build/classes/java/main \
|
||||||
|
-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml \
|
||||||
|
-Dsonar.exclusions=**/config/**,**/entity/**,**/dto/**,**/*Application.class,**/exception/**
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: app-builds
|
||||||
|
path: |
|
||||||
|
user/build/libs/*.jar
|
||||||
|
meeting/build/libs/*.jar
|
||||||
|
stt/build/libs/*.jar
|
||||||
|
ai/build/libs/*.jar
|
||||||
|
notification/build/libs/*.jar
|
||||||
|
|
||||||
|
- name: Set outputs
|
||||||
|
id: set_outputs
|
||||||
|
run: |
|
||||||
|
# Generate timestamp for image tag
|
||||||
|
IMAGE_TAG=$(date +%Y%m%d%H%M%S)
|
||||||
|
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
|
||||||
|
echo "environment=${{ steps.determine_env.outputs.environment }}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
release:
|
||||||
|
name: Build and Push Docker Images
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download build artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: app-builds
|
||||||
|
|
||||||
|
- name: Set environment variables from build job
|
||||||
|
run: |
|
||||||
|
echo "REGISTRY=${{ env.REGISTRY }}" >> $GITHUB_ENV
|
||||||
|
echo "IMAGE_ORG=${{ env.IMAGE_ORG }}" >> $GITHUB_ENV
|
||||||
|
echo "ENVIRONMENT=${{ needs.build.outputs.environment }}" >> $GITHUB_ENV
|
||||||
|
echo "IMAGE_TAG=${{ needs.build.outputs.image_tag }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to Docker Hub (prevent rate limit)
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Login to Azure Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ secrets.ACR_USERNAME }}
|
||||||
|
password: ${{ secrets.ACR_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Build and push Docker images for all services
|
||||||
|
run: |
|
||||||
|
# Define services array
|
||||||
|
services=(user meeting stt ai notification)
|
||||||
|
|
||||||
|
# Build and push each service image
|
||||||
|
for service in "${services[@]}"; do
|
||||||
|
echo "Building and pushing $service..."
|
||||||
|
docker build \
|
||||||
|
--build-arg BUILD_LIB_DIR="$service/build/libs" \
|
||||||
|
--build-arg ARTIFACTORY_FILE="$service.jar" \
|
||||||
|
-f deployment/container/Dockerfile-backend \
|
||||||
|
-t ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/$service:${{ needs.build.outputs.environment }}-${{ needs.build.outputs.image_tag }} .
|
||||||
|
|
||||||
|
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/$service:${{ needs.build.outputs.environment }}-${{ needs.build.outputs.image_tag }}
|
||||||
|
done
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
name: Deploy to Kubernetes
|
||||||
|
needs: [build, release]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set image tag environment variable
|
||||||
|
run: |
|
||||||
|
echo "IMAGE_TAG=${{ needs.build.outputs.image_tag }}" >> $GITHUB_ENV
|
||||||
|
echo "ENVIRONMENT=${{ needs.build.outputs.environment }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Install Azure CLI
|
||||||
|
run: |
|
||||||
|
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
|
||||||
|
|
||||||
|
- name: Azure Login
|
||||||
|
uses: azure/login@v1
|
||||||
|
with:
|
||||||
|
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
||||||
|
|
||||||
|
- name: Setup kubectl
|
||||||
|
uses: azure/setup-kubectl@v3
|
||||||
|
|
||||||
|
- name: Get AKS Credentials
|
||||||
|
run: |
|
||||||
|
az aks get-credentials --resource-group ${{ env.RESOURCE_GROUP }} --name ${{ env.AKS_CLUSTER }} --overwrite-existing
|
||||||
|
|
||||||
|
- name: Create namespace
|
||||||
|
run: |
|
||||||
|
kubectl create namespace ${{ env.NAMESPACE }} --dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
|
- name: Install Kustomize
|
||||||
|
run: |
|
||||||
|
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
|
||||||
|
sudo mv kustomize /usr/local/bin/
|
||||||
|
|
||||||
|
- name: Update Kustomize images and deploy
|
||||||
|
run: |
|
||||||
|
# 환경별 디렉토리로 이동
|
||||||
|
cd .github/kustomize/overlays/${{ env.ENVIRONMENT }}
|
||||||
|
|
||||||
|
# 각 서비스별 이미지 태그 업데이트
|
||||||
|
kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/user:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }}
|
||||||
|
kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/meeting:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }}
|
||||||
|
kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/stt:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }}
|
||||||
|
kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/ai:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }}
|
||||||
|
kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/notification:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }}
|
||||||
|
|
||||||
|
# 매니페스트 적용
|
||||||
|
kubectl apply -k .
|
||||||
|
|
||||||
|
- name: Wait for deployments to be ready
|
||||||
|
run: |
|
||||||
|
echo "Waiting for deployments to be ready..."
|
||||||
|
kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/user --timeout=300s || true
|
||||||
|
kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/meeting --timeout=300s || true
|
||||||
|
kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/stt --timeout=300s || true
|
||||||
|
kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/ai --timeout=300s || true
|
||||||
|
kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/notification --timeout=300s || true
|
||||||
@ -1,454 +0,0 @@
|
|||||||
# 유저스토리 v2.2.0 → v2.3.0 변경사항 보고서
|
|
||||||
|
|
||||||
**작성일**: 2025-10-25
|
|
||||||
**작성자**: 지수 (Product Designer), 민준 (Product Owner)
|
|
||||||
**문서 버전**: 1.0
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 개요
|
|
||||||
|
|
||||||
본 보고서는 AI기반 회의록 작성 및 이력 관리 개선 서비스의 유저스토리 문서가 v2.2.0에서 v2.3.0으로 업데이트되면서 변경된 내용과 그 의미를 분석합니다.
|
|
||||||
|
|
||||||
### 요약 통계
|
|
||||||
|
|
||||||
| 항목 | v2.2.0 | v2.3.0 | 변화 |
|
|
||||||
|------|--------|--------|------|
|
|
||||||
| **유저스토리 수** | 25개 | 27개 | +2개 (+8%) |
|
|
||||||
| **신규 추가** | - | 5개 | UFR-USER-010, UFR-USER-020, UFR-MEET-015, UFR-AI-030, UFR-NOTI-010 |
|
|
||||||
| **삭제/전환** | - | 2개 | AFR-USER-010, AFR-USER-020 → UFR로 전환 |
|
|
||||||
| **AFR 코드** | 2개 | 0개 | -2개 (100% 제거) |
|
|
||||||
| **UFR 코드** | 23개 | 27개 | +4개 (+17%) |
|
|
||||||
| **평균 상세도** | 20-30줄 | 60-100줄 | **약 3배 증가** |
|
|
||||||
| **프로토타입 연계** | 부분적 | 100% (10개 화면) | - |
|
|
||||||
| **표준 형식 적용** | 0% | 100% (27개) | - |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 한눈에 보는 변경사항
|
|
||||||
|
|
||||||
```
|
|
||||||
v2.2.0 (25개) v2.3.0 (27개)
|
|
||||||
┌─────────────────┐ ┌─────────────────┐
|
|
||||||
│ AFR-USER-010 │ ──────────────────>│ UFR-USER-010 ✨ │ (로그인 상세화)
|
|
||||||
│ AFR-USER-020 │ ──────────────────>│ UFR-USER-020 ✨ │ (대시보드 재설계)
|
|
||||||
├─────────────────┤ ├─────────────────┤
|
|
||||||
│ UFR-MEET-010 │ ──────────────────>│ UFR-MEET-010 ✨ │ (회의예약 개선)
|
|
||||||
│ │ │ UFR-MEET-015 🆕 │ (참석자 실시간 초대)
|
|
||||||
│ UFR-MEET-020 │ ──────────────────>│ UFR-MEET-020 ✨ │ (템플릿선택 상세화)
|
|
||||||
│ UFR-MEET-030 │ ──────────────────>│ UFR-MEET-030 ✨ │ (회의시작 4개 탭)
|
|
||||||
│ UFR-MEET-040 │ ──────────────────>│ UFR-MEET-040 ✨ │ (회의종료 3가지 액션)
|
|
||||||
│ UFR-MEET-050 │ ──────────────────>│ UFR-MEET-050 ✨ │ (최종확정 2가지 시나리오)
|
|
||||||
│ UFR-MEET-046 │ ──────────────────>│ UFR-MEET-046 ✨ │ (목록조회 샘플 30개)
|
|
||||||
│ UFR-MEET-047 │ ──────────────────>│ UFR-MEET-047 ✨ │ (상세조회 관련회의록)
|
|
||||||
│ UFR-MEET-055 │ ──────────────────>│ UFR-MEET-055 ✨ │ (회의록수정 3가지 시나리오)
|
|
||||||
├─────────────────┤ ├─────────────────┤
|
|
||||||
│ UFR-AI-010 │ ──────────────────>│ UFR-AI-010 │
|
|
||||||
│ UFR-AI-020 │ ──────────────────>│ UFR-AI-020 │
|
|
||||||
│ │ │ UFR-AI-030 🆕🎯 │ (실시간 AI 제안 - 차별화!)
|
|
||||||
│ UFR-AI-035 │ ──────────────────>│ UFR-AI-035 │
|
|
||||||
│ UFR-AI-036 │ ──────────────────>│ UFR-AI-036 │
|
|
||||||
│ UFR-AI-040 │ ──────────────────>│ UFR-AI-040 │
|
|
||||||
├─────────────────┤ ├─────────────────┤
|
|
||||||
│ UFR-STT-010 │ ──────────────────>│ UFR-STT-010 │
|
|
||||||
│ UFR-STT-020 │ ──────────────────>│ UFR-STT-020 │
|
|
||||||
├─────────────────┤ ├─────────────────┤
|
|
||||||
│ UFR-RAG-010 │ ──────────────────>│ UFR-RAG-010 │
|
|
||||||
│ UFR-RAG-020 │ ──────────────────>│ UFR-RAG-020 │
|
|
||||||
├─────────────────┤ ├─────────────────┤
|
|
||||||
│ UFR-COLLAB-010 │ ──────────────────>│ UFR-COLLAB-010 │
|
|
||||||
│ UFR-COLLAB-020 │ ──────────────────>│ UFR-COLLAB-020 │
|
|
||||||
│ UFR-COLLAB-030 │ ──────────────────>│ UFR-COLLAB-030 │
|
|
||||||
├─────────────────┤ ├─────────────────┤
|
|
||||||
│ UFR-TODO-010 │ ──────────────────>│ UFR-TODO-010 │
|
|
||||||
│ UFR-TODO-030 │ ──────────────────>│ UFR-TODO-030 │
|
|
||||||
│ UFR-TODO-040 │ ──────────────────>│ UFR-TODO-040 │
|
|
||||||
└─────────────────┘ ├─────────────────┤
|
|
||||||
│ UFR-NOTI-010 🆕 │ (알림발송 - 폴링 방식)
|
|
||||||
└─────────────────┘
|
|
||||||
|
|
||||||
범례:
|
|
||||||
🆕 = 완전 신규 추가
|
|
||||||
🎯 = 차별화 핵심 기능
|
|
||||||
✨ = 대폭 개선 (프로토타입 기반 재작성)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 핵심 변경사항
|
|
||||||
|
|
||||||
### 1. 신규 추가된 유저스토리 (5개)
|
|
||||||
|
|
||||||
#### 1.1 UFR-USER-010: 로그인 🆕
|
|
||||||
- **이전**: AFR-USER-010 (간략한 인증 설명)
|
|
||||||
- **변경**: UFR-USER-010으로 전환 및 상세화
|
|
||||||
- **의미**:
|
|
||||||
- 로그인 프로세스 단계별 명시 (Enter 키 동작, 로딩 상태 등)
|
|
||||||
- 예외처리 시나리오 구체화 (사번 미입력, 비밀번호 8자 미만 등)
|
|
||||||
- 프로토타입 `01-로그인.html`과 1:1 매핑
|
|
||||||
|
|
||||||
#### 1.2 UFR-USER-020: 대시보드 🆕
|
|
||||||
- **이전**: AFR-USER-020 (간략한 대시보드 설명)
|
|
||||||
- **변경**: UFR-USER-020으로 전환 및 대폭 확장
|
|
||||||
- **의미**:
|
|
||||||
- 통계 블록, 최근 회의, 나의 Todo, 나의 회의록 위젯 상세 명세
|
|
||||||
- FAB 버튼 2가지 액션 (회의예약/바로 시작) 명확화
|
|
||||||
- 프로토타입 `02-대시보드.html`과 1:1 매핑
|
|
||||||
|
|
||||||
#### 1.3 UFR-MEET-015: 참석자 실시간 초대 🆕
|
|
||||||
- **이전**: 없음
|
|
||||||
- **변경**: 완전 신규 추가
|
|
||||||
- **의미**:
|
|
||||||
- 회의 진행 중 "참석자" 탭에서 실시간으로 참석자 추가 기능
|
|
||||||
- 검색 모달 → 추가 → WebSocket 동기화 → 알림 발송 흐름 명시
|
|
||||||
- **효과**: 회의 진행 중 동적 참석자 관리로 유연성 향상
|
|
||||||
- 프로토타입 `05-회의진행.html`의 "참석자" 탭과 연계
|
|
||||||
|
|
||||||
#### 1.4 UFR-AI-030: 실시간 AI 제안 🆕🎯
|
|
||||||
- **이전**: 없음
|
|
||||||
- **변경**: 완전 신규 추가
|
|
||||||
- **의미**:
|
|
||||||
- **차별화 전략 "지능형 회의 진행 지원" 실현**
|
|
||||||
- STT 텍스트 실시간 분석 → 주요 내용 감지 → AI 제안 카드 생성
|
|
||||||
- 제안 카드에서 메모 탭으로 드래그 앤 드롭으로 추가
|
|
||||||
- **효과**: 회의 중 놓치는 내용 최소화, 차별화 핵심 기능
|
|
||||||
- 프로토타입 `05-회의진행.html`의 "AI 제안" 탭과 연계
|
|
||||||
|
|
||||||
#### 1.5 UFR-NOTI-010: 알림 발송 🆕
|
|
||||||
- **이전**: 없음 (암묵적으로 Meeting Service에서 직접 발송)
|
|
||||||
- **변경**: Notification 서비스의 독립적인 유저스토리로 추가
|
|
||||||
- **의미**:
|
|
||||||
- **알림 아키텍처를 폴링 방식으로 통일**
|
|
||||||
- 1분 간격 폴링 → 이메일 발송 → 최대 3회 재시도
|
|
||||||
- 6가지 알림 유형 명시 (Todo 할당, Todo 완료, 회의 시작, 회의록 확정, 참석자 초대, 회의록 수정)
|
|
||||||
- **효과**: Notification 서비스 독립성 확보, 시스템 안정성 향상
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. 대폭 개선된 유저스토리 (주요 8개)
|
|
||||||
|
|
||||||
#### 2.1 UFR-MEET-010: 회의예약
|
|
||||||
- **변경사항**:
|
|
||||||
- 수행절차 10단계 명시 (FAB 버튼 → 입력 → 저장/완료)
|
|
||||||
- 입력 필드별 상세 명세 (타입, 필수 여부, 최대/최소값, UI 요소)
|
|
||||||
- 임시저장/예약 완료 2가지 시나리오 구분
|
|
||||||
- 예외처리 7가지 추가 (제목 미입력, 과거 날짜, 참석자 미선택 등)
|
|
||||||
- **의미**: 프로토타입 `03-회의예약.html` 기반 전면 재작성
|
|
||||||
|
|
||||||
#### 2.2 UFR-MEET-030: 회의시작
|
|
||||||
- **변경사항**:
|
|
||||||
- 회의 진행 화면 4개 탭 상세 명세 (녹음/메모, 참석자, AI 제안, 안건)
|
|
||||||
- 녹음 시작/일시정지/재시작 플로우 명시
|
|
||||||
- 참석자 상태 표시 (온라인/오프라인/참석중)
|
|
||||||
- 탭별 UI 요소와 인터랙션 상세화
|
|
||||||
- **의미**: 프로토타입 `05-회의진행.html` 4개 탭 구조 반영
|
|
||||||
|
|
||||||
#### 2.3 UFR-MEET-040: 회의종료
|
|
||||||
- **변경사항**:
|
|
||||||
- 회의 종료 후 3가지 액션 명시 (바로 확정, 나중에 확정, 검토 후 확정)
|
|
||||||
- 각 액션별 이동 화면 명확화
|
|
||||||
- 안건 요약 및 검증 상태 표시 추가
|
|
||||||
- **의미**: 프로토타입 `07-회의종료.html` 반영, 사용자 선택권 강화
|
|
||||||
|
|
||||||
#### 2.4 UFR-MEET-050: 최종확정
|
|
||||||
- **변경사항**:
|
|
||||||
- 2가지 시나리오 분리 (검토 후 확정, 회의 종료 화면에서 바로 확정)
|
|
||||||
- 안건별 검증 완료 여부 체크 로직 추가
|
|
||||||
- 미검증 안건 있을 시 확정 불가 정책 명시
|
|
||||||
- **의미**: 회의록 품질 보증 메커니즘 강화
|
|
||||||
|
|
||||||
#### 2.5 UFR-MEET-046: 회의록목록조회
|
|
||||||
- **변경사항**:
|
|
||||||
- 샘플 데이터 30개 명시 (제목, 날짜, 상태, 검증 현황 등)
|
|
||||||
- 필터/정렬 기능 상세화 (기간, 상태, 폴더별)
|
|
||||||
- 상태 배지 5종 추가 (진행중, 검토중, 확정완료 등)
|
|
||||||
- **의미**: 프로토타입 `12-회의록목록조회.html` 반영
|
|
||||||
|
|
||||||
#### 2.6 UFR-MEET-047: 회의록상세조회
|
|
||||||
- **변경사항**:
|
|
||||||
- 관련 회의록 섹션 추가 (AI가 자동 연결한 회의록 3개 표시)
|
|
||||||
- 안건별 검증 상태 표시 추가
|
|
||||||
- 용어 팝업 연계 (UFR-RAG-010) 명시
|
|
||||||
- **의미**: 프로토타입 `10-회의록상세조회.html` 반영, RAG 기능 연계
|
|
||||||
|
|
||||||
#### 2.7 UFR-MEET-055: 회의록수정
|
|
||||||
- **변경사항**:
|
|
||||||
- 3가지 진입 시나리오 명시 (회의종료 화면, 목록 화면, 상세조회 화면)
|
|
||||||
- 실시간 협업 플로우 상세화 (UFR-COLLAB-010, UFR-COLLAB-020 연계)
|
|
||||||
- 수정 저장/임시저장/취소 3가지 액션 구분
|
|
||||||
- **의미**: 프로토타입 `11-회의록수정.html` 반영, 협업 기능 강화
|
|
||||||
|
|
||||||
#### 2.8 UFR-COLLAB-020: 충돌해결
|
|
||||||
- **변경사항**:
|
|
||||||
- 안건 기반 충돌 방지 메커니즘 상세화
|
|
||||||
- 동일 안건 동시 수정 시 경고 표시 및 잠금 정책 명시
|
|
||||||
- 충돌 해결 시나리오 3가지 (대기, 새 안건 작성, 취소)
|
|
||||||
- **의미**: 실시간 협업 안정성 강화
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 유지된 유저스토리 (14개)
|
|
||||||
|
|
||||||
다음 유저스토리들은 v2.2.0과 v2.3.0에서 ID와 핵심 내용이 유지되었습니다:
|
|
||||||
|
|
||||||
- UFR-AI-010 (회의록 자동 작성)
|
|
||||||
- UFR-AI-020 (Todo 자동 추출)
|
|
||||||
- UFR-AI-035 (섹션 AI 요약)
|
|
||||||
- UFR-AI-036 (AI 한줄 요약)
|
|
||||||
- UFR-AI-040 (관련 회의록 연결)
|
|
||||||
- UFR-STT-010 (음성 녹음 인식)
|
|
||||||
- UFR-STT-020 (텍스트 변환)
|
|
||||||
- UFR-RAG-010 (전문용어 감지)
|
|
||||||
- UFR-RAG-020 (맥락 기반 용어 설명)
|
|
||||||
- UFR-COLLAB-010 (회의록 수정 동기화)
|
|
||||||
- UFR-COLLAB-030 (검증 완료)
|
|
||||||
- UFR-TODO-010 (Todo 할당)
|
|
||||||
- UFR-TODO-030 (Todo 완료 처리)
|
|
||||||
- UFR-TODO-040 (Todo 관리)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📈 문서 품질 개선
|
|
||||||
|
|
||||||
### 3.1 유저스토리 형식 표준화
|
|
||||||
|
|
||||||
#### Before (v2.2.0) - 자유 형식
|
|
||||||
```
|
|
||||||
UFR-MEET-010: [회의예약] 회의 생성자로서 | 나는, ...
|
|
||||||
- 시나리오: 회의 예약 및 참석자 초대
|
|
||||||
회의 예약 화면에 접근한 상황에서 | ...
|
|
||||||
|
|
||||||
[입력 요구사항]
|
|
||||||
- 회의 제목: 최대 100자 (필수)
|
|
||||||
...
|
|
||||||
|
|
||||||
[처리 결과]
|
|
||||||
- 회의가 예약됨
|
|
||||||
...
|
|
||||||
|
|
||||||
- M/13
|
|
||||||
```
|
|
||||||
|
|
||||||
#### After (v2.3.0) - 표준 5단계 형식
|
|
||||||
```
|
|
||||||
### UFR-MEET-010: [회의예약] 회의 생성자로서 | 나는, ...
|
|
||||||
|
|
||||||
**수행절차:**
|
|
||||||
1. 대시보드에서 "회의예약" FAB 버튼 클릭
|
|
||||||
2. 회의 제목 입력 (최대 100자)
|
|
||||||
3. 날짜 선택 (오늘 이후 날짜, 달력 UI)
|
|
||||||
...
|
|
||||||
10. "임시저장" 버튼 또는 "예약 완료" 버튼 클릭
|
|
||||||
|
|
||||||
**입력:**
|
|
||||||
- 회의 제목: 텍스트 입력, 필수, 최대 100자, 문자 카운터 표시
|
|
||||||
- 날짜: date 타입, 필수, 오늘 이후 날짜만 선택 가능
|
|
||||||
...
|
|
||||||
|
|
||||||
**출력/결과:**
|
|
||||||
- 예약 완료: "회의가 예약되었습니다" 토스트 메시지, 대시보드로 이동
|
|
||||||
- 임시저장: "임시 저장되었습니다" 토스트 메시지
|
|
||||||
...
|
|
||||||
|
|
||||||
**예외처리:**
|
|
||||||
- 제목 미입력: "회의 제목을 입력해주세요" 토스트, 제목 필드 포커스
|
|
||||||
- 과거 날짜 선택: "과거 날짜는 선택할 수 없습니다" 토스트
|
|
||||||
...
|
|
||||||
|
|
||||||
**관련 유저스토리:**
|
|
||||||
- UFR-USER-020: 대시보드 조회
|
|
||||||
- UFR-MEET-020: 템플릿선택
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.2 개선 효과
|
|
||||||
|
|
||||||
| 섹션 | 개선 효과 |
|
|
||||||
|------|-----------|
|
|
||||||
| **수행절차** | 단계별 명확한 작업 흐름, 개발자가 UI 플로우 이해 가능 |
|
|
||||||
| **입력** | 필드 타입, 검증 규칙, UI 요소 상세 명세, API 명세서 작성 기준 제공 |
|
|
||||||
| **출력/결과** | 성공/실패 시나리오별 응답 명시, 테스트 케이스 작성 기준 제공 |
|
|
||||||
| **예외처리** | 에러 상황별 처리 방법 구체화, QA 시나리오 명확화 |
|
|
||||||
| **관련 유저스토리** | 기능 간 연계성 추적, 통합 테스트 범위 파악 용이 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🏗️ 프로토타입 연계 강화
|
|
||||||
|
|
||||||
v2.3.0에서는 모든 유저스토리가 프로토타입 화면과 명확하게 연계되었습니다.
|
|
||||||
|
|
||||||
| 프로토타입 화면 | 연계 유저스토리 | 상태 |
|
|
||||||
|----------------|----------------|------|
|
|
||||||
| 01-로그인.html | UFR-USER-010 | ✅ 1:1 매핑 |
|
|
||||||
| 02-대시보드.html | UFR-USER-020 | ✅ 1:1 매핑 |
|
|
||||||
| 03-회의예약.html | UFR-MEET-010 | ✅ 1:1 매핑 |
|
|
||||||
| 04-템플릿선택.html | UFR-MEET-020 | ✅ 1:1 매핑 |
|
|
||||||
| 05-회의진행.html | UFR-MEET-030, UFR-MEET-015 (신규), UFR-AI-030 (신규) | ✅ 1:N 매핑 |
|
|
||||||
| 07-회의종료.html | UFR-MEET-040 | ✅ 1:1 매핑 |
|
|
||||||
| 10-회의록상세조회.html | UFR-MEET-047 | ✅ 1:1 매핑 |
|
|
||||||
| 11-회의록수정.html | UFR-MEET-055 | ✅ 1:1 매핑 |
|
|
||||||
| 12-회의록목록조회.html | UFR-MEET-046 | ✅ 1:1 매핑 |
|
|
||||||
| 08-최종확정.html | UFR-MEET-050 | ✅ 1:1 매핑 |
|
|
||||||
|
|
||||||
**결과**: 10개 프로토타입 화면 100% 유저스토리 연계 완료
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔑 핵심 아키텍처 변경
|
|
||||||
|
|
||||||
### 알림 아키텍처: 실시간 → 폴링 방식
|
|
||||||
|
|
||||||
#### Before (v2.2.0)
|
|
||||||
```
|
|
||||||
[Meeting Service] ──(실시간 발송)──> [Notification Service] ──> [Email]
|
|
||||||
↓
|
|
||||||
Todo 할당 발생 → 즉시 이메일 발송
|
|
||||||
```
|
|
||||||
|
|
||||||
**문제점**:
|
|
||||||
- Meeting Service와 Notification Service 간 강한 결합
|
|
||||||
- 이메일 발송 실패 시 Meeting Service에 영향
|
|
||||||
|
|
||||||
#### After (v2.3.0)
|
|
||||||
```
|
|
||||||
[Meeting Service] ──(DB 레코드 생성)──> [Notification 테이블]
|
|
||||||
↓
|
|
||||||
(1분 간격 폴링)
|
|
||||||
↓
|
|
||||||
[Notification Service] ──> [Email]
|
|
||||||
↓
|
|
||||||
(발송 상태 업데이트)
|
|
||||||
```
|
|
||||||
|
|
||||||
**개선 효과**:
|
|
||||||
- ✅ **Notification 서비스 독립성 강화**: 마이크로서비스 간 느슨한 결합
|
|
||||||
- ✅ **시스템 안정성 향상**: 이메일 발송 실패 시 자동 재시도 (최대 3회)
|
|
||||||
- ✅ **확장성 확보**: 폴링 주기 조정으로 트래픽 제어 가능
|
|
||||||
- ✅ **모니터링 용이**: 발송 대기/성공/실패 상태 DB에서 추적
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 💡 변경의 의미와 개선 효과
|
|
||||||
|
|
||||||
### 1. 사용자 경험 (UX) 개선
|
|
||||||
|
|
||||||
| 영역 | 개선 내용 | 효과 |
|
|
||||||
|------|----------|------|
|
|
||||||
| **회의 진행 중 유연성** | UFR-MEET-015 (참석자 실시간 초대) | 회의 중 동적 참석자 관리 가능 |
|
|
||||||
| **회의 중 놓침 방지** | UFR-AI-030 (실시간 AI 제안) 🎯 | 차별화 핵심 기능, 회의 중 주요 내용 실시간 감지 |
|
|
||||||
| **회의 종료 후 선택권** | UFR-MEET-040 (3가지 액션) | 바로 확정/나중에 확정/검토 후 확정 |
|
|
||||||
| **회의록 품질 보증** | UFR-MEET-050 (검증 후 확정) | 미검증 안건 있을 시 확정 불가 정책 |
|
|
||||||
| **실시간 협업 안정성** | UFR-COLLAB-020 (안건 기반 충돌 방지) | 동일 안건 동시 수정 시 경고 및 잠금 |
|
|
||||||
|
|
||||||
### 2. 기능적 개선
|
|
||||||
|
|
||||||
| 영역 | 개선 내용 | 효과 |
|
|
||||||
|------|----------|------|
|
|
||||||
| **알림 시스템 안정성** | UFR-NOTI-010 (폴링 방식) | Notification 서비스 독립성 확보, 재시도 메커니즘 |
|
|
||||||
| **차별화 전략 실현** | UFR-AI-030 (실시간 AI 제안) 🎯 | "지능형 회의 진행 지원" 구체화 |
|
|
||||||
| **프로토타입 정합성** | 10개 화면 100% 매핑 | 기획-디자인-개발 간 일관성 확보 |
|
|
||||||
| **유저스토리 표준화** | 5단계 표준 형식 | 개발 가이드 역할 강화, API 명세서 작성 기준 제공 |
|
|
||||||
|
|
||||||
### 3. 문서화 개선
|
|
||||||
|
|
||||||
| 영역 | 개선 내용 | 효과 |
|
|
||||||
|------|----------|------|
|
|
||||||
| **상세도 3배 증가** | 20-30줄 → 60-100줄 | 개발자가 구현에 필요한 모든 정보 확보 |
|
|
||||||
| **AFR 코드 폐지** | AFR → UFR 통일 | 유저스토리 체계 단순화 |
|
|
||||||
| **예외처리 명시** | 각 유저스토리별 5-7개 예외 시나리오 | QA 테스트 케이스 작성 기준 제공 |
|
|
||||||
| **관련 유저스토리 연계** | 기능 간 의존성 추적 | 통합 테스트 범위 명확화 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 권장 후속 조치
|
|
||||||
|
|
||||||
### 🔴 긴급 (1주 내)
|
|
||||||
|
|
||||||
- [ ] **신규 유저스토리 3개 기반 API 설계**
|
|
||||||
- UFR-MEET-015: 참석자 실시간 초대 API
|
|
||||||
- UFR-AI-030: 실시간 AI 제안 API (SSE 또는 WebSocket)
|
|
||||||
- UFR-NOTI-010: 알림 폴링 및 발송 API
|
|
||||||
|
|
||||||
- [ ] **알림 아키텍처 폴링 방식 반영**
|
|
||||||
- 물리 아키텍처 다이어그램 업데이트
|
|
||||||
- Notification 테이블 스키마 정의
|
|
||||||
- 폴링 스케줄러 설계
|
|
||||||
|
|
||||||
- [ ] **프로토타입 ↔ 유저스토리 1:1 매핑 검증**
|
|
||||||
- 10개 화면별 유저스토리 매핑 검증
|
|
||||||
- 누락된 화면 또는 유저스토리 확인
|
|
||||||
|
|
||||||
### 🟡 중요 (2주 내)
|
|
||||||
|
|
||||||
- [ ] **API 설계서 v2.3.0 기반 전면 업데이트**
|
|
||||||
- 입력/출력 명세 반영 (타입, 필수 여부, 검증 규칙)
|
|
||||||
- 예외처리 시나리오 → HTTP 상태 코드 및 에러 메시지 매핑
|
|
||||||
- 관련 유저스토리 기반 API 그룹핑
|
|
||||||
|
|
||||||
- [ ] **예외처리 시나리오 → 테스트 케이스 전환**
|
|
||||||
- 각 유저스토리의 예외처리 섹션을 테스트 케이스로 변환
|
|
||||||
- 입력 검증 테스트 케이스 작성
|
|
||||||
|
|
||||||
- [ ] **관련 유저스토리 기반 통합 테스트 시나리오 작성**
|
|
||||||
- 예: UFR-MEET-010 → UFR-MEET-020 → UFR-MEET-030 전체 플로우 테스트
|
|
||||||
|
|
||||||
### 🟢 일반 (3주 내)
|
|
||||||
|
|
||||||
- [ ] **유저스토리별 개발 우선순위 재평가**
|
|
||||||
- 신규 유저스토리 3개 우선순위 결정
|
|
||||||
- 차별화 핵심 기능 (UFR-AI-030) 우선 개발 검토
|
|
||||||
|
|
||||||
- [ ] **신규 기능 3개 개발 일정 수립**
|
|
||||||
- UFR-MEET-015: 참석자 실시간 초대
|
|
||||||
- UFR-AI-030: 실시간 AI 제안 (Sprint 목표로 권장)
|
|
||||||
- UFR-NOTI-010: 알림 발송
|
|
||||||
|
|
||||||
- [ ] **프로토타입 기반 개발 가이드 작성**
|
|
||||||
- 프로토타입 → 유저스토리 → API → 컴포넌트 매핑 가이드
|
|
||||||
- 프론트엔드 개발자를 위한 프로토타입 활용 가이드
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔍 핵심 시사점 (Key Takeaways)
|
|
||||||
|
|
||||||
1. **v2.3.0은 프로토타입 분석을 통해 유저스토리를 전면 재정비한 버전**
|
|
||||||
- 10개 프로토타입 화면과 100% 매핑
|
|
||||||
- 실제 UI/UX 플로우를 유저스토리에 반영
|
|
||||||
|
|
||||||
2. **신규 기능 3개 추가로 차별화 강화**
|
|
||||||
- 특히 UFR-AI-030 (실시간 AI 제안)은 차별화 핵심 기능
|
|
||||||
|
|
||||||
3. **알림 아키텍처 폴링 방식으로 통일하여 시스템 안정성 확보**
|
|
||||||
- Notification 서비스 독립성 강화
|
|
||||||
- 재시도 메커니즘으로 안정성 향상
|
|
||||||
|
|
||||||
4. **유저스토리 형식 표준화로 개발 가이드 역할 강화**
|
|
||||||
- 5단계 표준 형식 (수행절차, 입력, 출력/결과, 예외처리, 관련 유저스토리)
|
|
||||||
- API 명세서 및 테스트 케이스 작성 기준 제공
|
|
||||||
|
|
||||||
5. **평균 유저스토리 상세도 약 3배 증가로 품질 대폭 향상**
|
|
||||||
- 개발자가 구현에 필요한 모든 정보 포함
|
|
||||||
- 예외처리, 검증 규칙, UI 요소까지 상세 명시
|
|
||||||
|
|
||||||
6. **기존 24개 유저스토리 ID 승계하여 연속성 유지**
|
|
||||||
- AFR-USER-010 → UFR-USER-010 전환
|
|
||||||
- 기존 설계 문서와의 연계성 유지
|
|
||||||
|
|
||||||
7. **프로토타입-유저스토리 1:1 매핑으로 개발 명확성 확보**
|
|
||||||
- 기획-디자인-개발 간 일관성 확보
|
|
||||||
- 개발 우선순위 및 Sprint 계획 수립 용이
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📎 참고 자료
|
|
||||||
|
|
||||||
- **상세 분석 (JSON)**: `claude/userstory-comparison-v2.2.0-to-v2.3.0.json` (19KB)
|
|
||||||
- **상세 분석 (Markdown)**: `claude/userstory-comparison-v2.2.0-to-v2.3.0.md` (16KB)
|
|
||||||
- **요약 분석**: `claude/userstory-comparison-summary.md` (11KB)
|
|
||||||
- **유저스토리 v2.2.0 백업**: `design/userstory_v2.2.0_backup.md`
|
|
||||||
- **유저스토리 v2.3.0 현재**: `design/userstory.md`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**보고서 작성**: 지수 (Product Designer), 민준 (Product Owner)
|
|
||||||
**분석 일시**: 2025-10-25
|
|
||||||
**문서 버전**: 1.0
|
|
||||||
@ -60,7 +60,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.meeting-title {
|
.meeting-title {
|
||||||
font-size: var(--font-large);
|
font-size: var(--font-h2);
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
color: var(--gray-800);
|
color: var(--gray-800);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -100,7 +100,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.recording-time {
|
.recording-time {
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,9 +173,9 @@
|
|||||||
.info-row {
|
.info-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
gap: var(--space-sm);
|
gap: 0;
|
||||||
padding: var(--space-xs) 0;
|
padding: var(--space-sm) 0;
|
||||||
border-bottom: 1px solid var(--gray-300);
|
border-bottom: 1px solid var(--gray-300);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,14 +186,28 @@
|
|||||||
.info-label {
|
.info-label {
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
color: var(--gray-600);
|
color: var(--gray-600);
|
||||||
font-size: var(--font-caption);
|
font-size: var(--font-small);
|
||||||
min-width: 60px;
|
width: 70px;
|
||||||
|
min-width: 70px;
|
||||||
|
max-width: 70px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
padding-right: var(--space-sm);
|
||||||
|
border-right: 1px solid var(--gray-300);
|
||||||
|
margin-right: var(--space-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 데스크톱에서 라벨 폭 확장 */
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.info-label {
|
||||||
|
width: 100px;
|
||||||
|
min-width: 100px;
|
||||||
|
max-width: 100px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-value {
|
.info-value {
|
||||||
color: var(--gray-800);
|
color: var(--gray-800);
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
@ -229,7 +243,7 @@
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
color: var(--gray-600);
|
color: var(--gray-600);
|
||||||
transition: all var(--transition-fast);
|
transition: all var(--transition-fast);
|
||||||
@ -289,13 +303,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.participant-section-title {
|
.participant-section-title {
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.participant-count {
|
.participant-count {
|
||||||
font-size: var(--font-caption);
|
font-size: var(--font-small);
|
||||||
color: var(--gray-600);
|
color: var(--gray-600);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,13 +321,13 @@
|
|||||||
|
|
||||||
.invite-input {
|
.invite-input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
padding: var(--space-sm);
|
padding: var(--space-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.invite-btn {
|
.invite-btn {
|
||||||
padding: var(--space-sm) var(--space-md);
|
padding: var(--space-sm) var(--space-md);
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
@ -337,13 +351,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.participant-name {
|
.participant-name {
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
color: var(--gray-800);
|
color: var(--gray-800);
|
||||||
}
|
}
|
||||||
|
|
||||||
.participant-email {
|
.participant-email {
|
||||||
font-size: var(--font-caption);
|
font-size: var(--font-small);
|
||||||
color: var(--gray-500);
|
color: var(--gray-500);
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -360,7 +374,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.memo-input-label {
|
.memo-input-label {
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
color: var(--gray-700);
|
color: var(--gray-700);
|
||||||
margin-bottom: var(--space-xs);
|
margin-bottom: var(--space-xs);
|
||||||
@ -373,7 +387,7 @@
|
|||||||
padding: var(--space-sm);
|
padding: var(--space-sm);
|
||||||
border: 1px solid var(--gray-300);
|
border: 1px solid var(--gray-300);
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
@ -388,11 +402,11 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: var(--space-xs);
|
margin-top: var(--space-xs);
|
||||||
padding: var(--space-sm);
|
padding: var(--space-sm);
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ai-suggestion-list-title {
|
.ai-suggestion-list-title {
|
||||||
font-size: var(--font-body);
|
font-size: var(--font-h3);
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
color: var(--gray-900);
|
color: var(--gray-900);
|
||||||
margin-bottom: var(--space-sm);
|
margin-bottom: var(--space-sm);
|
||||||
@ -416,7 +430,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ai-suggestion-time {
|
.ai-suggestion-time {
|
||||||
font-size: var(--font-caption);
|
font-size: var(--font-small);
|
||||||
color: var(--gray-500);
|
color: var(--gray-500);
|
||||||
font-weight: var(--font-weight-regular);
|
font-weight: var(--font-weight-regular);
|
||||||
}
|
}
|
||||||
@ -446,7 +460,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ai-suggestion-text {
|
.ai-suggestion-text {
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
color: var(--gray-700);
|
color: var(--gray-700);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
@ -460,13 +474,13 @@
|
|||||||
|
|
||||||
.term-search-input {
|
.term-search-input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
padding: var(--space-sm);
|
padding: var(--space-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.term-search-btn {
|
.term-search-btn {
|
||||||
padding: var(--space-sm) var(--space-md);
|
padding: var(--space-sm) var(--space-md);
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
@ -493,7 +507,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.term-name {
|
.term-name {
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -512,14 +526,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.term-definition {
|
.term-definition {
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
color: var(--gray-700);
|
color: var(--gray-700);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
margin-bottom: var(--space-xs);
|
margin-bottom: var(--space-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
.term-context {
|
.term-context {
|
||||||
font-size: 11px;
|
font-size: var(--font-small);
|
||||||
color: var(--gray-500);
|
color: var(--gray-500);
|
||||||
padding-top: var(--space-xs);
|
padding-top: var(--space-xs);
|
||||||
border-top: 1px dashed #D0D0D0;
|
border-top: 1px dashed #D0D0D0;
|
||||||
@ -546,7 +560,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.related-doc-title {
|
.related-doc-title {
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
margin-bottom: var(--space-xs);
|
margin-bottom: var(--space-xs);
|
||||||
@ -555,7 +569,7 @@
|
|||||||
.related-doc-meta {
|
.related-doc-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--space-xs);
|
gap: var(--space-xs);
|
||||||
font-size: var(--font-caption);
|
font-size: var(--font-small);
|
||||||
color: var(--gray-600);
|
color: var(--gray-600);
|
||||||
margin-bottom: var(--space-xs);
|
margin-bottom: var(--space-xs);
|
||||||
}
|
}
|
||||||
@ -566,7 +580,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.related-doc-text {
|
.related-doc-text {
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
color: var(--gray-700);
|
color: var(--gray-700);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -107,11 +107,41 @@
|
|||||||
margin-bottom: var(--space-md);
|
margin-bottom: var(--space-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 회의 제목 컨테이너 */
|
||||||
|
.meeting-title-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-sm);
|
||||||
|
margin-bottom: var(--space-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 배지 영역 (배지 + 크라운) */
|
||||||
|
.meeting-badges {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 회의 제목 */
|
||||||
.meeting-basic-info h2 {
|
.meeting-basic-info h2 {
|
||||||
font-size: var(--font-h2);
|
font-size: var(--font-h2);
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
color: var(--gray-900);
|
color: var(--gray-900);
|
||||||
margin-bottom: var(--space-sm);
|
margin: 0;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 데스크톱: 기존 가로 배치 유지 */
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.meeting-title-container {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.meeting-basic-info h2 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-row {
|
.info-row {
|
||||||
@ -165,23 +195,6 @@
|
|||||||
.participant {
|
.participant {
|
||||||
width: calc(50% - var(--space-md) / 2);
|
width: calc(50% - var(--space-md) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 통계 그리드: 모바일에서도 4열 유지, gap만 축소 */
|
|
||||||
.stats-grid {
|
|
||||||
gap: var(--space-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-item {
|
|
||||||
padding: var(--space-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-value {
|
|
||||||
font-size: var(--font-base);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-label {
|
|
||||||
font-size: var(--font-xs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 회의록 섹션 */
|
/* 회의록 섹션 */
|
||||||
@ -279,22 +292,6 @@
|
|||||||
margin-bottom: var(--space-md);
|
margin-bottom: var(--space-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.reference-item {
|
|
||||||
background: var(--white);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
padding: var(--space-sm);
|
|
||||||
margin-bottom: var(--space-sm);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reference-item:hover {
|
|
||||||
box-shadow: var(--shadow-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reference-item:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reference-header {
|
.reference-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -310,7 +307,7 @@
|
|||||||
|
|
||||||
.reference-title {
|
.reference-title {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-body);
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
color: var(--gray-900);
|
color: var(--gray-900);
|
||||||
}
|
}
|
||||||
@ -338,7 +335,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.reference-meta {
|
.reference-meta {
|
||||||
font-size: var(--font-caption);
|
font-size: var(--font-small);
|
||||||
color: var(--gray-500);
|
color: var(--gray-500);
|
||||||
margin-bottom: var(--space-xs);
|
margin-bottom: var(--space-xs);
|
||||||
}
|
}
|
||||||
@ -405,10 +402,11 @@
|
|||||||
color: var(--white);
|
color: var(--white);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 통계 그리드 - 모바일 기본 (2x2) */
|
||||||
.stats-grid {
|
.stats-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
gap: var(--space-md);
|
gap: var(--space-sm);
|
||||||
margin-top: var(--space-md);
|
margin-top: var(--space-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,6 +429,14 @@
|
|||||||
color: var(--gray-500);
|
color: var(--gray-500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 데스크톱: 1x4 그리드 */
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.stats-grid {
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: var(--space-md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 결정사항 카드 */
|
/* 결정사항 카드 */
|
||||||
.decision-card {
|
.decision-card {
|
||||||
background: var(--white);
|
background: var(--white);
|
||||||
@ -462,7 +468,7 @@
|
|||||||
border-top: 1px solid var(--gray-300);
|
border-top: 1px solid var(--gray-300);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Todo 진행상황 */
|
/* Todo 리스트 */
|
||||||
.todo-filters {
|
.todo-filters {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--space-sm);
|
gap: var(--space-sm);
|
||||||
@ -523,7 +529,64 @@
|
|||||||
margin-bottom: var(--space-xs);
|
margin-bottom: var(--space-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Todo 진행상황 - 09-Todo관리 스타일 적용 */
|
/* Todo 리스트 - 단순 조회 스타일 */
|
||||||
|
.simple-todo-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-todo-item {
|
||||||
|
position: relative;
|
||||||
|
padding: var(--space-md);
|
||||||
|
background: var(--white);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
border: 1px solid var(--gray-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-todo-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: var(--space-sm);
|
||||||
|
margin-bottom: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-todo-title {
|
||||||
|
flex: 1;
|
||||||
|
font-size: var(--font-body);
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
color: var(--gray-900);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-todo-edit-btn {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
font-size: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px;
|
||||||
|
color: var(--gray-500);
|
||||||
|
transition: all var(--transition-fast);
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-todo-edit-btn:hover {
|
||||||
|
color: var(--primary);
|
||||||
|
background: var(--primary-light);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-todo-meta {
|
||||||
|
font-size: var(--font-small);
|
||||||
|
color: var(--gray-600);
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-md);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Todo 리스트 - 09-Todo관리 스타일 적용 */
|
||||||
.todo-filters {
|
.todo-filters {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--space-sm);
|
gap: var(--space-sm);
|
||||||
@ -688,6 +751,24 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 대시보드 탭의 관련회의록 카드 스타일 강화 */
|
||||||
|
.card .reference-item {
|
||||||
|
border: 1px solid var(--gray-200) !important;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08) !important;
|
||||||
|
margin-bottom: var(--space-sm) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .reference-item:hover {
|
||||||
|
border-color: var(--primary) !important;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.12) !important;
|
||||||
|
transform: translateY(-1px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .reference-item:active {
|
||||||
|
transform: translateY(0) !important;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08) !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* 모바일 화면에서 관련회의록 왼쪽 정렬 */
|
/* 모바일 화면에서 관련회의록 왼쪽 정렬 */
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.reference-item {
|
.reference-item {
|
||||||
@ -738,10 +819,14 @@
|
|||||||
<!-- 기본 정보 카드 -->
|
<!-- 기본 정보 카드 -->
|
||||||
<div class="info-card">
|
<div class="info-card">
|
||||||
<div class="meeting-basic-info">
|
<div class="meeting-basic-info">
|
||||||
<div id="meeting-title-container" style="display: flex; align-items: center; gap: var(--space-sm); margin-bottom: var(--space-sm);">
|
<div class="meeting-title-container">
|
||||||
<span class="badge badge-complete">확정완료</span>
|
<!-- 배지 + 크라운 영역 -->
|
||||||
<!-- 생성자일 경우 👑 아이콘이 JavaScript로 추가됨 -->
|
<div class="meeting-badges" id="meeting-badges">
|
||||||
<h2 style="margin: 0;">2025년 1분기 제품 기획 회의</h2>
|
<span class="badge badge-complete">확정완료</span>
|
||||||
|
<!-- 생성자일 경우 👑 아이콘이 JavaScript로 추가됨 -->
|
||||||
|
</div>
|
||||||
|
<!-- 회의 제목 -->
|
||||||
|
<h2>2025년 1분기 제품 기획 회의</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
<span class="info-icon">📅</span>
|
<span class="info-icon">📅</span>
|
||||||
@ -757,7 +842,7 @@
|
|||||||
<div class="participant">
|
<div class="participant">
|
||||||
<div class="avatar avatar-green">김</div>
|
<div class="avatar avatar-green">김</div>
|
||||||
<span class="participant-name">김민준</span>
|
<span class="participant-name">김민준</span>
|
||||||
<span class="role-badge">작성자</span>
|
<span class="role-badge">생성자</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="participant">
|
<div class="participant">
|
||||||
<div class="avatar avatar-blue">박</div>
|
<div class="avatar avatar-blue">박</div>
|
||||||
@ -1044,60 +1129,65 @@
|
|||||||
|
|
||||||
<!-- Todo 단순 조회 (MVP 스코프 축소 v1.5.1) -->
|
<!-- Todo 단순 조회 (MVP 스코프 축소 v1.5.1) -->
|
||||||
<div class="card mb-lg">
|
<div class="card mb-lg">
|
||||||
<h3 class="card-title">📋 Todo 진행상황</h3>
|
<h3 class="card-title">📋 Todo 리스트</h3>
|
||||||
<p style="font-size: var(--font-small); color: var(--gray-600); margin-bottom: var(--space-md);">
|
<p style="font-size: var(--font-small); color: var(--gray-600); margin-bottom: var(--space-md);">
|
||||||
Todo 항목은 조회만 가능합니다. 제목, 담당자, 마감일 정보만 표시됩니다.
|
Todo 항목은 조회만 가능합니다. 제목, 담당자, 마감일 정보만 표시됩니다.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Todo 단순 조회 리스트 (제목 + 담당자 + 마감일만 표시) -->
|
<!-- Todo 단순 조회 리스트 (제목 + 담당자 + 마감일 + 수정 버튼) -->
|
||||||
<div style="display: flex; flex-direction: column; gap: var(--space-sm);">
|
<div class="simple-todo-list">
|
||||||
<div style="padding: var(--space-md); background: var(--gray-50); border-radius: var(--radius-md); border: 1px solid var(--gray-200);">
|
<div class="simple-todo-item">
|
||||||
<div style="font-weight: var(--font-weight-medium); color: var(--gray-900); margin-bottom: var(--space-xs);">
|
<div class="simple-todo-header">
|
||||||
데이터베이스 스키마 설계
|
<div class="simple-todo-title">데이터베이스 스키마 설계</div>
|
||||||
|
<button class="simple-todo-edit-btn" onclick="editTodo(1)" title="수정">✏️</button>
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size: var(--font-small); color: var(--gray-600);">
|
<div class="simple-todo-meta">
|
||||||
<span>👤 이준호</span>
|
<span>👤 이준호</span>
|
||||||
<span style="margin-left: var(--space-md);">📅 2025-10-20</span>
|
<span>📅 2025-10-20</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="padding: var(--space-md); background: var(--gray-50); border-radius: var(--radius-md); border: 1px solid var(--gray-200);">
|
<div class="simple-todo-item">
|
||||||
<div style="font-weight: var(--font-weight-medium); color: var(--gray-900); margin-bottom: var(--space-xs);">
|
<div class="simple-todo-header">
|
||||||
API 명세서 작성
|
<div class="simple-todo-title">API 명세서 작성</div>
|
||||||
|
<button class="simple-todo-edit-btn" onclick="editTodo(2)" title="수정">✏️</button>
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size: var(--font-small); color: var(--gray-600);">
|
<div class="simple-todo-meta">
|
||||||
<span>👤 이준호</span>
|
<span>👤 이준호</span>
|
||||||
<span style="margin-left: var(--space-md);">📅 2025-10-23</span>
|
<span>📅 2025-10-23</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="padding: var(--space-md); background: var(--gray-50); border-radius: var(--radius-md); border: 1px solid var(--gray-200);">
|
<div class="simple-todo-item">
|
||||||
<div style="font-weight: var(--font-weight-medium); color: var(--gray-900); margin-bottom: var(--space-xs);">
|
<div class="simple-todo-header">
|
||||||
예산 편성안 검토
|
<div class="simple-todo-title">예산 편성안 검토</div>
|
||||||
|
<button class="simple-todo-edit-btn" onclick="editTodo(3)" title="수정">✏️</button>
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size: var(--font-small); color: var(--gray-600);">
|
<div class="simple-todo-meta">
|
||||||
<span>👤 김민준</span>
|
<span>👤 김민준</span>
|
||||||
<span style="margin-left: var(--space-md);">📅 2025-10-22</span>
|
<span>📅 2025-10-22</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="padding: var(--space-md); background: var(--gray-50); border-radius: var(--radius-md); border: 1px solid var(--gray-200);">
|
<div class="simple-todo-item">
|
||||||
<div style="font-weight: var(--font-weight-medium); color: var(--gray-900); margin-bottom: var(--space-xs);">
|
<div class="simple-todo-header">
|
||||||
UI 프로토타입 디자인
|
<div class="simple-todo-title">UI 프로토타입 디자인</div>
|
||||||
|
<button class="simple-todo-edit-btn" onclick="editTodo(4)" title="수정">✏️</button>
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size: var(--font-small); color: var(--gray-600);">
|
<div class="simple-todo-meta">
|
||||||
<span>👤 최유진</span>
|
<span>👤 최유진</span>
|
||||||
<span style="margin-left: var(--space-md);">📅 2025-10-28</span>
|
<span>📅 2025-10-28</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="padding: var(--space-md); background: var(--gray-50); border-radius: var(--radius-md); border: 1px solid var(--gray-200);">
|
<div class="simple-todo-item">
|
||||||
<div style="font-weight: var(--font-weight-medium); color: var(--gray-900); margin-bottom: var(--space-xs);">
|
<div class="simple-todo-header">
|
||||||
사용자 피드백 분석
|
<div class="simple-todo-title">사용자 피드백 분석</div>
|
||||||
|
<button class="simple-todo-edit-btn" onclick="editTodo(5)" title="수정">✏️</button>
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size: var(--font-small); color: var(--gray-600);">
|
<div class="simple-todo-meta">
|
||||||
<span>👤 김민준</span>
|
<span>👤 김민준</span>
|
||||||
<span style="margin-left: var(--space-md);">📅 2025-10-19</span>
|
<span>📅 2025-10-19</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1113,7 +1203,7 @@
|
|||||||
<span class="reference-title">AI 기능 개선 회의</span>
|
<span class="reference-title">AI 기능 개선 회의</span>
|
||||||
<span class="relevance-badge relevance-high">92%</span>
|
<span class="relevance-badge relevance-high">92%</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="reference-meta">2025-10-23 15:00 · 이준호</div>
|
<div class="reference-meta">이준호 · 2025-10-23 15:00</div>
|
||||||
<div class="reference-summary">
|
<div class="reference-summary">
|
||||||
AI 요약 정확도 개선 방안 논의. BERT 모델 도입 및 학습 데이터 확보 계획 수립.
|
AI 요약 정확도 개선 방안 논의. BERT 모델 도입 및 학습 데이터 확보 계획 수립.
|
||||||
</div>
|
</div>
|
||||||
@ -1125,7 +1215,7 @@
|
|||||||
<span class="reference-title">개발 리소스 계획 회의</span>
|
<span class="reference-title">개발 리소스 계획 회의</span>
|
||||||
<span class="relevance-badge relevance-medium">88%</span>
|
<span class="relevance-badge relevance-medium">88%</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="reference-meta">2025-10-22 11:00 · 김민준</div>
|
<div class="reference-meta">김민준 · 2025-10-22 11:00</div>
|
||||||
<div class="reference-summary">
|
<div class="reference-summary">
|
||||||
Q4 개발 리소스 현황 및 배분 계획. 신규 프로젝트 우선순위 협의.
|
Q4 개발 리소스 현황 및 배분 계획. 신규 프로젝트 우선순위 협의.
|
||||||
</div>
|
</div>
|
||||||
@ -1137,7 +1227,7 @@
|
|||||||
<span class="reference-title">경쟁사 분석 회의</span>
|
<span class="reference-title">경쟁사 분석 회의</span>
|
||||||
<span class="relevance-badge relevance-medium">78%</span>
|
<span class="relevance-badge relevance-medium">78%</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="reference-meta">2025-10-20 10:00 · 박서연</div>
|
<div class="reference-meta">박서연 · 2025-10-20 10:00</div>
|
||||||
<div class="reference-summary">
|
<div class="reference-summary">
|
||||||
경쟁사 A, B, C 분석 결과. 우리의 차별점은 실시간 협업 및 검증 기능.
|
경쟁사 A, B, C 분석 결과. 우리의 차별점은 실시간 협업 및 검증 기능.
|
||||||
</div>
|
</div>
|
||||||
@ -1151,6 +1241,33 @@
|
|||||||
<button class="btn btn-secondary" onclick="navigateTo('11-회의록수정.html')">수정</button>
|
<button class="btn btn-secondary" onclick="navigateTo('11-회의록수정.html')">수정</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Todo 추가 모달 -->
|
||||||
|
<div class="modal-overlay" id="addTodoModal">
|
||||||
|
<div class="modal">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3 class="modal-title">Todo 추가</h3>
|
||||||
|
<button class="modal-close">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Todo 내용</label>
|
||||||
|
<input type="text" class="form-control" placeholder="할 일을 입력하세요">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">마감일</label>
|
||||||
|
<div class="date-input-wrapper">
|
||||||
|
<input type="date" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-ghost" onclick="closeModal('addTodoModal')">취소</button>
|
||||||
|
<button class="btn btn-primary" onclick="addTodo()">추가</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Todo 편집 모달 -->
|
<!-- Todo 편집 모달 -->
|
||||||
<div class="modal-overlay" id="editTodoModal">
|
<div class="modal-overlay" id="editTodoModal">
|
||||||
<div class="modal">
|
<div class="modal">
|
||||||
@ -1159,6 +1276,7 @@
|
|||||||
<button class="modal-close" onclick="closeModal('editTodoModal')">×</button>
|
<button class="modal-close" onclick="closeModal('editTodoModal')">×</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
<input type="hidden" id="editTodoId">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Todo 제목 <span class="text-error">*</span></label>
|
<label class="form-label">Todo 제목 <span class="text-error">*</span></label>
|
||||||
<input type="text" id="editTodoTitle" class="form-control" placeholder="할 일을 입력하세요">
|
<input type="text" id="editTodoTitle" class="form-control" placeholder="할 일을 입력하세요">
|
||||||
@ -1181,14 +1299,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="form-hint">📅 마감일 변경 시 캘린더가 자동 업데이트됩니다</p>
|
<p class="form-hint">📅 마감일 변경 시 캘린더가 자동 업데이트됩니다</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label">우선순위 <span class="text-error">*</span></label>
|
|
||||||
<select id="editTodoPriority" class="form-control">
|
|
||||||
<option value="high">높음</option>
|
|
||||||
<option value="medium">보통</option>
|
|
||||||
<option value="low">낮음</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<!-- 권한 안내 (동적 메시지) -->
|
<!-- 권한 안내 (동적 메시지) -->
|
||||||
<div class="alert alert-info" id="editTodoPermissionInfo">
|
<div class="alert alert-info" id="editTodoPermissionInfo">
|
||||||
<span class="material-icons" style="font-size: 20px;">info</span>
|
<span class="material-icons" style="font-size: 20px;">info</span>
|
||||||
@ -1612,6 +1722,136 @@
|
|||||||
if (filterCompletedCount) filterCompletedCount.textContent = completed;
|
if (filterCompletedCount) filterCompletedCount.textContent = completed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 회의 생성자 여부 확인
|
||||||
|
* @param {string} meetingId - 회의 ID
|
||||||
|
* @param {string} userName - 사용자 이름
|
||||||
|
* @returns {boolean} 회의 생성자 여부
|
||||||
|
*/
|
||||||
|
function checkIfUserIsCreator(meetingId, userName) {
|
||||||
|
// 실제로는 서버 API를 호출하여 확인
|
||||||
|
// 프로토타입에서는 샘플 데이터로 시뮬레이션
|
||||||
|
const meetingCreators = {
|
||||||
|
'meeting-001': '김민준',
|
||||||
|
'meeting-002': '이서연',
|
||||||
|
'meeting-003': '박준호'
|
||||||
|
};
|
||||||
|
|
||||||
|
return meetingCreators[meetingId] === userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Todo 수정 함수 (index 기반)
|
||||||
|
* @param {number} index - Todo 인덱스 (1-based)
|
||||||
|
*/
|
||||||
|
function editTodo(index) {
|
||||||
|
// 1-based index를 0-based로 변환
|
||||||
|
const todo = meetingTodos[index - 1];
|
||||||
|
if (!todo) {
|
||||||
|
showToast('Todo를 찾을 수 없습니다', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 모달에 데이터 채우기
|
||||||
|
document.getElementById('editTodoId').value = index;
|
||||||
|
document.getElementById('editTodoTitle').value = todo.title;
|
||||||
|
|
||||||
|
// 담당자 처리 (assignee가 객체인 경우 name 속성 사용)
|
||||||
|
const assigneeName = typeof todo.assignee === 'object' ? todo.assignee.name : todo.assignee;
|
||||||
|
document.getElementById('editTodoAssignee').value = assigneeName;
|
||||||
|
|
||||||
|
document.getElementById('editTodoDueDate').value = todo.dueDate;
|
||||||
|
|
||||||
|
// 회의 생성자 여부 확인
|
||||||
|
const currentUser = '김민준'; // 현재 로그인 사용자
|
||||||
|
const isCreator = checkIfUserIsCreator(CURRENT_MEETING_ID, currentUser);
|
||||||
|
|
||||||
|
// 담당자 필드 표시 여부 결정
|
||||||
|
const assigneeGroup = document.getElementById('editTodoAssigneeGroup');
|
||||||
|
const permissionText = document.getElementById('editTodoPermissionText');
|
||||||
|
|
||||||
|
if (isCreator) {
|
||||||
|
// 회의 생성자: 담당자 변경 가능
|
||||||
|
assigneeGroup.style.display = 'block';
|
||||||
|
permissionText.textContent = '회의 생성자로서 모든 항목을 수정할 수 있습니다.';
|
||||||
|
} else {
|
||||||
|
// 일반 담당자: 담당자 변경 불가
|
||||||
|
assigneeGroup.style.display = 'none';
|
||||||
|
permissionText.textContent = '본인에게 할당된 Todo만 수정할 수 있습니다. 담당자는 변경할 수 없습니다.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 모달 열기
|
||||||
|
openModal('editTodoModal');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Todo 수정 저장 (index 기반)
|
||||||
|
*/
|
||||||
|
function saveTodoEdit() {
|
||||||
|
const index = parseInt(document.getElementById('editTodoId').value);
|
||||||
|
const title = document.getElementById('editTodoTitle').value.trim();
|
||||||
|
const assignee = document.getElementById('editTodoAssignee').value.trim();
|
||||||
|
const dueDate = document.getElementById('editTodoDueDate').value;
|
||||||
|
|
||||||
|
if (!title) {
|
||||||
|
showToast('제목을 입력해주세요', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!assignee) {
|
||||||
|
showToast('담당자를 입력해주세요', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dueDate) {
|
||||||
|
showToast('마감일을 선택해주세요', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1-based index를 0-based로 변환하여 Todo 업데이트
|
||||||
|
const todoIndex = index - 1;
|
||||||
|
if (todoIndex >= 0 && todoIndex < meetingTodos.length) {
|
||||||
|
const todo = meetingTodos[todoIndex];
|
||||||
|
const oldAssignee = typeof todo.assignee === 'object' ? todo.assignee.name : todo.assignee;
|
||||||
|
const oldDueDate = todo.dueDate;
|
||||||
|
|
||||||
|
// Todo 업데이트 (실제로는 API 호출)
|
||||||
|
todo.title = title;
|
||||||
|
|
||||||
|
// assignee가 객체인 경우 name 속성만 업데이트
|
||||||
|
if (typeof todo.assignee === 'object') {
|
||||||
|
todo.assignee.name = assignee;
|
||||||
|
} else {
|
||||||
|
todo.assignee = assignee;
|
||||||
|
}
|
||||||
|
|
||||||
|
todo.dueDate = dueDate;
|
||||||
|
|
||||||
|
showToast('Todo가 수정되었습니다', 'success');
|
||||||
|
|
||||||
|
// 담당자 변경 시 알림
|
||||||
|
if (oldAssignee !== assignee) {
|
||||||
|
setTimeout(() => {
|
||||||
|
showToast(`${oldAssignee}와 ${assignee}에게 알림이 전송되었습니다`, 'info');
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 마감일 변경 시 알림
|
||||||
|
if (oldDueDate !== dueDate) {
|
||||||
|
setTimeout(() => {
|
||||||
|
showToast('캘린더가 업데이트되었습니다', 'info');
|
||||||
|
}, oldAssignee !== assignee ? 2000 : 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closeModal('editTodoModal');
|
||||||
|
|
||||||
|
// 페이지 새로고침 (실제로는 해당 Todo 항목만 업데이트)
|
||||||
|
setTimeout(() => {
|
||||||
|
location.reload();
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 페이지 초기화
|
* 페이지 초기화
|
||||||
*/
|
*/
|
||||||
@ -1621,13 +1861,13 @@
|
|||||||
const isCreator = checkIfUserIsCreator(CURRENT_MEETING_ID, currentUser);
|
const isCreator = checkIfUserIsCreator(CURRENT_MEETING_ID, currentUser);
|
||||||
|
|
||||||
if (isCreator) {
|
if (isCreator) {
|
||||||
const titleContainer = document.getElementById('meeting-title-container');
|
const badgesContainer = document.getElementById('meeting-badges');
|
||||||
const badge = titleContainer.querySelector('.badge');
|
|
||||||
const crownIcon = document.createElement('span');
|
const crownIcon = document.createElement('span');
|
||||||
crownIcon.textContent = '👑';
|
crownIcon.textContent = '👑';
|
||||||
crownIcon.style.fontSize = '24px';
|
crownIcon.style.fontSize = '20px';
|
||||||
// badge 다음에 👑 삽입
|
crownIcon.title = '회의 생성자';
|
||||||
badge.insertAdjacentElement('afterend', crownIcon);
|
// 배지 영역에 크라운 추가
|
||||||
|
badgesContainer.appendChild(crownIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTodoProgress();
|
updateTodoProgress();
|
||||||
|
|||||||
@ -174,6 +174,14 @@
|
|||||||
.reference-item {
|
.reference-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-right: 40px; /* 삭제 버튼 공간 확보 */
|
padding-right: 40px; /* 삭제 버튼 공간 확보 */
|
||||||
|
cursor: default; /* 카드 전체는 클릭 불가 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 수정 페이지에서는 카드 hover 효과 제거 (삭제 버튼만 클릭 가능) */
|
||||||
|
.reference-item:hover {
|
||||||
|
transform: none;
|
||||||
|
border-color: var(--gray-200);
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.remove-btn {
|
.remove-btn {
|
||||||
@ -399,7 +407,7 @@
|
|||||||
<div class="ai-summary-edit">
|
<div class="ai-summary-edit">
|
||||||
<div class="ai-summary-header">
|
<div class="ai-summary-header">
|
||||||
<span class="ai-summary-label">💡 AI 요약</span>
|
<span class="ai-summary-label">💡 AI 요약</span>
|
||||||
<button class="btn-primary btn-sm" onclick="regenerateSummary(1)">AI 재생성</button>
|
<button class="btn btn-primary btn-sm" onclick="regenerateSummary(1)">AI 재생성</button>
|
||||||
</div>
|
</div>
|
||||||
<textarea
|
<textarea
|
||||||
class="ai-summary-textarea"
|
class="ai-summary-textarea"
|
||||||
@ -492,7 +500,7 @@
|
|||||||
<div class="ai-summary-edit">
|
<div class="ai-summary-edit">
|
||||||
<div class="ai-summary-header">
|
<div class="ai-summary-header">
|
||||||
<span class="ai-summary-label">💡 AI 요약</span>
|
<span class="ai-summary-label">💡 AI 요약</span>
|
||||||
<button class="btn-primary btn-sm" onclick="regenerateSummary(2)">AI 재생성</button>
|
<button class="btn btn-primary btn-sm" onclick="regenerateSummary(2)">AI 재생성</button>
|
||||||
</div>
|
</div>
|
||||||
<textarea
|
<textarea
|
||||||
class="ai-summary-textarea"
|
class="ai-summary-textarea"
|
||||||
@ -569,7 +577,7 @@
|
|||||||
<div class="ai-summary-edit">
|
<div class="ai-summary-edit">
|
||||||
<div class="ai-summary-header">
|
<div class="ai-summary-header">
|
||||||
<span class="ai-summary-label">💡 AI 요약</span>
|
<span class="ai-summary-label">💡 AI 요약</span>
|
||||||
<button class="btn-primary btn-sm" onclick="regenerateSummary(3)">AI 재생성</button>
|
<button class="btn btn-primary btn-sm" onclick="regenerateSummary(3)">AI 재생성</button>
|
||||||
</div>
|
</div>
|
||||||
<textarea
|
<textarea
|
||||||
class="ai-summary-textarea"
|
class="ai-summary-textarea"
|
||||||
|
|||||||
@ -1429,11 +1429,12 @@ input[type="date"]::-webkit-calendar-picker-indicator {
|
|||||||
.related-meeting-item {
|
.related-meeting-item {
|
||||||
background: var(--white);
|
background: var(--white);
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
padding: var(--space-sm);
|
padding: var(--space-md);
|
||||||
margin-bottom: var(--space-sm);
|
margin-bottom: var(--space-sm);
|
||||||
display: flex;
|
cursor: pointer;
|
||||||
gap: var(--space-sm);
|
transition: all var(--transition-fast);
|
||||||
transition: background var(--transition-fast);
|
border: 1px solid var(--gray-200);
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.reference-item:last-child,
|
.reference-item:last-child,
|
||||||
@ -1443,7 +1444,15 @@ input[type="date"]::-webkit-calendar-picker-indicator {
|
|||||||
|
|
||||||
.reference-item:hover,
|
.reference-item:hover,
|
||||||
.related-meeting-item:hover {
|
.related-meeting-item:hover {
|
||||||
background: var(--gray-100);
|
border-color: var(--primary);
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.12);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.reference-item:active,
|
||||||
|
.related-meeting-item:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.reference-content,
|
.reference-content,
|
||||||
|
|||||||
@ -2131,8 +2131,8 @@ graph TD
|
|||||||
| 1.4.18 | 2025-10-24 | 강지수 | 05-회의진행 실시간 주요 메모 추천 기능 명확화 (유저스토리 v2.1.1)<br>- **AI 제안 탭 기능 상세화**: 실시간 주요 메모 추천 기능 명시 추가<br> - UFR-MEET-030: 실시간 AI 주요 메모 추천<br> - 음성→텍스트 변환 후 AI가 실시간 분석<br> - **중요한 내용으로 판단된 경우에만** 주요 메모 항목 추천<br> - 추천 빈도는 중요 내용 발생에 따라 가변적 (3-5초 고정 간격 아님)<br> - 각 추천 항목에 "주요 메모에 추가" 버튼 제공<br> - 실시간 업데이트: 새로운 추천은 상단에 표시<br>- **프로토타입 확인**: 05-회의진행.html의 AI 제안 탭이 실시간 주요 메모 추천 기능을 포함하고 있음을 확인<br>- **참조**: design/uiux/요구사항설계검토-report-V1.2.md (실시간 주요 메모 추천 명시 부족 개선) |
|
| 1.4.18 | 2025-10-24 | 강지수 | 05-회의진행 실시간 주요 메모 추천 기능 명확화 (유저스토리 v2.1.1)<br>- **AI 제안 탭 기능 상세화**: 실시간 주요 메모 추천 기능 명시 추가<br> - UFR-MEET-030: 실시간 AI 주요 메모 추천<br> - 음성→텍스트 변환 후 AI가 실시간 분석<br> - **중요한 내용으로 판단된 경우에만** 주요 메모 항목 추천<br> - 추천 빈도는 중요 내용 발생에 따라 가변적 (3-5초 고정 간격 아님)<br> - 각 추천 항목에 "주요 메모에 추가" 버튼 제공<br> - 실시간 업데이트: 새로운 추천은 상단에 표시<br>- **프로토타입 확인**: 05-회의진행.html의 AI 제안 탭이 실시간 주요 메모 추천 기능을 포함하고 있음을 확인<br>- **참조**: design/uiux/요구사항설계검토-report-V1.2.md (실시간 주요 메모 추천 명시 부족 개선) |
|
||||||
| 1.4.19 | 2025-10-24 | 강지수 | 05-회의진행 화면 설계서 프로토타입 기준 전면 수정<br>- **레이아웃 구조 변경**: "2열 구조" 표현 제거, "메인 콘텐츠 영역: 정보 패널 (탭 구조)"로 단순화<br> - 텍스트 편집 영역 관련 내용 모두 제거 (왼쪽 영역, 에디터 툴바, contentEditable 등)<br> - 현재 프로토타입은 헤더 + 탭 콘텐츠 구조만 보유<br>- **반응형 디자인 명확화**: Mobile/Desktop 모두 동일한 구조에 너비만 반응형<br> - "2열 구조를 1열로 전환", "바텀시트" 표현 제거<br> - Mobile: 전체 너비 사용, Desktop: 최대 너비 제한 없이 반응형<br>- **AI 제안 탭 기능 명확화**: 논의항목/결정사항 구분 제거<br> - "논의항목/결정사항 등의 구분 없이 중요 내용을 주요 메모로 제안" 명시<br> - AI는 단순히 중요한 내용을 주요 메모 항목으로 제안하는 역할만 수행<br>- **용어 사전 검색 기능 추가**: 검색 입력창 + 검색 버튼<br> - Enter 키 지원, 용어명과 정의 모두 검색<br> - 검색 동작 상세 설명: 일치하는 용어만 표시, 하이라이트 효과, 결과 없으면 전체 목록 표시<br>- **인터랙션 섹션 정리**: 텍스트 편집, 툴바 사용, 충돌 감지 등 편집 관련 내용 모두 제거<br> - 탭 전환, 회의 종료, 실시간 업데이트만 유지<br> - 실시간 업데이트 항목을 현재 화면에 맞게 수정 (AI 제안, 용어 사전, 관련 회의록)<br>- **데이터 요구사항 업데이트**: 사용자 편집 내용 제거, 참석자 초대 이메일 추가<br> - AI 제안을 "주요 메모 항목 제안"으로 명확히 표현<br>- **에러 처리 업데이트**: 편집 충돌 에러 제거, 용어 사전 로드 실패/참석자 초대 실패 추가<br>- **주요 기능 목록 정리**: 실시간 협업/수동 편집 제거, AI 주요 메모 제안/참석자 관리 추가<br>- **권한 항목 수정**: "회의록 편집: 모든 참석자" → "참석자 초대: 모든 참석자"<br>- **프로토타입 기준 반영**: 05-회의진행.html 실제 구현 상태 100% 반영 |
|
| 1.4.19 | 2025-10-24 | 강지수 | 05-회의진행 화면 설계서 프로토타입 기준 전면 수정<br>- **레이아웃 구조 변경**: "2열 구조" 표현 제거, "메인 콘텐츠 영역: 정보 패널 (탭 구조)"로 단순화<br> - 텍스트 편집 영역 관련 내용 모두 제거 (왼쪽 영역, 에디터 툴바, contentEditable 등)<br> - 현재 프로토타입은 헤더 + 탭 콘텐츠 구조만 보유<br>- **반응형 디자인 명확화**: Mobile/Desktop 모두 동일한 구조에 너비만 반응형<br> - "2열 구조를 1열로 전환", "바텀시트" 표현 제거<br> - Mobile: 전체 너비 사용, Desktop: 최대 너비 제한 없이 반응형<br>- **AI 제안 탭 기능 명확화**: 논의항목/결정사항 구분 제거<br> - "논의항목/결정사항 등의 구분 없이 중요 내용을 주요 메모로 제안" 명시<br> - AI는 단순히 중요한 내용을 주요 메모 항목으로 제안하는 역할만 수행<br>- **용어 사전 검색 기능 추가**: 검색 입력창 + 검색 버튼<br> - Enter 키 지원, 용어명과 정의 모두 검색<br> - 검색 동작 상세 설명: 일치하는 용어만 표시, 하이라이트 효과, 결과 없으면 전체 목록 표시<br>- **인터랙션 섹션 정리**: 텍스트 편집, 툴바 사용, 충돌 감지 등 편집 관련 내용 모두 제거<br> - 탭 전환, 회의 종료, 실시간 업데이트만 유지<br> - 실시간 업데이트 항목을 현재 화면에 맞게 수정 (AI 제안, 용어 사전, 관련 회의록)<br>- **데이터 요구사항 업데이트**: 사용자 편집 내용 제거, 참석자 초대 이메일 추가<br> - AI 제안을 "주요 메모 항목 제안"으로 명확히 표현<br>- **에러 처리 업데이트**: 편집 충돌 에러 제거, 용어 사전 로드 실패/참석자 초대 실패 추가<br>- **주요 기능 목록 정리**: 실시간 협업/수동 편집 제거, AI 주요 메모 제안/참석자 관리 추가<br>- **권한 항목 수정**: "회의록 편집: 모든 참석자" → "참석자 초대: 모든 참석자"<br>- **프로토타입 기준 반영**: 05-회의진행.html 실제 구현 상태 100% 반영 |
|
||||||
| 1.4.20 | 2025-10-25 | 이미준, 강지수 | 유저스토리 v2.3.0 반영<br>- 회의 종료 화면 정책 명확화 (확인 전용, 바로 최종 확정 옵션 상세화)<br>- UFR-MEET-050: 최종 확정 2가지 시나리오 설명 추가<br>- UFR-COLLAB-020: 안건 기반 충돌 해결 메커니즘 상세 추가<br>- 실시간 협업 충돌 방지 정책 강화 |
|
| 1.4.20 | 2025-10-25 | 이미준, 강지수 | 유저스토리 v2.3.0 반영<br>- 회의 종료 화면 정책 명확화 (확인 전용, 바로 최종 확정 옵션 상세화)<br>- UFR-MEET-050: 최종 확정 2가지 시나리오 설명 추가<br>- UFR-COLLAB-020: 안건 기반 충돌 해결 메커니즘 상세 추가<br>- 실시간 협업 충돌 방지 정책 강화 |
|
||||||
| 1.5.2 | 2025-10-27 | 강지수 | AI 요약 기능 통합 및 단순화 (유저스토리 v2.4.1 반영)<br>- **11-회의록수정**: AI 요약 기능 통합<br> - 명칭 변경: "AI 상세 요약" → "AI 요약"<br> - AI 요약 영역: AI 한줄 요약만 표시 (30자 이내, 읽기 전용)<br> - 텍스트 편집 영역: 안건 내용 자유 작성 (논의 주제, 발언자별 의견, 결정 사항 등)<br> - "AI 재생성" 버튼: 텍스트 편집 영역 내용 기반으로 AI 요약의 한줄 요약 재생성 (2-5초 처리)<br> - 재생성된 한줄 요약은 회의록 상세조회 화면의 대시보드 및 회의록 탭에 즉시 반영<br> - AI 상세 요약 및 한줄 요약 분리 표시 제거<br>- **프로토타입 UI 개선**:<br> - AI 재생성 버튼 스타일 통일: btn-secondary → btn-primary (다른 화면과 일관성)<br> - 안건별 검증완료 UI 단순화: 참석자는 체크박스만, 회의 생성자는 검증완료 시 잠금해제 버튼 표시<br> - .creator-only CSS 클래스 추가: data-is-creator 속성 기반 표시 제어<br>- **관련 유저스토리**: UFR-AI-036 (AI 한줄요약 확인 및 재생성), UFR-MEET-055 (안건별 검증), UFR-COLLAB-030 (충돌 방지) |
|
|
||||||
| 1.5.1 | 2025-10-27 | 강지수 | MVP 스코프 축소 v2.4.0 반영 (4개 화면 수정)<br>- **02-대시보드**: Todo 위젯 및 통계 제거 (UFR-USER-020 반영)<br> - Todo 위젯 전체 제거 (나의 Todo 섹션 삭제)<br> - 통계 카드: "나의 Todo" 제거, "작성중 회의록" 유지 (2개 항목)<br> - 네비게이션: 하단 네비게이션 및 사이드바에서 Todo 관리 메뉴 제거<br> - Desktop 통계 그리드: 2개 항목만 표시<br>- **05-회의진행**: "AI 제안" 탭 → "AI 기반 메모" 탭 기능 변경<br> - 메모 입력창 + 저장 버튼 추가<br> - AI가 감지한 주요 내용 리스트 표시 (시간 + 내용)<br> - 각 참석자별 개별 저장, 다른 참석자 메모 볼 수 없음<br> - 메모는 회의 종료 전까지만 표시/편집 가능<br> - 에러 처리: AI 주요 내용 감지 실패, 메모 저장 실패 추가<br>- **10-회의록상세조회**: Todo 단순 조회 기능으로 변경<br> - Todo는 제목 + 담당자 + 마감일만 표시<br> - D-day 라벨, 우선순위 배지, 진행률 바, 상태별 필터 제거<br> - Todo 관리 화면 연동 링크 제거 (화면 자체가 제거됨)<br> - "수정" 버튼을 헤더로 이동<br>- **11-회의록수정**: 실시간 협업 기능 제거, 안건 기반 충돌 방지 강화<br> - "편집 중" 표시 제거 (실시간 협업 기능 제거)<br> - Todo 편집/추가/삭제 기능 전체 제거 (단순 조회만 가능)<br> - AI 한줄 요약 재생성 불가 (회의 종료 시 1회만 생성)<br> - 검증률 표시 및 최종 확정 버튼 제거<br> - 저장 로직 추가: 검증완료 안건 저장 스킵, 저장 결과 알림<br> - 안건별 검증 완료 체크박스로 충돌 방지 (Last Write Wins 적용)<br> - 에러 처리: 충돌 경고 모달 제거 (LWW로 인해) |
|
| 1.5.1 | 2025-10-27 | 강지수 | MVP 스코프 축소 v2.4.0 반영 (4개 화면 수정)<br>- **02-대시보드**: Todo 위젯 및 통계 제거 (UFR-USER-020 반영)<br> - Todo 위젯 전체 제거 (나의 Todo 섹션 삭제)<br> - 통계 카드: "나의 Todo" 제거, "작성중 회의록" 유지 (2개 항목)<br> - 네비게이션: 하단 네비게이션 및 사이드바에서 Todo 관리 메뉴 제거<br> - Desktop 통계 그리드: 2개 항목만 표시<br>- **05-회의진행**: "AI 제안" 탭 → "AI 기반 메모" 탭 기능 변경<br> - 메모 입력창 + 저장 버튼 추가<br> - AI가 감지한 주요 내용 리스트 표시 (시간 + 내용)<br> - 각 참석자별 개별 저장, 다른 참석자 메모 볼 수 없음<br> - 메모는 회의 종료 전까지만 표시/편집 가능<br> - 에러 처리: AI 주요 내용 감지 실패, 메모 저장 실패 추가<br>- **10-회의록상세조회**: Todo 단순 조회 기능으로 변경<br> - Todo는 제목 + 담당자 + 마감일만 표시<br> - D-day 라벨, 우선순위 배지, 진행률 바, 상태별 필터 제거<br> - Todo 관리 화면 연동 링크 제거 (화면 자체가 제거됨)<br> - "수정" 버튼을 헤더로 이동<br>- **11-회의록수정**: 실시간 협업 기능 제거, 안건 기반 충돌 방지 강화<br> - "편집 중" 표시 제거 (실시간 협업 기능 제거)<br> - Todo 편집/추가/삭제 기능 전체 제거 (단순 조회만 가능)<br> - AI 한줄 요약 재생성 불가 (회의 종료 시 1회만 생성)<br> - 검증률 표시 및 최종 확정 버튼 제거<br> - 저장 로직 추가: 검증완료 안건 저장 스킵, 저장 결과 알림<br> - 안건별 검증 완료 체크박스로 충돌 방지 (Last Write Wins 적용)<br> - 에러 처리: 충돌 경고 모달 제거 (LWW로 인해) |
|
||||||
|
| 1.5.2 | 2025-10-27 | 강지수 | AI 요약 기능 통합 및 단순화 (유저스토리 v2.4.1 반영)<br>- **11-회의록수정**: AI 요약 기능 통합<br> - 명칭 변경: "AI 상세 요약" → "AI 요약"<br> - AI 요약 영역: AI 한줄 요약만 표시 (30자 이내, 읽기 전용)<br> - 텍스트 편집 영역: 안건 내용 자유 작성 (논의 주제, 발언자별 의견, 결정 사항 등)<br> - "AI 재생성" 버튼: 텍스트 편집 영역 내용 기반으로 AI 요약의 한줄 요약 재생성 (2-5초 처리)<br> - 재생성된 한줄 요약은 회의록 상세조회 화면의 대시보드 및 회의록 탭에 즉시 반영<br> - AI 상세 요약 및 한줄 요약 분리 표시 제거<br>- **프로토타입 UI 개선**:<br> - AI 재생성 버튼 스타일 통일: btn-secondary → btn-primary (다른 화면과 일관성)<br> - 안건별 검증완료 UI 단순화: 참석자는 체크박스만, 회의 생성자는 검증완료 시 잠금해제 버튼 표시<br> - .creator-only CSS 클래스 추가: data-is-creator 속성 기반 표시 제어<br>- **관련 유저스토리**: UFR-AI-036 (AI 한줄요약 확인 및 재생성), UFR-MEET-055 (안건별 검증), UFR-COLLAB-030 (충돌 방지) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ spring:
|
|||||||
datasource:
|
datasource:
|
||||||
url: jdbc:${DB_KIND:postgresql}://${DB_HOST:4.230.48.72}:${DB_PORT:5432}/${DB_NAME:meetingdb}
|
url: jdbc:${DB_KIND:postgresql}://${DB_HOST:4.230.48.72}:${DB_PORT:5432}/${DB_NAME:meetingdb}
|
||||||
username: ${DB_USERNAME:hgzerouser}
|
username: ${DB_USERNAME:hgzerouser}
|
||||||
password: ${DB_PASSWORD:}
|
password: ${DB_PASSWORD:Hi5Jessica!}
|
||||||
driver-class-name: org.postgresql.Driver
|
driver-class-name: org.postgresql.Driver
|
||||||
hikari:
|
hikari:
|
||||||
maximum-pool-size: 20
|
maximum-pool-size: 20
|
||||||
@ -35,7 +35,7 @@ spring:
|
|||||||
redis:
|
redis:
|
||||||
host: ${REDIS_HOST:20.249.177.114}
|
host: ${REDIS_HOST:20.249.177.114}
|
||||||
port: ${REDIS_PORT:6379}
|
port: ${REDIS_PORT:6379}
|
||||||
password: ${REDIS_PASSWORD:}
|
password: ${REDIS_PASSWORD:Hi5Jessica!}
|
||||||
timeout: 2000ms
|
timeout: 2000ms
|
||||||
lettuce:
|
lettuce:
|
||||||
pool:
|
pool:
|
||||||
@ -51,7 +51,7 @@ server:
|
|||||||
|
|
||||||
# JWT Configuration
|
# JWT Configuration
|
||||||
jwt:
|
jwt:
|
||||||
secret: ${JWT_SECRET:}
|
secret: ${JWT_SECRET:hgzero-jwt-secret-key-for-dev-environment-only-do-not-use-in-production-minimum-256-bits}
|
||||||
access-token-validity: ${JWT_ACCESS_TOKEN_VALIDITY:3600}
|
access-token-validity: ${JWT_ACCESS_TOKEN_VALIDITY:3600}
|
||||||
refresh-token-validity: ${JWT_REFRESH_TOKEN_VALIDITY:604800}
|
refresh-token-validity: ${JWT_REFRESH_TOKEN_VALIDITY:604800}
|
||||||
|
|
||||||
@ -125,5 +125,11 @@ api:
|
|||||||
# Azure EventHub Configuration
|
# Azure EventHub Configuration
|
||||||
eventhub:
|
eventhub:
|
||||||
connection-string: ${EVENTHUB_CONNECTION_STRING:}
|
connection-string: ${EVENTHUB_CONNECTION_STRING:}
|
||||||
name: ${EVENTHUB_NAME:hgzero-eventhub-name}
|
name: ${EVENTHUB_NAME:hgzero-events}
|
||||||
consumer-group: ${EVENTHUB_CONSUMER_GROUP:$Default}
|
consumer-group: ${EVENTHUB_CONSUMER_GROUP:$Default}
|
||||||
|
|
||||||
|
# Azure Storage Configuration (for EventHub checkpoints)
|
||||||
|
azure:
|
||||||
|
storage:
|
||||||
|
connection-string: ${AZURE_STORAGE_CONNECTION_STRING:}
|
||||||
|
container: ${AZURE_STORAGE_CONTAINER:hgzero-checkpoints}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user