Google Gemini 는 지난 12월 6일 공개된 따끈따끈한 기술이다!
멀티모달로 설계되어 텍스트, 이미지, 동영상, 코드 등 다양한 유형의 정보를 일반화하고, 원활하게 이해하여 여러 정보를 동시에 조합하여 활용할 수 있다고 한다.
Gemini API의 공식문서는 이곳에서 확인할 수 있다.
Gemini Pro를 사용하게 된 이유는 토큰 한도가 GPT-3.5 Turbo 모델에 비해 약 7배는 더 많았기 때문이다.

Gemini Pro는 입력 토큰 한도가 30720 tokens 이지만, GPT-3.5 Turbo는 Context Window가 4096 tokens 라 많은 정보를 처리하기엔 Gemini Pro가 더 적합하다고 느껴졌다.
게다가 Gemini Pro는 분당 60개의 요청까지는 무료이다!
공식 문서에서는 Gemini API를 Python, Node.js, Web ••• 등 다양한 플랫폼에서 사용하는 튜토리얼을 소개해 주고 있다.
하지만, Spring Boot 에서 사용하는 방법은 없어서 REST API 튜토리얼을 참고하여 본 블로그에서는 Spring에서 Gemini Pro를 사용하는 방법을 기술해보려고 한다 🤗

API 키는 이곳에 접속하여 발급받을 수 있다. 발급받는 과정은 생략하고, 바로 스프링에서 키를 등록하는 방법부터 소개하려고 한다.
(키를 발급받을 때는 꼭 조직 계정이 아닌 @gmail.com 으로 끝나는 계정으로 가입해야 한다!)
application.yml 에 다음 내용을 추가한 후,
gemini:
api:
url: ${GEMINI_URL}
key: ${GEMINI_KEY}
application.properties 에는 값을 입력해 주면 된다.
# Gemini
GEMINI_URL=https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent
GEMINI_KEY=(YOUR_GEMINI_KEY)
이제 이 값들은 @Value 어노테이션으로 주입받아 사용할 것이다.
Gemini API에 요청을 보내기 위해 RestTemplate을 설정하는 클래스를 생성해야 한다.
// GeminiRestTemplateConfig.java
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
@RequiredArgsConstructor
public class GeminiRestTemplateConfig {
@Bean
@Qualifier("geminiRestTemplate")
public RestTemplate geminiRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add((request, body, execution) -> execution.execute(request, body));
return restTemplate;
}
}
먼저, 공식문서를 참고하여 REST API로 요청을 보내보고 응답이 어떻게 오는지 확인해 보자.

요청 url에는 쿼리 파라미터로 API 키를 전송해야 한다.
https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent?key=(YOUR_API_KEY)
그리고 Request Body 와 Response Body 의 구조는 아래와 같다.
// Request Body
{
"contents": [
{
"parts":
{
"text": "안녕! 너는 누구야?"
}
}
],
"generationConfig": {
"candidate_count":1,
"max_output_tokens":1000,
"temperature": 0.7
}
}
// Response Body
{
"candidates": [
{
"content": {
"parts": [
{
"text": "저는 대규모 언어 모델로 Google에서 개발했습니다."
}
],
"role": "model"
},
"finishReason": "STOP",
"index": 0,
"safetyRatings": [
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_HARASSMENT",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"probability": "NEGLIGIBLE"
}
]
}
],
"promptFeedback": {
"safetyRatings": [
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_HARASSMENT",
"probability": "LOW"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"probability": "NEGLIGIBLE"
}
]
}
}
이제 이 구조를 참고하여 요청을 보낼 DTO와 응답을 받아올 DTO를 생성해 보자.
요청 DTO 코드는 다음과 같다.
// ChatRequest.java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ChatRequest {
private List<Content> contents;
private GenerationConfig generationConfig;
@Getter @Setter
public static class Content {
private Parts parts;
}
@Getter @Setter
public static class Parts {
private String text;
}
@Getter @Setter
public static class GenerationConfig {
private int candidate_count;
private int max_output_tokens;
private double temperature;
}
public ChatRequest(String prompt) {
this.contents = new ArrayList<>();
Content content = new Content();
Parts parts = new Parts();
parts.setText(prompt);
content.setParts(parts);
this.contents.add(content);
this.generationConfig = new GenerationConfig();
this.generationConfig.setCandidate_count(1);
this.generationConfig.setMax_output_tokens(1000);
this.generationConfig.setTemperature(0.7);
}
}
다음은 응답 DTO 코드이다.
// ChatResponse.java
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ChatResponse {
private List<Candidate> candidates;
private PromptFeedback promptFeedback;
@Getter
@Setter
public static class Candidate {
private Content content;
private String finishReason;
private int index;
private List<SafetyRating> safetyRatings;
}
@Getter @Setter
@ToString
public static class Content {
private List<Parts> parts;
private String role;
}
@Getter @Setter
@ToString
public static class Parts {
private String text;
}
@Getter @Setter
public static class SafetyRating {
private String category;
private String probability;
}
@Getter @Setter
public static class PromptFeedback {
private List<SafetyRating> safetyRatings;
}
}
Controller에서는 Get /gemini/chat 으로 요청만 받아오고, Gemini에 요청을 보내는 로직은 모두 Servie에서 구현했다.
// GeminiController.java
@RestController
@RequiredArgsConstructor
@RequestMapping("/gemini")
public class GeminiController {
private final GeminiService geminiService;
@GetMapping("/chat")
public ResponseEntity<?> gemini() {
try {
return ResponseEntity.ok().body(geminiService.getContents("안녕! 너는 누구야?"));
} catch (HttpClientErrorException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
}
서비스에서는 앞서 확인한 요청 url을 구성하고, RestTemplate 을 이용하여 Gemini API로 요청을 보냈다.
// GeminiService.java
@Service
@RequiredArgsConstructor
public class GeminiService {
@Qualifier("geminiRestTemplate")
@Autowired
private RestTemplate restTemplate;
@Value("${gemini.api.url}")
private String apiUrl;
@Value("${gemini.api.key}")
private String geminiApiKey;
public String getContents(String prompt) {
// Gemini에 요청 전송
String requestUrl = apiUrl + "?key=" + geminiApiKey;
ChatRequest request = new ChatRequest(prompt);
ChatResponse response = restTemplate.postForObject(requestUrl, request, ChatResponse.class);
String message = response.getCandidates().get(0).getContent().getParts().get(0).getText().toString();
return message;
}
}
이렇게 모두 구현한 후, 포스트맨으로 요청을 보내보면 다음과 같은 응답이 오는 걸 확인할 수 있다!

프로젝트의 비즈니스 로직에 맞게 prompt 에 DB에서 불러온 데이터를 추가하여 처리하면 다양하게 활용할 수 있을 것이다 🙌🏻
이렇게 구현한 방식이 굉장히 비효율적일 수도 있고, 잘못된 부분이 있을 수도 있다...! 🙃
다른 방법으로는 dependency를 추가하여 구현하는 방법도 있는 것 같다.
프로젝트에서 Gemini를 이용해야 하는데 어떻게 구현해야 할지 막막한 사람들에게 도움이 되었으면 한다! 👍🏻
너무 감사합니다!!