diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..23baf58
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# 디폴트 무시된 파일
+/shelf/
+/workspace.xml
diff --git a/.idea/lifesub-web.iml b/.idea/lifesub-web.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/.idea/lifesub-web.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..f03c948
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..c65f74c
--- /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/container/Dockerfile b/deployment/Dockerfile-lifesub-web
similarity index 100%
rename from container/Dockerfile
rename to deployment/Dockerfile-lifesub-web
diff --git a/deployment/Jenkinsfile b/deployment/Jenkinsfile
new file mode 100644
index 0000000..53f7808
--- /dev/null
+++ b/deployment/Jenkinsfile
@@ -0,0 +1,101 @@
+def PIPELINE_ID = "${env.BUILD_NUMBER}"
+
+def getImageTag() {
+ def dateFormat = new java.text.SimpleDateFormat('yyyyMMddHHmmss')
+ def currentDate = new Date()
+ return dateFormat.format(currentDate)
+}
+
+podTemplate(
+ label: "${PIPELINE_ID}",
+ serviceAccount: 'jenkins',
+ containers: [
+ containerTemplate(name: 'node', image: 'node:20-slim', ttyEnabled: true, command: 'cat'),
+ containerTemplate(name: 'podman', image: "mgoltzsche/podman", ttyEnabled: true, command: 'cat', privileged: true),
+ containerTemplate(name: 'azure-cli', image: 'hiondal/azure-kubectl:latest', command: 'cat', ttyEnabled: true),
+ containerTemplate(name: 'envsubst', image: "hiondal/envsubst", command: 'sleep', args: '1h')
+ ],
+ volumes: [
+ emptyDirVolume(mountPath: '/root/.azure', memory: false)
+ ]
+) {
+ node(PIPELINE_ID) {
+ def props
+ def imageTag = getImageTag()
+ def manifest = "deploy.yaml"
+ def namespace
+
+ stage("Get Source") {
+ checkout scm
+ props = readProperties file: "deployment/deploy_env_vars"
+ namespace = "${props.teamid}-${props.root_project}-ns"
+ }
+
+ stage("Setup AKS") {
+ container('azure-cli') {
+ withCredentials([azureServicePrincipal('azure-credentials')]) {
+ sh """
+ az login --service-principal -u \$AZURE_CLIENT_ID -p \$AZURE_CLIENT_SECRET -t \$AZURE_TENANT_ID
+ az aks get-credentials --resource-group ictcoe-edu --name ${props.teamid}-aks --overwrite-existing
+ kubectl create namespace ${namespace} --dry-run=client -o yaml | kubectl apply -f -
+ """
+ }
+ }
+ }
+
+ stage('Build & Push Image') {
+ container('podman') {
+ withCredentials([usernamePassword(
+ credentialsId: 'acr-credentials',
+ usernameVariable: 'USERNAME',
+ passwordVariable: 'PASSWORD'
+ )]) {
+ def imagePath = "${props.registry}/${props.image_org}/lifesub-web:${imageTag}"
+
+ sh """
+ podman login ${props.registry} --username \$USERNAME --password \$PASSWORD
+
+ podman build \
+ --build-arg PROJECT_FOLDER="." \
+ --build-arg REACT_APP_MEMBER_URL="${props.react_app_member_url}" \
+ --build-arg REACT_APP_MYSUB_URL="${props.react_app_mysub_url}" \
+ --build-arg REACT_APP_RECOMMEND_URL="${props.react_app_recommend_url}" \
+ --build-arg BUILD_FOLDER="container" \
+ --build-arg EXPORT_PORT="${props.export_port}" \
+ -f container/Dockerfile-lifesub-web \
+ -t ${imagePath} .
+
+ podman push ${imagePath}
+ """
+ }
+ }
+ }
+
+ stage('Generate & Apply Manifest') {
+ container('envsubst') {
+ sh """
+ export namespace=${namespace}
+ export lifesub_web_image_path=${props.registry}/${props.image_org}/lifesub-web:${imageTag}
+ export replicas=${props.replicas}
+ export export_port=${props.export_port}
+ export resources_requests_cpu=${props.resources_requests_cpu}
+ export resources_requests_memory=${props.resources_requests_memory}
+ export resources_limits_cpu=${props.resources_limits_cpu}
+ export resources_limits_memory=${props.resources_limits_memory}
+
+ envsubst < deployment/${manifest}.template > deployment/${manifest}
+ cat deployment/${manifest}
+ """
+ }
+
+ container('azure-cli') {
+ sh """
+ kubectl apply -f deployment/${manifest}
+
+ echo "Waiting for deployment to be ready..."
+ kubectl -n ${namespace} wait --for=condition=available deployment/lifesub-web --timeout=300s
+ """
+ }
+ }
+ }
+}
diff --git a/deployment/deploy.yaml.template b/deployment/deploy.yaml.template
new file mode 100644
index 0000000..0ccad5c
--- /dev/null
+++ b/deployment/deploy.yaml.template
@@ -0,0 +1,44 @@
+# Frontend Deployment
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: lifesub-web
+ namespace: ${namespace}
+spec:
+ replicas: ${replicas}
+ selector:
+ matchLabels:
+ app: lifesub-web
+ template:
+ metadata:
+ labels:
+ app: lifesub-web
+ spec:
+ containers:
+ - name: lifesub-web
+ image: ${lifesub_web_image_path}
+ imagePullPolicy: Always
+ ports:
+ - containerPort: ${export_port}
+ resources:
+ requests:
+ cpu: ${resources_requests_cpu}
+ memory: ${resources_requests_memory}
+ limits:
+ cpu: ${resources_limits_cpu}
+ memory: ${resources_limits_memory}
+
+---
+# Frontend Service
+apiVersion: v1
+kind: Service
+metadata:
+ name: lifesub-web
+ namespace: ${namespace}
+spec:
+ selector:
+ app: lifesub-web
+ ports:
+ - port: 80
+ targetPort: ${export_port}
+ type: LoadBalancer
\ No newline at end of file
diff --git a/deployment/deploy_env_vars b/deployment/deploy_env_vars
new file mode 100644
index 0000000..aa6cb1d
--- /dev/null
+++ b/deployment/deploy_env_vars
@@ -0,0 +1,22 @@
+# Team Settings
+teamid=dg0200
+root_project=lifesub-web
+
+# Container Registry Settings
+registry=dg0200cr.azurecr.io
+image_org=lifesub
+
+# Application Settings
+replicas=1
+export_port=18080
+
+# Backend Service URLs
+react_app_member_url=http://20.249.185.127/member
+react_app_mysub_url=http://20.249.185.127/mysub
+react_app_recommend_url=http://20.249.185.127/recommend
+
+# Resource Settings
+resources_requests_cpu=256m
+resources_requests_memory=256Mi
+resources_limits_cpu=1024m
+resources_limits_memory=1024Mi
\ No newline at end of file
diff --git a/container/nginx.conf b/deployment/nginx.conf
similarity index 100%
rename from container/nginx.conf
rename to deployment/nginx.conf