pgvector 벡터 검색이 HNSW 인덱스를 안 탈 때 예전에 pgvector로 RAG 챗봇의 벡터 검색을 만든 글을 썼었다. 그때는 HNSW 인덱스 걸고 잘 돌아갔는데, 데이터가 쌓이고 회사(테넌트)가 늘면서 어느 날부터 검색이 눈에 띄게 느려졌다. 원래 수십 밀리초면 끝나던 게 수 초씩 걸렸다. 원인을 찾는 데 시간이 좀 걸렸는데, 알고 보니 ...
CloudFront Function 으로 엣지에서 처리하기
CloudFront Function 으로 엣지에서 처리하기 메일 시스템 글에서 CloudFront로 프론트(React SPA)를 S3에 서빙한다고 했다. 근데 SPA를 S3 + CloudFront로 올리면 흔히 밟는 함정이 하나 있다. 사용자가 /posts/abc 같은 경로에서 새로고침을 하면 404가 뜬다. 이유는 단순하다. SPA는 라우팅을 브라...
멀티에이전트 RAG 직접 만들기
멀티에이전트 RAG 직접 만들기 지난 글에서 검색을 다듬고, 마지막에 LLM이 “이 자료로 답할 수 있나” 를 판단하게 했다. 사실 그 판단으로 재검색·되묻기·거절을 가르는 순간, 이미 단순 RAG를 넘어 에이전트의 영역에 발을 들인 거였다. 이번 글은 그걸 제대로 밀고 나간 이야기다. 역할을 나눈 여러 LLM이 협업하고, 자료로 안 되는 건 도구(t...
가상 스크롤 직접 구현하기
가상 스크롤 직접 구현하기 회사 관리자 화면에 목록 테이블이 하나 있었는데, 데이터가 쌓이면서 행이 수만 개가 됐다. 그랬더니 페이지가 열릴 때 몇 초씩 멈추고, 스크롤은 뚝뚝 끊겼다. 원인은 단순했다. 화면엔 기껏해야 20~30행이 보이는데, DOM에는 수만 개의 행이 전부 그려져 있었던 거다. 브라우저 입장에선 보이든 안 보이든 그 수만 개를 다...
RAG 검색 품질 끌어올리기
RAG 검색 품질 끌어올리기 지난 글에서 pgvector로 벡터 검색을 붙이고, 거기에 키워드 검색까지 섞은 하이브리드를 만들었다. 의미로도 찾고 정확한 단어로도 찾으니 후보를 빠짐없이 긁어오게 됐다. 근데 운영해보니 “후보를 잘 긁어온다” 와 “좋은 답이 나온다” 는 생각보다 거리가 멀었다. 문제는 이거였다. 하이브리드 검색이 후보를 20개쯤 가져...
RAG 챗봇 벡터 검색 만들기
RAG 챗봇 벡터 검색 만들기 회사에서 고객 문의에 자동으로 답하는 AI 챗봇을 만들게 됐다. 회사가 가진 문서나 FAQ를 바탕으로 답을 해주는, 요즘 흔히 말하는 RAG(Retrieval-Augmented Generation) 챗봇이다. 이름은 거창한데 핵심은 단순하다. 사용자가 질문하면, 우리 지식 중에서 그 질문과 관련있는 걸 찾아다가, 그걸 근...
멀티테넌트 컨텍스트 전파하기
멀티테넌트 컨텍스트 전파하기 여러 회사가 같이 쓰는 SaaS를 만들면, 데이터가 회사끼리 절대 섞이면 안된다는 게 제1원칙이 된다. A회사가 요청했는데 B회사 데이터가 조회되면 그건 버그가 아니라 사고다. 이걸 막는 방법으로 우리는 모든 테이블에 company_id 컬럼을 두고, 조회할 때마다 WHERE company_id = ? 를 붙이는 방식을 골...
CloudWatch Logs Insights 로 Lambda 로그 파보기
CloudWatch Logs Insights 로 Lambda 로그 파보기 Lambda를 운영하다 보면 로그를 봐야 할 일이 계속 생긴다. “어제 밤에 왜 에러가 났지”, “이 함수 콜드스타트가 얼마나 되지”, “특정 요청 하나가 어디서 막혔지” 같은거다. 근데 CloudWatch 콘솔에서 로그 그룹 들어가서 로그 스트림을 하나하나 열어보는건 정말 못할...
presigned URL 로 대용량 파일 업로드하기
presigned URL 로 대용량 파일 업로드하기 사용자가 파일을 올리는 기능을 만들 일이 생겼다. 처음엔 별거 아니라고 생각했다. 브라우저에서 multipart/form-data 로 파일을 보내고, 서버가 받아서 S3에 올리면 되는거 아닌가. 실제로 작은 파일은 그렇게 해도 잘 된다. 문제는 파일이 커지면서 시작됐다. 우리 백엔드는 Lambda로...
Java 가상 스레드 적용기
Java 가상 스레드 적용기 회사 서비스 중에 외부 API를 잔뜩 호출하는 녀석이 하나 있다. 요청 하나를 처리하려면 외부 시스템 서너곳에 다녀와야 하는데, 각 호출이 다 끝날때까지 스레드가 그냥 멍하니 기다린다. 트래픽이 몰리면 톰캣 스레드풀(기본 200개)이 금방 동나고, 그 다음 요청들은 큐에서 줄을 선다. CPU는 놀고 있는데 처리량은 안 올라...