AI를 활용한 API 문서 자동화 방법 – 학습한 교훈
요약
레거시 Django 코드베이스의 API 문서를 자동화하기 위해 LLM을 활용한 경험을 공유합니다. 단순 복사-붙여넣기나 API 스크립트의 한계를 극복하고, RAG 기반 파이프라인을 구축하여 문맥을 유지하며 정확한 docstring을 생성하는 방법을 다룹니다.
핵심 포인트
- 단순 프롬프팅은 토큰 제한과 일관성 문제로 대규모 코드에 부적합함
- API 사용 시 환각 현상과 포맷팅 오류 및 비용 문제가 발생할 수 있음
- 데이터 프라이버시를 위해 로컬 기반의 솔루션 검토가 필요함
- RAG와 LangChain을 활용해 코드 문맥을 유지하는 것이 핵심임
몇 달 전 저는 레거시 Django 코드베이스를 물려받았습니다. 코드는 작동했지만, docstring도, README도, 아무것도 없는 문서화가 전혀 되어 있지 않은 상태였습니다. 처음에는 수동으로 작성하려 했지만, 3일 동안 겨우 10%의 라우트(route)만 처리한 후 더 나은 방법이 있어야 한다는 것을 깨달았습니다.
그래서 LLM을 사용하여 문서를 자동으로 생성하는 방법을 조사하기 시작했습니다. 제가 발견한 것은 놀라웠습니다. 단순히 "코드를 GPT에 던져주고 문서를 받아내는 것"만큼 간단하지 않았습니다. 제가 시도했던 것, 실패했던 것, 그리고 결국 성공했던 방법을 소개합니다.
문제점: 레거시 코드, 주석 없음
이 프로젝트에는 여러 파일에 걸쳐 약 50개의 API 엔드포인트(endpoint)가 흩어져 있었습니다. 비즈니스 로직이 뷰(view)와 섞여 있었고, 일부 함수는 길이가 500줄에 달했습니다. 저는 모든 함수에 대한 docstring과 각 엔드포인트에 대한 상위 수준의 개요가 필요했습니다.
Doxygen이나 Sphinx 같은 도구를 사용할 수도 있었지만, 이들은 기존의 주석을 추출할 뿐 새로운 주석을 작성하지는 못합니다. 그래서 저는 LLM으로 눈을 돌렸습니다.
처음 시도했던 것 (그리고 왜 실패했는가)
시도 1: ChatGPT에 복사-붙여넣기
저는 말 그대로 200줄짜리 뷰 함수를 ChatGPT에 복사한 뒤 "이 함수에 대한 docstring을 작성해줘"라는 프롬프트(prompt)를 입력했습니다. 첫 번째는 성공했지만, 그 이후에는 다음과 같은 문제가 발생했습니다:
- 토큰 제한(Token limits) 때문에 코드를 수동으로 분할해야 했습니다.
- 결과물이 일관되지 않았습니다. 때로는 파라미터(parameter)를 잘못 추측하기도 했습니다.
- 50개의 엔드포인트에 대해 이 작업을 수행하는 것은 너무 지루했습니다.
시도 2: OpenAI API를 사용한 간단한 Python 스크립트
각 파일을 읽어 청크(chunk)를 GPT-3.5로 보내고 docstring을 삽입하는 간단한 스크립트를 작성했습니다. 하지만 속도 제한(rate limits)에 걸렸고, 포맷팅(formatting)이 엉망이 되었으며, LLM이 파라미터 타입을 환각(hallucination)하는 경우가 많았습니다.
import openai
openai.api_key = "sk-..."
...
```
이 방식은 작은 코드 조각에는 효과적이었지만, 큰 파일의 경우 함수 단위로 나누어야 했기에 문맥(context)을 잃게 되었습니다. 게다가 비용도 계속 쌓였습니다.
시도 3: 호스팅된 AI 문서화 서비스 사용
몇 가지 호스팅된 솔루션( https://ai.interwestinfo.com/ 에 있는 솔루션 포함)을 살펴보았습니다. 유망한 것들도 있었지만, 전체 코드베이스를 해당 서버에 업로드해야 한다는 점이 문제였습니다. 데이터 프라이버시 문제로 인해 이 클라이언트 프로젝트에서는 그럴 수 없었습니다. 저는 로컬(local)에서 작동하는 무언가가 필요했습니다.
결국 성공한 방법: RAG 기반 파이프라인
진짜 문제는 문맥(context)이라는 것을 깨달았습니다. LLM은 전체 코드베이스를 한 번에 읽을 수 없습니다. 저는 검색 증강 생성 (RAG, Retrieval-Augmented Generation) 접근 방식이 필요했습니다. 즉, 코드를 인덱싱(index)한 다음, 각 함수에 대해 관련 코드(import 문, 해당 함수가 호출하는 다른 함수들)를 검색하여 함수 자체와 함께 LLM에 입력하는 방식입니다.
저는 이를 오케스트레이션(orchestrate)하기 위해 LangChain을 사용했습니다. 단순화된 파이프라인은 다음과 같습니다:
- 코드베이스 로드 및 청킹 (Load and chunk): 코드베이스를 로드하고 함수당 하나의 청크(chunk)로 나눕니다.
- 각 청크 임베딩 (Embed each chunk): 로컬 임베딩 모델 (sentence-transformers)을 사용하여 각 청크를 임베딩합니다.
- 임베딩 저장 (Store embeddings): 벡터 스토어 (Chroma)에 임베딩을 저장합니다.
- 각 대상 함수에 대해 (For each target function): 벡터 스토어에 쿼리하여 가장 유사한 상위 3개의 코드 청크(예: 해당 함수가 import 하거나 의존하는 함수들)를 가져옵니다.
- 프롬프트 구축 (Build a prompt): 대상 함수 + 문맥(context) + 지침(instructions)을 포함하는 프롬프트를 작성합니다.
- GPT-4로 전송 (Send to GPT-4): (코드 이해도 측면에서 GPT-4가 3.5보다 훨씬 뛰어나다는 것을 확인했습니다).
- 응답 파싱 (Parse the response): 응답을 파싱하여 파일에 docstring을 다시 작성합니다.
다음은 핵심 코드입니다 (명확성을 위해 단순화되었습니다):
import os
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
...
이것은 환상적이었습니다. LLM이 정확한 docstring을 생성할 수 있을 만큼 충분한 문맥(context)을 갖게 되었습니다. 저는 팀의 스타일(Google-style docstrings)에 맞게 프롬프트를 미세 조정했습니다. 200개의 함수를 실행하는 데 총 2시간 정도가 소요되었으며, GPT-4 API 비용은 약 15달러 정도 들었습니다.
교훈 및 트레이드오프 (Lessons Learned & Trade-offs)
- GPT-4는 코드 작업에 비용을 들일 가치가 있습니다. GPT-3.5는 문맥을 놓치거나 파라미터(parameter)를 지어내는 경우가 많았습니다.
- **로컬 임베딩 (Local embeddings)**을 사용하여 검색 (retrieval) 단계에서 비용을 절감했습니다. 저는 저렴하고 빠른
all-MiniLM-L6-v2를 사용했습니다. - 컨텍스트 윈도우 (Context window)가 중요합니다: 매우 긴 함수(function)의 경우, 컨텍스트를 잘라내야(truncate) 했습니다. 그런 경우에는 더 작은 프롬프트 (prompt)로 대체했습니다.
- 모든 언어가 잘 작동하는 것은 아닙니다: Python은 괜찮았지만, Ruby 프로젝트에 시도했을 때는 결과가 더 좋지 않았습니다. 결과는 상황에 따라 다를 수 있습니다.
- 여전히 사람의 검토가 필요합니다: 생성된 문서는 약 80%의 정확도를 보였습니다. 파라미터 이름과 예외 케이스 (edge cases)를 직접 수정해야 했습니다.
- 데이터 프라이버시 (Data privacy): 로컬에서 실행하거나 (또는 프라이빗 LLM을 사용하면) 민감한 코드를 제3자에게 전송하는 것을 피할 수 있습니다. 이것이 제가 앞서 언급한 호스팅 서비스를 사용하지 않은 이유입니다. 물론 민감하지 않은 프로젝트라면 사용해도 괜찮을 것입니다.
다음에 다르게 할 점 (What I'd Do Differently Next Time)
- 일반적인 모델 대신
codebert나graphcodebert와 같은 **코드 특화 임베딩 모델 (code-specific embedding model)**을 사용할 것입니다. 그러면 유사도 매칭 (similarity matches) 성능이 향상될 수 있습니다. - **검증 단계 (validation step)**를 추가하겠습니다. docstring을 생성한 후, 언급된 모든 파라미터가 함수 시그니처 (function signature)에 실제로 존재하는지 간단한 정규 표현식 (regex)으로 확인하는 것입니다. 이를 통해 환각 (hallucinations) 현상을 잡아낼 수 있었습니다.
- 충분한 예시가 있다면 docstring 작업을 위해 **작은 모델을 파인튜닝 (fine-tuning)**하는 것을 고려할 것입니다. 하지만 일회성 프로젝트라면 GPT-4로도 충분했습니다.
더 큰 그림 (The Bigger Picture)
AI를 활용한 문서화 자동화는 충분히 가능하지만, 마법의 버튼은 아닙니다. 문맥, 비용, 그리고 프라이버시를 고려해야 합니다. 제가 설명한 방식인 LLM을 활용한 RAG (Retrieval-Augmented Generation)는 테스트 생성, 변경 사항 요약, 또는 복잡한 로직 설명과 같은 많은 코드 이해 작업에 적용될 수 있습니다.
유사한 프로젝트를 고려 중이라면 작게 시작하세요. 모듈 하나를 선택하여 파이프라인 (pipeline) 프로토타입을 만들고, 출력 품질이 기준에 부합하는지 확인하세요. 그 다음 규모를 키워나가면 됩니다.
AI가 생성한 코드 문서화에 대한 여러분의 경험은 어떠신가요? 컨텍스트 문제를 해결할 더 나은 방법을 찾으셨나요? 여러분에게 무엇이 효과가 있었는지, 혹은 실패했는지 듣고 싶습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기