가을기 Workspace

백엔드 개발자를 위한 AI Agent 입문 - LLM, 트랜스포머, Agent 기초 본문

개발

백엔드 개발자를 위한 AI Agent 입문 - LLM, 트랜스포머, Agent 기초

가을기_ 2026. 5. 26. 18:34

목차

  1. LLM이란 무엇인가?
  2. Token이란 무엇인가?
  3. Transformer란 무엇인가?
  4. 챗봇이란 무엇인가?
  5. AI Agent는 챗봇과 어떻게 다른가?
  6. Agent가 Tool을 호출한다니, 그게 어떻게 가능할까?
  7. Multi-Agent가 왜 필요할까? Agent 하나면 되는 거 아닌가?
  8. Langchain, 꼭 필요할까? 그냥 API 호출하면 안 되나?
  9. 정리

1. LLM이란 무엇인가?

LLM(Large Language Model) 은 "엄청 큰 언어 모델"이라는 뜻입니다. 쉽게 말하면 "다음에 올 단어를 정말 잘 맞히는 기계" 예요.

핵심 원리: 다음 단어 맞히기

입력:  "오늘 점심으로 김치"
출력:  "찌개" (확률 35%)
       "볶음밥" (확률 25%)
       "전" (확률 15%)
       ...

LLM은 인터넷에 있는 엄청난 양의 글(책, 위키백과, 블로그 등)을 읽고 "이런 단어 뒤에는 보통 이런 단어가 나오더라"를 학습한 거대한 통계 기계입니다.

백엔드 개발자식 비유

LLM = function next_token(context: string): string
  • 입력: 지금까지의 문장
  • 출력: 다음에 올 가장 그럴듯한 단어 하나
  • 이걸 반복해서 문장을 완성합니다.
flowchart LR
    A["입력: '안녕'"] --> B[LLM]
    B --> C["출력: '하세요'"]
    C --> D["다시 입력: '안녕하세요'"]
    D --> B
    B --> E["출력: ','"]
    E --> F["다시 입력: '안녕하세요,'"]
    F --> B
    B --> G["출력: '반갑'..."]

이렇게 한 단어씩 만들어내는 걸 자기회귀(autoregressive) 생성 이라고 부릅니다.


2. Token이란 무엇인가?

LLM은 사실 "단어"가 아니라 Token(토큰) 단위로 생각합니다.

Token = 글자 조각

토큰은 단어보다 작을 수도, 클 수도 있어요. 영어 단어 하나가 토큰 하나일 수도 있고, 긴 단어는 여러 토큰으로 쪼개지기도 합니다.

"Hello"             → ["Hello"]              (1 token)
"unbelievable"      → ["un", "believ", "able"] (3 tokens)
"안녕하세요"          → ["안", "녕", "하세요"]    (3 tokens 정도)

왜 토큰을 알아야 할까?

  1. 💰: API 호출 비용은 토큰 수로 계산됩니다.
  2. 속도 ⚡: 토큰이 많을수록 응답이 느려져요.
  3. 한계 📏: 모델마다 한 번에 처리할 수 있는 토큰 수가 정해져 있습니다(Context Window).
flowchart TD
    A["사용자 입력: '오늘 날씨 어때?'"] --> B[Tokenizer]
    B --> C["['오늘', ' 날씨', ' 어때', '?']"]
    C --> D[LLM이 숫자로 변환]
    D --> E["[2941, 8821, 5532, 33]"]
    E --> F[LLM 처리]
    F --> G["출력 토큰들"]
    G --> H[Detokenizer]
    H --> I["사람이 읽는 글자로 변환"]

3. Transformer란 무엇인가?

Transformer는 2017년 구글에서 발표한 신경망 구조예요. 지금 우리가 쓰는 거의 모든 LLM(GPT, Claude, Gemini 등)이 이 구조를 기반으로 합니다.

핵심 아이디어: Attention(주의 집중)

문장에서 어떤 단어가 다른 단어와 얼마나 관련이 있는지 계산하는 방식입니다.

"그 학생은 시험을 봤는데, 는 결과를 기다렸다."

여기서 "그"가 누구를 가리킬까요? 사람은 자연스럽게 "학생"이라고 알지만, 기계는 모든 단어를 비교해서 "그 ↔︎ 학생"의 관련도가 높다는 걸 계산합니다. 이게 Attention이에요.

flowchart TD
    A["그는"] -.->|0.05| B[학생은]
    A -.->|0.85| C[학생]
    A -.->|0.02| D[시험을]
    A -.->|0.03| E[봤는데]
    A -.->|0.05| F[결과를]

Transformer가 강력한 이유

  1. 병렬 처리: 기존 모델(RNN)은 단어를 하나씩 순서대로 읽었지만, Transformer는 한 번에 다 봅니다.
  2. 장거리 관계: 문장 앞쪽 단어와 뒤쪽 단어의 관계도 잘 파악합니다.
  3. 확장성: 데이터와 컴퓨터 자원을 더 쏟아부으면 더 똑똑해집니다.
