From 721e22298143391cc37d9b11c97046f3290d3177 Mon Sep 17 00:00:00 2001 From: sohyeonjung Date: Sat, 28 Jun 2025 15:21:52 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[feat]=20#29=20redis=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../kernellabs/global/config/RedisConfig.java | 41 +++++++++++++++++++ .../kernellabs/global/util/RedisUtil.java | 35 ++++++++++++++++ src/main/resources/application.yml | 6 +++ 4 files changed, 83 insertions(+) create mode 100644 src/main/java/com/kernellabs/kernellabs/global/config/RedisConfig.java create mode 100644 src/main/java/com/kernellabs/kernellabs/global/util/RedisUtil.java diff --git a/build.gradle b/build.gradle index 2977bf9..a706060 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-webflux' implementation 'com.google.genai:google-genai:1.6.0' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/src/main/java/com/kernellabs/kernellabs/global/config/RedisConfig.java b/src/main/java/com/kernellabs/kernellabs/global/config/RedisConfig.java new file mode 100644 index 0000000..9c9680a --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/global/config/RedisConfig.java @@ -0,0 +1,41 @@ +package com.kernellabs.kernellabs.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.RedisStandaloneConfiguration; +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.host}") + private String host; + + @Value("${spring.data.redis.port}") + private int port; + + @Value("${spring.data.redis.password}") + private String password; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(host, port); + redisStandaloneConfiguration.setHostName(host); + redisStandaloneConfiguration.setPort(port); + redisStandaloneConfiguration.setPassword(password); + return new LettuceConnectionFactory(redisStandaloneConfiguration); + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(redisConnectionFactory()); + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(new StringRedisSerializer()); + return template; + } +} \ No newline at end of file diff --git a/src/main/java/com/kernellabs/kernellabs/global/util/RedisUtil.java b/src/main/java/com/kernellabs/kernellabs/global/util/RedisUtil.java new file mode 100644 index 0000000..6a84fcd --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/global/util/RedisUtil.java @@ -0,0 +1,35 @@ +package com.kernellabs.kernellabs.global.util; + +import java.time.Duration; + +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Service; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class RedisUtil { + + private final StringRedisTemplate redisTemplate; + + public String getData(String key) { + ValueOperations valueOperations = redisTemplate.opsForValue(); + return valueOperations.get(key); + } + + public boolean existData(String key) { + return Boolean.TRUE.equals(redisTemplate.hasKey(key)); + } + + public void setDataExpire(String key, String value, long duration) { + ValueOperations valueOperations = redisTemplate.opsForValue(); + Duration expireDuration = Duration.ofSeconds(duration); + valueOperations.set(key, value, expireDuration); + } + + public void deleteData(String key) { + redisTemplate.delete(key); + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8765a96..417b50e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -15,6 +15,12 @@ spring: properties: hibernate: format_sql: true + + data: + redis: + host: ${REDIS_HOST} + password: ${REDIS_PASSWORD} + port: 6379 logging: level: root: INFO # 전체 로그 최소 INFO 이상 기록 :contentReference[oaicite:0]{index=0} From d856e8c3c71242ccb543d012619fdd0d3f60eb5f Mon Sep 17 00:00:00 2001 From: sohyeonjung Date: Sat, 28 Jun 2025 15:26:06 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[feat]=20#29=20gemini=20search=20policy=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kernellabs/application/PolicyService.java | 41 +++++++++++++++++++ .../external/GeminiSearchClient.java} | 6 +-- .../controller/GeminiController.java | 6 +-- .../controller/PolicyController.java | 27 ++++++++++++ .../dto/response/PolicyResponse.java | 10 +++++ 5 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/kernellabs/kernellabs/application/PolicyService.java rename src/main/java/com/kernellabs/kernellabs/{application/GeminiService.java => infrastructure/external/GeminiSearchClient.java} (91%) create mode 100644 src/main/java/com/kernellabs/kernellabs/presentation/controller/PolicyController.java create mode 100644 src/main/java/com/kernellabs/kernellabs/presentation/dto/response/PolicyResponse.java diff --git a/src/main/java/com/kernellabs/kernellabs/application/PolicyService.java b/src/main/java/com/kernellabs/kernellabs/application/PolicyService.java new file mode 100644 index 0000000..55af016 --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/application/PolicyService.java @@ -0,0 +1,41 @@ +package com.kernellabs.kernellabs.application; + +import org.springframework.stereotype.Service; + +import com.kernellabs.kernellabs.global.util.RedisUtil; +import com.kernellabs.kernellabs.infrastructure.external.GeminiApiClient; +import com.kernellabs.kernellabs.infrastructure.external.GeminiSearchClient; +import com.kernellabs.kernellabs.presentation.dto.response.PolicyResponse; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class PolicyService { + + private final String POLICY_REDIS_KEY = "policy"; + private final RedisUtil redisUtil; + private final GeminiSearchClient geminiSearchClient; + private final String prompt = """ + 의성군으로 이주하려는 사람들을 위한 최신 혜택 정보를 알려줘. + 주거, 교육, 귀농귀촌, 복지, 창업, 일자리 지원금, 정착금 등 모든 종류의 이주 및 정착 혜택을 포함해줘. + 각 혜택별로 지원 조건, 신청 방법, 담당 부서 또는 관련 웹사이트 링크 같은 상세 정보도 알려줘. + 정보를 대주제와 소주제로 나눠서 정리해줘. + 각 소주제 아래에 자세한 설명을 추가하고, 관련 링크가 있다면 URL 주소를 명확히 포함해줘. + 각 항목은 줄 바꿈(\n)을 사용해서 구분해줘. + 다른 지역 정보나 일반적인 내용은 제외하고, 오직 의성군 관련 혜택만 다뤄줘. + """; + + public PolicyResponse getCurrentPolicy() { + String result = ""; + if(redisUtil.getData(POLICY_REDIS_KEY).isEmpty()){ + result = geminiSearchClient.generateAnswer(prompt); + redisUtil.setDataExpire(POLICY_REDIS_KEY, result, 1000); + } + else{ + result = redisUtil.getData(POLICY_REDIS_KEY); + } + return PolicyResponse.builder().policy(result).build(); + + } +} diff --git a/src/main/java/com/kernellabs/kernellabs/application/GeminiService.java b/src/main/java/com/kernellabs/kernellabs/infrastructure/external/GeminiSearchClient.java similarity index 91% rename from src/main/java/com/kernellabs/kernellabs/application/GeminiService.java rename to src/main/java/com/kernellabs/kernellabs/infrastructure/external/GeminiSearchClient.java index e10234e..b53be79 100644 --- a/src/main/java/com/kernellabs/kernellabs/application/GeminiService.java +++ b/src/main/java/com/kernellabs/kernellabs/infrastructure/external/GeminiSearchClient.java @@ -1,4 +1,4 @@ -package com.kernellabs.kernellabs.application; +package com.kernellabs.kernellabs.infrastructure.external; import autovalue.shaded.com.google.common.collect.ImmutableList; import com.google.genai.Client; @@ -10,13 +10,13 @@ import org.springframework.stereotype.Service; @Service -public class GeminiService { +public class GeminiSearchClient { private final Client client; private final Tool googleSearchTool; private final String modelName; - public GeminiService( + public GeminiSearchClient( @Value("${gemini.api.key}") String apiKey, @Value("${gemini.model:gemini-2.5-flash}") String modelName ) { diff --git a/src/main/java/com/kernellabs/kernellabs/presentation/controller/GeminiController.java b/src/main/java/com/kernellabs/kernellabs/presentation/controller/GeminiController.java index 1ac63e8..67575ae 100644 --- a/src/main/java/com/kernellabs/kernellabs/presentation/controller/GeminiController.java +++ b/src/main/java/com/kernellabs/kernellabs/presentation/controller/GeminiController.java @@ -1,6 +1,6 @@ package com.kernellabs.kernellabs.presentation.controller; -import com.kernellabs.kernellabs.application.GeminiService; +import com.kernellabs.kernellabs.infrastructure.external.GeminiSearchClient; import jakarta.validation.Valid; import lombok.AllArgsConstructor; import lombok.Data; @@ -15,11 +15,11 @@ @RequestMapping("/api/genie") @AllArgsConstructor public class GeminiController { - private final GeminiService geminiService; + private final GeminiSearchClient geminiSearchClient; @PostMapping("/chat") public ResponseEntity chat(@Valid @RequestBody ChatRequest req) { - String answer = geminiService.generateAnswer(req.getPrompt()); + String answer = geminiSearchClient.generateAnswer(req.getPrompt()); return ResponseEntity.ok(new ChatResponse(answer)); } diff --git a/src/main/java/com/kernellabs/kernellabs/presentation/controller/PolicyController.java b/src/main/java/com/kernellabs/kernellabs/presentation/controller/PolicyController.java new file mode 100644 index 0000000..55505d3 --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/presentation/controller/PolicyController.java @@ -0,0 +1,27 @@ +package com.kernellabs.kernellabs.presentation.controller; + +import java.util.List; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.kernellabs.kernellabs.application.PolicyService; +import com.kernellabs.kernellabs.global.common.ApiResponse; +import com.kernellabs.kernellabs.presentation.dto.response.PolicyResponse; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/policies") +@RequiredArgsConstructor +public class PolicyController { + private final PolicyService policyService; + + @GetMapping("") + public ResponseEntity> getCurrentPolicy() { + PolicyResponse policyResponse = policyService.getCurrentPolicy(); + return ResponseEntity.ok(ApiResponse.success(policyResponse)); + } +} diff --git a/src/main/java/com/kernellabs/kernellabs/presentation/dto/response/PolicyResponse.java b/src/main/java/com/kernellabs/kernellabs/presentation/dto/response/PolicyResponse.java new file mode 100644 index 0000000..197ad56 --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/presentation/dto/response/PolicyResponse.java @@ -0,0 +1,10 @@ +package com.kernellabs.kernellabs.presentation.dto.response; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class PolicyResponse { + private String policy; +} From f569f33fcd9ed59d5b8b18f038707075341b3f61 Mon Sep 17 00:00:00 2001 From: HoyiTT Date: Sat, 28 Jun 2025 15:37:51 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[feat]#29=20=EC=8B=A4=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EB=A0=88=EB=94=94=EC=8A=A4=20=EC=BA=90?= =?UTF-8?q?=EC=8B=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kernellabs/kernellabs/application/PolicyService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/kernellabs/kernellabs/application/PolicyService.java b/src/main/java/com/kernellabs/kernellabs/application/PolicyService.java index 55af016..2498692 100644 --- a/src/main/java/com/kernellabs/kernellabs/application/PolicyService.java +++ b/src/main/java/com/kernellabs/kernellabs/application/PolicyService.java @@ -28,12 +28,12 @@ public class PolicyService { public PolicyResponse getCurrentPolicy() { String result = ""; - if(redisUtil.getData(POLICY_REDIS_KEY).isEmpty()){ - result = geminiSearchClient.generateAnswer(prompt); - redisUtil.setDataExpire(POLICY_REDIS_KEY, result, 1000); + if(redisUtil.existData(POLICY_REDIS_KEY)){ + result = redisUtil.getData(POLICY_REDIS_KEY); } else{ - result = redisUtil.getData(POLICY_REDIS_KEY); + result = geminiSearchClient.generateAnswer(prompt); + redisUtil.setDataExpire(POLICY_REDIS_KEY, result, 10800); } return PolicyResponse.builder().policy(result).build();