Skip to content

Commit bf3089a

Browse files
authored
Merge pull request #225 from mosu-dev/develop
prod : 프로덕션과 테스트 서버 파이프라인 분리
2 parents 60d067a + 29f3bcd commit bf3089a

651 files changed

Lines changed: 17184 additions & 3826 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.DS_Store

6 KB
Binary file not shown.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
---
2+
name: 성능 테스트 및 개선 리포트
3+
about: 성능 병목 테스트 및 개선 과정을 기록합니다.
4+
title: "[📈 성능 개선] OOO 기능의 부하 테스트 및 최적화"
5+
labels: "성능개선, 부하테스트, 최적화"
6+
assignees: ""
7+
---
8+
9+
## 🎯 테스트 시나리오 정의
10+
11+
> 어떤 기능에 대해 어떤 상황을 가정하여 성능 테스트를 진행할 것인지 구체적으로 작성합니다.
12+
13+
- **테스트 대상 API**: `[HTTP Method] /api/v1/...`
14+
- **핵심 로직**: (예: 게시글 목록 조회, 복잡한 조인 쿼리 포함)
15+
- **부하 테스트 도구**: (예: nGrinder, JMeter, k6)
16+
- **테스트 환경**: (예: 로컬 MacBook Pro (M1, 16GB), DB는 Docker, 애플리케이션 메모리 1GB)
17+
18+
---
19+
20+
## 📊 1차 성능 측정 결과 (개선 전)
21+
22+
> 개선 작업을 진행하기 전의 성능 지표를 기록합니다. (Before)
23+
24+
| 지표 (Metric) | 측정 값 (Value) | 단위 (Unit) |
25+
|:----------------------------|:-------------|:----------|
26+
| **VUser (가상 사용자 수)** | ||
27+
| **TPS (초당 트랜잭션 수)** | | tps |
28+
| **평균 응답 시간 (Avg. Latency)** | | ms |
29+
| **최대 응답 시간 (Max. Latency)** | | ms |
30+
| **에러율 (Error Rate)** | | % |
31+
32+
**[nGrinder, JMeter 등 결과 그래프 스크린샷을 여기에 첨부해 주세요]**
33+
34+
35+
---
36+
37+
## 🧐 병목 지점 분석 및 가설
38+
39+
> 1차 측정 결과를 바탕으로 어떤 부분에서 병목이 발생하고 있는지 분석하고, 원인에 대한 가설을 세웁니다.
40+
41+
**가설**:
42+
(예: N+1 쿼리 문제로 인해 DB 조회 시간이 과도하게 소요될 것으로 추정됨.)
43+
44+
**근거**:
45+
46+
- [ ] APM(Pinpoint, Sentry 등)에서 특정 메서드 지연 확인
47+
- [ ] 느린 쿼리 로그(Slow Query Log)에서 해당 쿼리 발견
48+
- [ ] 높은 CPU / Memory 사용량 확인 (구간: )
49+
- [ ] 코드 리뷰 (비효율적인 로직, 불필요한 API 호출 등)
50+
- **기타**:
51+
52+
---
53+
54+
## 🛠️ 개선 전략 및 실행
55+
56+
> 위 가설을 바탕으로 어떤 방식으로 문제를 해결할 것인지 구체적인 전략을 작성하고 실행합니다.
57+
58+
**전략**:
59+
(예: JPA의 Fetch Join을 사용하여 N+1 문제를 해결. 또는, 자주 변경되지 않는 데이터에 캐싱(Redis, Caffeine)을 적용.)
60+
61+
**관련 PR**:
62+
63+
- #(PR 번호)
64+
65+
---
66+
67+
## 📊 2차 성능 측정 결과 (개선 후)
68+
69+
> 개선 작업 후, 1차 측정과 동일한 조건으로 다시 성능을 측정하여 기록합니다. (After)
70+
71+
| 지표 (Metric) | 측정 값 (Value) | 단위 (Unit) |
72+
|:----------------------------|:-------------|:----------|
73+
| **VUser (가상 사용자 수)** | ||
74+
| **TPS (초당 트랜잭션 수)** | | tps |
75+
| **평균 응답 시간 (Avg. Latency)** | | ms |
76+
| **최대 응답 시간 (Max. Latency)** | | ms |
77+
| **에러율 (Error Rate)** | | % |
78+
79+
**[개선 후 결과 그래프 스크린샷을 여기에 첨부해 주세요]**
80+
81+
82+
---
83+
84+
## ✨ 최종 결론
85+
86+
> 개선 전/후 지표를 비교하여 개선 효과를 정량적으로 요약하고, 결론을 작성합니다.
87+
88+
**개선 효과 요약**:
89+
90+
- **TPS**: `(개선 전 값)` -> `(개선 후 값)` **( 약 O 배 증가 )**
91+
- **평균 응답 시간**: `(개선 전 값)` -> `(개선 후 값)` **( 약 O % 감소 )**
92+
93+
**결론**:
94+
(예: Fetch Join 적용으로 N+1 문제를 해결하여 DB I/O를 크게 줄였고, 그 결과 TPS는 2.5배 증가, 평균 응답 시간은 60% 감소하는 효과를 확인했습니다.)
95+
96+
**추가 논의 사항**:
97+
(예: 향후 트래픽 증가 시 스케일 아웃 전략 필요, 캐시 동기화 문제에 대한 추가 논의 필요 등)

