diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000..617b822 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,63 @@ +# 리뷰 언어 설정 - 한국어로 피드백을 받습니다 +language: "ko-KR" + +reviews: + # 리뷰 강도 설정 + # assertive(꼼꼼) / chill(부드럽게) 중 선택 + profile: "chill" + + # true로 설정 시, 모든 리뷰 코멘트가 resolve되면 자동으로 승인(Approve) + request_changes_workflow: false + + # PR 요약 + high_level_summary: true # PR 전반적인 변경 내용 요약 + changed_files_summary: true # 파일별 변경 내용 요약 + + # 워크스루를 접힌 상태로 표시해 PR 페이지가 너무 길어지는 것을 방지 + collapse_walkthrough: true + + # 코드 흐름을 시각적으로 파악할 수 있는 시퀀스 다이어그램 생성 + sequence_diagrams: true + + # 연결된 이슈를 분석해 리뷰에 맥락을 추가 + assess_linked_issues: true + + # 관련 이슈/PR 자동 링크 (팀 규모가 작아 비활성화) + related_issues: false + related_prs: false + + # 레이블 자동 제안/적용 (수동으로 관리) + suggested_labels: false + auto_apply_labels: false + + # 리뷰어 추천/자동 할당 (수동으로 지정) + suggested_reviewers: false + auto_assign_reviewers: false + + # -------------------------------------------------------------- # + # 자동 리뷰 설정 # + # -------------------------------------------------------------- # + auto_review: + # PR 생성 시 자동으로 리뷰 시작 + enabled: true + # 기존 리뷰가 있는 PR에 새 커밋이 추가되면 변경분만 다시 리뷰 + auto_incremental_review: true + +# ================================================================ # +# 채팅 설정 # +# ================================================================ # +chat: + # @coderabbitai 멘션 없이도 PR 코멘트에 자동으로 응답 + auto_reply: true + +# ================================================================ # +# 지식 베이스 설정 # +# ================================================================ # +knowledge_base: + # 리뷰 시 최신 문서나 정보를 웹에서 검색해 참고할 수 있도록 허용 + web_search: + enabled: true + + # 레포에 코딩 컨벤션이나 가이드라인 문서가 있다면 리뷰에 반영 + code_guidelines: + enabled: true \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index ea57d1d..6e2d5b8 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,7 +5,7 @@ - [ ] 기타 사소한 수정 ## ❗️ 관련 이슈 링크 -Close # +Closed # ## 📌 개요 - diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..5a77a76 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,85 @@ +name: SCOI CD +on: + push: + branches: [ "develop" ] + +env: + # 도커 허브 저장소 이름 (예: myusername/scoi-backend) + DOCKER_IMAGE_NAME: ${{ secrets.DOCKER_USERNAME }}/scoi-backend + +jobs: + # 📦 1단계: 빌드 및 도커 이미지 푸시 + build-and-push: + runs-on: ubuntu-latest + steps: + - name: 체크아웃 + uses: actions/checkout@v4 + + - name: 자바 설정 Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: 'gradle' + + - name: application.yml 주입 + shell: bash + run: | + mkdir -p src/main/resources + cat > src/main/resources/application.yml <<'EOF' + ${{ secrets.DEV_APPLICATION_YML }} + EOF + + - name: firebase json 주입 + shell: bash + run: | + mkdir -p src/main/resources + cat > src/main/resources/firebase-scoi.json <<'EOF' + ${{ secrets.FIREBASE_JSON }} + EOF + + - name: gradlew 실행 권한 부여 + run: chmod +x gradlew + + - name: gradle 빌드 (테스트 제외) + run: ./gradlew clean build -x test + + - name: Docker Hub 로그인 + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Docker 이미지 빌드 및 푸시 + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ env.DOCKER_IMAGE_NAME }}:latest + + + deploy: + needs: build-and-push + runs-on: ubuntu-latest + steps: + - name: EC2에 배포 (SSH 접속) + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.EC2_HOST }} + username: ${{ secrets.EC2_SSH_USER }} + key: ${{ secrets.EC2_SSH_KEY }} + + script: | + cd ~/scoi-Backend + + echo "${{ secrets.DEV_ENV_FILE }}" > .env + echo "DOCKER_USERNAME=${{ secrets.DOCKER_USERNAME }}" >> .env + + # Docker Hub에서 방금 만든 최신 이미지 가져오기 + sudo docker-compose pull + + # 컨테이너 재실행 + sudo docker-compose up -d + + # 불필요한 이미지 삭제 + sudo docker image prune -f diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..206cb9a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,76 @@ +name: SCOI CI + +on: + push: + branches: [ "main", "develop" ] + pull_request: + branches: [ "main", "develop" ] + +jobs: + build: + runs-on: ubuntu-latest + + # CI 서버 내부에서 테스트용 DB/Redis 컨테이너 실행 + services: + mysql: + image: mysql:8.0 + env: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: scoi + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + + redis: + image: redis:latest + ports: + - 6379:6379 + options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3 + + steps: + - name: 체크아웃 + uses: actions/checkout@v4 + + - name: 자바 설정 Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: 'gradle' + + - name: application.yml 주입 + shell: bash + run: | + mkdir -p src/main/resources + cat > src/main/resources/application.yml <<'EOF' + ${{ secrets.DEV_APPLICATION_YML }} + EOF + + # firebase 파일 생성 + - name: firebase json 주입 + shell: bash + run: | + mkdir -p src/main/resources + cat > src/main/resources/firebase-scoi.json <<'EOF' + ${{ secrets.FIREBASE_JSON }} + EOF + + - name: gradlew 실행 권한 부여 + run: chmod +x gradlew + + - name: gradle 빌드 + run: ./gradlew build + + # CI용 + env: + DB_URL: jdbc:mysql://localhost:3306/scoi?serverTimezone=Asia/Seoul + DB_USERNAME: root + DB_PASSWORD: root + REDIS_HOST: localhost + REDIS_PORT: 6379 + JWT_KEY: scoi-test-key + JWT_SECRET: scoi-test-secret-keysdsdsdsdsdsdssdsdsdsdsdsdsdsdsdsdsdsdsdsdsds + COOLSMS_API_URL: test_coolsms_api_key + COOLSMS_API_SECRET: test_coolsms_secret_key + COOLSMS_API_KEY: test_coolsms_key + COOLSMS_FROM_NUMBER: 01000000000 \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 10a708b..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,92 +0,0 @@ -name: Deploy (dev/main, Java 21, firebase shared) - -on: - push: - branches: [ "develop" ] # "main" 주석처리 - -jobs: - deploy: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Java 21 - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '21' - - # 공통 firebase 파일 생성 - - name: Inject firebase json (shared) - shell: bash - run: | - mkdir -p src/main/resources - cat > src/main/resources/firebase-scoi.json <<'EOF' - ${{ secrets.FIREBASE_JSON }} - EOF - - # # main application.yml (주석처리 — develop만 배포) - # - name: Inject application.yml (main) - # if: github.ref_name == 'main' - # shell: bash - # run: | - # mkdir -p src/main/resources - # cat > src/main/resources/application.yml <<'EOF' - # ${{ secrets.APP_YML_MAIN }} - # EOF - - # dev application.yml - - name: Inject application.yml (dev) - if: github.ref_name == 'develop' - shell: bash - run: | - mkdir -p src/main/resources - cat > src/main/resources/application.yml <<'EOF' - ${{ secrets.APP_YML_DEV }} - EOF - - - name: Build bootJar - run: | - chmod +x ./gradlew - ./gradlew clean bootJar -x test - - - name: Prepare SSH - run: | - mkdir -p ~/.ssh - echo "${{ secrets.EC2_SSH_KEY }}" > ~/.ssh/id_ed25519 - chmod 600 ~/.ssh/id_ed25519 - ssh-keyscan -H "${{ secrets.EC2_HOST }}" >> ~/.ssh/known_hosts - - - name: Upload jar to EC2 - shell: bash - run: | - # bootJar/ plain.jar 혼동 방지: plain 제외 우선 선택 - JAR_PATH=$(ls build/libs/*.jar | grep -v plain | head -n 1) - test -n "$JAR_PATH" - scp "$JAR_PATH" ${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }}:/home/${{ secrets.EC2_USER }}/app.jar - - # - name: Deploy & restart (main) # 주석처리 — develop만 배포 - # if: github.ref_name == 'main' - # run: | - # ssh ${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }} << 'EOF' - # set -e - # sudo mkdir -p /opt/myapp-main - # sudo mv /home/ec2-user/app.jar /opt/myapp-main/app.jar - # sudo chown -R ec2-user:ec2-user /opt/myapp-main - # sudo systemctl restart myapp-main - # sudo systemctl status myapp-main --no-pager - # EOF - - - name: Deploy & restart (dev) - if: github.ref_name == 'develop' - run: | - ssh ${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }} << 'EOF' - set -e - sudo mkdir -p /opt/myapp-dev - sudo mv /home/ec2-user/app.jar /opt/myapp-dev/app.jar - sudo chown -R ec2-user:ec2-user /opt/myapp-dev - sudo systemctl restart myapp-dev - sudo systemctl status myapp-dev --no-pager - EOF \ No newline at end of file diff --git a/.gitignore b/.gitignore index 060889e..9757ff1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ *.yml *.properties # GitHub Actions workflow는 예외로 추적 -!.github/workflows/deploy.yml +!.github/workflows/ ### workflow.yml 제외 ### !workflow.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..00ddcf2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM eclipse-temurin:21-jdk-alpine + +WORKDIR /app + +COPY build/libs/*SNAPSHOT.jar app.jar + +ENTRYPOINT ["java", "-jar", "-Duser.timezone=Asia/Seoul", "app.jar"] \ No newline at end of file diff --git a/README.md b/README.md index 6ecb84f..5555b17 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # 스코이 : 편리한 스테이블 코인 결제 플랫폼 + ## 💡 Project Overview -스코이는 스테이블코인을 일상적인 결제 수단으로 사용할 수 있도록 설계된 금융 플랫폼입니다. -결제부터 투자까지, 누구나 쉽게 사용할 수 있는 편리한 스테이블 코인 금융 환경을 제공하는 것을 목표로 합니다. +스코이는 스테이블코인을 일상적인 결제 수단으로 사용할 수 있도록 설계된 금융 플랫폼입니다 +결제부터 투자까지, 누구나 쉽게 사용할 수 있는 편리한 스테이블 코인 금융 환경을 제공하는 것을 목표로 합니다 image image diff --git a/build.gradle b/build.gradle index 8bdb235..9d9c57e 100644 --- a/build.gradle +++ b/build.gradle @@ -82,6 +82,12 @@ dependencies { // Retry implementation 'org.springframework.retry:spring-retry' + + // Spring Actuator + implementation 'org.springframework.boot:spring-boot-starter-actuator' + + // Prometheus + runtimeOnly 'io.micrometer:micrometer-registry-prometheus' } tasks.named('test') { diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..9c0ef24 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,32 @@ +version: '3.8' + +services: + scoi-backend: + image: ${DOCKER_USERNAME}/scoi-backend:latest + container_name: scoi-backend + ports: + - "8080:8080" + + env_file: + - .env + + depends_on: + - redis + + networks: + - scoi-network + + restart: always + + redis: + image: redis:latest + container_name: scoi-redis + ports: + - "6379:6379" + networks: + - scoi-network + restart: always + +networks: + scoi-network: + driver: bridge \ No newline at end of file diff --git a/src/main/java/com/example/scoi/global/config/SecurityConfig.java b/src/main/java/com/example/scoi/global/config/SecurityConfig.java index b017ef7..9343ee6 100644 --- a/src/main/java/com/example/scoi/global/config/SecurityConfig.java +++ b/src/main/java/com/example/scoi/global/config/SecurityConfig.java @@ -37,6 +37,7 @@ public class SecurityConfig { "/auth/login", // 로그인 "/auth/reissue", // 토큰 재발급 "/auth/password/reset", // 비인증 비밀번호 재설정 + "/actuator/**", // 모니터링 "/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", diff --git a/src/main/java/com/example/scoi/global/config/SwaggerConfig.java b/src/main/java/com/example/scoi/global/config/SwaggerConfig.java index 4add088..cdc676f 100644 --- a/src/main/java/com/example/scoi/global/config/SwaggerConfig.java +++ b/src/main/java/com/example/scoi/global/config/SwaggerConfig.java @@ -14,7 +14,7 @@ public class SwaggerConfig { @Bean public OpenAPI swagger() { - Info info = new Info().title("스코이").description("스코이 Swagger").version("0.4.0"); + Info info = new Info().title("스코이").description("스코이 Swagger").version("0.5.0"); // JWT 토큰 헤더 방식 String securityScheme = "JWT TOKEN";