Skip to content

Comments

feat(#7): 자동 배포를 위한 CD 워크플로우 추가#11

Merged
Yoonchulchung merged 13 commits intomainfrom
dev
Jan 16, 2026
Merged

feat(#7): 자동 배포를 위한 CD 워크플로우 추가#11
Yoonchulchung merged 13 commits intomainfrom
dev

Conversation

@ParkJiYeoung8297
Copy link
Contributor

related_to : #7

📝 작업 내용

자동 배포를 위한 CD 추가

✅ 변경 사항

  • cd-prod.yml 파일 추가
  • 서버에 .env 파일 추가 (노션 참고)

📷 스크린샷 (선택)

💬 리뷰어에게

Main 브랜치에 pr이 올라가야 작동하기 때문에, dev 브랜치 그대로 PR 날립니다.
환경변수는 서버에 직접 .env 파일로 올려두었습니다. application-prod.yml을 기준으로 작동합니다.
(→ SPRING_PROFILES_ACTIVE=prod)

Yoonchulchung and others added 12 commits January 3, 2026 19:17
Added commands for code style checks and Docker usage.
related_to : #7 

## 📝 작업 내용
CD 추가 과정에서 있었던 피드백을 반영했습니다. (PR #8 )

## ✅ 변경 사항
- [x] .env.* 추가
- [x] @requiredargsconstructor사용해서 DI로 수정
- [x] 기타 description과 api version 제거 

## 📷 스크린샷 (선택)


## 💬 리뷰어에게
아 로컬에서 PR을 안날리고 다시 했네요... 변경 사항 추가했습니다
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @ParkJiYeoung8297, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 애플리케이션의 지속적인 배포(CD) 환경을 구축하고, 전반적인 인프라를 강화하는 데 중점을 둡니다. JWT 기반의 강력한 보안 시스템을 도입하여 사용자 인증 및 권한 관리를 개선하고, Swagger를 통해 API 문서화를 자동화하여 개발 편의성을 높였습니다. 또한, Checkstyle을 도입하여 코드 품질을 일관되게 유지하며, 공통 엔티티 및 응답 형식을 정의하여 코드의 재사용성과 유지보수성을 향상시켰습니다.

Highlights

  • CD 워크플로우 추가 및 환경 설정: 자동 배포를 위한 CD 워크플로우를 추가하고, .env 파일을 통해 환경 변수를 관리하도록 docker-compose.yaml.gitignore를 업데이트했습니다. 프로덕션 및 테스트 환경을 위한 application-prod.ymlapplication-test.yml 파일이 새로 추가되었습니다.
  • 보안 및 인증 시스템 도입: Spring Security를 활용하여 JWT 기반의 인증 및 권한 부여 시스템을 구축했습니다. 이를 위해 SecurityConfig, JwtAuthenticationFilter, JwtProvider, CustomUserDetails 등의 클래스가 추가되었으며, CORS 설정과 커스텀 예외 처리 핸들러도 포함되었습니다.
  • API 문서화 (Swagger) 및 코드 품질 관리 (Checkstyle): Swagger (OpenAPI)를 통합하여 API 문서를 자동 생성하고, Checkstyle 플러그인을 build.gradle에 추가하여 코드 스타일 가이드를 적용했습니다. README.md에도 Checkstyle 실행 명령어가 추가되었습니다.
  • 공통 엔티티 및 응답 형식 정의: 엔티티의 생성 및 수정 시간을 자동으로 관리하는 BaseEntityUpdateBaseEntity 클래스를 추가했습니다. 또한, API 응답의 일관성을 위해 CustomResponse 클래스와 공통 에러/성공 코드 (GeneralErrorCode, GeneralSuccessCode)를 정의했습니다.
  • Docker 설정 및 포트 변경: Docker 컨테이너에서 애플리케이션이 노출하는 포트를 8080에서 6000으로 변경하고, Dockerfiledocker-compose.yaml에 반영했습니다.
Ignored Files
  • Ignored by pattern: .github/workflows/** (2)
    • .github/workflows/cd-prod.yml
    • .github/workflows/pr-check.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

이 PR은 자동 배포를 위한 CD 워크플로우와 관련 설정을 추가합니다. 전반적으로 Spring Security, JWT, Swagger, 표준 응답 구조 등 좋은 기반을 마련했습니다. 하지만 몇 가지 수정이 필요한 부분이 있습니다. build.gradle 파일에 중복된 의존성이 있고, SecurityConfig의 경로 설정이 컨트롤러와 일치하지 않아 API가 정상적으로 동작하지 않을 수 있습니다. 또한, application-test.yml 파일에 데이터베이스 접속 정보를 하드코딩한 것은 심각한 보안 문제이므로 반드시 수정해야 합니다. 자세한 내용은 각 파일에 대한 개별 코멘트를 참고해주세요.

Comment on lines 16 to 17
username: admin
password: secret
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

테스트용 설정 파일에 데이터베이스 사용자 이름과 비밀번호가 하드코딩되어 있습니다. 이는 심각한 보안 취약점입니다. 테스트 환경이라도 비밀번호와 같은 민감한 정보는 버전 관리에 포함되어서는 안 됩니다. application-prod.yml에서처럼 환경 변수를 사용하거나, git에서 추적하지 않는 로컬 설정 파일을 통해 주입하는 방식으로 변경해야 합니다.

    username: ${SPRING_DATASOURCE_USERNAME}
    password: ${SPRING_DATASOURCE_PASSWORD}

Comment on lines 28 to 35
private static final String[] PERMIT_ALL_URL_ARRAY = {
"/api/v1/test",
"/v3/api-docs/**", "/swagger-ui/**", "/swagger-resources/**", "/swagger-ui.html"
};

private static final String[] REQUEST_AUTHENTICATED_ARRAY = {
"/api/v1/test-auth"
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Security 설정에 정의된 URL 경로(PERMIT_ALL_URL_ARRAY, REQUEST_AUTHENTICATED_ARRAY)가 TestController에 설정된 경로와 일치하지 않습니다. SecurityConfig에는 /api/v1/test로 되어 있지만, 컨트롤러에는 /api/test로 되어 있어 v1이 빠져있습니다. 이로 인해 API 호출 시 403 Forbidden 오류가 발생할 수 있습니다. 경로를 일치시켜야 합니다.

Suggested change
private static final String[] PERMIT_ALL_URL_ARRAY = {
"/api/v1/test",
"/v3/api-docs/**", "/swagger-ui/**", "/swagger-resources/**", "/swagger-ui.html"
};
private static final String[] REQUEST_AUTHENTICATED_ARRAY = {
"/api/v1/test-auth"
};
private static final String[] PERMIT_ALL_URL_ARRAY = {
"/api/test",
"/v3/api-docs/**", "/swagger-ui/**", "/swagger-resources/**", "/swagger-ui.html"
};
private static final String[] REQUEST_AUTHENTICATED_ARRAY = {
"/api/test-auth"
};

Comment on lines 38 to 46
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

// Test dependencies
testRuntimeOnly 'com.h2database:h2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

build.gradle 파일에 중복된 테스트 의존성이 있습니다. testImplementation 'org.springframework.boot:spring-boot-starter-test'testRuntimeOnly 'org.junit.platform:junit-platform-launcher'가 여러 번 선언되었습니다. 중복을 제거하여 dependencies 블록을 더 깔끔하게 유지하는 것이 좋습니다.

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

    // Test dependencies
    testRuntimeOnly 'com.h2database:h2'
    testImplementation 'org.springframework.security:spring-security-test'

Comment on lines +33 to +36
public void softDelete(Integer deletedBy, LocalDateTime deletedAt) {
this.isDeleted = true;
this.deletedAt = deletedAt;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

softDelete 메소드의 deletedBy 파라미터가 사용되지 않고 있습니다. 삭제한 사용자를 기록하는 필드가 없다면 이 파라미터를 제거하는 것이 좋습니다. 만약 추후에 사용할 계획이라면, 해당 필드를 추가하고 값을 할당하는 로직을 구현해야 합니다.

Suggested change
public void softDelete(Integer deletedBy, LocalDateTime deletedAt) {
this.isDeleted = true;
this.deletedAt = deletedAt;
}
public void softDelete(LocalDateTime deletedAt) {
this.isDeleted = true;
this.deletedAt = deletedAt;
}

public CorsConfigurationSource corsConfigurationSource() {

CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of(allowedOrigin, "http://localhost:8080", swaggerUrl));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

CORS 설정에서 허용할 오리진으로 http://localhost:8080이 하드코딩되어 있습니다. 로컬 개발 환경을 위한 것이겠지만, 이 또한 application.yml 파일에서 설정값으로 관리하는 것이 더 유연하고 좋은 방법입니다.

Suggested change
configuration.setAllowedOrigins(List.of(allowedOrigin, "http://localhost:8080", swaggerUrl));
configuration.setAllowedOrigins(List.of(allowedOrigin, swaggerUrl));

.claim("role", role)
.issuedAt(new Date(now))
.expiration(new Date(now + expireMillis))
.signWith(secretKey, SignatureAlgorithm.HS256)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

jjwt 라이브러리의 최신 버전에서는 signWith(Key) 메소드 사용을 권장합니다. 현재 사용된 signWith(Key, SignatureAlgorithm)은 deprecated 될 수 있으며, Keys.hmacShaKeyFor()로 생성된 SecretKey에 이미 알고리즘 정보가 포함되어 있으므로, 알고리즘을 명시적으로 지정할 필요가 없습니다.

Suggested change
.signWith(secretKey, SignatureAlgorithm.HS256)
.signWith(secretKey)

"잘못된 요청입니다."),

INVALID_DATA(HttpStatus.BAD_REQUEST,
"COMMON4001_2",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

INVALID_DATA의 에러 코드가 COMMON4001_2로 되어있습니다. 다른 코드들과의 일관성을 위해 COMMON400_2로 수정하는 것이 좋아 보입니다.

Suggested change
"COMMON4001_2",
"COMMON400_2",


jpa:
hibernate:
ddl-auto: none
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

spring.jpa.hibernate.ddl-auto 옵션이 none으로 설정되어 있습니다. 일반적으로 테스트 환경에서는 각 테스트가 독립적으로 실행될 수 있도록 create-drop이나 create를 사용하여 매번 스키마를 재생성하는 것이 좋습니다. none으로 설정하면 외부에서 스키마를 관리해야 하므로 테스트가 깨지기 쉬울 수 있습니다. 의도된 설정이 아니라면 변경을 고려해보세요.

      ddl-auto: create-drop

Copy link
Member

@Yoonchulchung Yoonchulchung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 이전에 올린 코드에 조금 오류가 있습니다. main으로 merge 하시기 전에 수정 부탁드립니다.

context: .
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/spot-backend:prod
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 이전 프로젝트에 사용했던 flow가 올라온 것 같습니다. 확인 부탁드립니다. 도커 허브 주소는 노션에 남겨주시길 부탁드립니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프로젝트 관련 설정이 모두 spot으로 되어있어서 그거에 맞춰서 했습니다. 이걸 realmatch로 수정하겠습니다.

- name: Deploy
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.PROD_SERVER_IP }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PROD_SERVER_IP는 가비아 IP가 맞나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네네 노션에 올려주신 IP로 사용했습니다.

public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;

private static final String[] PERMIT_ALL_URL_ARRAY = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

하드코딩 되어 있는 API주소를 .env에서 관리될 수 있도록 변경해주세요

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

url을 .env로 관리하면, api 개발할 때마다 추가해줘야하고 가독성도 떨어져서 개발이 불편할 거 같은데,
따로 관리하려고 하시는 이유가 있을까요?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API 개발을 할때마다 .env는 항상 가지고 있어야 실행 가능하지 않나요? 따로 관리하는 이유는 인증을 개발하지 않는 개발자들도 쉽게 모든 사용자가 접근할 수 있는 API를 쉽게 추가할 수 있도록 구성하기 위해서 .env로 따로 관리하는 것이 좋을 것 같다고 생각했습니다. 또한 개발 단계에서는 고려하지 않아도 괜찮을 수 있지만, 배포를 고려하기도 하다 보니까 API가 노출되어 있으면 쉽게 공격당할 수 있지 않아요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.env로 관리할 경우 아래와 같은 과정이 반복되어서 불편할것이라고 생각했습니다.

  1. 로컬에서 .env 추가
  2. 서버에 접속해서 .env 추가
  3. 노션에서 .env 업데이트
  4. 다른 팀원들이 업데이트 된 .env 가져가기

.env가 깃으로 관리되는 것이 아니다보니 공유도 실시간으로 어려울거 같았습니다.

그리고 API는 브라우저에 공개되어 있는 값이라 url을 숨기는 것 보다는,
api요청이 들어왔을 때 인증을 거치도록 설정하면 보안상 괜찮을 것 같습니다.

어떻게 생각하시는지 의견 부탁드립니다!🙂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

관리가 불편한 것은 인프라로도 해결할 수 있는 부분이 있지만, 아직 거기까지는 하기는 어렵기 때문에 이 형태로 진행해도 괜찮을 것 같습니다. 👍

build.gradle Outdated
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9'

// postgresql (CI test 용)
runtimeOnly 'org.postgresql:postgresql'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

postgresql은 지워주시면 감사하겠습니다.


services:
postgres:
image: postgres:16
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분 mySQL에서 동작되도록 변경해주시면 감사하겠습니다.

postgres:
image: postgres:16
env:
POSTGRES_DB: myapp_db
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DB 정보는 secrets에서 관리될 수 있도록 해주시면 감사하겠습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

윤철님이 테스트용으로 가짜 값 넣어두신줄 알았습니다. mysql로 바꾸면서 시크릿으로 관리하겠습니다.

related_to : #7 

## 📝 작업 내용
CI와 CD 설정 파일 수정


## ✅ 변경 사항
- [x] 앱 이름 spot → application으로 수정
- [x] Ci 테스트를 위한 DB postgre → mysql로 수정


## 📷 스크린샷 (선택)


## 💬 리뷰어에게
CI 테스트 시 실제 운영 db에 연결해 테스트하는 것은 위험할 것 같아서, 테스트용 db로 변경했습니다. 테스트를 위한 가짜
값이기 때문에 따로 시크릿 처리하지 않았습니다.
Copy link
Member

@Yoonchulchung Yoonchulchung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋습니다

@Yoonchulchung Yoonchulchung merged commit ad951dc into main Jan 16, 2026
1 check passed
ParkJiYeoung8297 added a commit that referenced this pull request Jan 28, 2026
<!--
## PR 제목 컨벤션
[TYPE] 설명 (#이슈번호)

예시:
- [FEAT] 회원가입 API 구현 (#14)
- [FIX] 이미지 업로드 시 NPE 수정 (#23)
- [REFACTOR] 토큰 로직 분리 (#8)
- [DOCS] ERD 스키마 업데이트 (#6)
- [CHORE] CI/CD 파이프라인 추가 (#3)
- [RELEASE] v1.0.0 배포 (#30)

TYPE: FEAT, FIX, DOCS, REFACTOR, TEST, CHORE, RENAME, REMOVE, RELEASE
-->

## Summary
<!-- 변경 사항을 간단히 설명해주세요 -->


## Changes
<!-- 변경된 내용을 목록으로 작성해주세요 -->


## Type of Change
<!-- 해당하는 항목에 x 표시해주세요 -->
- [ ] Bug fix (기존 기능에 영향을 주지 않는 버그 수정)
- [x] New feature (기존 기능에 영향을 주지 않는 새로운 기능 추가)
- [ ] Breaking change (기존 기능에 영향을 주는 수정)
- [ ] Refactoring (기능 변경 없는 코드 개선)
- [ ] Documentation (문서 수정)
- [ ] Chore (빌드, 설정 등 기타 변경)
- [ ] Release (develop → main 배포)


## Related Issues
related_to : #11 

## 참고 사항
<!-- 리뷰어가 알아야 할 추가 정보가 있다면 작성해주세요 -->
@ParkJiYeoung8297 ParkJiYeoung8297 self-assigned this Feb 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants