[SQL] MySQL 격리 수준
·
SQL
✅ 트랜잭션 격리 수준이란?트랜잭션의 격리 수준(isolation level)이란 여러 트랜잭션이 동시에 처리될 때 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있게 허용할지 말지를 결정하는 것입니다.격리 수준은 크게 `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, `SERIALIZABLE`로 4가지로 나뉘며, 순서대로 뒤로 갈수록 각 트랜잭션 간의 데이터 격리(고립) 정도가 높아지고 동시 처리 성능이 떨어진다고 볼 수 있습니다. ✅ READ UNCOMMITTED`READ UNCOMMITTED` 격리 수준에서는 다음 시퀀스 다이어그램과 같이 각 트랜잭션에서의 변경 내용이 `commit`이나 `rollback` 여부에 상관없이 ..
[SQL] 실행 계획 분석해 성능 개선하기(MariaDB)
·
SQL
✅ 기존 쿼리 및 실행 계획 분석기존 쿼리 ↓더보기더보기analyzeselect a1_0.accommodation_id as '숙소 ID', a1_0.title as '숙소 제목', max(ap1_0.price) as '가격', coalesce(avg(r2_0.rating), 0.0) as '평균 평점', ai1_0.image_url as '썸네일', w1_0.wishlist_id is not null as '위시리스트 등록 여부', w1_0..
[Redis] 캐시 스탬피드 현상
·
트러블슈팅
✅ 개요Spring Boot 프로젝트에서 레디스를 사용하여 캐시를 사용하였을 때 발생한 캐시 스탬피드 현상에 대한 해결 과정을 기록하고자 합니다. ✅ 문제 상황먼저 캐싱 대상은 AI 후기 요약 결과로 다음과 같습니다. 캐싱을 적용하여 기본적인 응답 시간 감소와 함께 LLM을 호출하는 횟수를 줄여 토큰 비용 감소를 기대했습니다.@Cacheable(value = "postReviewSummary", key = "#postId")public String summarizePostReviews(Long postId) { List reviews = reviewQueryRepository.findTop30ByPostId(postId); if (reviews.isEmpty()) { return ..
Spring AI 개발 일지 (4) - RAG 개념 정리
·
Spring
✅ RAGLLM은 학습한 지식 안에서만 답변할 수 있기 때문에 특정 도메인 지식이 필요한 경우에는 전혀 엉뚱하거나 틀린 정보를 말하는 Hallucination이 발생할 수 있습니다.이런 문제를 해결하기 위해 RAG(Retrieval-Augmented Generation)라는 개념이 등장했습니다. RAG는 외부 데이터를 검색해 LLM에게 특정 맥락(context)을 제공하여, 보다 정확하고 신뢰할 수 있는 답변을 생성하도록 합니다.✅ RAG 파이프라인Spring AI에서 지원하는 대표적인 RAG 흐름은 다음과 같습니다.데이터 색인 (Data Indexing)문서 ChunkingPDF 또는 텍스트 문서 업로드텍스트 추출 및 정제LLM이 이해하기 좋은 단위로 Chunking(Text Splitting)벡터화 ..
Spring AI 개발 일지 (3) - 챗봇 구현
·
Spring
✅ 개요Spring AI를 사용해 RAG 없이 단순 대화 챗봇을 구현해보겠습니다.✅ 사전 준비1️⃣ 의존성 추가 및 환경설정implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-repository-jdbc'기본 Spring AI 의존성만 추가하면 인메모리 기반의 레포지토리 빈이 등록됩니다. 대화 내역을 DB에 저장하기 위해서 jdbc 의존성을 추가했습니다.spring: ai: chat: memory: repository: jdbc: initialize-schema: never자동 스키마 생성 기능을 사용할 경우 오류가 자주 발생한다고 해 never로 설정한 다음..
Spring AI 개발 일지 (2) - OpenAI 사용해 후기 요약 구현해보기
·
Spring
✅ 개요우선 벡터 DB, RAG와 같은 임베딩 기술 없이 LLM만 사용하여 Spring AI 기술을 사용해보기 위해 간단한 게시글에 대한 후기 요약 기능을 구현해보겠습니다.✅ 사전 준비1️⃣ 의존성 추가implementation("org.springframework.ai:spring-ai-starter-model-openai")2️⃣ 설정 파일 추가spring: ai: openai: api-key: ${OPENAI_API_KEY} chat: options: model: gpt-4.1-mini temperature: 0.0 max-tokens: 1024OpenAI Platform에서 API Key를 발급 받았습니다. (최..
Spring AI 개발 일지 (1) - Spring AI 소개와 핵심 모델
·
Spring
✅ Spring AI란?Spring AI는 Spring 개발자들을 위한 LLM 통합 도구로, AI와 연관된 도구들 쉽게 통합하도록 하는 프레임워크입니다.Spring AI 공식문서에 의하면 "Spring AI는 AI 애플리케이션 개발의 기반이 되는 추상화를 제공하여 최소한의 코드 변경으로 구성 요소를 쉽게 교체할 수 있다"고 소개하며, 대표적으로 다음과 같은 기능을 제공한다고 합니다.AI 모델 통합 API 제공OpenAI, Anthropic, Microsoft, Amazon, Google, Ollama 등 주요 AI Model Provider를 지원Chat Completion, Embedding, Text to Image, Text to Speech 등 다양한 모델 타입을 하나의 일관된 API로 제공모델 ..
[Infra] 무중단 배포 (롤링, 블루-그린, 카나리)
·
Infra
✅ 사전 지식1. CI/CDCICI (Continuous Integration)는 지속적 통합이라는 뜻으로, 개발자를 위해 빌드와 테스트를 자동화하는 과정을 의미합니다. CI는 변경 사항을 자동으로 테스트해 애플리케이션에 문제가 없다는 것을 보장합니다. 그리고 코드를 정기적으로 빌드하고, 테스트하여 여러 명이 동시에 작업을 하는 경우 충돌을 방지하고 모니터링할 수 있습니다.보통 코드 변경 사항이 깃허브와 같은 코드 저장소에 업로드되면 CI를 시작하고, CI 도중 문제가 생기면 실패하므로 코드의 오류도 쉽게 파악할 수 있습니다.CDCD는 CI 작업을 끝낸 다음 실행하는 작업으로, 배포 준비가 된 코드를 서버에 배포하는 작업을 자동화합니다. CI가 통과되면 개발자가 수작업으로 코드를 배포하지 않아도 자동으로..
[Spring WebSocket] STOMP - convertAndSendToUser 알고 사용하기
·
트러블슈팅
✅ 개요Spring Stomp 기술 중 `SimpMessageSendingOperations.convertAndSendToUser()` 메서드를 사용해 특정 사용자에게만 메시지를 전달하는 과정에서의 시행착오와 디버깅을 통해 해결하는 과정을 정리하고자 합니다. ✅ 문제 상황프로젝트 채팅 서비스 설계상 사용자 A가 사용자 B에게 채팅 요청을 보내면 사용자 B에게 채팅 요청을 수락 또는 거절할 수 있는 메시지를 보내는 이벤트를 발행하고 있습니다.그리고 이벤트를 받아 처리하는 서비스 로직에서 `convertAndSendToUser()` 메서드를 사용하고 있습니다.더보기@Component@RequiredArgsConstructorpublic class ChatEventListener { private fin..
PageableExecutionUtils.getPage로 페이징 성능 개선하기
·
Spring
✅ 개요Querydsl은 페이징 처리를 위해 `PageImpl()`의 최적화 버전인 `PageableExecutionUtils` 클래스의 정적 메서드 `getPage()`를 지원합니다. ` PageableExecutionUtils.getPage()`는 어떻게 페이징 쿼리를 최적화하는지 분석하고 정리하려고 합니다. ✅ PageableExecutionUtils먼저 `PageableExecutionUtils` 클래스 내부 코드를 살펴보았습니다.내부적으로 엄청나게 복잡한 로직으로 이루어져 있지는 않아서 다행이었습니다. 하나씩 이해하면 원리를 완벽히 이해하기에 큰 어려움은 없을 것 같습니다. ➡️ isPartialPage`getPage` 메서드는 먼저 `isPartialPage`인지 확인하고 있습니다. 여기서 f..