자바풀스택 과정/자바 풀 스택 : 수업내용정리

자바 풀 스택 3/20 하루 기록 078

파티피플지선 2025. 3. 20. 17:18

9:25 학원 도착

 

 

 

 

<9:30 1교시>

어제 하던거. 포스트맨으로 내 API 키에 요청하기.

근데 난 API 발급을 안받기로 했으니까 그냥 하는거 봐야징.

 

벡터 데이터베이스가 요즘 트렌드라고 한다. RDBMS 는 격자형 데이터베이스.

이미지의 경우 이미지의 각 요소를 벡터 요소로 저장해서, 이미지 검색할 때 예를 들어 꽃이 포함된 이미지들을 셀렉트해줘라고 하면 셀렉트 할 수 있는 형태라고 한다.

챗 지피티에게도 물어보자(그리고 주식 시장에서 알아보기 차원에서 회사도 ㅋ)

더보기

벡터 데이터베이스(Vector Database)는 고차원 벡터 데이터를 효율적으로 저장하고 검색하는 데 특화된 데이터베이스입니다. 이러한 벡터는 보통 머신러닝 모델이나 자연어 처리(NLP)에서 생성되는 임베딩(embedding) 형태의 데이터로, 텍스트, 이미지, 오디오 등 다양한 형태의 데이터를 수치적인 벡터로 변환한 것입니다. 벡터 데이터베이스는 이러한 벡터들 간의 유사도 비교를 빠르게 처리할 수 있는 기능을 제공합니다.

벡터 데이터베이스의 주요 용도는 유사도 검색(similarity search)입니다. 예를 들어, 특정 텍스트를 벡터로 변환하여, 그와 유사한 텍스트나 데이터를 빠르게 찾는 작업을 할 수 있습니다.

벡터 데이터베이스의 주요 기능:

  1. 고차원 벡터 처리: 벡터는 대개 고차원 데이터를 다루므로, 효율적인 검색을 위해 차원의 저주(curse of dimensionality)를 해결하는 알고리즘이 필요합니다.
  2. 근사 최근접 이웃 검색(ANN, Approximate Nearest Neighbor): 벡터 간의 유사도를 측정하여 가장 가까운 벡터를 찾는 기술입니다. 이는 검색 속도를 향상시키기 위해 정확도보다 속도를 우선시하는 방식으로 구현됩니다.
  3. 스케일링: 데이터가 매우 커질 수 있기 때문에, 분산처리와 같은 기술을 사용하여 대규모 데이터를 처리합니다.

벡터 데이터베이스를 제공하는 주요 회사들:

  1. Pinecone
    Pinecone은 고성능 벡터 검색 엔진으로, 실시간 유사도 검색 및 추천 시스템을 위한 서비스입니다. 고차원 데이터의 빠른 검색을 위해 최적화되어 있습니다.
  2. Weaviate
    Weaviate는 오픈소스 벡터 데이터베이스로, AI와 머신러닝 프로젝트에서 벡터 데이터를 관리하고 검색하는 데 특화되어 있습니다. Weaviate는 RESTful API와 GraphQL API를 지원하여 다양한 환경에서 유용하게 사용할 수 있습니다.
  3. Vespa
    Vespa는 Yahoo에서 개발한 오픈소스 플랫폼으로, 벡터 검색 기능을 제공하며, 대규모 데이터셋에 대한 실시간 검색을 지원합니다.
  4. Milvus
    Milvus는 인기 있는 오픈소스 벡터 데이터베이스로, 대규모 벡터 데이터를 실시간으로 검색하고 분석할 수 있는 기능을 제공합니다. 벡터 검색뿐만 아니라 다양한 AI 기반 응용 프로그램에 사용될 수 있습니다.
  5. Faiss (Facebook AI Similarity Search)
    Faiss는 Facebook AI Research에서 개발한 라이브러리로, 고차원 벡터 검색에 최적화되어 있으며, 주로 벡터의 유사도를 빠르게 계산하는 데 사용됩니다. 이는 오픈소스로 제공되며, 다양한 벡터 데이터베이스에 통합될 수 있습니다.
  6. Qdrant
    Qdrant는 실시간 벡터 검색을 위한 오픈소스 벡터 데이터베이스로, AI 및 머신러닝 기반 추천 시스템에 활용될 수 있습니다. 또한, Qdrant는 RESTful API와 WebSocket을 통해 벡터 데이터를 실시간으로 처리할 수 있습니다.
  7. Zilliz
    Zilliz는 Milvus 프로젝트의 개발팀으로, 벡터 검색 기술을 제공하는 기업입니다. Milvus 외에도 다양한 AI 솔루션을 제공하고 있습니다.
  8. Chroma
    Chroma는 AI 모델에서 생성된 벡터를 관리하고 검색하는 데 사용되는 벡터 데이터베이스입니다. 이를 통해 빠르고 효율적인 텍스트 및 이미지 검색을 가능하게 합니다.

