LLM 어플리케이션 이해하기 (Text to SQL, Finetuning, RAG 등)

2026. 5. 3. 15:45·기타

Encoder-Decoder

Encoder-Decoder 구조는 입력 데이터를 이해하는 인코더(Encoder) 와, 그 이해 결과를 바탕으로 출력을 생성하는 디코더(Decoder) 로 나뉜 신경망 구조입니다.

 

가장 대표적인 예시는 번역입니다.

입력: I am studying LLM applications.
출력: 나는 LLM 애플리케이션을 공부하고 있다.
 

이때 인코더는 영어 문장을 읽고 의미를 표현합니다.
디코더는 그 의미 표현을 참고해서 한국어 문장을 생성합니다.

사람에 비유하면, 인코더는 외국어 문장을 읽고 내용을 파악하는 사람이고, 디코더는 그 내용을 한국어로 말하는 사람입니다.

다만 실제 모델은 입력을 숫자 벡터들의 집합으로 바꾸고 그 벡터들 사이의 관계를 계산합니다.

 

Encoder-Decoder 구조는 대략 다음 흐름으로 동작합니다.

입력 문장
→ 토큰화
→ 임베딩
→ 인코더 처리
→ 디코더 생성
→ 출력 문장
 

예를 들어 영어 문장을 한국어로 번역한다고 해보겠습니다.

I am studying LLM applications.
 

먼저 문장이 토큰으로 나뉩니다.

["I", "am", "studying", "LLM", "applications", "."]
 

각 토큰은 숫자 벡터인 임베딩으로 바뀝니다. 모델은 텍스트를 직접 다루지 않고 숫자 벡터를 다룹니다.

"I" → [0.12, -0.03, 0.55, ...]
"studying" → [-0.21, 0.48, 0.17, ...]
 

그다음 인코더가 입력 문장 전체를 처리합니다. 인코더는 각 단어가 문장 안에서 어떤 역할을 하는지, 다른 단어와 어떤 관계를 갖는지 계산합니다. 예를 들어 "I"는 주어이고, "studying"은 동작이며, "LLM applications"는 공부 대상이라는 식의 관계가 내부적으로 반영됩니다.

이후 디코더는 인코더가 만든 표현을 참고하면서 출력 문장을 한 토큰씩 만듭니다.

나는
나는 LLM
나는 LLM 애플리케이션을
나는 LLM 애플리케이션을 공부하고
나는 LLM 애플리케이션을 공부하고 있다
 

디코더는 현재까지 생성한 출력도 함께 참고합니다.

"나는 LLM 애플리케이션을"까지 만들었다면, 다음에는 "공부하고" 같은 표현이 자연스럽다는 것을 계산합니다.

 

Encoder-Decoder 구조는 입력과 출력이 모두 시퀀스(sequence) 인 문제에 적합합니다.

여기서 시퀀스란 순서가 있는 데이터입니다. 문장, 음성, 코드, 시간 순서 데이터 등이 모두 시퀀스입니다.

 

Encoder-Decoder 구조의 장점은 입력 전체를 먼저 처리한 뒤, 그 결과를 바탕으로 출력 전체를 생성할 수 있다는 점입니다.

그래서 번역, 요약, 문장 변환처럼 입력 내용을 바탕으로 새로운 문장을 만드는 작업에 어울립니다.

GPT- Decoder-only

GPT의 Decoder-only 구조는 별도의 인코더 없이, 지금까지 주어진 토큰들을 보고 다음 토큰을 순차적으로 예측하는 Transformer 구조입니다.

 

다음 문장을 자동완성한다고 생각하면 됩니다.

나는 오늘 점심으로
 

모델은 다음에 올 가능성이 높은 토큰을 예측합니다.

나는 오늘 점심으로 김치찌개를
 

그다음 다시 이어서 예측합니다.

나는 오늘 점심으로 김치찌개를 먹었다.
 

GPT는 기본적으로 이 과정을 반복합니다.

 

즉, GPT의 핵심 동작은 “다음 토큰 예측”입니다.

대규모 데이터로 다음 토큰을 예측하도록 학습하면, 문법, 의미, 상식, 코드 패턴, 문서 구조, 대화 패턴, 추론 패턴까지 학습됩니다.

 

GPT는 대략 다음 흐름으로 동작합니다.

프롬프트 입력
→ 토큰화
→ 임베딩
→ 위치 정보 추가
→ Masked Self-Attention
→ 다음 토큰 확률 계산
→ 토큰 선택
→ 반복 생성
 

예를 들어 사용자가 다음과 같이 입력합니다.

RAG가 뭐야?
 

이 문장은 모델의 tokenizer에 의해 토큰으로 나뉩니다. 실제 토큰화 방식은 모델마다 다릅니다.

["R", "AG", "가", "뭐", "야", "?"]
 

각 토큰은 임베딩 벡터로 변환됩니다. 이때 모델은 단순히 토큰의 의미만 보는 것이 아니라, 토큰의 위치도 함께 반영합니다.

"나는 밥을 먹었다"와 "밥은 나를 먹었다"는 같은 단어가 들어 있지만 순서가 다르기 때문에 의미가 완전히 달라집니다. 그래서 Transformer 계열 모델에는 위치 정보가 필요합니다.

 

GPT의 핵심은 Masked Self-Attention입니다.
Self-Attention은 문장 안의 토큰들이 서로를 참고하는 메커니즘이고, Masked는 미래 토큰을 보지 못하도록 가리는 것을 뜻합니다.

예를 들어 학습 문장이 다음과 같다고 해보겠습니다.

나는 오늘 커피를 마셨다.
 

모델이 "커피를" 다음 토큰을 예측하는 시점에 "마셨다"를 미리 보면 안 됩니다. 그러면 정답을 훔쳐보는 셈이 됩니다.

그래서 GPT는 현재 위치보다 뒤에 있는 토큰을 볼 수 없도록 마스크를 씌웁니다.

이 구조를 Causal Language Modeling, 즉 인과적 언어 모델링이라고도 합니다.

“앞의 토큰들이 원인이 되어 다음 토큰을 예측한다”는 의미입니다.

 

GPT의 Decoder-only 구조는 현대 LLM 애플리케이션에서 가장 널리 쓰이는 구조입니다. 챗봇, 코드 생성, 요약, 번역, RAG 답변 생성, Text-to-SQL 등 많은 작업이 GPT류 모델을 기반으로 만들어집니다.

프롬프트를 잘 구성하면 다음과 같은 작업을 모두 다음 토큰 예측 문제로 바꿀 수 있습니다.

질문에 답하라.
문서를 요약하라.
SQL을 생성하라.
코드를 작성하라.
문장을 번역하라.
고객 문의를 분류하라.
 

예를 들어 Text-to-SQL도 결국 모델 입장에서는 다음과 같은 문제입니다.

입력:
사용자 질문과 DB 스키마가 주어졌다.
이에 맞는 SQL을 작성하라.

출력:
SELECT ...
 

RAG도 마찬가지입니다.

입력:
사용자 질문과 검색된 문서가 주어졌다.
문서에 근거해 답변하라.

출력:
답변 문장
 

즉, GPT는 다양한 작업을 “조건이 주어진 텍스트 생성 문제”로 처리할 수 있습니다.

 

GPT는 현재까지의 토큰을 바탕으로 다음 토큰의 확률 분포를 계산합니다.

예를 들어 다음과 같은 확률 분포를 만들 수 있습니다.

입력: "나는 오늘 점심으로"

다음 토큰 후보:
"김치찌개를" 0.22
"라면을"     0.18
"샐러드를"   0.11
"커피를"     0.03
...
 

모델은 이 분포에서 하나의 토큰을 선택하고, 선택된 토큰을 다시 입력에 붙여 다음 토큰을 예측합니다.

Attention

Attention은 모델이 현재 토큰을 해석하거나 다음 토큰을 생성할 때, 입력 안의 어떤 토큰을 얼마나 참고할지 계산하는 메커니즘입니다.

 

다음 문장을 보겠습니다.

그는 은행에 가서 돈을 찾았다.
 

여기서 "은행"은 금융기관입니다.
그 이유는 주변에 "돈", "찾았다" 같은 단어가 있기 때문입니다.

모델이 "은행"의 의미를 제대로 파악하려면 "돈"과 "찾았다"를 중요하게 봐야 합니다.

반대로 "그는" 같은 단어는 문법적으로 필요하지만, "은행"의 의미를 구분하는 데는 상대적으로 덜 중요합니다.

Attention은 이런 식으로 각 토큰이 다른 토큰들을 얼마나 참고해야 하는지 계산합니다.

 

Attention을 이해하려면 세 가지 개념이 필요합니다.

용어 한국어 뜻
Query 질의 벡터 현재 토큰이 무엇을 찾고 있는지를 나타냄
Key 키 벡터 각 토큰이 어떤 정보를 가지고 있는지를 나타냄
Value 값 벡터 실제로 전달될 정보

간단히 말하면 다음과 같습니다.

현재 토큰의 Query가
다른 토큰들의 Key와 비교되고,
비슷한 Key를 가진 토큰의 Value를 더 많이 반영한다.
 

조금 더 기술적으로 쓰면 Attention은 다음 수식으로 표현됩니다.

Attention(Q, K, V) = softmax(QKᵀ / √d) V
 

각 기호는 다음 의미입니다.

기호 의미
Q Query 벡터들의 행렬
K Key 벡터들의 행렬
V Value 벡터들의 행렬
Kᵀ Key 행렬을 전치한 것
QKᵀ Query와 Key 사이의 유사도 점수
d 벡터 차원 수
√d 점수가 너무 커지는 것을 막기 위한 정규화 값
softmax 점수를 확률처럼 합이 1인 가중치로 바꾸는 함수

이 수식은 관련 있는 토큰에는 높은 가중치를 주고, 관련 없는 토큰에는 낮은 가중치를 준다는 의미입니다.

 
예를 들어 "은행"이라는 토큰이 있을 때, Attention은 대략 이런 관계를 계산할 수 있습니다.
은행 ↔ 돈       관련 높음
은행 ↔ 찾았다   관련 높음
은행 ↔ 그는     관련 낮음
은행 ↔ 가서     관련 중간
 

물론 실제 모델은 고차원 벡터 연산을 통해 가중치를 계산합니다.

 