flowchart LR
    subgraph 기존방식[RNN: 순차 처리]
        A1[나는] --> A2[학교에] --> A3[갔다]
    end
    subgraph 트랜스포머[Transformer: 병렬 처리]
        B1[나는] -.-> B2[학교에]
        B1 -.-> B3[갔다]
        B2 -.-> B3
        B2 -.-> B1
        B3 -.-> B1
        B3 -.-> B2
    end

백엔드 비유: RNN은 for문, Transformer는 멀티스레딩이라고 생각해도 됩니다.


4. 챗봇이란 무엇인가?

챗봇 = LLM + 대화 인터페이스 + 대화 기록

LLM은 그 자체로는 그냥 "다음 단어 맞히기 기계"입니다. 챗봇은 여기에 다음을 더한 거예요.

  1. 사용자/AI 역할 구분 (system, user, assistant)
  2. 이전 대화를 기억 (Context로 계속 전달)
  3. 사람이 쓰기 좋은 UI

챗봇이 동작하는 방식

sequenceDiagram
    participant U as 사용자
    participant S as 서버
    participant L as LLM API

    U->>S: "안녕"
    S->>L: messages=[{user: "안녕"}]
    L->>S: "반가워요!"
    S->>U: "반가워요!"

    U->>S: "내 이름은 철수야"
    S->>L: messages=[{user: "안녕"}, {assistant: "반가워요!"}, {user: "내 이름은 철수야"}]
    L->>S: "철수님 반가워요"
    S->>U: "철수님 반가워요"

    U->>S: "내 이름이 뭐였지?"
    S->>L: messages=[전체 대화 기록]
    L->>S: "철수님이라고 하셨어요"
    S->>U: "철수님이라고 하셨어요"

중요한 사실

LLM은 사실 기억이 없습니다.

매 요청마다 이전 대화를 전부 다시 보내야 기억하는 것처럼 보이는 거예요. 백엔드 개발자라면 "stateless API에 매번 전체 컨텍스트를 전달하는 구조"라고 이해하면 됩니다.


5. AI Agent는 챗봇과 어떻게 다른가?

가장 큰 차이는 한 단어로 요약할 수 있습니다.

챗봇은 "말"을 한다. Agent는 "행동"을 한다.

비교표

구분 챗봇 AI Agent
입력 사용자 메시지 사용자 목표(goal)
출력 텍스트 응답 행동(action) + 결과
외부 시스템 못 건드림 API/DB/파일 조작 가능
반복 한 번 답하고 끝 목표 달성까지 스스로 반복
비유 똑똑한 친구 똑똑한 인턴

예시로 보기

챗봇에게: "내일 날씨 어때?" → "죄송해요, 저는 실시간 정보를 모릅니다."

Agent에게: "내일 날씨 어때?" → (속으로) "날씨 API를 호출해야겠다" → getWeather(date="2026-05-27", location="서울") 호출 → API 응답: { temp: 22, condition: "맑음" } → "내일 서울은 맑고 22도 정도예요!"

Agent의 동작 흐름

flowchart TD
    A[사용자 목표 입력] --> B[LLM이 상황 판단]
    B --> C{도구가 필요한가?}
    C -->|예| D[도구 호출]
    D --> E[결과 받기]
    E --> B
    C -->|아니오| F[최종 답변 생성]
    F --> G[사용자에게 응답]

"생각 → 행동 → 관찰 → 다시 생각" 의 반복(Loop)이 Agent의 핵심입니다. 이걸 ReAct 패턴(Reasoning + Acting) 이라고 부릅니다.


6. Agent가 Tool을 호출한다니, 그게 어떻게 가능할까?

"잠깐만요, LLM은 그냥 텍스트만 뱉는 거 아니에요? 어떻게 함수를 호출해요?"

이게 처음 보면 정말 신기한 부분입니다. 핵심을 먼저 말하면:

LLM이 직접 함수를 호출하는 게 아닙니다. LLM은 "이 함수를 이런 인자로 호출하고 싶어요"라고 JSON으로 말하고, 우리가 대신 호출해줍니다.

단계별로 보기

1단계: 도구 설명서를 LLM에게 알려줌

{
  "tools": [
    {
      "name": "getWeather",
      "description": "특정 도시의 현재 날씨를 가져옵니다",
      "parameters": {
        "city": { "type": "string", "description": "도시 이름" }
      }
    },
    {
      "name": "sendEmail",
      "description": "이메일을 보냅니다",
      "parameters": {
        "to": { "type": "string" },
        "subject": { "type": "string" },
        "body": { "type": "string" }
      }
    }
  ]
}

2단계: 사용자가 질문