이 회사들은 벡터 데이터베이스를 활용하여 검색, 추천 시스템, 자연어 처리, 이미지 분석 등 다양한 AI 기반 응용 프로그램에서 중요한 역할을 하고 있습니다. 벡터 데이터베이스를 통해 고차원 벡터 간의 유사도를 빠르게 계산하고, 대규모 데이터셋에서 실시간으로 검색할 수 있는 기능을 제공합니다.

 
 
 

 

 

 

 

 

 

<10:30 2교시 ~ 4교시>

AI 사용해서 응답하는 기능 마무리했음.  나는 물론 API 키를 받지 않은 상태여서 기능은 안함.

더보기
package com.example.spring16.controller;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.example.spring16.service.GeminiService;
import com.example.spring16.service.GeminiService2;

import reactor.core.publisher.Mono;

//  [Mono<T> 객체에 대하여]
//   - 비동기 동작을 지원하는 객체이다
//   - 컨트롤러에서 아직 동작이 완료되지 않은 Mono 객체를 리턴한다
//   - 리턴된 Mono 객체를 Spring WebFlux가 받아서 동작이 완료되면 결과를 클라이언트에게 응답한다. <-dependency의 webflux 덕분에 사용

@RestController
public class GeminiController {
	@Autowired GeminiService service;
	@Autowired GeminiService2 service2;
	
	// Post 방식 /ask 요청을 하면서 아래의 형식과 같은 json 문자열을 전송하면 된다.
	// {"prompt":"질문"}
	@PostMapping("/ask")
	public Mono<String> ask(@RequestBody Map<String, String> request){
		//질문 얻어내기
		String prompt = request.get("prompt");
		//서비스를 이용해서 질문에 대한 답을 리턴한다
		return service.getChatResponse(prompt);
	}
	

 	@PostMapping("/food")
 	public Mono<String> food(@RequestBody Map<String, String> request){
 		//질문 얻어내기
 		String prompt = request.get("prompt");
 		//서비스를 이용해서 질문에 대한 답을 리턴한다.
 		return service.getFoodCategory(prompt);
 	}
	
 	@PostMapping("/ask2")
 	public String ask2(@RequestBody Map<String, String> request){
 		//질문 얻어내기
 		String prompt = request.get("prompt");
 		//서비스를 이용해서 질문에 대한 답을 리턴한다.
 		return service2.getChatResponseSync(prompt);
 	}
}

 

 

package com.example.spring16.service;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;

import reactor.core.publisher.Mono;

@Service
public class GeminiService {
	// gemini api 키를 custom.properties 파일에서 읽어오기
	@Value("${gemini.key}") private String apiKey;

	// google ai에 요청할 클라이언트 객체
	WebClient webClient;

	// 생성자
	public GeminiService(WebClient.Builder builder) {
		this.webClient = builder.baseUrl("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash")
				.build();
	}

	// 질문을 던지면 Mono<String>객체를 리턴하는 메소드
	public Mono<String> getChatResponse(String prompt) {
		// 요청의 body 구성하기
		Map<String, Object> reqeustBody = Map.of("contents", List.of(Map.of("parts", List.of(Map.of("text", prompt))))); // 생김새는
																															// 마치
																															// {"contents":[{"parts":["text":"질문"}]}]}
																															// 형태
		Mono<String> mono = webClient.post()
				.uri(uriBuilder -> uriBuilder.path(":generateContent").queryParam("key", apiKey).build())
				.contentType(MediaType.APPLICATION_JSON).bodyValue(reqeustBody).retrieve().bodyToMono(String.class)
				.doOnNext(responseBody -> System.out.println(responseBody)).flatMap(responseBody -> {
					try {
						return Mono.just(extractResponse(responseBody));
					} catch (Exception e) {
						return Mono.error(new RuntimeException("JSON 파싱 오류", e));
					}
				});
		System.out.println("서비스 메소드가 리턴됨");
		return mono;
	}

	private final Gson gson = new Gson();
	//json으로 응답된 내용을 추출 및 병합해서 하나의 String 으로 얻어내는 유틸 메소드
	private String extractResponse(String responseJson) {
		try {

			GeminiResponse geminiResponse = gson.fromJson(responseJson, GeminiResponse.class);

			if (geminiResponse.getCandidates() != null && !geminiResponse.getCandidates().isEmpty()) {
				GeminiResponse.Candidate firstCandidate = geminiResponse.getCandidates().get(0);

				if (firstCandidate.getContent() != null && firstCandidate.getContent().getParts() != null) {
					return firstCandidate.getContent().getParts().stream().map(GeminiResponse.Part::getText)
							.reduce((a, b) -> a + "\n" + b) // 여러 개의 응답을 합침
							.orElse("응답 없음");
				}
			}
		} catch (JsonSyntaxException e) {
			return "JSON 파싱 오류: " + e.getMessage();
		}
		return "응답 없음";
	}
	