Attention은 Transformer의 핵심입니다.
이전의 순환 신경망 계열 모델은 문장을 순서대로 처리했기 때문에 긴 문장에서 멀리 떨어진 단어 간 관계를 유지하기 어려웠습니다. Attention은 문장 안의 모든 토큰이 서로를 직접 참고할 수 있게 해줍니다.

예를 들어 다음 문장을 봅니다.

철수는 영희에게 책을 빌려주었고, 그녀는 그것을 다음 날 돌려주었다.
 

여기서 "그녀"는 영희를 가리키고, "그것"은 책을 가리킵니다.

이런 참조 관계는 문장 안에서 떨어져 있는 단어들을 연결해야 이해할 수 있습니다.

Attention은 이런 관계를 계산하는 데 중요한 역할을 합니다.

 

Attention은 토큰 벡터 간의 관계를 계산해서 가중치를 부여합니다.

Attention은 가중치가 높다고 해서 가중치로만 판단하면 안되는 게, 모델의 전체 판단 과정은 여러 층의 비선형 계산을 거치기 때문에 단순히 “어떤 단어를 많이 봤으니 그게 이유다”라고 말하기는 어렵습니다.

 

Token, Embedding, Context

Token, Embedding, Context는 텍스트를 토큰이라는 처리 단위로 나누고, 각 토큰을 임베딩이라는 숫자 벡터로 바꾼 뒤, 주어진 문맥인 Context 안에서 의미를 계산하는 구조입니다.

 

다음 문장을 모델에 넣는다고 해보겠습니다.

오늘 날씨가 좋다.
 

모델은 이 문장을 그대로 이해하지 않고 먼저 토큰으로 나눕니다.

["오늘", "날씨", "가", "좋", "다", "."]
 

실제 토큰화 결과는 모델마다 다릅니다. 어떤 모델은 "좋다"를 하나의 토큰으로 볼 수도 있고, 어떤 모델은 "좋"과 "다"로 나눌 수도 있습니다.

그다음 각 토큰은 숫자 벡터로 바뀝니다.

"오늘" → [0.12, -0.41, 0.08, ...]
"날씨" → [0.33, 0.22, -0.19, ...]
 

이 벡터들이 모델 내부에서 계산됩니다.

텍스트
→ Token
→ Embedding
→ Context 안에서 Attention 계산
→ 출력 생성
 

Token은 모델이 텍스트를 처리하기 위해 사용하는 기본 단위입니다.

토큰은 반드시 단어와 일치하지 않습니다. 영어에서는 단어 단위에 가깝게 나뉘는 경우가 많지만, 한국어에서는 조사, 어미, 글자 조각, 단어 일부가 토큰이 될 수 있습니다.

예를 들어 다음 문장을 생각해보겠습니다.

ChatGPT는 편리하다.
 

토큰화 결과는 모델에 따라 다음처럼 될 수 있습니다.

["Chat", "G", "PT", "는", "편리", "하다", "."]
 

또는 다음처럼 될 수도 있습니다.

["ChatGPT", "는", "편", "리", "하다", "."]
 

LLM의 입력 길이, 출력 길이, 비용, 처리 속도는 보통 글자 수가 아니라 토큰 수를 기준으로 계산됩니다.

 

Embedding은 토큰을 의미 관계를 담은 숫자 벡터로 표현한 것입니다.

모델은 "고양이"라는 문자열 자체를 이해하지 않고 "고양이"에 해당하는 토큰 ID를 찾아 그 ID에 대응하는 벡터를 사용합니다.

 

임베딩에서 의미가 비슷한 표현은 벡터 공간에서 가깝게 배치되며, 의미가 다른 표현은 상대적으로 멀게 배치됩니다.

 

예를 들어 일반적으로 다음 단어들은 의미적으로 가깝습니다.

고양이, 강아지, 동물, 반려동물
 

반면 다음 단어들은 상대적으로 멀 수 있습니다.

고양이, SQL, 환율, 컨테이너
 

물론 임베딩은 사전적 정의를 저장한 것이 아닙니다.

임베딩은 대량의 데이터에서 단어와 문장이 어떤 맥락에서 사용되는지를 학습한 결과입니다.

 

Context는 모델이 현재 답변을 만들 때 참고할 수 있는 입력 범위입니다.

예를 들어 사용자가 이렇게 말합니다.

위 문서를 요약해줘.
 

이 요청만 보면 모델은 무엇을 요약해야 하는지 알 수 없습니다.

하지만 앞에 문서 내용이 함께 들어 있다면, 그 문서 내용이 Context가 됩니다.

LLM에서 Context Window는 모델이 한 번에 처리할 수 있는 최대 토큰 수를 뜻합니다.

예를 들어 어떤 모델의 context window가 128k tokens라면, 입력과 출력 전체를 합쳐 그 범위 안에서 처리할 수 있다는 뜻입니다.

RAG에서 검색된 문서를 프롬프트에 넣는 것도 결국 모델의 Context에 외부 지식을 추가하는 것입니다.

 

이 세 개념은 LLM 애플리케이션의 거의 모든 문제와 연결됩니다.

문제 관련 개념
긴 문서를 한 번에 넣기 어려운 이유 Context Window 제한
입력 비용이 증가하는 이유 Token 수 증가
검색된 문서를 프롬프트에 넣어야 하는 이유 Context에 근거 제공
RAG에서 임베딩을 사용하는 이유 문서와 질문의 의미 검색
한국어 처리 비용이 다르게 느껴지는 이유 Tokenizer 차이
답변이 이전 대화를 참고하는 이유 이전 대화가 Context에 포함되어 있기 때문

특히 RAG를 이해하려면 Embedding과 Context의 차이를 명확히 알아야 합니다.

Embedding은 검색을 위해 문서와 질문을 벡터화하는 데 주로 쓰입니다.
Context는 LLM이 답변을 만들 때 실제로 읽는 입력입니다.

즉, 벡터DB에 저장된 임베딩 자체를 LLM이 직접 읽는 것이 아닙니다.

RAG에서는 임베딩으로 관련 문서를 찾고, 찾은 문서의 원문 텍스트를 Context에 넣어 LLM이 답변하게 합니다.

 

임베딩은 사람이 이해하는 2차원 좌표가 아니라 수백 또는 수천 차원의 숫자 벡터입니다.

또 Context는 모델 입력에 실제로 포함된 토큰들의 범위입니다.

Text-to-SQL

Text-to-SQL은 사용자의 자연어 질문을 데이터베이스에서 실행 가능한 SQL 쿼리로 변환하는 기술입니다.

 

사용자가 이렇게 묻는다고 가정해보겠습니다.

지난달 신규 가입자 수를 알려줘.
 

DB에는 다음 테이블이 있다고 해보겠습니다.

users
- id
- name
- created_at
 

Text-to-SQL 시스템은 이 질문을 SQL로 변환합니다.

 
SELECT COUNT(*) AS new_user_count
FROM users
WHERE created_at >= DATE_TRUNC('month', CURRENT_DATE - INTERVAL '1 month')
  AND created_at < DATE_TRUNC('month', CURRENT_DATE);
 

DB 실행 결과가 다음과 같다면,

new_user_count = 1284
 

최종 답변은 이렇게 나갈 수 있습니다.

지난달 신규 가입자 수는 1,284명입니다.
 

 

Text-to-SQL은 다음 순서로 이해하면 됩니다.

자연어 질문
→ 스키마 이해
→ SQL 생성
→ DB 조회
→ 결과 해석
 

사용자는 SQL을 모릅니다. 그래서 일상적인 언어로 질문합니다.

지난주 매출이 가장 높았던 상품 5개를 보여줘.
 

이 질문에는 여러 의미가 숨어 있습니다.

지난주 정확한 날짜 범위
매출 어떤 금액 컬럼을 합산할 것인지
가장 높았던 내림차순 정렬
상품 상품 테이블 또는 상품 컬럼
5개 LIMIT 5
보여줘 결과를 사람이 읽기 쉽게 반환

시스템은 이를 명확한 조건과 연산으로 바꿔야 합니다.

 

스키마(Schema) 는 데이터베이스의 구조입니다.
테이블 이름, 컬럼 이름, 타입, 관계, 제약 조건 등이 포함됩니다.

예를 들어 다음과 같은 스키마가 있다고 해보겠습니다.

orders
- id
- user_id
- product_id
- total_amount
- status
- created_at

products
- id
- name
- category
 

사용자가 "상품"이라고 말하면 products 테이블을 떠올려야 합니다.
사용자가 "매출"이라고 말하면 orders.total_amount를 합산해야 할 가능성이 높습니다.
사용자가 "결제 완료된 주문"이라고 말하면 orders.status = 'PAID' 조건이 필요할 수 있습니다.

이처럼 자연어 표현과 DB 스키마를 연결하는 과정을 Schema Linking, 즉 스키마 연결이라고 합니다.

Text-to-SQL에서 가장 어려운 부분은 SQL 문법 자체보다 이 스키마 연결인 경우가 많습니다.

 

질문과 스키마를 바탕으로 SQL을 생성합니다.

 
SELECT
    p.name,
    SUM(o.total_amount) AS revenue
FROM orders o
JOIN products p ON o.product_id = p.id
WHERE o.status = 'PAID'
  AND o.created_at >= CURRENT_DATE - INTERVAL '7 days'
GROUP BY p.name
ORDER BY revenue DESC
LIMIT 5;
 

이 SQL 안에는 사용자의 질문에서 추출된 의미가 반영되어 있습니다.

JOIN products 상품명을 보여줘야 함
SUM(total_amount) 매출 합계
WHERE status = 'PAID' 실제 결제 완료 주문만 포함
날짜 조건 지난주
GROUP BY p.name 상품별 집계
ORDER BY revenue DESC 매출 높은 순
LIMIT 5 상위 5개

생성된 SQL을 DB에서 실행합니다.
실무에서는 이 단계에서 반드시 안전장치가 필요합니다.

예를 들어 다음과 같은 위험 쿼리는 막아야 합니다.

DELETE FROM users;
DROP TABLE orders;
UPDATE payments SET amount = 0;
 

Text-to-SQL 서비스는 보통 읽기 전용 계정을 사용하고, SELECT만 허용하며, 쿼리 timeout과 최대 row limit을 설정합니다.

DB 결과는 표 형태로 나옵니다.

name revenue
무선 마우스 12,300,000
기계식 키보드 9,800,000
USB-C 허브 7,500,000

LLM은 이 결과를 사람이 읽기 쉬운 답변으로 바꿉니다.

