Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .DS_Store
Binary file not shown.
62 changes: 62 additions & 0 deletions .github/workflows/cd-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: CD - PROD

on:
push:
branches: [ main ]

jobs:
deploy:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-java@v4
with:
java-version: "21"
distribution: "temurin"

- uses: gradle/actions/setup-gradle@v3

- name: Grant execute permission
run: chmod +x gradlew

- name: Build JAR
run: ./gradlew build -x test

- name: Docker Login
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build & Push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/realmatch-backend:prod
${{ secrets.DOCKERHUB_USERNAME }}/realmatch-backend:${{ github.sha }}

- name: Deploy
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.PROD_SERVER_IP }}
username: ubuntu
key: ${{ secrets.PROD_SERVER_SSH_KEY }}
script: |
cd /home/ubuntu/realmatch
git pull origin main

docker compose pull
docker compose up -d

echo "Waiting for app to start..."
sleep 10

if ! docker compose ps | grep "Up"; then
echo "❌ Container is not running"
docker compose logs
exit 1
fi
64 changes: 58 additions & 6 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,29 @@ jobs:
build-and-test:
runs-on: ubuntu-latest

services:
mysql:
image: mysql:8.0
env:
MYSQL_DATABASE: test_db
MYSQL_USER: test_user
MYSQL_PASSWORD: test
MYSQL_ROOT_PASSWORD: test_root
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -ptest_root"
--health-interval=10s
--health-timeout=5s
--health-retries=5


env:
SPRING_PROFILES_ACTIVE: test
JWT_SECRET: pPmJ9ViYt8f6HAh2q5s36QmEUeyEFRcquPaNpnIGUK8er5DjfTKa4xbDsTFXQ7HRVfTLR2DIYs7s9iGdJ+Yb7Q==
JWT_ACCESS_EXPIRE_MS: 600000
JWT_REFRESH_EXPIRE_MS: 1209600000

steps:
- name: Checkout code
uses: actions/checkout@v4
Expand All @@ -19,6 +42,9 @@ jobs:
distribution: 'temurin'
cache: gradle

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3

- name: Grant execute permission for gradlew
run: chmod +x gradlew

Expand All @@ -33,12 +59,38 @@ jobs:
run: ./gradlew test
continue-on-error: true

- name: Publish test results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: |
build/test-results/**/*.xml
- name: Test application startup
run: |
./gradlew bootRun > bootrun.log 2>&1 &
APP_PID=$!
echo "Started bootRun with PID: $APP_PID"

# 최대 60초 대기하면서 애플리케이션 시작 확인
for i in {1..60}; do
# 로그에서 시작 완료 메시지 확인
if grep -q "Started RealMatchApplication" bootrun.log; then
echo "✅ Application started successfully after ${i}s"
kill $APP_PID 2>/dev/null || true
exit 0
fi

# 프로세스가 죽었는지 확인
if ! kill -0 $APP_PID 2>/dev/null; then
echo "❌ Application process terminated unexpectedly"
echo "=== bootRun log ==="
cat bootrun.log
exit 1
fi

sleep 1
done

echo "❌ Application failed to start within 60 seconds"
echo "=== bootRun log ==="
cat bootrun.log
kill $APP_PID 2>/dev/null || true
exit 1
timeout-minutes: 2

- name: Upload build artifacts
if: failure()
Expand Down
14 changes: 13 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# 특정 파일 제외
src/main/resources/application-*.yml
#src/main/resources/application-*.yml
src/main/resources/application.yml
src/test/resources/application.yml
src/test/resources/application-*.yml

# 환경 변수
.env
.env.*

### Gradle ###
.gradle/
Expand Down Expand Up @@ -37,3 +44,8 @@ Thumbs.db
### Log files ###
*.log
logs/

### mysql ###
mysql_data/

bin/
Binary file removed Docker/.DS_Store
Binary file not shown.
6 changes: 4 additions & 2 deletions Docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ WORKDIR /app
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar

EXPOSE 8080
EXPOSE 6000

ENTRYPOINT ["java", "-jar", "app.jar"]
ENV SERVER_PORT=6000

ENTRYPOINT ["java", "-jar", "app.jar"]
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
git config core.hooksPath .githooks
```

아래 명령어를 이용해서 코드 스타일을 준수했는지 확인해주세요
```bash
./gradlew checkstyleMain checkstyleTest
```

아래 명령어를 통해 쉽게 도커 컨테이너에서 사용할 수 있습니다.

```bash
Expand Down
42 changes: 41 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
id 'java'
id 'org.springframework.boot' version '3.5.9'
id 'io.spring.dependency-management' version '1.1.7'
id 'checkstyle'
}

group = 'com.example'
Expand All @@ -25,16 +26,55 @@ repositories {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'

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'
Comment on lines +38 to 46
Copy link
Contributor

Choose a reason for hiding this comment

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

high

테스트 관련 의존성이 중복으로 선언되어 있습니다. 중복을 제거하고 그룹화하여 가독성을 높이는 것이 좋습니다.

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.security:spring-security-test'
    testRuntimeOnly 'com.h2database:h2'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'


//JWT
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
implementation 'io.jsonwebtoken:jjwt-impl:0.12.6'
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.6'

// mySQL
runtimeOnly 'com.mysql:mysql-connector-j'

// swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9'
}

