
최근 LLM을 활용한 서비스들이 빠르게 늘어나면서 Function Calling이라는 개념이 자주 등장하고 있습니다. 많은 개발자분들이 이 기능을 “AI가 코드를 실행하는 기능” 정도로 이해하시기도 하지만, 실제로는 그보다 훨씬 실용적인 의미를 갖고 있습니다. Function Calling은 AI가 직접 코드를 실행하는 기술이 아니라 AI가 어떤 기능을 호출해야 하는지 판단하는 의사결정 레이어에 가깝습니다.
LLM을 실제 서비스에 연결하려고 할 때 가장 큰 문제 중 하나는 AI가 외부 시스템과 직접 상호작용할 수 없다는 점입니다. 데이터베이스 조회, 내부 API 호출, 파일 처리, 통계 계산 같은 작업들은 결국 서버가 수행해야 합니다. 이때 Function Calling은 LLM이 “어떤 기능을 호출해야 하는지”를 결정하도록 만들어 주는 구조입니다. 다시 말해 AI는 판단을 담당하고, 실제 실행은 서버가 담당하는 구조라고 이해하시면 됩니다.
이 글에서는 Function Calling이 무엇인지 개념을 설명하고, 실제 서비스에서 어떻게 설계하고 구현하는지까지 예제를 통해 정리해 보겠습니다.
일반적인 프로그램에서는 개발자가 직접 함수를 호출합니다. 예를 들어 날씨 정보를 가져오는 함수가 있다고 가정해 보겠습니다.
getWeather("Seoul");
이 호출은 개발자가 명시적으로 작성합니다. 하지만 Function Calling 구조에서는 조금 다른 방식으로 동작합니다. 사용자가 질문을 입력하면 AI가 먼저 질문의 의도를 분석하고, 어떤 함수를 호출해야 하는지 판단합니다.
사용자가 다음과 같은 질문을 했다고 가정해 보겠습니다.
서울 날씨 알려줘
이 질문을 받은 LLM은 자연어 응답 대신 다음과 같은 JSON 형태의 함수 호출 정보를 반환합니다.
{
"name": "getWeather",
"arguments": {
"city": "Seoul"
}
}
여기서 중요한 점은 AI가 실제로 getWeather() 함수를 실행하는 것이 아니라는 점입니다. AI는 단지 “이 함수를 호출하면 될 것 같습니다”라는 판단 결과를 반환할 뿐입니다. 실제 함수 실행은 서버가 담당합니다.
즉 전체 구조는 다음과 같이 동작합니다.
사용자 질문 → LLM 분석 → 호출할 함수 결정 → 서버가 실제 함수 실행 → 결과 반환 → LLM이 자연어 응답 생성
이 구조 덕분에 AI는 외부 시스템과 안전하게 연결될 수 있습니다.
Function Calling을 서비스에 적용하면 다음과 같은 흐름이 만들어집니다.
사용자가 서비스에 질문을 입력하면 서버는 먼저 해당 질문을 LLM에게 전달합니다. 이때 단순히 질문만 보내는 것이 아니라 AI가 사용할 수 있는 함수 목록을 함께 전달합니다. LLM은 질문을 분석한 뒤, 해당 질문을 해결하기 위해 어떤 함수를 호출해야 하는지 판단합니다.
예를 들어 게임 데이터 분석 서비스를 만든다고 가정해 보겠습니다. 사용자가 다음과 같은 질문을 입력합니다.
롤 DAU 알려줘
이 질문을 받은 AI는 내부적으로 다음과 같은 판단을 하게 됩니다.
“이 질문은 특정 게임의 통계 정보를 요청하는 질문이므로 getGameStats 함수를 호출해야 한다.”
그래서 AI는 자연어 답변 대신 다음과 같은 구조를 반환합니다.
{
"name": "getGameStats",
"arguments": {
"game_name": "League of Legends",
"metric": "DAU"
}
}
이 응답을 받은 서버는 실제로 getGameStats() 함수를 실행합니다. 이 함수는 데이터베이스에서 해당 게임의 DAU 데이터를 조회합니다.
SELECT dau
FROM game_stats
WHERE game_name = 'League of Legends';
조회 결과가 서버로 반환되면 서버는 이 데이터를 다시 LLM에게 전달합니다. LLM은 이 데이터를 기반으로 자연어 응답을 생성합니다.
League of Legends의 DAU는 약 1,230만 명입니다.
이렇게 하면 AI는 자연어 이해와 의사결정을 담당하고, 실제 데이터 처리와 실행은 서버가 담당하게 됩니다.
Function Calling을 사용하려면 먼저 LLM에게 어떤 함수들이 존재하는지 정의해 주어야 합니다. 이 과정은 일반적인 API 설계와 매우 유사합니다.
예를 들어 게임 통계를 조회하는 함수는 다음과 같이 정의할 수 있습니다.
{
"name": "getGameStats",
"description": "특정 게임의 통계 정보를 조회한다",
"parameters": {
"type": "object",
"properties": {
"game_name": {
"type": "string",
"description": "조회할 게임 이름"
},
"metric": {
"type": "string",
"description": "조회할 지표 (DAU, MAU, Revenue)"
}
},
"required": ["game_name"]
}
}
이 정의를 LLM에게 전달하면 AI는 사용자 질문을 분석하면서 이 함수가 사용 가능한 기능인지 판단합니다. 여기서 중요한 부분은 description과 parameters입니다. LLM은 이 설명을 기반으로 함수의 역할을 이해하기 때문에 설명이 구체적일수록 정확도가 높아집니다.
Java Spring 환경에서는 Function Calling 결과를 다음과 같은 방식으로 처리할 수 있습니다.
먼저 LLM에게 메시지와 함께 함수 목록을 전달합니다.
ChatRequest request = new ChatRequest();
request.setMessages(messages);
request.setTools(functionDefinitions);
LLM이 응답을 반환하면 응답 안에 Function Call 정보가 포함되어 있는지 확인합니다.
ChatResponse response = openAI.chat(request);
ToolCall toolCall = response.getToolCall();
Function Call이 존재한다면 해당 함수를 실제로 실행합니다.
if(toolCall.getName().equals("getGameStats")){
GameStats stats = gameService.getGameStats(
toolCall.getArguments().get("game_name"),
toolCall.getArguments().get("metric")
);
}
데이터 조회 결과는 다시 LLM에게 전달됩니다.
messages.add(
new ToolMessage("getGameStats", stats.toString())
);
LLM은 이 데이터를 기반으로 최종 자연어 응답을 생성합니다.
LLM을 서비스에 연결할 때 가장 큰 문제는 AI가 실제 데이터를 알 수 없다는 점입니다. 학습 데이터에 포함되지 않은 최신 데이터나 내부 시스템 데이터는 AI가 직접 접근할 수 없습니다.
Function Calling은 이 문제를 해결합니다. AI는 자연어 이해와 의사결정을 담당하고, 실제 데이터 접근은 서버 API를 통해 이루어집니다. 이 구조 덕분에 LLM은 내부 시스템과 안전하게 연결될 수 있으며, 최신 데이터도 활용할 수 있습니다.
특히 다음과 같은 서비스에서 매우 유용하게 사용됩니다.
실무에서 Function Calling을 사용할 때 가장 중요한 점은 함수를 작게 설계하는 것입니다. 하나의 거대한 함수로 모든 작업을 처리하려고 하면 LLM이 어떤 함수를 선택해야 하는지 판단하기 어려워집니다.
예를 들어 다음과 같은 구조는 좋은 설계가 아닙니다.
queryDatabase(sql)
이 구조는 너무 범용적이기 때문에 AI가 적절하게 활용하기 어렵습니다.
반대로 다음과 같은 구조는 LLM이 이해하기 쉽습니다.
getGameStats()
getGameRevenue()
getTopGames()
각 함수가 명확한 역할을 가지도록 설계하는 것이 중요합니다.
Function Calling은 단순한 기능이 아니라 LLM을 실제 시스템과 연결하는 핵심 아키텍처 패턴입니다. AI는 자연어를 이해하고 어떤 기능을 사용해야 하는지 판단하며, 서버는 실제 로직을 실행합니다. 이 역할 분리가 이루어질 때 LLM은 단순한 챗봇을 넘어 실제 서비스를 구동하는 인터페이스로 발전할 수 있습니다.
특히 데이터 기반 서비스나 내부 업무 자동화 시스템을 구축할 때 Function Calling은 매우 강력한 도구가 됩니다. AI에게 모든 것을 맡기기보다는 AI는 판단을 담당하고 시스템은 실행을 담당하도록 구조를 설계하는 것이 안정적인 AI 서비스 개발의 핵심이라고 할 수 있습니다.