Skip to content

Commit eb45fe5

Browse files
authored
Merge pull request #29 from DearGraduate/develop
๐Ÿ”€ develop๋ธŒ๋žœ์น˜ ๋ณ‘ํ•ฉ
2 parents 66545d5 + 88338cb commit eb45fe5

File tree

57 files changed

+2275
-9
lines changed

Some content is hidden

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

57 files changed

+2275
-9
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
name: Build and Deploy
2+
3+
on:
4+
push:
5+
branches: [ "cicd", "main" ]
6+
7+
jobs:
8+
build:
9+
runs-on: ubuntu-latest
10+
steps:
11+
# Git ์ €์žฅ์†Œ ์ฒดํฌ์•„์›ƒ
12+
- uses: actions/checkout@v3
13+
14+
# JDK 17 ์„ค์ •
15+
- name: Set up JDK 17
16+
uses: actions/setup-java@v3
17+
with:
18+
java-version: '17'
19+
distribution: 'temurin'
20+
21+
# Gradle ์บ์‹ฑ (๋นŒ๋“œ ์‹œ๊ฐ„ ๋‹จ์ถ•)
22+
- name: Gradle Caching
23+
uses: actions/cache@v3
24+
with:
25+
path: |
26+
~/.gradle/caches
27+
~/.gradle/wrapper
28+
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
29+
restore-keys: |
30+
${{ runner.os }}-gradle-
31+
32+
# Gradle ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ
33+
- name: Grant execute permission for gradlew
34+
run: chmod +x gradlew
35+
36+
# Gradle ๋นŒ๋“œ
37+
- name: Build with Gradle
38+
run: ./gradlew clean build -x test
39+
40+
# Docker Hub ๋กœ๊ทธ์ธ
41+
- name: Docker Hub Login
42+
uses: docker/login-action@v2
43+
with:
44+
username: ${{ secrets.DOCKER_USERNAME }}
45+
password: ${{ secrets.DOCKER_PASSWORD }}
46+
47+
# Docker ๋นŒ๋“œ, ํ‘ธ์‰ฌ
48+
- name: Docker Build & Push Docker Image
49+
run: |
50+
echo "๐Ÿณ Building Docker image"
51+
docker build -t ${{ secrets.MY_DOCKER_ID }}/graduate:latest .
52+
53+
echo "๐Ÿš€ Pushing Docker image"
54+
docker push ${{ secrets.MY_DOCKER_ID }}/graduate:latest
55+
56+
deploy:
57+
runs-on: ubuntu-latest
58+
needs: build
59+
steps:
60+
# ๊นƒํ—ˆ๋ธŒ ๋ ˆํฌ์ง€ํ† ๋ฆฌ ์ฝ”๋“œ ๋‹ค์šด๋กœ๋“œ
61+
- name: Checkout repository
62+
uses: actions/checkout@v4
63+
64+
# ํŒŒ์ผ ๋ณต์‚ฌ
65+
- name: Copy docker-compose.yml to Server
66+
uses: appleboy/scp-action@master
67+
with:
68+
host: ${{ secrets.SERVER_IP }}
69+
username: ${{ secrets.USERNAME }}
70+
key: ${{ secrets.PRIVATE_KEY }}
71+
source: "docker-compose.yml,prometheus.yml"
72+
target: "~/app/" # ์„œ๋ฒ„์˜ ์ €์žฅ ๊ฒฝ๋กœ
73+
74+
# ์„œ๋ฒ„์— ๋ฐฐํฌ
75+
- name: Deploy to Server
76+
uses: appleboy/ssh-action@master
77+
with:
78+
host: ${{ secrets.SERVER_IP }} # EC2 ํผ๋ธ”๋ฆญ IP
79+
username: ${{ secrets.USERNAME }} # ํ™ˆ์„œ๋ฒ„ SSH ๊ณ„์ • (ex: ubuntu)
80+
key: ${{ secrets.PRIVATE_KEY }} # PEM ํ‚ค
81+
script: |
82+
cd ~/app
83+
84+
# Create .env file
85+
printf "${{ secrets.ENV_FILE }}" > .env
86+
87+
# 1๏ธโƒฃ ์ตœ์‹  Docker ์ด๋ฏธ์ง€ ํ’€
88+
docker compose pull
89+
# 2๏ธโƒฃ ์ด์ „ ์ปจํ…Œ์ด๋„ˆ ์‚ญ์ œ
90+
docker compose down
91+
# 3๏ธโƒฃ ์ปจํ…Œ์ด๋„ˆ์™€ ์„œ๋น„์Šค๋ฅผ Docker Compose๋กœ ์‹คํ–‰
92+
docker compose up -d
93+
# 4๏ธโƒฃ ๋ถˆํ•„์š”ํ•œ ์ด๋ฏธ์ง€ ์‚ญ์ œ
94+
docker image prune -f

โ€Ž.gitignoreโ€Ž

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,5 @@ out/
4343
/src/main/resources/application*.properties
4444

4545
### env ###
46-
.env
46+
.env
47+

