diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..07f7a04
Binary files /dev/null and b/.DS_Store differ
diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml
new file mode 100644
index 0000000..8133642
--- /dev/null
+++ b/.github/workflows/main.yaml
@@ -0,0 +1,77 @@
+name: Team1 CI - Build and Push Docker Image
+
+on:
+ push:
+ branches: ## TODO: dev or main
+ - dev
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ service: [mindscape-auth, mindscape-info, mindscape-service]
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+ cache: maven
+
+# TEST: mindscape-info
+ - name: Build with Maven
+ run: mvn -B -DskipTests package --file pom.xml
+ working-directory: ./${{ matrix.service }}
+
+## JAR
+ - name: Rename jar file
+ run: mv ./target/*.jar ./target/app.jar
+ working-directory: ./${{ matrix.service }}
+
+ - name: Check jar file
+ run: ls ./target
+ working-directory: ./${{ matrix.service }}
+
+ - uses: actions/upload-artifact@v4
+ with:
+ name: app-${{ matrix.service }}
+ path: ./${{ matrix.service }}/target/*.jar
+## AWS SETTINGS
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ aws-region: ap-northeast-2
+
+ - name: Set SHORT_SHA environment variable
+ run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV
+## DOCKER
+ - name: Setup Docker Buildx
+ uses: docker/setup-buildx-action@v3
+ with:
+ install: true
+ driver: docker-container
+
+ - name: Login to Amazon ECR
+ uses: aws-actions/amazon-ecr-login@v2
+
+ - name: Build and push Docker image to ECR
+ uses: docker/build-push-action@v6
+ with:
+ context: ./${{ matrix.service }}
+ file: ./${{ matrix.service }}/Dockerfile
+ push: true
+ platforms: linux/amd64,linux/arm64
+ tags: |
+ ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.ap-northeast-2.amazonaws.com/team1-${{ matrix.service }}:${{ env.SHORT_SHA }}
+ ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.ap-northeast-2.amazonaws.com/team1-${{ matrix.service }}:latest
+
+
+
+
diff --git a/.github/workflows/update-secret.yaml b/.github/workflows/update-secret.yaml
new file mode 100644
index 0000000..a5fd524
--- /dev/null
+++ b/.github/workflows/update-secret.yaml
@@ -0,0 +1,45 @@
+# name: Inject RDS/Redis into SealedSecret
+
+# on:
+# push:
+# branches:
+# - dev-terraform
+# workflow_dispatch:
+
+# jobs:
+# inject-secrets:
+# runs-on: ubuntu-latest
+# steps:
+# - name: Checkout repo
+# uses: actions/checkout@v3
+
+# - name: Configure AWS credentials
+# uses: aws-actions/configure-aws-credentials@v2
+# with:
+# role-to-assume: arn:aws:iam::194722398200:role/Team1-github-role
+# aws-region: ap-northeast-2
+
+# - name: Get Terraform outputs
+# run: |
+# cd terraform/
+# DB_HOST=$(terraform output -raw rds_endpoint)
+# REDIS_HOST=$(terraform output -raw redis_endpoint)
+# echo "DB_HOST=$DB_HOST" >> $GITHUB_ENV
+# echo "REDIS_HOST=$REDIS_HOST" >> $GITHUB_ENV
+
+# - name: Generate Sealed Secret
+# run: |
+# DB_URL="jdbc:mysql://${{ env.DB_HOST }}:3306/mindscape?serverTimezone=Asia/Seoul"
+# sed -e "s|REPLACE_DB_URL|$DB_URL|" \
+# -e "s|REPLACE_REDIS_HOST|$REDIS_HOST|" \
+# sealed-secret-add.yaml > sealed-secret.yaml
+
+# kubeseal \
+# --controller-name=sealed-secrets-controller \
+# --controller-namespace=kube-system \
+# --format=yaml \
+# < sealed-secret.yaml > sealed-secret-final.yaml
+
+# - name: Apply Sealed Secret to cluster
+# run: |
+# kubectl apply -f sealed-secret-final.yaml
diff --git a/.gitignore b/.gitignore
index 0cb43d3..2847dde 100644
--- a/.gitignore
+++ b/.gitignore
@@ -224,5 +224,49 @@ buildNumber.properties
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath
+
+# End of https://www.toptal.com/developers/gitignore/api/eclipse,intellij,maven,java
+
+
+# Local .terraform directories
+.terraform/
+
+# .tfstate files
+*.tfstate
+*.tfstate.*
+
+# Crash log files
+crash.log
+crash.*.log
+
+# Exclude all .tfvars files, which are likely to contain sensitive data, such as
+# password, private keys, and other secrets. These should not be part of version
+# control as they are data points which are potentially sensitive and subject
+# to change depending on the environment.
+*.tfvars
+*.tfvars.json
+
+# Ignore override files as they are usually used to override resources locally and so
+# are not checked in
+override.tf
+override.tf.json
+*_override.tf
+*_override.tf.json
+
+# Ignore transient lock info files created by terraform apply
+.terraform.tfstate.lock.info
+
+# Include override files you do wish to add to version control using negated pattern
+# !example_override.tf
+
+# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
+# example: *tfplan*
+
+# Ignore CLI configuration files
+.terraformrc
+terraform.rc
+
+terraform.tfvars
+
*.properties
# End of https://www.toptal.com/developers/gitignore/api/eclipse,intellij,maven,java
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..3b60efd
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,14 @@
+<<<<<<< HEAD
+# Default ignored files
+/shelf/
+/workspace.xml
+=======
+# 디폴트 무시된 파일
+/shelf/
+/workspace.xml
+# 에디터 기반 HTTP 클라이언트 요청
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+>>>>>>> user-test
diff --git a/.idea/Final-Team1-Backend-dev 2.iml b/.idea/Final-Team1-Backend-dev 2.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/.idea/Final-Team1-Backend-dev 2.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/Final-Team1-Backend-dev.iml b/.idea/Final-Team1-Backend-dev.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/.idea/Final-Team1-Backend-dev.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/Final-Team1-Backend-main.iml b/.idea/Final-Team1-Backend-main.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/.idea/Final-Team1-Backend-main.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/Final-Team1-Backend.iml b/.idea/Final-Team1-Backend.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/.idea/Final-Team1-Backend.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..8993d89
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<<<<<<< HEAD
+=======
+
+
+
+
+
+
+
+
+>>>>>>> e65f3bd ([feat] feat/search)
+
+
+
+
+
+<<<<<<< HEAD
+=======
+
+>>>>>>> e65f3bd ([feat] feat/search)
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..d3c6eeb
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,15 @@
+
+
+
+
+<<<<<<< HEAD
+
+
+=======
+
+
+
+>>>>>>> e65f3bd ([feat] feat/search)
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..712ab9d
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..f41bea0
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..6231f27
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Final-Team1-Backend b/Final-Team1-Backend
new file mode 160000
index 0000000..1341661
--- /dev/null
+++ b/Final-Team1-Backend
@@ -0,0 +1 @@
+Subproject commit 134166116e6dc35fa93589abf8a98dffc72469b9
diff --git a/argocd/manifests/alert/alert-discord.yaml b/argocd/manifests/alert/alert-discord.yaml
new file mode 100644
index 0000000..2883251
--- /dev/null
+++ b/argocd/manifests/alert/alert-discord.yaml
@@ -0,0 +1,42 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ annotations:
+ argocd.argoproj.io/sync-wave: "1"
+ name: alertmanager-discord
+ namespace: prometheus
+ labels: { app: alertmanager-discord }
+spec:
+ replicas: 1
+ selector:
+ matchLabels: { app: alertmanager-discord }
+ template:
+ metadata:
+ labels: { app: alertmanager-discord }
+ spec:
+ containers:
+ - name: alertmanager-discord
+ image: benjojo/alertmanager-discord:latest
+ ports:
+ - containerPort: 9094
+ env:
+ - name: DISCORD_WEBHOOK
+ valueFrom:
+ secretKeyRef:
+ name: alertmanager-discord-secret # ← SealedSecret가 생성해줄 평문 Secret 이름과 일치
+ key: DISCORD_WEBHOOK
+ - name: DISCORD_USERNAME
+ value: "Prometheus Bot"
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: alertmanager-discord
+ namespace: prometheus
+ labels: { app: alertmanager-discord }
+spec:
+ selector: { app: alertmanager-discord }
+ ports:
+ - name: http
+ port: 9094
+ targetPort: 9094
diff --git a/argocd/manifests/alert/alertmanager-discord-sealed.yaml b/argocd/manifests/alert/alertmanager-discord-sealed.yaml
new file mode 100644
index 0000000..f5e941d
--- /dev/null
+++ b/argocd/manifests/alert/alertmanager-discord-sealed.yaml
@@ -0,0 +1,15 @@
+
+apiVersion: bitnami.com/v1alpha1
+kind: SealedSecret
+metadata:
+ creationTimestamp: null
+ name: alertmanager-discord-secret
+ namespace: prometheus
+spec:
+ encryptedData:
+ DISCORD_WEBHOOK: AgA/gEY4QOcc5IEQJ0hhSEPVP/kHhc2g5bUTQFAwbFw4VpzYq5WjXrm7jYtswPF0j2V5qZcJgbcLafxst5ryw0a1r7ZUDm5X2BNreYYAzhANAXyYv+8kIZtENRKjiKVBvqxR6XhdE8cijAIXX1ipUKVmTfVFWhaQxhY07U/NACM2JYqqosKe8EwZmsreWe30EN4XVko/duuV2iuSbmZrIfcWWafGUGDJHc4T/76I4CucAGzD/X4Pp27Jp1mWumi8tmawHeemPfHikbfcAANaLkL60KGhWOLEIN8kkFIM+m+1hMRgM4Jxtmk/LsXubXpgYkHcMh6CY1WgcdIxRFWmBtAi/ZShRSvKMaJPPGGVYTR0K5Cp1/QdNo3Cx7fZRkqDbu5YH0VatOCD8NmXX5GvVSERPeI3nMSDnKyRREhze7yShSNMk+Rj8EkdCx85GlBZ8XXM9t0NVx13GYr9hyKOyFyj+I7Lp7RWnCkUPoTewojt9kcVfUodQ+ijeb5PPwMtX5cp13jiFOARxa39mtdVDkgilR/c2EFhkGZ/00DEEzupLanMhyt1AYqSIjFEK2WOgN3EtXs9lhaZ50/cMLdOh4/PWYFLxsS3NSbTXxHszEV6Sox5O8Z1sjI1q2UZb98kkSbLYFm4+7z/L7Y33tvE2HqmvAnEEdNiBbxzFHoGOdZ6pWxQZn/r8tRqIqEXEAduGB9TMV5HP217pOB/mbh4hx5wDFYCCKsTdndUGpHLAJZK0PT8vzftHriyAt7t/WasF0sCcmVhJPwl3A2zmx2h+GGMqFQ/X+/S/+aDbO0YDDmZY0rCy7oBtnHH0xxLQolXLrMIMaTGIebmdKrwBIQ9nRZBr/Cz9wQgfzknoIS+
+ template:
+ metadata:
+ creationTimestamp: null
+ name: alertmanager-discord-secret
+ namespace: prometheus
diff --git a/argocd/manifests/alert/alertmanager-secret.yaml b/argocd/manifests/alert/alertmanager-secret.yaml
new file mode 100644
index 0000000..a05baf0
--- /dev/null
+++ b/argocd/manifests/alert/alertmanager-secret.yaml
@@ -0,0 +1,36 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: alertmanager-prometheus-kube-prometheus-alertmanager
+ namespace: prometheus
+type: Opaque
+stringData:
+ alertmanager.yaml: |
+ global:
+ resolve_timeout: 5m
+ http_config:
+ follow_redirects: true
+ enable_http2: true
+
+ route:
+ receiver: discord
+ group_by: [alertname]
+ group_wait: 3s # 최초 묶음 대기 짧게
+ group_interval: 20s # 같은 그룹의 추가 알림 최소 간격
+ repeat_interval: 1m # 같은 건이 계속 firing이면 재전송 간격
+ routes:
+ - matchers: ['alertname="Watchdog"']
+ receiver: "null"
+ - matchers: ['alertname="InfoInhibitor"']
+ receiver: "null"
+ - matchers: ['severity="info"']
+ receiver: "null"
+
+
+ receivers:
+ - name: discord
+ webhook_configs:
+ - url: http://alertmanager-discord.prometheus.svc.cluster.local:9094/
+ send_resolved: true
+ - name: "null"
+
diff --git a/argocd/manifests/alert/kustomization.yaml b/argocd/manifests/alert/kustomization.yaml
new file mode 100644
index 0000000..8480b90
--- /dev/null
+++ b/argocd/manifests/alert/kustomization.yaml
@@ -0,0 +1,9 @@
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+namespace: prometheus
+
+resources:
+ - alertmanager-discord-sealed.yaml # 방금 만든 SealedSecret
+ - alert-discord.yaml # 디스코드 브릿지(Deployment+Service)
+ - alertmanager-secret.yaml # Alertmanager 라우팅 설정
+ - prometheusRules.yaml # CPU 알림 룰
diff --git a/argocd/manifests/alert/prometheusRules.yaml b/argocd/manifests/alert/prometheusRules.yaml
new file mode 100644
index 0000000..73dbf63
--- /dev/null
+++ b/argocd/manifests/alert/prometheusRules.yaml
@@ -0,0 +1,35 @@
+apiVersion: monitoring.coreos.com/v1
+kind: PrometheusRule
+metadata:
+ name: pod-high-cpu
+ namespace: prometheus
+ labels:
+ release: prometheus # ← 필수! (Prometheus CR의 ruleSelector에 맞춤)
+spec:
+ groups:
+ - name: pod-cpu.rules
+ interval: 5s
+ rules:
+ - alert: PodHighCPU
+ expr: |
+ (
+ 100 *
+ sum by (namespace, pod) (
+ rate(container_cpu_usage_seconds_total{container!="POD"}[1m])
+ ) /
+ clamp_min(
+ sum by (namespace, pod) (
+ kube_pod_container_resource_requests{resource="cpu"}
+ ),
+ 0.001
+ )
+ ) > 70
+ labels:
+ severity: warning
+ annotations:
+ summary: "🔥 CPU 과부하: {{ $labels.namespace }}/{{ $labels.pod }}"
+ description: |
+ • 사용률: {{ printf "%.1f" $value }}%
+ • 임계치: > 70%
+ • namespace: {{ $labels.namespace }}
+ • pod: {{ $labels.pod }}
diff --git a/argocd/manifests/ingress/alb-controller/application.yaml b/argocd/manifests/ingress/alb-controller/application.yaml
new file mode 100644
index 0000000..64e9692
--- /dev/null
+++ b/argocd/manifests/ingress/alb-controller/application.yaml
@@ -0,0 +1,28 @@
+apiVersion: argoproj.io/v1alpha1
+kind: Application
+metadata:
+ name: alb-controller
+ namespace: argocd
+spec:
+ project: default
+ source:
+ repoURL: https://aws.github.io/eks-charts
+ chart: aws-load-balancer-controller
+ targetRevision: 1.6.2
+ helm:
+ values: |
+ clusterName: Team1-backend-eks-cluster
+ region: ap-northeast-2
+ vpcId: vpc-0c9b08096e32bf821 # 계속 변경
+ serviceAccount:
+ create: true
+ name: aws-load-balancer-controller
+ annotations:
+ eks.amazonaws.com/role-arn: arn:aws:iam::194722398200:role/Team1-backend-alb-irsa-role # 확인해야함
+ destination:
+ server: https://kubernetes.default.svc
+ namespace: kube-system
+ syncPolicy:
+ automated:
+ prune: true
+ selfHeal: true
diff --git a/argocd/manifests/ingress/app/ingress.yaml b/argocd/manifests/ingress/app/ingress.yaml
new file mode 100644
index 0000000..df90e5a
--- /dev/null
+++ b/argocd/manifests/ingress/app/ingress.yaml
@@ -0,0 +1,72 @@
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: app-ingress
+ namespace: app
+ annotations:
+ kubernetes.io/ingress.class: alb
+ alb.ingress.kubernetes.io/scheme: internet-facing
+ alb.ingress.kubernetes.io/target-type: ip
+ alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
+ alb.ingress.kubernetes.io/group.name: app
+ alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-2:194722398200:certificate/6dbefdb5-afea-4d21-9928-8536cb706e50
+
+spec:
+ rules:
+ - http:
+ paths:
+ - path: /api/content
+ pathType: Prefix
+ backend:
+ service:
+ name: mindscape-service
+ port:
+ number: 80
+
+ - path: /api/response
+ pathType: Prefix
+ backend:
+ service:
+ name: mindscape-service
+ port:
+ number: 80
+
+ - path: /api/gemini/recommend
+ pathType: Prefix
+ backend:
+ service:
+ name: mindscape-service
+ port:
+ number: 80
+
+ - path: /auth
+ pathType: Prefix
+ backend:
+ service:
+ name: mindscape-auth
+ port:
+ number: 80
+
+ - path: /login
+ pathType: Prefix
+ backend:
+ service:
+ name: mindscape-auth
+ port:
+ number: 80
+
+ - path: /oauth2
+ pathType: Prefix
+ backend:
+ service:
+ name: mindscape-auth
+ port:
+ number: 80
+
+ - path: /api/test
+ pathType: Prefix
+ backend:
+ service:
+ name: mindscape-info
+ port:
+ number: 80
diff --git a/argocd/manifests/ingress/grafana/ingress.yaml b/argocd/manifests/ingress/grafana/ingress.yaml
new file mode 100644
index 0000000..692743a
--- /dev/null
+++ b/argocd/manifests/ingress/grafana/ingress.yaml
@@ -0,0 +1,23 @@
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: grafana-ingress
+ namespace: grafana
+ annotations:
+ kubernetes.io/ingress.class: alb
+ alb.ingress.kubernetes.io/scheme: internet-facing
+ alb.ingress.kubernetes.io/target-type: ip
+ alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'
+ alb.ingress.kubernetes.io/group.name: monitoring
+spec:
+ ingressClassName: alb
+ rules:
+ - http:
+ paths:
+ - path: /grafana
+ pathType: Prefix
+ backend:
+ service:
+ name: grafana
+ port:
+ number: 80
diff --git a/argocd/manifests/ingress/prometheus/ingress.yaml b/argocd/manifests/ingress/prometheus/ingress.yaml
new file mode 100644
index 0000000..91d55bd
--- /dev/null
+++ b/argocd/manifests/ingress/prometheus/ingress.yaml
@@ -0,0 +1,43 @@
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: prometheus-ingress
+ namespace: prometheus
+ annotations:
+ kubernetes.io/ingress.class: alb
+ alb.ingress.kubernetes.io/scheme: internet-facing
+ alb.ingress.kubernetes.io/target-type: ip
+ alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80}]'
+ alb.ingress.kubernetes.io/group.name: monitoring
+ alb.ingress.kubernetes.io/target-group-port: "9090"
+ alb.ingress.kubernetes.io/healthcheck-path: /prometheus/-/healthy
+ alb.ingress.kubernetes.io/healthcheck-port: "9090"
+ alb.ingress.kubernetes.io/healthcheck-protocol: HTTP
+spec:
+ ingressClassName: alb
+ rules:
+ - host: k8s-monitoring-c127b00cfa-1844328069.ap-northeast-2.elb.amazonaws.com # alb dns 주소
+ http:
+ paths:
+ - path: /prometheus
+ pathType: Prefix
+ backend:
+ service:
+ name: prometheus-kube-prometheus-prometheus
+ port:
+ number: 9090
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: prometheus-kube-prometheus-alertmanager
+ port:
+ number: 9093
+ - path: /api/v2
+ pathType: Prefix
+ backend:
+ service:
+ name: prometheus-kube-prometheus-alertmanager
+ port:
+ number: 9093
+
diff --git a/argocd/manifests/msa/auth/app-deployment.yaml b/argocd/manifests/msa/auth/app-deployment.yaml
new file mode 100644
index 0000000..afcf8a5
--- /dev/null
+++ b/argocd/manifests/msa/auth/app-deployment.yaml
@@ -0,0 +1,30 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: mindscape-auth
+ namespace: app
+spec:
+ # replicas: 1
+ selector:
+ matchLabels:
+ app: mindscape-auth
+ template:
+ metadata:
+ labels:
+ app: mindscape-auth
+ spec:
+ containers:
+ - name: mindscape-auth
+ image: 194722398200.dkr.ecr.ap-northeast-2.amazonaws.com/team1-mindscape-auth:latest
+ ports:
+ - containerPort: 8080
+ envFrom:
+ - secretRef:
+ name: mindscape-secret
+ resources:
+ requests:
+ cpu: 300m
+ memory: 256Mi
+ limits:
+ cpu: 500m
+ memory: 512Mi
diff --git a/argocd/manifests/msa/auth/app-service.yaml b/argocd/manifests/msa/auth/app-service.yaml
new file mode 100644
index 0000000..ebf44a5
--- /dev/null
+++ b/argocd/manifests/msa/auth/app-service.yaml
@@ -0,0 +1,13 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: mindscape-auth
+ namespace: app
+spec:
+ type: ClusterIP
+ selector:
+ app: mindscape-auth
+ ports:
+ - port: 80
+ targetPort: 8080
+ protocol: TCP
\ No newline at end of file
diff --git a/argocd/manifests/msa/auth/hpa.yaml b/argocd/manifests/msa/auth/hpa.yaml
new file mode 100644
index 0000000..c2d6c5b
--- /dev/null
+++ b/argocd/manifests/msa/auth/hpa.yaml
@@ -0,0 +1,31 @@
+apiVersion: autoscaling/v2
+kind: HorizontalPodAutoscaler
+metadata:
+ name: mindscape-auth
+spec:
+ scaleTargetRef:
+ apiVersion: apps/v1
+ kind: Deployment
+ name: mindscape-auth
+ minReplicas: 1
+ maxReplicas: 20
+ metrics:
+ - type: Resource
+ resource:
+ name: cpu
+ target:
+ type: Utilization
+ averageUtilization: 70
+ behavior:
+ scaleUp:
+ stabilizationWindowSeconds: 60
+ policies:
+ - type: Percent
+ value: 100
+ periodSeconds: 15
+ scaleDown:
+ stabilizationWindowSeconds: 300
+ policies:
+ - type: Percent
+ value: 100
+ periodSeconds: 15
diff --git a/argocd/manifests/msa/auth/kustomization.yaml b/argocd/manifests/msa/auth/kustomization.yaml
new file mode 100644
index 0000000..e1cdf94
--- /dev/null
+++ b/argocd/manifests/msa/auth/kustomization.yaml
@@ -0,0 +1,4 @@
+resources:
+ - app-deployment.yaml
+ - app-service.yaml
+ - hpa.yaml
\ No newline at end of file
diff --git a/argocd/manifests/msa/info/app-deployment.yaml b/argocd/manifests/msa/info/app-deployment.yaml
new file mode 100644
index 0000000..d169e6a
--- /dev/null
+++ b/argocd/manifests/msa/info/app-deployment.yaml
@@ -0,0 +1,30 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: mindscape-info
+ namespace: app
+spec:
+ # replicas: 1
+ selector:
+ matchLabels:
+ app: mindscape-info
+ template:
+ metadata:
+ labels:
+ app: mindscape-info
+ spec:
+ containers:
+ - name: mindscape-info
+ image: 194722398200.dkr.ecr.ap-northeast-2.amazonaws.com/team1-mindscape-info:latest
+ ports:
+ - containerPort: 8080
+ envFrom:
+ - secretRef:
+ name: mindscape-secret
+ resources:
+ requests:
+ cpu: 300m
+ memory: 256Mi
+ limits:
+ cpu: 500m
+ memory: 512Mi
diff --git a/argocd/manifests/msa/info/app-service.yaml b/argocd/manifests/msa/info/app-service.yaml
new file mode 100644
index 0000000..0de7534
--- /dev/null
+++ b/argocd/manifests/msa/info/app-service.yaml
@@ -0,0 +1,13 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: mindscape-info
+ namespace: app
+spec:
+ type: ClusterIP
+ selector:
+ app: mindscape-info
+ ports:
+ - port: 80
+ targetPort: 8080
+ protocol: TCP
\ No newline at end of file
diff --git a/argocd/manifests/msa/info/hpa.yaml b/argocd/manifests/msa/info/hpa.yaml
new file mode 100644
index 0000000..54e26d3
--- /dev/null
+++ b/argocd/manifests/msa/info/hpa.yaml
@@ -0,0 +1,32 @@
+apiVersion: autoscaling/v2
+kind: HorizontalPodAutoscaler
+metadata:
+ name: mindscape-info
+spec:
+ scaleTargetRef:
+ apiVersion: apps/v1
+ kind: Deployment
+ name: mindscape-info
+ minReplicas: 1
+ maxReplicas: 10
+ metrics:
+ - type: Resource
+ resource:
+ name: cpu
+ target:
+ type: Utilization
+ averageUtilization: 70
+ # 앱 배포 초반 spike 방지용
+ behavior:
+ scaleUp:
+ stabilizationWindowSeconds: 60
+ policies:
+ - type: Percent
+ value: 100
+ periodSeconds: 15
+ scaleDown:
+ stabilizationWindowSeconds: 300
+ policies:
+ - type: Percent
+ value: 100
+ periodSeconds: 15
diff --git a/argocd/manifests/msa/info/kustomization.yaml b/argocd/manifests/msa/info/kustomization.yaml
new file mode 100644
index 0000000..e1cdf94
--- /dev/null
+++ b/argocd/manifests/msa/info/kustomization.yaml
@@ -0,0 +1,4 @@
+resources:
+ - app-deployment.yaml
+ - app-service.yaml
+ - hpa.yaml
\ No newline at end of file
diff --git a/argocd/manifests/msa/kustomization.yaml b/argocd/manifests/msa/kustomization.yaml
new file mode 100644
index 0000000..252e28f
--- /dev/null
+++ b/argocd/manifests/msa/kustomization.yaml
@@ -0,0 +1,5 @@
+resources:
+ - auth/
+ - info/
+ - service/
+ - secret/
\ No newline at end of file
diff --git a/argocd/manifests/msa/secret/kustomization.yaml b/argocd/manifests/msa/secret/kustomization.yaml
new file mode 100644
index 0000000..1fea742
--- /dev/null
+++ b/argocd/manifests/msa/secret/kustomization.yaml
@@ -0,0 +1,2 @@
+resources:
+ - sealed-secret.yaml
diff --git a/argocd/manifests/msa/secret/sealed-secret.yaml b/argocd/manifests/msa/secret/sealed-secret.yaml
new file mode 100644
index 0000000..f8467b4
--- /dev/null
+++ b/argocd/manifests/msa/secret/sealed-secret.yaml
@@ -0,0 +1,51 @@
+---
+apiVersion: bitnami.com/v1alpha1
+kind: SealedSecret
+metadata:
+ creationTimestamp: null
+ name: mindscape-secret
+ namespace: app
+spec:
+ encryptedData:
+ GEMINI_API_KEY: AgAf8de+9HuXlGAR0ayOwhGghxFb5Z1APs+k0NyCsiqVVdDo/RXf8WWCdcW7HycQcaZiksffBs5pSN0tbXqSqUZoCPrMRj3RfbQAgKLV9/SJlGSEG8skqU9R0FLt0OUNvV9WDP32J7lROEN4jOftHRfX3gAeC2arWMiF1x2XybxOd5sNfTNBqMVRcvpPZky40KPrGdAo9c3qJkF4KuBtUUmCx4sRfaakwesn6LIKZDOD1BZ4IG7TT1nrYe3HPulQkUDVaZ4I6ath5A/4sgLSjW3y9loi5NhzJQQG7gBicPEtBiMulBn757qYwFXV/E/kVaUreOu+x07F1Se3ISCEsGW7PRpAl5KDpCILFkviPC+27L3nZW4fxgDrWjXhBPnxWvrvgPJBjxbE589apu4p9p0WW0PDOLSGZHfdev+UU/7K1ngmOYbwZuqDQEU/XyInW7qbvwBIRiyC0nM3bYAHHy1FZnIg7aLsl1STjG7K7fCRxfRIgbAwN3p8Gl/qDGXrFQ4GbDLPDoSYb8f/mOJw84ka7g5tbaD6k1lG/A+emEdpwQJnojqKgse/If6DaGiVITciyq8xlcmmNLW0srY4VD9MBQwkENVyyF1e3wpcdcS/jwD75hP8+2ufm9Q1H2bknogHBt7tAQv9QfT1Bz+e0+qSHJLlnOhGfnZYmmNE1TTAeUCv9KoDmUXpUdj6ZA0gHG6Cc2L43ZOOnXwnBnouTY36eomXz/GePrh08ohovt8nm6hsWbL1LBg=
+ GEMINI_MODEL_NAME: AgBpaqFlp6N7Fpbd/zFmlYaoHTtNFAZJ/0mn1G1WfUIyxTf0Ord2a1swFvKzWKH0SkOYe9sIzYMiTdAOqxw2PCHB7CkcwytY5Yk0iPFtwPQvkfOonnVyAlzhXTkJfBfmfYtWIbsUNtjAxPGRWGDAFk9IeNXFHS9PyFMV6nd4ohEto6zywy4IS9qxuCdvcoy9J6Yx5nfL6pqyDW9BIsb+qO+quW0WZ/ckBcdLQAntsYPwnvWoeFCnYKkXu0plObVhEumo/wQGqD7LoTLUsfHqlxxQaHJmDVqDpCMwPfnP2wAcMlwihVCXB7DgnoIxdENnddKwRlesVQFQmrcBXNa/9G14uNAFxOpHEHSyOAOxpaf5qs2IXjPFRweil7zkGs+XWrWnGtPMWtVDg5GVxbAldReV4ode3dnnZOSsk8kJ3WPeRfv+QFRXSVTiYGM0nCI+VNt5PuT1hvpz5ljCXG9tMx334fx/7W53/oszu1iTPJcV5pUgn/4DgPC4fh24Qr0TNrcVec8s+1KeESOfKPMrDJegyTHrJUG0JGuMBfJ8w+tTQiYn9ZyPyip9L7yrHZ6yBrScfsVL7+unRx4YtymdgVnDDP1fWyJ3duGp34Vtx6dgmT7WcO4nINO4TEXhkbCXPn98G2OwBels1f0r8qkOJqrAEC3DtziZVrRMcQaTxWnLguemeYVaOCVKzdBePHaByqUV/ZhxjlKCFEyeFXVm4KRjkwjfOMDeoRaHSkUoGcKxkMtm4A==
+ GOOGLE_CLIENT_ID: AgA2rAK6PuED7B18R8IrrM4xyWuMBdOgOWqBHVkcAfxtU+J1Kgohlc03mbkI47QS3K1e7Hko5DowdiJHs8KdGwxO5/iEf9prJG03nAR3F0HCiEnKmPUeeeTpjyZoSI6gSs+biEu7h93qPFolEugI0pxBGef9E15jzSqkxq5/4A5IcFBUS6rWZ+hPOqlWOH85baqA4Xz5ma6rM2SjzUjT+0ZyKTVqYFwrPrklJWaDvI2upYhBw7iU/AQyulSiH7SHTji5Wf+ts80WjVprVmI1fBnYTJNG8B4EeJzxku2z//ACnmsCYLCv8tyBsg/0fWpkD1/rxRjDM955bIn29ESdvdU2Rpd6CjL8ZvdgQasl4/hpUr6wIwkM5oIPinnfuQeMYj9lp1HSirOra0JZlEjf9NkMkWarqGSMWZi5xsqQdokA9IADzDmLqsL2i7b63Xamw2obWPoMAp8fR3Aa6ADE9RSeYjgzvz098/qKIueUXvh8JdL9sUHFjLL+hj8GkzroRDQoUp62Lq4p2GRGyotMDpg5aApRTS1WISbpE2ryXZHOLxvjtOdqkfN4+/fyxGBEOoc7/gvotj5ypZM2baVCF1kjaybnOvb3PhPo66YbQUIsbFkX5UrY2Mk73F00d0Mff/1CHf+jkTl5RN0RXpMupDv6MFrRHUnOMV65gyvO774WQI2wqOTnw3/3ass3LOfAW1YRDjQ9WphOpJ9Bz/VRzw7vge2T6+UptV1pQGEuTzajGXIp5xROm++CsumNhx7QCPWgo+ny19VDeIWwwviuZw3WRTLn//9W40I=
+ GOOGLE_CLIENT_SECRET: AgCQ2VegosZBi0jN4ae3awcUf18UL2GHoU05bVEegka+CzlIIM4B4pLapVpg8IU/LaaiLSwxTXRTXm8Q9LMAwTmIEmrcP9oEEKwx3Zv23d5OyakFgdye0Lej7Lxfo8adNsExrfPEiSMSCZQ4iXyPsrkgQvZGEgvhNI5/4DVKCu97aA6OmhJm1INf77+ZZjyaXf8QqDqfgh8ZSr1/wkPkFImsal79l22r3qCz4G2IDJZYobNY4uWe/UnG/Bjs5lZwAwRpUa/AxkLTYUCYiDh9QBVPQXWG76xgE3zcIudpugld0i+5CCOhDkoyAO4fQPNUfO9hoILIrhCcjbG/m6APYtYSYTG7uIpsp2NrfkjI64GkyAPxba+JY6vkEKExFRpXWl/aLffxgPfZfbxahAUFm3MjLyHp0KkWTetN2s5/feeqmOiyIaiVR2eaLXnrFiJMKD8/Bbf+iPstxrdqsreaqcv/99VknT/M0qc2+6qEhZpkntIYSkk1p67MO2N8actiYlKy/OXOA3QcoBEL+j7ngNG0FgRisRE3/yOTgw8PPooPzSLC7emGY909/0Za4elO1JKX/8ENAfMplNPrVPI7FtDt1W2reKqe1JgtE7uV90m9lJKwNn8M2nsNz3YcIYlFEJNSU4bODmpZzAPrOshG9zObBFNYVxgdOLnuLrtnBTHubhTRlFw+21DDWJd2gHFsUIoF14vadJ5uPHXDm7snspQ5TJ/E3A9djKh2GU/Jdw09hwwXUw==
+ GOOGLE_REDIRECT_URI: AgAg3nAw0EbsX9xt4wo4y62bE8N0I7uaqVuAOMM3IM1WqEmtjCUJMoGIqtJAEQEnIPmVVDlW7zlvOw2t8Je6RWYqmxdbETfmxeQSTOItSR65vmOjmfWUi9GlKWvujFVcPunUBcriZ30eAKQUQCJmvuLaQOLqinzBvDag9xiViZPgOMDl7+LJCxklIU5VK6i0ER3C5sH4JtVRfSyEHIvEU9ZvmRc8aZs+aJeAKAx3S6/7vyIOnV0I0P3QDlr8OSTR1vVrNdRDa1uV4BL6kzGQqFzCM+XWWkDSDADIwCXskGn+jgrhb/OJ/ZjR82gk1D3yKwpH1N+N6fEgO70wPSXEtMdpFlFd28HWkpyGXG/S2qacDoG7n5yNDkOhi7OZtncq6rf04Yq3oLwYQeILXqlxqopAjymcakL4O4+S2S94ik/v9iEpWzbIMhG2vfG0ndjMABCw4dS71tAxegSZqSYr4W4LqdDab+FxjPWIGRNIuzWHMpnR8RGCXivSAySpy9ZZUtGf1kKo4iFqlq5nxX8tfmOL428bA42Q/UMYsxnUMacporXGMOcNA2hLOuGGQeCzC7IByJwWkPGKFFJJQxw1cMZsFswKcWx97Oqd1ZnWp1I0PykpzdMDp1Y63m5M/ZzntMDZddX6UAraomhCce500/Kvfbp9/RD2VLlXsgDD91EkuLM1Jo5qZO2uUxemU6h5tMLAYkTNG8EAKmrvoxceGN2iehkimnGWKe+fVugXKsCcn8jb1UeY+rgSXBnDMQ==
+ GOOGLE_SCOPE: AgBcqq0ypsJK6hRDVaQ+5k9T6jLj/5YBdefy+ydmovGfJwjK+QBA9Uep6x2BvYOmuy/sHzCOWXydmQ/VD5/qqv+b5J7hVnOxE1de6MUwg4FsjpZzwt0re7QFAseZRAzuSUt6Jc/0ZnESVkG4NQTe/G0IMmSD0QScFT3zYZ0vh0OfVriUJd2LOUVGyZjey4niJn8RmM6mZOugin7GbykLd7KkCHCj+vhakRt3A/gaxhgAUu7T7byHyj1BuQgJqkmVBIfYUtddu4x7tXt8Gn8dMXfiOUM08tPhZttLu/qUcIptIXeMTe9FRV6TlWgGh8eCCpjbKl5qCWWz+eNX24PUREJQZbD83whA+yW28iGEQImb4OjdtG6ETPBxrVBXs9FhlKbm5Wh1ss4jmgz90WI3P2bcF30ixbiHsExhIvWwFqVatg8IsaDbAbRhrRuUS3fjilMW9oHsU6roiaABSnZlWDqHtkZ8jj8ag2uLpMwUgL5ZkwVhg8rSQblerZGJ0OIQiCLdyO8rAOvf5wJWaC76MENJbNyFEU4Rv9UmDR1+VzWm53KHPYx9+bSfqdglqkQkUd0+3tT9meUIc+StZ2hHv1Drfgr1Jr3E5EkSPdMqY1SIvmXUlVS+WTdKXQKiWjHDFLgqVf24BhL2qjyQ6LWRwxZWHE/TzfvUfQlsVlihmiPnHrjGSH3vnP8VNf7z4OCKAQbvAHsCaVB7h1f/CoLq
+ INFO_APP_URL: AgBa9Azp897RvdjsYr4Fbqk9V34ItJLf9mWAv+CJ+SGUkOFU7bl5XV/iF0GNc8nQSlL/2gkvXmoVa4XTA2U2r8Hl41u7KbK8+0+3Dy803OOCIi3NydAbdMiINqJ2gg5mEdrQltDkPQLOCOESwoz3gbjKdbRYZaXdtNyGgIbkLtu6tJOL5pV7C+PfsTIDys/EtGGje9hUAAVVAojQGXyd7ubwOlmMlazlE/jIdr4Ct2mWVnfSbHqhJDFazDusNIe5xn/KQGVfQtiMrao1iLYLX3B/vmLw8SMwJeYSPEQog7olaWK5snDIpMOHKwJ8ZMrDs6IaT8WVBMMexiRgRrgGDNUt9nmP+hG9GgqTQ+VhGzSQOdWGsafXGLSjbviUY3cpEADONPoUsPfC4o0TMGi4G0rDXF79wYpj5xdEUiv/tQwPOOqPRZdD7DvLIwYL+zlfGmisdQTBPah7HP2/I573EFXQRX48wJS2yXL2j1lKI6yTa4IifyX1Ju1zvYS6BWz7bbkqsiG+eyR5EeHFCT7GY/KdSW8rYouXQkxdyTlrmsyRoquZXKL2advX8t2/CwRBdeaF4CqK/CDsjchyNT7QtIWUP1kqWv3Q8R4nVh/rtxiDNcKumpx3+9D4A4F4HxwIibQCWgLJC14hbLEnVHcYueNNApic++fKm0Nx3vb+mHfFlL2bTyZ7EYRD/vXfWEJyzV4J45xfwbCh49fl/vyTv20qhZEnPeFd34JA4k7KtKP+wTONz8zZw9w=
+ JWT_ACCESSTOKEN_EXPIRATIONTIME: AgBbv0C11fxUSU1kxrCo/IMznNok6EL5MoswpcPGMEo6Ei03UPt1AT064qxvoq/qAGUtWi4iGGG5t75Qb0D8Fi3Ej0QTU0Wl4DDaRcR+wJJcCA1yEBcx7hsTrkdGVFTGfAsW7bpa8Mui+6W0E9+raS0RfboExzguHwCT2tdmvdiZmXbfFjECmzPKPmLY6mJ/Y/TdLVyLUQXxymBqYj+W/td8HXIO+5lY2RDtOTcUwB9GNSPFBocAcxRUW/lhiMFRY6F6rgVDqRiWd9tUuTJmsrO08J8v8VGFuJFtMftN0uevRw9C7pQZvZcTVA0GNOAiO1BCTLPvc0ud/8E2TjPvb/GC1lTyj1ijP/Jo36Ft7w38D48b+gr1PzYSYRGs3vphjFGG6cBdLxFmQmdsVPUn2I10gzq0jz+p3ZURCMkzBrZ61MI2QDTKEkvI9fS1BuDA9MO7LXyvnsUVojMJZWOuO5Vgt+NNHy3v9rA8Jmc8p+OcZgVKOqI5JqpKh6Na5ST8GkewNIXJU2yD8sbEKadTLDy4kdeg5Y11X8QvFd9DJ3HEBPPhrsOMcGt+F340TM9VLMazbP7whqv+3Z56rlQpb2JVU1HDuNXLatUk/3Br95HIEQZEVFAJ8bm1i/CweM8E5OdL70yTS98b2mggLyICCK48ZpGkQoVCnQaZI2H7f7QtBxU2QWtz1I7IwUFPWX8YQm/pcmDMy3BU
+ JWT_REFRESHTOKEN_EXPIRATIONTIME: AgApX3CFkAkpqr/OSOQ3y0JqjxQqpWSeaxnPNZAKE6Ro6e7WaNwcpLHexOXp4x7MEhXuEzH+tvKbRtssejNNEt90rAmfNaMUO16O4Vxgwz2vCmJBtlheMLFnwjnFmV8iQ+xrROweMKFkFzCOS351DVr2gUwRPHj9Buz/ppc0i636PaGhjHuYhxEPTVLjTc4R7ip7CK9YuH+9OCdTR2ysHwOkuYljxqO9lyjmKYWUVavEygxm1oljPNh9Uc/ymnKURNFc0rZUgG0XumyJzWE3BnLA7esIDDlEv4mmN8MrJYLYB/+uNWjYV16zx/jOHn7LU8WM6sbDnVRwsIKzn6fxdiupk3bc66Hn7EiT3qUJryxJrWNRmiRhHept02iBBHYxC6duB3QLl+YWm2miMq+uSwKaX1DqqhxsnvSzpQsDMX6m2m/tpfODs6HblX4kgw0vk59dhpwR9oCqhXK4QttX3UNMbXo2JHvCKW6D/4KwnZKdXiq/BkT4mXxf1DEsHa4kOVSDtHoywLs7rCPM76VLa6KLHvvHKeoAqJYFmybK2Ivhsk+pIhIw1BkEPKFAGQRH6NZb7CqyS0kpCBFzhiCMfamGhmmAt8ndLWYDNP3+cR6HpFY0Tn06m58F/cdrz+wsIMwm0iVTPwi3VolQLprO/P1EjEdd6DkHSXaat7FbnpMIaYYsTgh8wBmtuAV0iOPHa11oHpuFhxNLDRbz
+ JWT_SECRET: AgCBfuFHQw0Cc4sCs7R2Gcae+jvuq01cdPMFpGzKulQ35cfIj+Apn1ABCuOmqBM0WOqZC7j1moKkNNM7P2weH9R22tHcDLSp/qg+5ZxVVJmpJauj2i5cEhIvzrSx61433KPEisIu7tkBEULCWDM1W9HmFkeZ/tjumoU1M7AFrTAhaFwztD51m9b1uWrg7ZIonMzsabp4w+CewUsmZEA5iTUEdVmtasfHN4eweIjmIFa8sa/BBPraEAPTwujKTnm6+BpcMeyfACZvnY4hKLS1o5kBE/N5IzacKzTz27BL4ZJ1ZCbk1zP3g1uwEkYeXQsL5msFNdSPCkHzfEuDfq1J3kI34R7AHdWvTVjEFNiTJpjv1fDtK/tefQhyPDQMlTEzA/aUnS8HD6tx2UMCdRn9pM3KnKxTF4hR5D9TRFWG1jkp7QIY/Eel+NQjnC+TT+tCz47oC8c8SiUapaz27DZFNzqdf3qRd3ZivufgAHFZSq6QFAtwkg3Ias05R3MAmipYG3Ayt9hKF4Uc+RdZzQcLduri0t2qXSrBF+3JM5e9gMzyoRK5Ecn/avtKJzu5e2j1RWycMAdfe/50hpdTniucgZMN9FRxagy4XXGIfg/00328iV9Z+dKwT3MWsFId/JhcZAPYw509BGNHKUkvGBSEOcNFcr1cqi26/mRDEtkD3u+rT7fa81A2gRnjoHkqsxC+spzEZczqM7jqTYvgOnmMSEw9j3PoGIt/495/YZnv1ADOlQIgYX0TSKYDEO1V0aTF85pAYqyjr/eW8E98uA==
+ MANAGEMENT_ENDPOINT_PROMETHEUS_ACCESS: AgAHNBdCIAou2gWHpgNJzeyFTSPfoJi0o9Oz/Ka6jYudwYIwkBBOhLyOGuNgv8WPZRgGPcBq6gzcfDblOaeda4OFgnDcYAnFBFFrFcE7a8JDJgCQLMuQ9jorEkPijXIgHAc8pwC3NylEEG9mggTH9B5qpJvi6KbQSAQGmJWMav8zYWj8DL5UcLBcRC6Th8NSXI+0fiaOv4Qtw0QcVvtfHKVYhU129dmuuPTdQ2V9Hk4eB7+tzMOLIQzuh1BpBXPfQw9gcwASf5pqXUAemhY0FxmKFXuXCSG68FSk39tqszBVDj6Egs9kzo1V9KRzfHJfZosCjVKDuGZnP3sJAtK/P+OgD4cHJE4syowQ4kIJV08vRFpSq0k46HALA2hterBbJpT2Hwx8Jbg7Uc0CVqlf1yYev5aXMDmokCFHiNKzXan0iKUqrbSNiNI9YqIatoqwiBQ+xeq4Oi3wSpx7ywLKxqeU8Y41QjNT63DlZBaaS92jE5OVSwTH1NkjCLPilzrJYwN4/D/rYZAK4LjG93h8lbYg2/nmkYgCCWu8tvdPlDw1rjsc+R7K6SRbf/UBtURRJe7gvIQI1WXNKDOovXAh0sceMJtnTfTnvOtVIAoSu4e+pRdCm7SXzTawWUJzE3fGCTXDmMgde3C9GPkAkV3ig24AcxCNYl4i2cAnzDfnKj2LTOSJsArHDG1kmRjGEYGAL5XbJcw+ipUoWIA=
+ MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: AgA0G9KsThAzr/FSv2aMWATCwrJnZ9cyUHxz7wQ0GVX/LKBwbRfv4CBIiQXTWA1FI7t1YC7JG9Flkb1Ro7V5deGfmhDP7Xi8br6JeG1mghqsra9vne/vsgaGeg2yPpXRoCHjlwP0BFOanpkVYa85cznQ0JoBQ/6ORcom3I26t9mdH8YPO+WbS9rbwi/SHjpbzw2zruxYYlWXhHe8hENeA0+/w5D5qQIke7mRUmV0c4VolHlbSGGXs4uwCmwOxLmRWzeGqjMyNjBtaRVCP/hoQeAIs83po5SByPPjjCYHoaBpeZZbqqF1mC9hG9Hzsu0Ofis2vUI3aDcUVHEaLPOkxGdlmrqeZ/KWIlegMC8516/ryBJS9EYaYWQE7GPoejiu4RrFU83teZQY6xyINkJQwraJej2JOvlC4KB5DYQQz2AJs9/bUSIe+rTh3mFABsEzviCWI4a/+Oa15yB2YaelKezIYs7IX/vPRDlL33bCtks9FBdMEpthq1l3LLZ9B5RwSO4W5fjIRpTyhFIzJDsKrvVbMBc8VN4J7k3UIW/0MERXkxJxXloNMyAF3VbWX48d1OrrPquyhM7TO/rJ9kci3psgY03JzRBPE+BmDqWXfbatwfAQlz4bhI6YgsgG/F4EXYqCDoMeOWJ/oMS5h4Peq3cpNnJrbMvz27l11/4KXh/dTHGzX9s3oH4KtL9ol89VdgpWEBJ9/Uhxms+sMo1FHaxzAZ+Z+xMf
+ MANAGEMENT_PROMETHEUS_METRICS_EXPORT_ENABLED: AgCIHFnzz7gr99h2V1iDSymfTwk3hsc4FDwmGVTqgkoMfsNrHPCX0xNaelBoZf+kvnhyTMl/1LG43XK1ZJhYZ0UNpEDkMG3LI07H5B88U1mjM0MGIpNMfrtdlS4lBKrnAKk0IGlRtacI4QEZifV5NcgF3w5eqeNSBk4teXdV0qXS5noQ6ZwDtyWL6mNmtQdZb1L497wcXOcB6tRjdXW2szIaJfd6rMywaj+nUpg5gbGp9St8sCdoCRVnL840MsNB2ihZVHaA8/fNr0cuHcJC6L+ZFeAC2nDZNt/VB8GojUjQPLNLTHqs7H7/sJ5Auc/eWTw7yMJHmeDqCSS3n82BZmNcW78H7m5E7zf9zxMrcgJZgO9o7t2lYm6zCTdqAC2OivSPPZ99PckFZywFH2v6VmznCItzAl0nhEvh3WthbI2R0e7DaGL2tdcnxvinCF9u+GChEYpBxU03XiA18z4bgGP0c7+7WRJOePwjGxgM28W8wddTV4Kpo0f0acI8HX61LPPy0UxyNiWMJ6/e9RYOrOYahu0/uMl5l8oTEkBTrx680+oSwTVUoKdSxR4lF20HtbEwVbSe+0Kg79nU6l8OkBYp/wyrAlCVE5oBpoebTCVL0GkPw7/EnGrZdIKiP9Vu39Lpgh3gbj+nE3ZiVYnPjeAvE5L7VsSudI0oU/zsTF4k+vDlRj9e7ndQl8AbWd4/VQmW2qV8
+ SERVER_FRONTEND_PAGEURL: AgBTz29XF9j2M7QoVlpZwcXQSz0TUe4F1elUIr3ENWNWmq4AaKx8T942v8y7ZbRaWa94hhenzYu5NAM2TYwAnT4JSqQqG78vEkxQURScOWSprDCS1XyCXfPzqOF3tdWEGIKU+7rQlHoI87iiKFZvRQsCBYOnjjl0P/gt9sHNCMw+zltEctuqRkurXTcOPxjFNuR+4AXN7tGI7GvXb+3fGINw8U5liUs7+XzL9DAqj3M/DLdyX8dzzIshxXOsp8Fn2gO+aRQVmNJCHztUBT43ruiLrsVV14SSTT96Aru71jAfGwFdsYpKR5kpTByVmZMPoh/gGx5bn6fw5YHxPGMAxupN2BMshrlfCKuGJ+S6ywj8lyEwnxWF9AyqN6kZ+Kx6KWuv+xeCL8E0cJwJMjiVOYivbYPNapk/GwoHlpDejy/26AlGGHTGot84jx6jMS507LCTcZqFyw7GglsA9nlTDBAX4gmsrLmG9KtbrvlAZIKbfvjI9vnw0ba3iIx7rYRS1q5pKcr/aY4nbS3udatXSEXEBYTgnX8/voYepr4+tb93qCsPwah2bd11aF2nHp9Ayj+O2TeOA+g4SvLroWaMhZ6QSJ9aw9r0lkfSw4yZ+fcRi9yNk/+tGDqOcSKBVls+KpqlG8HbJFvKcPskBucRsBSD5HCsWxI/5YC2ABL1fgKS9cqWiItBYA3R6EQl4tnlhBthCKdIqk0v1Lc7NWy+v0io73PCZkKvu7JOKIGG
+ SERVER_REDIS_HOST: AgB4QahicOAA3LHQDo6K1ZpZz6GAjYnBv5MTCd++6cOGmoNWKG2MahxNMxRC6uR6kBL4pNan97qVQz0eGET+uYGV7UDe5zYfK8ivX39vNonEzy8H/k1NModJfuARRcRt358R7Yi657XA70bbacj6ER4dfwC6sbVkBvVv35dL4Skvemb7mVpMDKWP90FpGOC8Jf+VUEANO47KM61gD7b61oleWL+f2TkPEPNgPBNaQwFFK2T9g/l94OxLHGgwuP/6zGqjLNZW2vcc6g7ozhJaqu/RwloJ0apgVnPDYW1m0VfG9/m2uMBLvmo2YmKg5oneRwYd9nSeCY1TUyqn2AnEeu9fUNlJxjmeurC1vHCOIM/GHT7FwEUCaGVnmRd8H2u4s8cxN6ZJpKsfv7c2AyT5TrnRga6XEt4hGdMLEeAzLcK3qJkLbKgvLqkZro3KQRguy2PzyWorQAVvcZ7/mxoSk50SFpwGIy9qpmsmlHU6qGQ1Y33YHWE+OpLb64YqVu9XC3V+X3si7qzfReudDl86RTNf2dgZLd0A1aZ/IT3YdWX4GERMzSrSnPm66DyeE6duOy6fedIYvItCc231z9yku9xpzwlQxEuiCfUnlTFGj2ugLbyL+4dZhr6QCnAqOoTFlzFQ1tZE620nFzgVUU7D/+UeBJsfOP42MCEEIJro7bYmmTFbXJ+VybaT330AJ4BWdbHHaIQapCCwa5IFfNYOloOhMsCk+l4EA+hcO3eKURUHQ+bPBgcd4DZiGhtdJsk4mzI=
+ SERVER_REDIS_PORT: AgAHg+QlwP+qaFH4L9DRlWSg/I17uKo5XJ+3ZZ/IEFNu+sqdjc+Vav9vNrw/EcjrYBxQeP0ky49Hauq7kgxTNVwOuEbw05DGhYLUyzcoNc5z01UDSTWxYmGqSAWt5h1KvyhjmchgAGB0diEUftRk/Mq9clCVXkJ12r0v+lWPSQzfOgvMNuGQOnBMi+EUwKXySf8qZ5gfwwdWelQwYZPniQcBY8BItfvxpvJC+NCk8/kD56hbhkWP+1XhbRMcu8lTESNt2QoNn8FP4k+Zk4Ki8k2rr4Go0oqTc1u/qaN7FGKMzHMcEcniZ7EXELwdCNIPqZsx2t5N7HCVfxto+66vb853B/R5sslE/H+Xr8EHMg/1BPMr9JKi4fw1yJoWBIGyNyG/ikBVCV3/LJpAI7kQpcyQ2gk8o5M0a9zplt8pL7Lh0kTVZwMTLz9eFf+FwXSvTWKjnvJxjuu+qQtcPIl63zIcY5PJYpNm9jFCDNafeQDg9U43r8x66Vf9Fus+Wytw54R0WdIQsuW8RkyQmf9EC9BjDzNqDe+LMY0+BER0y7PcCfZfasy/suCRipYlf3H0orYToZMbui4csscdNkmZ7aIOLcPW3oCZHmt7LpM2WOnVsrVOVYjCvJefWeI778F+rBZYUF+ykxJtkNDyMugoJ0EvTNV4CmLbVj3Do+AAj2zHhxCVybmjg58rn2jBGG+S6373CFmt
+ SERVER_REDIS_TIMEOUT: AgCSALf0IDXi0pAZ0J6sQM8tD61f0KhtWLXrF46CGDieMwS9AzMtNaeli+52BnH0v2AtNptnahZTjSEYtW0CnaTmaEM+MYsX62TVDygAkka6UjX1mJUYmKQ2NOuhel1dwgN9kQPHPkNzlfMVpL0UCb8XG2qP3UnIZJXa9y9Kh+hhocQZ0WV29e4YaUdub0rTMnUx45ullXfNqCNxQCKqrmF0eHMqXXw0UnHPxO+WbzAa8K5SN2BMmbivYM3QT+7cXqXUiMIj4OWbogGGzK0Rqmpnn4kuTqMBqxkg4peA0YhjuPEmgJ7FWegDHoTXvCfg1Ebyz0HxyEI9v0F5KPMcvI6TVVgggi6YjkQzm4ae543XflQcasoIA+jXNmNiIo+6Z1U8ocZFUBcsYeO4uJC9vdcCDbYZfnb8WYAPeHnOu3Med/E9+Ec+hyoGZg1+kJDpo5j6HMbcJdOOmKSazoNmi6Ok4z0NI4Pt7o3iADFx/qaxsaqpPoSb8JCHEp4MGDIyt9qZBCn9ZdudxH8MyhKQn1YuhO46UFev5Dtx1oAwMn7GYYr/NIVKdDGxWiS1ir3JBgeDIlHM74V/kPWE6HuRb1Zxy/WaVUJAqVqwUINaJbSLvXjeayHF8/I33xW+fz45ZVQ+0XdRsico8qH4AnoWvEr0m4435CWL9WW4OJ2iYS2kwsGv0MXxSWDF3skSDBYzBZQdf6wi
+ SERVER_SECURITY_ENCRYPTKEY: AgBZ0ips468b+LEMiEO/koJezE38ZXairQewNJYidPPGekIQv3wdc4njP4qHPHmM9UoX4+QESz5GURm2atNR0BwSeCwq/+4B3a8LhMYa1tWxpfAkU2ahVdZbVW5y27zykWDyFscfBhk6E73ILdbAwRL6FDt1tOi57EZYbEKHsasi6rB6xPBtXcJdSmJiSfrCybltrMi4UxIQ9YHV7egtnDbPLA8q384FsOif4JIqo3RVlqioUvM2QU29NbPKSZzJG38eWVIo79B7aPpg2XGr7FXo2oZ25Em5LjSxdyCLchRwz4CsMDWteZlzVR8zg819iWN2yE7lHdYhKqnuP0yfAVizVPT6N7kNy4WmyuWwxhKJXxuRSR/fs4Pvl3mXB89gg+Pfi2OR37fsjbaw+Qdx3Jn+ISB9eeWqzg9XFtnAI1REWSu/+/UQdqA5hhNcbUzsX4EHNTa29juJ5oH6PFQmsE+JH7tJRvSFnb9dBRcb+coUtIceAjCAuUcQWmW/i3bXr3d+NcMP5SYLb4pajKqysTMt40mbsMhOsJWGeU2lMSERkLg+U8WPKKCsZRHiq0RHBkcQJbnEGnPWCszj+VQJ8GF4ndCQFjMdAQ+jNaJ4Jwjbz6xYhoxRiklPdJ7cA/b3HrbQMLu+hUQFwhVpPeAnnNa5Rrppeg2dBI885XlBTuQxXXN1x1gQ5fkveRMfwKaO7KQ2z4k83jQtK0o3PVWbi/Y0
+ SERVICE_API_KAKAOBOOKS: AgBkkakD89PJ4BNLMV+77a4fmsJRiSy3dq+ICqpsda8b+C01TVYDbY5osJKQfa9r5RHgbR+wX2I+pa9cH9ytCBMogWBcwwtnoQPem5qUI9ZqiM2Q/YJYx+fKeHDP3pwnyf4L4x0UY205sNhdi646mZahVFwwQthTH1NpgF1sffQPUlijxCoDTh9WWJqSDg5zm8RiFHJMSyQnP4VhG6ZlkLjZd3/F/q5xvZf1EDOx7tAOl6wsYxOuQZqYTWHzVaIwa66vyTawaVK9XtrbAw42PJ/mEXEHIcHxNLRoU6YOCsG5QmONLO+Vk7jkRpbh0MlYzqgZx6u5nagXb4qkejTUeDr3qFt9pfR8Bp1FixpTUhlHkiEQVLd+HXA3egSCIf5JpQ4GRmcGq7vcRcucBsH5bBp2pMhtO4asZyLEDQyaQd65TTAvft4DxhWzQX25z2QI5+4f3r8UU+eLrJQE8a0Zq2gQAN5rkuh3z/TKoGZPcmG7pDpzuuK49N2Om/J1hriyz3nO4VsB+Qt5sKCatT1USrGOU3EWn1/+t0vAOH+eSv8It2kLM55n1QqngHl1+6RQHeNNHgjDrmDDZZfq+CZTbiY2zqixl4ALYAD8GAKC3GgO+/COF8TDdoHssQYfyCksNJ4wMY2a/8jzcum7/ekqUiXDcSIBU42VqgLbyyY2jls3EHitfyW3g5d+M1YEtfmEDx78DeQm8kJtFj5Ekmt3LgxgjqfKmAYL3oo+hy+2oEEPiA==
+ SERVICE_API_LASTFM: AgBi4MRe4wrCI0wvsL1oXfOZRqkvuHiCNMcwNBwf2JAj7t8o3bnHU3ev7Z+7IAPSh2wtz5XBuIJdRzMB3uY9CE5z5Kc4UKuHYedwg+zig+Bw3xAS8PQNKYf3252A5aLwo6Y73KVc8/fXatkp2xayMeZjNxwHatkHbk9OLhrog54XIvP7SiNkD5s4CcerHGAmn+WG4rsYUcbHbjkSer97siCfImbC7JbyJDrv9KdZl6GM5KZgn7ARLs/YgkWXPH1SqTy4OTi9nb0JsVNK0lOMIMLMENu+ckrErdORXUQYkbOYJJhrdskYE4e0kLPqk2vuTzVLaGCcGNdxy0CtewpfUKJMX+Ny6Tdoy6c7FM1N5TlkytWAUqt6tQxbzDFtVZE4sNzzdpUfH7DIRqxB4KH6GQ9Z9iI1VJlQTOytosa5OUzCwBA8xo0QpuDYHO5ME6HOczPd8sxAg5A+GSVs8I4QWp+LdBR+HP0d7hByUSkuy6036G+zqF3NDNUWq/vrjjCNqLZJwWjolC7yfetLPYctTNLmDVeAZiyjvxO0ujZ6t4zJpStZnWPIoHTm/ZrUYP2FHT1CLVGAzXr9Bcm21MvRz5f88zLnccz43Bb8A+ZiEAWurXp9QWOQ9jh1fshRo2KuYLVrT33szlYlaw/6k7UgkjI1XJvmKcktbu67KKM1tj1hxvAlMbHRagbbHLG2a3uk2VXaXAYxYxZA4pP6366cFOTwwBSloNzyukxb6zT98rFhKg==
+ SPRING_DATA_REDIS_HOST: AgBn8ESMW5IZ4RoDE7Dn9fckp5R68Fv9Rp7l3rfafue6aNGuj6A3drQfKtTaHPkjSxpPdaGE7xTtyyj/j2JLzBHRZUgmDz7FfRIv10iU39qdDb9N1B2+j70spsNQ+v99UZoX0KsnDKmPN6azGp0gWctZUuS6QiYBZqFRIwL3Bv2x2QoGQZcHfwfD2ZxkIbFhG/C/XCGgmyumBCmN8tVpRkMFAL673G0XHiH0cFq3QoXrk8xRc0v3awIrU4u1S8tvWHU+pIhB43hyFK2DHpNy++2tqrQfEVU6Pxi8kApbzTe/laRg8a2gOiC0r3LROjaJtlIRWf7/iUtSrHabiugkvTSa1FCDP9gTY+jnX8AwVWDyRPg2kINES7JVDqB0huyINXkrPjxyyAhXqZwrsntpQ/7M/yXqwm7gXmwbd+/MhUdvg4cXboJpVp4QcX7y/yOAciW9zcw4crEI9+v231d4JJXwFejUhIvB2QE+Tc2FNT5myVLJvmGu2o1wzwVSNDTXICxooMWzOY8cl/KZeNC+FPU+lAowo5CPVPk5SHB7COfA4jyJvM/iRk4Qknmdu88FasqJpzcdLaRuiaT5c07Id8ZWDxa5Pbew6Kyb8upYS6xYIhFbGacnBiUv2ilKhAz+xgdRIVDiWKzYrQha0JE7ab1Oa6z7dWoqj1gWEzYM1p1wa7Gh8Ri3GgtUb6v7/FZAxWVaoqYGhehSs+x9uhDFGMbPu7RidihGzF+k+j88U/38sjwxyhKuzb5QHW5ertQPNGw=
+ SPRING_DATA_REDIS_PORT: AgAoyeE/XS9DPMG93pqgg4RRRxU4O1DclecQ2kcrx3FHiQejWR1/ExxkVJjFNr8jiYMpVDfnzf6SH7bJLWMbZLNuwtWEj0yEcsrvDV9vbWAq/2PF0gv8+GUrnezX3AmE8a3hZzaAbp10rjkS+B3/ZDL46ugZS1IjqKy2hmYjK0NaRyBqKsaOxzrj3F2KOtT6tKv48f1VhL7XESZamrS7uozSBIe2Su6GsRDhS9VEvb/EUC0VNi5b06X50T9Av9nYcJXYOFEwTBdLfU9MHfwXX7ESIUqIeSMbkoHgwOkob51QBGVC5gyHfd/suFdGu/v6M8JOZrFq8o+g1/tXnUzWXL7tUM3sMTIRGhW2BTvF/sj1gDUsuSl0qJyq+wTxtdtThsQS+EcR6gTAvDVUs0ziYtiSBdhJ2JA7xpmBu9rJT0mDBwVM5VsMltGe+vr64nwsvfrCinqx+0nfs5W63Ok5GdPSTPYXwew15ZGIOEf/zWpLjjeTZV9QZpnTY9KXQT9ecPQScPa+LxdTp1G6NIMTJYtMqRqankopnW8rstD/NbGeFHnB4+dawLxcoePWjUZnlnqPRj5URyp0HlpQzMJP8mQbHc0U7kUWqGpaDIuGNDOMwXdP1VX6l2HEVbISRa2jsob4o5OCVpAuyM0805KixZ/GGo6SvozIcWJ0n00swJaNcM3ZB+muuYS2EjpPP2yPbUHCEoC1
+ SPRING_DATA_REDIS_TIMEOUT: AgARvu2np3az2pPmi7eiDkyUi7+hy2COoTyOCUaLv8pdQp4ZWHlPCxa0NqkehqqiJCPfTkslpN5oULr3A8HgR5Q5/6CJUmR1f0/Xsh+EGR2Z/pY5i6UG3en2kuuXJy6InZgwdDbTkq9iMSqm0J140tapvnzbRqGEi9Q9yCzP3OJeWm/pNNkN4nQmx+LkqlFKk9asz9dMhbvdDZz6udbckC1UV7iNbesrm8OdvgaPa4keLF8YGNvTIHs/tIYA99Fm2qMTg1nc5BhTTIk7OGy+n++ZbpVfIpULDXdzdDxuOmj1Li7vc4OImehX1Gj84n/yKRmzhYwBJW8GYv/ECt/ydGjwqqkZlCiFWgTRSqRjQqKx/+UPte2puEr7/xFlbV7HeS3WEYWm5ai23VOLIehEwaF7NIMSuIi26dYLpD4OfHo+giyPTfqpirOAUOOfRzdIWLzB9q7OKCevpmfzc4qvG7nsR7taNCXDS3c+etYE94u42fGiaGInBNZPzFi/EjnoRmIU0InRUcX5k0AX61rP06Ou5Qi1jGQVE380MU/xvfN9tqbTMH3LZOltkjbiUqAGwxI1fru3gkKxI90JhBAaqlcQrXpc3SUjyLu8QPPQujn8skDWXjR8wDgnGHCbA21z2QpFMAf3vbEP2NASvpwHSZpLAR4Bu6GK+Ogr9tqOGb1X2pvBxt0gLRhHeRMKcVNSIkrflTui
+ SPRING_DATASOURCE_DRIVER_CLASS_NAME: AgA/LpMBQoANmidPU4IQkp5agkpRi76NGFN6dCqBQ7ksAXGe8P14jooMKBw05OaNTAPNCC+YuiYevFcOE6bogvJjCFvn2BKJHBLZY+oWqxe6wZZfdIm8sj9oeyT/OBwPDrO25wSQfKrLwgFkFX7MGrlsfDo4hrfbdxOTI9uYwyOyXlunY8ohe4nZFI0QWz88vuW9YJZMoywcykRY9G0ZqHsnOIMLyg5EEu0GKoiXFq54wfM1B8KATszqrhywHea36HVFHnHmH1/Lc5tE3AUoNjLeUWUii/NwyCzLHts/HdnjWRDBY9VUpBvtVprvL7dV87WDj14gwDGpqUJl1AWrK8tGumuimpO/7QD5fa1ZAhmKC193GcRHKDd/C7lUAtNxGSMnQTZ/VWVh//lfaVUAqkDwdzVtlLWAYE/gH8AR/0jnGmVBY7S9gDTXuTSTFsBkiys6d+cZPZWwS2Rcc7Ise+8sMU1pVkyBQhFVYAJR4yHq3MTv0Nc8h8Vnrb66GjwecEMChKrKyw2ZcXKugldDeKjgiKXv1T4Lx82qEiP4DF0Nm/cd/TnzuidP8Ox74E5v8hKTxGzbw5dOhw9KZLF/XmcpkjUmRZ0n8AqPVoc/lWqEdwx6K8XNMJgQueRgfXAeQqLNTAQ4gRWvMDYcmvUgV0WB107+P61nB1U8NB9BlaulcOPqpLUPCsYV3DKySnVu2YL9Dr4HJBluzsQTxEb7HtEOmLxbjznJ2IQ=
+ SPRING_DATASOURCE_PASSWORD: AgAPVxVhmHUl5JbbkdCR8GcH3l8ngyqKWbvHj0aTDU9NSjdjjKnH8QIFlsNnr31+XH4WIhhqehnnXh4NsnpElhAIAACYHw74waUe2HzTf79S48+wbQor+Kvm2B/dBE9BPn2IbOsvMCX5IMhl6prwGZjmhGIkGzx++o5pOk1vRqZ4ZRq8RChjzJ0Gpt92forSVorYM78mDxQ/p/Z3l3EiVBpyPvvLibUoo0f8HGZeWWBP5G7ZCoWBFEpdANmvXL4WJsQr4dE09KQGmzFDbt9XkcHgdD7XOusbpIVtRUFBj0ON3lZ4WZLBeJg75lWPsu6GJHalKhnAXQuc+UyV036Bhqozq8Jl6dI1q/368oy3fx/x6BDNQ+lsaJZS6R8GQmvurqqC140xoV+bCnr8JFSpmd1y8T+BzRkz6sJzJH2ChcpSC/MKHsUO+dxLwVuI9NEavqJ8mFiO4kPHsr0ZpcbISGCtiwF24Ae8l4b7YZxej5Y8UfdqmOeEt5C2C5iH9RPeqrJJYhvwp2GxqP4esOFAh23jqG3zsX4t9pgdGKJBssTXeD8oaquGWcyJ719BMn6OjfiYGztuCqCbLKDL+sj5y0Ik3FU7URJ5vEHOPLUs22KTQhFtOWBgqGgygGasnz7HICbLNuaunL4kdfIc24TgM22FCFIrrrEUurPaHi1EMgxXfv9+9ULU5d6FyzqmfVQTaRtMoiq/vr51AQ==
+ SPRING_DATASOURCE_URL: AgAtb0YS4NzbzPkQ7s9FIZGnV2GjNXJdhOr2CPqnxPih285R7aEI0CoPy276E3rdQHp2Fp6mtifQVR6T5qQbYJ8ub/5M9mvLRXLlWF2ikezzkWmRpzHcCmw6CpFyP2sPlViUU27/tElN4y6oDyQDzT6ALx9gzGB/UCREKSTcbe9ABleB0Wcx2kK4Uo5+fTZ/tC4kh3AFSE5KGfpFiKKeG6h7dL23d4usSbg2XRJPea8/S/HmDJ6XfESEzZO11y6tRagtsP1YUPphrUIsWasua2o2DVBTL2UTpjm4rGNmfAFp9+3NbBTrAuZ3+y8G+VkPw2nE+oY4Z7T5OHzPZydAqly/i6Re5ow6mHlIZh4jxOK86PVwLjA97d9xmoBcDx8lQPJCFY9gdkCB6pUIPxVufX/431BE5J/K+Bw3XEVCGGl3uKytZ4dea34T9FBC8JuEvLdYVU8NNnCOdP9P0f+HxwAPUOIlXBAiWql/dzdd+S9Wrq4+pDDVPitz6PSNqFwjRk8ldKXlhkxIbd5sedPN+EHh63JDnILZ0BRB++0gOo2Qi0BR5gOTwXciNo1F3jYSwsDTTxqHT72RG5M7l4d7duGfUH1dig00CbSXxAZswpsIwsTj+FWuwiR0eOG/NyARRJ/JSlgYon059sAwMpsvKTRsHtwxORsrYsr5xsTIpgPbaiFfzEW7iRFW5VZN2ovravH0jVPxWwbb6zmr9ts+ARCFBe3qy1fnbVa/LpTQ7SJAXas4UWuqcP3pluGAs2f5KelQ0CCuyJ2drquhUYqsM6BoiY9OGYYROX1Bg57zpHDkfNHz13+kHTU/UjGa6lRhNB0TesxasVRuTUjXi04swSSAQw0fVD3vx1Y=
+ SPRING_DATASOURCE_USERNAME: AgAbMaJsJ/3HCwa78QX62rXgiOF9+yElu+y9ky46X19bYK/KStabyooX7V+4Me4s8854T8WPApnkEi3io5JhwW6RGZk49HVofF9Q9UFyi41YJ/rnF5dm9CScZ9KZUAFDJikyZu2g8yqtruiy6x3OkGIN4e3BBiY94rO5w+pM8Erxxn30fT4MyBt4oYbwj/4MiWilXa5ZrrX5shRt4hLYWyBR7vBIU8GPeW0OlMSOE+N/3FUcMZqAGzy/umJoLq4hYnML2S2r1b8/Z/2M9mIJiW1nGggfai+erW1oW2V6hc3sMdPrdNFUgcisXAT3f6agFyTlgDNvXtFYAjPFziYJKZkKnqB+nD1AzNXc+rtY6mRUOGww6sev5ueI+OJ74uFrJBL4wPsR9fl03tmyCoaqoMTRdMZLtH02KcLj1JqbaGG/23uWhkDfU65plrvxDiyBKseI5ElinlGAB6x0e0Lt/yg/maPr4KrKCCnqFiSp9gD4Aiw5VXPM4zZW3FP3fQ+7ujuo2VSmHciEwaj0OKB3rGs4gw2wTT/zdHAjKoi4pdyVrlrGEy7w+vJTfl8SrpSEEI4Dg3CzLzwVv0vwC5VNmOtZATrbeFfeSEm5Pxg1LcVUpadz4FokBgMvrHH3cCdrlm7aLWHgdx8hgXQMkN5rpt59mLyKHxZ0S5M/ATBFFJDQqVEPNoiKrPFB1CThhsDuVFWAF/Z/
+ SPRING_JPA_HIBERNATE_DDL_AUTO: AgAXANCAP6pLQWuC+V2Y3pUR+YV/SHySIbNjDfq1/+a2BZhqMGvaJN7e64qL17CFC1wy5Reyigj1d9LNICtleXrVfvbgiFo/gZ/5UWPYjU3ghdYbiab1JAsLVuo90Y+ZA0HyXuW4ZOKYXIEclZjl5vmLiImLPaMbDD7+NBLPJc49KBpxwaCZiQcDoyUKoaAfYYEp7QCtakHxU84HSEhpU2Zz39CDtia/n2UzOO9THGLNCX6oYZzf42TP/oEJHGI8gsA49YuNtX1uYFnTM1YqrRjcZjjVXoX/daxfGh4gf0kwnAc9DE2qvi2eR0sG+rkhqIKGWD3zkl4rh82hpMkyYvM5oqjdnD3rmvE1tfcahjj3OIAy5i//OP8lF0tuGeU5BKTOvdqR+lYsjZ9b4XYuzld+huvWa/lMnQ0Ugdly36BZg0D+dOge+lgTbZl4xpmpuSSs9qqL18/pAutRjBviMs0HPLg0FQWMaO2auBDKrGag0i6Yr/kejQJKh44eD7NxsxP4Mx1durPLl9q03XDuyLz/PHizhzg1UuOogpNh6Vpkrl9mHDkKbUr5bz4+F04DEwL+6r6NoDz7OhKNqfERnCKCP+6OUqw9v+tb/S1hHKvsdFZuKpRVyc8zUz8m0kCEYI9s+pToAQr+fuD1f9J35Xcm/WvX6SWPirN2H+0EVKMH00HH2fT9BsupQwi6z5Bg+IdPB/FfmHo=
+ SPRING_JPA_PROPERTIES_HIBERNATE_DIALECT: AgBYvJ1p/O9PlWY0+lhxppoiL5FWC0lV6nOudss37fgUoR0fOuHcIkO5sMEw7xMPOubXqpUOdReV3U/6ym90qkjy7eLVBPfb6KhfthgTzKoKRvDgSvOHGmTndFwKbxq69nXyMNcpzpwhrd1ZnmlGh/IA2iryLGABRAYvfd4NlJapelpJz7tlqASaFsJln7HzTsI9OmyzIFhBGYvgf1u+sU8lJfk4BrOex8Np2kGW4nJ7obqIe4wqKLrhR9X6wKXBOIh0aBDCRHEvF1IPTj9rPEmURtDHOWTt6/PpbsKu2LNAWrcRt8RyC7To4toUX/L97KyrfQkTWCsJqYtg4xP44yLW4boVeMYDSlwsurgjQ1LLG3K7w/y3mPEyYlyK6uda+Msa1LUDtiWqnDvmQYjzlOPGVPrQYFXUdsr6mLspgacJMKUNXwVwtom4OrlNrDfXrTzbrQsou0wXyWlbAaBwL5LmR3hFN6bczrKvi9MU4X+aNjVt94C5x0aeOQ4uuMYyM5ec2dFBgbEWUUQts54iDMQUwFpVRN03Ab7FrzdcptMiTM7oq7mRvPmL+msrcozsn7Si7+BzmeriSyRR7ktybsJ20mcGtB7+Qp5P+SMvoKH5mB0OxQ7f68c9Yv6itnGguy6aLUCMsmS+orcGl2oM/lsIGT89j23DE21iCQnor4nUYeNlAT9ER/jiu5qxy220JTsNex6uhsN1ZsJfsb4HJJL2PG0eI/LORgF30D6+wlgk1nSxFQ==
+ SPRING_JPA_PROPERTIES_HIBERNATE_FORMAT_SQL: AgBiwfWe5+oIh52/yW1veP7426aZDnIQBCDIF6vzAJLU/sKEVs5M4+BVCmkPXzUg/c8LP72VuzMdRxAne8ysz9bJT3vVy65RSJTTakicnF7phKT30N/imMdDjXJSMI5k8yn0gMUnG1WiM6fDbvbX/2RqbSc+TAKZly9apXEPFj3J6kg6rccNKcYxIHE7/NHC6fYsT5jRr4g49Q+7amAjiNl5nb589FGoj8E+VHNJNfbjfIkUN8sYcCokLHqDXx3EQZehEBxSaZ8ivo+oKj2pgyPqpEmOzgECiFE3GSXX3x445A4XAA2DobE82q/E06OFbp7WOTzuhShossezYBef6a48M1fR0abACs9KBMMzJgDtWLqtZGRdRpkD1TLaScneSNU7IRWNd5Dzmr5G7d8sNM4zXofDxVG+k9xnXMzcmX/hnk5vV6eS9Mc2jAodgGIjTYz7HNCr98Pn5JXnTSxMfyIkBOI0UEZaGmga4KTyzrelwxAVjYdYFYhmt4EAJ2xg08J8hLYenG15rQp+Lu2YNcMKGPVcOYaiq/K5zlsHdFub0kgszleIF30MTgwg8FaaKzRdAGN8hqwSnjzFVEYjrTxaOR2tvlp/bVVVT0PTrXFuNIyjhIhRe6kKnhow78ZjMnM1OYU3VIYS2aj7BOU3fo6hpngy6xbpCo+gJXEf5UfuWigCTIdWzPqxlJffIhWG3My295Mz
+ SPRING_JPA_SHOW_SQL: AgAjSIbqHUVvtbxhUF329597r+SGkBA8QGkv7B9uSun1yXs7N8f2+dGLbAYa4z28PWQa6Y+6g/dqh4l4BRe3tBpwOVASMXjoJYWRgnOdMnUgQfaW5D1G6DYQgpvFohCryO0yu05yYMSY4ukLD4/uzZH9Uvp1QgTHmJJXtPVaHTukAy+cEIxu+dGdFysEfN9F8DQ6OpcB0p4rsUV5jVAP/4hSBWcA5i83hpRqwJ7QsMcKxXAvMEsxF/i9nOgpid/MQIpfMtmzoT29yT3fLKijTDdn1Xnt1z6VkVEEgtoy1On/5bdl2XQhhxWx8Q53HydMxawnndr1VBFgsU858C4pbZXgo3D8sJFJ69Jd6gL/v3VA8jj0+6sBior169q6gsLgTVfu3HIG9rbp3+x7/Q0c8qdPDBnzSFbk4Y142CzHJuQwy0PLxbOt0POhDJiZ06ltB4Hiad396B5TghgPB0e1IxJbFZESsNn8A2wQQ+cYVSyLcpQNCn2eF6E59wJqLSLdRRvqNmnXKC4aEpKeVx8fY2SpY0FGDYZ10GBIzLY+WCiaWT3IcnfJZCV2nTPYzsm2iDMCicVneOCIAXjh6n1LF3v231gUcl20lVmccAtXKg06DAtTV2g2Pl/MI1b9U+lcO+Xmb2OS4foW6YFXKILoZ7WlW0A/z4XvaHk8GOugE7+gpsUozAsUXb4MAp6uRzLPSdDTa6ay
+ SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT_ID: AgB7VAh3NMzGACO2e0ZAr1RM9te3JoLg89dz7HKRVqUKosFQJOBmQG233/d+JXl39Kp2Ok8RK22mL8hwMH1R7WQo8E/Exyaw/4gmYLI2tEP+49c/3wwHDsSep9Ly0+35X8eBRrAa6bGog3Gl9an6dsIMqgtPYbRNKUUXa7AbDeEHtx6QTmA3cKnimZmB3b74ZShK27pNB5YX9gBj1RmPAA4djdUOICF187Coz1idC9zj4/Okv0sYUL16BQb33gEkq+gVB3yowxjorvxxVUeVGqBOoW00IHS4D1zDD7n72+SU4LiNqyJktB+5clwsXC+NMefHCybCSu+o3CDBThw93SJE1O6bBUatZ1ix0d/fJcJU1g+qw8R0MZPMKrb2WUeX+F6jMIkNz2URC8wTorr08YP4Sx1VGszFP+WaQhCfM5Rs8P7ocsYpwrc1+Uo0Z5cXNIia3YrZDufqhJUKfjO2deTqIMcHLLBsIvewB8DduOn3KtfX1uFh2lSW9VNPgyiPXfc5ay+hK/wwIfb2xDZP6NFeSnygDCzmiAii0SHyavN5f8ZJIG61j1ZxVoXyUiBBianlB6Pjhg8lrFCGo+UVIyOMprfvHo7+TRcXTQxJxd9Q8dQ95116ZhPvCo1wKvyTOSzh7wXgj1+DgmCiHMVsAu0FMSSGfAXWriKNJDuPije1CGf/NVVlfMs4rDiMo+BFgbas/d+NWL2g0bBxh1BRMZF6cgLg7R/N7OntkCC8IyJuiEfagDbOyAdKGbTC0RmE9PdkN8orbzaOpycbpDkH9RP7c20IS62Vwcs=
+ SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT_SECRET: AgAlbFxBCxka16j+gTGRp3meNib6yA5DV86mNeFlcLseyA8IbzD2MGJjHTH+dkl023C4HhRFrz4R1gZQ65tk6ij9VC4PY9Vw+/+rbIhNg/18pXcIrjlOQlzK/a2EBEgZNJIRjAspMHMaKuiVnT92JeN8QPShYT0dBXjPtt2TPZH+CfPBhWFHoHgekl2q4TTtjLAVj6Ylm89Ub9URZsAvxd6/cZgJd+gK1QOWfxSLQ3P/U9Ky9vBHYX99xEdtS7YdWty0dvPZZPWn/ZXZswaUliueLez5bnRldnBGtmTJEAvzqKO5yJsNZPRWI3I0HybjxrlKyRui+6atTlazf0rgRHIBiWAGVSmCj30CLBmx+LR4NKPJkQ6DOTe9MFmUUAwNXdH0FG2H8tdjLGE0g3I5T6lKhyY7DYxmC6mK9TfRiPqs61KEXYbp+qYdZrFsjEnyWArFdBE8leDJPciXseNmP7wfHCE9Ml6t6jBTBJEBPl5Kt0ZReXYpe84xkO8csPEQbcBOiDASunK8na4sw+helSkYH/PIWXIzRBcPO1uUq/YXxUVacO6TcAqhTWrJMjtAL/2VcY2BLcaYFATZ9Id3FVWk0ch8H2aGw1cIluts8d/jQsJlcNp3PeGKYf3eB36+TCZe6J2oP/vPsLANts4jap3j6IsbHG3PzG7lShprvuVEx+pDcMtfcQzE0vDtUXmIYht7RbuVKld7XGgwMTjKdBNFfU94o+9eRKuQYpD3BU6rLN9m4g==
+ SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_REDIRECT_URL: AgBR2HY+qAfLKdIWo8C2cYXE+G/boUVoSTqhAx2ftxyEdFVwapT3PtYl0dyhKmUvVLm6YhexqB2n33YtVQLil6gBMEdVvGL1FANOtQOr3FIU3teg9j6zlBbYLstb20YiAEVcwpf7G4f7WY+ywiEBdWGJVMM6lj2WhqAgeYVJk1All5xECWDNGIm0IELJWRLzpG4G2zmp1wHvHwe8JEZokGnIsHsFSO+ys2idlTIBjUk4OAr53oG28MmuKCoyvELFhubhM+6shyugcvp+qlMovDy2b4jR3kHfX4i6DhHq5JZ9NYPyeen1mGlsX2ZPtuhO/d9ExnfFf0KvN7/GKt5n3g22lta/APFSDfNlYt4p7bIW16YBiOaL8Mkkk7qZ9pUTxkQW5eXrgQUlu0PbcIPjKw274noxc+Zdm7JEUTaKwfl3UEDMNRDlse7E4Z2wajehWE9RRhGp54mxPeNWv6P49O4T8YasWKx6OhpLcNroEyP60s/PvRuTt4hQ0AX0ybhCXaYyXTUyieWGYmLjI9MvdTuwBLqxM4dlxhp96PLYvVBeQ249Ve84U9+53bt3IpJ21cli2I5FNErr8NTiVGLyWakJxThauj36Um/xyIOs0FSPqxx7GaI7qY+JWiY8zAiAendbXRCetl9yc+FftvawICw7Me6fq/0SalNfi/AHv/L6h83xgeDa647IZ3uOx3kgEPoiEqvB6yNDImtU6AMwDD/UvqLVjjbDbGQo/V3bBZ7oYNMDdSmVex9yVYBGIA==
+ SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_SCOPE: AgBS5scIbyS12+OLJe5rBBvY+PV45cG+XmnHoOyxxqwXPYug4Y0FtbmErcO+pEUFLeJeX8HCqK2WCpr/e87900GDYOyNYiq1moou0Kdkaumg/8PTpm1owdBrIl+rjsuDkBvOIwAJf+dGMYXnAMQH2upesUNTuK4uO3gOhQWxsOh1kK+8J480uNxLHIavAYRKSO0r7aEKV/H7BdTED8g1yQc99s3xQS0U3xUXBCIr1/CaN3PLa75ASazVfkj0Jg/sRdM5ho6YaiJ41NmUhOp5T3FJMqV9Fc/3ckRIIZzql2Pu3EPRBFlLoxajvfhrKyyRupF4eeIwyJg3IMsC6y88s0orojhMpDimug8UpzqYbh59DtXcbbv+bMEjY5ZyGmLoqW1wghi2CfftSFuz9eTUD5yB3bN5Josg4oIfN4OgIzP1aaXn/O/vh758qC3CYmNK3CeSjvBDKGzw5n1PGGHfNSkxOZJjM+F8L241fAfZko9ZxQJgvm9RpgseRVlIj5t7MsmkD6Sei+DnR8I+5bWIfOYA2nJovYrSBMtbdA9pZubUZ7zMgbRuogcS+y7Lkyl5F8Z/MEp1d0RiXFe73l399zSr4jZMglfQcClxu0MuQh831Jjw7H5zzSfhe/gdTQqBej7J9Ct0pUHuXaNNALlMFCbGzvgp3yll1z5/MsMyUQpYSBBUnRbryAI/Dx5EEI/mtY4HLbi0ybZK/O4oDIzU
+ TMDB_API_KEY: AgBfAY6k854QT+IOHrRhBtaqLV8ZICSKqUUGR7rAPPuw0GMlgVG4Yg/9rOYUtKbH+N8tJlPGQt5FxdTFibp2SM6sVI1aVhs1+o5Xdp4fSYf8pJs++meIEMy+JkSQ+RGgpRaAZhmtf32BcwZJRnENWjE6Heso+C1N3WMdqcxgySr3lV0EXEw0cq1OYO9uD6VL1iSfapSDj5kbzrnt9rshpng5WRIL21lqftT8NuCrPjap0Je0XV1hbVBsFHtS869NxiB5L1zxz7arKfl+MoZ6mELZejPdODHukpL55nvD/Nt/zYrgsskZUUJZ6IyWrxPcGil5TzGvVe8niHVaSAGuzqR0R61kQ3B8eUo8CPqIeMXXb6m00u8q3l+2e5RHCzoufR6W8x9L5xf8L+TFyBZhNCCD09WsLWGQG/FmL4lrLRaXGkXklHCxp8kelRABcdExU1f4e1v5ipWa85VmhJy9NZ3pXmSjDTWhULTw+IzAVKlDou3+A3QtlWwAuBDD8wQlZmPoZVfDOoGojbCh8TPRbyyjBE5qdNDvVwktSCfmxrJt58NGVuj08P1ZxJ/98O3Vp7sSEgz0ZSgDfGJpYo48GXHWD+pexaiB3aM64ijS/V3dyRUlZjVqDouMC+bFQkOLOhjwLDv+7pJ9fmX0wyR0pOO6/THhpj9C0zRd8LbS/cC2MEUWTXF2WoDFeStHX81iMVxBguBVOEGGJ2teJgLYg84gkJxDltpvlvIXR429nXc/Fg==
+ template:
+ metadata:
+ creationTimestamp: null
+ name: mindscape-secret
+ namespace: app
+ type: Opaque
diff --git a/argocd/manifests/msa/service/app-deployment.yaml b/argocd/manifests/msa/service/app-deployment.yaml
new file mode 100644
index 0000000..9c72930
--- /dev/null
+++ b/argocd/manifests/msa/service/app-deployment.yaml
@@ -0,0 +1,30 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: mindscape-service
+ namespace: app
+spec:
+ # replicas: 1
+ selector:
+ matchLabels:
+ app: mindscape-service
+ template:
+ metadata:
+ labels:
+ app: mindscape-service
+ spec:
+ containers:
+ - name: mindscape-service
+ image: 194722398200.dkr.ecr.ap-northeast-2.amazonaws.com/team1-mindscape-service:latest
+ ports:
+ - containerPort: 8080
+ envFrom:
+ - secretRef:
+ name: mindscape-secret
+ resources:
+ requests:
+ cpu: 300m
+ memory: 256Mi
+ limits:
+ cpu: 500m
+ memory: 512Mi
diff --git a/argocd/manifests/msa/service/app-service.yaml b/argocd/manifests/msa/service/app-service.yaml
new file mode 100644
index 0000000..4c0e300
--- /dev/null
+++ b/argocd/manifests/msa/service/app-service.yaml
@@ -0,0 +1,13 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: mindscape-service
+ namespace: app
+spec:
+ type: ClusterIP
+ selector:
+ app: mindscape-service
+ ports:
+ - port: 80
+ targetPort: 8080
+ protocol: TCP
\ No newline at end of file
diff --git a/argocd/manifests/msa/service/hpa.yaml b/argocd/manifests/msa/service/hpa.yaml
new file mode 100644
index 0000000..076d176
--- /dev/null
+++ b/argocd/manifests/msa/service/hpa.yaml
@@ -0,0 +1,32 @@
+apiVersion: autoscaling/v2
+kind: HorizontalPodAutoscaler
+metadata:
+ name: mindscape-service
+spec:
+ scaleTargetRef:
+ apiVersion: apps/v1
+ kind: Deployment
+ name: mindscape-service
+ minReplicas: 1
+ maxReplicas: 10
+ metrics:
+ - type: Resource
+ resource:
+ name: cpu
+ target:
+ type: Utilization
+ averageUtilization: 70
+ # 앱 배포 초반 spike 방지용
+ behavior:
+ scaleUp:
+ stabilizationWindowSeconds: 60
+ policies:
+ - type: Percent
+ value: 100
+ periodSeconds: 15
+ scaleDown:
+ stabilizationWindowSeconds: 300
+ policies:
+ - type: Percent
+ value: 100
+ periodSeconds: 15
diff --git a/argocd/manifests/msa/service/kustomization.yaml b/argocd/manifests/msa/service/kustomization.yaml
new file mode 100644
index 0000000..e1cdf94
--- /dev/null
+++ b/argocd/manifests/msa/service/kustomization.yaml
@@ -0,0 +1,4 @@
+resources:
+ - app-deployment.yaml
+ - app-service.yaml
+ - hpa.yaml
\ No newline at end of file
diff --git a/manifest/.DS_Store b/manifest/.DS_Store
new file mode 100644
index 0000000..2828553
Binary files /dev/null and b/manifest/.DS_Store differ
diff --git a/manifest/monitoring/a.txt b/manifest/monitoring/a.txt
new file mode 100644
index 0000000..e69de29
diff --git a/manifest/msa/auth/app-deployment.yaml b/manifest/msa/auth/app-deployment.yaml
new file mode 100644
index 0000000..b344ada
--- /dev/null
+++ b/manifest/msa/auth/app-deployment.yaml
@@ -0,0 +1,23 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: mindscape-auth
+ namespace: app
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: mindscape-auth
+ template:
+ metadata:
+ labels:
+ app: mindscape-auth
+ spec:
+ containers:
+ - name: mindscape-auth
+ image: 727646470302.dkr.ecr.ap-northeast-2.amazonaws.com/team1-mindscape-auth:latest
+ ports:
+ - containerPort: 8080
+ envFrom:
+ - secretRef:
+ name: mindscape-secret
\ No newline at end of file
diff --git a/manifest/msa/auth/app-service.yaml b/manifest/msa/auth/app-service.yaml
new file mode 100644
index 0000000..ebf44a5
--- /dev/null
+++ b/manifest/msa/auth/app-service.yaml
@@ -0,0 +1,13 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: mindscape-auth
+ namespace: app
+spec:
+ type: ClusterIP
+ selector:
+ app: mindscape-auth
+ ports:
+ - port: 80
+ targetPort: 8080
+ protocol: TCP
\ No newline at end of file
diff --git a/manifest/msa/auth/kustomization.yaml b/manifest/msa/auth/kustomization.yaml
new file mode 100644
index 0000000..97448eb
--- /dev/null
+++ b/manifest/msa/auth/kustomization.yaml
@@ -0,0 +1,3 @@
+resources:
+ - app-deployment.yaml
+ - app-service.yaml
\ No newline at end of file
diff --git a/manifest/msa/info/app-deployment.yaml b/manifest/msa/info/app-deployment.yaml
new file mode 100644
index 0000000..c3d0b08
--- /dev/null
+++ b/manifest/msa/info/app-deployment.yaml
@@ -0,0 +1,23 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: mindscape-info
+ namespace: app
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: mindscape-info
+ template:
+ metadata:
+ labels:
+ app: mindscape-info
+ spec:
+ containers:
+ - name: mindscape-info
+ image: 727646470302.dkr.ecr.ap-northeast-2.amazonaws.com/team1-mindscape-info:latest
+ ports:
+ - containerPort: 8080
+ envFrom:
+ - secretRef:
+ name: mindscape-secret
\ No newline at end of file
diff --git a/manifest/msa/info/app-service.yaml b/manifest/msa/info/app-service.yaml
new file mode 100644
index 0000000..0de7534
--- /dev/null
+++ b/manifest/msa/info/app-service.yaml
@@ -0,0 +1,13 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: mindscape-info
+ namespace: app
+spec:
+ type: ClusterIP
+ selector:
+ app: mindscape-info
+ ports:
+ - port: 80
+ targetPort: 8080
+ protocol: TCP
\ No newline at end of file
diff --git a/manifest/msa/info/kustomization.yaml b/manifest/msa/info/kustomization.yaml
new file mode 100644
index 0000000..97448eb
--- /dev/null
+++ b/manifest/msa/info/kustomization.yaml
@@ -0,0 +1,3 @@
+resources:
+ - app-deployment.yaml
+ - app-service.yaml
\ No newline at end of file
diff --git a/manifest/msa/kustomization.yaml b/manifest/msa/kustomization.yaml
new file mode 100644
index 0000000..35e3d97
--- /dev/null
+++ b/manifest/msa/kustomization.yaml
@@ -0,0 +1,5 @@
+resources:
+ - auth/
+ - info/
+ - service/
+ - sealed-secret.yaml
\ No newline at end of file
diff --git a/manifest/msa/sealed-secret-add.yaml b/manifest/msa/sealed-secret-add.yaml
new file mode 100644
index 0000000..23fbccb
--- /dev/null
+++ b/manifest/msa/sealed-secret-add.yaml
@@ -0,0 +1,9 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: mindscape-secret
+ namespace: app
+type: Opaque
+stringData:
+ SPRING_DATASOURCE_URL: REPLACE_DB_URL
+ SPRING_REDIS_HOST: REPLACE_REDIS_HOST
\ No newline at end of file
diff --git a/manifest/msa/sealed-secret.yaml b/manifest/msa/sealed-secret.yaml
new file mode 100644
index 0000000..76600ef
--- /dev/null
+++ b/manifest/msa/sealed-secret.yaml
@@ -0,0 +1,30 @@
+---
+apiVersion: bitnami.com/v1alpha1
+kind: SealedSecret
+metadata:
+ creationTimestamp: null
+ name: mindscape-secret
+ namespace: app
+spec:
+ encryptedData:
+ GEMINI_API_KEY: AgAweotXAYurcNF2aK5pUL+d7EKW9BVbnr/BK6CVMvbLqbiiDc9LvaWUtBeLIZDbyBhYXYval0FCLfee1tFPCWcyqCYG/Xk87LYN9mrA9DaeD/duWYJnlV95YnDrECXkJcdwu5PJKLDpE8RFkoBS7ajdndCwNti3jGo3an+2O2xZwHHcCoWF43eQx/jvIflRuq9frj0/6E/WVOm2tCI9olZxlF6LBQYYcTZ3OAV12ITm9SjkEq9gAxIvexM96T2IeZRT0aurOVi0bwaNiy3K32BUhcWexFQ7E6OFEdEIVPp0tSGaitShnqVYPuwcUgGLISycbquHnWuVn+DLU1XTb/4WxQq/pJVXok1SGA+6WctOYkT9THTUTQveGLHdxVMfTLwzOFxDyeOwcg1QXRy0VQNxMa6iBj5UTmRzRfwHq/e9UQEWiQh1ygBKTUEm+VpQvEbIkayVxmwzQAWMT0J2Hd+QKpzBRqe7y8ddwP31qhtkGVvWJD++bUlS8rBQGjVROjvzQJ4V3sd69wn8B6mEQNZJDGK/l5JPX3IraCD/MqpN0op66/OHPfJo10m9BV7pMyExzFQ/4ByoCGfdKvWEQtLfcYiXgdRoiRa3C1DldxiyWr7OwYFhsW6EuuMAkP5rQ2WY6smHv0o/zR2MPZIEFOtp3kXfn/74n4cpY9zO27mMJRWSRMeZh8hmUO1kpnWhdY5u3Lu6H4oQ+sDgAoA0NU6vFN5fUgiuFyd8YVpbB4TuHUJUw2tVpGk=
+ GEMINI_MODEL_NAME: AgBnSUqNn4kOC4LCOSNHfgqufnvrMTO8onOFY9AYWNwRv+ASQYq4J0YLFG3OMuAcBz/il2j6acBR/DSQB2Wa6a6lOqu3uREXQqDI3/tLMBUAbDjxZGUwLXrahOocscUpllD+eJEMmOnGI3HNtcSnPIzX5DX5CNlmNVlOoayBUIhLgNQb/uz2ACJTtVC6zMze6HoDhJzLtX8cQ2kvU/ut4kG9IUiMfRdGeftrxPWOcbutnOfBOzPwCi5ooxKOLI043cXi150Ztkisfy0iqqz7+j9ZC6ZLgPutOedJLJxYQ3pWdYjdYzt0/s/Oulrh3sMoXXMy8PE2MRoor8x8i4pb7yxEYNMFqSyRU4SvMuJNc0kRadaAjSfn0qhGa7gbW1jvzVc03YxbYHTnhyP8p2Ap1U+acECBVfmdt7JVCeY9K5hS6Hsqjea4U5gzy8GQ02V/vqzOuYoywMxN87FwedFgXjP7Feg+c1OiD1DfxpcWnvaiMorwdv4u1rrm1DobkdjJDZPypDqebkNfavbzq8Z+Rcj+OpG577cHyK3ruQutDuR4ctBtQP289JMyNLfNJ+lq38vlyyQomFHrc7UbxRsUXlbxyRvvqbjT53urPIQf9liYGG3o4sYStE7G77lLBqanRe+2MHGb/r8MqBlccMFqPTtNDRRVKghabDaeJAGXOG+relLI9Apfq/p7AOgRYVsAOCxYJlP4CZH3DnEpZb5PWN2PhxzStdq9mkrjKk3PjgMM/iqEVw==
+ GOOGLE_CLIENT_ID: AgBV/c0uj2NXls5mCEq9Cp89rvrzP21CpP9Jl88fTBL81DASw4EIUdjVceYUOz5ALgkDbXfKFS8pS7fW0WcRIa45OkljtzrTKf+2U6z2SBA+DeIIeBEIM83VWO4rzzPle4H+kW5iZrRTKQhq6Dgp3gBSE1HUDqufNVjuNDRQnVG2qZ/O3GRgdcOJAn06Keqr7xiZ/xb/+bvdG0IUbIhhYMka0bHcqhd31q+TwuVIgEmD7khCfQrmBccLhM3vqjOR6JNmJLwDYQoAJttVuUZRLB5EXjwTrT28qPPnTuacgs5+XryPfzJ3t+T6k77ceFn3obSYlqiEQzNYDnGnlwHpkSRAbGg/Sm4Msi2C0w6YtD2BrY2nV3aMcdSHhGX3JZ1XrTeHddwCBQBEGDp0cZetGEH10U7nJMzG4TfEiFuYfljXhNVbcMX7lMhJnTFIhxj3Suu1ayiXeIGk7iIozE7nCYRzfMskWeA+Gxi2UnWOcAwdnhdbK0oWmWx7aVGdiyhYPiL2ZyZw/09KZMBN09RENNepYzEmNJUln6ZUfl2dQUev8DqoAUNWoiAai/JZCXkpm/iOrSkRbz5CTBS/UrH0NC6kOU0Mkyrfe/7baVAor0MzHckGfG5qGPFky4Kt+jo64QUSXpYdY4Bt5xJlLAo/zzFLtJ5FW/CBQeYN1bbFgDdxrGaooGVL5dBjVzUtA/EtdghClecJTKPt7dwOUPgWx6DdNpbyCAEhB4IeS76JL8NhcQ1E1dFGRqXGabJGTy5Y2GusfWrGaV7Bg421yfzvAuG9WM5kdd2uj+0=
+ GOOGLE_CLIENT_SECRET: AgCXz6BBUN+fnKPMtiPylkLOtnlQBddlnIj/2M4M96ZPO1w2HEsWJGVoZ9VxA5yBP0qRBIkoz/wprm+AEECheTUA0FKvEVS30RYGOHjEGcpo9eIZSJdWC4nk5cUTi0a+s7enf3/5SM9e2e9B3bKrSclTwdEUE+k5itms7/kjj9QBqPSL6RWsr5epXfv9nKOjlFtJUzb7Dpg/J5nz11/YzEWzsgohFXWPYKsOVZBPcoD8dXQ3NqbKHE7tEubvHk0njLpCqeBN7Fw0BbfjgpjOsrKvjB753ITv+Xaeg4/ulROlYz0x1wF0k/wRx2VDZSJ6briTsFGUut9kecvsRwIGnpS2PJXNqXuS8z5EBOc6b8pIg83ktkMkLnOcGm9PQ5KCzxX8mtm0Wd0+tfs2Z0Esi9M9QHMqcxu5TCPmfy/5yiYfT53souryVcV8DiM/sY43cp0HIQtP1oTNB2ryKq9/nPUB/RYYNM2LXPGU/07NcdWuwR8maO5k3WCHrR6mzH84AwDbClq6vjJOJibw1UHQvftEX/c2Q3OpBlscG5qDVAqJ3YLP/O0nCtANt4v50ArG6NNVYK5YxO9kg9G023CPFEGIHdrC1QxWi0+/aKcMLGEn8Ch3Cm65kLrNdCfEKu2IP5uL05mZGjsbjSbETTzhyTH/0e0kDeDdgQ//Yt52lQ1Gyho6UVrgQ7S/drJCHDMKRryfyFwT3mxfibtz1U9tkUnDsiAgQG1g+1rt2HBUTGfQQdSDsQ==
+ JWT_ACCESS_TOKEN_EXPIRATION: AgAcmHwZejQ4m1IHmJuYkQaXglep4rBw8ffqWQp0fgGnmB0fCpSNjX1M3+NMrVbZfZKMQYrAXHXOvZ5LkHD2KRZl815Uri/kTiKxTZqK8D1RpvTFD+AoSVYQHD9jM9UDOM2OrJaJbaB46+KpL7lGR/jguneNWMLlIbxf2oEFWdvFFsxDj1ZejHzuuY9FytqYzCWBahSA5jVyTTEVatyYStO5paRp91omrwvldBjhbZfVY9ZMNVhP3ISJFuGNQpcY8gHAbKYoELyxjq5rG4LYtpbTL16XFUwbJ/krBq4RmifkMojkFoHTkGCG2SJdHdrIg969dgDaMdjaucSLKRM4Bv6KQzQsuKfNHmvUSY+kILM+HHRlNm7w14o2v9q+ICdbcOAoVvtYtK+Ty922q2wc2i9+ZmXD1j0FWfWRLEM9RFqKBPrup8kIwLVHYxVE8W6Mk7HIIAeA/Sy7Jd5/QFS/WuqF4jvfstMGd/XkTWiwhrxlJuO2jlgbK7Ekijh8NVf2i9hvh9WFO6ndUBJFCxTCbkCp5mnf3HsVrK7ywyjWOYYJWQtDdN/q/cwHoy5pOig7a9Y6PDYH1/kAkorIVgjwoRKot+kx9PL4MGAFKw8MjHVnYPO5gILGZOnyIaBvkBE4cM310mmaJyq/raxkGGPsGO1stBE5CQ6nCg6TWzmfgtWuXVacWPeqYEfdT2gd8RhGfkAwHl3zPYsj
+ JWT_REFRESH_TOKEN_EXPIRATION: AgAmZHjpZjNGTJE/WPgz8d+EvcXciybJn9K3LQAMvc6I7Huaody/lUU3UwkLFgZKiu556YR8+0vnvME9VMjuBwH1K/RgKT6qNaIxyVESdHIHPMvmczsCS+wS4b3yzolyOMrmLHo9DJKIHu8r5F+c/7nlq3+MiTp+uICgK78BVbzfgJupxoh44U+AWGE5lohLMmzWGCiztOYdv/EnkkMxw2Mq44RUGVvp3HxyHKVcvqF0KBCCzU5bAIH9cb3O8q2wDH7XX6PzeHUj+Q8fhrjl8Q4RPJINakCG7nISAhd7BfSEyax6iiu07kcmdRAR2wdqtHAtROVxOnTeOcxonCrPKlDjUdRtOyVWR6CvoSIyq/6lUPR2S0NBN5egfSjWdAwwrcndGXd0wBx9tApPfwOLygQRba6C/3KqdmcjTyMePz1fC8bwK7qsIaM0SVPKV7En+UfUeuQ1Akqp1zUNxL1eCdqUjS1KcHhJpNwjjoCk7EEnZd3aWGhr5TkHS4csXf/kx4YMXEqGkgnMeQHYCgnF307+dTWGVF2Wj2CyHEtmzV1ZtRdqPb5KSMW8vbDiliAkR8xmBCKedE8OebDrj8jl5HiyQjoilPxwyCLHL+eRAc6fJAF8DdKLbWTTdXY+njzLjXWSEkG/2/JItW/t/Lmfhdzhzb1gMjfsQjtpNCDieO0vEXWRXm5F+9APbtJCVD4uMcE7vVZ6Xj8pdx34
+ JWT_SECRET: AgC/lr9bzH0fQZ9ZYL2EmUgwZRwsaHgfD/Nxr57WUVnO0BHwCPcrWUuDwXCFLMtSuThYrzcLCQtJm684BeBXJSSKY2gbBEbSL+j9SVINSr6vxxpRBDWX6mc+VpAztxufAIOVEM39ZGZMG7MEAJ2I4Qyam7e3/zG1VZwyLIIbYVtz3MNHM//p+cIaSAX/cWKpaSYyTXIarCDqZm5NPSVpAXGIMC6a85gsVmyiWXyS85Bz3TpnDJ6f/mlqua9pioHn4OCfpcNRxPLjiEe8SUHvMWJm4M/7xurXM6jznGz0rPTaXU3QXtTgLUkpJ59UVz9bQmRC8cIDeA6mcGC899/SsjYL1Vw4+2twuM6a/qb6HMQNMd/8qW521Sz5y8nPMo0frbyGQidXotPjbTQ11PhC+NwEReKjMzJMfuhP3LXldNsOc4n03nTXjYQOuVG7zVzZ+9631O+PEGTJ8dck8B0CNc4I38fiFr/FQmIaNqzAQDRsUvG3DeO/G56p3tdIlLNfanXUM1bxH//gqJEgUMLJLI7QxTGUSsHx2OXUR2yinZiP8G7dOCHQr/mWPOMYboE9EVwZBAELNs7q/9nVtxz1a8mypZkaFf4ji4Nb1T3/rAVF+xzFhTqLYQlNLkXSgAiOA3qmDEkTcr7YqGjSCYpCajutkM/GwPhRryA2n56qcxTeGD0/1APMtHGb/nlzw/m3UT+MKTBjiv0VR+h9T7YeVh2MEqMgHH9cngycAutsX0cITEPLBm5JCdxHcz06vN1KazVuHYbd4vxLjR29vw==
+ KAKAOBOOKS_API_KEY: AgBmoQ6rt5wZRhrqykmWmYTYkY1w4LbEDb20HpFXQXujTf6HWuQopeNTFSnWJLCoP2hX4fEB5oInPxly+vveENU9CL8Va3sMQ83iug4j7LW8ELfTR77o2elUfM4IPFltol0mitVLdgWDVj11WpnWoW4lO1JzXADwnNBSZjSy9t0TaNq8wTG/UKOTByKep2qqem4NrZ3jL9OHUWaeMAVXK6Do8I6T3pJAFyQXpWPsIq0zWXprkaqkZNYcVbs8KfmflAaOvyL8Pp0gnMeFV9x9k5Bc6aLmX+NAa64PtPrUARtO6aEkVTZSx7aS352jvLBURoAkGWcmgxiE0ykD0A8DKgEee77v7fCyUizSk3Uf7hXgACgMWlSaEVNgNcUW+5HIIRu+NAUxp+IwONhuZmhJfKctFfzKKg670lWh43JPkNHvjSXe/t9GHuoOtg4A2eX2ST7e/2QqaaK5RE94yxigyJNTic8MDPdxbK6Dp5TEeNAmbpc3rvIVmit7whTDFM9KAxnlmaN4FtO2s6XPUZeJrGh9FTETYuoGHxakLj71T4oAU0Lr+fRrNJ59YJpvrO69M7S51Y3L5PUzUDaFuuKnhPB1W/RsOpJIJe9xG+9e1mddenkS/RlKDfy0ovK8/G1Db0gqqpOmiRz/qbGUC7EEIGzAYen4+77V3ZJHhjCqzqK+DLH0e+PXwghRnqYapJ5lrG/gr+gL2k2pmjEthvVwDobucco0CdJpUtXDjuTjXu1DMg==
+ LASTFM_API_KEY: AgCSfC220VgPsPY50L8TRg+6zJqnHGjHbyH2n650GJCW5/PEvbK4UsTM74qx3+JWPfW37ESRIVGxZEAcg4IwW4K4tQ7rQFWmmcDQ1jfcRZsr8UxHge47VbjEDYrUBi1DCwL6iMQOKLHIOPTjchcjRSaiXWwjJZI/G2odOxxb3Ibxyl5U/p0paeWP3/avTkifKFFzoctFS8qRZWe7nJudNLJs7KSDNY9yUhlZzN+syTilVKJWe7ZwYUb/sg2G6QhIEneslPa+wYaIHsk4KTeV12T9duH2ZNFcz+uMyj/oVV7kBXIW/AYULJlrjzMLammETsrTjbjpt6Hmg8Sqkb6POe6vGoZ9eN5dqlQOY6v8l7kcARekBGMy4no5z+38B2KptfPT0ap37X5qNklgLXyiFzqyoeXgQF8YKR4Hse/d0lIOODrZ/92IcnwIHTYSH0DkCbRVGS6GHi6WUeMlz48GYfgw/l1hj+91JHto7ctwiWKN9eKfn9+/TqE1LL1UBdZ5d4aCnDsGdnpSLkxv048w2PhZCxMssj3f1Unukx9QlUoIwFT7omhPL05bFKlep9KRtGA5OKmSZ4WN02pIQv9tCdNoQ+xAK83RpgbgZ57ZJcMKhxont5lehhIzAvqWh3j45yLCXSp4qJcHVDxk3KsL1UxEz2ubAXbCbhTy/5Qc2Iv2bL9z4gsFnYHIQm93g2b7bZ53Tun1x2wHZZPUExaiZGKnRFBLwzCR2zB7pjMtPSBPpQ==
+ SPRING_DATASOURCE_PASSWORD: AgAxYuOOdSji7anSUesGffCAiInR4jARO9uwkwyLj5hbHw5S6kIeZxwtYpHytYHovzDxd2XTJqzqa9FAK6UAuyCBNG/H1+aG2GZ2hDQN516wO02DBYTBmNCvR9MoeRyLRaOWvqgaWOik+ogGJ1SlrEu8kd0fSIXOQk64gs4dqZTlcgjPaHYtnPQ2/Yim8hQiXd5ziGTV72YYfHmNDHKvHpmj8bV9jKydAZqFq/0gi2Hzs724Bmahy3fAq4cwEppYB2MCKnfq7LA5HG6pT1uwD27NxwjwESwfYUaFcW64FjhmlSqnSD51HbwAoK/p5vSixO5DKPfZOTrZViJyMPuZr4Fid4uNW+14DDloezVaBzKgEa1fTpWr1p4zxWlL4VJB9F8ucowpm9Owjzp1BBpOIvGEm/K9vIw1Qqqo6dDVk7VmF0z0mb5TQjrFTQGt8ejohTWKh2f187riVvKv9yFbo9uKpVFP2Qt4Dak5F4+/Lg6Py1uZInuu/mqWaPlhDpfsqMo6X1C/i48wmchkM6GiqWhmQvcUhu3c1zvEfg7jhhVK/4Ld4WdMIL+zEB6Ke5RpDaiRxFZlFNoDrBaxDlioj4RmDsSz8T8aTBGM8Qgr4EA7wIUnQRcSDKBHTVSqWyuah5tYUO2zhYvKfX3UxTRbMSLcrQhqYs2XG6xzK4DulFGIThV0Qs9Gh2K+9HPhG8nfrfM2yeeJWWKqUQ==
+ SPRING_DATASOURCE_URL: AgApIFFdHyE8r+fmNQ7vJt39M4wBDr+xx6k3ILz7XWmE2RUhHwTM3Yb1T3uZ9MnoiuNqB0+S3y2TNuJnyh0c5A7PPkOXerdpJUCFCpAeoHBHCp3RyViV4O41lR0AoPGNGsWOBL/Nbk/9oim/Kuxij2ttCB7KV+GHi7CKrcyPPAsdyiYQ168lUAh0cHlA63n7BpIEBwSKT6ivF/L0+E7G6yrdFUx7p7vyv/FqW3P3fy4ErFvjhGL70vZLte2lbun7b9d+kfLIveIwrxuwC4lYrGwptFElsfPpdsvuKqHPPlR4TtxBdIi4mVTweUCP/Xo257x9MmF/qJJaasUBYXrTqOseVcCYAMJtgzkmlA/dp8+VQiFG/bzjBknCEADghgNJW9qSaeXFxuWiF1jSBGgcwNbTciKnmcLxMfmICHF6sgK78wPEhA/ni3kaI84yj72h3HdsJZzo2UILyG755U9YHGrFSF47+P+tDaYyef8U7gAVZ3CnAWxQWvftQR0akA1ERmzgJQX/W8SGEGSoFDX6Lc0lsdeJJ1m3heeEasuONoDKn6aA+9HvYhGCHp9mEO6PAR9w8vikfMWLZ99T5f/Rq/0GItXQSArzz4ddJ8LygVs0XUC8g3lkLq1fDfNpha+xq2XZgBrqKfOEIqj+T6Y7LsbW0YwgH71/Ioyc2RUrUkd2B1MHzI1Kq6IpaPcDCBYm+LHijcVk0QKJPiFydd5IY3UD3TB5j8k1SMlTkKWOf9XpptXZwMAkkRoVrkSnIcJfyOMFrKxd/czBp1i2vb/0F1Z9IjHX/L2Wjeajigi77gfCz5V01U0lZJqftFYSkgFeH9KeMBQqQg9krF/XC93l4tybEoiu+rGnuCs=
+ SPRING_DATASOURCE_USERNAME: AgAahwAYZH0klBPHM+d/5LTcSHmuNNZrql4qQadjH9z3nqZ3xQz7Par9IipkuP5+/VS3PBnexLRzKD+Tpavp0d4tX7I6c4sJp87asCBfjN+BHZfm8PYSlbxA+4GH/UO4gzLrFtP6PIkFBQA4BSp4NkEHuE+y3RaRCY3Tc9HmL/oQoVe54CXPHzJPStqoaWjljsJhuDiXU7bN1u+YDjzkmfHoxSdxSSx9fIfmm0sheSAh6VmLhWqXkkGt/RupRmMdSWcYRjVEMo6o9JGZC8iyKuK2u0Ody2pZBBER77ZhpiQn9+J6gIvOovzHKjieICWCIrdmWN7oyG0ujEyEVpLSyBdOWKdvmXN/6TJlqlcHU6uIjxB0jGVuKuhu/3xT0G6sxGyi+eMKNMVsP/kaSFr239PihWTqhTdkNY23tG9qrDM2SiGzYWk+jqM7DDD7it3RClfIsF9EKSah2LsShsfaNNhpqI4KFwpmSzQNFYJhG6lTxf0KPF6t5B3Swi+b9knTCZ0XeYLASKE0Rg1+H5WleJUf9Qgin6ZHCchlINuEfsznr6nwEVc6lOWAChAWBu6tKX4+Q0L/V+VwwqGSeESRei4a57a2i330AY76SRagA60UF4EnB5a5h9XVoIiLev/M6oPacCwyh1gZCuU0lWAXzR6bq7HN0eWak2MfiG80yehYRq8/Kh43m1M5hlC3hobdZxM4JRaw
+ SPRING_REDIS_HOST: AgAJuuuddj9HoM8HPEkZ2dJlICnI1pvzfK9jQMx14L3c1W8tf+lGvnGbV7l29rU7rJUOEiTE8+NA+8YfOw3saTTK3MJQAk2uu4t1F9gII6qCLXH1FNxm+1UesGZAZD4EuaJlgZwSdnRZYO20/fDP05S44lK5fcg+PU1EImacYECnAE8hEXzPpGmThzlQ/zDizp630UiQAUHQHXIim5aFG38Eo4eFxwJOewuOQWIS5EbB/28ytQ1cLHQQw8NrdzgQXUIN3X4Dc1B1uRR/41z/07TnG37zfcBVdWUfjpzSpOe3TgBqZGXvzZLynkVEHVOdeHnBHvwL5a1woVZVI/uXZ9s+DbgLLkNGwNhLOSgMGpW5EOIxDBLZ5DW4X7nn0PV+jgTTS7hKOdUbloQPQRT7e3QrOSFO/kEEsRcKUmtiAnbaA5OpZvqdvArHgazMVoalzoTGzP0BJFbNO5o7MnjvkNBqPiHvW9umlMfUpH4LcIuvw61OM5hnWwZ61w6n+OgU4BeaJmbdwQdanTM8Kt6hl+TXezKRFLjxGCdJvhSXYT+Yjobj7phh9RQYIN2yl6Gz69SZnBIl19lH00fYdd6ZKZ0ENHvQNUN+OdqZGTScJwt9fkiB7tXVyL5+V3/toXALp+WHj+QiH1v+5X/MztyIU00LJC2CgMreMEOslvnDJ8RLevSa/VBTAyUt1R2pdUjQnF8gbvz7tJBl6FVr3zqwyzaOow0Tgnd7PydWlDv77DY7z46o7RldWz82PP0CVsAr+t0=
+ SPRING_REDIS_PORT: AgBDshqbORSfAx0g25jGGGAUbyySO+IJEm5l3txqGhDxVdG0lBmTb0/Y7kkiAD4SLfnT33a6LvBf0C6Buo+jVjkPfQMsziRLf/03uvOUzRrpOMKYO4GbByi6ORdPGDE+swUwF9kNIUCG6sktu3K0qypn707uOXK/VvCIcLqfGc2ekBKPdAS9dkHd8Aa2qmTp1iIZBS48pDQhu7L5FmENWWpDkHEfW+pTkAXCnqkOOL70aNgar97f70EFB2bjCQaa1l61w4qUMO6l8jLSqu+f0ttr9Ux70Www8Dsb9b6bNm/HPP9VDzyYmrNSzoW8OG7N1Bnm61W9wVTerSXHbyXR+Dwt/vYmuLL6e7fa+PUt6xr/Ntxeg5ehr+VBNzWyNzevSohaJjkYU+z2wbXpWLKc8Ja5xLGhriluJPmfgBykTCokdOjYoutE6Wl/m6evSxF+IsSKmh7LrUJN0djQUfAf9nPFpVkLZ+Kn8GpkMoe4NKJ1ljjrK0Lq34hSireg+VxFjKIu9398Lr+4IRWUASJtMzDb51JrsZnE+uO9lXQrq0Xu8yJB0uhimEnGQ2f52WIPzWi4kQRKL36xYEtHEm6Zr89fsVGF7WevxtBLOwfl+WIaUM6uHOek4X29gyDfWEhfIz7qRrk9GygRvw/PnGqg8nAWEbUARKSqAE8S8MSAMyWQYL0hM1VRNJx6a/trQ58MdBk9HfHp
+ TMDB_API_KEY: AgCidkV044iXOr62n/wizzIaRGIGnqbDIvjJ58opMMf0XtbZDi3arQb7NX7KQDyZCgLbte2YRm3dxVY7uqZB6Fr3g6T5XTd7Kftkj8Pv4FwY+sDwLRFOA/9H3ubF1CfoMgMEE/VnhEpKAYFsYo2CiuHJGV+zognFSQwZBEc+mWS8klN39zGI28DFMJQNfpQAvr/3IuY5cp+61LK+F2ypS8yMXZrEyOFefp8Qzk+s7uOgRJV9N3W46Xypy6G3V9DCDNDyijeQ+Jd+gb9jbIW0uy5SJlgUS3w5iCwaVrNtlHvydItnKM+UuIcwD68EUEawlVPZmTtDvsLcB62mMZuNzOvM63rX53AvH/QCBDTZDiAoQxXdU50iBzMUg++jLaUQVRk5wQX+Fa0htVxycTD7Qb74hKwVhH4jeke0WBap2+dsRf9Hht23eve5ORJwvjuVGJZpY9ACRejNDDvLXA9k87r3oNdHjpzO121X7dK6ZjS9DTgk6tMaPbwy0PeF/HvL0B/F08walnq4GxTWHO3GoniO1PoUU4zQSFoN65cdI+ZoMUU5SdI820kxSD4Jt8jpMLVCDiPIzQrjCuxR29oCyd5hmnV8NiSfeoqHWARTBZChaiB6W2B1HPGxXUgFrqfgdeIVOfvL2pd6iZR9o9vux1/9GMHHw81NzePUQ5Lloq5XjGKR5JPNV2U2Xh+n6Dtzrhp/r4e50ACa7f1fFglH9bLeRr9em97dc+IxqqzmLcpvbA==
+ template:
+ metadata:
+ creationTimestamp: null
+ name: mindscape-secret
+ namespace: app
+ type: Opaque
\ No newline at end of file
diff --git a/manifest/msa/service/app-deployment.yaml b/manifest/msa/service/app-deployment.yaml
new file mode 100644
index 0000000..d28defc
--- /dev/null
+++ b/manifest/msa/service/app-deployment.yaml
@@ -0,0 +1,23 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: mindscape-service
+ namespace: app
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: mindscape-service
+ template:
+ metadata:
+ labels:
+ app: mindscape-service
+ spec:
+ containers:
+ - name: mindscape-service
+ image: 727646470302.dkr.ecr.ap-northeast-2.amazonaws.com/team1-mindscape-service:latest
+ ports:
+ - containerPort: 8080
+ envFrom:
+ - secretRef:
+ name: mindscape-secret
\ No newline at end of file
diff --git a/manifest/msa/service/app-service.yaml b/manifest/msa/service/app-service.yaml
new file mode 100644
index 0000000..4c0e300
--- /dev/null
+++ b/manifest/msa/service/app-service.yaml
@@ -0,0 +1,13 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: mindscape-service
+ namespace: app
+spec:
+ type: ClusterIP
+ selector:
+ app: mindscape-service
+ ports:
+ - port: 80
+ targetPort: 8080
+ protocol: TCP
\ No newline at end of file
diff --git a/manifest/msa/service/kustomization.yaml b/manifest/msa/service/kustomization.yaml
new file mode 100644
index 0000000..97448eb
--- /dev/null
+++ b/manifest/msa/service/kustomization.yaml
@@ -0,0 +1,3 @@
+resources:
+ - app-deployment.yaml
+ - app-service.yaml
\ No newline at end of file
diff --git a/mindscape-auth/Dockerfile b/mindscape-auth/Dockerfile
new file mode 100644
index 0000000..b71999c
--- /dev/null
+++ b/mindscape-auth/Dockerfile
@@ -0,0 +1,4 @@
+FROM openjdk:17
+ARG JAR_FILE=target/*.jar
+COPY ${JAR_FILE} app.jar
+ENTRYPOINT ["java", "-jar", "/app.jar"]
\ No newline at end of file
diff --git a/mindscape-auth/pom.xml b/mindscape-auth/pom.xml
index 27bf349..99d7f3f 100644
--- a/mindscape-auth/pom.xml
+++ b/mindscape-auth/pom.xml
@@ -44,13 +44,46 @@
org.projectlombok
lombok
- true
+ 1.18.34
+ provided
org.springframework.boot
spring-boot-starter-test
test
+
+ com.auth0
+ java-jwt
+ 4.4.0
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ com.mysql
+ mysql-connector-j
+ runtime
+
+
+ org.thymeleaf.extras
+ thymeleaf-extras-springsecurity6
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-client
+
+
@@ -63,6 +96,7 @@
org.projectlombok
lombok
+ 1.18.34
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/controller/AuthController.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/controller/AuthController.java
new file mode 100644
index 0000000..991229b
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/controller/AuthController.java
@@ -0,0 +1,97 @@
+package likelion.team1.mindscape.controller;
+
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletResponse;
+import likelion.team1.mindscape.dto.UserResponseDTO;
+import likelion.team1.mindscape.entity.User;
+import likelion.team1.mindscape.repository.UserRepository;
+import likelion.team1.mindscape.security.jwt.JwtProperties;
+import likelion.team1.mindscape.service.PrincipalDetails;
+import likelion.team1.mindscape.service.RedisRefreshTokenService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+
+@ResponseBody
+@Controller
+@RequestMapping("/auth")
+@RequiredArgsConstructor
+public class AuthController {
+
+ private final UserRepository userRepository;
+ private final PasswordEncoder bCryptPasswordEncoder;
+ private final JwtProperties jwtProperties;
+ private final RedisRefreshTokenService redisRefreshTokenService;
+
+ //사용자 인증 검토
+ @GetMapping("/me")
+ public ResponseEntity info(@AuthenticationPrincipal PrincipalDetails principalDetails) {
+
+ if (principalDetails == null) {
+ return ResponseEntity.status(204).build();
+ }
+
+ User user = userRepository.findByAccountId(principalDetails.getAccountId());
+
+ return ResponseEntity.ok(UserResponseDTO.from(user));
+
+ }
+
+ //회원가입
+ @PostMapping("/join")
+ public ResponseEntity join(@RequestBody User User){
+
+ try {
+ User.setPassword(bCryptPasswordEncoder.encode(User.getPassword()));
+ userRepository.save(User);
+ } catch (Exception e) {
+ return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).build();
+ }
+
+ return ResponseEntity.ok(HttpStatus.CREATED);
+ }
+
+ //아아디 중복확인
+ @GetMapping("/duplicate")
+ public ResponseEntity duplicate(@RequestParam String accountId){
+
+ User user = userRepository.findByAccountId(accountId);
+
+ if(user != null){
+ return ResponseEntity.status(HttpStatus.CONFLICT).build();
+ }
+
+ return ResponseEntity.ok(HttpStatus.NOT_ACCEPTABLE);
+ }
+
+
+ //로그아웃
+ @PostMapping("/logout")
+ public ResponseEntity logout(@AuthenticationPrincipal PrincipalDetails principalDetails,
+ HttpServletResponse response) {
+
+ // 로그아웃 시. 리프레시 토큰 삭제
+ if(principalDetails != null){
+ redisRefreshTokenService.deleteRefreshToken(principalDetails.getAccountId());
+ }
+
+ // AccessToken 만료 처리
+ String expiredAccessToken = "AccessToken=; Path=/; Max-Age=0; HttpOnly; Secure; SameSite=None";
+
+ // RefreshToken 만료 처리
+ String expiredRefreshToken = "RefreshToken=; Path=/; Max-Age=0; HttpOnly; Secure; SameSite=None";
+
+ // 응답 헤더로 만료 쿠키 추가
+ response.addHeader("Set-Cookie", expiredAccessToken);
+ response.addHeader("Set-Cookie", expiredRefreshToken);
+
+
+ return ResponseEntity.ok(HttpStatus.OK);
+ }
+
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/dto/LoginRequestDTO.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/dto/LoginRequestDTO.java
new file mode 100644
index 0000000..b643b37
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/dto/LoginRequestDTO.java
@@ -0,0 +1,9 @@
+package likelion.team1.mindscape.dto;
+
+import lombok.Data;
+
+@Data
+public class LoginRequestDTO {
+ private String accountId;
+ private String password;
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/dto/UserResponseDTO.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/dto/UserResponseDTO.java
new file mode 100644
index 0000000..0845b76
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/dto/UserResponseDTO.java
@@ -0,0 +1,21 @@
+package likelion.team1.mindscape.dto;
+
+import likelion.team1.mindscape.entity.User;
+import lombok.Builder;
+import lombok.Getter;
+
+@Getter
+@Builder
+public class UserResponseDTO {
+ private Long id;
+ private String accountId;
+ private String username;
+
+ public static UserResponseDTO from(User user) {
+ return UserResponseDTO.builder()
+ .id(user.getId())
+ .accountId(user.getAccountId())
+ .username(user.getUsername())
+ .build();
+ }
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/entity/User.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/entity/User.java
new file mode 100644
index 0000000..33662c0
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/entity/User.java
@@ -0,0 +1,67 @@
+package likelion.team1.mindscape.entity;
+
+import jakarta.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Entity
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class User {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private long id;
+
+ @Column(nullable = false, unique = true)
+ private String accountId;
+
+ @Column(nullable = false)
+ private String username;
+
+ @Column
+ private String password;
+
+ @Column
+ private String provider; // OAuth2 제공자 (google)
+
+ @Column
+ private String providerId; // OAuth2 제공자가 제공하는 ID
+
+ // 리프레시 토큰 저장 필드
+ @Transient // DB컬럼 생성 제외 - redis로 관리하기 때문에.
+ private String refreshToken;
+
+ // 리프레시 토큰 만료기간
+ @Transient // DB컬럼 생성 제외 - redis로 관리하기 때문에.
+ private LocalDateTime tokenExpiryDate;
+
+ // 리프레시 토큰 만료기간 업데이트
+ public void updateRefreshToken(String refreshToken) {
+ this.refreshToken = refreshToken;
+ this.tokenExpiryDate = LocalDateTime.now().plusDays(14);
+ }
+
+ // 리프레시 토큰 유효성 검사
+ public boolean isRefreshTokenValid() {
+ return refreshToken != null && //refreshToken 있어야하고,
+ tokenExpiryDate != null && // tokenExpiryDate 있어야하고,
+ LocalDateTime.now().isBefore(tokenExpiryDate); // 재발급시기가 올바른시점이여야 한다.
+ }
+
+ // 리프레시 토큰 제거 (로그아웃 등)
+ public void clearRefreshToken() {
+ this.refreshToken = null;
+ this.tokenExpiryDate = null;
+ }
+
+ public boolean vailateRefreshToken(String refreshToken) {
+ return this.refreshToken.equals(refreshToken);
+ }
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/repository/UserRepository.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/repository/UserRepository.java
new file mode 100644
index 0000000..80de267
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/repository/UserRepository.java
@@ -0,0 +1,8 @@
+package likelion.team1.mindscape.repository;
+
+import likelion.team1.mindscape.entity.User;
+import org.springframework.data.repository.CrudRepository;
+
+public interface UserRepository extends CrudRepository {
+ User findByAccountId(String accountid);
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/security/CorsConfig.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/CorsConfig.java
new file mode 100644
index 0000000..888e2ec
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/CorsConfig.java
@@ -0,0 +1,42 @@
+package likelion.team1.mindscape.security;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import java.util.Arrays;
+
+@Configuration
+public class CorsConfig {
+
+ @Value("${server.frontend.pageUrl}")
+ private String pageUrl;
+
+ @Bean
+ public CorsFilter corsFilter() {
+ // CORS 설정을 URL 패턴별로 적용할 수 있게 해주는 클래스
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+
+ CorsConfiguration config = new CorsConfiguration();
+
+ // allowCredentials를 true로 설정하면 allowedOrigins에는 *를 사용할 수 없습니다.
+ config.setAllowCredentials(true);
+
+ // 구체적인 origin을 설정
+ config.addAllowedOrigin(pageUrl);
+
+ // 모든 헤더와 메소드 허용
+ config.addAllowedHeader("*");
+ config.addAllowedMethod("*");
+
+ // preflight 요청의 캐시 시간을 1시간으로 설정
+ config.setMaxAge(3600L);
+
+ source.registerCorsConfiguration("/**", config);
+ return new CorsFilter(source);
+
+ }
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/security/RedisConfig.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/RedisConfig.java
new file mode 100644
index 0000000..9b6b05e
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/RedisConfig.java
@@ -0,0 +1,36 @@
+package likelion.team1.mindscape.security;
+
+import likelion.team1.mindscape.security.jwt.JwtProperties;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+@Configuration
+public class RedisConfig {
+
+ @Value("${server.redis.host}")
+ private String redisHost;
+
+ @Value("${server.redis.port}")
+ private int redisPort;
+
+ @Bean
+ public RedisConnectionFactory redisConnectionFactory() {
+ return new LettuceConnectionFactory(redisHost, redisPort);
+ }
+
+ @Bean
+ public RedisTemplate redisTemplate() {
+ RedisTemplate redisTemplate = new RedisTemplate<>();
+ redisTemplate.setConnectionFactory(redisConnectionFactory());
+ redisTemplate.setKeySerializer(new StringRedisSerializer());
+ redisTemplate.setValueSerializer(new StringRedisSerializer());
+ return redisTemplate;
+ }
+
+
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/security/SecurityConfig.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/SecurityConfig.java
new file mode 100644
index 0000000..ad68b2d
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/SecurityConfig.java
@@ -0,0 +1,102 @@
+package likelion.team1.mindscape.security;
+
+
+import likelion.team1.mindscape.repository.UserRepository;
+import likelion.team1.mindscape.security.jwt.JwtAuthenticationFilter;
+import likelion.team1.mindscape.security.jwt.JwtAuthorizationFilter;
+import likelion.team1.mindscape.security.jwt.JwtProperties;
+import likelion.team1.mindscape.security.oauth.OAuth2FailureHandler;
+import likelion.team1.mindscape.security.oauth.OAuth2SuccessHandler;
+import likelion.team1.mindscape.service.OAuth2UserService;
+import likelion.team1.mindscape.service.RedisRefreshTokenService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.SecurityFilterChain;
+
+@Configuration // 스프링의 설정 클래스임을 나타내는 어노테이션
+@EnableWebSecurity // Spring Security 설정을 활성화하는 어노테이션
+@RequiredArgsConstructor // final 필드에 대한 생성자를 자동으로 생성하는 롬복 어노테이션
+public class SecurityConfig {
+
+ private final UserRepository userRepository;
+ private final CorsConfig corsConfig;
+ private final JwtProperties jwtProperties;
+ private final RedisRefreshTokenService redisRefreshTokenService;
+ private final OAuth2UserService oAuth2UserService;
+
+
+
+ //AuthenticationManager 빈을 생성하는 메소드
+ //스프링 시큐리티의 인증을 담당하는 매니저를 설정
+ @Bean
+ public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
+ return authenticationConfiguration.getAuthenticationManager();
+ }
+
+ //비밀번호 암호화를 위한 인코더를 빈으로 등록
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+ //스프링 시큐리티의 필터 체인을 구성하는 메소드
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http,
+ OAuth2SuccessHandler oAuth2SuccessHandler,
+ OAuth2FailureHandler oAuth2FailureHandler,
+ AuthenticationManager authenticationManager) throws Exception {
+ http
+ .addFilter(corsConfig.corsFilter())
+ // JWT 인증 필터 추가
+ .addFilter(new JwtAuthenticationFilter(authenticationManager, jwtProperties, redisRefreshTokenService))
+
+ // JWT 인가 필터 추가
+ .addFilter(new JwtAuthorizationFilter(authenticationManager, userRepository, jwtProperties))
+
+ // google OAuth 로그인
+ .oauth2Login(oauth2 -> oauth2.userInfoEndpoint(
+ userInfo -> userInfo.userService(oAuth2UserService))
+ .successHandler(oAuth2SuccessHandler)
+ .failureHandler(oAuth2FailureHandler)
+ )
+
+ // CSRF 보호 비활성화 (JWT 사용으로 불필요) 왜지?
+ // JWT를 사용하는 REST API에서는 CSRF 공격 방지가 불필요
+ // 토큰 기반 인증이 CSRF 공격을 방지할 수 있기 때문
+ .csrf(AbstractHttpConfigurer::disable)
+
+ // 세션 설정 (JWT는 세션을 사용하지 않음)
+ // JWT는 상태를 저장하지 않는(stateless) 방식이므로 세션이 불필요
+ // 서버의 확장성과 성능 향상을 위해 세션을 사용하지 않음
+ .sessionManagement(session -> session
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+
+ // 폼 로그인 비활성화
+ // REST API에서는 폼 로그인 방식을 사용하지 않음
+ // JWT 토큰 기반의 인증을 사용하므로 불필요
+ .formLogin(AbstractHttpConfigurer::disable)
+
+ // HTTP Basic 인증 비활성화
+ // 기본 인증은 보안에 취약하고 JWT를 사용하므로 불필요
+ // 매 요청마다 인증 정보를 보내는 방식이라 보안에 취약
+ .httpBasic(AbstractHttpConfigurer::disable)
+
+
+ .authorizeHttpRequests(authorize -> authorize
+ .requestMatchers("/api/**").authenticated() //모든 API 호출 유저 인증 필요.
+ .anyRequest().permitAll()); // 이 외 요청은 권한 필요 X
+
+
+ return http.build();
+ }
+
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/security/jwt/JwtAuthenticationFilter.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/jwt/JwtAuthenticationFilter.java
new file mode 100644
index 0000000..92aed4b
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/jwt/JwtAuthenticationFilter.java
@@ -0,0 +1,129 @@
+package likelion.team1.mindscape.security.jwt;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import likelion.team1.mindscape.dto.LoginRequestDTO;
+import likelion.team1.mindscape.entity.User;
+import likelion.team1.mindscape.repository.UserRepository;
+import likelion.team1.mindscape.service.PrincipalDetails;
+import likelion.team1.mindscape.service.RedisRefreshTokenService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+import java.io.IOException;
+import java.util.Date;
+
+@RequiredArgsConstructor
+public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
+
+ //인증(Authentication)을 처리하는 핵심 인터페이스
+ private final AuthenticationManager authenticationManager;
+ private final JwtProperties jwtProperties;
+ private final RedisRefreshTokenService redisRefreshTokenService;
+
+
+// // 생성자에서 로그인 URL 설정
+// public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
+// super(authenticationManager);
+// // 로그인 URL 설정 - 기본값은 /login
+// setFilterProcessesUrl("/login"); // 여기서 URL 변경 가능
+// }
+
+
+ // 인증 시도 메소드
+ // 클라이언트로부터 받은 인증 정보로 로그인을 시도
+ @Override
+ public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
+
+ deleteExistingTokenCookies(response);
+
+ // 1. HTTP 요청 본문을 DTO로 변환
+ ObjectMapper mapper = new ObjectMapper();
+ LoginRequestDTO loginRequestDTO = null;
+
+ try {
+ loginRequestDTO = mapper.readValue(request.getInputStream(), LoginRequestDTO.class);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ // 2. 인증 토큰 생성 (아직 인증된거 아님)
+ UsernamePasswordAuthenticationToken authToken =
+ new UsernamePasswordAuthenticationToken(loginRequestDTO.getAccountId(), loginRequestDTO.getPassword());
+
+ // 3. 인증 검토 -> 여기서 인증 처리
+ Authentication authentication = authenticationManager.authenticate(authToken);
+
+
+ return authentication;
+ }
+
+ // JWT 토큰 발급
+ // 인증 성공 시, 해당 메소드 호출
+ @Override
+ protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
+
+ PrincipalDetails principalDetails = (PrincipalDetails) authResult.getPrincipal();
+
+ deleteExistingTokenCookies(response);
+
+ // accessToken 생성
+ String accessToken = JWT.create()
+ .withSubject(principalDetails.getAccountId())
+ .withClaim("uid", principalDetails.getUser().getId())
+ .withClaim("uname", principalDetails.getUser().getUsername())
+ .withExpiresAt(new Date(System.currentTimeMillis() + jwtProperties.getACCESS_TOKEN_EXPIRATION()))
+ .sign(Algorithm.HMAC512(jwtProperties.getSECRET()));
+
+
+ String refreshToken = JWT.create()
+ .withSubject(principalDetails.getAccountId())
+ .withExpiresAt(new Date(System.currentTimeMillis() + jwtProperties.getREFRESH_TOKEN_EXPIRATION()))
+ .sign(Algorithm.HMAC512(jwtProperties.getSECRET()));
+
+
+
+ redisRefreshTokenService.saveRefreshToken(principalDetails.getAccountId(), refreshToken,
+ jwtProperties.getREFRESH_TOKEN_EXPIRATION());
+
+
+
+ String accessCookie = String.format(
+ "AccessToken=%s; Path=/; Max-Age=%d; HttpOnly; Secure; SameSite=None",
+ accessToken,jwtProperties.getACCESS_TOKEN_EXPIRATION());
+
+
+ String refreshCookie = String.format(
+ "RefreshToken=%s; Path=/; Max-Age=%d; HttpOnly; Secure; SameSite=None",
+ refreshToken, jwtProperties.getREFRESH_TOKEN_EXPIRATION());
+
+
+
+ response.addHeader("Set-Cookie", accessCookie);
+ response.addHeader("Set-Cookie", refreshCookie);
+ }
+
+ // 기존 토큰 쿠키 삭제 메서드
+ private void deleteExistingTokenCookies(HttpServletResponse response) {
+ // AccessToken 만료 처리
+ String expiredAccessToken = "AccessToken=; Path=/; Max-Age=0; HttpOnly; Secure; SameSite=None";
+
+ // RefreshToken 만료 처리
+ String expiredRefreshToken = "RefreshToken=; Path=/; Max-Age=0; HttpOnly; Secure; SameSite=None";
+
+ // 응답 헤더로 만료 쿠키 추가
+ response.addHeader("Set-Cookie", expiredAccessToken);
+ response.addHeader("Set-Cookie", expiredRefreshToken);
+ }
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/security/jwt/JwtAuthorizationFilter.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/jwt/JwtAuthorizationFilter.java
new file mode 100644
index 0000000..4d2bbae
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/jwt/JwtAuthorizationFilter.java
@@ -0,0 +1,131 @@
+package likelion.team1.mindscape.security.jwt;
+
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.TokenExpiredException;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import likelion.team1.mindscape.entity.User;
+import likelion.team1.mindscape.repository.UserRepository;
+import likelion.team1.mindscape.service.PrincipalDetails;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
+
+import java.io.IOException;
+import java.util.Date;
+
+
+public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
+
+ private final UserRepository userRepository;
+ private final JwtProperties jwtProperties;
+
+ public JwtAuthorizationFilter(AuthenticationManager authenticationManager, UserRepository userRepository, JwtProperties jwtProperties) {
+ super(authenticationManager);
+ this.userRepository = userRepository;
+ this.jwtProperties = jwtProperties;
+ }
+ // 실제 필터링 로직이 수행되는 메소드
+ // JWT 토큰을 검증하고 인증 정보를 설정
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
+
+ //jwt 토큰 가져오기
+ String accessToken = null;
+ String refreshToken = null;
+
+ Cookie[] cookies = request.getCookies();
+ if (cookies != null) {
+ for (Cookie cookie : cookies) {
+ if (cookie.getName().equals(JwtProperties.ACCESS_TOKEN_STRING)) {
+ accessToken = cookie.getValue();
+ }
+ if (cookie.getName().equals(JwtProperties.REFRESH_TOKEN_STRING)) {
+ refreshToken = cookie.getValue();
+ }
+ }
+ } else {
+ chain.doFilter(request, response);
+ return;
+ }
+
+ try {
+ // JWT 검증 및 사용자 정보 추출
+ String accountId = JWT.require(Algorithm.HMAC512(jwtProperties.getSECRET()))
+ .build()
+ .verify(accessToken)
+ .getSubject();
+
+ if(accountId != null) {
+ // 추출된 사용자가 현재 DB에 등록된 사용자인지 확인
+ User user = userRepository.findByAccountId(accountId);
+
+ // 사용자 정보
+ PrincipalDetails principalDetails = new PrincipalDetails(user);
+
+ Authentication authentication =
+ new UsernamePasswordAuthenticationToken(
+ principalDetails, // 사용자 정보
+ null, // 인증 완료로 비밀번호 불필요
+ principalDetails.getAuthorities()); // 권한 정보
+
+ //SecurityContext에 인증 정보 저장
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+ } catch (TokenExpiredException e){ // 토큰이 만료된 경우
+
+ if(refreshToken != null) {
+
+ String accountId = JWT.require(Algorithm.HMAC512(jwtProperties.getSECRET()))
+ .build()
+ .verify(refreshToken)
+ .getSubject();
+
+ User user = userRepository.findByAccountId(accountId);
+
+ if(user.vailateRefreshToken(refreshToken)) {
+
+ String newAccessToken = JWT.create()
+ .withSubject(user.getAccountId())
+ .withClaim("uid", user.getId())
+ .withClaim("uname", user.getUsername())
+ .withExpiresAt(new Date(System.currentTimeMillis() + jwtProperties.getACCESS_TOKEN_EXPIRATION()))
+ .sign(Algorithm.HMAC512(jwtProperties.getSECRET()));
+
+ String accessCookie = String.format(
+ "AccessToken=%s; Path=/; Max-Age=%d; HttpOnly; Secure; SameSite=None",
+ newAccessToken,jwtProperties.getACCESS_TOKEN_EXPIRATION());
+ response.addHeader("Set-Cookie", accessCookie);
+
+ // 인증 정보 설정
+ PrincipalDetails principalDetails = new PrincipalDetails(user);
+
+ Authentication authentication = new UsernamePasswordAuthenticationToken(
+ principalDetails,
+ null,
+ principalDetails.getAuthorities());
+
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+
+ }
+
+ } else {
+ chain.doFilter(request, response);
+ return;
+ }
+
+ } catch (Exception ex) {
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+// throw new RuntimeException(ex);
+ }
+
+ chain.doFilter(request, response);
+ }
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/security/jwt/JwtProperties.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/jwt/JwtProperties.java
new file mode 100644
index 0000000..532bb52
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/jwt/JwtProperties.java
@@ -0,0 +1,23 @@
+package likelion.team1.mindscape.security.jwt;
+
+import lombok.Getter;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Getter
+@Component
+public class JwtProperties {
+
+ @Value("${jwt.secret}")
+ private String SECRET;
+
+ @Value("${jwt.accessToken.ExpirationTime}")
+ private int ACCESS_TOKEN_EXPIRATION;
+
+ @Value("${jwt.refreshToken.ExpirationTime}")
+ private int REFRESH_TOKEN_EXPIRATION;
+
+ public static final String ACCESS_TOKEN_STRING = "AccessToken";
+ public static final String REFRESH_TOKEN_STRING = "RefreshToken";
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/security/oauth/OAuth2FailureHandler.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/oauth/OAuth2FailureHandler.java
new file mode 100644
index 0000000..0b73245
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/oauth/OAuth2FailureHandler.java
@@ -0,0 +1,23 @@
+package likelion.team1.mindscape.security.oauth;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+@Component
+public class OAuth2FailureHandler implements AuthenticationFailureHandler {
+
+ @Value("${server.frontend.pageUrl}")
+ private String redirectPageUrl;
+
+ @Override
+ public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
+ response.sendRedirect(redirectPageUrl);
+ }
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/security/oauth/OAuth2SuccessHandler.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/oauth/OAuth2SuccessHandler.java
new file mode 100644
index 0000000..0acd32a
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/security/oauth/OAuth2SuccessHandler.java
@@ -0,0 +1,85 @@
+package likelion.team1.mindscape.security.oauth;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.algorithms.Algorithm;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import likelion.team1.mindscape.entity.User;
+import likelion.team1.mindscape.repository.UserRepository;
+import likelion.team1.mindscape.security.jwt.JwtProperties;
+import likelion.team1.mindscape.service.PrincipalDetails;
+import likelion.team1.mindscape.service.RedisRefreshTokenService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.Date;
+
+@Component
+@RequiredArgsConstructor
+public class OAuth2SuccessHandler implements AuthenticationSuccessHandler {
+
+ @Value("${server.frontend.pageUrl}")
+ private String redirectPageUrl;
+
+ private final UserRepository userRepository;
+ private final JwtProperties jwtProperties;
+ private final RedisRefreshTokenService redisRefreshTokenService;
+
+ @Override
+ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
+
+ PrincipalDetails principalDetails = (PrincipalDetails) authentication.getPrincipal();
+ User requestUser = principalDetails.getUser();
+
+ String accountid = requestUser.getAccountId();
+
+ User user = userRepository.findByAccountId(accountid);
+ if (user == null) {
+ throw new ServletException("User not found");
+ }
+
+ String accessToken = JWT.create()
+ .withSubject(user.getAccountId())
+ .withClaim("uid", user.getId())
+ .withClaim("uname", user.getUsername())
+ .withExpiresAt(new Date(System.currentTimeMillis() + jwtProperties.getACCESS_TOKEN_EXPIRATION()))
+ .sign(Algorithm.HMAC512(jwtProperties.getSECRET()));
+
+ String refreshToken = JWT.create()
+ .withSubject(user.getAccountId())
+ .withExpiresAt(new Date(System.currentTimeMillis() + jwtProperties.getREFRESH_TOKEN_EXPIRATION()))
+ .sign(Algorithm.HMAC512(jwtProperties.getSECRET()));
+
+ redisRefreshTokenService.saveRefreshToken(user.getAccountId(), refreshToken,
+ jwtProperties.getREFRESH_TOKEN_EXPIRATION());
+
+
+ String accessCookie = String.format(
+ "AccessToken=%s; Path=/; Max-Age=%d; HttpOnly; Secure; SameSite=None",
+ accessToken,jwtProperties.getACCESS_TOKEN_EXPIRATION());
+
+
+
+ String refreshCookie = String.format(
+ "RefreshToken=%s; Path=/; Max-Age=%d; HttpOnly; Secure; SameSite=None",
+ refreshToken, jwtProperties.getREFRESH_TOKEN_EXPIRATION());
+
+
+ Cookie jsessionidCookie = new Cookie("JSESSIONID", null);
+ jsessionidCookie.setMaxAge(0);
+ jsessionidCookie.setPath("/");
+
+
+ response.addHeader("Set-Cookie", accessCookie);
+ response.addHeader("Set-Cookie", refreshCookie);
+ response.addCookie(jsessionidCookie);
+
+ response.sendRedirect(redirectPageUrl);
+ }
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/service/OAuth2UserService.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/service/OAuth2UserService.java
new file mode 100644
index 0000000..abeb2bf
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/service/OAuth2UserService.java
@@ -0,0 +1,44 @@
+package likelion.team1.mindscape.service;
+
+import likelion.team1.mindscape.entity.User;
+import likelion.team1.mindscape.repository.UserRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
+import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
+import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
+import org.springframework.security.oauth2.core.user.OAuth2User;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class OAuth2UserService extends DefaultOAuth2UserService {
+
+ private final UserRepository userRepository;
+
+ @Override
+ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
+ OAuth2User oAuth2User = super.loadUser(userRequest);
+
+ String provider = userRequest.getClientRegistration().getRegistrationId();
+ String providerId = oAuth2User.getAttribute("sub");
+ String email = oAuth2User.getAttribute("email");
+ String name = oAuth2User.getAttribute("name");
+
+ // 구글 로그인에서는 accountId = email로 취급.
+ User user = userRepository.findByAccountId(email);
+
+ if(user == null){
+ user = User.builder()
+ .accountId(email)
+ .username(name)
+ .provider(provider)
+ .providerId(providerId)
+ .build();
+
+ userRepository.save(user);
+ }
+
+
+ return new PrincipalDetails(user, oAuth2User.getAttributes());
+ }
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/service/PrincipalDetails.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/service/PrincipalDetails.java
new file mode 100644
index 0000000..b596a4b
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/service/PrincipalDetails.java
@@ -0,0 +1,59 @@
+package likelion.team1.mindscape.service;
+
+import likelion.team1.mindscape.entity.User;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.oauth2.core.user.OAuth2User;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+@Getter
+public class PrincipalDetails implements UserDetails, OAuth2User {
+
+ private final User user;
+ private Map attributes;
+
+ public PrincipalDetails(User user) {
+ this.user = user;
+ }
+
+ public PrincipalDetails(User user, Map attributes) {
+ this.user = user;
+ this.attributes = attributes;
+ }
+
+ @Override
+ public Map getAttributes() {
+ return attributes;
+ }
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ return List.of();
+ }
+
+ @Override
+ public String getPassword() {
+ return user.getPassword();
+ }
+
+ @Override
+ public String getUsername() {
+ return user.getUsername();
+ }
+
+ public String getAccountId() {
+ return user.getAccountId();
+ }
+
+ @Override
+ public String getName() {
+ return user.getUsername();
+ }
+
+
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/service/PrincipalDetailsService.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/service/PrincipalDetailsService.java
new file mode 100644
index 0000000..776037f
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/service/PrincipalDetailsService.java
@@ -0,0 +1,25 @@
+package likelion.team1.mindscape.service;
+
+import likelion.team1.mindscape.entity.User;
+import likelion.team1.mindscape.repository.UserRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class PrincipalDetailsService implements UserDetailsService {
+
+ private final UserRepository userRepository;
+
+ // 로그인 시도할 때 스프링 시큐리티가 자동으로 호출하는 메소드
+ // acoountid로 DB에서 사용자를 조회하여 PrincipalDetails 객체로 변환
+ @Override
+ public UserDetails loadUserByUsername(String accountid) throws UsernameNotFoundException {
+ User user = userRepository.findByAccountId(accountid);
+ return new PrincipalDetails(user);
+ }
+
+}
diff --git a/mindscape-auth/src/main/java/likelion/team1/mindscape/service/RedisRefreshTokenService.java b/mindscape-auth/src/main/java/likelion/team1/mindscape/service/RedisRefreshTokenService.java
new file mode 100644
index 0000000..cccc121
--- /dev/null
+++ b/mindscape-auth/src/main/java/likelion/team1/mindscape/service/RedisRefreshTokenService.java
@@ -0,0 +1,35 @@
+package likelion.team1.mindscape.service;
+
+import likelion.team1.mindscape.security.jwt.JwtProperties;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import java.util.concurrent.TimeUnit;
+
+@Service
+@RequiredArgsConstructor
+public class RedisRefreshTokenService {
+ private final RedisTemplate redisTemplate;
+ private static final String REFRESH_TOKEN_PREFIX = JwtProperties.REFRESH_TOKEN_STRING + ":";
+
+ public void saveRefreshToken(String accountId, String refreshToken, long expirationTime) {
+ String key = REFRESH_TOKEN_PREFIX + accountId;
+ redisTemplate.opsForValue().set(key, refreshToken, expirationTime, TimeUnit.MILLISECONDS);
+ }
+
+ public String getRefreshToken(String accountId) {
+ String key = REFRESH_TOKEN_PREFIX + accountId;
+ return redisTemplate.opsForValue().get(key);
+ }
+
+ public void deleteRefreshToken(String accountId) {
+ String key = REFRESH_TOKEN_PREFIX + accountId;
+ redisTemplate.delete(key);
+ }
+
+ public boolean validateRefreshToken(String accountId, String refreshToken) {
+ String savedToken = getRefreshToken(accountId);
+ return refreshToken.equals(savedToken);
+ }
+}
diff --git a/mindscape-auth/src/main/resources/application.properties b/mindscape-auth/src/main/resources/application.properties
deleted file mode 100644
index 6618ef0..0000000
--- a/mindscape-auth/src/main/resources/application.properties
+++ /dev/null
@@ -1 +0,0 @@
-spring.application.name=mindscape
diff --git a/mindscape-info/.gitignore b/mindscape-info/.gitignore
index a43b27c..4622b4b 100644
--- a/mindscape-info/.gitignore
+++ b/mindscape-info/.gitignore
@@ -31,4 +31,10 @@ build/
### VS Code ###
.vscode/
+
+
+
+### properties ###
+
*.properties
+
diff --git a/mindscape-info/Dockerfile b/mindscape-info/Dockerfile
new file mode 100644
index 0000000..b71999c
--- /dev/null
+++ b/mindscape-info/Dockerfile
@@ -0,0 +1,4 @@
+FROM openjdk:17
+ARG JAR_FILE=target/*.jar
+COPY ${JAR_FILE} app.jar
+ENTRYPOINT ["java", "-jar", "/app.jar"]
\ No newline at end of file
diff --git a/mindscape-info/pom.xml b/mindscape-info/pom.xml
index 2b7383d..f6a9fbd 100644
--- a/mindscape-info/pom.xml
+++ b/mindscape-info/pom.xml
@@ -44,13 +44,48 @@
org.projectlombok
lombok
- true
+ 1.18.30
+ provided
org.springframework.boot
spring-boot-starter-test
test
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ com.mysql
+ mysql-connector-j
+ 8.0.33
+
+
+ com.auth0
+ java-jwt
+ 4.4.0
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.11.5
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.11.5
+ runtime
+
+
+
@@ -58,11 +93,13 @@
org.apache.maven.plugins
maven-compiler-plugin
+ 3.11.0
org.projectlombok
lombok
+ 1.18.30
@@ -71,12 +108,6 @@
org.springframework.boot
spring-boot-maven-plugin
-
-
- org.projectlombok
- lombok
-
-
diff --git a/mindscape-info/src/main/java/likelion/team1/mindscape/WebConfig.java b/mindscape-info/src/main/java/likelion/team1/mindscape/WebConfig.java
new file mode 100644
index 0000000..5d752ef
--- /dev/null
+++ b/mindscape-info/src/main/java/likelion/team1/mindscape/WebConfig.java
@@ -0,0 +1,22 @@
+package likelion.team1.mindscape;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class WebConfig implements WebMvcConfigurer {
+
+ @Value("${server.frontend.pageUrl}")
+ private String pageUrl;
+
+ @Override
+ public void addCorsMappings(CorsRegistry registry) {
+ registry.addMapping("/**") // 모든 요청 경로에 대해
+ .allowedOrigins(pageUrl) //서비스 페이지
+ .allowedMethods("*") // GET, POST, PUT 등 모두 허용
+ .allowedHeaders("*") // 모든 헤더 허용
+ .allowCredentials(true); // 인증 정보 포함 허용 (JWT 등)
+ }
+}
diff --git a/mindscape-info/src/main/java/likelion/team1/mindscape/test/controller/TestController.java b/mindscape-info/src/main/java/likelion/team1/mindscape/test/controller/TestController.java
new file mode 100644
index 0000000..79c45f7
--- /dev/null
+++ b/mindscape-info/src/main/java/likelion/team1/mindscape/test/controller/TestController.java
@@ -0,0 +1,63 @@
+package likelion.team1.mindscape.test.controller;
+
+import likelion.team1.mindscape.test.dto.TestRequestDto;
+import likelion.team1.mindscape.test.dto.TestResponseDto;
+import likelion.team1.mindscape.test.dto.TestResponseSimpleDto;
+import likelion.team1.mindscape.test.service.TestService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/test")
+@RequiredArgsConstructor
+public class TestController {
+
+ private final TestService testService;
+
+ //테스트 저장
+ @PostMapping("/save")
+ public ResponseEntity