다소 제목이 거창하지만
구글에 자료가 너무 없어서 챗지피티를 질질 붙잡고 몇 시간 만에 해결했다...
우선 내가 구성한 프로젝트의 흐름은
Vision api 불러서 ocr 기능으로 카페 메뉴판 인식
-> 추출된 메뉴 중 사용자에게 맞는 음료 추천을 위해 Gemini api 호출
이때 Gemini를 호출하는 건 단순히 지정된 프롬프트 형식
(이 중에 당이 가장 낮은 / 적절한 메뉴를 추천해 줘)
였기 때문에 단순히 Gemini만 부르면 된다고 생각했다
그런데 계속되는 404 not found
알고 보니 Gemini pro, 즉 지금 사용되고 있는 모델을 호출하려면
Vertex ai도 필수로 사용해야 된다고 한다 (거쳐서 호출하나봄)
내가 모델을 만드는 건 아니라 필요없을 줄 알았는데 이상한 데서 뒤통수 맞음
원래는 Vision API 권한이 있는 사용자의 키를 google credentials에 넣어 뒀었는데
삭제하고 Vertex 권한과 통합했다.
일단 google cloud console에 api 라이브러리 들어가서
vision 이랑 vertex api 다 사용 누르고,
IAM 및 관리자 - 서비스 계정에서 새로운 계정을 만들고
한 사용자한테 이 권한을 다 넣어줬다
(Vertex AI 사용자, Vision AI 관리자 권한을 추가해 줌)
그리고 키 추가 - json으로 파일을 생성해서
인텔리제이 내 환경변수 설정에서 GOOGLE_APPLICATION_CREDENTIALS 에 넣어준다.
이렇게 필요한 키를 넣어뒀다
이제 총 흐름을 보자
Vision api에서 메뉴를 읽어오면,
아래의 RecommendService에서 Gemini에 보낼 프롬프트를 작성하고 요청을 보낸다.
@Service
@RequiredArgsConstructor
public class RecommendService {
private final VisionService visionService;
private final MealService mealService;
private final GeminiService geminiService;
private static final int LOW_SUGAR_STANDARD = 25;
public RecommendResponse recommendMenu(String userId, MultipartFile image) throws Exception {
List<String> menus = visionService.menuFromImage(image);
if (menus.isEmpty()) {
throw new IllegalArgumentException("메뉴를 추출할 수 없습니다.");
}
LocalDate today = LocalDate.now();
DailyMealResponse response = mealService.getDailyMeals(userId, today);
double todaySugar = response.getDailyTotalSugar();
// 조건 설정
String condition = todaySugar < LOW_SUGAR_STANDARD
? "오늘 섭취한 당이 적으니 당이 조금 높은 음료를 추천해줘."
: "오늘 섭취한 당이 많으니 당이 적은 음료를 추천해줘.";
// Gemini에 프롬프트 작성
String prompt = makePrompt(menus, condition);
// Gemini에게 요청 보내서 추천 결과 받기
String recommendedMenu = geminiService.recommendMenu(prompt);
return new RecommendResponse(recommendedMenu);
}
private String makePrompt(List<String> menus, String condition) {
StringBuilder sb = new StringBuilder();
sb.append("다음 메뉴 중에서 ").append(condition).append("\n");
for (String menu : menus) {
sb.append("- ").append(menu).append("\n");
}
sb.append("한 가지만 추천해줘. 추천 이유는 짧게 덧붙여줘.");
return sb.toString();
}
}
다음은 Recommend에서 연결되는 Gemini 호출 서비스 로직이다
@Service
public class GeminiService {
private final WebClient webClient;
private final String apiKey = System.getenv("GEMINI_API_KEY");
public GeminiService(WebClient.Builder builder) {
this.webClient = builder
.baseUrl("https://generativelanguage.googleapis.com/v1beta")
.build();
}
public String recommendMenu(String prompt) {
String body = """
{
"contents": [{
"parts": [
{ "text": "%s" }
]
}]
}
""".formatted(prompt);
return webClient.post()
.uri(uriBuilder -> uriBuilder
.path("/models/gemini-2.0-flash:generateContent")
.queryParam("key", apiKey)
.build())
.header(HttpHeaders.CONTENT_TYPE, "application/json")
.bodyValue(body)
.retrieve()
.bodyToMono(String.class)
.block();
}
}
엔드포인트 작성도 꽤 고생을 했는데
현재 Gemini에서 공식적으로 제공하는 가이드는 다음과 같으니
엔드포인트 및 요청 구조는 이 부분을 참고하면 된다.
https://aistudio.google.com/apikey
포스트맨에서 userId와 image 를 첨부하여 요청을 보냈고
응답을 받는 데 성공했다!
내가 이김
'🍏 Spring Boot' 카테고리의 다른 글
[Spring Boot] 스프링부트 GCP 배포하기 (Google Cloud Platform) (0) | 2025.05.11 |
---|---|
[Spring Boot] @Value 어노테이션 오류 / application.yml 값 인식 실패 (0) | 2025.05.07 |
[Spring Boot] Firebase & Google OAuth2 로그인 / 액세스 차단 문제 해결 (0) | 2025.04.30 |
[Spring Boot] Google OAuth2 로그인 설정 (0) | 2025.04.14 |
[Spring Boot] Google Firebase와 Spring Boot 연결 (1) | 2025.04.14 |