MCP로 설계하는 LLM 아키텍처

Kirogramer·2026년 2월 19일

AI

목록 보기
7/10
post-thumbnail

요즘은 누구나 한 번쯤 LLM을 써봅니다.
OpenAI든, Claude든, 사내 모델이든.

보통은 이렇게 시작합니다.

"최근 3개월 매출 추이를 요약해줘"

처음엔 신기합니다.
꽤 그럴듯한 문장을 뱉어냅니다.

그런데 이걸 서비스에 붙이는 순간 문제가 시작됩니다.

매출 데이터는 어디서 가져오지?
사용자 권한은?
JSON으로 받아야 프론트에서 쓰기 쉬운데?
다른 요청이 들어오면 프롬프트를 또 고쳐야 하나?

이쯤 되면 느끼게 됩니다.

“이걸 그냥 프롬프트로 계속 붙이는 게 맞나?”

여기서 등장하는 개념이 MCP(Model Context Protocol) 입니다.


MCP를 한 줄로 설명하면

LLM이 “아무 말이나 하는 모델”이 아니라 M정해진 범위 안에서 일하는 컴포넌트가 되도록 만드는 방식입니다.
조금 더 쉽게 말하면, LLM에게 “이 안에서만 움직여”라고 구조를 만들어주는 것 입니다.

그냥 쓰면 왜 불편해질까?

예를 들어 이런 요청이 들어왔다고 해봅시다.

“최근 6개월 매출 증가율 보여줘”

이걸 그대로 LLM에 던지면 모델은 알아서 계산한 것처럼 말해줍니다.
하지만 실제 서비스라면 진짜 매출 데이터로 계산해야 합니다.

그럼 보통 이렇게 됩니다.

  1. 서버에서 DB 조회
  2. 결과를 문자열로 붙임
  3. “이 데이터를 기반으로 설명해줘”라고 다시 LLM 호출

코드는 점점 이렇게 변합니다.

List<Sales> data = salesRepository.findLast6Months();
String prompt = buildPromptWithData(data);
String result = openAiClient.chat(prompt);

처음엔 괜찮습니다.

하지만 기능이 늘어나면 프롬프트는 점점 길어지고, 설명해야 할 데이터 구조도 늘어나고, 출력 포맷 요구도 붙습니다. 어느 순간 프롬프트가 1000줄이 됩니다.

MCP는 여기서 접근이 달라진다

MCP 구조에서는 LLM이 직접 계산하지 않습니다.
대신 “무엇을 할지”만 결정합니다.
예를 들어 서버는 이런 기능을 제공합니다.

{
  "name": "getMonthlyRevenue",
  "description": "최근 N개월 매출 데이터 조회",
  "parameters": {
    "months": "integer"
  }
}

이제 LLM은 이렇게 응답합니다.

{
  "tool_call": "getMonthlyRevenue",
  "arguments": { "months": 6 }
}

서버가 실제 데이터를 가져옵니다.

List<Sales> data = salesService.getMonthlyRevenue(6);

그 다음에야 LLM이 설명을 만듭니다.

이 구조의 차이가 꽤 큽니다.

모델은 계산기를 대신하는 게 아니라, 의사결정자가 됩니다.

세팅은 생각보다 어렵지 않다

많은 분들이 MCP라고 하면
뭔가 거대한 프레임워크를 떠올립니다.

하지만 시작은 간단합니다.

  1. LLM에게 줄 수 있는 기능 목록 정의
  2. 기능 호출은 반드시 서버가 수행
  3. 응답 포맷은 JSON으로 고정

예를 들어 Spring 기반이라면 Tool을 그냥 서비스 메서드로 정의해도 됩니다.

public List<SalesDto> getMonthlyRevenue(int months) {
    return salesRepository.findLastMonths(months);
}

LLM이 “이걸 호출하겠다”고 하면 그때 실제 메서드를 실행하는 구조입니다.
이렇게만 해도 이미 MCP에 가까워진 겁니다.

왜 이게 실무에서 중요해질까

서비스가 커질수록
LLM이 할 수 있는 일도 늘어납니다.

  • 매출 분석
  • 사용자 통계
  • 리포트 생성
  • 로그 요약
  • 코드 분석

이걸 전부 프롬프트로 관리하면 나중에 수정이 거의 불가능해집니다.
하지만 기능 단위로 분리해두면 새로운 분석이 추가될 때 Tool만 하나 더 만들면 됩니다.
이 차이는 유지보수 단계에서 크게 느껴집니다.

출력 포맷을 고정하는 순간 안정된다

LLM을 UI와 직접 연결해보면 가끔 이런 일이 생깁니다.
어느 날은 배열을 주고 어느 날은 객체를 줍니다.

그래서 MCP 구조에서는 데이터는 항상 서버가 만든 JSON으로 전달하고 LLM은 설명만 붙이게 합니다.

예를 들어:

{
  "data": [
    { "month": "2025-09", "growth": 12 },
    { "month": "2025-10", "growth": 18 }
  ]
}

이건 변하지 않습니다.
LLM은 이렇게만 합니다.

“10월은 9월 대비 6% 추가 상승했습니다.”

이렇게 역할을 나누면 UI가 절대 깨지지 않습니다.

여기까지 오면 달라지는 점

처음에는 LLM을 “API 호출”처럼 씁니다.
조금 지나면 LLM이 여러 기능을 호출하기 시작합니다.
더 지나면 LLM이 작업을 나눠서 실행합니다.

질문
  ↓
LLM이 필요한 기능 판단
  ↓
서버 기능 실행
  ↓
결과 요약

이 구조가 자리 잡으면 LLM은 더 이상 “챗봇”이 아닙니다.
서비스 안에서 의도를 해석하는 인터페이스 레이어가 됩니다.

MCP는 거창한 기술이 아닙니다. 프롬프트를 잘 쓰는 걸 넘어서,

  • 모델이 직접 데이터를 다루지 않게 하고
  • 서버가 항상 통제권을 갖고
  • 결과 구조를 고정하는 것

이 세 가지만 해도 이미 시작입니다.
그 다음 단계에서야 멀티 에이전트나 오케스트레이션 같은 이야기가 나옵니다.


LLM을 처음 붙이면 “와, 똑똑하다”에서 끝납니다.
하지만 서비스에 넣는 순간 “어떻게 통제하지?”라는 고민이 시작됩니다.

MCP는 그 통제 구조를 만드는 접근입니다.

프롬프트를 잘 쓰는 개발자에서 LLM을 설계하는 개발자로 넘어가는 단계라고 보면 됩니다.

그리고 이건 생각보다 어렵지 않습니다.
이미 하고 있는 서비스 설계와 본질적으로 다르지 않기 때문입니다.

profile
기로그래머

0개의 댓글