Today I Learned
요즘은 Ai가 대세라고 한다. 현재 진행 중인 팀 프로젝트에 Google Gemini API를 사용하여 제품의 설명을 추천받고 RestTemplate을 사용하여 요청에 대한 응답을 DB에 저장하도록 하자!!
- 목표
🔥Gemini를 사용하여 제품의 설명글 추천받기, 응답내용 DB에 저장하기
[ Gemini 설정 ]
API 키를 발급
- https://aistudio.google.com/ 에 들아가서 API 키를 발급 받습니다.
application.yml
RequestDto
@NoArgsConstructor
@Setter
@Getter
public class GeminiRequest {
private List<Content> contents;
@Getter
@NoArgsConstructor
@AllArgsConstructor
private static class Content {
private List<TextPart> parts;
}
interface Part {}
@Getter
@NoArgsConstructor
@AllArgsConstructor
private static class TextPart implements Part {
public String text;
}
@Getter
@AllArgsConstructor
private static class InlineDataPart implements Part {
public InlineData inlineData;
}
@Getter
@AllArgsConstructor
public static class InlineData {
private String mimeType;
private String data;
}
}
ResponseDto
@NoArgsConstructor
@Getter
public class GeminiResponse {
private List<Candidate> candidates;
@Getter
public static class Candidate {
private Content content;
private String finishReason;
private int index;
List<SafetyRating> safetyRatings;
}
@Getter
public static class Content {
private List<TextPart> parts;
private String role;
}
@Getter
public static class TextPart {
private String text;
}
@Getter
public static class SafetyRating {
private String category;
private String probability;
}
}
Interface
@HttpExchange("/v1beta/models/")
public interface GeminiInterface {
@PostExchange("gemini-1.5-flash-latest:generateContent")
GeminiResponse getCompletion(
@PathVariable String model,
@RequestBody GeminiRequest request
);
}
AppConfig
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public RestClient geminiRestClient(@Value("${gemini.baseurl}") String baseUrl,
@Value("${googleai.api.key}") String apiKey) {
return RestClient.builder()
.baseUrl(baseUrl)
.defaultHeader("x-goog-api-key", apiKey)
.defaultHeader("Content-Type", "application/json")
.defaultHeader("Accept", "application/json")
.build();
}
@Bean
public GeminiInterface geminiInterface(@Qualifier("geminiRestClient") RestClient client) {
RestClientAdapter adapter = RestClientAdapter.create(client);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
return factory.createClient(GeminiInterface.class);
}
[ 결과 확인 ]
- 만약 실제 google cloud에 결제가 연동되어있다면 잦은요청으로 실제 비용이 발생할 수 있으니 주의하도록 합시다!
curl \
-H 'Content-Type: application/json' \
-d '{"contents":[{"parts":[{"text":"만두 상품의 이름을 추천해줘"}]}]}' \
-X POST 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=YOUR_API_KEY'
Headers
- "Content-Type : application/json" 추가
- Default로 들어가 있지만 혹시 없을 경우 추가해 주도록 합시다.
요청 및 응답
현재의 상태는 우리가 잼민이(Gemini)에게 요청을 보내면 잼민이가 바로 응답을 해주는 상태입니다.
즉, Spring을 거치지 않고 응답을 해주는 상태라고 볼 수 있습니다.
🤔 그렇다면 응답의 내용을 DB에 저장하고 싶다면 어떻게 해야할까?
💁 RestTemplate을 사용해 Gemini API를 호출하고 응답을 DB에 저장해 보도록 하자!
[ 응답내용 DB에 저장하기 ]
Controller
@RestController
@RequiredArgsConstructor
public class GeminiController {
private final GeminiService geminiService;
@PostMapping("/ai/recommend")
public GeminiResponse getGeminiCompletion(
@RequestBody
GeminiRequest request
) {
// 서비스에서 Gemini API 호출하고 응답 받기
return geminiService.RequestAndResponse(request);
}
}
Service
- RestTemplate을 사용하여 Gemini API를 호출한다.
- 요청 및 응답
- restTemplate.exchage()를 통하여 요청 및 응답을 받는다
- 응답받은 내용을 저장한다 / saveRequestResponse메서드 생성
- 응답받은 내용을 전부 저장하면 DataIntegrityViolationException 에러가 발생합니다.
- DataIntegrityViolationException: 자료형에 너무 긴 자료를 담으려고 할 때 발생합니다.
- 우리는 DB에 저장되는 모습만 확인하면 되므로 .substring() 메서드를 사용하여 저장하도록 합시다.
@Service
@RequiredArgsConstructor
public class GeminiService {
private final GeminiRepository geminiRepository;
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
@Value("${googleai.api.key}")
private String apiKey;
// 서비스에서 Gemini API 호출하고 응답 받기
public GeminiResponse RequestAndResponse(GeminiRequest request) {
String uri = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON); // Request Body를 사용
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.set("x-goog-api-key", apiKey);
HttpEntity<GeminiRequest> entity = new HttpEntity<>(request, headers);
ResponseEntity<GeminiResponse> response = restTemplate.exchange(uri, HttpMethod.POST, entity, GeminiResponse.class);
// 요청과 응답을 DB에 저장하는 메서드
saveRequestResponse(request, response);
return response.getBody();
}
// 요청과 응답을 DB에 저장하는 메서드
private void saveRequestResponse(GeminiRequest request, ResponseEntity<GeminiResponse> response) {
try {
// 요청 내용을 JSON 문자열로 변환
String requestString = objectMapper.writeValueAsString(request.getContents());
// 응답 내용을 JSON 문자열로 변환
String responseString = objectMapper.writeValueAsString(response.getBody());
// JSON 문자열을 Java에서 사용 가능하도록 역직렬화
GeminiResponse geminiResponse = objectMapper.readValue(responseString, GeminiResponse.class);
String text = geminiResponse.getCandidates().get(0).getContent().getParts().get(0).getText();
text = text.replaceAll("[\\r\\n*]", "").substring(0,50);
//DB에 저장
geminiRepository.save(new Gemini(requestString, text));
} catch (Exception e) {
// 변환 또는 저장 중 에러가 발생하면 출력
e.printStackTrace();
}
}
}
[ 결과 확인 ]
요청 및 응답
DB 확인
AI API 잼민이를 사용하여 메뉴 등록 시 메뉴의 이름을 추천받고, 이를 팀 프로젝트에 적용시켜 보았습니다. 이로써 우리의 서비스를 사용하는 가게 주인에게 더욱 편리한 서비스를 제공할 수 있게 되었습니다.
읽어주셔서 감사합니다 😊
'해피 코딩 > Today I Learned' 카테고리의 다른 글
[TIL 21] 팀 프로젝트 ERD 다시 그려보기 (0) | 2024.09.04 |
---|---|
[TIL 20] Redis를 사용하여 로그아웃 블랙리스트 처리하기 (0) | 2024.09.03 |
[TIL 18] 프로젝트에 Swagger 적용하기 (4) | 2024.09.01 |
[TIL 17] 프로젝트 중간 회고 (0) | 2024.08.29 |
[TIL 16] 스파르타 첫 번째 팀 프로젝트 시작 (1) | 2024.08.23 |