.github/workflows/docker-ci.yaml

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@ name: Docker CI/CD
22

33
on:
44
pull_request:
5-
branches:
6-
- 'develop'
5+
branches: [ develop ]
76
push:
8-
branches:
9-
- 'develop'
10-
7+
branches: [ develop ]
118
workflow_dispatch:
129

1310
jobs:
@@ -24,8 +21,24 @@ jobs:
2421
java-version: '21'
2522
distribution: 'temurin'
2623

24+
- name: Cache Gradle files
25+
uses: actions/cache@v3
26+
with:
27+
path: |
28+
~/.gradle/caches
29+
~/.gradle/wrapper
30+
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
31+
restore-keys: |
32+
gradle-${{ runner.os }}-
33+
34+
- name: Clone external repo with jar into libs/
35+
run: |
36+
mkdir -p libs
37+
git clone https://x-access-token:${{ secrets.GH_PAT }}@github.com/mosu-dev/mosu-kmc-jar.git temp-jar
38+
cp temp-jar/*.jar libs/
39+
2740
- name: Build with Gradle
28-
run: ./gradlew clean build -x test
41+
run: ./gradlew build -x test
2942

3043
- name: Log in to DockerHub
3144
uses: docker/login-action@v3
@@ -34,9 +47,7 @@ jobs:
3447
password: ${{ secrets.DOCKER_PASSWORD }}
3548

3649
- name: Build Docker image
37-
run: |
38-
docker build -t kangtaehyun1107/mosu-server:${{ github.sha }} .
39-
50+
run: docker build -t kangtaehyun1107/mosu-server:${{ github.sha }} .
51+
working-directory:
4052
- name: Push Docker image
41-
run: |
42-
docker push kangtaehyun1107/mosu-server:${{ github.sha }}
53+
run: docker push kangtaehyun1107/mosu-server:${{ github.sha }}

.github/workflows/docker-depoly.yaml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,9 @@ jobs:
2222
script: |
2323
cd /home/ubuntu/mosu
2424
25-
echo "${{ secrets.env }}" > .env.prod
25+
echo "${{ secrets.ENV_BLUE }}" > .env.blue
26+
echo "${{ secrets.ENV_GREEN }}" > .env.green
2627
27-
echo "APP_IMAGE_VERSION=${{ github.sha }}" >> .env.prod
28-
29-
sudo docker compose pull
30-
sudo docker compose down
31-
sudo docker compose -f docker-compose.yml --env-file .env.prod up -d --build
28+
echo "APP_IMAGE_VERSION=${{ github.sha }}" >> .env.blue
29+
echo "APP_IMAGE_VERSION=${{ github.sha }}" >> .env.green
30+
./deploy.sh
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Docker PR build
2+
3+
on:
4+
pull_request:
5+
branches: [ develop ]
6+
7+
jobs:
8+
build-and-deploy:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- name: Checkout source
13+
uses: actions/checkout@v3
14+
15+
- name: Set up JDK 21
16+
uses: actions/setup-java@v3
17+
with:
18+
java-version: '21'
19+
distribution: 'temurin'
20+
21+
- name: Cache Gradle files
22+
uses: actions/cache@v3
23+
with:
24+
path: |
25+
~/.gradle/caches
26+
~/.gradle/wrapper
27+
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
28+
restore-keys: |
29+
gradle-${{ runner.os }}-
30+
31+
- name: Clone external repo with jar into libs/
32+
run: |
33+
mkdir -p libs
34+
git clone https://x-access-token:${{ secrets.GH_PAT }}@github.com/mosu-dev/mosu-kmc-jar.git temp-jar
35+
cp temp-jar/*.jar libs/
36+
37+
- name: Build with Gradle
38+
run: ./gradlew build -x test

.github/workflows/self-depoly.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Docker CI/CD - Deploy
2+
3+
on:
4+
workflow_dispatch:
5+
branches:
6+
- test
7+
jobs:
8+
deploy:
9+
runs-on: self-hosted
10+
11+
steps:
12+
- name: Deploy via SSH
13+
run: |
14+
cd ~/mosu-server
15+
16+
echo "${{ secrets.TEST_ENV_BLUE }}" > .env.blue
17+
echo "${{ secrets.TEST_ENV_GREEN }}" > .env.green
18+
echo "APP_IMAGE_VERSION=${{ github.sha }}" >> .env.blue
19+
echo "APP_IMAGE_VERSION=${{ github.sha }}" >> .env.green
20+
sudo docker stop $(sudo docker ps -aq) || true
21+
sudo docker rm $(sudo docker ps -aq) || true
22+
echo "Stopping all containers..."
23+
24+
sudo ./deploy.sh

.gitignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ build/
44
!gradle/wrapper/gradle-wrapper.jar
55
!**/src/main/**/build/
66
!**/src/test/**/build/
7+
src/main/generated
78

