diff --git a/build.gradle b/build.gradle index a58d679..2a1615c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,32 +1,51 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.3.6' + id 'org.springframework.boot' version '3.5.9' id 'io.spring.dependency-management' version '1.1.7' + id 'com.diffplug.spotless' version '6.25.0' } group = 'inha' version = '0.0.1-SNAPSHOT' -/* ===== Java Toolchain ===== */ java { toolchain { - languageVersion = JavaLanguageVersion.of(17) + languageVersion = JavaLanguageVersion.of(25) } } -/* ===== Configurations ===== */ configurations { compileOnly { extendsFrom annotationProcessor } } -/* ===== Repositories ===== */ repositories { mavenCentral() } -/* ===== Dependencies ===== */ +/* ===== Version Pins (필요한 것만) ===== */ +ext { + awspringBomVersion = '3.4.2' + springdocVersion = '2.8.15' + flywayVersion = '11.19.0' + postgresVersion = '42.7.3' + hibernateTypes60 = '2.21.1' + dotenvVersion = '5.2.2' + querydslVersion = '6.10.1' + jjwtVersion = '0.13.0' +} + +/* ===== Vulnerability Pins (Mend 경고 제거용) ===== */ +configurations.configureEach { + resolutionStrategy { + // CVE-2025-48924 대응 + force 'org.apache.commons:commons-lang3:3.18.0' + // CVE-2021-47621 대응 + force 'io.github.classgraph:classgraph:4.8.112' + } +} + dependencies { // --- Spring Boot Starters --- implementation 'org.springframework.boot:spring-boot-starter-web' @@ -35,32 +54,43 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-mail' + // --- Swagger / OpenAPI (springdoc) --- + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:${springdocVersion}") { + exclude group: "org.apache.commons", module: "commons-lang3" + } + implementation "org.apache.commons:commons-lang3:3.18.0" + // --- DB & JPA Utils --- - implementation 'com.vladmihalcea:hibernate-types-60:2.21.1' - implementation 'org.postgresql:postgresql:42.7.3' - runtimeOnly 'com.h2database:h2' // 테스트/로컬용 인메모리 DB + implementation "com.vladmihalcea:hibernate-types-60:${hibernateTypes60}" + implementation "org.postgresql:postgresql:${postgresVersion}" + runtimeOnly 'com.h2database:h2' + + // --- Querydsl (OpenFeign fork) --- + implementation "io.github.openfeign.querydsl:querydsl-jpa:${querydslVersion}" + annotationProcessor "io.github.openfeign.querydsl:querydsl-apt:${querydslVersion}:jakarta" + annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" - // --- QueryDSL --- - implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' - annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta' - annotationProcessor 'jakarta.annotation:jakarta.annotation-api' - annotationProcessor 'jakarta.persistence:jakarta.persistence-api' + // Querydsl APT helper APIs (컴파일 타임만) + compileOnly 'jakarta.annotation:jakarta.annotation-api' + compileOnly 'jakarta.persistence:jakarta.persistence-api' - // --- JWT (JJWT 0.9.x, 레거시 패키지) --- - implementation 'io.jsonwebtoken:jjwt:0.9.1' + // --- JWT --- + implementation "io.jsonwebtoken:jjwt-api:${jjwtVersion}" + runtimeOnly "io.jsonwebtoken:jjwt-impl:${jjwtVersion}" + runtimeOnly "io.jsonwebtoken:jjwt-jackson:${jjwtVersion}" // --- AWS (S3) --- implementation 'io.awspring.cloud:spring-cloud-aws-starter-s3' - // --- Flyway (DB Migration) --- - implementation "org.flywaydb:flyway-core:10.21.0" - implementation "org.flywaydb:flyway-database-postgresql:10.21.0" + // --- Flyway --- + implementation "org.flywaydb:flyway-core:${flywayVersion}" + implementation "org.flywaydb:flyway-database-postgresql:${flywayVersion}" // --- 환경변수(.env) --- - implementation 'io.github.cdimascio:java-dotenv:5.2.2' + implementation "io.github.cdimascio:java-dotenv:${dotenvVersion}" // --- Lombok --- - compileOnly 'org.projectlombok:lombok' + compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' // --- Test --- @@ -68,29 +98,29 @@ dependencies { testImplementation 'org.mockito:mockito-core:5.6.0' testImplementation 'org.mockito:mockito-junit-jupiter:5.6.0' testImplementation 'org.assertj:assertj-core:3.24.2' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - - // swagger - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } dependencyManagement { imports { - mavenBom "io.awspring.cloud:spring-cloud-aws-dependencies:3.1.1" - // ↑ 3.x 대 사용 (프로젝트에 맞는 최신 3.x 가능) + mavenBom "io.awspring.cloud:spring-cloud-aws-dependencies:${awspringBomVersion}" } } -/* ===== Tasks ===== */ +spotless { + java { + googleJavaFormat('1.22.0') + target 'src/**/*.java' + } +} -// 테스트: 프로필과 JUnit 플랫폼 한 곳에서 설정 tasks.test { useJUnitPlatform() systemProperty "spring.profiles.active", "test" } -// QueryDSL 생성물 경로 고정 tasks.withType(JavaCompile).configureEach { - options.annotationProcessorGeneratedSourcesDirectory = file("build/generated/sources/annotationProcessor/java/main") + options.generatedSourceOutputDirectory.set(file("build/generated/sources/annotationProcessor/java/main")) } + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e2847c8..2e11132 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/inha/gdgoc/domain/resource/service/S3Service.java b/src/main/java/inha/gdgoc/domain/resource/service/S3Service.java index 1546431..6f4a2b2 100644 --- a/src/main/java/inha/gdgoc/domain/resource/service/S3Service.java +++ b/src/main/java/inha/gdgoc/domain/resource/service/S3Service.java @@ -1,10 +1,10 @@ package inha.gdgoc.domain.resource.service; import inha.gdgoc.domain.resource.enums.S3KeyType; +import inha.gdgoc.global.config.s3.S3Properties; import java.io.IOException; import java.util.UUID; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import software.amazon.awssdk.core.sync.RequestBody; @@ -17,16 +17,14 @@ public class S3Service { private final S3Client s3Client; - - @Value("${cloud.aws.s3.bucket}") - private String bucketName; + private final S3Properties s3Properties; public String upload(Long userId, S3KeyType s3key, MultipartFile file) throws IOException { String fileName = UUID.randomUUID() + "-" + file.getOriginalFilename(); String key = "user/%d/%s/%s".formatted(userId, s3key.getValue(), fileName); PutObjectRequest putReq = PutObjectRequest.builder() - .bucket(bucketName) + .bucket(s3Properties.getBucket()) .key(key) .contentType(file.getContentType()) .build(); @@ -37,7 +35,7 @@ public String upload(Long userId, S3KeyType s3key, MultipartFile file) throws IO public String getS3FileUrl(String key) { return s3Client.utilities() - .getUrl(GetUrlRequest.builder().bucket(bucketName).key(key).build()) + .getUrl(GetUrlRequest.builder().bucket(s3Properties.getBucket()).key(key).build()) .toExternalForm(); } } diff --git a/src/main/java/inha/gdgoc/global/config/s3/S3Properties.java b/src/main/java/inha/gdgoc/global/config/s3/S3Properties.java new file mode 100644 index 0000000..a7e562c --- /dev/null +++ b/src/main/java/inha/gdgoc/global/config/s3/S3Properties.java @@ -0,0 +1,14 @@ +package inha.gdgoc.global.config.s3; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Getter +@Setter +@Component +@ConfigurationProperties("app.s3") +public class S3Properties { + private String bucket; +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 47d075e..855549c 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -46,6 +46,18 @@ spring: auth: true starttls: enable: true + cloud: + aws: + credentials: + access-key: ${AWS_ACCESS_KEY_ID} + secret-key: ${AWS_SECRET_ACCESS_KEY} + region: + static: ${AWS_REGION} + + +app: + s3: + bucket: ${AWS_TEST_RESOURCE_BUCKET} logging: level: @@ -62,13 +74,3 @@ jwt: googleIssuer: ${GOOGLE_ISSUER} selfIssuer: ${SELF_ISSUER} secretKey: ${SECRET_KEY} - -cloud: - aws: - credentials: - access-key: ${AWS_ACCESS_KEY_ID} - secret-key: ${AWS_SECRET_ACCESS_KEY} - region: - static: ${AWS_REGION} - s3: - bucket: ${AWS_TEST_RESOURCE_BUCKET} diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 5a665bf..d43d8f1 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -45,6 +45,17 @@ spring: auth: true starttls: enable: true + cloud: + aws: + credentials: + access-key: ${AWS_ACCESS_KEY_ID} + secret-key: ${AWS_SECRET_ACCESS_KEY} + region: + static: ${AWS_REGION} + +app: + s3: + bucket: ${AWS_TEST_RESOURCE_BUCKET} google: client-id: ${GOOGLE_CLIENT_ID} @@ -61,12 +72,3 @@ jwt: selfIssuer: ${SELF_ISSUER} secretKey: ${SECRET_KEY} -cloud: - aws: - credentials: - access-key: ${AWS_ACCESS_KEY_ID} - secret-key: ${AWS_SECRET_ACCESS_KEY} - region: - static: ${AWS_REGION} - s3: - bucket: ${AWS_TEST_RESOURCE_BUCKET} diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index bd3381c..e6bb01f 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -46,6 +46,17 @@ spring: auth: true starttls: enable: true + cloud: + aws: + credentials: + access-key: ${AWS_ACCESS_KEY_ID} + secret-key: ${AWS_SECRET_ACCESS_KEY} + region: + static: ${AWS_REGION} + +app: + s3: + bucket: ${AWS_RESOURCE_BUCKET} logging: level: @@ -64,12 +75,3 @@ jwt: selfIssuer: ${SELF_ISSUER} secretKey: ${SECRET_KEY} -cloud: - aws: - credentials: - access-key: ${AWS_ACCESS_KEY_ID} - secret-key: ${AWS_SECRET_ACCESS_KEY} - region: - static: ${AWS_REGION} - s3: - bucket: ${AWS_RESOURCE_BUCKET} diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index f9473ec..50c6866 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -46,6 +46,18 @@ spring: main: allow-bean-definition-overriding: true + cloud: + aws: + credentials: + access-key: test + secret-key: test + region: + static: ap-northeast-2 + +app: + s3: + bucket: test-bucket + logging: level: org.hibernate.SQL: warn @@ -61,12 +73,3 @@ jwt: selfIssuer: test-self-issuer secretKey: MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY= -cloud: - aws: - credentials: - access-key: test - secret-key: test - region: - static: ap-northeast-2 - s3: - bucket: test-bucket