Skip to content

Commit fdf06e8

Browse files
committed
demo version
1 parent db90b20 commit fdf06e8

19 files changed

Lines changed: 797 additions & 6 deletions

.github/workflows/deploy.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Backend Deploy
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
7+
jobs:
8+
deploy:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- uses: actions/checkout@v4
13+
14+
- name: Setup Java
15+
uses: actions/setup-java@v4
16+
with:
17+
distribution: corretto
18+
java-version: 21
19+
20+
- name: Build
21+
run: ./gradlew clean build
22+
23+
- name: Copy jar to EC2
24+
uses: appleboy/scp-action@v0.1.4
25+
with:
26+
host: ${{ secrets.EC2_HOST }}
27+
username: ${{ secrets.EC2_USER }}
28+
key: ${{ secrets.EC2_SSH_KEY }}
29+
source: build/libs/${{ secrets.APP_NAME }}
30+
target: /home/ec2-user/
31+
32+
- name: Restart app on EC2
33+
uses: appleboy/ssh-action@v1.0.3
34+
with:
35+
host: ${{ secrets.EC2_HOST }}
36+
username: ${{ secrets.EC2_USER }}
37+
key: ${{ secrets.EC2_SSH_KEY }}
38+
script: |
39+
pkill -f 'demo-backend'
40+
nohup java -jar /home/ec2-user/${{ secrets.APP_NAME }} \
41+
--spring.profiles.active=prod \
42+
> app.log 2>&1 &

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,7 @@ out/
3535

3636
### VS Code ###
3737
.vscode/
38+
39+
### yml ###
40+
application-local.yml
41+
application-prod.yml

build.gradle

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
plugins {
22
id 'java'
3-
id 'org.springframework.boot' version '3.5.9'
3+
id 'org.springframework.boot' version '3.3.6'
44
id 'io.spring.dependency-management' version '1.1.7'
55
}
66

@@ -25,13 +25,26 @@ repositories {
2525
}
2626

2727
dependencies {
28-
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
29-
implementation 'org.springframework.boot:spring-boot-starter-validation'
28+
// Spring Boot 기본
3029
implementation 'org.springframework.boot:spring-boot-starter-web'
31-
compileOnly 'org.projectlombok:lombok'
32-
annotationProcessor 'org.projectlombok:lombok'
30+
implementation 'org.springframework.boot:spring-boot-starter-validation'
31+
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
32+
33+
// Swagger
34+
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'
35+
36+
// AWS S3
37+
implementation 'software.amazon.awssdk:s3:2.25.60'
38+
39+
// 로그 (명시적으로 고정)
40+
implementation 'org.slf4j:slf4j-api:2.0.16'
41+
implementation 'ch.qos.logback:logback-classic:1.5.12'
42+
43+
// Lombok
44+
compileOnly 'org.projectlombok:lombok:1.18.36'
45+
annotationProcessor 'org.projectlombok:lombok:1.18.36'
46+
3347
testImplementation 'org.springframework.boot:spring-boot-starter-test'
34-
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
3548
}
3649

3750
tasks.named('test') {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.gathertree.demo.global.config;
2+
3+
import org.springframework.context.annotation.Configuration;
4+
import org.springframework.web.servlet.config.annotation.CorsRegistry;
5+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
6+
7+
@Configuration
8+
public class CorsConfig implements WebMvcConfigurer {
9+
10+
@Override
11+
public void addCorsMappings(CorsRegistry registry) {
12+
registry.addMapping("/**")
13+
.allowedOrigins(
14+
"http://localhost:5173", // 프론트 로컬 (Vite)
15+
"http://localhost:8080", // Swagger 로컬
16+
"https://beour.store" // 프론트 운영
17+
)
18+
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
19+
.allowedHeaders("*")
20+
.allowCredentials(false);
21+
}
22+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.gathertree.demo.global.config;
2+
3+
import org.springframework.context.annotation.Bean;
4+
import org.springframework.context.annotation.Configuration;
5+
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
6+
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
7+
import software.amazon.awssdk.regions.Region;
8+
import software.amazon.awssdk.services.s3.S3Client;
9+
10+
@Configuration
11+
public class S3Config {
12+
13+
@Bean
14+
public S3Client s3Client() {
15+
return S3Client.builder()
16+
.region(Region.AP_NORTHEAST_2)
17+
.credentialsProvider(
18+
StaticCredentialsProvider.create(
19+
AwsBasicCredentials.create(
20+
"ACCESS_KEY",
21+
"SECRET_KEY"
22+
)
23+
)
24+
)
25+
.build();
26+
}
27+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.gathertree.demo.global.config;
2+
3+
import io.swagger.v3.oas.models.OpenAPI;
4+
import io.swagger.v3.oas.models.info.Info;
5+
import io.swagger.v3.oas.models.servers.Server;
6+
import org.springframework.context.annotation.Bean;
7+
import org.springframework.context.annotation.Configuration;
8+
9+
import java.util.List;
10+
11+
/*
12+
Swagger 접속 URL
13+
- Local : http://localhost:8080/swagger-ui/index.html
14+
- Prod : https://api.beour.store/swagger-ui/index.html (인프라 구성 후)
15+
*/
16+
@Configuration
17+
public class SwaggerConfig {
18+
19+
@Bean
20+
public OpenAPI openAPI() {
21+
return new OpenAPI()
22+
.info(new Info()
23+
.title("🎄 GatherTree API")
24+
.description("GatherTree 크리스마스 이벤트 백엔드 API 명세")
25+
.version("v1")
26+
)
27+
.servers(List.of(
28+
// ✅ 지금은 로컬만
29+
new Server()
30+
.url("http://localhost:8080")
31+
.description("Local server")
32+
33+
// 🚫 인프라 구성 전이므로 제거
34+
// new Server()
35+
// .url("https://api.beour.store")
36+
// .description("Production server")
37+
));
38+
}
39+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.gathertree.demo.global.util;
2+
3+
import java.util.Base64;
4+
5+
public class Base64ImageUtil {
6+
7+
public static byte[] decode(String base64) {
8+
if (!base64.contains(",")) {
9+
throw new IllegalArgumentException("Invalid base64 image format");
10+
}
11+
return Base64.getDecoder().decode(base64.split(",")[1]);
12+
}
13+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.gathertree.demo.image.service;
2+
3+
import com.gathertree.demo.global.response.exception.GeneralException;
4+
import com.gathertree.demo.global.response.status.ErrorStatus;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.stereotype.Service;
7+
import software.amazon.awssdk.core.sync.RequestBody;
8+
import software.amazon.awssdk.services.s3.S3Client;
9+
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
10+
11+
@Service
12+
@RequiredArgsConstructor
13+
public class S3ImageService {
14+
15+
private final S3Client s3Client;
16+
private final String bucket = "gathertree-prod"; // TODO: @Value로 분리
17+
18+
public String upload(byte[] image, String key) {
19+
try {
20+
PutObjectRequest request = PutObjectRequest.builder()
21+
.bucket(bucket)
22+
.key(key)
23+
.contentType("image/png")
24+
.build();
25+
26+
s3Client.putObject(request, RequestBody.fromBytes(image));
27+
28+
return "https://" + bucket + ".s3.amazonaws.com/" + key;
29+
30+
} catch (Exception e) {
31+
throw new GeneralException(ErrorStatus.IMAGE_UPLOAD_FAIL, e);
32+
}
33+
}
34+
}

0 commit comments

Comments
 (0)