본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 24. · 5 Views
RAG로 코드베이스 검색 기능 완벽 가이드
대규모 코드베이스에서 관련 코드를 빠르게 찾아주는 RAG 기반 검색 시스템을 구축하는 방법을 배웁니다. 벡터 DB와 임베딩을 활용한 Semantic Code Search부터 ChromaDB를 이용한 실전 구현까지 단계별로 알아봅니다.
목차
- 코드베이스_임베딩과_벡터_DB_구축
- Semantic_Code_Search_구현
- 관련_함수와_클래스_자동_찾기
- 코드_스니펫_검색과_활용
- 실습_ChromaDB로_코드_벡터_DB_구축
- 실습_비슷한_코드_찾기_기능_구현
1. 코드베이스 임베딩과 벡터 DB 구축
어느 날 김개발 씨는 회사에서 대규모 레거시 프로젝트를 맡게 되었습니다. 수천 개의 파일이 있는 프로젝트에서 특정 기능을 담당하는 코드를 찾으려니 막막했습니다.
선배 박시니어 씨가 다가와 물었습니다. "김 개발님, 혹시 RAG 기반 코드 검색 시스템 들어보셨나요?"
코드베이스 임베딩은 소스 코드를 벡터 형태로 변환하여 의미적으로 저장하는 기술입니다. 마치 책의 내용을 숫자 형태로 압축해서 저장하는 것과 같습니다.
이렇게 변환된 벡터를 벡터 DB에 저장하면 나중에 비슷한 의미를 가진 코드를 빠르게 찾을 수 있습니다.
다음 코드를 살펴봅시다.
import openai
from chromadb import Client
import os
# OpenAI API를 사용한 코드 임베딩 생성
def embed_code(code_snippet):
# 코드를 벡터로 변환합니다
response = openai.Embedding.create(
input=code_snippet,
model="text-embedding-ada-002"
)
return response['data'][0]['embedding']
# ChromaDB 클라이언트 초기화
client = Client()
collection = client.create_collection("code_embeddings")
# 코드 스니펫을 벡터 DB에 저장합니다
code = "def calculate_total(items): return sum(item.price for item in items)"
embedding = embed_code(code)
collection.add(embeddings=[embedding], documents=[code], ids=["func_001"])
김개발 씨는 입사 6개월 차 개발자입니다. 오늘도 열심히 코드를 작성하던 중, 이전에 누군가 작성해둔 비슷한 기능이 있을 것 같은데 찾을 수가 없었습니다.
Ctrl+F로 검색해봐도 변수명이 달라서 찾을 수가 없었습니다. 박시니어 씨가 다가와 화면을 살펴봅니다.
"아, 이럴 때 필요한 게 바로 코드 임베딩이에요." 그렇다면 코드 임베딩이란 정확히 무엇일까요? 쉽게 비유하자면, 코드 임베딩은 마치 책의 내용을 요약해서 숫자로 표현하는 것과 같습니다.
예를 들어 "해리 포터"라는 책을 읽고 "마법, 모험, 성장"이라는 핵심 키워드를 뽑아내는 것처럼, 코드도 그 의미를 숫자 배열로 표현할 수 있습니다. 이 숫자 배열을 벡터라고 부릅니다.
임베딩이 없던 시절에는 어땠을까요? 개발자들은 코드를 찾을 때 오로지 텍스트 매칭에 의존해야 했습니다.
함수 이름이 정확히 일치해야만 찾을 수 있었죠. 하지만 같은 기능을 하는 코드라도 변수명이나 함수명이 다르면 찾을 수 없었습니다.
더 큰 문제는 "비슷한 기능을 하는 코드"를 찾는 것이 거의 불가능했다는 점입니다. 바로 이런 문제를 해결하기 위해 코드 임베딩과 벡터 DB가 등장했습니다.
코드 임베딩을 사용하면 의미적으로 비슷한 코드를 찾을 수 있습니다. 예를 들어 "사용자 인증"이라는 키워드로 검색하면, 실제로 "authenticate_user", "verify_login", "check_credentials" 같은 다양한 이름의 함수들을 모두 찾아낼 수 있습니다.
무엇보다 자연어 질문으로도 코드를 찾을 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 embed_code 함수는 OpenAI의 임베딩 API를 호출합니다. 이 API는 입력받은 코드 문자열을 1536차원의 벡터로 변환해줍니다.
이 벡터는 코드의 의미를 숫자로 표현한 것입니다. 다음으로 ChromaDB 클라이언트를 초기화하고 컬렉션을 생성합니다.
컬렉션은 마치 데이터베이스의 테이블과 같은 개념입니다. 마지막으로 collection.add 메서드로 임베딩 벡터와 원본 코드, 그리고 고유 ID를 함께 저장합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 대규모 전자상거래 플랫폼을 개발한다고 가정해봅시다.
수백 명의 개발자가 수년간 작성한 코드가 쌓여 있습니다. 새로운 개발자가 "결제 검증 로직"을 찾아야 할 때, 코드 임베딩을 활용하면 관련된 모든 함수를 몇 초 만에 찾을 수 있습니다.
구글, 마이크로소프트 같은 대기업에서도 이런 패턴을 적극적으로 사용하고 있습니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 모든 코드를 한 번에 임베딩하려는 것입니다. 이렇게 하면 API 비용이 폭발적으로 증가하고 시간도 오래 걸립니다.
따라서 함수 단위나 클래스 단위로 나눠서 임베딩하는 것이 좋습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "아, 그래서 의미 기반 검색이 가능했군요!" 코드 임베딩을 제대로 이해하면 대규모 코드베이스에서도 원하는 코드를 빠르게 찾을 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 임베딩 모델은 OpenAI의 text-embedding-ada-002가 가성비가 좋습니다
- 코드는 함수 또는 클래스 단위로 쪼개서 임베딩하세요
- 메타데이터(파일 경로, 작성자 등)도 함께 저장하면 필터링에 유용합니다
2. Semantic Code Search 구현
김개발 씨가 벡터 DB에 코드를 저장하는 데는 성공했지만, 이제 어떻게 검색해야 할지 막막했습니다. 그때 박시니어 씨가 말했습니다.
"이제 시맨틱 검색을 구현해볼까요? 자연어로 질문하면 관련 코드를 찾아주는 기능이에요."
시맨틱 코드 서치는 사용자의 자연어 질문을 이해하고, 의미적으로 가장 관련성 높은 코드를 찾아주는 검색 시스템입니다. 마치 도서관 사서가 질문을 듣고 적절한 책을 찾아주는 것처럼, AI가 질문의 의도를 파악하여 코드를 추천합니다.
단순 키워드 매칭이 아닌 의미 기반 검색이 핵심입니다.
다음 코드를 살펴봅시다.
def semantic_search(query, collection, top_k=5):
# 사용자 질문을 벡터로 변환합니다
query_embedding = embed_code(query)
# 벡터 DB에서 유사한 코드를 검색합니다
results = collection.query(
query_embeddings=[query_embedding],
n_results=top_k
)
# 검색 결과를 정리하여 반환합니다
found_codes = []
for i, doc in enumerate(results['documents'][0]):
found_codes.append({
'code': doc,
'score': results['distances'][0][i],
'id': results['ids'][0][i]
})
return found_codes
# 실제 사용 예시
results = semantic_search("사용자 인증을 처리하는 함수", collection)
김개발 씨는 이제 코드를 벡터 DB에 저장하는 방법을 알게 되었습니다. 하지만 실제로 검색하려고 하니 또 다른 문제가 생겼습니다.
"어떻게 검색해야 하지?" 박시니어 씨가 웃으며 말했습니다. "검색도 임베딩을 사용하면 됩니다.
질문을 벡터로 만들어서 저장된 코드 벡터와 비교하는 거예요." 그렇다면 시맨틱 코드 서치란 정확히 무엇일까요? 쉽게 비유하자면, 시맨틱 서치는 마치 똑똑한 도서관 사서와 같습니다.
"재미있는 판타지 소설 추천해주세요"라고 물으면, 사서는 "반지의 제왕", "나니아 연대기" 같은 책을 추천해줍니다. 정확히 "판타지"라는 단어가 제목에 없어도 의미를 이해하고 찾아주는 것이죠.
시맨틱 코드 서치도 이처럼 질문의 의미를 이해하여 관련 코드를 찾아냅니다. 전통적인 키워드 검색의 한계는 무엇이었을까요?
개발자들은 코드를 찾을 때 정확한 함수명이나 변수명을 알아야 했습니다. "authenticate"라고 검색하면 "verify"나 "check"로 시작하는 함수는 찾을 수 없었죠.
더 큰 문제는 "사용자가 로그인할 수 있게 하는 코드"처럼 자연어로 검색하는 것이 불가능했다는 점입니다. 결국 개발자들은 수많은 파일을 일일이 열어보며 시간을 낭비했습니다.
바로 이런 문제를 해결하기 위해 시맨틱 검색이 등장했습니다. 시맨틱 검색을 사용하면 자연어 질문으로 코드를 찾을 수 있습니다.
"주문 총액을 계산하는 로직"이라고 검색하면 실제 함수명이 calculate_total, sum_order_amount, get_cart_price 중 무엇이든 모두 찾아냅니다. 또한 유사도 점수를 제공하여 가장 관련성 높은 코드부터 보여줍니다.
무엇보다 맥락을 이해한다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 semantic_search 함수는 사용자의 자연어 질문을 받습니다. 이 질문을 embed_code 함수로 벡터로 변환합니다.
그 다음 collection.query 메서드를 호출하여 벡터 DB에서 가장 유사한 벡터를 찾습니다. n_results 파라미터는 상위 몇 개의 결과를 가져올지 지정합니다.
마지막으로 결과를 정리하여 코드, 유사도 점수, ID를 포함한 딕셔너리 리스트로 반환합니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 대규모 핀테크 서비스를 개발한다고 가정해봅시다. 신입 개발자가 "송금 실패 시 롤백 처리"를 구현해야 합니다.
시맨틱 검색에 "송금 실패했을 때 되돌리는 코드"라고 입력하면, 트랜잭션 롤백과 관련된 모든 함수를 찾아줍니다. 실리콘밸리의 많은 스타트업들이 이런 방식으로 개발 생산성을 크게 향상시켰습니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 너무 모호한 질문을 하는 것입니다.
"좋은 코드 찾아줘"처럼 추상적인 질문은 정확한 결과를 얻기 어렵습니다. 따라서 "특정 기능을 수행하는" 형태로 구체적인 질문을 하는 것이 좋습니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 감탄했습니다.
"와, 이제 자연어로 코드를 찾을 수 있겠네요!" 시맨틱 검색을 제대로 구현하면 개발 생산성이 몇 배나 향상됩니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - top_k 값은 보통 3~10 사이가 적당합니다
- 질문은 구체적이고 명확하게 작성하세요
- 유사도 점수가 낮으면 관련성이 떨어지므로 임계값을 설정하세요
3. 관련 함수와 클래스 자동 찾기
김개발 씨가 코드를 수정하다가 궁금해졌습니다. "이 함수를 수정하면 어떤 다른 함수들이 영향을 받을까?" 박시니어 씨가 말했습니다.
"RAG를 활용하면 관련된 함수와 클래스를 자동으로 찾을 수 있어요."
관련 함수/클래스 자동 찾기는 특정 코드와 의미적으로 연관된 다른 코드들을 자동으로 탐색하는 기능입니다. 마치 소셜 네트워크에서 친구 추천을 받는 것처럼, 한 함수와 관련성이 높은 다른 함수들을 찾아줍니다.
이를 통해 코드 의존성을 파악하고 리팩토링 영향 범위를 예측할 수 있습니다.
다음 코드를 살펴봅시다.
def find_related_code(code_id, collection, threshold=0.7):
# 특정 코드의 임베딩을 가져옵니다
target_code = collection.get(ids=[code_id])
if not target_code['embeddings']:
return []
target_embedding = target_code['embeddings'][0]
# 유사한 코드들을 검색합니다
results = collection.query(
query_embeddings=[target_embedding],
n_results=20
)
# 임계값 이상의 유사도를 가진 코드만 필터링
related = []
for i, doc in enumerate(results['documents'][0]):
score = 1 - results['distances'][0][i] # 거리를 유사도로 변환
if score >= threshold and results['ids'][0][i] != code_id:
related.append({'code': doc, 'similarity': score})
return related
# 사용 예시
related_funcs = find_related_code("func_001", collection, threshold=0.75)
김개발 씨는 레거시 코드를 리팩토링하는 업무를 맡았습니다. 한 함수를 수정하려고 하는데, 이 함수를 수정하면 어떤 다른 코드들이 영향을 받을지 알 수가 없었습니다.
모든 코드를 읽어볼 수는 없고, 막막했습니다. 박시니어 씨가 화면을 보더니 말했습니다.
"이럴 때 관련 함수 자동 찾기 기능이 유용해요. 벡터 유사도로 연관된 코드들을 찾아주거든요." 그렇다면 관련 함수 자동 찾기란 정확히 무엇일까요?
쉽게 비유하자면, 이 기능은 마치 유튜브의 추천 알고리즘과 같습니다. 한 영상을 보면 관련된 다른 영상들을 추천해주죠.
마찬가지로 한 함수를 선택하면, 비슷한 기능을 하거나 함께 사용될 가능성이 높은 다른 함수들을 자동으로 찾아줍니다. 이는 벡터 간 거리 계산을 통해 이루어집니다.
수동으로 관련 코드를 찾던 시절의 문제점은 무엇이었을까요? 개발자들은 IDE의 "참조 찾기" 기능에 의존했습니다.
하지만 이 기능은 직접적인 호출 관계만 보여줍니다. 비슷한 기능을 하지만 직접 호출하지 않는 함수들은 찾을 수 없었죠.
더 큰 문제는 리팩토링할 때 숨겨진 의존성을 놓치기 쉬웠다는 점입니다. 결국 버그가 발생하고 나서야 문제를 발견하곤 했습니다.
바로 이런 문제를 해결하기 위해 의미 기반 관련 코드 찾기가 등장했습니다. 이 기능을 사용하면 간접적인 연관성도 파악할 수 있습니다.
예를 들어 "사용자 검증" 함수를 수정하면, "권한 확인", "세션 관리" 같은 관련 함수들을 자동으로 찾아줍니다. 또한 유사도 점수를 제공하여 얼마나 강하게 연관되어 있는지 알 수 있습니다.
무엇보다 리팩토링 영향 범위를 사전에 파악할 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 find_related_code 함수는 찾고자 하는 코드의 ID를 받습니다. collection.get 메서드로 해당 코드의 임베딩 벡터를 가져옵니다.
그 다음 이 벡터를 쿼리로 사용하여 유사한 코드들을 검색합니다. 거리 값을 유사도 점수로 변환하고, threshold 값 이상인 것만 필터링합니다.
마지막으로 자기 자신은 제외하고 결과를 반환합니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 대규모 게임 서버를 개발한다고 가정해봅시다. "아이템 지급" 함수를 수정해야 합니다.
관련 함수 찾기를 실행하면 "인벤토리 업데이트", "로그 기록", "알림 발송" 같은 연관 함수들이 나타납니다. 이를 통해 수정 전에 영향 범위를 파악하고, 관련 테스트 케이스를 미리 준비할 수 있습니다.
넷플릭스 같은 회사들이 이런 방식으로 안전한 리팩토링을 수행합니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 임계값을 너무 낮게 설정하는 것입니다. 임계값이 0.5 이하면 관련 없는 코드까지 너무 많이 나와서 오히려 혼란스러울 수 있습니다.
따라서 0.7 이상으로 설정하는 것이 좋습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 눈이 반짝였습니다. "이제 안전하게 리팩토링할 수 있겠어요!" 관련 함수 자동 찾기를 활용하면 코드 수정이 훨씬 안전해집니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 임계값은 0.7~0.8 사이가 적당합니다
- 결과는 유사도 높은 순으로 정렬하여 보여주세요
- 직접 호출 관계와 의미적 유사성을 함께 고려하면 더 좋습니다
4. 코드 스니펫 검색과 활용
김개발 씨가 새로운 기능을 구현하려다가 생각했습니다. "분명 이전에 비슷한 걸 본 것 같은데..." 박시니어 씨가 말했습니다.
"코드 스니펫 검색을 사용해보세요. 이전에 작성한 패턴을 재사용할 수 있어요."
코드 스니펫 검색은 프로젝트 전체에서 재사용 가능한 코드 조각들을 찾아주는 기능입니다. 마치 요리할 때 레시피북을 찾아보는 것처럼, 이전에 작성했던 검증된 코드 패턴을 빠르게 찾아서 활용할 수 있습니다.
이를 통해 코드 중복을 줄이고 개발 속도를 향상시킬 수 있습니다.
다음 코드를 살펴봅시다.
def search_code_snippets(query, collection, language=None, min_lines=5):
# 질문을 임베딩으로 변환
query_embedding = embed_code(query)
# 기본 검색 수행
results = collection.query(
query_embeddings=[query_embedding],
n_results=15,
where={"language": language} if language else None
)
# 스니펫 길이와 품질로 필터링
snippets = []
for i, doc in enumerate(results['documents'][0]):
lines = doc.split('\n')
if len(lines) >= min_lines:
snippets.append({
'code': doc,
'language': results['metadatas'][0][i].get('language'),
'file_path': results['metadatas'][0][i].get('path'),
'score': 1 - results['distances'][0][i]
})
return sorted(snippets, key=lambda x: x['score'], reverse=True)
# 사용 예시
snippets = search_code_snippets("API 요청 에러 핸들링", collection, language="python")
김개발 씨는 API 에러 처리 로직을 작성하고 있었습니다. 처음부터 작성하려니 시간이 오래 걸렸습니다.
분명 팀에서 누군가 비슷한 코드를 작성했을 텐데, 어디 있는지 찾을 수가 없었습니다. 박시니어 씨가 웃으며 말했습니다.
"코드 스니펫 검색을 사용하면 이전에 작성한 좋은 패턴들을 바로 찾을 수 있어요. 바퀴를 다시 발명할 필요가 없죠." 그렇다면 코드 스니펫 검색이란 정확히 무엇일까요?
쉽게 비유하자면, 코드 스니펫 검색은 마치 요리 레시피 검색과 같습니다. "간단한 파스타 요리"를 검색하면 여러 레시피가 나오듯이, "JWT 토큰 검증"을 검색하면 프로젝트 내의 관련 코드 조각들이 나타납니다.
이미 검증된 레시피를 따라 하면 실패할 확률이 낮아지는 것처럼, 검증된 코드 패턴을 재사용하면 버그가 줄어들고 개발 시간이 단축됩니다. 스니펫 검색이 없던 시절의 문제점은 무엇이었을까요?
개발자들은 비슷한 코드를 여러 번 작성했습니다. 한 개발자가 완벽한 에러 처리 로직을 만들어도, 다른 개발자는 그것을 모르고 처음부터 다시 작성했죠.
이렇게 코드 중복이 발생하면 유지보수가 어려워집니다. 한 곳의 버그를 고쳐도 다른 곳에서 같은 버그가 다시 나타날 수 있으니까요.
더 큰 문제는 좋은 패턴이 공유되지 않아 코드 품질이 일관되지 않았다는 점입니다. 바로 이런 문제를 해결하기 위해 코드 스니펫 검색이 등장했습니다.
스니펫 검색을 사용하면 검증된 패턴을 재사용할 수 있습니다. 시니어 개발자가 작성한 우수한 코드를 주니어 개발자도 쉽게 찾아서 학습하고 적용할 수 있습니다.
또한 언어별 필터링이 가능하여 원하는 언어의 코드만 볼 수 있습니다. 무엇보다 코드 품질이 향상되고 개발 속도가 빨라진다는 큰 이점이 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 search_code_snippets 함수는 자연어 질문과 함께 언어 필터, 최소 줄 수를 받습니다.
질문을 임베딩으로 변환한 후 collection.query를 호출합니다. where 파라미터로 특정 언어만 검색할 수 있습니다.
검색된 결과 중 min_lines 이상의 코드만 필터링합니다. 너무 짧은 코드는 재사용 가치가 낮기 때문입니다.
마지막으로 유사도 점수로 정렬하여 반환합니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 대규모 SaaS 플랫폼을 개발한다고 가정해봅시다. 새로운 API 엔드포인트를 만들어야 합니다.
"인증과 권한 확인을 포함한 API 엔드포인트"로 검색하면, 이전에 작성된 우수한 패턴들이 나타납니다. 이를 참고하여 일관된 구조로 빠르게 개발할 수 있습니다.
Airbnb, Uber 같은 회사들이 내부 개발자 도구로 이런 시스템을 구축하여 사용합니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 코드를 이해하지 않고 복사하는 것입니다. 스니펫은 참고용이지 맹목적으로 복사할 대상이 아닙니다.
반드시 코드를 읽고 이해한 후 자신의 상황에 맞게 수정해서 사용해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 감탄했습니다. "팀의 지식이 자동으로 공유되는 거네요!" 코드 스니펫 검색을 활용하면 팀 전체의 코드 품질이 향상됩니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 언어별 필터를 활용하여 원하는 언어의 코드만 검색하세요
- 너무 짧은 스니펫보다는 5줄 이상의 의미 있는 패턴을 찾으세요
- 검색된 코드는 반드시 이해한 후 사용하세요
5. 실습 ChromaDB로 코드 벡터 DB 구축
이론은 충분히 배웠다고 생각한 김개발 씨가 실습을 시작했습니다. 박시니어 씨가 말했습니다.
"이제 ChromaDB를 사용해서 실제로 코드 벡터 DB를 구축해볼까요? 생각보다 간단해요."
ChromaDB는 임베딩 벡터를 저장하고 검색하는 오픈소스 벡터 데이터베이스입니다. 마치 PostgreSQL이 일반 데이터를 저장하듯이, ChromaDB는 벡터 데이터에 특화되어 있습니다.
설치와 사용이 간단하여 프로토타입 개발에 적합하고, 나중에 프로덕션 환경으로 확장하기도 쉽습니다.
다음 코드를 살펴봅시다.
import chromadb
from chromadb.config import Settings
import os
# ChromaDB 클라이언트 초기화 (영구 저장소 사용)
client = chromadb.Client(Settings(
chroma_db_impl="duckdb+parquet",
persist_directory="./code_vectordb"
))
# 컬렉션 생성 (이미 있으면 가져오기)
collection = client.get_or_create_collection(
name="my_codebase",
metadata={"description": "프로젝트 코드베이스"}
)
# 여러 코드 파일을 한 번에 추가
codes = [
"def authenticate(username, password): return verify_credentials(username, password)",
"def validate_email(email): import re; return re.match(r'^[\\w.-]+@[\\w.-]+\\.\\w+$', email)",
"def calculate_discount(price, rate): return price * (1 - rate)"
]
embeddings = [embed_code(code) for code in codes]
ids = [f"func_{i:03d}" for i in range(len(codes))]
collection.add(embeddings=embeddings, documents=codes, ids=ids)
# 저장 확인
print(f"총 {collection.count()}개의 코드가 저장되었습니다")
김개발 씨는 드디어 실습에 들어갔습니다. 지금까지는 개념만 배웠는데, 이제 직접 만들어볼 차례입니다.
컴퓨터 앞에 앉아 ChromaDB 설치부터 시작했습니다. 박시니어 씨가 옆에서 지켜보며 말했습니다.
"ChromaDB는 정말 간단해요. pip install chromadb 한 줄이면 끝이에요." 그렇다면 ChromaDB란 정확히 무엇일까요?
쉽게 비유하자면, ChromaDB는 마치 벡터 전용 창고와 같습니다. 일반 창고(MySQL, PostgreSQL)는 상자, 가구 같은 물건을 저장하지만, 벡터 창고는 오로지 숫자 배열만 효율적으로 저장합니다.
벽면에 좌표를 붙여서 비슷한 위치의 물건을 빠르게 찾을 수 있도록 설계되어 있죠. ChromaDB도 이처럼 벡터 간 거리 계산에 최적화되어 있습니다.
벡터 DB가 없던 시절에는 어떻게 했을까요? 개발자들은 일반 데이터베이스에 벡터를 저장했습니다.
하지만 검색할 때마다 모든 벡터와 거리를 계산해야 했습니다. 벡터가 1000개면 괜찮지만, 100만 개가 넘어가면 검색 속도가 너무 느려졌죠.
더 큰 문제는 인덱싱이 제대로 되지 않아 최적화가 불가능했다는 점입니다. 결국 실시간 검색은 꿈도 꿀 수 없었습니다.
바로 이런 문제를 해결하기 위해 전문 벡터 DB가 등장했습니다. ChromaDB를 사용하면 빠른 유사도 검색이 가능합니다.
내부적으로 HNSW 같은 알고리즘을 사용하여 수백만 개의 벡터 중에서도 밀리초 단위로 검색할 수 있습니다. 또한 메타데이터 필터링을 지원하여 언어, 파일 경로 같은 조건으로 필터링할 수 있습니다.
무엇보다 설정이 간단하고 로컬에서 바로 실행할 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 chromadb.Client를 생성할 때 Settings를 전달합니다. persist_directory를 지정하면 데이터가 디스크에 영구 저장됩니다.
프로그램을 껐다 켜도 데이터가 사라지지 않는 거죠. get_or_create_collection은 컬렉션이 없으면 만들고, 있으면 기존 것을 가져옵니다.
여러 코드를 리스트로 준비하고, 각각을 임베딩으로 변환합니다. collection.add로 한 번에 저장할 수 있습니다.
마지막으로 collection.count()로 잘 저장되었는지 확인합니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 스타트업에서 AI 코드 어시스턴트를 만든다고 가정해봅시다. 프로토타입 단계에서는 ChromaDB로 빠르게 구축합니다.
로컬 개발 환경에서 충분히 테스트할 수 있습니다. 나중에 사용자가 늘어나면 Pinecone이나 Weaviate 같은 클라우드 벡터 DB로 마이그레이션할 수 있습니다.
많은 AI 스타트업들이 이런 방식으로 빠르게 MVP를 만들어 시장 검증을 합니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 persist_directory를 지정하지 않는 것입니다. 그러면 데이터가 메모리에만 저장되어 프로그램을 종료하면 모두 사라집니다.
반드시 영구 저장소 경로를 지정하세요. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 도움으로 김개발 씨는 성공적으로 벡터 DB를 구축했습니다. "생각보다 간단하네요!" ChromaDB로 시작하면 벡터 DB를 쉽게 배울 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - persist_directory는 반드시 지정하여 데이터를 영구 저장하세요
- 메타데이터에 파일 경로, 언어, 작성자 등을 포함하면 나중에 필터링하기 좋습니다
- 배치로 여러 데이터를 한 번에 추가하면 성능이 더 좋습니다
6. 실습 비슷한 코드 찾기 기능 구현
마지막 실습 시간입니다. 김개발 씨는 이제 모든 것을 종합하여 실제로 사용할 수 있는 코드 검색 시스템을 만들어보려 합니다.
박시니어 씨가 격려했습니다. "지금까지 배운 걸 다 합치면 훌륭한 시스템이 완성될 거예요."
비슷한 코드 찾기는 RAG 기반 코드 검색의 핵심 기능으로, 사용자가 원하는 기능을 설명하면 프로젝트 내에서 관련 코드를 자동으로 찾아주는 완전한 시스템입니다. 마치 구글에서 검색하듯이 자연스럽게 코드를 찾을 수 있습니다.
이를 통해 개발 생산성이 향상되고 코드 재사용이 활성화됩니다.
다음 코드를 살펴봅시다.
class CodeSearchSystem:
def __init__(self, db_path="./code_vectordb"):
self.client = chromadb.Client(Settings(
persist_directory=db_path
))
self.collection = self.client.get_or_create_collection("codebase")
def index_codebase(self, code_files):
# 코드 파일들을 읽어서 인덱싱
for file_path, code in code_files.items():
embedding = embed_code(code)
self.collection.add(
embeddings=[embedding],
documents=[code],
ids=[file_path],
metadatas=[{"path": file_path, "language": "python"}]
)
def find_similar(self, query, top_k=5, min_score=0.6):
# 자연어 질문으로 유사 코드 검색
query_embedding = embed_code(query)
results = self.collection.query(
query_embeddings=[query_embedding],
n_results=top_k
)
# 결과를 정리하여 반환
similar_codes = []
for i, doc in enumerate(results['documents'][0]):
score = 1 - results['distances'][0][i]
if score >= min_score:
similar_codes.append({
'code': doc,
'path': results['metadatas'][0][i]['path'],
'score': score
})
return similar_codes
# 사용 예시
system = CodeSearchSystem()
results = system.find_similar("데이터베이스 연결을 관리하는 코드")
for r in results:
print(f"파일: {r['path']}, 유사도: {r['score']:.2f}")
김개발 씨는 이제 최종 프로젝트를 시작했습니다. 지금까지 배운 모든 것을 하나로 합쳐서 실제로 사용할 수 있는 시스템을 만드는 것입니다.
손가락에 힘을 주고 코딩을 시작했습니다. 박시니어 씨가 커피를 한 잔 건네며 말했습니다.
"차근차근 하나씩 만들어보세요. 생각보다 어렵지 않을 거예요." 그렇다면 비슷한 코드 찾기 시스템이란 정확히 무엇일까요?
쉽게 비유하자면, 이 시스템은 마치 개인 비서와 같습니다. "서울에서 맛있는 이탈리안 레스토랑 찾아줘"라고 하면 비서가 여러 옵션을 추천해주듯이, "파일 업로드 처리하는 코드 찾아줘"라고 하면 관련된 모든 코드를 찾아줍니다.
단순한 검색 엔진이 아니라 맥락을 이해하는 똑똑한 어시스턴트입니다. 이런 시스템이 없던 시절에는 어떻게 코드를 찾았을까요?
개발자들은 프로젝트 폴더를 하나하나 열어보며 코드를 찾았습니다. 파일이 수백 개가 넘으면 몇 시간씩 걸리기도 했죠.
또는 동료에게 "이거 어디 있어요?"라고 물어보며 시간을 허비했습니다. 더 큰 문제는 존재하는 좋은 코드를 모르고 처음부터 다시 작성하는 일이 빈번했다는 점입니다.
결국 코드 중복이 심해지고 품질이 떨어졌습니다. 바로 이런 문제를 해결하기 위해 통합 코드 검색 시스템이 필요했습니다.
이 시스템을 사용하면 자연어로 질문할 수 있습니다. 전문 용어를 몰라도 괜찮습니다.
"사용자한테 이메일 보내는 거"라고 검색해도 send_notification 같은 함수를 찾아줍니다. 또한 유사도 점수를 보여주어 얼마나 관련성이 높은지 판단할 수 있습니다.
무엇보다 전체 코드베이스를 인덱싱하여 어떤 코드든 빠르게 찾을 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 CodeSearchSystem 클래스를 정의합니다. 객체지향으로 만들어 재사용하기 쉽게 했습니다.
__init__ 메서드에서 ChromaDB 클라이언트와 컬렉션을 초기화합니다. index_codebase 메서드는 여러 코드 파일을 받아서 한 번에 인덱싱합니다.
파일 경로를 ID로 사용하고, 메타데이터에도 경로를 저장합니다. find_similar 메서드는 실제 검색을 수행합니다.
min_score를 설정하여 관련성이 낮은 결과는 필터링합니다. 결과를 보기 좋게 정리하여 반환합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 대규모 오픈소스 프로젝트에 기여하려는 신입 개발자가 있다고 가정해봅시다.
프로젝트가 너무 커서 어디서부터 시작해야 할지 막막합니다. 이 시스템에 "REST API 엔드포인트 추가하는 방법"이라고 검색하면, 기존에 작성된 엔드포인트 코드들이 나타납니다.
그것을 참고하여 일관된 스타일로 새 엔드포인트를 만들 수 있습니다. GitHub Copilot 같은 도구들도 내부적으로 비슷한 원리를 사용합니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 모든 코드를 한 번에 인덱싱하려는 것입니다.
프로젝트가 크면 시간이 너무 오래 걸리고 API 비용도 많이 나옵니다. 따라서 중요한 파일부터 단계적으로 인덱싱하는 것이 좋습니다.
또한 정기적으로 인덱스를 업데이트해야 최신 코드를 반영할 수 있습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 격려 속에 김개발 씨는 마침내 완전한 시스템을 완성했습니다. "드디어 완성했어요!" 테스트를 해보니 정말로 자연어 질문만으로 코드를 찾을 수 있었습니다.
"이제 우리 팀 전체가 사용할 수 있겠어요!" 김개발 씨는 뿌듯해하며 말했습니다. 비슷한 코드 찾기 시스템을 구축하면 팀 전체의 생산성이 향상됩니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요. 처음에는 작은 프로젝트부터 시작하여 점차 확장해나가는 것을 추천합니다.
실전 팁
💡 - 전체 코드베이스를 한 번에 인덱싱하지 말고 중요한 파일부터 시작하세요
- 코드가 수정될 때마다 자동으로 재인덱싱하는 워크플로우를 구축하세요
- 검색 결과에 코드 컨텍스트(주변 코드)도 함께 보여주면 더 유용합니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
ReAct 패턴 마스터 완벽 가이드
LLM이 생각하고 행동하는 ReAct 패턴을 처음부터 끝까지 배웁니다. Thought-Action-Observation 루프로 똑똑한 에이전트를 만들고, 실전 예제로 웹 검색과 계산을 결합한 강력한 AI 시스템을 구축합니다.
AI 에이전트의 모든 것 - 개념부터 실습까지
AI 에이전트란 무엇일까요? 단순한 LLM 호출과 어떻게 다를까요? 초급 개발자를 위해 에이전트의 핵심 개념부터 실제 구현까지 이북처럼 술술 읽히는 스타일로 설명합니다.
프로덕션 RAG 시스템 완벽 가이드
검색 증강 생성(RAG) 시스템을 실제 서비스로 배포하기 위한 확장성, 비용 최적화, 모니터링 전략을 다룹니다. AWS/GCP 배포 실습과 대시보드 구축까지 프로덕션 환경의 모든 것을 담았습니다.
RAG 캐싱 전략 완벽 가이드
RAG 시스템의 성능을 획기적으로 개선하는 캐싱 전략을 배웁니다. 쿼리 캐싱부터 임베딩 캐싱, Redis 통합까지 실무에서 바로 적용할 수 있는 최적화 기법을 다룹니다.
실시간으로 답변하는 RAG 시스템 만들기
사용자가 질문하면 즉시 답변이 스트리밍되는 RAG 시스템을 구축하는 방법을 배웁니다. 실시간 응답 생성부터 청크별 스트리밍, 사용자 경험 최적화까지 실무에서 바로 적용할 수 있는 완전한 가이드입니다.