0Pricing
AI Engineering Academy · 강의

코드와 HTML을 위한 문서별 전략

AST 기반 함수 분할기를 사용해 Python 코드에 특화된 청킹을 적용하고, HTML에는 태그를 인식하는 파서를, Markdown에는 머리글 계층 구조를 적용합니다.

코드와 HTML을 위한 문서별 전략은(는) CoddyKit의 무료 AI Engineering Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Engineering Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

전용 문서에서 일반 조각화가 실패하는 이유

텍스트 기반 조각화는 산문을 위해 설계되었지만, 실제 데이터에는 소스 코드, HTML 페이지, Markdown 문서도 포함됩니다. 고정된 문자 경계에서 코드를 나누면 함수 본문 중간이 끊어져 검색에 쓸모없는 조각이 될 수 있습니다. 전용 문서에는 단순히 길이만 고려하는 것이 아니라 내부 구조를 이해하는 조각화기가 필요합니다.

AST 기반 Python 코드 조각화

Python 파일의 추상 구문 트리(AST)는 모든 함수, 클래스, 모듈을 구조화된 노드로 표현합니다. AST를 순회하면 각 함수나 메서드를 하나의 조각으로 추출하여 시그니처, 독스트링, 본문을 함께 유지할 수 있습니다. LangChain의 PythonCodeTextSplitter는 내부적으로 이 방식을 사용합니다.

import ast
import textwrap

def extract_functions(source_code: str) -> list[dict]:
    tree = ast.parse(source_code)
    chunks = []
    for node in ast.walk(tree):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
            start = node.lineno - 1
            end = node.end_lineno
            lines = source_code.splitlines()[start:end]
            chunks.append({
                'name': node.name,
                'code': '\n'.join(lines),
                'start_line': node.lineno,
            })
    return chunks

클래스 경계에 따른 조각화

객체 지향 코드베이스에서는 함수 수준보다 클래스 수준에서 조각화하는 편이 더 나은 경우가 많습니다. 클래스 조각은 메서드와 메서드가 함께 사용하는 공유 상태 사이의 관계를 유지합니다. 클래스 독스트링과 모든 메서드 본문을 하나의 조각에 포함한 다음, 긴 메서드에 대해서만 더 세분화된 조각을 별도로 만들 수 있습니다.

from langchain_text_splitters import Language, RecursiveCharacterTextSplitter

python_splitter = RecursiveCharacterTextSplitter.from_language(
    language=Language.PYTHON,
    chunk_size=1000,
    chunk_overlap=100,
)

with open('my_module.py', 'r') as f:
    source = f.read()

chunks = python_splitter.create_documents([source])
print(f'Created {len(chunks)} code chunks')

조각에 코드 메타데이터 추가

원시 코드 조각의 유용성은 메타데이터에 달려 있습니다. 벡터 데이터베이스에 코드 조각을 저장할 때 파일 경로, 함수 이름, 프로그래밍 언어, 줄 범위를 포함하세요. 이 메타데이터를 사용하면 검색기가 언어나 파일로 필터링할 수 있고, LLM은 답변에서 정확한 소스 위치를 인용할 수 있습니다.

from langchain_core.documents import Document

def chunk_python_file(filepath: str) -> list[Document]:
    with open(filepath) as f:
        source = f.read()

    functions = extract_functions(source)  # from previous example
    docs = []
    for fn in functions:
        docs.append(Document(
            page_content=fn['code'],
            metadata={
                'source': filepath,
                'function': fn['name'],
                'language': 'python',
                'start_line': fn['start_line'],
            }
        ))
    return docs

HTML: 문자보다 구조 우선

HTML 문서는 제목, 섹션, 문단, 목록으로 계층적으로 구성됩니다. 문자 수를 기준으로 HTML을 나누면 태그 중간이 잘려 잘못된 조각이 만들어지는 경우가 많습니다. 올바른 방법은 BeautifulSoup 같은 적절한 파서로 HTML을 분석하고 <article>, <section>, <p> 태그처럼 의미가 있는 요소를 추출하는 것입니다.

from bs4 import BeautifulSoup