지난주 매출이 가장 높았던 상품은 무선 마우스이며, 매출은 1,230만 원입니다.
상위 5개 상품은 무선 마우스, 기계식 키보드, USB-C 허브 순입니다.
 

Text-to-SQL을 사용하면 사용자가 자연어로 질문하고, 시스템이 SQL을 생성해 결과를 가져올 수 있습니다.

대표적인 사용 사례는 다음과 같습니다.

매출 분석 이번 달 매출은 얼마야?
제품 분석 가장 많이 클릭된 기능은?
고객 분석 재구매율이 높은 고객군은?
운영 모니터링 오늘 결제 실패 건수는?
CS 분석 환불 요청이 많은 상품은?
마케팅 분석 캠페인별 전환율은?

Text-to-SQL은 특히 정확한 숫자, 집계, 필터링, 조인, 정렬이 필요한 질문에 적합합니다.

RAG

한 문장 정의

RAG(Retrieval-Augmented Generation, 검색 증강 생성) 는 LLM이 답변을 생성하기 전에 외부 문서에서 관련 정보를 검색하고, 그 검색 결과를 프롬프트에 넣어 답변을 만드는 방식입니다.

 

사용자가 사내 챗봇에게 묻습니다.

부모상 휴가는 며칠이야?
 

기본 LLM은 회사의 내부 규정을 알지 못합니다.
그래서 RAG 시스템은 먼저 사내 문서에서 관련 내용을 찾습니다.

검색된 문서에 다음 내용이 있다고 해보겠습니다.

경조사 휴가 규정:
부모상은 5일, 조부모상은 3일, 본인 결혼은 5일의 휴가를 부여한다.
 

이 문서를 LLM에게 함께 제공합니다.

아래 문서를 근거로 답변하라.

[문서]
경조사 휴가 규정:
부모상은 5일, 조부모상은 3일...

[질문]
부모상 휴가는 며칠이야?
 

그러면 LLM은 다음처럼 답변할 수 있습니다.

부모상 휴가는 5일입니다.
 

RAG는 다음 순서로 이해해야 합니다.

문서 수집
→ 청킹
→ 임베딩
→ 벡터DB 저장
→ 유사도 검색
→ 프롬프트 구성
→ LLM 답변 생성
 

먼저 LLM이 참고할 외부 지식을 모읍니다.

예를 들어 사내 챗봇이라면 다음 문서들이 대상이 될 수 있습니다.

사내 규정
복지 제도 문서
제품 매뉴얼
고객센터 FAQ
API 문서
장애 대응 매뉴얼
법무 검토 문서
정책 문서
 

문서 수집 단계에서 중요한 것은 양보다 품질입니다.
잘못된 문서, 오래된 문서, 중복 문서, 권한이 맞지 않는 문서가 들어가면 RAG의 답변 품질도 떨어집니다.

 

청킹(Chunking) 은 긴 문서를 검색 가능한 작은 조각으로 나누는 과정입니다.

예를 들어 “사내 복지 규정”이라는 문서가 100페이지라면, 사용자의 질문 하나에 문서 전체가 필요한 경우는 많지 않습니다.

보통 특정 섹션만 필요합니다.

전체 문서: 사내 복지 규정

Chunk 1: 연차 휴가
Chunk 2: 병가
Chunk 3: 경조사 휴가
Chunk 4: 재택근무
Chunk 5: 교육비 지원
 

질문이 다음과 같다면,

부모상 휴가는 며칠이야?
 

필요한 청크는 대개 경조사 휴가 섹션입니다.

청킹을 잘못하면 검색 품질이 크게 떨어집니다.
청크가 너무 작으면 문맥이 부족하고, 청크가 너무 크면 검색 결과에 불필요한 내용이 많이 섞입니다.

 

임베딩(Embedding) 은 텍스트를 의미를 담은 숫자 벡터로 바꾸는 과정입니다.

문서 청크를 임베딩하면 다음과 같은 벡터가 만들어집니다.

"부모상은 5일의 경조사 휴가를 부여한다."
→ [0.21, -0.14, 0.88, ...]
 

사용자 질문도 임베딩합니다.

"부모님이 돌아가시면 휴가 며칠이야?"
→ [0.19, -0.11, 0.84, ...]
 

두 문장은 표현은 다르지만 의미는 비슷합니다. 임베딩은 이런 의미적 유사성을 벡터 공간에서 가깝게 표현하려고 합니다.

 

벡터DB(Vector Database) 는 임베딩 벡터를 저장하고, 비슷한 벡터를 빠르게 찾기 위한 데이터베이스입니다.

보통 다음 정보를 함께 저장합니다.

 
{
  "chunk_id": "hr_policy_003",
  "title": "경조사 휴가 규정",
  "text": "부모상은 5일, 조부모상은 3일...",
  "embedding": [0.21, -0.14, 0.88],
  "metadata": {
    "department": "HR",
    "updated_at": "2026-02-01",
    "permission": "employee_only"
  }
}
 

여기서 중요한 것은 embedding뿐만 아니라 text와 metadata도 함께 저장한다는 점입니다.

LLM이 답변할 때 읽는 것은 임베딩 벡터가 아니라 원문 텍스트입니다.
임베딩은 관련 문서를 찾기 위한 검색용 표현입니다.

 

사용자가 질문하면, 질문도 임베딩 벡터로 변환합니다.

질문:
부모상 휴가는 며칠이야?

질문 벡터:
[0.20, -0.13, 0.85, ...]
 

이 질문 벡터와 벡터DB에 저장된 문서 청크 벡터들을 비교해 가장 비슷한 문서를 찾습니다.

 
검색된 청크 유사도
경조사 휴가 규정 0.88
장례 지원금 규정 0.73
연차 휴가 규정 0.42

이때 자주 쓰이는 기준이 Cosine Similarity, 즉 코사인 유사도입니다.

 

검색된 문서를 LLM에게 그냥 던지는 것이 아니라, 답변 지시문과 함께 구성합니다.

너는 사내 규정 안내 챗봇이다.
아래 문서에 근거해서만 답변하라.
문서에 없는 내용은 추측하지 말고 "문서에서 확인되지 않습니다"라고 말하라.

[검색된 문서]
경조사 휴가 규정:
부모상은 5일, 조부모상은 3일...

[사용자 질문]
부모상 휴가는 며칠이야?
 

검색 결과가 좋아도 프롬프트가 나쁘면 LLM이 문서를 무시하거나, 문서에 없는 내용을 지어낼 수 있습니다.

 

LLM은 프롬프트에 포함된 문서를 바탕으로 답변을 생성합니다.

부모상 휴가는 5일입니다.
 

실무에서는 가능하면 출처도 함께 제공합니다.

부모상 휴가는 5일입니다.

출처: 경조사 휴가 규정
 

 

기본 LLM은 다음을 알지 못할 수 있습니다.

우리 회사의 최신 규정
비공개 사내 문서
특정 제품의 최신 매뉴얼
최근 변경된 정책
고객사별 계약 조건
내부 장애 대응 절차
 

이런 정보를 모델에게 fine-tuning으로 모두 외우게 하는 것은 적합하지 않은 경우가 많습니다.

정보가 자주 바뀌고, 출처가 필요하며, 접근 권한도 관리해야 하기 때문입니다.

RAG를 사용하면 문서를 최신 상태로 관리하고, 질문 시점에 필요한 문서만 검색해서 LLM에게 제공할 수 있습니다.

 

RAG는 문서를 미리 청크로 나누고, 임베딩을 저장해두고, 질문과 가까운 청크만 찾아 LLM에게 제공합니다.

검색이 실패하면 답변도 실패할 수 있습니다.

 

RAG에서 Embedding과 Cosine Similarity는 질문과 문서를 같은 벡터 공간에 표현하고, 두 벡터의 방향이 얼마나 비슷한지 계산해 관련 문서를 찾는 방법입니다.

 

연차는 언제까지 이월돼?
 

문서 A:

사용하지 않은 연차는 다음 해 3월까지 사용할 수 있다.
 

문서 B:

점심시간은 12시부터 13시까지이다.
 

사람이 보면 문서 A가 질문과 관련 있다는 것을 바로 알 수 있습니다.
RAG 시스템은 이것을 숫자 벡터로 계산합니다.

질문 벡터: [0.10, 0.50, 0.80, ...]
문서 A 벡터: [0.12, 0.48, 0.79, ...]
문서 B 벡터: [-0.30, 0.02, 0.15, ...]
 

질문 벡터와 문서 A 벡터는 방향이 비슷합니다.
그래서 문서 A의 유사도가 높게 나옵니다.

 

전체 흐름은 다음과 같습니다.

문서 청킹
→ 각 청크 임베딩
→ 질문 임베딩
→ 질문 벡터와 청크 벡터 비교
→ 유사도가 높은 청크 선택
→ LLM 프롬프트에 삽입
 

먼저 문서를 청크로 나눕니다.

Chunk 1: 연차 발생 기준
Chunk 2: 연차 이월 규정
Chunk 3: 병가 신청 방법
 

각 청크를 임베딩합니다.

Chunk 1 → vector_1
Chunk 2 → vector_2
Chunk 3 → vector_3
 

사용자 질문도 임베딩합니다.

"연차는 언제까지 이월돼?"
→ query_vector
 

그다음 질문 벡터와 각 문서 벡터를 비교합니다.

대표적으로 사용하는 유사도 계산 방식이 코사인 유사도입니다.

cosine_similarity(A, B) = (A · B) / (||A|| ||B||)
 

각 기호의 의미는 다음과 같습니다.

기호 의미
A 질문 벡터
B 문서 청크 벡터
A · B 두 벡터의 내적
||A|| A 벡터의 길이
||B|| B 벡터의 길이
결과값 두 벡터의 방향 유사도

코사인 유사도는 벡터의 크기보다 방향이 얼마나 비슷한지를 봅니다.
즉, 두 문장이 의미적으로 비슷한 방향을 가진다고 판단되면 유사도가 높아집니다.

예를 들어 결과가 다음처럼 나왔다고 해보겠습니다.

 

청크 유사도
연차 이월 규정 0.87
연차 발생 기준 0.68
병가 신청 방법 0.29

이 경우 RAG 시스템은 연차 이월 규정 청크를 우선적으로 LLM에게 제공합니다.

 

RAG에서 답변 품질은 검색 품질에 크게 의존합니다.
LLM이 아무리 좋아도, 잘못된 문서가 들어가면 좋은 답을 하기 어렵습니다.

