From 4e2938bd0ebe319fb52f19a1dd20f81f4523e7cb Mon Sep 17 00:00:00 2001 From: JeekLee Date: Fri, 20 Mar 2026 22:21:46 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feat:=20=EB=A6=AC=EC=86=8C=EC=8A=A4=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EA=B5=AC=EC=A1=B0=20=ED=99=95=EC=9D=B8=20?= =?UTF-8?q?=EC=A0=84=20=EB=BC=88=EB=8C=80=20=EB=AF=B8=EB=A6=AC=20=EC=9E=A1?= =?UTF-8?q?=EC=95=84=EB=91=90=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-and-deploy.yml | 108 +++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 .github/workflows/build-and-deploy.yml diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..a263430d --- /dev/null +++ b/.github/workflows/build-and-deploy.yml @@ -0,0 +1,108 @@ +name: Release - Code Deploy with Github Actions (WIF & IAP Version) + +on: + push: + tags: + - 'v*' + +env: + RESOURCE_DIR: src/main/resources + GCR_PACKAGE_NAME: pfplay-backend-java + PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} + GCE_INSTANCE: pfplay-api + GCE_ZONE: asia-northeast3-a + +jobs: + deploy: + name: Build and dockerize & deploy + runs-on: ubuntu-latest + permissions: # WIF 사용을 위해 반드시 필요 + contents: read + packages: write + id-token: write + + defaults: + run: + working-directory: api + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set env +# run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV + run: echo "RELEASE_VERSION=v0.0.0" >> $GITHUB_ENV + + # --- [기존 리소스 파일 생성 로직 유지] --- + - name: Create directory for resources + run: mkdir -p $RESOURCE_DIR/key + + - name: Set application.yml + env: + PROPERTY_FILE: ${{ secrets.PROD_PROFILE }} + run: echo $PROPERTY_FILE | base64 --decode > $RESOURCE_DIR/application.yml + + - name: Set JWT keys + run: | + echo "${{ secrets.JWT_PRIVATE_KEY }}" | base64 --decode > $RESOURCE_DIR/key/private_key.pem + echo "${{ secrets.JWT_PUBLIC_KEY }}" | base64 --decode > $RESOURCE_DIR/key/public_key.pem + + # --- [빌드 및 도커화] --- + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: 'gradle' + + - name: Build with Gradle + run: chmod +x ./gradlew && ./gradlew build -x test + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: JeekLee + password: ${{ secrets.PACKAGE_ACCESS_TOKEN }} + + - name: Build and Push image + uses: docker/build-push-action@v5 + with: + context: . + file: ./api/Dockerfile-deploy + push: true + tags: | + ghcr.io/pfplay/${{ env.GCR_PACKAGE_NAME }}:latest + ghcr.io/pfplay/${{ env.GCR_PACKAGE_NAME }}:${{ env.RELEASE_VERSION }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # --- [GCP 인증 및 IAP 배포] --- + - id: 'auth' + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@v2' + with: + workload_identity_provider: ${{ secrets.WIF_PROVIDER }} + service_account: ${{ secrets.WIF_SERVICE_ACCOUNT }} + + - name: 'Set up Cloud SDK' + uses: 'google-github-actions/setup-gcloud@v2' + + - name: 'Deploy to VM via IAP Tunnel' + run: | + gcloud compute ssh ${{ env.GCE_INSTANCE }} \ + --zone=${{ env.GCE_ZONE }} \ + --tunnel-through-iap \ + --project=${{ env.PROJECT_ID }} \ + --command=" + echo '${{ secrets.PACKAGE_ACCESS_TOKEN }}' | sudo docker login ghcr.io -u JeekLee --password-stdin && + sudo docker pull ghcr.io/pfplay/${{ env.GCR_PACKAGE_NAME }}:latest && + sudo docker stop pfplay-api-server || true && + sudo docker rm pfplay-api-server || true && + sudo docker run -d --name pfplay-api-server \ + -p 8088:8080 \ + --restart unless-stopped \ + ghcr.io/pfplay/${{ env.GCR_PACKAGE_NAME }}:latest && + sudo docker network connect api_backend pfplay-api-server || true && + sudo docker image prune -f + " \ No newline at end of file From c60bb058652762ae2e4d0c1cddc509e3c0ce19af Mon Sep 17 00:00:00 2001 From: JeekLee Date: Sat, 21 Mar 2026 20:51:52 +0900 Subject: [PATCH 02/11] =?UTF-8?q?feat:=20=EB=85=B8=EC=B6=9C=EB=90=9C=20jwt?= =?UTF-8?q?=ED=82=A4=20=EB=B0=8F=20=ED=99=98=EA=B2=BD=20=EB=B3=80=EC=88=98?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-and-deploy.yml | 19 ++- app/.gitignore | 4 +- app/src/main/resources/application.yml | 149 --------------------- app/src/main/resources/key/private_key.pem | 28 ---- app/src/main/resources/key/public_key.pem | 9 -- 5 files changed, 11 insertions(+), 198 deletions(-) delete mode 100644 app/src/main/resources/application.yml delete mode 100644 app/src/main/resources/key/private_key.pem delete mode 100644 app/src/main/resources/key/public_key.pem diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index a263430d..081f414c 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -1,14 +1,17 @@ name: Release - Code Deploy with Github Actions (WIF & IAP Version) +#on: +# push: +# tags: +# - 'v*' on: push: - tags: - - 'v*' + branches: [ feature/code-deploy-gcpp ] env: + PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} RESOURCE_DIR: src/main/resources GCR_PACKAGE_NAME: pfplay-backend-java - PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} GCE_INSTANCE: pfplay-api GCE_ZONE: asia-northeast3-a @@ -21,17 +24,13 @@ jobs: packages: write id-token: write - defaults: - run: - working-directory: api - steps: - name: Checkout uses: actions/checkout@v4 - - name: Set env -# run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV - run: echo "RELEASE_VERSION=v0.0.0" >> $GITHUB_ENV + - name: Set release version +# run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $RELEASE_VERSION + run: echo "RELEASE_VERSION=v0.0.0" >> $RELEASE_VERSION # --- [기존 리소스 파일 생성 로직 유지] --- - name: Create directory for resources diff --git a/app/.gitignore b/app/.gitignore index 13ad77e0..953428d9 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -37,6 +37,6 @@ out/ .vscode/ /src/test/resources/key/** -api/src/main/resources/** +app/src/main/resources/** -*.env +*.env \ No newline at end of file diff --git a/app/src/main/resources/application.yml b/app/src/main/resources/application.yml deleted file mode 100644 index df231751..00000000 --- a/app/src/main/resources/application.yml +++ /dev/null @@ -1,149 +0,0 @@ -spring: - profiles: - active: local # default - group: - local: - - common - dev: - - common - prod: - - common - ---- -# 🟢 공통 설정 -spring: - config: - activate: - on-profile: common - - datasource: - url: ${DB_URL:jdbc:mysql://localhost:3306/pfplay?characterEncoding=UTF-8&useUnicode=true&serverTimezone=Asia/Seoul} - username: ${DB_USERNAME:pfplay} - password: ${DB_PASSWORD} - driver-class-name: com.mysql.cj.jdbc.Driver - - jpa: - show-sql: true - hibernate: - ddl-auto: create - properties: - hibernate: - format_sql: true - - sql: - init: - mode: always - - data: - redis: - host: localhost - port: 6379 - repositories: - enabled: false - -server: - tomcat: - relaxed-query-chars: '[,],|,{,},^,`,<,>' - error: - include-message: always - include-binding-errors: always - include-stacktrace: on_param - include-exception: false - -springdoc: - version: 'v1' - swagger-ui: - display-request-duration: true - groups-order: DESC - operationsSorter: method - disable-swagger-default-url: true - use-root-path: false - path: /spec/api - show-actuator: true - -logging: - level: - org.springframework.security: DEBUG - org.springframework.web.reactive.function.client: DEBUG - -service-api: - pytube: - uri: ${PYTUBE_URI:http://localhost:9000} - api-key: ${PYTUBE_API_KEY:pfplay_streaming} - api-secret: ${PYTUBE_API_SECRET} - -# 🔐 커스텀 앱 설정 -app: - oauth2: - providers: - google: - client-id: ${GOOGLE_CLIENT_ID} - client-secret: ${GOOGLE_CLIENT_SECRET} - redirect-uri: ${GOOGLE_REDIRECT_URI:https://localhost:3000/auth/callback/google} - authorization-uri: https://accounts.google.com/o/oauth2/v2/auth - token-uri: https://oauth2.googleapis.com/token - user-info-uri: https://www.googleapis.com/oauth2/v2/userinfo - scopes: - - email - access-type: offline # Google 전용: refresh token 받기 - prompt: consent # Google 전용: 매번 동의 화면 - - twitter: - client-id: ${TWITTER_CLIENT_ID} - client-secret: ${TWITTER_CLIENT_SECRET} - redirect-uri: ${TWITTER_REDIRECT_URI:http://localhost:3000/auth/callback/twitter} - authorization-uri: https://twitter.com/i/oauth2/authorize - token-uri: https://api.twitter.com/2/oauth2/token - user-info-uri: https://api.twitter.com/2/users/me - scopes: - - users.read - - tweet.read - - jwt: - secret: ${JWT_SECRET} - expiration-ms: 86400000 # 24시간 - refresh-expiration-ms: 604800000 # 7일 - - cookie: - access-token-name: AccessToken - refresh-token-name: RefreshToken - domain: localhost - path: / - access-token-expiry-seconds: 86400 - refresh-token-expiry-seconds: 604800 - secure: true - same-site: None - - webclient: - timeout: - connection: 5000 - read: 5000 - write: 5000 - ---- - -# 🟡 개발 환경 (추후 필요 시 확장) -spring: - config: - activate: - on-profile: dev - ---- - -# 🔴 운영 환경 -spring: - config: - activate: - on-profile: prod - -app: - jwt: - cookie: - secure: true - same-site: Strict - domain: ${PRODUCTION_DOMAIN} - -springdoc: - # 운영에서는 Swagger 비활성화 권장 - swagger-ui: - enabled: false diff --git a/app/src/main/resources/key/private_key.pem b/app/src/main/resources/key/private_key.pem deleted file mode 100644 index 0c65211e..00000000 --- a/app/src/main/resources/key/private_key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCcyqqVvy//SPG8 -O4q7HCt+6ANRqf3AVQViCXCaBEHJ0ONabNh7gnfdb2wpaREw/TIaooPyQqlHcb3f -xatevHHy2ru96R7vTvCeFEYU7/Zii+dgDLdOM1vqq4ZeiJqvPjdjQBCR+oHC21Ug -7RxzEYmua45/xRT4kbl+GWlIqnILj1aEpsUSfr/p4EecscGxlc9SdwQhT0OwKu/X -Q/4+Srh7EKVvVPuy4Sq939TtGU7s4sbMIKwuzOs1j4n6sKTss6IcxERYyZKqC5Ar -hUtVyXdLwvZhmnT+cuhIPOuknrpzNkMReC2qDxWCAZXhwv8GdUu2/0QtpCcFwW8g -7uTc0mUvAgMBAAECggEAAvFs7/WlmCmi2/vUIqdTNlpy4YAK3XwwZy2H2bmVuzeQ -D5z2bU8UIpRvrSal5iECj+xtWfP1YHwalSONOsXXxQ1lCRoMn2+rED86LN9vj7Gn -l1kgbGKGFFVsPBmyqZ0bMLeS+A0sjV9M7ZrLIL/QsUpJ46Jr6S83Ek5gzbaMAHqE -lxp9n6pWd8+U0Jhl90E2dvVc0RD2A5tRqbFZV5D22gj5WPRaEEcLdsEdXV8UrOUy -BgkjMwWd4TQjRx9tX9DlM51Sisrhx5KVaw68yvmYnPGYvckfnr3EVpvt/J7wFrnr -wKw7/j19FhOBnpIcaORCT3ko88aMBn2+wePDujcUdQKBgQDbP2GAm1oqv9Rt0r5O -sXUA4mL99VQafsZq/0WGcb1bxKBtVajOIxrY15bXsuHH1bMUD6j9cqtUyqhmLaHV -CjqLSKorjPAsww9uM3GyR5Qx9/JXnDBE/ROyga8a1nsngKmuWv2qG0uYVLnf+99e -ql+Q36o8uM3wneuPOxOkzD4nXQKBgQC3ExnmeZQquVBtyCwk7jG4lg7eTOhPm7uW -kIsRdxjpWuAHa1iEatBltgYYJRI7qJtoZU8E4/x/HOVOMxbTGCRRlKA4eST+T8rr -I7X7r679+Y8iNkTRc4Eu9/3kqx/I2KxSIqjZ9b5A0Ffm2vNXl8ZmpngTOkGZjub9 -xGHVRy0x+wKBgQCnds/4tbC3enK9t/zYir89/61xIVQuMKtJytXGDVZ/o76OBKG9 -Yh79hh6XfvDp/JaeOh/wBwhK6cia0zwLR5bUEJq6TRqedhdxKyYq8HWHfUXEGW7d -IxlKfbfMHgtPbv0sSh/7MIIbRov7Q52n5ya7t7uGSfozUcw9SPiGJeNLmQKBgBYb -fmMwh03pdubq5znGccaiiEUb8Gm8mpiB9+kyApYbXTZIJjZHV8cQhgfgYiVGJQ4k -PbiPd2w6Jw6caMMxBSG82uQ27Nqw9AYVF+K+wlj1C+3Ls9+/HPh5BfXZHEt6rsR0 -XwODA2h8QEEma4BGWbUHoTRhMo7CjO5UCqRgpfqFAoGAUMSpZHVlg69hSV4JPY4W -TidlxZZ2EkNSFR53mvdfYwBimZJfQOMPhB1zzW4k1gnqvRL3ZOy2SEIqkRtB1EDo -hsa8urDYeKX0Wn49nCI7YpN6l5TZi15z3so7Mi5R504R05TTnJeUG9qDiLxgoeYw -AeA0DNNt/boaEGAdi2KBajU= ------END PRIVATE KEY----- diff --git a/app/src/main/resources/key/public_key.pem b/app/src/main/resources/key/public_key.pem deleted file mode 100644 index 2120d910..00000000 --- a/app/src/main/resources/key/public_key.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnMqqlb8v/0jxvDuKuxwr -fugDUan9wFUFYglwmgRBydDjWmzYe4J33W9sKWkRMP0yGqKD8kKpR3G938WrXrxx -8tq7veke707wnhRGFO/2YovnYAy3TjNb6quGXoiarz43Y0AQkfqBwttVIO0ccxGJ -rmuOf8UU+JG5fhlpSKpyC49WhKbFEn6/6eBHnLHBsZXPUncEIU9DsCrv10P+Pkq4 -exClb1T7suEqvd/U7RlO7OLGzCCsLszrNY+J+rCk7LOiHMREWMmSqguQK4VLVcl3 -S8L2YZp0/nLoSDzrpJ66czZDEXgtqg8VggGV4cL/BnVLtv9ELaQnBcFvIO7k3NJl -LwIDAQAB ------END PUBLIC KEY----- From 7f232fbb4db80a6817c76e53d723a2c5d7173e3f Mon Sep 17 00:00:00 2001 From: JeekLee Date: Sat, 21 Mar 2026 22:52:05 +0900 Subject: [PATCH 03/11] =?UTF-8?q?feat:=20=EB=8F=84=EC=BB=A4=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20build=20and=20depl?= =?UTF-8?q?oy=20script=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-and-deploy.yml | 105 +++++++++-------- app/.gitignore | 5 +- app/Dockerfile | 30 +---- app/Dockerfile-deploy | 5 - app/src/main/resources/application.yml | 149 +++++++++++++++++++++++++ 5 files changed, 211 insertions(+), 83 deletions(-) delete mode 100644 app/Dockerfile-deploy create mode 100644 app/src/main/resources/application.yml diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 081f414c..d99ff5dd 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -1,52 +1,35 @@ name: Release - Code Deploy with Github Actions (WIF & IAP Version) -#on: +on: # push: # tags: # - 'v*' -on: push: - branches: [ feature/code-deploy-gcpp ] + branches: [ feature/code-deploy-gcp ] # 테스트용 env: PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} - RESOURCE_DIR: src/main/resources - GCR_PACKAGE_NAME: pfplay-backend-java + IMAGE_NAME: pfplay-api GCE_INSTANCE: pfplay-api GCE_ZONE: asia-northeast3-a jobs: deploy: - name: Build and dockerize & deploy + name: Build, Dockerize & Deploy runs-on: ubuntu-latest - permissions: # WIF 사용을 위해 반드시 필요 - contents: read - packages: write - id-token: write + permissions: + contents: write # GitHub Release 생성 + packages: write # GHCR 이미지 push + id-token: write # WIF 인증 steps: - name: Checkout uses: actions/checkout@v4 - - name: Set release version -# run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $RELEASE_VERSION - run: echo "RELEASE_VERSION=v0.0.0" >> $RELEASE_VERSION - - # --- [기존 리소스 파일 생성 로직 유지] --- - - name: Create directory for resources - run: mkdir -p $RESOURCE_DIR/key - - - name: Set application.yml - env: - PROPERTY_FILE: ${{ secrets.PROD_PROFILE }} - run: echo $PROPERTY_FILE | base64 --decode > $RESOURCE_DIR/application.yml + - name: Extract release version from tag + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV - - name: Set JWT keys - run: | - echo "${{ secrets.JWT_PRIVATE_KEY }}" | base64 --decode > $RESOURCE_DIR/key/private_key.pem - echo "${{ secrets.JWT_PUBLIC_KEY }}" | base64 --decode > $RESOURCE_DIR/key/public_key.pem - - # --- [빌드 및 도커화] --- + # --- [빌드] --- - name: Set up JDK uses: actions/setup-java@v4 with: @@ -55,39 +38,63 @@ jobs: cache: 'gradle' - name: Build with Gradle - run: chmod +x ./gradlew && ./gradlew build -x test + run: chmod +x ./gradlew && ./gradlew :app:build -x test + + # --- [릴리스 생성] --- + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ env.RELEASE_VERSION }} + name: Release ${{ env.RELEASE_VERSION }} + generate_release_notes: true + + # --- [도커화 및 GHCR 업로드] --- + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io - username: JeekLee - password: ${{ secrets.PACKAGE_ACCESS_TOKEN }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Build and Push image uses: docker/build-push-action@v5 with: context: . - file: ./api/Dockerfile-deploy + file: ./app/Dockerfile push: true tags: | - ghcr.io/pfplay/${{ env.GCR_PACKAGE_NAME }}:latest - ghcr.io/pfplay/${{ env.GCR_PACKAGE_NAME }}:${{ env.RELEASE_VERSION }} + ghcr.io/pfplay/${{ env.IMAGE_NAME }}:latest + ghcr.io/pfplay/${{ env.IMAGE_NAME }}:${{ env.RELEASE_VERSION }} cache-from: type=gha cache-to: type=gha,mode=max - # --- [GCP 인증 및 IAP 배포] --- - - id: 'auth' - name: 'Authenticate to Google Cloud' - uses: 'google-github-actions/auth@v2' + # --- [GCP 인증] --- + - name: Authenticate to Google Cloud + id: auth + uses: google-github-actions/auth@v2 with: workload_identity_provider: ${{ secrets.WIF_PROVIDER }} service_account: ${{ secrets.WIF_SERVICE_ACCOUNT }} - - name: 'Set up Cloud SDK' - uses: 'google-github-actions/setup-gcloud@v2' + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v2 + + # --- [환경변수 파일 생성 및 전송] --- + - name: Generate .env file + run: echo "${{ secrets.ENV_FILE }}" > pfplay.env + + - name: Copy .env to VM + run: | + gcloud compute scp pfplay.env ${{ env.GCE_INSTANCE }}:/tmp/pfplay.env \ + --zone=${{ env.GCE_ZONE }} \ + --tunnel-through-iap \ + --project=${{ env.PROJECT_ID }} - - name: 'Deploy to VM via IAP Tunnel' + # --- [IAP 배포] --- + - name: Deploy to VM via IAP Tunnel run: | gcloud compute ssh ${{ env.GCE_INSTANCE }} \ --zone=${{ env.GCE_ZONE }} \ @@ -95,13 +102,15 @@ jobs: --project=${{ env.PROJECT_ID }} \ --command=" echo '${{ secrets.PACKAGE_ACCESS_TOKEN }}' | sudo docker login ghcr.io -u JeekLee --password-stdin && - sudo docker pull ghcr.io/pfplay/${{ env.GCR_PACKAGE_NAME }}:latest && - sudo docker stop pfplay-api-server || true && - sudo docker rm pfplay-api-server || true && - sudo docker run -d --name pfplay-api-server \ + sudo docker pull ghcr.io/pfplay/${{ env.IMAGE_NAME }}:latest && + sudo docker stop pfplay-api 2>/dev/null || true && + sudo docker rm pfplay-api 2>/dev/null || true && + sudo mkdir -p /app && sudo mv /tmp/pfplay.env /app/pfplay.env && sudo chmod 600 /app/pfplay.env && + sudo docker run -d \ + --name pfplay-api \ + --env-file /app/pfplay.env \ -p 8088:8080 \ --restart unless-stopped \ - ghcr.io/pfplay/${{ env.GCR_PACKAGE_NAME }}:latest && - sudo docker network connect api_backend pfplay-api-server || true && + ghcr.io/pfplay/${{ env.IMAGE_NAME }}:latest && sudo docker image prune -f - " \ No newline at end of file + " diff --git a/app/.gitignore b/app/.gitignore index 953428d9..9a14210d 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -36,7 +36,4 @@ out/ ### VS Code ### .vscode/ -/src/test/resources/key/** -app/src/main/resources/** - -*.env \ No newline at end of file +*.env diff --git a/app/Dockerfile b/app/Dockerfile index fd6e996e..2368bfc9 100644 --- a/app/Dockerfile +++ b/app/Dockerfile @@ -1,26 +1,4 @@ -#FROM eclipse-temurin:17-jdk-alpine as build -##FROM --platform=linux/amd64 eclipse-temurin:17-jdk-alpine as build -#WORKDIR /opt/pfplay -#COPY . /opt/pfplay -#RUN --mount=type=cache,target=/root/.gradle ./gradlew clean build -x test --no-daemon -##RUN ./gradlew clean build -x test --refresh-dependencies -#RUN mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*-SNAPSHOT.jar) -# -#FROM eclipse-temurin:17-jre-alpine -#WORKDIR /opt/api -#ARG JAR_FILE=/opt/pfplay/build/libs/*.jar -#ENV JAR_NAME=pfplay-api-v1.jar -#COPY --from=build ${JAR_FILE} ${JAR_NAME} -#ENTRYPOINT ["sh", "-c", "java -jar ${JAR_NAME}"] -FROM eclipse-temurin:17-jdk-alpine as build - -ARG SPRING_PROFILES_ACTIVE -ENV SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE} -RUN echo ${SPRING_PROFILES_ACTIVE} - -WORKDIR /opt/pfplay -COPY . . -RUN ./gradlew clean build -x test --refresh-dependencies -#ENTRYPOINT ["sh", "-c", "java -jar ./build/libs/*-SNAPSHOT.jar"] -ENTRYPOINT ["sh", "-c", "java -jar -Dspring.profiles.active=$SPRING_PROFILES_ACTIVE ./build/libs/*-SNAPSHOT.jar"] - +FROM eclipse-temurin:17-jre-alpine +WORKDIR /opt/api +COPY app/build/libs/*.jar app.jar +ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/app/Dockerfile-deploy b/app/Dockerfile-deploy deleted file mode 100644 index 10521318..00000000 --- a/app/Dockerfile-deploy +++ /dev/null @@ -1,5 +0,0 @@ -FROM eclipse-temurin:17-jdk - -COPY api/build/libs/*-SNAPSHOT.jar app.jar - -ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=dev", "app.jar"] \ No newline at end of file diff --git a/app/src/main/resources/application.yml b/app/src/main/resources/application.yml new file mode 100644 index 00000000..df231751 --- /dev/null +++ b/app/src/main/resources/application.yml @@ -0,0 +1,149 @@ +spring: + profiles: + active: local # default + group: + local: + - common + dev: + - common + prod: + - common + +--- +# 🟢 공통 설정 +spring: + config: + activate: + on-profile: common + + datasource: + url: ${DB_URL:jdbc:mysql://localhost:3306/pfplay?characterEncoding=UTF-8&useUnicode=true&serverTimezone=Asia/Seoul} + username: ${DB_USERNAME:pfplay} + password: ${DB_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver + + jpa: + show-sql: true + hibernate: + ddl-auto: create + properties: + hibernate: + format_sql: true + + sql: + init: + mode: always + + data: + redis: + host: localhost + port: 6379 + repositories: + enabled: false + +server: + tomcat: + relaxed-query-chars: '[,],|,{,},^,`,<,>' + error: + include-message: always + include-binding-errors: always + include-stacktrace: on_param + include-exception: false + +springdoc: + version: 'v1' + swagger-ui: + display-request-duration: true + groups-order: DESC + operationsSorter: method + disable-swagger-default-url: true + use-root-path: false + path: /spec/api + show-actuator: true + +logging: + level: + org.springframework.security: DEBUG + org.springframework.web.reactive.function.client: DEBUG + +service-api: + pytube: + uri: ${PYTUBE_URI:http://localhost:9000} + api-key: ${PYTUBE_API_KEY:pfplay_streaming} + api-secret: ${PYTUBE_API_SECRET} + +# 🔐 커스텀 앱 설정 +app: + oauth2: + providers: + google: + client-id: ${GOOGLE_CLIENT_ID} + client-secret: ${GOOGLE_CLIENT_SECRET} + redirect-uri: ${GOOGLE_REDIRECT_URI:https://localhost:3000/auth/callback/google} + authorization-uri: https://accounts.google.com/o/oauth2/v2/auth + token-uri: https://oauth2.googleapis.com/token + user-info-uri: https://www.googleapis.com/oauth2/v2/userinfo + scopes: + - email + access-type: offline # Google 전용: refresh token 받기 + prompt: consent # Google 전용: 매번 동의 화면 + + twitter: + client-id: ${TWITTER_CLIENT_ID} + client-secret: ${TWITTER_CLIENT_SECRET} + redirect-uri: ${TWITTER_REDIRECT_URI:http://localhost:3000/auth/callback/twitter} + authorization-uri: https://twitter.com/i/oauth2/authorize + token-uri: https://api.twitter.com/2/oauth2/token + user-info-uri: https://api.twitter.com/2/users/me + scopes: + - users.read + - tweet.read + + jwt: + secret: ${JWT_SECRET} + expiration-ms: 86400000 # 24시간 + refresh-expiration-ms: 604800000 # 7일 + + cookie: + access-token-name: AccessToken + refresh-token-name: RefreshToken + domain: localhost + path: / + access-token-expiry-seconds: 86400 + refresh-token-expiry-seconds: 604800 + secure: true + same-site: None + + webclient: + timeout: + connection: 5000 + read: 5000 + write: 5000 + +--- + +# 🟡 개발 환경 (추후 필요 시 확장) +spring: + config: + activate: + on-profile: dev + +--- + +# 🔴 운영 환경 +spring: + config: + activate: + on-profile: prod + +app: + jwt: + cookie: + secure: true + same-site: Strict + domain: ${PRODUCTION_DOMAIN} + +springdoc: + # 운영에서는 Swagger 비활성화 권장 + swagger-ui: + enabled: false From bc9d7fc43c06208e31f34f00e5d843a296e19d6f Mon Sep 17 00:00:00 2001 From: JeekLee Date: Sat, 21 Mar 2026 22:59:32 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feat:=20=EB=8F=84=EC=BB=A4=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20build=20and=20depl?= =?UTF-8?q?oy=20script=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-and-deploy.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index d99ff5dd..01b56ba6 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -108,8 +108,9 @@ jobs: sudo mkdir -p /app && sudo mv /tmp/pfplay.env /app/pfplay.env && sudo chmod 600 /app/pfplay.env && sudo docker run -d \ --name pfplay-api \ + --network api_backend \ --env-file /app/pfplay.env \ - -p 8088:8080 \ + -p 8080:8080 \ --restart unless-stopped \ ghcr.io/pfplay/${{ env.IMAGE_NAME }}:latest && sudo docker image prune -f From 3875b58291ca37f96e4b58e284a537ffc51fd8fa Mon Sep 17 00:00:00 2001 From: JeekLee Date: Sat, 21 Mar 2026 23:03:20 +0900 Subject: [PATCH 05/11] =?UTF-8?q?test:=20=EB=A6=B4=EB=A6=AC=EC=A6=88=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=B6=80=EB=B6=84=20=EC=A3=BC=EC=84=9D?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-and-deploy.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 01b56ba6..b9749832 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -27,7 +27,8 @@ jobs: uses: actions/checkout@v4 - name: Extract release version from tag - run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV + run: echo "RELEASE_VERSION=0.0.0" >> $GITHUB_ENV +# run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV # --- [빌드] --- - name: Set up JDK @@ -41,12 +42,12 @@ jobs: run: chmod +x ./gradlew && ./gradlew :app:build -x test # --- [릴리스 생성] --- - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - tag_name: ${{ env.RELEASE_VERSION }} - name: Release ${{ env.RELEASE_VERSION }} - generate_release_notes: true +# - name: Create GitHub Release +# uses: softprops/action-gh-release@v2 +# with: +# tag_name: ${{ env.RELEASE_VERSION }} +# name: Release ${{ env.RELEASE_VERSION }} +# generate_release_notes: true # --- [도커화 및 GHCR 업로드] --- - name: Set up Docker Buildx From 265743b1f93aee5bc4947a2bd131a104083be57e Mon Sep 17 00:00:00 2001 From: JeekLee Date: Sat, 21 Mar 2026 23:19:32 +0900 Subject: [PATCH 06/11] test: add whoami step to identify GHA SSH username --- .github/workflows/build-and-deploy.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index b9749832..3c722a56 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -95,6 +95,14 @@ jobs: --project=${{ env.PROJECT_ID }} # --- [IAP 배포] --- + - name: Check SSH username + run: | + gcloud compute ssh ${{ env.GCE_INSTANCE }} \ + --zone=${{ env.GCE_ZONE }} \ + --tunnel-through-iap \ + --project=${{ env.PROJECT_ID }} \ + --command="whoami" + - name: Deploy to VM via IAP Tunnel run: | gcloud compute ssh ${{ env.GCE_INSTANCE }} \ From 86707a6a3a7784b4482bf8caea136514a9c4e651 Mon Sep 17 00:00:00 2001 From: JeekLee Date: Sat, 21 Mar 2026 23:30:06 +0900 Subject: [PATCH 07/11] fix: remove sudo from docker commands, use docker group --- .github/workflows/build-and-deploy.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 3c722a56..88935980 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -110,17 +110,17 @@ jobs: --tunnel-through-iap \ --project=${{ env.PROJECT_ID }} \ --command=" - echo '${{ secrets.PACKAGE_ACCESS_TOKEN }}' | sudo docker login ghcr.io -u JeekLee --password-stdin && - sudo docker pull ghcr.io/pfplay/${{ env.IMAGE_NAME }}:latest && - sudo docker stop pfplay-api 2>/dev/null || true && - sudo docker rm pfplay-api 2>/dev/null || true && + echo '${{ secrets.PACKAGE_ACCESS_TOKEN }}' | docker login ghcr.io -u JeekLee --password-stdin && + docker pull ghcr.io/pfplay/${{ env.IMAGE_NAME }}:latest && + docker stop pfplay-api 2>/dev/null || true && + docker rm pfplay-api 2>/dev/null || true && sudo mkdir -p /app && sudo mv /tmp/pfplay.env /app/pfplay.env && sudo chmod 600 /app/pfplay.env && - sudo docker run -d \ + docker run -d \ --name pfplay-api \ --network api_backend \ --env-file /app/pfplay.env \ -p 8080:8080 \ --restart unless-stopped \ ghcr.io/pfplay/${{ env.IMAGE_NAME }}:latest && - sudo docker image prune -f + docker image prune -f " From 98d56f3ebd71dca18a467f33ba080d19bd67fc9c Mon Sep 17 00:00:00 2001 From: JeekLee Date: Sat, 21 Mar 2026 23:35:11 +0900 Subject: [PATCH 08/11] fix: store .env in SA home dir to avoid sudo for file ops --- .github/workflows/build-and-deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 88935980..d6f5dc98 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -114,11 +114,11 @@ jobs: docker pull ghcr.io/pfplay/${{ env.IMAGE_NAME }}:latest && docker stop pfplay-api 2>/dev/null || true && docker rm pfplay-api 2>/dev/null || true && - sudo mkdir -p /app && sudo mv /tmp/pfplay.env /app/pfplay.env && sudo chmod 600 /app/pfplay.env && + mv /tmp/pfplay.env ~/pfplay.env && chmod 600 ~/pfplay.env && docker run -d \ --name pfplay-api \ --network api_backend \ - --env-file /app/pfplay.env \ + --env-file ~/pfplay.env \ -p 8080:8080 \ --restart unless-stopped \ ghcr.io/pfplay/${{ env.IMAGE_NAME }}:latest && From 3a12d90758578a0b15c116dbcdf700bf512b2ecb Mon Sep 17 00:00:00 2001 From: JeekLee Date: Sat, 21 Mar 2026 23:40:33 +0900 Subject: [PATCH 09/11] =?UTF-8?q?test:=20.env=20=EC=8B=9C=ED=81=AC?= =?UTF-8?q?=EB=A6=BF=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-and-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index d6f5dc98..07155bfc 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -85,7 +85,7 @@ jobs: # --- [환경변수 파일 생성 및 전송] --- - name: Generate .env file - run: echo "${{ secrets.ENV_FILE }}" > pfplay.env + run: echo "${{ secrets.DOT_ENV }}" > pfplay.env - name: Copy .env to VM run: | From 93a26766d5ad63c787c21d14884704c395429294 Mon Sep 17 00:00:00 2001 From: JeekLee Date: Sat, 21 Mar 2026 23:50:40 +0900 Subject: [PATCH 10/11] fix: externalize Redis host/port to environment variables --- app/src/main/resources/application.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/resources/application.yml b/app/src/main/resources/application.yml index df231751..e4e40262 100644 --- a/app/src/main/resources/application.yml +++ b/app/src/main/resources/application.yml @@ -36,8 +36,8 @@ spring: data: redis: - host: localhost - port: 6379 + host: ${REDIS_HOST:localhost} + port: ${REDIS_PORT:6379} repositories: enabled: false From 4d1c2f7b72c6a352005d388e9b6b2c688c2dbbc0 Mon Sep 17 00:00:00 2001 From: JeekLee Date: Sat, 21 Mar 2026 23:58:31 +0900 Subject: [PATCH 11/11] =?UTF-8?q?test:=20=EC=9D=B4=EC=A0=84=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=20workflow=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-and-deploy.yml | 2 +- .../workflows/config/release-draft-config.yml | 25 ---- .github/workflows/release-code-deploy.yml | 111 ------------------ .github/workflows/release-draft.yml | 16 --- 4 files changed, 1 insertion(+), 153 deletions(-) delete mode 100644 .github/workflows/config/release-draft-config.yml delete mode 100644 .github/workflows/release-code-deploy.yml delete mode 100644 .github/workflows/release-draft.yml diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 07155bfc..5408d4c5 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -5,7 +5,7 @@ on: # tags: # - 'v*' push: - branches: [ feature/code-deploy-gcp ] # 테스트용 + branches: [ main ] # 버전 관리 적용 전 임의로 0.0.0 버전으로 생성 및 적용 env: PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} diff --git a/.github/workflows/config/release-draft-config.yml b/.github/workflows/config/release-draft-config.yml deleted file mode 100644 index d8a71cb0..00000000 --- a/.github/workflows/config/release-draft-config.yml +++ /dev/null @@ -1,25 +0,0 @@ -name-template: 'v$RESOLVED_VERSION' -tag-template: 'v$RESOLVED_VERSION' -categories: - - title: '🚀 Features' - label: 'enhancement' - - title: '🐛 Bugfixes' - label: 'bug' - - title: '📚 Docs' - label: 'documentation' -change-template: '- $TITLE #$NUMBER @$AUTHOR ' -template: | - $CHANGES - ### 모든 PR이 반영된 후에는 Pre-release에서 release로 변경 부탁드립니다. - -version-resolver: - major: - labels: - - 'major' - minor: - labels: - - 'minor' - patch: - labels: - - 'patch' - default: patch \ No newline at end of file diff --git a/.github/workflows/release-code-deploy.yml b/.github/workflows/release-code-deploy.yml deleted file mode 100644 index 7e45fb48..00000000 --- a/.github/workflows/release-code-deploy.yml +++ /dev/null @@ -1,111 +0,0 @@ -name: Release - Code Deploy with Github Actions - -on: - push: - tags: - - 'v*' - -env: - RESOURCE_DIR: src/main/resources - GCR_PACKAGE_NAME: prod-pfplay-backend-java -jobs: - deploy: - name: Build and dockerize & deploy - runs-on: ubuntu-latest - - defaults: - run: - working-directory: api - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Set env - run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV - - - name: Create directory for resources - run: mkdir -p $RESOURCE_DIR/key - - - name: Set application.yml - env: - PROPERTY_FILE: ${{ secrets.PROD_PROFILE }} - PROPERTY_FILE_NAME: application.yml - run: echo $PROPERTY_FILE | base64 --decode > $RESOURCE_DIR/$PROPERTY_FILE_NAME - - - name: Set JWT private key - env: - JWT_PRIVATE_KEY_FILE: ${{ secrets.JWT_PRIVATE_KEY }} - JWT_PRIVATE_KEY_FILE_NAME: private_key.pem - run: echo $JWT_PRIVATE_KEY_FILE | base64 --decode > $RESOURCE_DIR/key/$JWT_PRIVATE_KEY_FILE_NAME - - - name: Set JWT public key - env: - JWT_PUBLIC_KEY_FILE: ${{ secrets.JWT_PUBLIC_KEY }} - JWT_PUBLIC_KEY_FILE_NAME: public_key.pem - run: echo $JWT_PUBLIC_KEY_FILE | base64 --decode > $RESOURCE_DIR/key/$JWT_PUBLIC_KEY_FILE_NAME - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - - - name: Set up Git Actions cache - uses: actions/cache@v3 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - - name: Grant execute permission for gradlew - run: chmod +x ./gradlew - - - name: Build with Gradle - run: ./gradlew build -x test - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: JeekLee - password: ${{ secrets.PACKAGE_ACCESS_TOKEN }} - - - name: Build, tag and push image to Github Container Registry - uses: docker/build-push-action@v2 - with: - context: . - file: ./api/Dockerfile-deploy - push: true - tags: | - ghcr.io/pfplay/${{ env.GCR_PACKAGE_NAME }}:latest - ghcr.io/pfplay/${{ env.GCR_PACKAGE_NAME }}:${{ env.RELEASE_VERSION }} - cache-from: type=gha # Refer: https://docs.docker.com/build/ci/github-actions/cache/ - cache-to: type=gha,mode=max - - - name: Pull image from Github registry to GCP VM - uses: appleboy/ssh-action@master - env: - PACKAGE_ACCESS_TOKEN: ${{ secrets.PACKAGE_ACCESS_TOKEN }} - GCR_PACKAGE_NAME: ${{ env.GCR_PACKAGE_NAME }} - with: - host: ${{ secrets.GCP_VM_INSTANCE }} - username: gm - port: 22 - key: ${{ secrets.GCP_VM_SSH_SECRET}} - passphrase: ${{ secrets.GCP_VM_SSH_PASSPHRASE }} - envs: PACKAGE_ACCESS_TOKEN, GCR_PACKAGE_NAME - script: | - docker stop pfplay-api-server && docker rm pfplay-api-server - sudo docker rmi $(docker images | grep "prod-pfplay-backend-java") -f - echo $PACKAGE_ACCESS_TOKEN | docker login ghcr.io -u JeekLee --password-stdin - docker pull ghcr.io/pfplay/$GCR_PACKAGE_NAME:latest - docker run -d --name pfplay-api-server -p 8088:8080 --restart unless-stopped ghcr.io/pfplay/$GCR_PACKAGE_NAME:latest - docker network connect api_backend pfplay-api-server - diff --git a/.github/workflows/release-draft.yml b/.github/workflows/release-draft.yml deleted file mode 100644 index ebe930b6..00000000 --- a/.github/workflows/release-draft.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Release - Release Draft with Github Actions - -on: - push: - branches: - - release - -jobs: - update_release_draft: - runs-on: ubuntu-latest - steps: - - uses: release-drafter/release-drafter@v5 - with: - config-name: workflows/config/release-draft-config.yml - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file