def chunk_html_by_section(html: str) -> list[dict]:
    soup = BeautifulSoup(html, 'html.parser')
    chunks = []
    for tag in soup.find_all(['h1', 'h2', 'h3', 'p', 'li']):
        text = tag.get_text(separator=' ', strip=True)
        if len(text) > 40:  # skip trivial fragments
            chunks.append({
                'tag': tag.name,
                'text': text,
            })
    return chunks

헤더 계층 기반 HTML 조각화

더 정교한 HTML 전략은 콘텐츠를 가장 가까운 제목 아래에 그룹화합니다. <h2> 헤더 뒤에 나오는 모든 문단과 목록은 해당 섹션에 속합니다. 텍스트를 상위 제목과 함께 그룹화하면 독립된 문단만으로는 잃게 되는 주제 맥락을 보존할 수 있습니다. LangChain의 HTMLHeaderTextSplitter가 이를 자동으로 구현합니다.

from langchain_text_splitters import HTMLHeaderTextSplitter

headers_to_split_on = [
    ('h1', 'Header 1'),
    ('h2', 'Header 2'),
    ('h3', 'Header 3'),
]

splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)

with open('page.html') as f:
    html = f.read()

sections = splitter.split_text(html)
for sec in sections[:3]:
    print(sec.metadata)
    print(sec.page_content[:200])
    print('---')

Markdown: 제목 계층 유지

Markdown 문서는 #, ##, ### 제목으로 구성됩니다. MarkdownHeaderTextSplitter는 제목 경계에서 나누고 제목 계층을 메타데이터에 저장합니다. 따라서 모든 조각이 전체 제목 경로를 알 수 있으며, 사용자가 문서의 특정 섹션에 대해 질문할 때 검색된 맥락의 관련성이 크게 향상됩니다.

from langchain_text_splitters import MarkdownHeaderTextSplitter

headers_to_split_on = [
    ('#', 'H1'),
    ('##', 'H2'),
    ('###', 'H3'),
]

md_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)

with open('README.md') as f:
    markdown = f.read()

docs = md_splitter.split_text(markdown)
for doc in docs[:2]:
    print('Metadata:', doc.metadata)
    print('Content:', doc.page_content[:300])
    print()

헤더 분할 후 추가 분할

제목을 기준으로 나눈 후에도 개별 섹션이 임베딩 모델의 토큰 제한을 초과할 수 있습니다. 권장 패턴은 2단계 분할입니다. 먼저 제목 계층을 기준으로 나누어 의미 맥락을 보존한 다음, 조각 크기 제한을 초과하는 섹션에 문자 기반 분할기를 적용합니다. 이렇게 하면 제목 메타데이터를 유지하면서 어떤 조각도 지나치게 커지지 않도록 할 수 있습니다.

from langchain_text_splitters import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter

header_splitter = MarkdownHeaderTextSplitter(
    headers_to_split_on=[('#', 'H1'), ('##', 'H2')]
)
char_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
)

with open('docs.md') as f:
    md = f.read()

header_chunks = header_splitter.split_text(md)
final_chunks = char_splitter.split_documents(header_chunks)
print(f'{len(final_chunks)} final chunks produced')

표 인식을 고려한 PDF 조각화

PyMuPDF나 PDF플러머 같은 도구로 추출한 PDF는 표 구조가 손실되어 텍스트 행이 뒤섞이는 경우가 많습니다. 이를 처리하려면 표의 경계 상자를 감지하고, 조각화하기 전에 표를 Markdown 또는 CSV 형식으로 변환하는 레이아웃 인식 PDF 파서를 사용하세요. 각 표는 산문이 아니라 표임을 나타내는 구조화된 메타데이터와 함께 하나의 조각으로 처리해야 합니다.

import pdfplumber

def extract_pdf_chunks(pdf_path: str) -> list[dict]:
    chunks = []
    with pdfplumber.open(pdf_path) as pdf:
        for page_num, page in enumerate(pdf.pages):
            # Extract tables separately
            for table in page.extract_tables():
                rows = ['|'.join(str(c) for c in row) for row in table]
                chunks.append({
                    'type': 'table',
                    'content': '\n'.join(rows),
                    'page': page_num + 1,
                })
            # Extract prose text
            text = page.extract_text()
            if text:
                chunks.append({'type': 'text', 'content': text, 'page': page_num + 1})
    return chunks