퇴직금 계산 기준을 알려줘.
 

위 질문에서 검색된 문서가 다음이라면 문제가 됩니다.

신입사원 온보딩 일정
 

이 경우 LLM은 질문과 관련 없는 문서를 받게 됩니다. 모델이 추측해서 답변하면 환각이 발생할 수 있고, 문서에 근거하려고 하면 “확인할 수 없다”고 답할 수밖에 없습니다.

그래서 RAG에서는 다음 관계가 중요합니다.

좋은 답변 = 좋은 검색 결과 + 좋은 프롬프트 + 좋은 생성 모델
 

여기서 좋은 검색 결과를 만드는 핵심 기반이 임베딩과 유사도 검색입니다.

 

실제 임베딩 공간은 보통 수백 또는 수천 차원의 벡터 공간입니다.

또한 의미가 비슷하다고 해서 항상 정답 문서라는 뜻은 아닙니다.

예를 들어 다음 두 문장은 매우 비슷해 보이지만 중요한 숫자가 다릅니다.

연차는 다음 해 3월까지 사용할 수 있다.
연차는 다음 해 6월까지 사용할 수 있다.
 

임베딩 검색은 두 문장을 비슷하다고 볼 수 있습니다. 그러나 답변에서는 3월과 6월의 차이가 매우 중요합니다. 그래서 임베딩 검색만 믿지 않고, reranking, keyword search, metadata filtering, 평가셋 등을 함께 사용합니다.

RAG 시스템 아키텍처

RAG 시스템 아키텍처는 문서를 수집하고 검색 가능한 형태로 저장하는 색인 파이프라인과, 사용자 질문에 맞는 문서를 찾아 LLM 답변을 생성하는 질의 파이프라인으로 구성됩니다.

 

사내 문서 기반 Q&A 챗봇을 만든다고 해보겠습니다.

사용자 질문은 다음과 같습니다.

재택근무는 주 몇 회까지 가능해?
 

이 질문에 답하려면 시스템은 다음을 해야 합니다.

사내 규정 문서를 가져온다.
문서를 적절히 나눈다.
각 문서를 임베딩해서 저장한다.
질문이 들어오면 관련 문서를 검색한다.
검색된 문서를 LLM에게 넣는다.
LLM이 문서에 근거해 답한다.
출처를 함께 보여준다.
 

RAG 아키텍처는 크게 두 부분으로 나눌 수 있습니다.

1. 문서 색인 파이프라인
2. 질문 처리 파이프라인
 

문서 색인 파이프라인은 사용자가 질문하기 전에 미리 수행되는 과정입니다.

문서 수집
→ 전처리
→ 청킹
→ 임베딩
→ 벡터DB 저장
 

여러 문서 소스에서 데이터를 가져옵니다.

Notion
Confluence
Google Drive
PDF
웹페이지
GitHub 문서
API 문서
고객센터 FAQ
사내 DB
 

이 단계에서는 문서의 최신성, 권한, 중복 여부를 함께 관리해야 합니다.

 

수집된 문서를 그대로 쓰면 문제가 생기는 경우가 많습니다.

예를 들어 PDF에는 페이지 번호, 머리말, 꼬리말, 줄바꿈 깨짐이 섞여 있을 수 있습니다.

웹 문서에는 메뉴, 광고, 버튼 텍스트가 포함될 수 있습니다. 이런 불필요한 요소를 정리해야 검색 품질이 좋아집니다.

전처리 예시는 다음과 같습니다.

HTML 태그 제거
중복 문서 제거
깨진 문자 정리
표 구조 변환
이미지 설명 추출
페이지 번호 제거
권한 정보 정리
문서 버전 확인
 

문서를 검색에 적합한 단위로 나눕니다.

청킹 전략은 RAG 성능에 매우 큰 영향을 줍니다.
문서가 너무 작게 나뉘면 필요한 문맥이 사라지고, 너무 크게 나뉘면 검색 결과에 불필요한 정보가 많이 들어갑니다.

좋은 청크는 보통 다음 특성을 가집니다.

하나의 주제를 담고 있다.
질문에 답할 만큼 충분한 문맥이 있다.
불필요하게 길지 않다.
제목이나 섹션 정보를 포함한다.
 

예:

제목: 재택근무 규정
본문: 재택근무는 주 2회까지 가능하며, 수습 기간 중에는 팀장 승인이 필요하다.
 

제목을 함께 넣으면 검색 품질이 좋아질 수 있습니다. 사용자가 "재택 몇 번 가능해?"라고 묻더라도, 제목의 "재택근무 규정"이 의미 연결에 도움을 주기 때문입니다.

 

각 청크를 임베딩 모델에 넣어 벡터로 변환합니다.

청크 텍스트 → 임베딩 벡터
 

이 임베딩 벡터는 나중에 사용자의 질문 벡터와 비교됩니다.

 

벡터DB에는 보통 다음이 저장됩니다.

chunk_id
문서 제목
문서 본문
임베딩 벡터
metadata
원본 문서 링크
업데이트 날짜
접근 권한
 

metadata는 실무에서 매우 중요합니다.
예를 들어 사용자가 인사팀 문서를 볼 권한이 없다면 검색 결과에서도 제외되어야 합니다.

 

사용자가 질문했을 때 실행되는 과정입니다.

사용자 질문
→ 질문 전처리
→ 검색
→ reranking
→ 프롬프트 구성
→ LLM 답변 생성
→ 후처리
 

다음은 사용자 질문 예시입니다.

재택근무는 주 몇 회 가능해?
 

사용자 질문은 짧거나 모호한 경우가 많습니다.

재택 몇 번 돼?
 

이 질문만 보면 검색 품질이 낮을 수 있습니다.
대화 문맥을 참고해 다음처럼 재작성할 수 있습니다.

사내 재택근무 규정에서 주당 재택근무 가능 횟수는 몇 회인가?
 

이 과정을 Query Rewriting, 즉 질의 재작성이라고 합니다.

 

검색 방식은 여러 가지가 있습니다.

방식 설명
Vector Search 의미적으로 비슷한 문서를 찾음
Keyword Search 정확한 단어가 포함된 문서를 찾음
Hybrid Search 벡터 검색과 키워드 검색을 결합
Metadata Filtering 부서, 제품, 날짜, 권한 등으로 검색 범위 제한

예를 들어 "PG-ERR-4821" 같은 에러 코드는 의미 검색보다 키워드 검색이 더 적합할 수 있습니다.

반면 "부모님 상을 당하면 쉬는 날" 같은 질문은 의미 검색이 더 적합할 수 있습니다.

실무에서는 둘을 결합한 Hybrid Search를 많이 사용합니다.

 

1차 검색 결과는 빠르지만 완벽하지 않습니다.
그래서 후보 문서를 넉넉히 가져온 뒤, 더 정교한 모델로 다시 순위를 매길 수 있습니다.

1차 검색: top 30
Reranking: top 5 재선정
 

Reranker는 질문과 문서의 관련성을 더 세밀하게 판단합니다.
RAG 품질을 올리는 데 매우 효과적인 방법 중 하나입니다.

 

검색된 문서를 LLM에게 전달합니다.

예:

너는 사내 규정 안내 챗봇이다.
아래 문서에 근거해서만 답변하라.
문서에 없는 내용은 추측하지 말라.
답변 끝에 사용한 문서 제목을 표시하라.

[문서 1]
재택근무는 주 2회까지 가능하다.

[질문]
재택근무는 주 몇 회 가능해?
 

프롬프트는 단순히 문서를 붙이는 작업이 아닙니다.
모델이 문서를 어떻게 사용해야 하는지, 문서에 없는 내용은 어떻게 처리해야 하는지, 답변 형식은 어떻게 해야 하는지 명확히 알려주는 단계입니다.

 

LLM이 문서를 바탕으로 답변합니다.

재택근무는 주 2회까지 가능합니다.
 

최종 답변을 그대로 내보내지 않고, 서비스 정책에 맞게 다듬습니다.

출처 표시
민감 정보 마스킹
금칙어 검사
답변 형식 정리
신뢰도 낮은 답변 차단
로그 저장
사용자 피드백 수집
 

 

실제 서비스에서는 검색, 권한, 문서 품질, 프롬프트, 평가, 운영 모니터링까지 모두 설계해야 합니다.

좋은 RAG 시스템은 다음 요구사항을 만족해야 합니다.

요구사항 의미
정확성 관련 문서를 제대로 찾아야 함
최신성 문서 변경이 반영되어야 함
권한 관리 사용자가 볼 수 있는 문서만 검색되어야 함
출처 추적 답변 근거를 확인할 수 있어야 함
안정성 검색 실패나 환각을 줄여야 함
평가 가능성 개선 여부를 측정할 수 있어야 함
비용 관리 검색 비용, LLM 비용, 토큰 비용을 관리해야 함

RAG에서는 문서 수집, 청킹, 임베딩, 벡터 검색, 키워드 검색, metadata filtering, reranking, 권한 검사 등이 모두 포함됩니다.

 


Text-to-SQL과 RAG의 차이

Text-to-SQL과 RAG의 차이는 Text-to-SQL이 정형 데이터베이스를 SQL로 조회하는 방식이고, RAG는 문서를 검색해 LLM 답변의 근거로 제공하는 방식이라는 점입니다.

 

다음 질문은 Text-to-SQL에 적합합니다.

지난달 결제 완료된 주문 수는 몇 건이야?
 

이 질문은 DB에서 orders 테이블을 조회하고, status = 'PAID' 조건을 걸고, COUNT(*)를 계산하면 됩니다.

반면 다음 질문은 RAG에 적합합니다.

우리 회사 환불 정책을 쉽게 설명해줘.
 

이 질문은 환불 정책 문서를 찾아 읽고, 내용을 요약하거나 설명해야 합니다.

즉, 질문의 대상이 다릅니다.

숫자, 집계, 테이블 데이터 → Text-to-SQL
문서, 정책, 설명, 매뉴얼 → RAG
 

Text-to-SQL은 다음 순서로 동작합니다.

자연어 질문
→ 스키마 이해
→ SQL 생성
→ DB 조회
→ 결과 해석
 

RAG는 다음 순서로 동작합니다.

문서 수집
→ 청킹
→ 임베딩
→ 벡터DB 저장
→ 유사도 검색
→ 프롬프트 구성
→ LLM 답변 생성
 

두 방식 모두 LLM을 사용할 수 있지만, LLM이 맡는 역할이 다릅니다.

