[Spring Boot / Java17] Gemini Pro 사용하기

hh·2024년 2월 10일
post-thumbnail

Gemini?

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를 사용하는 방법을 기술해보려고 한다 🤗

Spring에서 Gemini Pro 사용하기

API 키 등록하기

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 어노테이션으로 주입받아 사용할 것이다.

RestTemplate Config 생성

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 BodyResponse 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, 응답 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 & Service

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;
    }
}

결과 확인

이렇게 모두 구현한 후, 포스트맨으로 요청을 보내보면 다음과 같은 응답이 오는 걸 확인할 수 있다!

프로젝트의 비즈니스 로직에 맞게 promptDB에서 불러온 데이터를 추가하여 처리하면 다양하게 활용할 수 있을 것이다 🙌🏻

마치며

이렇게 구현한 방식이 굉장히 비효율적일 수도 있고, 잘못된 부분이 있을 수도 있다...! 🙃

다른 방법으로는 dependency를 추가하여 구현하는 방법도 있는 것 같다.

프로젝트에서 Gemini를 이용해야 하는데 어떻게 구현해야 할지 막막한 사람들에게 도움이 되었으면 한다! 👍🏻

1개의 댓글

comment-user-thumbnail
2024년 5월 5일

너무 감사합니다!!

답글 달기