혼합 말뭉치를 위한 언어 감지

기업 지식 기반에는 Python 스크립트, HTML로 작성된 API 문서, Markdown으로 작성된 아키텍처 노트, CSV로 내보낸 데이터 등 다양한 파일 유형이 함께 포함되는 경우가 많습니다. 견고한 조각화 파이프라인은 확장자나 MIME 유형에서 파일 유형을 감지하고 각 문서를 적절한 전용 조각화기로 전달해야 합니다. 이렇게 하면 산문에 코드 분할 로직을 적용하거나 그 반대의 상황을 방지할 수 있습니다.

from pathlib import Path

def route_document(filepath: str) -> list[dict]:
    ext = Path(filepath).suffix.lower()
    if ext == '.py':
        return chunk_python_file(filepath)
    elif ext in ('.html', '.htm'):
        with open(filepath) as f:
            return chunk_html_by_section(f.read())
    elif ext == '.md':
        # use MarkdownHeaderTextSplitter
        return chunk_markdown(filepath)
    elif ext == '.pdf':
        return extract_pdf_chunks(filepath)
    else:
        # fallback: plain text recursive splitter
        return chunk_plain_text(filepath)

주변 줄로 맥락 보존

함수 단위로 코드를 조각화할 때는 파일 상단의 import 문이나 메서드를 포함하는 클래스 정의처럼 주변 맥락을 몇 줄 포함하는 것이 유용한 경우가 많습니다. 이러한 맥락은 LLM이 사용할 수 있는 라이브러리와 더 큰 클래스 안에서 함수가 맡은 역할을 이해하는 데 도움을 주어 생성된 답변의 품질을 높입니다.

def chunk_with_imports(source_code: str, fn_node, lines: list[str]) -> str:
    # Gather top-of-file imports (first block before first non-import)
    import_lines = []
    for line in lines:
        stripped = line.strip()
        if stripped.startswith('import ') or stripped.startswith('from '):
            import_lines.append(line)
        elif stripped and not stripped.startswith('#'):
            break

    fn_body = '\n'.join(lines[fn_node.lineno - 1:fn_node.end_lineno])
    return '\n'.join(import_lines) + '\n\n' + fn_body

빠른 확인

이 단원에서 배운 문서별 조각화 전략에 대한 이해도를 확인해 보세요.

단원 복습

이 단원에서는 AST 기반 조각화가 Python 함수와 클래스의 경계를 보존한다는 점, HTMLHeaderTextSplitter와 MarkdownHeaderTextSplitter가 제목 계층을 존중하여 섹션의 맥락을 유지한다는 점, 그리고 2단계 접근 방식(제목 분할 후 문자 분할)을 사용하면 구조 메타데이터를 잃지 않고 지나치게 큰 섹션을 처리할 수 있다는 점을 배웠습니다. 다음으로는 밀집 검색과 희소 검색을 결합하는 하이브리드 검색을 살펴봅니다.

자주 묻는 질문

“코드와 HTML을 위한 문서별 전략” 강의는 무료인가요?

네 — “코드와 HTML을 위한 문서별 전략” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Engineering Academy 강의 전체를 잠금 해제할 수 있습니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“코드와 HTML을 위한 문서별 전략”에서 뭘 배우나요?

AST 기반 함수 분할기를 사용해 Python 코드에 특화된 청킹을 적용하고, HTML에는 태그를 인식하는 파서를, Markdown에는 머리글 계층 구조를 적용합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Engineering Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

AI Engineering Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 AI Engineering Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“코드와 HTML을 위한 문서별 전략” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 AI Engineering Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 AI Engineering Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 순진한 청킹이 검색에 해로운 이유
  2. 임베딩 유사도를 활용한 의미 기반 청킹
  3. 상위-하위 및 작은 단위에서 큰 단위로 검색
  4. 코드와 HTML을 위한 문서별 전략
← AI Engineering Academy(으)로 돌아가기