Text-to-SQL에서 LLM은 주로 자연어 질문을 SQL로 바꾸는 역할을 합니다.
RAG에서 LLM은 검색된 문서를 바탕으로 답변을 생성하는 역할을 합니다.

 

기준 Text-to-SQL RAG
주 대상 정형 데이터 비정형 또는 반정형 문서
대표 저장소 RDBMS, Data Warehouse 문서 저장소, 벡터DB, 검색엔진
핵심 변환 자연어 → SQL 자연어 → 검색 쿼리/임베딩
중간 결과 SQL 쿼리 검색된 문서 청크
최종 근거 DB 조회 결과 검색된 문서
강점 정확한 숫자, 집계, 조인, 필터 문서 기반 설명, 요약, 정책 답변
대표 질문 “지난달 매출은?” “환불 정책은?”
주요 실패 잘못된 SQL, 잘못된 조인, 보안 위험 잘못된 문서 검색, 문맥 누락, 환각
평가 방식 SQL 실행 결과 정확도 검색 정확도와 답변 충실도

실무에서는 Text-to-SQL과 RAG가 함께 필요한 질문도 많습니다.

예를 들어 다음 질문을 보겠습니다.

지난달 환불 건수가 왜 늘었는지 환불 정책과 실제 데이터를 기준으로 설명해줘.
 

이 질문은 두 가지가 필요합니다.

첫째, 실제 환불 건수를 DB에서 조회해야 합니다.

Text-to-SQL:
지난달 환불 건수
상품별 환불률
환불 사유별 비율
 

둘째, 환불 정책 문서를 찾아야 합니다.

RAG:
환불 가능 조건
환불 제한 조건
정책 변경 이력
 

마지막으로 LLM이 두 결과를 종합해 설명합니다.

지난달 환불 건수는 전월 대비 18% 증가했습니다.
특히 A 상품군의 환불률이 높았고, 검색된 환불 정책에 따르면...
 

이처럼 LLM 애플리케이션에서는 질문을 먼저 분류하고, 필요한 도구를 선택하는 라우팅 구조가 중요합니다.

 

Text-to-SQL과 RAG를 구분하지 못하면 시스템 설계가 잘못됩니다.

예를 들어 “지난달 매출”을 RAG로 처리하려고 하면 문서에서 매출 숫자를 찾으려 할 수 있습니다. 하지만 정확한 매출은 DB에서 계산해야 합니다.

반대로 “복지 규정을 설명해줘”를 Text-to-SQL로 처리하려고 하면 적절한 SQL을 만들 수 없습니다. 정책 문서는 보통 테이블 형태가 아니라 문서 형태로 저장되어 있기 때문입니다.

따라서 질문을 보면 먼저 다음을 판단해야 합니다.

이 질문은 DB에서 계산해야 하는가?
문서에서 근거를 찾아야 하는가?
둘 다 필요한가?
 

Text-to-SQL은 장부에서 숫자를 계산하는 일에 가깝습니다.
RAG는 설명서를 찾아 읽고 답하는 일에 가깝습니다.

하지만 실제 서비스에서는 장부와 설명서를 함께 봐야 하는 경우가 많습니다.
그래서 둘을 서로 다른 데이터 접근 방식으로 이해하는 것이 좋습니다.

 

NLP

NLP(Natural Language Processing, 자연어 처리) 는 사람이 쓰는 언어를 컴퓨터가 분석하고, 구조화하고, 변환하고, 생성할 수 있게 하는 기술입니다.

 

사용자가 이렇게 말합니다.

지난달 잘 팔린 상품 좀 알려줘.
 

사람은 대충 이해할 수 있지만, 컴퓨터가 처리하려면 여러 가지를 명확히 해야 합니다.

지난달은 정확히 어느 날짜 범위인가?
잘 팔린다는 것은 판매 수량 기준인가, 매출 기준인가?
상품은 어떤 테이블에 있는가?
몇 개를 보여줘야 하는가?
취소된 주문은 제외해야 하는가?
 

이처럼 자연어는 유연하지만 모호합니다.
NLP는 이 자연어를 시스템이 처리할 수 있는 구조로 바꾸는 역할을 합니다.

 

Text-to-SQL에서는 사용자의 자연어 질문을 SQL로 바꿔야 합니다.

이번 주 가장 많이 팔린 카테고리 알려줘.
 

이 질문을 처리하려면 다음과 같은 해석이 필요합니다.

자연어 표현 시스템 해석
이번 주 날짜 범위
가장 많이 팔린 판매량 또는 매출 기준 정렬
카테고리 products.category
알려줘 SELECT 결과 반환

이 과정에서 NLP는 단순히 문장을 읽는 역할만 하지 않습니다.
질문 의도를 파악하고, 조건을 추출하고, DB 스키마와 연결하고, 필요한 SQL 구조를 결정합니다.

특히 "매출", "활성 사용자", "전환율", "재구매 고객" 같은 비즈니스 용어는 회사마다 정의가 다를 수 있습니다.

예를 들어 "매출"이라는 말은 다음 중 하나일 수 있습니다.

주문 금액 합계
결제 완료 금액 합계
환불 제외 순매출
세금 제외 금액
쿠폰 할인 전 금액
쿠폰 할인 후 금액
 

이 정의를 잘못 해석하면 SQL이 문법적으로 맞아도 결과는 틀립니다.

 

RAG에서는 사용자의 질문과 문서 표현이 다를 수 있습니다.

예를 들어 사용자가 이렇게 묻습니다.

부모님이 돌아가시면 며칠 쉴 수 있어?
 

문서에는 이렇게 적혀 있을 수 있습니다.

직계존속 사망 시 경조 휴가 5일을 부여한다.
 

키워드만 보면 "부모님", "돌아가시면", "쉴 수 있어"라는 표현은 문서에 없습니다.
하지만 의미적으로는 "직계존속", "사망", "경조 휴가"와 연결됩니다.

NLP와 임베딩 기반 의미 검색은 이런 표현 차이를 연결해줍니다.

또한 RAG에서는 다음 작업에도 NLP가 필요합니다.

질문 재작성
동의어 처리
문서 요약
검색 결과 통합
출처 기반 답변 생성
문서에 없는 내용인지 판단
 


사용자는 시스템이 원하는 형식으로 질문하지 않습니다.

실제 사용자 질문은 보통 이런 형태입니다.

어제 많이 나간 거 뭐야?
지난번 기준으로 다시 봐줘.
이번 달 성과 어때?
재택 몇 번 가능?
이 에러 왜 나는 거야?
 

이 질문들은 짧고, 생략이 많고, 문맥에 의존합니다.
NLP가 없다면 이런 표현을 SQL, 검색 쿼리, 프롬프트, 답변으로 안정적으로 변환하기 어렵습니다.

 

사용자의 말이 모호하면 NLP도 모호하게 해석할 수 있습니다.

예를 들어 다음 질문은 불명확합니다.

지난달 성과 알려줘.
 

여기서 성과가 매출인지, 가입자 수인지, 전환율인지, 장애 감소율인지 알 수 없습니다.
이런 경우 좋은 시스템은 무리해서 답을 만들지 않고 되물어야 합니다.

성과 기준을 선택해주세요. 매출, 주문 수, 신규 가입자 수, 전환율 중 어떤 지표를 조회할까요?
 

Fine-tuning

Fine-tuning은 이미 학습된 모델을 특정 작업, 도메인, 말투, 출력 형식에 맞게 추가 학습하여 모델의 파라미터를 조정하는 과정입니다.

 

기본 LLM은 일반적인 질문에는 잘 답합니다.
하지만 회사 고객센터에서는 항상 다음 형식으로 답변해야 한다고 해보겠습니다.

1. 문제 원인
2. 해결 방법
3. 추가 확인 사항
 

프롬프트로도 어느 정도 제어할 수 있지만, 질문이 다양해지고 응답이 길어지면 형식이 흔들릴 수 있습니다.
이럴 때 고객센터 상담 데이터와 정답 응답 형식을 모아 모델을 추가 학습시키면, 모델이 해당 형식을 더 안정적으로 따를 수 있습니다.

 

Fine-tuning은 보통 다음 순서로 진행됩니다.

기본 모델 선택
→ 학습 데이터 준비
→ 데이터 포맷 구성
→ 추가 학습
→ 평가
→ 배포
 

처음부터 모델을 새로 학습하는 것은 비용이 매우 큽니다.
그래서 보통 이미 대규모 데이터로 학습된 모델을 선택합니다.

일반 대화 모델
코드 특화 모델
한국어 성능이 좋은 모델
작은 온프레미스 모델
오픈소스 LLM
 

 

 

Fine-tuning에는 입력과 원하는 출력의 예시가 필요합니다.

고객센터 답변 모델이라면 다음과 같은 데이터가 필요할 수 있습니다.

 
{
  "input": "배송이 지연됐어요.",
  "output": "불편을 드려 죄송합니다. 주문번호를 확인해주시면 배송 상태를 확인해드리겠습니다."
}
 

Text-to-SQL 모델이라면 다음과 같은 데이터가 필요할 수 있습니다.

 
{
  "question": "지난달 신규 가입자 수는?",
  "schema": "users(id, created_at)",
  "sql": "SELECT COUNT(*) FROM users WHERE ..."
}
 

데이터 품질은 Fine-tuning 성능을 크게 좌우합니다.
잘못된 예시를 넣으면 모델이 잘못된 패턴을 학습합니다.

 

모델이 학습할 수 있는 일관된 형식으로 데이터를 정리합니다.

예:

### Instruction
사용자의 문의에 고객센터 상담원처럼 답변하라.

### Input
배송이 지연됐어요.

### Output
불편을 드려 죄송합니다. 주문번호를 확인해주시면 배송 상태를 확인해드리겠습니다.
 

형식이 일관되어야 모델이 원하는 패턴을 잘 배웁니다.

 

모델은 입력을 보고 원하는 출력을 내도록 파라미터를 조정합니다.
여기서 파라미터는 모델 내부의 가중치입니다.

전체 모델을 모두 학습할 수도 있지만, 비용이 크기 때문에 일부 파라미터만 학습하는 방법도 많이 씁니다. 대표적으로 LoRA 같은 방식은 전체 모델을 바꾸지 않고 작은 추가 파라미터를 학습해 비용을 줄입니다.

 

Fine-tuning 후에는 반드시 평가해야 합니다.

평가할 항목은 다음과 같습니다.

