본문 바로가기
해피 코딩/Today I Learned

[TIL 19] Ai가 대세! Gemini API 사용하기

by happy-coding 2024. 9. 1.

 Today I Learned

요즘은  Ai가 대세라고 한다. 현재 진행 중인 팀 프로젝트에 Google Gemini API를 사용하여 제품의 설명을 추천받고 RestTemplate을 사용하여 요청에 대한 응답을 DB에 저장하도록 하자!!
  • 목표
🔥Gemini를 사용하여 제품의 설명글 추천받기, 응답내용 DB에 저장하기

[ Gemini 설정 ]

API 키를 발급

좌측 상단의 Get API key를 클릭하여 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 잼민이를 사용하여 메뉴 등록 시 메뉴의 이름을 추천받고, 이를 팀 프로젝트에 적용시켜 보았습니다. 이로써 우리의 서비스를 사용하는 가게 주인에게 더욱 편리한 서비스를 제공할 수 있게 되었습니다.

 

읽어주셔서 감사합니다 😊