89
### STS ###
910
.apt_generated
@@ -37,4 +38,8 @@ out/
3738
.vscode/
3839

3940
docker-compose/.env
40-
docker-compose/.env.local
41+
docker-compose/.env.local
42+
docker-compose/.env.prod
43+
44+
/logs/**
45+
/libs/**

Dockerfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM openjdk:21-jdk
2-
ARG JAR_FILE=build/libs/*.jar
3-
ADD ${JAR_FILE} app.jar
4-
ENTRYPOINT ["java", "-Duser.timezone=GMT+9", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app.jar"]
1+
FROM amazoncorretto:21
2+
COPY build/libs/*SNAPSHOT.war app.war
3+
4+
ENTRYPOINT ["java", "-Duser.timezone=GMT+9", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app.war"]

build.gradle

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ plugins {
22
id 'java'
33
id 'org.springframework.boot' version '3.4.5'
44
id 'io.spring.dependency-management' version '1.1.7'
5+
id 'war'
56
}
67

78
group = 'life.mosu'
@@ -24,7 +25,7 @@ repositories {
2425
}
2526

2627
dependencies {
27-
28+
implementation fileTree(dir: 'libs', include: ['*.jar'])
2829
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
2930
implementation 'org.springframework.boot:spring-boot-starter-web'
3031
implementation 'org.springframework.boot:spring-boot-starter-validation'
@@ -38,8 +39,12 @@ dependencies {
3839
testImplementation 'org.springframework.boot:spring-boot-starter-test'
3940
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
4041

42+
// 인증사 관련 의존성
43+
implementation 'javax.servlet:jstl:1.2'
44+
implementation "org.apache.tomcat.embed:tomcat-embed-jasper"
45+
4146
// swagger
42-
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0"
47+
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9"
4348

4449
// jwt
4550
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
@@ -58,9 +63,17 @@ dependencies {
5863
implementation 'org.flywaydb:flyway-mysql'
5964
runtimeOnly 'com.mysql:mysql-connector-j'
6065

66+
// Testcontainers
67+
testImplementation 'org.springframework.boot:spring-boot-testcontainers:3.3.5'
68+
testImplementation 'org.testcontainers:testcontainers:1.19.3'
69+
testImplementation 'org.testcontainers:junit-jupiter:1.19.3'
70+
testImplementation 'org.testcontainers:mysql:1.20.0'
71+
72+
6173
// security
6274
implementation 'org.springframework.boot:spring-boot-starter-security'
6375
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
76+
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
6477

6578
// aws
6679
implementation platform('software.amazon.awssdk:bom:2.20.0')
@@ -85,6 +98,31 @@ dependencies {
8598

8699
// poi-excel
87100
implementation 'org.apache.poi:poi-ooxml:5.4.0'
101+
102+
implementation 'org.springframework.boot:spring-boot-starter-webflux'
103+
104+
runtimeOnly 'com.h2database:h2'
105+
106+
testImplementation 'org.springframework.boot:spring-boot-testcontainers:3.3.5'
107+
testImplementation 'org.testcontainers:testcontainers:1.19.3'
108+
testImplementation 'org.testcontainers:junit-jupiter:1.19.3'
109+
testImplementation 'org.testcoscntainers:mysql:1.20.0'
110+
111+
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
112+
113+
implementation 'org.apache.commons:commons-pool2:2.12.1'
114+
115+
//scheduler
116+
implementation 'org.springframework.boot:spring-boot-starter-quartz'
117+
118+
//타임리프
119+
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
120+
}
121+
122+
configurations.configureEach {
123+
resolutionStrategy {
124+
force 'org.apache.commons:commons-lang3:3.18.0'
125+
}
88126
}
89127

90128
tasks.named('test') {

0 commit comments

Comments
 (0)