diff --git a/smarketing-ai/app.py b/smarketing-ai/app.py index 41d772a..d3c91da 100644 --- a/smarketing-ai/app.py +++ b/smarketing-ai/app.py @@ -74,8 +74,11 @@ def create_app(): platform=data.get('platform'), images=data.get('images', []), requirement=data.get('requirement'), - toneAndManner=data.get('toneAndManner'), - emotionIntensity=data.get('emotionIntensity'), + storeName=data.get('storeName'), + storeType=data.get('storeType'), + target=data.get('target'), + #toneAndManner=data.get('toneAndManner'), + #emotionIntensity=data.get('emotionIntensity'), menuName=data.get('menuName'), eventName=data.get('eventName'), startDate=data.get('startDate'), diff --git a/smarketing-ai/deployment/Jenkinsfile b/smarketing-ai/deployment/Jenkinsfile new file mode 100644 index 0000000..a478c49 --- /dev/null +++ b/smarketing-ai/deployment/Jenkinsfile @@ -0,0 +1,157 @@ +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: '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: '/run/podman', memory: false), + 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.namespace}" + + echo "Registry: ${props.registry}" + echo "Image Org: ${props.image_org}" + echo "Team ID: ${props.teamid}" + } + + 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 rg-digitalgarage-02 --name aks-digitalgarage-02 --overwrite-existing + kubectl create namespace ${namespace} --dry-run=client -o yaml | kubectl apply -f - + """ + } + } + } + + stage('Build & Push Docker Image') { + container('podman') { + sh 'podman system service -t 0 unix:///run/podman/podman.sock & sleep 2' + + withCredentials([usernamePassword( + credentialsId: 'acr-credentials', + usernameVariable: 'ACR_USERNAME', + passwordVariable: 'ACR_PASSWORD' + )]) { + sh """ + echo "==========================================" + echo "Building smarketing-ai Python Flask application" + echo "Image Tag: ${imageTag}" + echo "==========================================" + + # ACR 로그인 + echo \$ACR_PASSWORD | podman login ${props.registry} --username \$ACR_USERNAME --password-stdin + + # Docker 이미지 빌드 + podman build \ + -f deployment/container/Dockerfile \ + -t ${props.registry}/${props.image_org}/smarketing-ai:${imageTag} . + + # 이미지 푸시 + podman push ${props.registry}/${props.image_org}/smarketing-ai:${imageTag} + + echo "Successfully built and pushed: ${props.registry}/${props.image_org}/smarketing-ai:${imageTag}" + """ + } + } + } + + stage('Generate & Apply Manifest') { + container('envsubst') { + withCredentials([ + string(credentialsId: 'secret-key', variable: 'SECRET_KEY'), + string(credentialsId: 'claude-api-key', variable: 'CLAUDE_API_KEY'), + string(credentialsId: 'openai-api-key', variable: 'OPENAI_API_KEY'), + string(credentialsId: 'azure-storage-account-name', variable: 'AZURE_STORAGE_ACCOUNT_NAME'), + string(credentialsId: 'azure-storage-account-key', variable: 'AZURE_STORAGE_ACCOUNT_KEY') + ]) { + sh """ + export namespace=${namespace} + export replicas=${props.replicas} + 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} + export upload_folder=${props.upload_folder} + export max_content_length=${props.max_content_length} + export allowed_extensions=${props.allowed_extensions} + export server_host=${props.server_host} + export server_port=${props.server_port} + export azure_storage_container_name=${props.azure_storage_container_name} + + # 이미지 경로 환경변수 설정 + export smarketing_image_path=${props.registry}/${props.image_org}/smarketing-ai:${imageTag} + + # Sensitive 환경변수 설정 (Jenkins Credentials에서) + export secret_key=\$SECRET_KEY + export claude_api_key=\$CLAUDE_API_KEY + export openai_api_key=\$OPENAI_API_KEY + export azure_storage_account_name=\$AZURE_STORAGE_ACCOUNT_NAME + export azure_storage_account_key=\$AZURE_STORAGE_ACCOUNT_KEY + + # manifest 생성 + envsubst < deployment/${manifest}.template > deployment/${manifest} + echo "Generated manifest file:" + cat deployment/${manifest} + """ + } + } + + container('azure-cli') { + sh """ + kubectl apply -f deployment/${manifest} + + echo "Waiting for smarketing deployment to be ready..." + kubectl -n ${namespace} wait --for=condition=available deployment/smarketing --timeout=300s + + echo "==========================================" + echo "Getting LoadBalancer External IP..." + + # External IP 확인 (최대 5분 대기) + for i in {1..30}; do + EXTERNAL_IP=\$(kubectl -n ${namespace} get service smarketing-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + if [ "\$EXTERNAL_IP" != "" ] && [ "\$EXTERNAL_IP" != "null" ]; then + echo "External IP assigned: \$EXTERNAL_IP" + break + fi + echo "Waiting for External IP... (attempt \$i/30)" + sleep 10 + done + + # 서비스 상태 확인 + kubectl -n ${namespace} get pods -l app=smarketing + kubectl -n ${namespace} get service smarketing-service + + echo "==========================================" + echo "Deployment Complete!" + echo "Service URL: http://\$EXTERNAL_IP:${props.server_port}" + echo "Health Check: http://\$EXTERNAL_IP:${props.server_port}/health" + echo "==========================================" + """ + } + } + } +} \ No newline at end of file diff --git a/smarketing-ai/deployment/Jenkinsfile_ArgoCD b/smarketing-ai/deployment/Jenkinsfile_ArgoCD new file mode 100644 index 0000000..1f86a02 --- /dev/null +++ b/smarketing-ai/deployment/Jenkinsfile_ArgoCD @@ -0,0 +1,170 @@ +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: 'podman', image: "mgoltzsche/podman", ttyEnabled: true, command: 'cat', privileged: true), + containerTemplate(name: 'git', image: 'alpine/git:latest', command: 'cat', ttyEnabled: true) + ], + volumes: [ + emptyDirVolume(mountPath: '/run/podman', memory: false) + ] +) { + node(PIPELINE_ID) { + def props + def imageTag = getImageTag() + + stage("Get Source") { + checkout scm + props = readProperties file: "deployment/deploy_env_vars" + } + + stage('Build & Push Docker Image') { + container('podman') { + sh 'podman system service -t 0 unix:///run/podman/podman.sock & sleep 2' + + withCredentials([usernamePassword( + credentialsId: 'acr-credentials', + usernameVariable: 'ACR_USERNAME', + passwordVariable: 'ACR_PASSWORD' + )]) { + sh """ + echo "==========================================" + echo "Building smarketing-ai for ArgoCD GitOps" + echo "Image Tag: ${imageTag}" + echo "==========================================" + + # ACR 로그인 + echo \$ACR_PASSWORD | podman login ${props.registry} --username \$ACR_USERNAME --password-stdin + + # Docker 이미지 빌드 + podman build \ + -f deployment/container/Dockerfile \ + -t ${props.registry}/${props.image_org}/smarketing-ai:${imageTag} . + + # 이미지 푸시 + podman push ${props.registry}/${props.image_org}/smarketing-ai:${imageTag} + + echo "Successfully built and pushed: ${props.registry}/${props.image_org}/smarketing-ai:${imageTag}" + """ + } + } + } + + stage('Update Manifest Repository') { + container('git') { + withCredentials([usernamePassword( + credentialsId: 'github-credentials-${props.teamid}', + usernameVariable: 'GIT_USERNAME', + passwordVariable: 'GIT_PASSWORD' + )]) { + sh """ + # Git 설정 + git config --global user.email "jenkins@company.com" + git config --global user.name "Jenkins CI" + + # Manifest 저장소 클론 (팀별 저장소로 수정 필요) + git clone https://\${GIT_USERNAME}:\${GIT_PASSWORD}@github.com/your-team/smarketing-ai-manifest.git + cd smarketing-ai-manifest + + echo "==========================================" + echo "Updating smarketing-ai manifest repository:" + echo "New Image: ${props.registry}/${props.image_org}/smarketing-ai:${imageTag}" + + # smarketing deployment 파일 업데이트 + if [ -f "smarketing/smarketing-deployment.yaml" ]; then + # 이미지 태그 업데이트 + sed -i "s|image: ${props.registry}/${props.image_org}/smarketing-ai:.*|image: ${props.registry}/${props.image_org}/smarketing-ai:${imageTag}|g" \ + smarketing/smarketing-deployment.yaml + + echo "Updated smarketing deployment to image tag: ${imageTag}" + cat smarketing/smarketing-deployment.yaml | grep "image:" + else + echo "Warning: smarketing-deployment.yaml not found" + echo "Creating manifest directory structure..." + + # 기본 구조 생성 + mkdir -p smarketing + + # 기본 deployment 파일 생성 + cat > smarketing/smarketing-deployment.yaml << EOF +apiVersion: apps/v1 +kind: Deployment +metadata: + name: smarketing + namespace: smarketing + labels: + app: smarketing +spec: + replicas: 1 + selector: + matchLabels: + app: smarketing + template: + metadata: + labels: + app: smarketing + spec: + imagePullSecrets: + - name: acr-secret + containers: + - name: smarketing + image: ${props.registry}/${props.image_org}/smarketing-ai:${imageTag} + imagePullPolicy: Always + ports: + - containerPort: 5001 + resources: + requests: + cpu: 256m + memory: 512Mi + limits: + cpu: 1024m + memory: 2048Mi + envFrom: + - configMapRef: + name: smarketing-config + - secretRef: + name: smarketing-secret + volumeMounts: + - name: upload-storage + mountPath: /app/uploads + - name: temp-storage + mountPath: /app/uploads/temp + volumes: + - name: upload-storage + emptyDir: {} + - name: temp-storage + emptyDir: {} +EOF + echo "Created basic smarketing-deployment.yaml" + fi + + # 변경사항 커밋 및 푸시 + git add . + git commit -m "Update smarketing-ai image tag to ${imageTag} + + Image: ${props.registry}/${props.image_org}/smarketing-ai:${imageTag} + Build: ${env.BUILD_NUMBER} + Branch: ${env.BRANCH_NAME} + Commit: ${env.GIT_COMMIT}" + + git push origin main + + echo "==========================================" + echo "ArgoCD GitOps Update Completed!" + echo "Updated Service: smarketing-ai:${imageTag}" + echo "ArgoCD will automatically detect and deploy these changes." + echo "==========================================" + """ + } + } + } + } +} \ No newline at end of file diff --git a/smarketing-ai/deployment/deploy.yaml.template b/smarketing-ai/deployment/deploy.yaml.template new file mode 100644 index 0000000..2f35b44 --- /dev/null +++ b/smarketing-ai/deployment/deploy.yaml.template @@ -0,0 +1,113 @@ +# ConfigMap +apiVersion: v1 +kind: ConfigMap +metadata: + name: smarketing-config + namespace: ${namespace} +data: + SERVER_HOST: "${server_host}" + SERVER_PORT: "${server_port}" + UPLOAD_FOLDER: "${upload_folder}" + MAX_CONTENT_LENGTH: "${max_content_length}" + ALLOWED_EXTENSIONS: "${allowed_extensions}" + AZURE_STORAGE_CONTAINER_NAME: "${azure_storage_container_name}" + +--- +# Secret +apiVersion: v1 +kind: Secret +metadata: + name: smarketing-secret + namespace: ${namespace} +type: Opaque +stringData: + SECRET_KEY: "${secret_key}" + CLAUDE_API_KEY: "${claude_api_key}" + OPENAI_API_KEY: "${openai_api_key}" + AZURE_STORAGE_ACCOUNT_NAME: "${azure_storage_account_name}" + AZURE_STORAGE_ACCOUNT_KEY: "${azure_storage_account_key}" + +--- +# Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: smarketing + namespace: ${namespace} + labels: + app: smarketing +spec: + replicas: ${replicas} + selector: + matchLabels: + app: smarketing + template: + metadata: + labels: + app: smarketing + spec: + imagePullSecrets: + - name: acr-secret + containers: + - name: smarketing + image: ${smarketing_image_path} + imagePullPolicy: Always + ports: + - containerPort: 5001 + resources: + requests: + cpu: ${resources_requests_cpu} + memory: ${resources_requests_memory} + limits: + cpu: ${resources_limits_cpu} + memory: ${resources_limits_memory} + envFrom: + - configMapRef: + name: smarketing-config + - secretRef: + name: smarketing-secret + volumeMounts: + - name: upload-storage + mountPath: /app/uploads + - name: temp-storage + mountPath: /app/uploads/temp + livenessProbe: + httpGet: + path: /health + port: 5001 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /health + port: 5001 + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + volumes: + - name: upload-storage + emptyDir: {} + - name: temp-storage + emptyDir: {} + +--- +# Service (LoadBalancer type for External IP) +apiVersion: v1 +kind: Service +metadata: + name: smarketing-service + namespace: ${namespace} + labels: + app: smarketing +spec: + type: LoadBalancer + ports: + - port: 5001 + targetPort: 5001 + protocol: TCP + name: http + selector: + app: smarketing \ No newline at end of file diff --git a/smarketing-ai/deployment/deploy_env_vars b/smarketing-ai/deployment/deploy_env_vars new file mode 100644 index 0000000..6f33b33 --- /dev/null +++ b/smarketing-ai/deployment/deploy_env_vars @@ -0,0 +1,27 @@ +# Team Settings +teamid=won +root_project=smarketing-ai +namespace=smarketing + +# Container Registry Settings +registry=acrdigitalgarage02.azurecr.io +image_org=won + +# Application Settings +replicas=1 + +# Resource Settings +resources_requests_cpu=256m +resources_requests_memory=512Mi +resources_limits_cpu=1024m +resources_limits_memory=2048Mi + +# Flask App Settings (non-sensitive) +upload_folder=/app/uploads +max_content_length=16777216 +allowed_extensions=png,jpg,jpeg,gif,webp +server_host=0.0.0.0 +server_port=5001 + +# Azure Storage Settings (non-sensitive) +azure_storage_container_name=ai-content \ No newline at end of file diff --git a/smarketing-ai/deployment/manifest/deployment.yaml b/smarketing-ai/deployment/manifest/deployment.yaml index 1a5df1d..cc53cb5 100644 --- a/smarketing-ai/deployment/manifest/deployment.yaml +++ b/smarketing-ai/deployment/manifest/deployment.yaml @@ -19,7 +19,7 @@ spec: - name: acr-secret containers: - name: smarketing - image: dg0408cr.azurecr.io/smarketing-ai:latest + image: acrdigitalgarage02.azurecr.io/smarketing-ai:latest imagePullPolicy: Always ports: - containerPort: 5001 diff --git a/smarketing-ai/deployment/manifest/secret.yaml b/smarketing-ai/deployment/manifest/secret.yaml index d013ead..e489d01 100644 --- a/smarketing-ai/deployment/manifest/secret.yaml +++ b/smarketing-ai/deployment/manifest/secret.yaml @@ -5,6 +5,10 @@ metadata: namespace: smarketing type: Opaque stringData: - SECRET_KEY: "your-secret-key-change-in-production" - CLAUDE_API_KEY: "your-claude-api-key" - OPENAI_API_KEY: "your-openai-api-key" \ No newline at end of file + SECRET_KEY: + CLAUDE_API_KEY: + OPENAI_API_KEY: + AZURE_STORAGE_ACCOUNT_NAME: "stdigitalgarage02" + AZURE_STORAGE_ACCOUNT_KEY: + AZURE_STORAGE_CONTAINER_NAME: "ai-content" + diff --git a/smarketing-ai/deployment/manifest/service.yaml b/smarketing-ai/deployment/manifest/service.yaml index 87ba6f0..08dc1e8 100644 --- a/smarketing-ai/deployment/manifest/service.yaml +++ b/smarketing-ai/deployment/manifest/service.yaml @@ -6,7 +6,7 @@ metadata: labels: app: smarketing spec: - type: ClusterIP + type: LoadBalancer ports: - port: 5001 targetPort: 5001 diff --git a/smarketing-ai/models/request_models.py b/smarketing-ai/models/request_models.py index 41b36d2..3f6952d 100644 --- a/smarketing-ai/models/request_models.py +++ b/smarketing-ai/models/request_models.py @@ -16,9 +16,12 @@ class SnsContentGetRequest: contentType: str platform: str images: List[str] # 이미지 URL 리스트 + target : Optional[str] = None # 타켓 requirement: Optional[str] = None - toneAndManner: Optional[str] = None - emotionIntensity: Optional[str] = None + storeName: Optional[str] = None + storeType: Optional[str] = None + #toneAndManner: Optional[str] = None + #emotionIntensity: Optional[str] = None menuName: Optional[str] = None eventName: Optional[str] = None startDate: Optional[date] = None # LocalDate -> date diff --git a/smarketing-ai/services/sns_content_service.py b/smarketing-ai/services/sns_content_service.py index 24fa7a5..680a1e7 100644 --- a/smarketing-ai/services/sns_content_service.py +++ b/smarketing-ai/services/sns_content_service.py @@ -16,6 +16,1325 @@ class SnsContentService: self.ai_client = AIClient() self.image_processor = ImageProcessor() + # 블로그 글 예시 + self.blog_example = [ + { + "raw_html": """
팔공
+
+
+ +++중화요리 팔공
위치안내: 서울 관악구 남부순환로 1680
영업시간: 11시 20분 ~ 21시 30분( 15시 ~ 17시 브레이크타임, 일요일 휴무)
메뉴: 짜장면, 해물짬뽕, 고기짬뽕, 볶음밥, 탕수육등
+++3명이 주문한 메뉴는 짜장면, 옛날볶음밥, 팔공해물짬뽕 2개 총 4가지 주문을 합니다.
+
오랜만에 오셨네요 하셔서 " 이젠 와인 못 마시겠네요 "했더니 웃으시더군요 ㅎ
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+++최종평가: 올해 먹은 짬뽕 중 최고라고 감히 말을 할 수 있을 거 같습니다. 예전보다 더 맛있어 졌으니 사람이 더 많아졌겠죠. 참고로 옛날고기짬뽕은 1시30분전에 솔드아웃된다고 합니다.
+
​
​
​
​
​
[남천동 맛집] 안목 - 훌륭한 돼지국밥 한 그릇
​
​
​
​
​
미쉐린에 선택한 식당에 특별히 호감이 가는 것은 아니다.
하지만 궁금하기는 하다.
어떤 점에서 좋게 보고 선정을 한 것인지 궁금했다.
내가 가본 식당이라면 판단하면 되겠지만 가보지 않은 식당이라면 그 궁금증은 더 크다.
특히 가장 대중적인 음식이라면 더 클 것이다.
​
부산의 미쉐린 빕구르망에 2년 연속 선정한 돼지국밥집이 있다.
오가며 보기는 했지만 아직 가보진 못했다.
일부러 찾아가 보았다.
​
남천동의 "안목"이다.
​
​
+
+
+ 정문 사진을 찍지 못해서 구글에서 하나 가져왔다. 밖에서 봐도 돼지국밥집 같아 보이지 않는다. 깔끔하고 모던하다.
남천동 등기소 바로 옆 건물이다. 주차장은 별도로 없으니 뒷골목의 주차장을 이용하여야 한다.
그런데 상호의 느낌은 일본풍같이 느껴진다. 혹시 그 뜻을 아시는 분들은 좀 알려주시면 고맙겠다.
​
​
​
+
+
+ 좌석은 테이블은 없고 카운터석으로만 되어 있다. 최근 이름난 돼지국밥집들은 다 이런 식으로 만드는 것 같다.
전에 지나다 줄을 서는 것을 보았는데 이날 비가 와서 그랬는지 한가하다.
​
​
​
+
+
+ 메뉴가 심플하다. 그냥 돼지국밥에 머릿고기 국밥 정도이다. 수육과 냉제육이 있는데 다음에 가게 되면 먹어보고 싶다.
가격은 비싸지 않은 것은 아닌데 더 비싸지 않아서 다행스럽다.
​
​
​
+
+
+ 첨가할 수 있는 여러 가지
​
​
​
+이런 것들이 있는데 마늘만 넣어 먹었다.
​
​
​
+
+
+ 내가 주문한 머릿고기 국밥이다. 1인분씩 담겨 나온다.
​
​
​
+머리 위의 선반에 쟁반이 올려져 있으면 그것을 내가 받아서 먹어야 한다. 반찬은 특별한 것은 없는데 이날 풋고추가 맛있었다.
​
​
​
+
+
+ 굉장히 뽀얀 국물의 국밥이다. 머릿고기가 올려져 있다.
​
​
​
+
+
+ 이것은 아내가 먹은 그냥 돼지국밥이다. 고기만 다른 국밥이다.
​
​
​
+
+
+ 국밥에는 간이 되어 있어서 더 넣지 않아도 충분히 먹을 수 있었다. 그러니 다진 양념이나 새우젓은 맛을 보고 첨가하시길....
​
​
​
+
+
+ 일본 라멘에 넣는 마늘을 짜서 넣는다. 하나 정도면 충분하겠다.
​
​
​
+
+
+ 맛있게 잘 먹었다.
​
​
​
맛있다. 쵸 근래 너무 저가의 돼지국밥만 먹고 다녀서인지 안목의 국밥은 맛있었다.
국물이 너무 무겁지도 않으면서도 진득했다.
완성도가 높다. 국물은 손가락에 꼽을 정도로 괜찮았다.
​
고기의 품질도 좋았고 손질도 잘했다. 부드럽고 또 비계 부분은 쫄깃했다.
다만 고기가 많아 보이지만 한 점 한 점이 굉장히 얇아서 무게로 치면 그렇게 많은 양은 아닐 것이다.
그리고 국밥 전체적으로 양은 그다지 많은 편은 아니다.
​
이 정도의 맛이면 미쉐린 빕구르망에 선정되는 것인지는 모르겠지만 나로서는 충분하다고 느껴진다.
내가 추구하는 수더분하고 푸짐한 국밥하고는 반대편에 있는 국밥이지만 완성도가 높으니 다 괜찮아 보인다.
​
좀 편하게 갈 수 있다면 가끔 가고 싶다.
서면과 부산역에 분점이 있다고 하니 그곳이 좀 편하겠다.
​
​
​
​
+​
​
​
​
+
+
+ +++서울 미쉐린맛집 한식전문 목멱산방
-투쁠한우 육회비빔밥
-검은깨두부 보쌈
+++서울 중구 퇴계로20길 71
​
영업시간
매일
11:00 - 20:00
라스트오더
19:20
​
전화
02-318-4790
​
+서울 남산은 참 묘한 매력이 있는 곳 같아요!
도시 속인데도 한 발짝만 올라오면
바람도 다르고, 공기도 다르고,
마음까지 탁 트이는 그런 느낌!
​
​
+그런 남산 한켠에 있는
서울 미쉐린 맛집
목멱산방 본점에서
특별한 한 끼를 즐기고 왔어요!
​
​
+식사 중간중간 보니 외국인 관광객도 많았고
데이트나 가족 외식으로 많이들 오더라고요~
실내는 군더더기 없이 깔끔하고
모던한 느낌이라
전통 한식을
더 세련되게 느낄 수 있어요.
​
​
+주문은 셀프 방식으로
키오스크로 하면돼요~
방송에도 여러번 나오고
미쉐린 맛집답게
주말에는 사람이 많아요!
​
​
+
+
+ 이날 저희가 선택한 메뉴는
검은깨두부와 보쌈,
그리고
시그니처 메뉴인
투뿔한우 육회비빔밥을
주문했는데
기대 이상이었어요!
​
​
++++검은깨두부&보쌈
먼저 검은깨두부와 보쌈!!
검은깨 두부는
보기만 해도
고소한 향이 물씬 풍기는것같고
입에 넣자마자 사르르 녹아요!!
​
+정말 진한 고소함이 입안에 퍼지는데,
이게 그냥 두부가 아니라는 걸
한입만 먹어도 느낄 수 있어요.
​
+그 두부와 함께 나오는 보쌈은
지방과 살코기 비율이 완벽해서
쫀득하면서도 부드러워요.
거기에 곁들여지는
볶음김치와 특제 야채무침이
보쌈 맛을 확 살려줘서,
딱 한식의 진수라는 말이
떠오르더라고요!
​
​
++++투쁠한우 육회비빔밥
대망의 투쁠한우 육회비빔밥!
비주얼도 예쁘고
정말 먹음직 스러웠어요!
​
​
+이건 먼저 육회만 따로 맛봤는데,
신선한 투뿔 채끝살에
유자청과 꿀로 살짝 단맛을 더한
양념이 어우러져,
하나도 느끼하지 않고 깔끔했어요.!!
​
​
+비빔밥은 나물과 함께
조심스럽게 비벼 한입 먹었을 때,
고추장을 넣지 않고도
양념된 육회와 참기름만으로
깊은 맛이 나는 게,
정말 재료 하나하나에
얼마나 정성을 들였는지
알겠더라고요.
​
​
+비빔밥 안에 들어가는 나물도
건나물, 생야채, 표고버섯,
도라지, 고사리 등
제철에 맞춰 엄선된
나물들이 들어가는데,
하나하나 다 본연의 맛이 좋았어요~
​
​
+삼광쌀로 지은 밥도 맛있더라구요~
밥 한 숟가락에
입안이 꽉 차는 느낌이 넘 좋았어요!
​
+함께 주문하고 싶은 사이드 메뉴는
바로 치즈김치전!
피자치즈와 모짜렐라가
가득 들어간 김치전인데,
겉은 바삭하고 속은 촉촉한 게
비빔밥이랑 궁합 최고예요.
​
+술 한잔 곁들이고 싶다면,
비빔밥 전용 막걸리도 있어요.
‘한 잔 막걸리’라는 이름답게
식전–식중–식후로 나눠 마시는 재미가 있어요.
과일향도 은은하고,
단맛과 신맛이 균형 잡혀 있어서
비빔밥과 찰떡이에요.
​
남산 산책하다가,
혹은 명동역 근처로
들리기 좋은 곳이랍니다^^
​
+​
+