โ€ŽDockerfileโ€Ž

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# JDK 17์„ ๋ฒ ์ด์Šค ์ด๋ฏธ์ง€๋กœ ์‚ฌ์šฉ
2+
FROM openjdk:17-jdk-slim
3+
4+
# ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ JAR ํŒŒ์ผ์„ ๋ณต์‚ฌ
5+
COPY build/libs/*SNAPSHOT.jar /app.jar
6+
7+
ENTRYPOINT ["java", "-jar", "/app.jar"]

โ€Žbuild.gradleโ€Ž

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ dependencies {
6060
testImplementation 'io.projectreactor:reactor-test'
6161
testImplementation 'org.springframework.security:spring-security-test'
6262
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
63+
64+
// OAuth2
65+
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
66+
67+
//s3
68+
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
6369
}
6470

6571
tasks.named('test') {

โ€Ždocker-compose.ymlโ€Ž

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
services:
2+
mysql:
3+
image: mysql:latest
4+
environment:
5+
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
6+
MYSQL_DATABASE: ${MYSQL_DATABASE}
7+
TZ: Asia/Seoul
8+
volumes:
9+
- ./mysql_data:/var/lib/mysql
10+
ports:
11+
- 3306:3306
12+
healthcheck:
13+
test: [ "CMD", "mysqladmin", "ping" ] # MySQL์ด healthy ํ•œ ์ง€ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๋Š” ๋ช…๋ น์–ด
14+
interval: 5s # 5์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ ์ฒดํฌ
15+
retries: 10 # 10๋ฒˆ๊นŒ์ง€ ์žฌ์‹œ๋„
16+
17+
redis:
18+
image: redis:latest
19+
ports:
20+
- 6379:6379
21+
healthcheck:
22+
test: [ "CMD", "redis-cli", "ping" ]
23+
interval: 5s
24+
retries: 10
25+
26+
graduate:
27+
env_file:
28+
- .env
29+
image: ${DOCKER_USERNAME}/graduate:latest # ์ด๋ฏธ์ง€ ์ด๋ฆ„์„ ๋ช…์‹œ์ ์œผ๋กœ ์„ค์ •
30+
ports:
31+
- 8080:8080
32+
environment:
33+
SPRING_PROFILES_ACTIVE: prod # Spring ํ”„๋กœํŒŒ์ผ์„ prod๋กœ ์„ค์ •
34+
depends_on:
35+
mysql:
36+
condition: service_healthy
37+
redis:
38+
condition: service_healthy
39+
40+
nginx:
41+
image: nginx:latest
42+
container_name: nginx
43+
ports:
44+
- "80:80"
45+
- "443:443"
46+
volumes:
47+
- ./nginx/conf.d:/etc/nginx/conf.d
48+
- /etc/letsencrypt:/etc/letsencrypt:ro # SSL ์ธ์ฆ์„œ ๊ณต์œ 
49+
depends_on:
50+
- graduate
51+
52+
dozzle:
53+
image: amir20/dozzle:latest
54+
container_name: dozzle
55+
ports:
56+
- "8081:8080"
57+
volumes:
58+
- /var/run/docker.sock:/var/run/docker.sock # ๋กœ๊ทธ ์ ‘๊ทผ์„ ์œ„ํ•œ ๋„์ปค ์†Œ์ผ“ ๋งˆ์šดํŠธ
59+
restart: unless-stopped
60+
61+
prometheus:
62+
image: prom/prometheus:latest
63+
container_name: prometheus
64+
ports:
65+
- "9090:9090"
66+
volumes:
67+
- ./prometheus.yml:/etc/prometheus/prometheus.yml
68+
69+
grafana:
70+
image: grafana/grafana
71+
container_name: grafana
72+
ports:
73+
- "3000:3000"
74+
volumes:
75+
- grafana-storage:/var/lib/grafana
76+
77+
node-exporter:
78+
image: prom/node-exporter
79+
container_name: node-exporter
80+
ports:
81+
- "9100:9100"
82+
83+
volumes:
84+
mysql_data:
85+
grafana-storage:

โ€Žprometheus.ymlโ€Ž

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
global:
2+
scrape_interval: 5s
3+
4+
scrape_configs:
5+
- job_name: 'prometheus'
6+
static_configs:
7+
- targets: ['prometheus:9090']
8+
9+
- job_name: 'graduate'
10+
metrics_path: '/actuator/prometheus'
11+
static_configs:
12+
- targets: ['graduate:8080']
13+
14+
- job_name: 'node-exporter'
15+
static_configs:
16+
- targets: ['node-exporter:9100']

โ€Žsrc/main/java/com/example/graduate/GraduateApplication.javaโ€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
55
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
6+
import org.springframework.scheduling.annotation.EnableScheduling;
67

78
@EnableJpaAuditing
89
@SpringBootApplication
10+
@EnableScheduling
911
public class GraduateApplication {
1012

1113
public static void main(String[] args) {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.example.graduate.domain.album.controller;
2+
3+
import com.example.graduate.domain.album.dto.AlbumRequestDTO;
4+
import com.example.graduate.domain.album.dto.AlbumResponseDTO;
5+
import com.example.graduate.domain.album.service.AlbumService;
6+
import com.example.graduate.global.apiPayload.ApiResponse;
7+
import com.example.graduate.domain.album.status.AlbumSuccessStatus;
8+
import io.swagger.v3.oas.annotations.Operation;
9+
import io.swagger.v3.oas.annotations.tags.Tag;
10+
import lombok.RequiredArgsConstructor;
11+
import org.springframework.http.ResponseEntity;
12+
import org.springframework.web.bind.annotation.*;
13+
import jakarta.validation.Valid;
14+
15+
@RestController
16+
@RequestMapping("/api/albums")
17+
@RequiredArgsConstructor
18+
@Tag(name = "์•จ๋ฒ” API", description = "์•จ๋ฒ” ๊ด€๋ จ API์ž…๋‹ˆ๋‹ค.")
19+
public class AlbumController {
20+
21+
private final AlbumService albumService;
22+
23+
@Operation(summary = "์•จ๋ฒ” ์ƒ์„ฑ")
24+
@PostMapping
25+
public ResponseEntity<ApiResponse<?>> createAlbum(
26+
@Valid @RequestBody AlbumRequestDTO dto) {
27+
AlbumResponseDTO result = albumService.createAlbum(dto);
28+
return ResponseEntity
29+
.status(AlbumSuccessStatus._CREATE_ALBUM_SUCCESS.getHttpStatus())
30+
.body(ApiResponse.of(AlbumSuccessStatus._CREATE_ALBUM_SUCCESS, result));
31+
}
32+
33+
@Operation(summary = "์•จ๋ฒ” ์ˆ˜์ •")
34+
@PatchMapping
35+
public ResponseEntity<ApiResponse<?>> updateAlbum (
36+
@Valid @RequestBody AlbumRequestDTO dto){
37+
albumService.updateAlbum(dto);
38+
return ResponseEntity
39+
.status(AlbumSuccessStatus._UPDATE_ALBUM_SUCCESS.getHttpStatus())
40+
.body(ApiResponse.of(AlbumSuccessStatus._UPDATE_ALBUM_SUCCESS));
41+
}
42+
43+
44+
@Operation(summary = "์•จ๋ฒ” ์‚ญ์ œ")
45+
@DeleteMapping
46+
public ResponseEntity<ApiResponse<?>> deleteAlbum() {
47+
albumService.deleteAlbum();
48+
return ResponseEntity
49+
.status(AlbumSuccessStatus._DELETE_ALBUM_SUCCESS.getHttpStatus())
50+
.body(ApiResponse.of(AlbumSuccessStatus._DELETE_ALBUM_SUCCESS));
51+
}
52+
53+
@Operation(summary = "์•จ๋ฒ” ์กฐํšŒ")
54+
@GetMapping
55+
public ResponseEntity<ApiResponse<AlbumResponseDTO>> getAlbum() {
56+
AlbumResponseDTO album = albumService.getAlbum();
57+
return ResponseEntity
58+
.status(AlbumSuccessStatus._GET_ALBUM_SUCCESS.getHttpStatus())
59+
.body(ApiResponse.of(AlbumSuccessStatus._GET_ALBUM_SUCCESS, album));
60+
}
61+
62+
63+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.example.graduate.domain.album.domain;
2+
3+
import ch.qos.logback.core.Layout;
4+
import com.example.graduate.global.common.BaseEntity;
5+
import jakarta.persistence.*;
6+
import lombok.*;
7+
8+
import java.time.LocalDate;
9+
10+
@Entity
11+
@Table(name = "album")
12+
@Getter
13+
@Setter
14+
@Builder
15+
@NoArgsConstructor
16+
@AllArgsConstructor
17+
public class Album extends BaseEntity {
18+
19+
@Id
20+
@GeneratedValue(strategy = GenerationType.IDENTITY)
21+
private Long id;
22+
23+
@Column(nullable = false, unique = true)
24+
private Long userId;
25+
26+
@Setter
27+
@Column(nullable = false)
28+
private LocalDate graduationDate;
29+
30+
@Setter
31+
@Column(length = 5)
32+
private String albumName;
33+
34+
@Setter
35+
@Column(length = 20)
36+
private String description;
37+
}
38+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.example.graduate.domain.album.dto;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
import jakarta.validation.constraints.Size;
5+
import lombok.Getter;
6+
import lombok.Setter;
7+
8+
import java.time.LocalDate;
9+
10+
@Getter
11+
@Setter
12+
public class AlbumRequestDTO {
13+
private LocalDate graduationDate;
14+
@NotBlank(message = "์•จ๋ฒ” ์ œ๋ชฉ์€ ๋น„์–ด ์žˆ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
15+
@Size(max = 5, message = "์•จ๋ฒ” ์ œ๋ชฉ์€ ์ตœ๋Œ€ 5๊ธ€์ž๊นŒ์ง€ ์ž…๋ ฅ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.")
16+
private String albumName;
17+
@Size(max = 20, message = "์•จ๋ฒ” ์„ค๋ช…์€ ์ตœ๋Œ€ 20๊ธ€์ž๊นŒ์ง€ ์ž…๋ ฅ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.")
18+
private String description;
19+
}

0 commit comments

Comments
ย (0)