diff --git a/.github/workflows/prod-server.yml b/.github/workflows/prod-server.yml index d4062802..79a5f3bb 100644 --- a/.github/workflows/prod-server.yml +++ b/.github/workflows/prod-server.yml @@ -25,7 +25,7 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Deploy to Test Environment + - name: Deploy to Production Environment uses: appleboy/ssh-action@v0.1.7 with: host: ${{ secrets.PROD_SERVER_HOST }} @@ -39,6 +39,58 @@ jobs: # 최신 이미지 pull docker pull ghcr.io/${{ github.repository }}/zoopzoop:latest + # .env 파일 생성 + cat << EOF > .env + # DB + SPRING_DATASOURCE_URL=${{secrets.PROD_DB_URL}} + SPRING_DATASOURCE_USERNAME=${{secrets.PROD_DB_USERNAME}} + SPRING_DATASOURCE_PASSWORD=${{secrets.PROD_DB_PASSWORD}} + + # RabbitMQ + SPRING_RABBITMQ_HOST=${{secrets.PROD_RABBITMQ_HOST}} + SPRING_RABBITMQ_USERNAME=${{secrets.PROD_RABBITMQ_USERNAME}} + SPRING_RABBITMQ_PASSWORD=${{secrets.PROD_RABBITMQ_PASSWORD}} + + # Kakao OAuth + OAUTH_KAKAO_CLIENT_ID=${{secrets.OAUTH_KAKAO_CLIENT_ID}} + OAUTH_KAKAO_CLIENT_SECRET=${{secrets.OAUTH_KAKAO_CLIENT_SECRET}} + OAUTH_KAKAO_REDIRECT_URI=${{secrets.PROD_OAUTH_KAKAO_REDIRECT_URI}} + + # Google OAuth + OAUTH_GOOGLE_CLIENT_ID=${{secrets.OAUTH_GOOGLE_CLIENT_ID}} + OAUTH_GOOGLE_CLIENT_SECRET=${{secrets.OAUTH_GOOGLE_CLIENT_SECRET}} + OAUTH_GOOGLE_REDIRECT_URI=${{secrets.PROD_OAUTH_GOOGLE_REDIRECT_URI}} + + # AWS + AWS_ACCESS_KEY_ID=${{secrets.AWS_ACCESS_KEY_ID}} + AWS_SECRET_ACCESS_KEY=${{secrets.AWS_SECRET_ACCESS_KEY}} + AWS_REGION=${{secrets.AWS_REGION}} + AWS_S3_BUCKET_NAME=${{secrets.AWS_S3_BUCKET_NAME}} + + # Redis + REDIS_HOST=${{secrets.REDIS_HOST}} + REDIS_PORT=${{secrets.REDIS_PORT}} + REDIS_PASSWORD=${{secrets.REDIS_PASSWORD}} + + # Naver API + NAVER_CLIENT_ID=${{secrets.NAVER_CLIENT_ID}} + NAVER_CLIENT_SECRET=${{secrets.NAVER_CLIENT_SECRET}} + + # JWT + JWT_SECRET_KEY=${{secrets.JWT_SECRET_KEY}} + JWT_ACCESS_TOKEN_VALIDITY=${{secrets.JWT_ACCESS_TOKEN_VALIDITY}} + JWT_REFRESH_TOKEN_VALIDITY=${{secrets.JWT_REFRESH_TOKEN_VALIDITY}} + + # Frontend Redirect + FRONT_REDIRECT_DOMAIN=${{secrets.FRONT_REDIRECT_DOMAIN}} + + # Liveblocks + LIVEBLOCKS_SECRET_KEY=${{secrets.LIVEBLOCKS_SECRET_KEY}} + + # Sentry + SENTRY_DSN=${{secrets.SENTRY_DSN}} + EOF + # NPM API 설정 NPM_HOST="localhost:81" NPM_EMAIL="${{secrets.NPM_ADMIN_EMAIL}}" @@ -77,17 +129,15 @@ jobs: NEW_PORT=8081 fi + # 스크립트 종료 시 .env 파일 자동 삭제 + trap 'rm -f .env' EXIT + echo "Starting new container: $NEW_CONTAINER on port $NEW_PORT" docker run -d --restart unless-stopped \ -p $NEW_PORT:8080 \ --name $NEW_CONTAINER \ --network common \ - -e SPRING_DATASOURCE_URL="${{secrets.PROD_DB_URL}}" \ - -e SPRING_DATASOURCE_USERNAME="${{secrets.PROD_DB_USERNAME}}" \ - -e SPRING_DATASOURCE_PASSWORD="${{secrets.PROD_DB_PASSWORD}}" \ - -e SPRING_RABBITMQ_HOST="${{secrets.PROD_RABBITMQ_HOST}}" \ - -e SPRING_RABBITMQ_USERNAME="${{secrets.PROD_RABBITMQ_USERNAME}}" \ - -e SPRING_RABBITMQ_PASSWORD="${{secrets.PROD_RABBITMQ_PASSWORD}}" \ + --env-file .env \ ghcr.io/${{ github.repository }}/zoopzoop:latest diff --git a/.github/workflows/test-server-cd.yml b/.github/workflows/test-server-cd.yml index 4162dd05..d0626d00 100644 --- a/.github/workflows/test-server-cd.yml +++ b/.github/workflows/test-server-cd.yml @@ -24,15 +24,69 @@ jobs: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.repository_owner }}" --password-stdin docker pull ghcr.io/${{ github.repository }}/zoopzoop:latest + # .env 파일 생성 + cat << EOF > .env + # DB + SPRING_DATASOURCE_URL=${{secrets.TEST_DB_URL}} + SPRING_DATASOURCE_USERNAME=${{secrets.TEST_DB_USERNAME}} + SPRING_DATASOURCE_PASSWORD=${{secrets.TEST_DB_PASSWORD}} + + # RabbitMQ + SPRING_RABBITMQ_HOST=${{secrets.TEST_RABBITMQ_HOST}} + SPRING_RABBITMQ_USERNAME=${{secrets.TEST_RABBITMQ_USERNAME}} + SPRING_RABBITMQ_PASSWORD=${{secrets.TEST_RABBITMQ_PASSWORD}} + + # Kakao OAuth + OAUTH_KAKAO_CLIENT_ID=${{secrets.OAUTH_KAKAO_CLIENT_ID}} + OAUTH_KAKAO_CLIENT_SECRET=${{secrets.OAUTH_KAKAO_CLIENT_SECRET}} + OAUTH_KAKAO_REDIRECT_URI=${{secrets.TEST_OAUTH_KAKAO_REDIRECT_URI}} + + # Google OAuth + OAUTH_GOOGLE_CLIENT_ID=${{secrets.OAUTH_GOOGLE_CLIENT_ID}} + OAUTH_GOOGLE_CLIENT_SECRET=${{secrets.OAUTH_GOOGLE_CLIENT_SECRET}} + OAUTH_GOOGLE_REDIRECT_URI=${{secrets.TEST_OAUTH_GOOGLE_REDIRECT_URI}} + + # AWS + AWS_ACCESS_KEY_ID=${{secrets.AWS_ACCESS_KEY_ID}} + AWS_SECRET_ACCESS_KEY=${{secrets.AWS_SECRET_ACCESS_KEY}} + AWS_REGION=${{secrets.AWS_REGION}} + AWS_S3_BUCKET_NAME=${{secrets.AWS_S3_BUCKET_NAME}} + + # Redis + REDIS_HOST=${{secrets.REDIS_HOST}} + REDIS_PORT=${{secrets.REDIS_PORT}} + REDIS_PASSWORD=${{secrets.REDIS_PASSWORD}} + + # Naver API + NAVER_CLIENT_ID=${{secrets.NAVER_CLIENT_ID}} + NAVER_CLIENT_SECRET=${{secrets.NAVER_CLIENT_SECRET}} + + # JWT + JWT_SECRET_KEY=${{secrets.JWT_SECRET_KEY}} + JWT_ACCESS_TOKEN_VALIDITY=${{secrets.JWT_ACCESS_TOKEN_VALIDITY}} + JWT_REFRESH_TOKEN_VALIDITY=${{secrets.JWT_REFRESH_TOKEN_VALIDITY}} + + # Frontend Redirect + FRONT_REDIRECT_DOMAIN=${{secrets.FRONT_REDIRECT_DOMAIN}} + + # Liveblocks + LIVEBLOCKS_SECRET_KEY=${{secrets.LIVEBLOCKS_SECRET_KEY}} + + # Sentry + SENTRY_DSN=${{secrets.SENTRY_DSN}} + EOF + + # NPM API 설정 NPM_HOST="localhost:81" - NPM_EMAIL="${{ secrets.NPM_ADMIN_EMAIL }}" - NPM_PASSWORD="${{ secrets.NPM_ADMIN_PASSWORD }}" - PROXY_HOST_ID="${{ secrets.NPM_PROXY_HOST_ID }}" - + NPM_EMAIL="${{secrets.NPM_ADMIN_EMAIL}}" + NPM_PASSWORD="${{secrets.NPM_ADMIN_PASSWORD}}" + PROXY_HOST_ID="${{secrets.NPM_PROXY_HOST_ID}}" + # NPM 토큰 TOKEN=$(curl -s -X POST "http://${NPM_HOST}/api/tokens" \ -H "Content-Type: application/json" \ -d "{\"identity\":\"${NPM_EMAIL}\",\"secret\":\"${NPM_PASSWORD}\"}" | jq -r '.token') + if [ -z "$TOKEN" ] || [ "$TOKEN" == "null" ]; then echo "❌ Failed to get NPM API token" exit 1 @@ -54,17 +108,16 @@ jobs: OLD_CONTAINER=zoopzoop-green NEW_PORT=8081 fi + + # 스크립트 종료 시 .env 파일 자동 삭제 + trap 'rm -f .env' EXIT + # 새 컨테이너 실행 (.env 사용) docker run -d --restart unless-stopped \ -p $NEW_PORT:8080 \ --name $NEW_CONTAINER \ --network common \ - -e SPRING_DATASOURCE_URL="${{secrets.TEST_DB_URL}}" \ - -e SPRING_DATASOURCE_USERNAME="${{secrets.TEST_DB_USERNAME}}" \ - -e SPRING_DATASOURCE_PASSWORD="${{secrets.TEST_DB_PASSWORD}}" \ - -e SPRING_RABBITMQ_HOST="${{secrets.TEST_RABBITMQ_HOST}}" \ - -e SPRING_RABBITMQ_USERNAME="${{secrets.TEST_RABBITMQ_USERNAME}}" \ - -e SPRING_RABBITMQ_PASSWORD="${{secrets.TEST_RABBITMQ_PASSWORD}}" \ + --env-file .env \ ghcr.io/${{ github.repository }}/zoopzoop:latest # 헬스체크 diff --git a/.github/workflows/test-server-ci.yml b/.github/workflows/test-server-ci.yml index 14655ca7..03e5bd59 100644 --- a/.github/workflows/test-server-ci.yml +++ b/.github/workflows/test-server-ci.yml @@ -1,7 +1,6 @@ # 워크플로우 이름 name: Spring CI/CD Pipeline (Develop) -# develop 브랜치 PR에서만 실행 on: pull_request: branches: @@ -15,22 +14,16 @@ on: - '.github/workflows/**' jobs: - # ================================== - # CI: Test and Build and Push Docker Image - # ================================== ci: runs-on: ubuntu-latest env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - - # CI 작업이 실행되는 동안 RabbitMQ 서비스 컨테이너를 함께 실행 services: rabbitmq: image: rabbitmq:3-management ports: - 5672:5672 - # RabbitMQ가 완전히 준비될 때까지 기다리는 상태 확인 옵션 options: >- --health-cmd "rabbitmq-diagnostics check_running" --health-interval 10s @@ -38,18 +31,15 @@ jobs: --health-retries 5 steps: - # 1. 소스 코드 체크아웃 - name: Checkout source code uses: actions/checkout@v4 - # 2. JDK 21 설치 - name: Set up JDK 21 uses: actions/setup-java@v4 with: java-version: '21' distribution: 'temurin' - # 3. Gradle 캐시 설정 - name: Cache Gradle packages uses: actions/cache@v4 with: @@ -60,43 +50,43 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - # 4. gradlew 실행 권한 부여 - name: Grant execute permission for gradlew run: chmod +x ./gradlew - # 5. application-secrets.yml 생성 - - name: Generate application-secrets.yml - run: | - mkdir -p src/main/resources - echo "${{ secrets.APPLICATION_SECRET_YML }}" > src/main/resources/application-secrets.yml - echo "OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}" >> src/main/resources/application-secrets.yml - - echo "spring.cloud.aws.region.static: ${{ secrets.AWS_REGION }}" >> src/main/resources/application-secrets.yml - echo "spring.cloud.aws.credentials.access-key: ${{ secrets.AWS_ACCESS_KEY_ID }}" >> src/main/resources/application-secrets.yml - echo "spring.cloud.aws.credentials.secret-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}" >> src/main/resources/application-secrets.yml - echo "spring.cloud.aws.s3.bucket: ${{ secrets.AWS_S3_BUCKET_NAME }}" >> src/main/resources/application-secrets.yml - echo "spring.cloud.aws.stack.auto: false" >> src/main/resources/application-secrets.yml - - echo "liveblocks.secret-key: ${{ secrets.LIVEBLOCKS_SECRET_KEY }}" >> src/main/resources/application-secrets.yml - - # 6. application-secrets-server.yml 생성 - - name: Generate application-secrets-server.yml + # ---------------------------------- + # 1. 테스트용 임시 디렉토리 생성 + secrets yml 생성 + # ---------------------------------- + - name: Create temp directory for secrets run: | - mkdir -p src/main/resources - echo "${{ secrets.APPLICATION_SECRET_SERVER_YML }}" > src/main/resources/application-secrets-server.yml - echo "OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}" >> src/main/resources/application-secrets-server.yml - - # 7. Gradle 테스트 실행 + TMP_DIR=$(mktemp -d) + # application-secrets.yml + echo "${{ secrets.APPLICATION_SECRET_YML }}" > $TMP_DIR/application-secrets.yml + echo "OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}" >> $TMP_DIR/application-secrets.yml + echo "spring.cloud.aws.region.static: ${{ secrets.AWS_REGION }}" >> $TMP_DIR/application-secrets.yml + echo "spring.cloud.aws.credentials.access-key: ${{ secrets.AWS_ACCESS_KEY_ID }}" >> $TMP_DIR/application-secrets.yml + echo "spring.cloud.aws.credentials.secret-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}" >> $TMP_DIR/application-secrets.yml + echo "spring.cloud.aws.s3.bucket: ${{ secrets.AWS_S3_BUCKET_NAME }}" >> $TMP_DIR/application-secrets.yml + echo "spring.cloud.aws.stack.auto: false" >> $TMP_DIR/application-secrets.yml + echo "liveblocks.secret-key: ${{ secrets.LIVEBLOCKS_SECRET_KEY }}" >> $TMP_DIR/application-secrets.yml + # application-secrets-server.yml + echo "${{ secrets.APPLICATION_SECRET_SERVER_YML }}" > $TMP_DIR/application-secrets-server.yml + echo "OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}" >> $TMP_DIR/application-secrets-server.yml + echo "TMP_DIR=$TMP_DIR" >> $GITHUB_ENV + + # ---------------------------------- + # 2. Gradle 테스트 실행 + # ---------------------------------- - name: Test with Gradle - # 테스트 단계에서 RabbitMQ 연결을 위한 환경 변수 설정 env: SPRING_RABBITMQ_HOST: localhost SPRING_RABBITMQ_PORT: 5672 SPRING_RABBITMQ_USERNAME: guest SPRING_RABBITMQ_PASSWORD: guest + SPRING_CONFIG_ADDITIONAL_LOCATION: "file://$TMP_DIR/application-secrets.yml,file://$TMP_DIR/application-secrets-server.yml" run: ./gradlew test - - # 8. 테스트 결과 요약 출력 + # ---------------------------------- + # 3. 테스트 결과 요약 + # ---------------------------------- - name: Show test results run: | echo "==== Test Results ====" @@ -113,20 +103,18 @@ jobs: echo "No test results found." fi - # 8-1. S3 자격 증명 제거 (빌드 전에만) - - name: Remove only S3 credentials before building - run: | - CONFIG_FILE="src/main/resources/application-secrets.yml" - sed -i '/spring.cloud.aws.credentials.access-key/d' $CONFIG_FILE - sed -i '/spring.cloud.aws.credentials.secret-key/d' $CONFIG_FILE - sed -i '/spring.cloud.aws.region.static/d' $CONFIG_FILE - sed -i '/spring.cloud.aws.stack.auto/d' $CONFIG_FILE + # ---------------------------------- + # 4. 테스트용 secrets 임시 디렉토리 삭제 + # ---------------------------------- + - name: Remove temp secrets directory + run: rm -rf ${TMP_DIR} - # 9. Gradle 빌드 실행 (테스트 성공 시) + # ---------------------------------- + # 5. Gradle 빌드 (테스트 제외) + # ---------------------------------- - name: Build with Gradle run: ./gradlew build -x test - # 10. GHCR 로그인 - name: Log in to GHCR uses: docker/login-action@v2 with: @@ -134,14 +122,10 @@ jobs: username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - # 11. Docker 이미지 빌드 & 푸시 - name: Build & Push Docker Image run: | IMAGE_NAME=ghcr.io/${{ github.repository }}/zoopzoop - - docker build -t $IMAGE_NAME:${GITHUB_SHA} . + docker build -t $IMAGE_NAME:${GITHUB_SHA} . docker tag $IMAGE_NAME:${GITHUB_SHA} $IMAGE_NAME:latest - docker push $IMAGE_NAME:${GITHUB_SHA} docker push $IMAGE_NAME:latest - diff --git a/src/main/resources/application-server.yml b/src/main/resources/application-server.yml index 30b1df91..b020162c 100644 --- a/src/main/resources/application-server.yml +++ b/src/main/resources/application-server.yml @@ -1,4 +1,5 @@ spring: + #DB 설정 datasource: url: ${SPRING_DATASOURCE_URL} username: ${SPRING_DATASOURCE_USERNAME} @@ -8,18 +9,90 @@ spring: hibernate: ddl-auto: update - data: #RedisTemplate 등을 사용하기 위한 직접 연결용 + #OAuth 설정 + security: + oauth2: + client: + registration: + kakao: + client-id: ${OAUTH_KAKAO_CLIENT_ID} + client-secret: ${OAUTH_KAKAO_CLIENT_SECRET} + redirect-uri: ${OAUTH_KAKAO_REDIRECT_URI} + scope: + -profile_nickname + -profile_image + authorization-grant-type: authorization_code + google: + client-id: ${OAUTH_GOOGLE_CLIENT_ID} + client-secret: ${OAUTH_GOOGLE_CLIENT_SECRET} + redirect-uri: ${OAUTH_GOOGLE_REDIRECT_URI} + scope: + - profile + authorization-grant-type: authorization_code + provider: + kakao: + authorization-uri: https://kauth.kakao.com/oauth/authorize + token-uri: https://kauth.kakao.com/oauth/token + user-info-uri: https://kapi.kakao.com/v2/user/me + user-name-attribute: id + 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/v3/userinfo + user-name-attribute: sub + + #AWS 설정 + cloud: + aws: + credentials: + access-key: ${AWS_ACCESS_KEY_ID} + secret-key: ${AWS_SECRET_ACCESS_KEY} + region: + static: ${AWS_REGION} + s3: + bucket: ${AWS_S3_BUCKET_NAME} + stack: + auto: false + + #Redis 설정 + data: redis: - host: 10.0.1.246 - port: 6379 + host: ${REDIS_HOST} + port: ${REDIS_PORT} + password: ${REDIS_PASSWORD} timeout: 6000 - password: zoopzoopRedisTest! - cache: #Spring Cache를 사용하기 위한 Redis + + cache: type: redis redis: time-to-live: 300000 cache-null-values: false +# Naver API +naver: + client_id: ${NAVER_CLIENT_ID} + client_secret: ${NAVER_CLIENT_SECRET} + +# JWT +jwt: + secret-key: ${JWT_SECRET_KEY} + access-token-validity: ${JWT_ACCESS_TOKEN_VALIDITY} + refresh-token-validity: ${JWT_REFRESH_TOKEN_VALIDITY} + +#Frontend +front: + redirect-domain: ${FRONT_REDIRECT_DOMAIN} + +# Liveblocks +liveblocks: + secret-key: ${LIVEBLOCKS_SECRET_KEY} + +# App 설정 +app: + seed: + enabled: true + +#Actuator management: endpoints: web: @@ -29,8 +102,9 @@ management: health: show-details: always +#Sentry sentry: - dsn: https://60f1acad189d2994353d59b7895076ee@o4510100579155968.ingest.us.sentry.io/4510100584923136 + dsn: ${SENTRY_DSN} # Add data like request headers and IP for users, # see https://docs.sentry.io/platforms/java/guides/spring-boot/data-management/data-collected/ for more info send-default-pii: true