tasks.named('test') {
useJUnitPlatform()
}

tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}

checkstyle {
toolVersion = '10.12.5'
configFile = file("${rootProject.projectDir}/config/checkstyle/checkstyle.xml")
maxWarnings = 0
ignoreFailures = false
}

tasks.withType(Checkstyle) {
reports {
xml.required = true
html.required = true
}
}
27 changes: 14 additions & 13 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ services:
container_name: mysql_db
restart: always
environment:
MYSQL_DATABASE: myapp_db
MYSQL_USER: admin
MYSQL_PASSWORD: secret
MYSQL_ROOT_PASSWORD: rootsecret
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
ports:
- "3306:3306"
volumes:
Expand All @@ -30,16 +30,17 @@ services:
dockerfile: Docker/Dockerfile
container_name: spring_app
ports:
- "8080:8080"
- "6000:6000"
depends_on:
- db
- redis
env_file:
- .env
environment:
- SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/myapp_db?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Seoul
- SPRING_DATASOURCE_USERNAME=admin
- SPRING_DATASOURCE_PASSWORD=secret
- SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver
- SPRING_REDIS_HOST=redis
- SPRING_REDIS_PORT=6379
- SPRING_PROFILES_ACTIVE=dev

SPRING_DATASOURCE_URL: ${SPRING_DATASOURCE_URL}
SPRING_DATASOURCE_USERNAME: ${SPRING_DATASOURCE_USERNAME}
SPRING_DATASOURCE_PASSWORD: ${SPRING_DATASOURCE_PASSWORD}
SPRING_DATASOURCE_DRIVER_CLASS_NAME: ${SPRING_DATASOURCE_DRIVER_CLASS_NAME}
SPRING_REDIS_HOST: ${SPRING_REDIS_HOST}
SPRING_REDIS_PORT: ${SPRING_REDIS_PORT}
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE}
Comment on lines 39 to +46
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

env_file을 통해 .env 파일의 변수들을 이미 컨테이너로 전달하고 있으므로, environment 섹션에서 동일한 변수들을 다시 선언하는 것은 불필요합니다. 코드를 더 간결하게 유지하기 위해 이 블록을 제거하는 것을 고려해보세요. Docker Compose는 env_file에 정의된 변수들을 자동으로 환경 변수로 설정해줍니다.

6 changes: 3 additions & 3 deletions src/main/java/com/example/RealMatch/RealMatchApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
@SpringBootApplication
public class RealMatchApplication {

public static void main(String[] args) {
SpringApplication.run(RealMatchApplication.class, args);
}
public static void main(String[] args) {
SpringApplication.run(RealMatchApplication.class, args);
}

}
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
24 changes: 24 additions & 0 deletions src/main/java/com/example/RealMatch/global/common/BaseEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.RealMatch.global.common;

import java.time.LocalDateTime;

import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@NoArgsConstructor(access = lombok.AccessLevel.PROTECTED)
public abstract class BaseEntity {
Comment on lines +14 to +18
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

@CreatedDate와 같은 JPA Auditing 기능을 사용하려면, 애플리케이션의 메인 클래스나 설정 클래스에 @EnableJpaAuditing 어노테이션을 추가해야 합니다. 이 어노테이션이 없으면 createdAt 필드가 자동으로 채워지지 않아 null로 저장됩니다. RealMatchApplication 클래스에 추가하는 것을 권장합니다.

@EnableJpaAuditing
@SpringBootApplication
public class RealMatchApplication {
    // ...
}


@CreatedDate
@Column(name = "created_at", updatable = false, nullable = false)
private LocalDateTime createdAt;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.example.RealMatch.global.common;

import java.time.LocalDateTime;

import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class UpdateBaseEntity extends BaseEntity {

@LastModifiedDate
@Column(name = "updated_at")
private LocalDateTime updatedAt;

@Column(name = "deleted_at")
private LocalDateTime deletedAt;

@Column(name = "is_deleted", nullable = false)
private boolean isDeleted;

protected UpdateBaseEntity() {
super();
this.isDeleted = false;
}

public void softDelete(Integer deletedBy, LocalDateTime deletedAt) {
Copy link
Contributor

Choose a reason for hiding this comment

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

high

softDelete 메소드의 deletedBy 파라미터가 사용되지 않고 있습니다. 만약 삭제한 사용자를 기록할 의도였다면, UpdateBaseEntitydeletedBy 필드를 추가하고 이 메소드에서 값을 설정해야 합니다. 그렇지 않다면 불필요한 파라미터를 제거하는 것이 좋습니다.

Suggested change
public void softDelete(Integer deletedBy, LocalDateTime deletedAt) {
public void softDelete(LocalDateTime deletedAt) {

this.isDeleted = true;
this.deletedAt = deletedAt;
}

public void restore() {
this.isDeleted = false;
this.deletedAt = null;
}
}
Loading
Loading