"서울 날씨 알려줘"

3단계: LLM이 텍스트 대신 JSON을 뱉음

{
  "tool_call": {
    "name": "getWeather",
    "arguments": { "city": "서울" }
  }
}

4단계: 우리(백엔드 코드)가 실제로 함수 실행

result = getWeather(city="서울")
# → { "temp": 22, "condition": "맑음" }

5단계: 결과를 다시 LLM에게 전달

"getWeather 결과: 온도 22도, 맑음"

6단계: LLM이 사람 말로 답변

"서울은 지금 22도이고 맑아요!"

전체 흐름도

sequenceDiagram
    participant U as 사용자
    participant A as Agent 서버
    participant L as LLM
    participant T as Tool/API

    U->>A: "서울 날씨 알려줘"
    A->>L: 메시지 + tool 정의 목록
    L->>A: tool_call: getWeather(city="서울")
    Note over A: LLM은 직접 호출 못함<br/>JSON으로만 요청
    A->>T: getWeather("서울")
    T->>A: {temp: 22, condition: "맑음"}
    A->>L: tool 결과 = {temp:22, ...}
    L->>A: "서울은 22도이고 맑아요"
    A->>U: "서울은 22도이고 맑아요"

더 깊이: 어떻게 LLM이 정확한 JSON을 뱉을까?

  1. 학습 데이터: LLM은 학습 단계에서 "이런 상황에선 이런 JSON을 만들어라"를 엄청나게 많이 본 상태입니다.
  2. System Prompt: "도구를 쓰려면 반드시 이 형식의 JSON으로 응답하라"고 시스템 메시지로 강제합니다.
  3. Structured Output: 최신 API는 아예 "이 schema에 맞는 JSON만 뱉어라"고 강제하는 기능이 있어요(잘못된 JSON이 절대 안 나오게 막아줌).

핵심 깨달음: Tool calling은 마법이 아니라 "잘 정의된 프로토콜 + LLM의 패턴 매칭 능력" 입니다. 백엔드식으로 보면 RPC와 비슷해요.


7. Multi-Agent가 왜 필요할까? Agent 하나면 되는 거 아닌가?

좋은 질문입니다. 처음엔 다 한 Agent로 해보는 게 맞아요. 하지만 일이 복잡해지면 한계가 옵니다.

한 Agent가 모든 걸 다 할 때의 문제점

flowchart TD
    A[슈퍼 Agent] --> B[도구 1: 검색]
    A --> C[도구 2: 코드 실행]
    A --> D[도구 3: 이메일]
    A --> E[도구 4: DB 조회]
    A --> F[도구 5: 결제]
    A --> G[도구 6: 번역]
    A --> H[도구 7: ...]
    A --> I[도구 50: ...]
  1. System prompt가 너무 길어짐: 도구 50개 설명을 다 넣으면 매번 수만 토큰을 보내야 함 → 비쌈, 느림.
  2. 헷갈림: 도구가 많으면 LLM이 잘못된 도구를 고를 확률이 올라감.
  3. 유지보수 지옥: "이 Agent는 도대체 뭘 하는 애야?" 한 명이 모든 걸 하면 디버깅이 어려움.

Multi-Agent: 회사 조직도처럼 나누기

flowchart TD
    U[사용자] --> O[Orchestrator Agent<br/>조율자]
    O --> R[Research Agent<br/>리서치 담당]
    O --> C[Coder Agent<br/>코딩 담당]
    O --> W[Writer Agent<br/>문서 작성 담당]

    R --> R1[웹 검색]
    R --> R2[논문 검색]

    C --> C1[코드 실행]
    C --> C2[테스트 실행]

    W --> W1[Markdown 생성]
    W --> W2[번역]

백엔드 개발자식 비유

  • 단일 Agent = God Object (모든 책임을 가진 거대한 클래스)
  • Multi-Agent = 마이크로서비스 아키텍처 (각 서비스가 하나의 일만 잘함)

실전 예시: "AI에 대한 블로그 글 써줘"

sequenceDiagram
    participant U as 사용자
    participant O as Orchestrator
    participant R as Research Agent
    participant W as Writer Agent
    participant E as Editor Agent

    U->>O: "AI 에이전트 블로그 글 써줘"
    O->>R: "AI Agent 최신 트렌드 조사해줘"
    R->>R: 웹 검색, 논문 검색
    R->>O: 자료 정리본
    O->>W: "이 자료로 초안 써줘"
    W->>O: 초안
    O->>E: "문법/논리 검토해줘"
    E->>O: 수정본
    O->>U: 최종 글

언제 Multi-Agent로 가야 하나?

  • ✅ 단일 Agent의 system prompt가 너무 길어졌을 때
  • ✅ 도구가 명확히 다른 도메인으로 나뉠 때
  • ✅ 각 단계를 다른 모델로 쓰고 싶을 때 (예: 리서치는 큰 모델, 단순 변환은 작은 모델)
  • ❌ 단순한 task에 굳이 나누지 말 것. 오버엔지니어링 주의.