원하는 출력 형식을 지키는가?
특정 도메인 용어를 잘 사용하는가?
기존 일반 성능이 나빠지지 않았는가?
환각이 줄었는가?
민감한 요청을 잘 거절하는가?
실제 사용자 질문에서 안정적인가?
 

Fine-tuning은 모델을 바꾸는 작업이므로, 예상치 못한 부작용이 생길 수 있습니다. 그래서 평가 없이 배포하면 위험합니다.

 

Fine-tuning은 다음 상황에서 유용합니다.

첫째, 출력 형식을 매우 안정적으로 지켜야 할 때입니다.

예를 들어 항상 JSON으로 답해야 하는 분류 모델이 있다고 해보겠습니다.

 
{
  "category": "billing",
  "priority": "high",
  "summary": "결제 실패 문의"
}
 

프롬프트만으로 형식 오류가 자주 발생한다면 Fine-tuning이 도움이 될 수 있습니다.

 

둘째, 특정 도메인의 반복적인 작업 패턴을 학습시켜야 할 때입니다.
예를 들어 의료 문서 분류, 법률 문서 요약, 금융 리포트 작성, 고객센터 답변 스타일 같은 작업은 특정한 표현과 형식이 반복됩니다.

 

셋째, Text-to-SQL에서 회사 고유의 SQL 패턴을 학습시키고 싶을 때입니다.
예를 들어 회사에서 "매출"이라고 하면 항상 다음 조건이 포함되어야 한다고 해보겠습니다.

 
WHERE payment_status = 'PAID'
  AND canceled_at IS NULL
 

이런 규칙이 반복된다면 few-shot prompting으로도 처리할 수 있지만, 규모가 커지면 Fine-tuning을 고려할 수 있습니다.

 

정보가 자주 바뀌고, 출처가 중요하고, 문서량이 많다면 RAG가 더 적합합니다.

예:

사내 규정
가격 정책
제품 매뉴얼
API 문서
법률 문서
계약 조건
 

이런 정보를 모델에 Fine-tuning으로 외우게 하려고 하면 업데이트가 어렵습니다.
문서가 바뀔 때마다 다시 학습해야 할 수 있고, 모델이 어떤 근거로 답했는지도 추적하기 어렵습니다.

반면 RAG는 문서를 갱신하면 검색 결과가 바뀌고, 답변에 출처를 붙일 수도 있습니다.

 

Fine-tuning을 RAG의 대체재로 오해하면 시스템 설계가 잘못됩니다.

간단히 구분하면 다음과 같습니다.

모델의 답변 스타일이나 출력 형식을 바꾸고 싶다 → Fine-tuning
모델에게 최신 문서나 사내 지식을 제공하고 싶다 → RAG
정확한 DB 값을 조회하고 싶다 → Text-to-SQL
 

Fine-tuning은 모델의 행동 방식을 바꾸는 데 강합니다.
RAG는 외부 지식을 제공하는 데 강합니다.
Text-to-SQL은 정형 데이터를 조회하는 데 강합니다.

RAG와 Text-to-SQL의 성능 개선 방법

사내 데이터 챗봇이 다음 질문에 답해야 한다고 해보겠습니다.

지난달 환불 정책 위반 사례가 몇 건이야?
 

이 질문은 복합적입니다.

환불 정책이 무엇인지 알아야 한다. → RAG
실제 환불 사례를 DB에서 조회해야 한다. → Text-to-SQL
정책 위반 여부를 판단해야 한다. → 규칙 또는 LLM 판단
결과를 설명해야 한다. → LLM 답변 생성
 

성능이 낮으면 다음 문제가 생길 수 있습니다.

RAG가 잘못된 정책 문서를 검색한다.
Text-to-SQL이 잘못된 테이블을 조회한다.
SQL이 실행되지 않는다.
DB 결과는 맞지만 LLM이 잘못 해석한다.
평가셋이 없어 개선 여부를 알 수 없다.
 

성능 개선은 이런 실패를 단계별로 줄이는 작업입니다.

 

RAG는 다음 흐름에서 각 단계를 개선해야 합니다.

문서 수집
→ 청킹
→ 임베딩
→ 검색
→ reranking
→ 프롬프트
→ 답변 생성
→ 평가
 

RAG는 문서를 근거로 답합니다.
따라서 문서 자체가 틀리거나 오래되면 답변도 틀릴 가능성이 큽니다.

개선할 항목은 다음과 같습니다.

오래된 문서 제거
중복 문서 제거
문서 버전 관리
문서 제목 정리
표와 이미지 내용 정제
깨진 PDF 텍스트 정리
권한 정보 추가
최신 문서 우선순위 부여
 

문서 품질 개선은 기술적으로 화려하지 않지만, RAG 성능에 매우 큰 영향을 줍니다.

 

청킹은 RAG 성능의 핵심입니다.

청크가 너무 작으면 필요한 맥락이 사라집니다.

부모상: 5일
 

이것만 보면 어떤 제도인지, 어느 회사 규정인지, 적용 조건이 무엇인지 부족할 수 있습니다.

반대로 청크가 너무 크면 검색 정확도가 떨어집니다.

사내 복지 규정 전체 100페이지
 

이렇게 크면 질문과 직접 관련 없는 내용이 너무 많이 포함됩니다.

실무적으로는 다음을 조정합니다.

전략 설명
chunk size 조정 예: 300~800 tokens부터 실험
overlap 적용 앞뒤 문맥 일부를 겹치게 유지
heading 포함 제목과 섹션명을 청크에 포함
semantic chunking 문단이나 의미 단위로 나누기
table-aware chunking 표를 행 또는 섹션 단위로 변환
parent-child chunking 작은 청크로 검색하고 큰 문맥을 제공

Parent-child chunking은 특히 유용합니다.
작은 청크는 검색 정확도가 높고, 큰 청크는 답변에 필요한 문맥을 제공합니다.

그래서 작은 청크로 먼저 찾고, 답변에는 그 청크가 속한 큰 문단이나 섹션을 넣는 방식입니다.

 

임베딩 모델에 따라 검색 결과가 달라집니다.

선택 기준은 다음과 같습니다.

 

기준 설명
언어 성능 한국어 문서라면 한국어 또는 다국어 성능이 중요
도메인 적합성 법률, 의료, 코드, CS 문서 등 도메인별 차이
비용 API 비용 또는 자체 운영 비용
속도 대량 문서 임베딩과 검색 속도
벡터 차원 저장 비용과 검색 성능에 영향
재임베딩 부담 모델 교체 시 기존 문서 전체 재임베딩 필요

한국어 문서가 많다면 영어 벤치마크만 보고 임베딩 모델을 선택하면 안 됩니다.

실제 사내 문서와 실제 질문으로 평가해야 합니다.

 

Hybrid Search는 벡터 검색과 키워드 검색을 함께 사용하는 방식입니다.

벡터 검색은 표현이 달라도 의미가 비슷한 문서를 찾는 데 강합니다.

질문: 부모님이 돌아가시면 며칠 쉬어?
문서: 직계존속 사망 시 경조 휴가 5일
 

키워드 검색은 정확한 문자열이 중요한 경우에 강합니다.

질문: PG-ERR-4821 원인
문서: PG-ERR-4821 오류는 결제 승인 실패 시 발생한다.
 

RAG에서는 둘 다 필요합니다.
그래서 BM25 같은 키워드 검색과 벡터 검색을 결합하는 경우가 많습니다.

 

Metadata Filtering은 문서의 부가 정보를 이용해 검색 범위를 제한하는 방식입니다.

 
{
  "department": "HR",
  "product": "payment",
  "updated_at": "2026-02-01",
  "permission": "employee_only",
  "language": "ko"
}
 

사용자가 결제 API에 대해 질문한다면 product = payment인 문서만 검색할 수 있습니다.
사용자가 인사팀 문서 권한이 없다면 해당 문서는 검색 결과에서 제외해야 합니다.

Metadata Filtering은 정확도뿐 아니라 보안에도 중요합니다.

 

Query Rewriting은 사용자 질문을 검색에 더 적합한 형태로 바꾸는 방식입니다.

원 질문:

그거 신청 어떻게 해?
 

대화 문맥:

이전 질문에서 재택근무에 대해 이야기함
 

재작성:

재택근무 신청 방법은 무엇인가?
 

짧고 생략이 많은 질문에서는 Query Rewriting이 검색 품질을 크게 높일 수 있습니다.

 

Multi-query Retrieval은 하나의 질문을 여러 검색 질문으로 확장하는 방식입니다.

원 질문:

연차 이월 규정 알려줘.
 

확장 질문:

미사용 연차는 언제까지 사용할 수 있는가?
연차 이월 기준은 무엇인가?
연차 소멸 시점은 언제인가?
 

여러 검색 결과를 합치면 놓치는 문서를 줄일 수 있습니다.
다만 너무 많은 쿼리를 만들면 비용과 지연시간이 늘어날 수 있으므로 균형이 필요합니다.

 

Reranking은 1차 검색 결과를 더 정교한 모델로 다시 정렬하는 방식입니다.

1차 검색: 빠르게 top 30 후보 검색
Reranker: 질문과 각 문서의 관련성 재평가
최종 선택: top 5
 

벡터 검색은 빠르지만 미세한 관련성 판단이 약할 수 있습니다.
Reranker는 질문과 문서를 함께 보고 더 정밀하게 판단하기 때문에, 최종 Context 품질을 높이는 데 효과적입니다.

 

검색된 문서를 LLM에게 어떻게 넣느냐도 중요합니다.

좋은 프롬프트는 다음을 명확히 해야 합니다.

문서에 근거해서만 답변하라.
문서에 없는 내용은 추측하지 말라.
답변에 사용한 출처를 표시하라.
상충되는 문서가 있으면 최신 문서를 우선하라.
불확실하면 불확실하다고 말하라.
 

예:

아래 문서 내용만 근거로 답변하라.
문서에 없는 내용은 "제공된 문서에서 확인되지 않습니다"라고 답하라.
답변 끝에 사용한 문서 제목을 표시하라.
 

이런 지시가 없으면 LLM은 검색 문서와 자체 지식을 섞어서 답할 수 있습니다.

 

Evaluation Set은 성능을 측정하기 위한 기준 질문과 정답 모음입니다.

{
  "question": "부모상 휴가는 며칠인가?",
  "expected_answer": "5일",
  "expected_source": "경조사 휴가 규정"
}
 

