+ (25.10.13) ๋ฒ์ ์ด ์ ๊ทธ๋ ์ด๋๋ ์ดํ API ํธ์ถ ์ฃผ์๊ฐ ๋ณ๊ฒฝ๋ ๊ฒ ๊ฐ๋ค.
ํ์ธํด ๋ณด๋ ๊ธฐ์กด APIํค๋ ๋ชจ๋ ์ญ์ ๋์๋ค.
https://ai.google.dev/gemini-api/docs/quickstart?hl=ko
Gemini API ๋น ๋ฅธ ์์ | Google AI for Developers
๊ฐ๋ฐ์๋ฅผ ์ํ Gemini API ์์ํ๊ธฐ
ai.google.dev
ํด๋น ์ฌ์ดํธ์ ์ ์ํ๋ฉด ์ฐ๋ ๋ฐฉ๋ฒ์ด ์์ธํ ์๋ด๋์ด์๋ค.
์๋ ์ฝ๋๋ฅผ ์ฐธ๊ณ ํ๋ฉด์ ์ API ์ฃผ์๋ก ํ ์คํธํด๋ณด๋ฉด ๋ ๊ฒ ๊ฐ๋ค.

๋ญ๊ฐ AI์ฑ๋ด์ ๋ง๋ค์ด๋ณด๊ณ ์ถ์ด์
๊ฐ์ธ์ ์ผ๋ก ๋ง๋ค์ด๋์ ๋ค์ด์ด๋ฆฌ์ ์ด๋ค๊ธฐ๋ฅ์ ๋ฃ์ด๋ณผ๊นํ๋ค๊ฐ
๋จ์ด์ฅ์ ๋ฒ์ญ๊ธฐ ๊ธฐ๋ฅ์ ๋ฃ์ผ๋ฉด ์ข๊ฒ ๋ค ์๊ฐํด์
์ ๋ฏธ๋์ด๊ฐ ๋ฒ์ญํด์ฃผ๋ ๋ฒ์ญ๊ธฐ๋ฅผ ์ถ๊ฐํด๋ณด์๋ค.
์๋๋ GPT์จฉ์ ์ฐ๊ณ ์ถ์์ง๋ง ใ ใ ์ ๋ฃ๋ก ๋ฐ๋์ด์..
๋ฌด๋ฃํค๋ผ...์๋ต๋๋๋ฆฌ๊ณ ..๊ทธ๋ ์ง๋ง..๋ฌด๋ฃ๋๊น............
๋ฒ์ญ๊ธฐ๋ ์์ ๋ด์ฉ์ด ๋ถ๋๋ฌ์ฐ๋๊น ๋ค๋ฅธ ์์์ฝ๋๋ก..
๊ฐ๋จํ๊ฒ ํ ์คํธํด๋ณด๋ ์ฝ๋๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ๋ค.
๋จผ์ ์ ๋ฏธ๋์ด apiํค๋ฅผ ๋ฐ๊ธ๋ฐ์์ผ ํ๋ค.
https://aistudio.google.com/apikey
๋ก๊ทธ์ธ - Google ๊ณ์
์ด๋ฉ์ผ ๋๋ ํด๋์ ํ
accounts.google.com
์ฌ๊ธฐ์ ๋ก๊ทธ์ธ์ ํ ํ APIํค๋ฅผ ๋ฌด๋ฃ๋ก ๋ฐ๊ธ๋ฐ์ผ๋ฉด ๋๊ณ ์ด๊ฒ ๋์ด ์๋๋ผ
์ ๋ฏธ๋์ด api๋ฅผ ํ์ฑํด์ฃผ์ด์ผ ๋น๋ก์ ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค.
https://console.cloud.google.com/apis/
Google ํด๋ผ์ฐ๋ ํ๋ซํผ
๋ก๊ทธ์ธ Google ํด๋ผ์ฐ๋ ํ๋ซํผ์ผ๋ก ์ด๋
accounts.google.com
๊ทธ๊ฑด ์ฌ๊ธฐ์์ ๋ก๊ทธ์ธ์ ํ๋ค์


gemini api๋ฅผ ์ ํํด์ฃผ๊ณ
์๋ง ๋ฒํผ์ด ์์ํ ๋ฐ ํ์ฑ์ด์๋์ง ์ฌ์ฉ์ด์๋์ง ๊ธฐ์ต์ด ์๋์ง๋ง
๊ทธ ๋ฒํผ์ ๋๋ฌ์ฃผ๋ฉด

์ง์ ์ด๋ ๊ฒ ๋ณํ๋ค.
์ด๋ฌ๋ฉด ์ด์ gemini api๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋๊ฒ์ด๋ค.
์๊น ๋ดค๋ get api key์ฌ์ดํธ์ ๋ค์ด๊ฐ๋ณด๋ฉด
curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent" \
-H 'Content-Type: application/json' \
-H 'X-goog-api-key: GEMINI_API_KEY' \
-X POST \
-d '{
"contents": [
{
"parts": [
{
"text": "Explain how AI works in a few words"
}
]
}
]
}'
์ด๋ฐ์์ผ๋ก ์ฌ์ฉ๋ฐฉ๋ฒ์ด ๋์์๋ค.
๊ทธ๋๋ก postman์์ ํ์ธํด๋ณด๋ฉด ์๋ต์ด ์ด๋ป๊ฒ ์ค๋์ง ์์์์๊ฒ์ด๋ค.