	/*
	 *  클라이언트가 입력한 내용을 이용해서 Gemini 에 질문할 새로운 질문을 만들어낸다.
	 *  
	 *  대답의 형식을 구체적으로 제한한다. 
	 */
	public Mono<String> getFoodCategory(String food){
		/*
		String str = """
	        클라이언트가 입력한 음식: "%s"
	        
	        해당 음식의 카테고리를 반환해줘.
	        반환할 문자열은 ["한식","중식","일식","양식","기타"] 중에서 하나만 반환해야 해.
	        다른 설명 없이 오직 카테고리 이름만 반환해.
		 """.formatted(food);
		 */
	    String str = """
            클라이언트가 입력한 음식: "%s"
            
            해당 음식의 카테고리를 JSON 형식으로 반환해.
            응답은 아래 형식을 따라야 해:
            { "category": "한식" }
            
            ["한식", "중식", "일식", "양식", "기타"] 중 하나만 "category" 값으로 넣어줘.
            설명 없이 JSON 객체만 반환해.
            markdown 형식으로 응답하면 안되.
	     """.formatted(food);
		
		return getChatResponse(str);
	}
	
	

}

 

 

package com.example.spring16.service;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;

import reactor.core.publisher.Mono;

@Service
public class GeminiService2 {
    private final WebClient webClient;
    private final Gson gson = new Gson();
    private final String apiKey;
    private final String url;
    
    //생성자에 필요한 data 3 개 전달받기 
    public GeminiService2(WebClient.Builder builder, 
    		@Value("${gemini.key}") String apiKey,
    		@Value("${gemini.url}") String url  //서비스 객체가 생성되는 시점에 @Value 가 읽어와 지지 않는다.
    ) {
    	//전달 받은 내용을 필드에 저장하기 
	   this.apiKey=apiKey;
	   this.url=url;
	   this.webClient=builder.baseUrl(url).build();
    }

    public Mono<String> getFoodCategory(String food) {
        String str = """
            클라이언트가 입력한 음식: "%s"
            
            해당 음식의 카테고리를 JSON 형식으로 반환해.
            응답은 아래 형식을 따라야 해:
            { "category": "한식" }
            
            ["한식", "중식", "일식", "양식", "기타"] 중 하나만 "category" 값으로 넣어줘.
            설명 없이 JSON 객체만 반환해.
            markdown 형식으로 응답하면 안되.
        """.formatted(food);

        return getChatResponse(str);
    }

    public String getChatResponseSync(String prompt) {
    	//GeminiRequest 구성하기 
        GeminiRequest request = new GeminiRequest();
        GeminiRequest.Content content = new GeminiRequest.Content();
        GeminiRequest.Part part = new GeminiRequest.Part();
        //part 에 질문을 담는다. 
        part.setText(prompt);
        content.setParts(List.of(part));
        request.setContents(List.of(content));

        String result =  webClient.post()
                .uri(uriBuilder -> uriBuilder.path(":generateContent")
                        .queryParam("key", apiKey)
                        .build())
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(request) // Map 객체 대신에 GeminiRequest 객체를 넣어주면 json 으로 변경된다.
                .retrieve()
                .bodyToMono(String.class)
                .doOnNext(responseBody -> System.out.println(responseBody))
                .flatMap(responseBody -> {
                    try {
                        return Mono.just(extractResponse(responseBody));
                    } catch (Exception e) {
                        return Mono.error(new RuntimeException("JSON 파싱 오류", e));
                    }
                }).block();//block 메소드를 호출하면 결과 문자열을 받아올때까지 기다린다(굳이 비동기 동작이 필요없다면)
        return result;
    }
    
 // 질문을 던지면 Mono<String>객체를 리턴하는 메소드
 	public Mono<String> getChatResponse(String prompt) {
 		// 요청의 body 구성하기
 		Map<String, Object> reqeustBody = Map.of("contents", List.of(Map.of("parts", List.of(Map.of("text", prompt))))); // 생김새는
 																															// 마치
 																															// {"contents":[{"parts":["text":"질문"}]}]}
 																															// 형태
 		Mono<String> mono = webClient.post()
 				.uri(uriBuilder -> uriBuilder.path(":generateContent").queryParam("key", apiKey).build())
 				.contentType(MediaType.APPLICATION_JSON).bodyValue(reqeustBody).retrieve().bodyToMono(String.class)
 				.doOnNext(responseBody -> System.out.println(responseBody)).flatMap(responseBody -> {
 					try {
 						return Mono.just(extractResponse(responseBody));
 					} catch (Exception e) {
 						return Mono.error(new RuntimeException("JSON 파싱 오류", e));
 					}
 				});
 		System.out.println("서비스 메소드가 리턴됨");
 		return mono;
 	}
    