RAG 평가에서는 다음을 봅니다.

평가 항목 의미
Retrieval Recall 정답 문서가 검색 결과에 포함되었는가
Context Precision 검색된 문서 중 관련 없는 문서가 적은가
Answer Correctness 답변 내용이 맞는가
Faithfulness 답변이 문서에 근거하는가
Citation Accuracy 출처가 정확한가
Latency 응답 시간이 적절한가
Cost 비용이 허용 가능한가

평가셋이 없으면 개선이 감으로 진행됩니다.
평가셋이 있으면 청킹, 임베딩 모델, top-k, reranking, 프롬프트를 바꿨을 때 실제로 좋아졌는지 측정할 수 있습니다.

 

Text-to-SQL은 다음 흐름에서 각 단계를 개선해야 합니다.

질문 이해
→ 스키마 연결
→ SQL 생성
→ SQL 검증
→ 실행
→ 결과 해석
→ 평가
 

LLM에게 테이블과 컬럼 이름만 주면 부족한 경우가 많습니다.

나쁜 예:

orders(id, user_id, amount, status, created_at)
 

좋은 예:

orders:
- id: 주문 ID
- user_id: 주문한 사용자 ID
- amount: 주문 금액. 취소 주문도 포함될 수 있음.
- status: 주문 상태. PAID, CANCELED, REFUNDED 중 하나.
- created_at: 주문 생성 시각.
 

컬럼 설명, 값의 의미, 조인 관계, 비즈니스 규칙을 함께 제공하면 SQL 생성 정확도가 올라갑니다.

 

사용자의 자연어 표현과 DB 정의를 연결하는 사전이 필요합니다.

사용자 표현 실제 정의
매출 결제 완료 주문의 orders.total_amount 합계
순매출 매출에서 환불 금액을 제외한 값
신규 고객 첫 주문을 완료한 사용자
활성 사용자 최근 30일 내 로그인한 사용자
이탈 고객 최근 90일간 구매가 없는 사용자
전환율 방문자 중 구매 완료 사용자 비율

이런 정의가 없으면 모델이 임의로 해석할 수 있습니다.
Text-to-SQL에서는 “문법적으로 맞는 SQL”보다 “비즈니스적으로 맞는 SQL”이 더 중요합니다.

 

Few-shot Prompting은 예시 몇 개를 프롬프트에 넣어 모델이 원하는 패턴을 따르게 하는 방법입니다.

예시 1
질문: 지난달 신규 가입자 수는?
SQL:
SELECT COUNT(*) AS count
FROM users
WHERE created_at >= DATE_TRUNC('month', CURRENT_DATE - INTERVAL '1 month')
  AND created_at < DATE_TRUNC('month', CURRENT_DATE);

예시 2
질문: 이번 달 결제 완료 주문 수는?
SQL:
SELECT COUNT(*) AS count
FROM orders
WHERE status = 'PAID'
  AND created_at >= DATE_TRUNC('month', CURRENT_DATE);
 

이런 예시를 넣으면 모델이 회사의 날짜 처리 방식, 상태값 처리 방식, alias 스타일, 집계 방식을 더 잘 따를 수 있습니다.

 

DB마다 SQL 문법이 다릅니다.

DB 날짜 처리 예시
PostgreSQL DATE_TRUNC('month', CURRENT_DATE)
MySQL DATE_FORMAT(NOW(), '%Y-%m-01')
BigQuery DATE_TRUNC(CURRENT_DATE(), MONTH)
Snowflake DATE_TRUNC('MONTH', CURRENT_DATE())

프롬프트에 사용하는 DB 종류를 명확히 알려야 합니다.

Use PostgreSQL syntax.
 

또는 한국어로:

PostgreSQL 문법으로 SQL을 작성하라.
 

이 지시가 없으면 모델이 여러 SQL dialect를 섞어 잘못된 쿼리를 만들 수 있습니다.

 

SQL Validation은 생성된 SQL이 안전하고 실행 가능한지 검사하는 단계입니다.

검사 항목은 다음과 같습니다.

존재하지 않는 테이블 사용 여부
존재하지 않는 컬럼 사용 여부
잘못된 조인 여부
문법 오류
위험 명령어 포함 여부
권한 없는 테이블 접근 여부
너무 큰 결과 반환 여부
쿼리 실행 시간 초과 가능성
 

실무에서는 보통 다음 명령어를 막습니다.

DELETE
UPDATE
INSERT
DROP
ALTER
TRUNCATE
 

Text-to-SQL 서비스는 가능하면 read-only DB 계정을 사용해야 합니다.

 

Execution Feedback Loop -> 생성된 SQL을 실행했는데 오류가 나면, 오류 메시지를 바탕으로 다시 수정하게 할 수 있습니다.

SQL 생성
→ 실행
→ 오류 발생
→ 오류 메시지와 SQL을 LLM에 제공
→ 수정 SQL 생성
→ 재실행
 

예:

오류:
column "user_name" does not exist

수정:
users.name 컬럼을 사용하도록 변경
 

다만 무한 반복을 막기 위해 재시도 횟수는 제한해야 합니다.

 

SQL이 실행되었다고 해서 답이 맞는 것은 아닙니다.

예를 들어 사용자는 “지난달”을 물었는데 SQL은 “최근 30일”을 조회할 수 있습니다.
문법적으로는 맞지만 의미적으로는 틀릴 수 있습니다.

검증할 항목은 다음과 같습니다.

날짜 범위가 맞는가?
집계 기준이 맞는가?
취소/환불 데이터 제외 여부가 맞는가?
조인으로 중복 카운트가 생기지 않았는가?
NULL 처리가 적절한가?
LIMIT가 필요한가?
사용자 권한에 맞는 데이터인가?
 

모호한 질문에는 무리해서 SQL을 만들지 않는 것이 좋습니다.

예:

지난달 성과 알려줘.
 

이 질문은 기준이 불명확합니다.

성과 = 매출?
성과 = 가입자 수?
성과 = 전환율?
성과 = 주문 수?
 

좋은 시스템은 다음처럼 되묻습니다.

성과 기준을 선택해주세요. 매출, 주문 수, 신규 가입자 수, 전환율 중 어떤 지표를 조회할까요?
 

Text-to-SQL에서 되묻기는 정확도를 높이는 중요한 UX입니다.

 

Text-to-SQL은 실제 DB에 접근하기 때문에 보안이 매우 중요합니다.

필수 안전장치는 다음과 같습니다.

read-only 계정 사용
사용자별 테이블 접근 권한 제한
민감 정보 마스킹
쿼리 timeout 설정
최대 row limit 설정
감사 로그 저장
위험 SQL 차단
운영 DB 직접 접근 제한
 

LLM이 만든 SQL을 검증 없이 운영 DB에 실행하는 것은 위험합니다.

 

Text-to-SQL도 평가셋이 필요합니다.

{
  "question": "지난달 결제 완료 주문 수는?",
  "expected_sql": "SELECT COUNT(*) FROM orders WHERE status = 'PAID' ...",
  "expected_result": 5821
}
 

평가 방식은 다음과 같습니다.

평가 항목 설명
Exact Match SQL 문자열이 정답 SQL과 같은가
Execution Accuracy 실행 결과가 정답과 같은가
Semantic Accuracy SQL 형태는 달라도 의미가 같은가
Safety 위험 쿼리를 만들지 않는가
Latency 응답 시간이 적절한가
User Satisfaction 실제 사용자가 만족하는가

실무에서는 SQL 문자열이 정답과 달라도 실행 결과가 같으면 맞는 경우가 많습니다. 그래서 Execution Accuracy가 중요합니다.

 

RAG와 Text-to-SQL은 실패 방식이 다릅니다.

RAG 정답 문서 미검색, 잘못된 문서 검색, 문맥 누락, 출처 오류
Text-to-SQL 잘못된 컬럼, 잘못된 조인, 잘못된 집계, SQL 오류, 보안 위험

따라서 개선 방법도 다릅니다.

RAG는 검색 품질과 문서 품질이 핵심이다.
Text-to-SQL은 스키마 이해, SQL 검증, 실행 결과 정확성이 핵심이다.
 

공통적으로 중요한 것은 평가셋입니다.
평가셋이 없으면 어떤 변경이 실제로 성능을 올렸는지 알기 어렵습니다.

 

RAG에서는 문서, 청킹, 검색, 프롬프트, 평가가 함께 중요합니다.
Text-to-SQL에서는 스키마, 용어 정의, SQL 생성, 검증, 보안, 평가가 함께 중요합니다.

머신러닝 직접 구현

머신러닝을 직접 만든다는 것은 데이터를 사용해 모델이라는 함수를 학습시키고, 손실함수로 틀린 정도를 측정하며, 최적화를 통해 파라미터를 조정하는 과정을 설계하고 구현하는 것입니다.

 

집값을 예측하는 모델을 만든다고 해보겠습니다.

입력은 다음과 같습니다.

면적: 84㎡
방 개수: 3개
위치 점수: 8
건축 연도: 2018
 

출력은 다음과 같습니다.

예상 집값: 7억 원
 

이 문제를 함수처럼 보면 다음과 같습니다.

집값 = f(면적, 방 개수, 위치 점수, 건축 연도)
 

여기서 f가 모델입니다.
머신러닝은 이 함수 f를 사람이 직접 규칙으로 작성하는 것이 아니라, 데이터를 통해 학습시키는 방식입니다.

 

머신러닝에서 모델(Model) 은 입력을 받아 출력을 내는 함수입니다.

y = f(x)
 

각 기호는 다음 의미입니다.

기호의미
x 입력 데이터
f 모델 함수
y 예측 결과

예를 들어 구매 예측 모델은 다음과 같은 함수로 볼 수 있습니다.

구매 확률 = f(사용자 나이, 방문 횟수, 장바구니 금액, 최근 클릭 수)
 

스팸 분류 모델은 다음과 같습니다.

스팸 여부 = f(이메일 제목, 본문, 발신자, 링크 수)
 

LLM도 넓게 보면 함수입니다.

다음 토큰의 확률 분포 = f(이전 토큰들)
 

즉, LLM은 이전 토큰들이 주어졌을 때 다음 토큰이 무엇일지 확률을 계산하는 매우 큰 함수라고 볼 수 있습니다.

 

학습(Training) 은 모델 내부의 파라미터를 조정해 예측을 정답에 가깝게 만드는 과정입니다.

가장 단순한 선형 모델을 보겠습니다.

