From eb5bc5cb44a28979a8de9a3f0f5cd2443aa3a3c4 Mon Sep 17 00:00:00 2001 From: Hankyeol Choi Date: Thu, 8 Jan 2026 16:41:26 +0900 Subject: [PATCH 1/5] =?UTF-8?q?=EA=B9=83=ED=97=99=20=EC=95=A1=EC=85=98=20?= =?UTF-8?q?=EC=88=98=EB=8F=99=EB=B0=B0=ED=8F=AC=20=EC=B6=94=EA=B0=80,=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=ED=99=94=20(#475)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/_deploy-native.yml | 72 +++++++++++++++++++ .github/workflows/_deploy.yml | 71 ++++++++++++++++++ .github/workflows/deploy-api-dev-native.yml | 41 ++--------- .github/workflows/deploy-api-dev.yml | 41 ++--------- .github/workflows/deploy-api-prod-native.yml | 41 ++--------- .github/workflows/deploy-api-prod.yml | 45 +++--------- .github/workflows/deploy-batch-dev-native.yml | 41 ++--------- .github/workflows/deploy-batch-dev.yml | 41 ++--------- .../workflows/deploy-batch-prod-native.yml | 41 ++--------- .github/workflows/deploy-batch-prod.yml | 41 ++--------- .github/workflows/deploy-manual-native.yml | 29 ++++++++ .github/workflows/deploy-manual.yml | 28 ++++++++ 12 files changed, 260 insertions(+), 272 deletions(-) create mode 100644 .github/workflows/_deploy-native.yml create mode 100644 .github/workflows/_deploy.yml create mode 100644 .github/workflows/deploy-manual-native.yml create mode 100644 .github/workflows/deploy-manual.yml diff --git a/.github/workflows/_deploy-native.yml b/.github/workflows/_deploy-native.yml new file mode 100644 index 00000000..ba48d90e --- /dev/null +++ b/.github/workflows/_deploy-native.yml @@ -0,0 +1,72 @@ +name: deploy-native-template + +on: + workflow_call: + inputs: + target_branch: + required: false + type: string + default: '' + ecr_repository: + required: true + type: string + dockerfile: + required: true + type: string + description: 'Dockerfile path (e.g., api/Dockerfile-native)' + secrets: + AWS_ACCESS_KEY_ID: + required: true + AWS_SECRET_ACCESS_KEY: + required: true + +jobs: + deploy: + name: Deploy Native + runs-on: ubuntu-24.04-arm + + env: + IMAGE_TAG: ${{ github.run_number }} + BUILD_NUMBER: ${{ github.run_number }} + ECR_REGISTRY: 405906814034.dkr.ecr.ap-northeast-2.amazonaws.com + ECR_REPOSITORY: ${{ inputs.ecr_repository }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ inputs.target_branch || github.ref }} + + - 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: Login to ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Get and save Auth Token for CodeArtifact + id: get-save-codeartifact-auth-token + run: | + aws codeartifact get-authorization-token \ + --domain wafflestudio \ + --domain-owner 405906814034 \ + --query authorizationToken \ + --region ap-northeast-1 \ + --output text > .codeartifact_token + + - name: Docker build, tag, and push image to ECR + id: build-push-image + run: | + docker build \ + --secret id=codeartifact_token,src=./.codeartifact_token \ + -f ${{ inputs.dockerfile }} \ + -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \ + . \ + --platform linux/arm64 + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT + diff --git a/.github/workflows/_deploy.yml b/.github/workflows/_deploy.yml new file mode 100644 index 00000000..e313cc36 --- /dev/null +++ b/.github/workflows/_deploy.yml @@ -0,0 +1,71 @@ +name: deploy-template + +on: + workflow_call: + inputs: + target_branch: + required: false + type: string + default: '' + ecr_repository: + required: true + type: string + dockerfile: + required: true + type: string + description: 'Dockerfile path (e.g., api/Dockerfile)' + secrets: + AWS_ACCESS_KEY_ID: + required: true + AWS_SECRET_ACCESS_KEY: + required: true + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-24.04-arm + + env: + IMAGE_TAG: ${{ github.run_number }} + BUILD_NUMBER: ${{ github.run_number }} + ECR_REGISTRY: 405906814034.dkr.ecr.ap-northeast-2.amazonaws.com + ECR_REPOSITORY: ${{ inputs.ecr_repository }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ inputs.target_branch || github.ref }} + + - 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: Login to ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Get and save Auth Token for CodeArtifact + id: get-save-codeartifact-auth-token + run: | + aws codeartifact get-authorization-token \ + --domain wafflestudio \ + --domain-owner 405906814034 \ + --query authorizationToken \ + --region ap-northeast-1 \ + --output text > .codeartifact_token + + - name: Docker build, tag, and push image to ECR + id: build-push-image + run: | + docker build \ + --secret id=codeartifact_token,src=./.codeartifact_token \ + -f ${{ inputs.dockerfile }} \ + -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \ + . \ + --platform linux/arm64 + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT diff --git a/.github/workflows/deploy-api-dev-native.yml b/.github/workflows/deploy-api-dev-native.yml index 9aa78b2e..739e19e9 100644 --- a/.github/workflows/deploy-api-dev-native.yml +++ b/.github/workflows/deploy-api-dev-native.yml @@ -6,37 +6,10 @@ on: jobs: deploy: - name: Deploy-api-dev-native - runs-on: ubuntu-24.04-arm - env: - IMAGE_TAG: ${{ github.run_number }} - BUILD_NUMBER: ${{ github.run_number }} - ECR_REGISTRY: 405906814034.dkr.ecr.ap-northeast-2.amazonaws.com - ECR_REPOSITORY: snutt-dev/snutt-timetable - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - 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: Login to ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2 - - - name: Get and save Auth Token for CodeArtifact - id: get-save-codeartifact-auth-token - run: | - aws codeartifact get-authorization-token --domain wafflestudio --domain-owner 405906814034 --query authorizationToken --region ap-northeast-1 --output text > .codeartifact_token - - - name: Docker build, tag, and push image to ECR - id: build-push-image - run: | - docker build --secret id=codeartifact_token,src=./.codeartifact_token -f api/Dockerfile-native -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . --platform linux/arm64 - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" + uses: ./.github/workflows/_deploy-native.yml + with: + ecr_repository: snutt-dev/snutt-timetable + dockerfile: api/Dockerfile-native + secrets: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/.github/workflows/deploy-api-dev.yml b/.github/workflows/deploy-api-dev.yml index 9800b01d..fd7362c5 100644 --- a/.github/workflows/deploy-api-dev.yml +++ b/.github/workflows/deploy-api-dev.yml @@ -6,37 +6,10 @@ on: jobs: deploy: - name: Deploy-api-dev - runs-on: ubuntu-24.04-arm - env: - IMAGE_TAG: ${{ github.run_number }} - BUILD_NUMBER: ${{ github.run_number }} - ECR_REGISTRY: 405906814034.dkr.ecr.ap-northeast-2.amazonaws.com - ECR_REPOSITORY: snutt-dev/snutt-timetable - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - 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: Login to ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2 - - - name: Get and save Auth Token for CodeArtifact - id: get-save-codeartifact-auth-token - run: | - aws codeartifact get-authorization-token --domain wafflestudio --domain-owner 405906814034 --query authorizationToken --region ap-northeast-1 --output text > .codeartifact_token - - - name: Docker build, tag, and push image to ECR - id: build-push-image - run: | - docker build --secret id=codeartifact_token,src=./.codeartifact_token -f api/Dockerfile -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . --platform linux/arm64 - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" + uses: ./.github/workflows/_deploy.yml + with: + ecr_repository: snutt-dev/snutt-timetable + dockerfile: api/Dockerfile + secrets: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/.github/workflows/deploy-api-prod-native.yml b/.github/workflows/deploy-api-prod-native.yml index 5762f6ec..edd8ad7d 100644 --- a/.github/workflows/deploy-api-prod-native.yml +++ b/.github/workflows/deploy-api-prod-native.yml @@ -6,37 +6,10 @@ on: jobs: deploy: - name: Deploy-api-prod-native - runs-on: ubuntu-24.04-arm - env: - IMAGE_TAG: ${{ github.run_number }} - BUILD_NUMBER: ${{ github.run_number }} - ECR_REGISTRY: 405906814034.dkr.ecr.ap-northeast-2.amazonaws.com - ECR_REPOSITORY: snutt-prod/snutt-timetable - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - 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: Login to ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2 - - - name: Get and save Auth Token for CodeArtifact - id: get-save-codeartifact-auth-token - run: | - aws codeartifact get-authorization-token --domain wafflestudio --domain-owner 405906814034 --query authorizationToken --region ap-northeast-1 --output text > .codeartifact_token - - - name: Docker build, tag, and push image to ECR - id: build-push-image - run: | - docker build --secret id=codeartifact_token,src=./.codeartifact_token -f api/Dockerfile-native -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . --platform linux/arm64 - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" + uses: ./.github/workflows/_deploy-native.yml + with: + ecr_repository: snutt-prod/snutt-timetable + dockerfile: api/Dockerfile-native + secrets: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/.github/workflows/deploy-api-prod.yml b/.github/workflows/deploy-api-prod.yml index ea105ae8..e84b126c 100644 --- a/.github/workflows/deploy-api-prod.yml +++ b/.github/workflows/deploy-api-prod.yml @@ -6,41 +6,18 @@ on: jobs: deploy: - name: Deploy-api-prod - runs-on: ubuntu-24.04-arm - env: - IMAGE_TAG: ${{ github.run_number }} - BUILD_NUMBER: ${{ github.run_number }} - ECR_REGISTRY: 405906814034.dkr.ecr.ap-northeast-2.amazonaws.com - ECR_REPOSITORY: snutt-prod/snutt-timetable - + uses: ./.github/workflows/_deploy.yml + with: + ecr_repository: snutt-prod/snutt-timetable + dockerfile: api/Dockerfile + secrets: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + + notify: + needs: deploy + runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 - - - 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: Login to ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2 - - - name: Get and save Auth Token for CodeArtifact - id: get-save-codeartifact-auth-token - run: | - aws codeartifact get-authorization-token --domain wafflestudio --domain-owner 405906814034 --query authorizationToken --region ap-northeast-1 --output text > .codeartifact_token - - - name: Docker build, tag, and push image to ECR - id: build-push-image - run: | - docker build --secret id=codeartifact_token,src=./.codeartifact_token -f api/Dockerfile -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . --platform linux/arm64 - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" - - name: Slack Notify uses: rtCamp/action-slack-notify@v2.3.3 env: diff --git a/.github/workflows/deploy-batch-dev-native.yml b/.github/workflows/deploy-batch-dev-native.yml index 23ebb567..a52efb03 100644 --- a/.github/workflows/deploy-batch-dev-native.yml +++ b/.github/workflows/deploy-batch-dev-native.yml @@ -6,37 +6,10 @@ on: jobs: deploy: - name: Deploy-batch-dev-native - runs-on: ubuntu-24.04-arm - env: - IMAGE_TAG: ${{ github.run_number }} - BUILD_NUMBER: ${{ github.run_number }} - ECR_REGISTRY: 405906814034.dkr.ecr.ap-northeast-2.amazonaws.com - ECR_BATCH_REPOSITORY: snutt-dev/snutt-timetable-batch - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - 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: Login to ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2 - - - name: Get and save Auth Token for CodeArtifact - id: get-save-codeartifact-auth-token - run: | - aws codeartifact get-authorization-token --domain wafflestudio --domain-owner 405906814034 --query authorizationToken --region ap-northeast-1 --output text > .codeartifact_token - - - name: Docker build, tag, and push image to ECR - id: build-push-image - run: | - docker build --secret id=codeartifact_token,src=./.codeartifact_token -f batch/Dockerfile-native -t $ECR_REGISTRY/$ECR_BATCH_REPOSITORY:$IMAGE_TAG . --platform linux/arm64 - docker push $ECR_REGISTRY/$ECR_BATCH_REPOSITORY:$IMAGE_TAG - echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" + uses: ./.github/workflows/_deploy-native.yml + with: + ecr_repository: snutt-dev/snutt-timetable-batch + dockerfile: batch/Dockerfile-native + secrets: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/.github/workflows/deploy-batch-dev.yml b/.github/workflows/deploy-batch-dev.yml index ad6c2b3e..df0841ad 100644 --- a/.github/workflows/deploy-batch-dev.yml +++ b/.github/workflows/deploy-batch-dev.yml @@ -6,37 +6,10 @@ on: jobs: deploy: - name: Deploy-batch-dev - runs-on: ubuntu-24.04-arm - env: - IMAGE_TAG: ${{ github.run_number }} - BUILD_NUMBER: ${{ github.run_number }} - ECR_REGISTRY: 405906814034.dkr.ecr.ap-northeast-2.amazonaws.com - ECR_BATCH_REPOSITORY: snutt-dev/snutt-timetable-batch - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - 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: Login to ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2 - - - name: Get and save Auth Token for CodeArtifact - id: get-save-codeartifact-auth-token - run: | - aws codeartifact get-authorization-token --domain wafflestudio --domain-owner 405906814034 --query authorizationToken --region ap-northeast-1 --output text > .codeartifact_token - - - name: Docker build, tag, and push image to ECR - id: build-push-image - run: | - docker build --secret id=codeartifact_token,src=./.codeartifact_token -f batch/Dockerfile -t $ECR_REGISTRY/$ECR_BATCH_REPOSITORY:$IMAGE_TAG . --platform linux/arm64 - docker push $ECR_REGISTRY/$ECR_BATCH_REPOSITORY:$IMAGE_TAG - echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" + uses: ./.github/workflows/_deploy.yml + with: + ecr_repository: snutt-dev/snutt-timetable-batch + dockerfile: batch/Dockerfile + secrets: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/.github/workflows/deploy-batch-prod-native.yml b/.github/workflows/deploy-batch-prod-native.yml index 8b2677d5..0413702c 100644 --- a/.github/workflows/deploy-batch-prod-native.yml +++ b/.github/workflows/deploy-batch-prod-native.yml @@ -6,37 +6,10 @@ on: jobs: deploy: - name: Deploy-batch-prod-native - runs-on: ubuntu-24.04-arm - env: - IMAGE_TAG: ${{ github.run_number }} - BUILD_NUMBER: ${{ github.run_number }} - ECR_REGISTRY: 405906814034.dkr.ecr.ap-northeast-2.amazonaws.com - ECR_BATCH_REPOSITORY: snutt-prod/snutt-timetable-batch - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - 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: Login to ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2 - - - name: Get and save Auth Token for CodeArtifact - id: get-save-codeartifact-auth-token - run: | - aws codeartifact get-authorization-token --domain wafflestudio --domain-owner 405906814034 --query authorizationToken --region ap-northeast-1 --output text > .codeartifact_token - - - name: Docker build, tag, and push image to ECR - id: build-push-image - run: | - docker build --secret id=codeartifact_token,src=./.codeartifact_token -f batch/Dockerfile-native -t $ECR_REGISTRY/$ECR_BATCH_REPOSITORY:$IMAGE_TAG . --platform linux/arm64 - docker push $ECR_REGISTRY/$ECR_BATCH_REPOSITORY:$IMAGE_TAG - echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" + uses: ./.github/workflows/_deploy-native.yml + with: + ecr_repository: snutt-prod/snutt-timetable-batch + dockerfile: batch/Dockerfile-native + secrets: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/.github/workflows/deploy-batch-prod.yml b/.github/workflows/deploy-batch-prod.yml index ae527fbc..62b0f65a 100644 --- a/.github/workflows/deploy-batch-prod.yml +++ b/.github/workflows/deploy-batch-prod.yml @@ -6,37 +6,10 @@ on: jobs: deploy: - name: Deploy-batch-prod - runs-on: ubuntu-24.04-arm - env: - IMAGE_TAG: ${{ github.run_number }} - BUILD_NUMBER: ${{ github.run_number }} - ECR_REGISTRY: 405906814034.dkr.ecr.ap-northeast-2.amazonaws.com - ECR_BATCH_REPOSITORY: snutt-prod/snutt-timetable-batch - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - 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: Login to ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2 - - - name: Get and save Auth Token for CodeArtifact - id: get-save-codeartifact-auth-token - run: | - aws codeartifact get-authorization-token --domain wafflestudio --domain-owner 405906814034 --query authorizationToken --region ap-northeast-1 --output text > .codeartifact_token - - - name: Docker build, tag, and push image to ECR - id: build-push-image - run: | - docker build --secret id=codeartifact_token,src=./.codeartifact_token -f batch/Dockerfile -t $ECR_REGISTRY/$ECR_BATCH_REPOSITORY:$IMAGE_TAG . --platform linux/arm64 - docker push $ECR_REGISTRY/$ECR_BATCH_REPOSITORY:$IMAGE_TAG - echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" + uses: ./.github/workflows/_deploy.yml + with: + ecr_repository: snutt-prod/snutt-timetable-batch + dockerfile: batch/Dockerfile + secrets: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/.github/workflows/deploy-manual-native.yml b/.github/workflows/deploy-manual-native.yml new file mode 100644 index 00000000..0b1fe105 --- /dev/null +++ b/.github/workflows/deploy-manual-native.yml @@ -0,0 +1,29 @@ +name: Deploy Manual Native + +on: + workflow_dispatch: + inputs: + target_branch: + description: '배포할 브랜치명' + required: true + type: string + ecr_repository: + description: 'ECR 리포지토리 (예: snutt-dev/snutt-timetable)' + required: true + type: string + dockerfile: + description: 'Dockerfile 경로 (예: api/Dockerfile-native)' + required: true + type: string + +jobs: + deploy: + uses: ./.github/workflows/_deploy-native.yml + with: + target_branch: ${{ inputs.target_branch }} + ecr_repository: ${{ inputs.ecr_repository }} + dockerfile: ${{ inputs.dockerfile }} + secrets: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + diff --git a/.github/workflows/deploy-manual.yml b/.github/workflows/deploy-manual.yml new file mode 100644 index 00000000..3fd63b42 --- /dev/null +++ b/.github/workflows/deploy-manual.yml @@ -0,0 +1,28 @@ +name: Deploy Manual + +on: + workflow_dispatch: + inputs: + target_branch: + description: '배포할 브랜치명' + required: true + type: string + ecr_repository: + description: 'ECR 리포지토리 (예: snutt-dev/snutt-timetable)' + required: true + type: string + dockerfile: + description: 'Dockerfile 경로 (예: api/Dockerfile)' + required: true + type: string + +jobs: + deploy: + uses: ./.github/workflows/_deploy.yml + with: + target_branch: ${{ inputs.target_branch }} + ecr_repository: ${{ inputs.ecr_repository }} + dockerfile: ${{ inputs.dockerfile }} + secrets: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} From b4665e8e0d48f9413d015874462b369be0c59356 Mon Sep 17 00:00:00 2001 From: Hank-Choi Date: Thu, 8 Jan 2026 16:59:20 +0900 Subject: [PATCH 2/5] =?UTF-8?q?api=20native=20=EB=B0=B0=ED=8F=AC=EB=8F=84?= =?UTF-8?q?=20slack=20=EC=95=8C=EB=A6=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-api-prod-native.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/deploy-api-prod-native.yml b/.github/workflows/deploy-api-prod-native.yml index edd8ad7d..e9d322b9 100644 --- a/.github/workflows/deploy-api-prod-native.yml +++ b/.github/workflows/deploy-api-prod-native.yml @@ -13,3 +13,17 @@ jobs: secrets: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + + notify: + needs: deploy + runs-on: ubuntu-latest + steps: + - name: Slack Notify + uses: rtCamp/action-slack-notify@v2.3.3 + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + SLACK_CHANNEL: team-snutt-deploy + SLACK_TITLE: NEW RELEASE + SLACK_USERNAME: snutt-timetable + SLACK_ICON: https://user-images.githubusercontent.com/35535636/103177470-2237cb00-48be-11eb-9211-3ffa567c8ac3.png + SLACK_MESSAGE: Check for updated environment From 3adf143c8101e9d0053c7831427457b8138d72d9 Mon Sep 17 00:00:00 2001 From: Hankyeol Choi Date: Thu, 8 Jan 2026 19:33:55 +0900 Subject: [PATCH 3/5] =?UTF-8?q?jackson=20fail-on-null-for-primitives=20?= =?UTF-8?q?=EC=98=B5=EC=85=98=20=EB=B3=80=EA=B2=BD=20(#474)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/src/main/resources/application-common.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/resources/application-common.yml b/core/src/main/resources/application-common.yml index f3a85f87..60f38931 100644 --- a/core/src/main/resources/application-common.yml +++ b/core/src/main/resources/application-common.yml @@ -9,6 +9,9 @@ spring: enabled: false host: database: + jackson: + deserialization: + fail-on-null-for-primitives: false google: firebase: From 03dd773b757a4bb2c1c1b668f826c73926c5daf2 Mon Sep 17 00:00:00 2001 From: Hankyeol Choi Date: Thu, 8 Jan 2026 20:58:36 +0900 Subject: [PATCH 4/5] =?UTF-8?q?openapi=EC=97=90=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=ED=83=80=EC=9E=85(application/json)=20=EC=B6=94=EA=B0=80=20(#4?= =?UTF-8?q?76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/src/main/kotlin/controller/AdminController.kt | 7 ++++++- api/src/main/kotlin/controller/AuthController.kt | 7 ++++++- .../main/kotlin/controller/BookmarkController.kt | 7 ++++++- .../main/kotlin/controller/BuildingController.kt | 7 ++++++- api/src/main/kotlin/controller/ConfigController.kt | 7 ++++++- .../main/kotlin/controller/CoursebookController.kt | 7 ++++++- api/src/main/kotlin/controller/DeviceController.kt | 7 ++++++- api/src/main/kotlin/controller/DiaryController.kt | 7 ++++++- api/src/main/kotlin/controller/EvController.kt | 7 ++++++- .../main/kotlin/controller/EvServiceController.kt | 7 ++++++- .../main/kotlin/controller/FeedbackController.kt | 7 ++++++- api/src/main/kotlin/controller/FriendController.kt | 7 ++++++- .../main/kotlin/controller/FriendTableController.kt | 7 ++++++- .../kotlin/controller/LectureSearchController.kt | 7 ++++++- .../kotlin/controller/NotificationController.kt | 7 ++++++- api/src/main/kotlin/controller/PopupController.kt | 7 ++++++- .../kotlin/controller/PushPreferenceController.kt | 7 ++++++- .../main/kotlin/controller/SemesterController.kt | 7 ++++++- .../main/kotlin/controller/StaticPageController.kt | 6 +++++- api/src/main/kotlin/controller/TagController.kt | 7 ++++++- .../main/kotlin/controller/TimetableController.kt | 7 ++++++- .../kotlin/controller/TimetableLectureController.kt | 7 ++++++- .../TimetableLectureReminderController.kt | 7 ++++++- .../kotlin/controller/TimetableThemeController.kt | 7 ++++++- api/src/main/kotlin/controller/UserController.kt | 13 +++++++++++-- .../controller/VacancyNotificationController.kt | 7 ++++++- 26 files changed, 160 insertions(+), 27 deletions(-) diff --git a/api/src/main/kotlin/controller/AdminController.kt b/api/src/main/kotlin/controller/AdminController.kt index 389f3ce9..e5bb041d 100644 --- a/api/src/main/kotlin/controller/AdminController.kt +++ b/api/src/main/kotlin/controller/AdminController.kt @@ -18,6 +18,7 @@ import com.wafflestudio.snutt.popup.dto.PopupResponse import com.wafflestudio.snutt.popup.dto.PostPopupRequest import com.wafflestudio.snutt.popup.service.PopupService import notification.dto.InsertNotificationRequest +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PatchMapping @@ -30,7 +31,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttAdminApiFilterTarget -@RequestMapping("/v1/admin", "/admin") +@RequestMapping( + "/v1/admin", + "/admin", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class AdminController( private val notificationAdminService: NotificationAdminService, private val configService: ClientConfigService, diff --git a/api/src/main/kotlin/controller/AuthController.kt b/api/src/main/kotlin/controller/AuthController.kt index 6d9681dd..3457da54 100644 --- a/api/src/main/kotlin/controller/AuthController.kt +++ b/api/src/main/kotlin/controller/AuthController.kt @@ -15,6 +15,7 @@ import com.wafflestudio.snutt.users.dto.SendEmailRequest import com.wafflestudio.snutt.users.dto.SocialLoginRequest import com.wafflestudio.snutt.users.dto.VerificationCodeRequest import com.wafflestudio.snutt.users.service.UserService +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping @@ -22,7 +23,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttNoAuthApiFilterTarget -@RequestMapping("/v1/auth", "/auth") +@RequestMapping( + "/v1/auth", + "/auth", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class AuthController( private val userService: UserService, ) { diff --git a/api/src/main/kotlin/controller/BookmarkController.kt b/api/src/main/kotlin/controller/BookmarkController.kt index d35e6e38..3e238386 100644 --- a/api/src/main/kotlin/controller/BookmarkController.kt +++ b/api/src/main/kotlin/controller/BookmarkController.kt @@ -8,6 +8,7 @@ import com.wafflestudio.snutt.common.enums.Semester import com.wafflestudio.snutt.config.CurrentUser import com.wafflestudio.snutt.filter.SnuttDefaultApiFilterTarget import com.wafflestudio.snutt.users.data.User +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable @@ -19,7 +20,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/bookmarks", "/bookmarks") +@RequestMapping( + "/v1/bookmarks", + "/bookmarks", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class BookmarkController( private val bookmarkService: BookmarkService, ) { diff --git a/api/src/main/kotlin/controller/BuildingController.kt b/api/src/main/kotlin/controller/BuildingController.kt index c8fdf68a..22b96394 100644 --- a/api/src/main/kotlin/controller/BuildingController.kt +++ b/api/src/main/kotlin/controller/BuildingController.kt @@ -4,6 +4,7 @@ import com.wafflestudio.snutt.common.dto.ListResponse import com.wafflestudio.snutt.filter.SnuttNoAuthApiFilterTarget import com.wafflestudio.snutt.lecturebuildings.data.PlaceInfo import com.wafflestudio.snutt.lecturebuildings.service.LectureBuildingService +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam @@ -11,7 +12,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttNoAuthApiFilterTarget -@RequestMapping("/v1/buildings", "/buildings") +@RequestMapping( + "/v1/buildings", + "/buildings", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class BuildingController( private val lectureBuildingService: LectureBuildingService, ) { diff --git a/api/src/main/kotlin/controller/ConfigController.kt b/api/src/main/kotlin/controller/ConfigController.kt index 943a57e7..3cadb890 100644 --- a/api/src/main/kotlin/controller/ConfigController.kt +++ b/api/src/main/kotlin/controller/ConfigController.kt @@ -3,6 +3,7 @@ package com.wafflestudio.snutt.controller import com.wafflestudio.snutt.clientconfig.service.ClientConfigService import com.wafflestudio.snutt.common.client.ClientInfo import com.wafflestudio.snutt.filter.SnuttNoAuthApiFilterTarget +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestAttribute import org.springframework.web.bind.annotation.RequestMapping @@ -12,7 +13,11 @@ import tools.jackson.databind.ObjectMapper @RestController @SnuttNoAuthApiFilterTarget -@RequestMapping("/v1/configs", "/configs") +@RequestMapping( + "/v1/configs", + "/configs", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class ConfigController( private val configService: ClientConfigService, private val objectMapper: ObjectMapper, diff --git a/api/src/main/kotlin/controller/CoursebookController.kt b/api/src/main/kotlin/controller/CoursebookController.kt index a9688649..8a67b28e 100644 --- a/api/src/main/kotlin/controller/CoursebookController.kt +++ b/api/src/main/kotlin/controller/CoursebookController.kt @@ -7,6 +7,7 @@ import com.wafflestudio.snutt.coursebook.data.CoursebookOfficialResponse import com.wafflestudio.snutt.coursebook.data.CoursebookResponse import com.wafflestudio.snutt.coursebook.service.CoursebookService import com.wafflestudio.snutt.filter.SnuttNoAuthApiFilterTarget +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam @@ -14,7 +15,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttNoAuthApiFilterTarget -@RequestMapping("/v1/course_books", "/course_books") +@RequestMapping( + "/v1/course_books", + "/course_books", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class CoursebookController( private val coursebookService: CoursebookService, ) { diff --git a/api/src/main/kotlin/controller/DeviceController.kt b/api/src/main/kotlin/controller/DeviceController.kt index 40a357eb..13b745f1 100644 --- a/api/src/main/kotlin/controller/DeviceController.kt +++ b/api/src/main/kotlin/controller/DeviceController.kt @@ -7,6 +7,7 @@ import com.wafflestudio.snutt.config.CurrentUser import com.wafflestudio.snutt.filter.SnuttDefaultApiFilterTarget import com.wafflestudio.snutt.notification.service.DeviceService import com.wafflestudio.snutt.users.data.User +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping @@ -16,7 +17,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/user/device", "/user/device") +@RequestMapping( + "/v1/user/device", + "/user/device", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class DeviceController( private val deviceService: DeviceService, ) { diff --git a/api/src/main/kotlin/controller/DiaryController.kt b/api/src/main/kotlin/controller/DiaryController.kt index 19a9d277..ce2254ed 100644 --- a/api/src/main/kotlin/controller/DiaryController.kt +++ b/api/src/main/kotlin/controller/DiaryController.kt @@ -11,6 +11,7 @@ import com.wafflestudio.snutt.diary.dto.request.DiarySubmissionRequestDto import com.wafflestudio.snutt.diary.service.DiaryService import com.wafflestudio.snutt.filter.SnuttDefaultApiFilterTarget import com.wafflestudio.snutt.users.data.User +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable @@ -21,7 +22,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/diary", "/diary") +@RequestMapping( + "/v1/diary", + "/diary", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class DiaryController( private val diaryService: DiaryService, ) { diff --git a/api/src/main/kotlin/controller/EvController.kt b/api/src/main/kotlin/controller/EvController.kt index 7717946d..039a56eb 100644 --- a/api/src/main/kotlin/controller/EvController.kt +++ b/api/src/main/kotlin/controller/EvController.kt @@ -2,6 +2,7 @@ package com.wafflestudio.snutt.controller import com.wafflestudio.snutt.evaluation.service.EvService import com.wafflestudio.snutt.filter.SnuttNoAuthApiFilterTarget +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestMapping @@ -9,7 +10,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttNoAuthApiFilterTarget -@RequestMapping("/v1/ev", "/ev") +@RequestMapping( + "/v1/ev", + "/ev", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class EvController( private val evService: EvService, ) { diff --git a/api/src/main/kotlin/controller/EvServiceController.kt b/api/src/main/kotlin/controller/EvServiceController.kt index ce0795fc..3ed3c2d2 100644 --- a/api/src/main/kotlin/controller/EvServiceController.kt +++ b/api/src/main/kotlin/controller/EvServiceController.kt @@ -12,6 +12,7 @@ import com.wafflestudio.snutt.filter.SnuttDefaultApiFilterTarget import com.wafflestudio.snutt.users.data.User import org.springframework.aot.hint.annotation.RegisterReflectionForBinding import org.springframework.http.HttpMethod +import org.springframework.http.MediaType import org.springframework.util.MultiValueMap import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping @@ -33,7 +34,11 @@ import org.springframework.web.bind.annotation.RestController SnuttEvLectureSummaryDto::class, ListResponse::class, ) -@RequestMapping("/v1/ev-service", "/ev-service") +@RequestMapping( + "/v1/ev-service", + "/ev-service", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class EvServiceController( private val evService: EvService, ) { diff --git a/api/src/main/kotlin/controller/FeedbackController.kt b/api/src/main/kotlin/controller/FeedbackController.kt index ca213c66..5385335a 100644 --- a/api/src/main/kotlin/controller/FeedbackController.kt +++ b/api/src/main/kotlin/controller/FeedbackController.kt @@ -6,6 +6,7 @@ import com.wafflestudio.snutt.common.dto.OkResponse import com.wafflestudio.snutt.feedback.dto.FeedbackPostRequestDto import com.wafflestudio.snutt.feedback.service.FeedbackService import com.wafflestudio.snutt.filter.SnuttNoAuthApiFilterTarget +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestAttribute import org.springframework.web.bind.annotation.RequestBody @@ -14,7 +15,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttNoAuthApiFilterTarget -@RequestMapping("/v1/feedback", "/feedback") +@RequestMapping( + "/v1/feedback", + "/feedback", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class FeedbackController( private val feedbackService: FeedbackService, ) { diff --git a/api/src/main/kotlin/controller/FriendController.kt b/api/src/main/kotlin/controller/FriendController.kt index 93e64292..d8b9b3db 100644 --- a/api/src/main/kotlin/controller/FriendController.kt +++ b/api/src/main/kotlin/controller/FriendController.kt @@ -11,6 +11,7 @@ import com.wafflestudio.snutt.friend.dto.UpdateFriendDisplayNameRequest import com.wafflestudio.snutt.friend.service.FriendService import com.wafflestudio.snutt.users.data.User import com.wafflestudio.snutt.users.service.UserNicknameService +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PatchMapping @@ -23,7 +24,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/friends", "/friends") +@RequestMapping( + "/v1/friends", + "/friends", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class FriendController( private val friendService: FriendService, private val userNicknameService: UserNicknameService, diff --git a/api/src/main/kotlin/controller/FriendTableController.kt b/api/src/main/kotlin/controller/FriendTableController.kt index 478da168..4e95e471 100644 --- a/api/src/main/kotlin/controller/FriendTableController.kt +++ b/api/src/main/kotlin/controller/FriendTableController.kt @@ -9,6 +9,7 @@ import com.wafflestudio.snutt.friend.service.FriendService import com.wafflestudio.snutt.timetables.dto.TimetableDto import com.wafflestudio.snutt.timetables.service.TimetableService import com.wafflestudio.snutt.users.data.User +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestMapping @@ -17,7 +18,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/friends/{friendId}", "/friends/{friendId}") +@RequestMapping( + "/v1/friends/{friendId}", + "/friends/{friendId}", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class FriendTableController( private val friendService: FriendService, private val timetableService: TimetableService, diff --git a/api/src/main/kotlin/controller/LectureSearchController.kt b/api/src/main/kotlin/controller/LectureSearchController.kt index bce05569..830a7ac2 100644 --- a/api/src/main/kotlin/controller/LectureSearchController.kt +++ b/api/src/main/kotlin/controller/LectureSearchController.kt @@ -7,6 +7,7 @@ import com.wafflestudio.snutt.lectures.dto.SearchDto import com.wafflestudio.snutt.lectures.dto.SearchTimeDto import com.wafflestudio.snutt.lectures.service.LectureService import kotlinx.coroutines.flow.toList +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping @@ -14,7 +15,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttNoAuthApiFilterTarget -@RequestMapping("/v1/search_query", "/search_query") +@RequestMapping( + "/v1/search_query", + "/search_query", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class LectureSearchController( private val lectureService: LectureService, ) { diff --git a/api/src/main/kotlin/controller/NotificationController.kt b/api/src/main/kotlin/controller/NotificationController.kt index ca04426d..6349a388 100644 --- a/api/src/main/kotlin/controller/NotificationController.kt +++ b/api/src/main/kotlin/controller/NotificationController.kt @@ -7,6 +7,7 @@ import com.wafflestudio.snutt.notification.dto.NotificationQuery import com.wafflestudio.snutt.notification.dto.NotificationResponse import com.wafflestudio.snutt.notification.service.NotificationService import com.wafflestudio.snutt.users.data.User +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam @@ -14,7 +15,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/notification", "/notification") +@RequestMapping( + "/v1/notification", + "/notification", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class NotificationController( private val notificationService: NotificationService, ) { diff --git a/api/src/main/kotlin/controller/PopupController.kt b/api/src/main/kotlin/controller/PopupController.kt index 4c75c06f..85b082d0 100644 --- a/api/src/main/kotlin/controller/PopupController.kt +++ b/api/src/main/kotlin/controller/PopupController.kt @@ -5,6 +5,7 @@ import com.wafflestudio.snutt.common.dto.ListResponse import com.wafflestudio.snutt.filter.SnuttNoAuthApiFilterTarget import com.wafflestudio.snutt.popup.dto.PopupResponse import com.wafflestudio.snutt.popup.service.PopupService +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestAttribute import org.springframework.web.bind.annotation.RequestMapping @@ -12,7 +13,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttNoAuthApiFilterTarget -@RequestMapping("/v1/popups", "/popups") +@RequestMapping( + "/v1/popups", + "/popups", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class PopupController( private val popupService: PopupService, ) { diff --git a/api/src/main/kotlin/controller/PushPreferenceController.kt b/api/src/main/kotlin/controller/PushPreferenceController.kt index 80d2e3b4..85c0b3d8 100644 --- a/api/src/main/kotlin/controller/PushPreferenceController.kt +++ b/api/src/main/kotlin/controller/PushPreferenceController.kt @@ -5,6 +5,7 @@ import com.wafflestudio.snutt.filter.SnuttDefaultApiFilterTarget import com.wafflestudio.snutt.notification.dto.PushPreferenceDto import com.wafflestudio.snutt.notification.service.PushPreferenceService import com.wafflestudio.snutt.users.data.User +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody @@ -13,7 +14,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/push/preferences", "/push/preferences") +@RequestMapping( + "/v1/push/preferences", + "/push/preferences", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class PushPreferenceController( private val pushPreferenceService: PushPreferenceService, ) { diff --git a/api/src/main/kotlin/controller/SemesterController.kt b/api/src/main/kotlin/controller/SemesterController.kt index 1cc97b95..20b24594 100644 --- a/api/src/main/kotlin/controller/SemesterController.kt +++ b/api/src/main/kotlin/controller/SemesterController.kt @@ -3,6 +3,7 @@ package com.wafflestudio.snutt.controller import com.wafflestudio.snutt.filter.SnuttDefaultApiFilterTarget import com.wafflestudio.snutt.semester.dto.GetSemesterStatusResponse import com.wafflestudio.snutt.semester.service.SemesterService +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @@ -10,7 +11,11 @@ import java.time.Instant @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/semesters", "/semesters") +@RequestMapping( + "/v1/semesters", + "/semesters", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class SemesterController( private val semesterService: SemesterService, ) { diff --git a/api/src/main/kotlin/controller/StaticPageController.kt b/api/src/main/kotlin/controller/StaticPageController.kt index fbc07ce8..c0625e6c 100644 --- a/api/src/main/kotlin/controller/StaticPageController.kt +++ b/api/src/main/kotlin/controller/StaticPageController.kt @@ -3,6 +3,7 @@ package com.wafflestudio.snutt.controller import org.springframework.core.io.Resource import org.springframework.core.io.ResourceLoader import org.springframework.http.CacheControl +import org.springframework.http.MediaType import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping @@ -10,7 +11,10 @@ import org.springframework.web.bind.annotation.RestController import java.time.Duration @RestController -@RequestMapping("") +@RequestMapping( + "", + produces = [MediaType.TEXT_HTML_VALUE], +) class StaticPageController( private val resourceLoader: ResourceLoader, ) { diff --git a/api/src/main/kotlin/controller/TagController.kt b/api/src/main/kotlin/controller/TagController.kt index 0a965bc5..0c93cdb9 100644 --- a/api/src/main/kotlin/controller/TagController.kt +++ b/api/src/main/kotlin/controller/TagController.kt @@ -5,6 +5,7 @@ import com.wafflestudio.snutt.filter.SnuttDefaultApiFilterTarget import com.wafflestudio.snutt.tag.TagListService import com.wafflestudio.snutt.tag.data.TagListResponse import com.wafflestudio.snutt.tag.data.TagListUpdateTimeResponse +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestMapping @@ -12,7 +13,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/tags", "/tags") +@RequestMapping( + "/v1/tags", + "/tags", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class TagController( private val tagService: TagListService, ) { diff --git a/api/src/main/kotlin/controller/TimetableController.kt b/api/src/main/kotlin/controller/TimetableController.kt index 3856cf11..4c9d7872 100644 --- a/api/src/main/kotlin/controller/TimetableController.kt +++ b/api/src/main/kotlin/controller/TimetableController.kt @@ -9,6 +9,7 @@ import com.wafflestudio.snutt.timetables.dto.request.TimetableModifyRequestDto import com.wafflestudio.snutt.timetables.service.TimetableService import com.wafflestudio.snutt.users.data.User import kotlinx.coroutines.flow.toList +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable @@ -22,7 +23,11 @@ import timetables.dto.TimetableBriefDto @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/tables", "/tables") +@RequestMapping( + "/v1/tables", + "/tables", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class TimetableController( private val timetableService: TimetableService, ) { diff --git a/api/src/main/kotlin/controller/TimetableLectureController.kt b/api/src/main/kotlin/controller/TimetableLectureController.kt index aafb1fee..48daa0d5 100644 --- a/api/src/main/kotlin/controller/TimetableLectureController.kt +++ b/api/src/main/kotlin/controller/TimetableLectureController.kt @@ -8,6 +8,7 @@ import com.wafflestudio.snutt.timetables.dto.request.TimetableLectureModifyLegac import com.wafflestudio.snutt.timetables.service.TimetableLectureService import com.wafflestudio.snutt.timetables.service.TimetableService import com.wafflestudio.snutt.users.data.User +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping @@ -19,7 +20,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/tables/{timetableId}/lecture", "/tables/{timetableId}/lecture") +@RequestMapping( + "/v1/tables/{timetableId}/lecture", + "/tables/{timetableId}/lecture", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class TimetableLectureController( private val timetableLectureService: TimetableLectureService, private val timetableService: TimetableService, diff --git a/api/src/main/kotlin/controller/TimetableLectureReminderController.kt b/api/src/main/kotlin/controller/TimetableLectureReminderController.kt index b3831261..cfa3c5db 100644 --- a/api/src/main/kotlin/controller/TimetableLectureReminderController.kt +++ b/api/src/main/kotlin/controller/TimetableLectureReminderController.kt @@ -4,6 +4,7 @@ import com.wafflestudio.snutt.filter.SnuttDefaultApiFilterTarget import com.wafflestudio.snutt.timetablelecturereminder.dto.TimetableLectureReminderDto import com.wafflestudio.snutt.timetablelecturereminder.dto.request.TimetableLectureReminderModifyRequestDto import com.wafflestudio.snutt.timetablelecturereminder.service.TimetableLectureReminderService +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PutMapping @@ -13,7 +14,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/tables/{timetableId}", "/tables/{timetableId}") +@RequestMapping( + "/v1/tables/{timetableId}", + "/tables/{timetableId}", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class TimetableLectureReminderController( private val timetableLectureReminderService: TimetableLectureReminderService, ) { diff --git a/api/src/main/kotlin/controller/TimetableThemeController.kt b/api/src/main/kotlin/controller/TimetableThemeController.kt index 249ce34a..423b2eeb 100644 --- a/api/src/main/kotlin/controller/TimetableThemeController.kt +++ b/api/src/main/kotlin/controller/TimetableThemeController.kt @@ -13,6 +13,7 @@ import com.wafflestudio.snutt.theme.dto.request.TimetableThemeModifyRequestDto import com.wafflestudio.snutt.theme.dto.request.TimetableThemePublishRequestDto import com.wafflestudio.snutt.theme.service.TimetableThemeService import com.wafflestudio.snutt.users.data.User +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PatchMapping @@ -25,7 +26,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/themes", "/themes") +@RequestMapping( + "/v1/themes", + "/themes", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class TimetableThemeController( private val timetableThemeService: TimetableThemeService, ) { diff --git a/api/src/main/kotlin/controller/UserController.kt b/api/src/main/kotlin/controller/UserController.kt index 0a603bac..ba02a7e6 100644 --- a/api/src/main/kotlin/controller/UserController.kt +++ b/api/src/main/kotlin/controller/UserController.kt @@ -18,6 +18,7 @@ import com.wafflestudio.snutt.users.dto.UserPatchRequest import com.wafflestudio.snutt.users.dto.VerificationCodeRequest import com.wafflestudio.snutt.users.service.UserNicknameService import com.wafflestudio.snutt.users.service.UserService +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PatchMapping @@ -29,7 +30,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/user", "/user") +@RequestMapping( + "/v1/user", + "/user", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class UserController( private val userService: UserService, ) { @@ -144,7 +149,11 @@ class UserController( @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/users", "/users") +@RequestMapping( + "/v1/users", + "/users", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class UsersController( private val userService: UserService, private val userNicknameService: UserNicknameService, diff --git a/api/src/main/kotlin/controller/VacancyNotificationController.kt b/api/src/main/kotlin/controller/VacancyNotificationController.kt index 531b6f40..1b246f32 100644 --- a/api/src/main/kotlin/controller/VacancyNotificationController.kt +++ b/api/src/main/kotlin/controller/VacancyNotificationController.kt @@ -7,6 +7,7 @@ import com.wafflestudio.snutt.lectures.service.LectureService import com.wafflestudio.snutt.users.data.User import com.wafflestudio.snutt.vacancynotification.dto.VacancyNotificationLecturesResponse import com.wafflestudio.snutt.vacancynotification.service.VacancyNotificationService +import org.springframework.http.MediaType import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable @@ -16,7 +17,11 @@ import org.springframework.web.bind.annotation.RestController @RestController @SnuttDefaultApiFilterTarget -@RequestMapping("/v1/vacancy-notifications", "/vacancy-notifications") +@RequestMapping( + "/v1/vacancy-notifications", + "/vacancy-notifications", + produces = [MediaType.APPLICATION_JSON_VALUE], +) class VacancyNotificationController( private val vacancyNotificationService: VacancyNotificationService, private val lectureService: LectureService, From bbd58e5359b47b18639b16dcac6e01388df9f9c6 Mon Sep 17 00:00:00 2001 From: Hankyeol Choi Date: Fri, 9 Jan 2026 16:57:48 +0900 Subject: [PATCH 5/5] =?UTF-8?q?=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=ED=83=80=EC=9E=85=20=EA=B5=AC=EC=B2=B4?= =?UTF-8?q?=ED=99=94=20(#477)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/controller/BuildingController.kt | 3 ++- .../controller/TimetableThemeController.kt | 20 +++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/api/src/main/kotlin/controller/BuildingController.kt b/api/src/main/kotlin/controller/BuildingController.kt index 22b96394..cdaeb9b7 100644 --- a/api/src/main/kotlin/controller/BuildingController.kt +++ b/api/src/main/kotlin/controller/BuildingController.kt @@ -2,6 +2,7 @@ package com.wafflestudio.snutt.controller import com.wafflestudio.snutt.common.dto.ListResponse import com.wafflestudio.snutt.filter.SnuttNoAuthApiFilterTarget +import com.wafflestudio.snutt.lecturebuildings.data.LectureBuilding import com.wafflestudio.snutt.lecturebuildings.data.PlaceInfo import com.wafflestudio.snutt.lecturebuildings.service.LectureBuildingService import org.springframework.http.MediaType @@ -23,7 +24,7 @@ class BuildingController( @GetMapping("") suspend fun searchBuildings( @RequestParam places: String, - ): ListResponse<*> { + ): ListResponse { val placesQuery = places .split(",") diff --git a/api/src/main/kotlin/controller/TimetableThemeController.kt b/api/src/main/kotlin/controller/TimetableThemeController.kt index 423b2eeb..1c29cc2b 100644 --- a/api/src/main/kotlin/controller/TimetableThemeController.kt +++ b/api/src/main/kotlin/controller/TimetableThemeController.kt @@ -42,7 +42,7 @@ class TimetableThemeController( @GetMapping("/best") suspend fun getBestThemes( @RequestParam page: Int, - ): ListResponse<*> { + ): ListResponse { val themes = timetableThemeService.getBestThemes(page) val result = timetableThemeService.convertThemesToTimetableDtos(themes) return ListResponse(result) @@ -52,7 +52,7 @@ class TimetableThemeController( suspend fun getFriendsThemes( @CurrentUser user: User, @RequestParam page: Int, - ): ListResponse<*> { + ): ListResponse { val themes = timetableThemeService.getFriendsThemes(user.id!!, page) val result = timetableThemeService.convertThemesToTimetableDtos(themes) return ListResponse(result) @@ -62,20 +62,20 @@ class TimetableThemeController( suspend fun getTheme( @CurrentUser user: User, @PathVariable themeId: String, - ) = TimetableThemeDto(timetableThemeService.getTheme(user.id!!, themeId)) + ): TimetableThemeDto = TimetableThemeDto(timetableThemeService.getTheme(user.id!!, themeId)) @PostMapping("") suspend fun addTheme( @CurrentUser user: User, @RequestBody body: TimetableThemeAddRequestDto, - ) = timetableThemeService.addTheme(user.id!!, body.name, body.colors).let(::TimetableThemeDto) + ): TimetableThemeDto = timetableThemeService.addTheme(user.id!!, body.name, body.colors).let(::TimetableThemeDto) @PatchMapping("/{themeId}") suspend fun modifyTheme( @CurrentUser user: User, @PathVariable themeId: String, @RequestBody body: TimetableThemeModifyRequestDto, - ) = timetableThemeService.modifyTheme(user.id!!, themeId, body.name, body.colors).let(::TimetableThemeDto) + ): TimetableThemeDto = timetableThemeService.modifyTheme(user.id!!, themeId, body.name, body.colors).let(::TimetableThemeDto) @PostMapping("/{themeId}/publish") suspend fun publishTheme( @@ -92,12 +92,12 @@ class TimetableThemeController( @CurrentUser user: User, @PathVariable themeId: String, @RequestBody body: TimetableThemeDownloadRequestDto, - ) = TimetableThemeDto(timetableThemeService.downloadTheme(user.id!!, themeId, body.name)) + ): TimetableThemeDto = TimetableThemeDto(timetableThemeService.downloadTheme(user.id!!, themeId, body.name)) @PostMapping("/search") suspend fun searchThemes( @RequestParam query: String, - ): ListResponse<*> { + ): ListResponse { val themes = timetableThemeService.searchThemes(query) val result = timetableThemeService.convertThemesToTimetableDtos(themes) return ListResponse(result) @@ -119,13 +119,13 @@ class TimetableThemeController( suspend fun copyTheme( @CurrentUser user: User, @PathVariable themeId: String, - ) = timetableThemeService.copyTheme(user.id!!, themeId).let(::TimetableThemeDto) + ): TimetableThemeDto = timetableThemeService.copyTheme(user.id!!, themeId).let(::TimetableThemeDto) @PostMapping("/{themeId}/default") suspend fun setDefault( @CurrentUser user: User, @PathVariable themeId: String, - ) = timetableThemeService.setDefault(user.id!!, themeId).let(::TimetableThemeDto) + ): TimetableThemeDto = timetableThemeService.setDefault(user.id!!, themeId).let(::TimetableThemeDto) @PostMapping("/basic/{basicThemeTypeValue}/default") suspend fun setBasicThemeTypeDefault( @@ -142,7 +142,7 @@ class TimetableThemeController( suspend fun unsetDefault( @CurrentUser user: User, @PathVariable themeId: String, - ) = timetableThemeService.unsetDefault(user.id!!, themeId).let(::TimetableThemeDto) + ): TimetableThemeDto = timetableThemeService.unsetDefault(user.id!!, themeId).let(::TimetableThemeDto) @DeleteMapping("/basic/{basicThemeTypeValue}/default") suspend fun unsetBasicThemeTypeDefault(