    //응답된 json 을 파싱해서 문자열 얻어내는 메소드 
    private String extractResponse(String responseJson) {
        try {
            GeminiResponse geminiResponse = gson.fromJson(responseJson, GeminiResponse.class);

            return Optional.ofNullable(geminiResponse)
                    .map(GeminiResponse::getCandidates)
                    .filter(candidates -> !candidates.isEmpty())
                    .map(candidates -> candidates.get(0))
                    .map(GeminiResponse.Candidate::getContent)
                    .map(GeminiResponse.Content::getParts)
                    .filter(parts -> !parts.isEmpty())
                    .map(parts -> parts.stream().map(GeminiResponse.Part::getText).collect(Collectors.joining("\n")))
                    .orElse("응답 없음");

        } catch (JsonSyntaxException e) {
            return "JSON 파싱 오류: " + e.getMessage();
        }
    }
}

 

service는 비동기 방식, service2는 동기 방식이라고 하심. 이김에 챗지피티에게 한 번 더물어보기.

더보기

동기 방식과 비동기 방식은 프로그램이나 시스템에서 작업을 처리하는 방식에 따라 차이를 두는 개념입니다. 간단히 말해, 두 방식은 작업 처리 방식의 순서와 기다림에 따라 구분됩니다.

1. 동기 방식 (Synchronous)

동기 방식은 작업을 순차적으로 처리하는 방식입니다. 하나의 작업이 끝나야 그 다음 작업을 시작할 수 있습니다. 즉, 작업이 완료될 때까지 기다린 후, 그 결과를 받아서 다음 작업을 진행합니다.

  • 예시:
    • 밥을 지을 때, 물이 끓을 때까지 기다리고, 끓은 물에 밥을 넣고 다시 밥이 다 될 때까지 기다린다.
  • 특징:
    • 작업 순서가 정해져 있고, 이전 작업이 완료될 때까지 다음 작업을 시작할 수 없다.
    • 프로그램에서 다른 작업을 동시에 처리하지 못하고, 하나씩 처리된다.

2. 비동기 방식 (Asynchronous)

비동기 방식은 작업을 동시에 처리할 수 있는 방식입니다. 작업을 요청한 후, 그 작업이 완료될 때까지 기다리지 않고 다른 작업을 먼저 처리하며, 작업이 끝나면 결과를 받아옵니다.

  • 예시:
    • 밥을 짓는 동안 다른 일을 할 수 있다. 예를 들어, 밥을 짓고 있는 동안 다른 요리를 하거나 청소를 한다. 밥이 다 되면 알림이 오거나 밥을 확인할 수 있다.
  • 특징:
    • 작업을 요청하고 그 결과를 기다리는 동안 다른 일을 동시에 할 수 있다.
    • 작업이 끝났을 때 알림을 받거나 결과를 받아와서 후속 작업을 진행한다.

요약

  • 동기 방식: 순차적으로 처리, 하나의 작업이 끝날 때까지 기다려야 한다.
  • 비동기 방식: 동시에 여러 작업을 처리, 하나의 작업을 기다리지 않고 다른 작업을 진행한다.

프로그래밍에서는 비동기 방식이 주로 I/O 작업(파일 읽기/쓰기, 네트워크 요청 등)을 처리할 때 많이 사용되며, 프로그램의 효율성을 높이는 데 도움을 줍니다.

 

 

<14:30 5교시>

리액트로 퀴즈 풀어서 제출하면 AI가 풀이를 채점해주는 프로그램을 만들려고 한다.

 

안하려고 했었는데 다른 사람들 하는거 보니까 재밌어 보여서 해보고 싶어지긴 한데... API 무료 버전 쓰는게 영 맘에 걸린다. 자주 안쓰는 구글 계정으로 해보던가 해야겠다.

 

 

<15:30 6교시>

AI에게 어떻게 질문할지를 작성하는 프로그래밍을 프롬프트 엔지니어링이라고 한다.

이 질문이면 될 것 같은데, API를 연결하지 않아서 되는지는 모르겠당 ..ㅎㅎ

 

 

 

<16:30 7교시>

체점 결과를 퀴즈 화면에 나오게 하기.

 

 

 

 

<17:30 8교시> 대학원 수업 참여로 인한 조퇴