예측값 = w × 입력값 + b
 

각 기호는 다음 의미입니다.

w weight, 가중치
b bias, 편향
입력값 모델에 들어가는 값
예측값 모델이 출력하는 값

예를 들어 면적만으로 집값을 예측한다고 해보겠습니다.

예측 집값 = w × 면적 + b
 

처음에는 w와 b가 적절하지 않을 수 있습니다.

w = 100만 원
b = 1억 원
 

면적이 84㎡라면 모델의 예측은 다음과 같습니다.

예측 집값 = 100만 × 84 + 1억 = 1억 8400만 원
 

그런데 실제 집값이 7억 원이라면 모델은 크게 틀렸습니다.

학습은 이런 오차를 줄이도록 w와 b를 계속 조정하는 과정입니다.

 

손실함수(Loss Function) 는 모델의 예측이 정답과 얼마나 다른지 측정하는 함수입니다.

예를 들어 실제 집값이 7억 원이고 모델 예측이 6억 원이라면 차이는 1억 원입니다.

간단한 손실은 다음처럼 정의할 수 있습니다.

손실 = |실제값 - 예측값|
 

또는 제곱 오차를 사용할 수도 있습니다.

손실 = (실제값 - 예측값)²
 

각 항목의 의미는 다음과 같습니다.

실제값 정답 데이터
예측값 모델이 낸 값
손실 모델이 얼마나 틀렸는지 나타내는 값

손실이 작을수록 모델이 정답에 가까운 예측을 하고 있다는 뜻입니다.

분류 문제에서는 다른 손실함수를 씁니다. 예를 들어 스팸 분류에서는 모델이 “스팸일 확률”을 예측하고, 실제 라벨과의 차이를 측정합니다. LLM 학습에서는 다음 토큰에 대한 확률 분포와 실제 정답 토큰 사이의 차이를 측정하는 손실함수를 사용합니다.

 

최적화(Optimization) 는 손실함수를 작게 만들도록 파라미터를 조정하는 과정입니다.

가장 대표적인 방법은 Gradient Descent, 즉 경사하강법입니다.

직관적으로는 이런 과정입니다.

현재 파라미터로 예측한다.
예측이 얼마나 틀렸는지 손실을 계산한다.
손실이 줄어드는 방향을 찾는다.
그 방향으로 파라미터를 조금 조정한다.
이 과정을 반복한다.
 

비유하면 산에서 가장 낮은 골짜기를 찾아 내려가는 과정과 비슷합니다.
현재 위치의 기울기를 보고 어느 방향으로 내려가야 높이가 낮아지는지 판단합니다.

여기서 산의 높이는 손실값이고, 현재 위치는 파라미터 값입니다.
모델은 손실이 줄어드는 방향으로 파라미터를 조금씩 바꿉니다.

 

머신러닝 모델을 직접 만든다는 것은 보통 다음 전체 과정을 포함합니다.

문제 정의
→ 데이터 수집
→ 데이터 전처리
→ 모델 선택
→ 학습
→ 평가
→ 배포
→ 모니터링
 

먼저 무엇을 예측하거나 분류할지 정해야 합니다.

스팸 분류 이메일 텍스트 스팸/정상
집값 예측 면적, 위치, 연식 가격
고객 이탈 예측 사용 로그 이탈 확률
상품 추천 사용자 행동 추천 상품
다음 토큰 예측 이전 토큰들 다음 토큰 확률

문제 정의가 불명확하면 데이터도 잘못 모으고, 모델도 잘못 만들고, 평가도 흔들립니다.

 

모델은 데이터를 보고 학습합니다.
따라서 데이터가 부족하거나 잘못되어 있으면 좋은 모델을 만들기 어렵습니다.

이메일 본문 + 스팸 여부
집 정보 + 실제 거래 가격
사용자 행동 로그 + 구매 여부
질문 + 정답 SQL
문서 + 정답 답변
 

데이터를 모델이 사용할 수 있는 형태로 정리합니다.

결측치 처리
이상치 제거
텍스트 토큰화
숫자 정규화
카테고리 인코딩
라벨 정리
학습/검증/테스트 데이터 분리
 

전처리는 모델 성능에 큰 영향을 줍니다.
좋은 모델을 써도 데이터가 엉망이면 성능이 나오기 어렵습니다.

 

문제에 맞는 모델을 선택합니다.

간단한 수치 예측 선형 회귀
이진 분류 로지스틱 회귀
표 데이터 예측 트리 모델, Gradient Boosting
이미지 처리 CNN, Vision Transformer
텍스트 처리 Transformer, LLM
추천 Matrix Factorization, 딥러닝 추천 모델

모델 선택은 문제 유형, 데이터 크기, 해석 가능성, 비용, 지연시간 요구사항에 따라 달라집니다.

 

학습은 다음 과정을 반복합니다.

입력 데이터를 모델에 넣는다.
모델이 예측값을 낸다.
정답과 예측값을 비교한다.
손실함수를 계산한다.
손실을 줄이는 방향으로 파라미터를 업데이트한다.
반복한다.
 

이 반복을 통해 모델은 점점 데이터 패턴에 맞게 조정됩니다.

 

학습 데이터에서 잘 맞는다고 해서 좋은 모델은 아닙니다.
모델이 학습 데이터만 외우고 새로운 데이터에는 약할 수 있기 때문입니다. 이를 Overfitting, 즉 과적합이라고 합니다.

그래서 데이터를 보통 나눕니다.

Training Set 모델 학습
Validation Set 모델 튜닝
Test Set 최종 성능 평가

좋은 모델은 학습 데이터뿐 아니라 새로운 데이터에서도 잘 작동해야 합니다. 이를 Generalization, 즉 일반화라고 합니다.

 

학습된 모델을 실제 서비스에 적용합니다.

예:

사용자가 이메일을 받는다.
모델이 스팸 확률을 계산한다.
확률이 높으면 스팸함으로 보낸다.
 

배포 단계에서는 정확도뿐 아니라 지연시간, 비용, 안정성, 장애 대응도 중요합니다.

 

모델은 배포 후에도 계속 관리해야 합니다.

실제 서비스 데이터는 시간이 지나면서 바뀔 수 있습니다.

예:

사용자 행동 변화
새로운 상품 등장
스팸 패턴 변화
경제 상황 변화
정책 변경
데이터 수집 방식 변경
 

이런 변화로 모델 성능이 떨어질 수 있습니다.
그래서 운영 중에도 성능을 모니터링하고 필요하면 재학습해야 합니다.

 

LLM 애플리케이션 개발은 보통 이미 만들어진 모델을 활용하는 일입니다.

LLM API 호출
프롬프트 작성
RAG 파이프라인 구성
Text-to-SQL 파이프라인 구성
DB 연동
권한 처리
로그 저장
평가 시스템 구축
 

반면 머신러닝을 직접 만든다는 것은 모델 학습 자체를 다루는 일입니다.

데이터 준비
모델 구조 선택
손실함수 정의
최적화 수행
파라미터 학습
평가
배포
모니터링
 

머신러닝 모델은 사람이 직접 모든 규칙을 작성하지 않습니다.
데이터를 보고 규칙에 해당하는 파라미터를 학습합니다.

전통적인 프로그램은 이런 방식입니다.

if amount > 1000000:
    risk = "high"
 

머신러닝은 이런 방식에 가깝습니다.

risk = f(amount, user_history, device, location, ...)
 

여기서 f는 데이터로 학습된 함수입니다.

 

LLM 기반 애플리케이션을 이해할 때 가장 중요한 것은 개별 기술을 따로 외우는 것이 아니라, 어느 상황에 어떤 방법을 사용해야 하는지 판단하는 능력입니다.
정형 데이터에서 정확한 수치나 집계가 필요하면 Text-to-SQL, 문서 기반 지식이나 최신 정보를 활용해야 한다면 RAG가 적합합니다.
모델의 답변 형식, 말투, 반복적인 작업 패턴을 개선해야 한다면 Fine-tuning을 고려할 수 있습니다.
반대로 기존 LLM이나 검색 구조로 해결하기 어렵고, 특정 데이터를 기반으로 예측·분류·추천 함수를 직접 만들어야 한다면 실제 머신러닝 모델 구축이 선택지가 됩니다.
결국 LLM 애플리케이션 개발의 핵심은 Text-to-SQL, RAG, Fine-tuning, 직접 머신러닝 모델 구축 중에서 문제의 성격에 맞는 방법을 선택하고 조합하는 것이라고 볼 수 있습니다.

'기타' 카테고리의 다른 글

금융 산업 이해하기 - 은행은 무슨 일을 할까 (1)  (3) 2026.03.04
[인프라] AWS 백엔드 아키텍처  (4) 2025.09.03
[firebase] 앱 알림 구축  (0) 2025.08.23
메모 도구 정리 obsidian vs edrawmind  (2) 2025.08.01
[단위테스트] 원칙과 패턴 (9장) - 목 처리에 대한 모범 사례  (0) 2025.04.22
'기타' 카테고리의 다른 글
  • 금융 산업 이해하기 - 은행은 무슨 일을 할까 (1)
  • [인프라] AWS 백엔드 아키텍처
  • [firebase] 앱 알림 구축
  • 메모 도구 정리 obsidian vs edrawmind
ksngh
ksngh
웹 백엔드 개발 블로그입니다. https://github.com/ksngh
  • ksngh
    featherdale
    ksngh
  • 전체
    오늘
    어제
    • 분류 전체보기 (68)
      • 데이터베이스 (10)
      • spring (11)
      • redis (7)
      • ELK (11)
      • 회고 (6)
      • 기타 (13)
        • java (2)
        • 디자인패턴 (2)
        • 영어 (1)
        • 자바스크립트 (1)
        • graphQL (2)
        • 블록체인 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    레디스
    PostgreSQL
    데이터베이스
    NoriTokenizer
    엘라스틱 서치 인 액션
    회고
    자료구조
    대용량데이터베이스
    단위 테스트
    nori tokenizer
    NORI
    Spring Core
    Redis
    연말 회고
    spring
    조인의 종류
    Mock
    엘라스틱서치
    Text to SQL
    단위테스트
    elastic search in action
    Elasticsearch
    대용량 데이터 베이스
    디자인패턴
    Elastic Search
    core
    graphql
    엘라스틱 서치
    gof
    Spy
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
ksngh
LLM 어플리케이션 이해하기 (Text to SQL, Finetuning, RAG 등)
상단으로

티스토리툴바