diff --git a/build.gradle b/build.gradle index f9e93bf..50b5c57 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,26 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.projectreactor:reactor-test' testImplementation 'org.springframework.security:spring-security-test' + + // s3 + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + + // swagger + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' + + // jwt + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' + implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' + + // p6spy + implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.9.0' + + // querydsl + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" } tasks.named('bootBuildImage') { @@ -42,3 +62,8 @@ tasks.named('bootBuildImage') { tasks.named('test') { useJUnitPlatform() } + +//SNAPSHOT 생성 x +jar { + enabled = false +} diff --git a/src/main/java/jikgong/global/config/CorsConfig.java b/src/main/java/jikgong/global/config/CorsConfig.java new file mode 100644 index 0000000..7f621e7 --- /dev/null +++ b/src/main/java/jikgong/global/config/CorsConfig.java @@ -0,0 +1,28 @@ +package jikgong.global.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.List; + +@Configuration +public class CorsConfig { + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration config = new CorsConfiguration(); + + config.setAllowCredentials(true); + config.setAllowedOrigins(List.of("http://localhost:8080rrrrrr")); + config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")); + config.setAllowedHeaders(List.of("*")); + config.setExposedHeaders(List.of("*")); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + return source; + } + +} diff --git a/src/main/java/jikgong/global/config/P6SpySqlFormatter.java b/src/main/java/jikgong/global/config/P6SpySqlFormatter.java new file mode 100644 index 0000000..4181296 --- /dev/null +++ b/src/main/java/jikgong/global/config/P6SpySqlFormatter.java @@ -0,0 +1,38 @@ +package jikgong.global.config; + +import com.p6spy.engine.logging.Category; +import com.p6spy.engine.spy.P6SpyOptions; +import com.p6spy.engine.spy.appender.MessageFormattingStrategy; +import jakarta.annotation.PostConstruct; +import org.hibernate.engine.jdbc.internal.FormatStyle; +import org.springframework.context.annotation.Configuration; + +import java.util.Locale; + +@Configuration +public class P6SpySqlFormatter implements MessageFormattingStrategy { + + @PostConstruct + public void setLogMessageFormat() { + P6SpyOptions.getActiveInstance().setLogMessageFormat(this.getClass().getName()); + } + + @Override + public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql, String url) { + sql = formatSql(category, sql); + return String.format("[%s] | %d ms | %s", category, elapsed, formatSql(category, sql)); + } + + private String formatSql(String category, String sql) { + if (sql != null && !sql.trim().isEmpty() && Category.STATEMENT.getName().equals(category)) { + String trimmedSQL = sql.trim().toLowerCase(Locale.ROOT); + if (trimmedSQL.startsWith("create") || trimmedSQL.startsWith("alter") || trimmedSQL.startsWith("comment")) { + sql = FormatStyle.DDL.getFormatter().format(sql); + } else { + sql = FormatStyle.BASIC.getFormatter().format(sql); + } + return sql; + } + return sql; + } +} \ No newline at end of file diff --git a/src/main/java/jikgong/global/config/RedisConfig.java b/src/main/java/jikgong/global/config/RedisConfig.java new file mode 100644 index 0000000..7a4a5a6 --- /dev/null +++ b/src/main/java/jikgong/global/config/RedisConfig.java @@ -0,0 +1,38 @@ +package jikgong.global.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +public class RedisConfig { + + @Value("${spring.data.redis.port}") + private int port; + + @Value("${spring.data.redis.host}") + private String host; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + return new LettuceConnectionFactory(host, port); + } + + @Bean + public RedisTemplate redisTemplate() { + // redisTemplate를 받아와서 set, get, delete를 사용 + RedisTemplate redisTemplate = new RedisTemplate<>(); + // setKeySerializer, setValueSerializer 설정 + // redis-cli을 통해 직접 데이터를 조회 시 알아볼 수 없는 형태로 출력되는 것을 방지 + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + + return redisTemplate; + } + +} \ No newline at end of file diff --git a/src/main/java/jikgong/global/config/RestTemplateConfig.java b/src/main/java/jikgong/global/config/RestTemplateConfig.java new file mode 100644 index 0000000..67ea5ab --- /dev/null +++ b/src/main/java/jikgong/global/config/RestTemplateConfig.java @@ -0,0 +1,14 @@ +package jikgong.global.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} \ No newline at end of file diff --git a/src/main/java/jikgong/global/config/S3Config.java b/src/main/java/jikgong/global/config/S3Config.java new file mode 100644 index 0000000..ee809bb --- /dev/null +++ b/src/main/java/jikgong/global/config/S3Config.java @@ -0,0 +1,28 @@ +package jikgong.global.config; + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class S3Config { + @Value("${cloud.aws.credentials.access-key}") + private String accessKey; + @Value("${cloud.aws.credentials.secret-key}") + private String secretKey; + @Value("${cloud.aws.region.static}") + private String region; + + @Bean + public AmazonS3Client amazonS3Client() { + BasicAWSCredentials awsCredentials= new BasicAWSCredentials(accessKey, secretKey); + return (AmazonS3Client) AmazonS3ClientBuilder.standard() + .withRegion(region) + .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/jikgong/global/config/SwaggerConfig.java b/src/main/java/jikgong/global/config/SwaggerConfig.java new file mode 100644 index 0000000..0a2b95d --- /dev/null +++ b/src/main/java/jikgong/global/config/SwaggerConfig.java @@ -0,0 +1,38 @@ +package jikgong.global.config; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Arrays; + +@OpenAPIDefinition( + info = @Info(title = "tickerBell API 명세서", + description = "tickerBell API 명세서입니다.", + version = "v1")) +@Configuration +public class SwaggerConfig { + + /** + * http://localhost:8080/swagger-ui/index.html + */ + + // JWT + swagger + @Bean + public OpenAPI openAPI(){ + SecurityScheme securityScheme = new SecurityScheme() + .type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT") + .in(SecurityScheme.In.HEADER).name("Authorization"); + SecurityRequirement securityRequirement = new SecurityRequirement().addList("bearerAuth"); + + return new OpenAPI() + .components(new Components().addSecuritySchemes("bearerAuth", securityScheme)) + .security(Arrays.asList(securityRequirement)); + } + +} \ No newline at end of file diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml new file mode 100644 index 0000000..3cacfbb --- /dev/null +++ b/src/main/resources/application-local.yml @@ -0,0 +1,5 @@ +spring: + data: + redis: + host: localhost + port: 6379 \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 8b13789..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..941f200 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,51 @@ +spring: + profiles: + active: ${environment} + + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: ${DB_ENDPOINT} + username: ${DB_USERNAME} + password: ${DB_PASSWORD} + + jpa: + hibernate: + ddl-auto: create + properties: + hibernate: + default_batch_fetch_size: 1000 + dialect: org.hibernate.dialect.MySQLDialect + +# 배치 관련 설정 +# batch: +# jdbc: +# initialize-schema: always # batch repo 테이블 자동 생성 +# job: +# enabled: false # 시작과 동시에 실행되는 것 방지 + +app: + auth: + secret-key: ${SECRET_KEY} + +cloud: + aws: + credentials: + access-key: ${S3_ACCESS_KEY} + secret-key: ${S3_SECRET_ACCESS_KEY} + s3: + bucket: tickerbell-image + stack.auto: false #기본 cloudFormation 구성 시작 사용 x + region: + static: ap-northeast-2 + +# sms 관련 설정 +#naver-cloud-sms: +# accessKey: ${SMS_ACCESS_KEY} +# secretKey: ${SMS_SECRET_KEY} +# serviceId: ${SMS_SERVICE_ID} +# senderPhone: ${SMS_SENDER_PHONE} + +# 네이버 지도 관련 설정 +#naver-cloud-map: +# api-key-id: ${MAP_API_KEY_ID} +# api-key: ${MAP_API_KEY}