8. Langchain, 꼭 필요할까? 그냥 API 호출하면 안 되나?

답부터 말하면: 꼭 필요하진 않습니다. 그냥 API 호출로도 충분히 만들 수 있어요.

하지만 "왜 사람들이 Langchain 같은 프레임워크를 쓰는가"를 이해하면 좋습니다.

그냥 API 호출로 Agent 만들기 (Pseudo code)

import anthropic

client = anthropic.Anthropic()
messages = []
tools = [...]  # 도구 정의

def run_agent(user_input):
    messages.append({"role": "user", "content": user_input})

    while True:
        response = client.messages.create(
            model="claude-opus-4-7",
            messages=messages,
            tools=tools
        )

        if response.stop_reason == "tool_use":
            tool_call = response.content[-1]
            result = execute_tool(tool_call.name, tool_call.input)
            messages.append({"role": "assistant", "content": response.content})
            messages.append({"role": "user", "content": [
                {"type": "tool_result", "tool_use_id": tool_call.id, "content": result}
            ]})
        else:
            return response.content[0].text

이게 사실상 Agent의 전부예요. 약 30줄 이면 됩니다.

그럼 Langchain은 뭘 해주는데?

  1. 추상화: OpenAI/Anthropic/Gemini 등을 같은 인터페이스로 쓸 수 있게.
  2. 유틸리티 모음: 문서 로딩, 청킹, 벡터 DB 연결, 메모리 관리 등 자주 쓰는 걸 미리 만들어 놓음.
  3. 체인 구성: 여러 단계의 LLM 호출을 파이프라인처럼 엮을 수 있음.
  4. 콜백/로깅: 어떤 단계에서 무슨 일이 일어났는지 추적.
flowchart LR
    subgraph 직접구현[직접 구현]
        A1[내가 다 짜야 함]
        A2[모델 바꿀 때마다 코드 수정]
        A3[메모리/로깅 직접]
    end
    subgraph 프레임워크[Langchain 등 사용]
        B1[추상화 제공]
        B2[모델 교체 쉬움]
        B3[유틸 풍부]
        B4[추상화의 무게 ⚠️]
    end

Langchain을 쓰지 않으면 무슨 일이 벌어지나?

  1. 좋은 점

    • 코드가 짧고 명확함. "이 줄에서 정확히 뭐가 일어나는지" 안다.
    • 디버깅이 쉬움. 모든 게 내 코드.
    • 외부 라이브러리 업데이트로 망가질 일 없음.
    • 의존성이 적어서 보안/유지보수에 좋음.
  2. 불편한 점

    • 자주 쓰는 패턴(메모리, 벡터 검색, 도구 실행 루프)을 직접 짜야 함.
    • 여러 모델 지원하려면 어댑터를 만들어야 함.
    • 복잡한 워크플로(분기, 병렬)는 직접 설계해야 함.

실전 권장 사항

  • 프로토타입/학습 단계: 그냥 SDK(Anthropic SDK 등)로 직접 호출하세요. Agent의 작동 원리를 체감할 수 있습니다.
  • 간단한 프로덕션: 직접 호출이 더 명확하고 안정적입니다.
  • 복잡한 워크플로/여러 모델 동시 지원: LangGraph, CrewAI, LlamaIndex 같은 프레임워크 검토.

핵심: 프레임워크는 도구일 뿐, 본질을 가리지 마세요. Agent의 원리(LLM + Tool Loop)를 이해하면 어떤 프레임워크를 써도 됩니다.


9. 정리

지금까지 살펴본 내용을 한 장으로 요약하면 다음과 같습니다.

flowchart TD
    A[Token<br/>글자 조각] --> B[LLM<br/>다음 토큰 예측기]
    C[Transformer<br/>병렬 신경망 구조] --> B
    B --> D[Chatbot<br/>LLM + 대화 기록]
    D --> E[Agent<br/>Chatbot + Tool 사용 루프]
    E --> F[Multi-Agent<br/>여러 Agent의 협력]
    F --> I[고도화된 AI 시스템]

핵심 요약 5줄

  1. LLM은 다음 토큰을 예측하는 통계 기계 입니다.
  2. 챗봇은 LLM에 대화 기록을 계속 던지는 stateless API 구조 입니다.
  3. Agent는 챗봇에 "Tool 호출 루프"를 더한 것 입니다.
  4. Tool calling은 마법이 아니라 LLM이 JSON으로 의도를 전달하면 우리가 대신 실행 해주는 프로토콜입니다.
  5. Langchain은 편의일 뿐 본질은 아닙니다. 직접 만들 수 있어야 진짜 이해한 거예요.
Comments