์์ด์ api ํค๋ก ์ ๋ฏธ๋์ด์ ์๊ธฐ๋ ํด๋ดค๊ฒ ๋ค ๋ณธ๊ฒฉ์ ์ผ๋ก ์ฐ๋์ ํด๋ณด์.
build.gradle์ ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํด์ค๋ค.
implementation 'org.springframework.boot:spring-boot-starter-webflux'
Spring ๋น๋๊ธฐ HTTP ํด๋ผ์ด์ธํธ๋ค.
๋์ถฉ ์ด๋ฐ ์ญํ ์ด๋ผ๊ณ ํ๋ค.
spring-boot-starter-webflux๋ WebClient ๋ฅผ ์ฐ๊ธฐ ์ํด ํ์ํ ์์กด์ฑ
REST API ์์ฒญ์ ๋น๋๊ธฐ๋ก ๋ ๋ฆฌ๊ณ , ์๋ต์ ์ด๋ฒคํธ์ฒ๋ผ ๋ฐ์ (์ฑ๋ฅ ํจ์จ์ )
application.properties์๋ api key๋ฅผ ๋ฃ์ด์ค๋ค.
# Gemini KEY
gemini.api.key=YOUR_KEY
GeminiSimpleController
@RestController
public class GeminiSimpleController {
private final WebClient webClient = WebClient.create("https://generativelanguage.googleapis.com");
@Value("${gemini.api.key}")
private String apiKey;
@PostMapping("/gemini/simple")
public Mono<String> callGemini(@RequestBody String input) {
Map<String, Object> body = Map.of(
"contents", List.of(Map.of(
"parts", List.of(Map.of("text", "๊ฐ๋จํ ๋๋ตํด์ค:\n\n" + input))
))
);
return webClient.post()
.uri(uriBuilder -> uriBuilder
.path("/v1beta/models/gemini-1.5-flash-latest:generateContent")
.queryParam("key", apiKey)
.build())
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(body)
.retrieve()
.bodyToMono(String.class);
}
}
TestController
package org.example.test.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class TestController {
@GetMapping("/test")
public String test() {
return "test";
}
}
test.html (ํ์๋ฆฌํ)
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Gemini ํ
์คํธ</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/css/output.css" rel="stylesheet" th:href="@{/css/output.css}">
</head>
<body class="bg-gray-100 min-h-screen p-6">
<div class="max-w-md mx-auto bg-white p-6 rounded shadow space-y-4">
<h1 class="text-xl font-bold text-center text-gray-800">Gemini ๊ฐ๋จ ํ
์คํธ</h1>
<!-- ์
๋ ฅ ํผ -->
<form id="chat-form" class="flex gap-2">
<input type="text" id="chat-input" class="flex-1 border rounded p-2" placeholder="์ง๋ฌธ ์
๋ ฅ..." required>
<button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">๋ณด๋ด๊ธฐ</button>
</form>
<!-- ์ฑํ
๋ฐ์ค -->
<div id="chat-box" class="h-64 overflow-y-auto border rounded p-3 bg-gray-50 space-y-2 text-sm">
<!-- ๋ฉ์์ง ์ถ๋ ฅ๋จ -->
</div>
</div>
<script th:inline="javascript">
const form = document.getElementById("chat-form");
const input = document.getElementById("chat-input");
const chatBox = document.getElementById("chat-box");
form.addEventListener("submit", async (e) => {
e.preventDefault();
const userMsg = input.value.trim();
if (!userMsg) return;
appendMessage("๋", userMsg);
input.value = "";
try {
const res = await fetch(/*[[${'/gemini/simple'}]]*/ "/gemini/simple", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(userMsg)
});
const data = await res.text();
appendMessage("Gemini", data);
} catch (err) {
appendMessage("Gemini", "(์๋ฌ ๋ฐ์: " + err.message + ")");
}
});
function appendMessage(sender, message) {
const div = document.createElement("div");
div.innerHTML = `<strong>${sender}:</strong> <div class="whitespace-pre-line">${message}</div>`;
chatBox.appendChild(div);
chatBox.scrollTop = chatBox.scrollHeight;
}
</script>
</body>
</html>
๊ฒฐ๊ณผํ๋ฉด

๋ง์ฝ text๋ง ํ์ฑํด์ ์๋ ํ์ธ์!๋ง ๊น๋ํ๊ฒ ์ถ์ถํ๊ณ ์ถ์ผ๋ฉด
const data = await res.text();
let reply = "(์๋ต ํ์์ด ์ด์ํฉ๋๋ค)";
try {
const json = JSON.parse(data);
const text = json.candidates?.[0]?.content?.parts?.[0]?.text;
if (text) reply = text;
} catch (err) {
reply = "(JSON ํ์ฑ ์คํจ)";
}
appendMessage("Gemini", reply);
์ด๋ฐ์์ผ๋ก ํ์ฑํด์ ๋ฃ์ผ๋ฉด ๋๋ค~