2025년 4월 7일 - Tmap API를 활용한 MCP 서버 구축

오늘 Tmap REST API 기반의 MCP(Model Context Protocol) 서버 구축에 성공했다. 판다스스튜디오의 'pymcp' 패키지 덕분에 구현 과정은 순조롭게 진행되었다. 이 MCP 서버는 Cursor 편집기와 Tmap API 사이의 연결 고리 역할을 하며, AI 도구가 위치 정보와 교통 데이터에 직접 접근할 수 있게 해준다.

서버 구현의 핵심 코드

서버 구현은 mcp_server.py 파일을 중심으로 이루어졌다. 다음은 MCP 서버 초기화 및 Tmap API 연동 코드의 핵심 부분이다:

import os
from pymcp import PyMCP, mcpwrap
from tmap_api.tmap_api import TmapAPI

# Tmap API 클라이언트 초기화
TMAP_APP_KEY = os.environ.get("TMAP_APP_KEY")
if not TMAP_APP_KEY:
    raise ValueError("TMAP_APP_KEY 환경 변수가 설정되지 않았습니다.")

tmap_client = TmapAPI(app_key=TMAP_APP_KEY)

# MCP 서버 생성
tmap_server = PyMCP(
    name="Tmap API Server",
    instructions="A server providing Tmap API functions for location search, geocoding, route planning, and more"
)

이 코드는 Tmap API 클라이언트를 초기화하고 MCP 서버를 생성한다. 환경 변수에서 API 키를 가져와 안전하게 관리하는 방식을 사용했다.

Tmap API 함수를 MCP 서버에 등록하는 방식은 다음과 같이 데코레이터 패턴을 활용했다:

@tmap_server.wrap_function(name="search_poi_keyword")
def search_poi_keyword(keyword: str, search_type: str = "all", count: int = 20):
    """
    Search for Points of Interest (POI) using keywords
    
    Args:
        keyword: Search keyword
        search_type: Search type (all, name, telno)
        count: Maximum number of search results
    
    Returns:
        POI search result data
    """
    return tmap_client.search_poi_keyword(keyword, search_type, count)

이러한 방식으로 모든 Tmap API 기능을 MCP 서버에 등록했다. pymcp 라이브러리의 데코레이터 패턴을 활용해 Python 함수를 MCP 프로토콜을 통해 호출 가능한 함수로 변환했다.

서버 실행을 위해 run_mcp_server.bat 파일을 만들어 환경 설정과 실행을 자동화했다:

@echo off
echo Starting Tmap MCP Server...

rem Set environment variables if not already set
if "%TMAP_APP_KEY%"=="" (
    echo TMAP_APP_KEY environment variable is not set.
    echo Please set your Tmap API key:
    set /p TMAP_APP_KEY="Enter your Tmap API key: "
)

rem Add current directory to Python path
set PYTHONPATH=%PYTHONPATH%;%~dp0

rem Run the MCP server
python %~dp0\mcp_server.py

pause

또한 Cursor 편집기에 MCP 서버를 등록하기 위한 setup_cursor.py 스크립트도 개발했다:

def register_mcp_server():
    """pymcp 도구를 사용하여 Cursor 편집기에 MCP 서버를 등록합니다."""
    print("Registering Tmap MCP server with Cursor editor...")
    
    # 현재 스크립트 디렉토리
    script_dir = get_script_path()
    
    # mcp_server.py 파일의 절대 경로 구성
    server_path = os.path.join(script_dir, "mcp_server.py")
    server_path = os.path.normpath(server_path)
    
    # Python 인터프리터 경로
    python_path = get_python_path()
    
    # pymcp 명령어 구성
    cmd = [
        python_path, 
        "-m", 
        "pymcp", 
        "cursor", 
        "add-server", 
        "tmap-api", 
        server_path, 
        "--python", 
        python_path
    ]
    
    # 명령어 실행
    try:
        result = subprocess.run(cmd, capture_output=True, text=True)
        if result.returncode == 0:
            print("Successfully registered Tmap MCP server with Cursor editor!")
            print(result.stdout)
        else:
            print("Failed to register Tmap MCP server:")
            print(result.stderr)
    except Exception as e:
        print(f"Error registering MCP server: {e}")

이렇게 개발한 MCP 서버를 기반으로 지능형 일정 계획 시스템을 구현했다. 시스템은 다음과 같은 단계로 작동한다:

  1. AI(Claude, Cursor)가 순차적 사고법(Sequential Thinking)을 통해 검색 전략을 수립한다.
  2. 네이버 로컬서치와 블로그 API, 그리고 Tmap을 통해 주변 식당과 카페 정보를 수집한다.
  3. 수집된 정보를 바탕으로 사용자에게 적합한 후보 일정들을 생성한다.
  4. Tmap 경로 API를 활용해 각 장소 간 이동 거리와 시간을 정확히 계산한다.

이러한 접근법은 사용자가 특정 지역을 방문할 때 시간과 이동 효율성을 극대화할 수 있는 맞춤형 일정을 제공한다. Tmap의 실시간 데이터를 활용함으로써 이전의 대략적인 소요시간 예측이 아닌 현실적이고 실용적인 일정 계획이 가능해졌다.

2025년 4월 9일 - Gemini API의 멀티모달 기능 활용 연구

Google Gemini API의 멀티모달 기능을 활용한 선제적 분류 시스템 개발에 착수했다. 특히 GPT-4 Vision과의 비교 테스트를 통해 흥미로운 점을 발견했다.

Gemini는 같은 멀티모달 작업에서 GPT-4에 비해 현저히 낮은 비용으로 이미지를 처리할 수 있었다. 이는 Gemini가 base64 인코딩된 이미지를 특별한 방식으로 처리하여 토큰 수를 크게 줄이기 때문인 것으로 보인다. 이러한 비용 효율성은 대규모 이미지 처리가 필요한 프로젝트에서 상당한 경제적 이점을 제공할 것이다.

이 기술은 제품 이미지 자동 분류, 의료 영상 사전 스크리닝, 부동산 사진 분석 등 다양한 분야에서 활용 가능성이 높다. 이번 연구를 통해 Gemini가 비용 효율적인 멀티모달 AI 솔루션으로서 높은 잠재력을 가지고 있음을 확인했다.

2025년 4월 10일 - Tmap API 확장 및 대중교통 기능 통합

Tmap API의 대중교통 관련 기능을 MCP 서버에 추가로 통합했다. 새롭게 추가된 기능은 다음과 같다.

지하철 혼잡도 정보 API 연동

@tmap_server.wrap_function(name="get_subway_congestion")
def get_subway_congestion(route_nm: str, station_nm: str, dow: str = None, hh: str = None):
    """
    Get train congestion information for a subway station
    
    Args:
        route_nm: Subway line name (e.g., "Line 1")
        station_nm: Station name (e.g., "Seoul Station")
        dow: Day of week (MON, TUE, WED, THU, FRI, SAT, SUN)
             Returns current day's data if not specified
        hh: Hour (05~23)
             Returns current hour's data if not specified
    
    Returns:
        Train congestion information at 10-minute intervals
    """
    return tmap_client.get_subway_congestion(route_nm, station_nm, dow, hh)

이 함수는 특정 지하철 노선과 역에 대한 열차 혼잡도 정보를 제공한다. 사용자는 요일과 시간을 지정하여 해당 시점의 예상 혼잡도를 조회할 수 있다.

대중교통 경로 탐색 API 연동

@tmap_server.wrap_function(name="public_transit_route")
def public_transit_route(start_x: str, start_y: str, end_x: str, end_y: str,
                        lang: int = 0, format: str = "json", count: int = 10,
                        search_dttm: str = None):
    """
    Search for public transit routes
    
    Args:
        start_x: Starting point X coordinate (longitude) - WGS84
        start_y: Starting point Y coordinate (latitude) - WGS84
        end_x: Destination X coordinate (longitude) - WGS84
        end_y: Destination Y coordinate (latitude) - WGS84
        lang: Language selection (0: Korean, 1: English)
        format: Output format (json, xml)
        count: Maximum number of results (1~10)
        search_dttm: Time machine search date (yyyymmddhhmi)
    
    Returns:
        Public transit route information
    """
    return tmap_client.public_transit_route(
        start_x, start_y, end_x, end_y,
        lang, format, count, search_dttm
    )

이 함수는 출발지와 목적지 좌표를 기반으로 대중교통 경로를 탐색한다. 버스와 지하철을 포함한 복합 경로를 제공하며, 타임머신 기능을 통해 미래 시점의 경로도 조회할 수 있다.

지하철 칸별 혼잡도 및 하차 비율 조회

@tmap_server.wrap_function(name="get_subway_car_congestion")
def get_subway_car_congestion(route_nm: str, station_nm: str, dow: str = None, hh: str = None):
    """
    Get car-specific congestion information for a subway station
    
    Args:
        route_nm: Subway line name (e.g., "Line 1")
        station_nm: Station name (e.g., "Seoul Station")
        dow: Day of week (MON, TUE, WED, THU, FRI, SAT, SUN)
             Returns current day's data if not specified
        hh: Hour (05~23)
             Returns current hour's data if not specified
    
    Returns:
        Car-by-car congestion information at 10-minute intervals
    """
    return tmap_client.get_subway_car_congestion(route_nm, station_nm, dow, hh)

이 기능은 지하철 열차의 각 칸별 혼잡도 정보를 제공한다. 이를 통해 사용자는 덜 혼잡한 칸을 선택하여 더 쾌적한 승차 경험을 할 수 있다.

@tmap_server.wrap_function(name="get_subway_car_getoff_rate")
def get_subway_car_getoff_rate(route_nm: str, station_nm: str, dow: str = None, hh: str = None):
    """
    Get car-specific passenger exit rate information for a subway station
    
    Args:
        route_nm: Subway line name (e.g., "Line 1")
        station_nm: Station name (e.g., "Seoul Station")
        dow: Day of week (MON, TUE, WED, THU, FRI, SAT, SUN)
             Returns current day's data if not specified
        hh: Hour (05~23)
             Returns current hour's data if not specified
    
    Returns:
        Car-by-car passenger exit rate information at 10-minute intervals
    """
    return tmap_client.get_subway_car_getoff_rate(route_nm, station_nm, dow, hh)

이 함수는 지하철 열차의 각 칸별 하차 비율 정보를 제공한다. 이를 통해 특정 역에서 어느 칸에서 많은 승객이 하차하는지 파악할 수 있다.

대중교통 경로 요약 정보 API

@tmap_server.wrap_function(name="public_transit_route_summary")
def public_transit_route_summary(start_x: str, start_y: str, end_x: str, end_y: str,
                               format: str = "json", count: int = 10,
                               search_dttm: str = None):
    """
    Get summary of public transit route information
    
    Args:
        start_x: Starting point X coordinate (longitude) - WGS84
        start_y: Starting point Y coordinate (latitude) - WGS84
        end_x: Destination X coordinate (longitude) - WGS84
        end_y: Destination Y coordinate (latitude) - WGS84
        format: Output format (json, xml)
        count: Maximum number of results (1~10)
        search_dttm: Time machine search date (yyyymmddhhmi)
    
    Returns:
        Summary of public transit route information including total time, distance, and transfers
    """
    return tmap_client.public_transit_route_summary(
        start_x, start_y, end_x, end_y,
        format, count, search_dttm
    )

이 함수는 대중교통 경로의 요약 정보를 제공한다. 총 소요 시간, 거리, 환승 횟수 등의 정보를 간략하게 조회할 수 있어 효율적인 경로 계획을 수립하는 데 도움이 된다.

이러한 기능 확장을 통해 MCP 서버는 이제 자동차 경로뿐만 아니라 대중교통을 활용한 이동 계획도 지원할 수 있게 되었다. 사용자는 자동차와 대중교통의 이동 시간과 비용을 비교하여 최적의 이동 수단을 선택할 수 있으며, 대중교통 이용 시에는 혼잡도 정보를 참고하여 더 쾌적한 이동 경험을 누릴 수 있다.

결론 및 향후 계획

최근 연구를 통해 Tmap API 기반 MCP 서버 구축 경험이라는 중요한 성과를 이루었다. 이러한 기술적 발전은 더 스마트한 이동 및 일정 계획 솔루션의 기반이 될 것이다.

향후에는 자동화해서 일정 생성에 대한 고도화 툴을 만들어보고자 한다.

 

2024년 3월 31일: 든든전세주택 시각화 프로젝트 업데이트

오늘은 기존에 진행 중이던 든든전세주택 시각화 프로젝트에 새로운 데이터가 업데이트되어 이를 반영하는 작업을 시작했다. 최근 데이터 양이 크게 증가하면서 웹 크롤링 과정에서 예상보다 많은 시간이 소요되는 문제에 직면했다.

크롤링 최적화 고려사항

초기에는 비동기 처리를 통한 효율화를 고려했으나, 동일 사이트에 대한 크롤링이라는 특성을 고려하여 배치 단위 처리 방식으로 전환하기로 결정했다. 이는 서버에 과도한 부하를 주지 않으면서도 효율적인 데이터 수집이 가능한 접근법이다.

GitHub 프로젝트에서 이 문제를 해결하기 위해 다음과 같은 배치 처리 함수를 구현했습니다:

# 배치 처리 방식으로 이미지 링크 수집
def get_all_img_links_batch(href_ids, dt='20241007', batch_size=5):
    results = []
    for i in tqdm(range(0, len(href_ids), batch_size)):
        batch = href_ids[i:i + batch_size]
        for href_id in batch:
            img_link = get_img_link(href_id, dt)
            results.append(img_link)
        # 배치 처리 후 잠시 대기
        time.sleep(1.5)
    return results

이 함수는 웹사이트에 과도한 요청을 방지하기 위해 배치 단위로 처리하고 각 배치 사이에 적절한 대기 시간을 두는 방식을 구현했습니다.

시각화 방식 업데이트

기존 코드 중 일부가 최신 라이브러리 버전과 호환되지 않는 문제가 발견되어 시각화 방식을 변경하기로 했다. 더불어 LLM 멀티모달 기능을 활용하여 구조도 이미지에서 방 개수와 같은 정보를 자동으로 추출하는 기능을 추가하기로 계획했다.

모델 테스트 결과

Gemini-2.5, 2.0 Flash, Flash Lite 모델을 테스트해본 결과, 일부 정보 추출에서 정확도가 떨어지는 경우가 발견되었다. 이를 개선하기 위해 두 가지 접근법을 고려 중이다:

  1. 프롬프트 엔지니어링을 통한 정확도 향상
  2. RPM(Request Per Minute) 제한을 고려한 루프 시간 조정

2024년 4월 1일: 데이터 기반 제안서 작성 방법론 개발

오늘은 제안서 작업 시 효율적인 데이터 수집 및 정리 방법론을 개발했다.

LLM을 활용한 리서치 프로세스

  1. 패턴 딥리서치 접근법: Gemini와 ChatGPT를 활용하여 자료를 상세히 조사한 후, 해당 내용을 요구사항에 맞게 명사형 종결 형식으로 재구성하는 방식을 개발했다.
  2. 언어 모델 특성화 활용: 한국어 글쓰기 능력이 비교적 우수한 Claude를 활용하여 최종 내용 재구성 작업을 진행했다.

효율적인 문서 구조화 방법

전체적인 흐름을 먼저 설정하는 것이 중요하다는 점을 발견했다. Napkin AI를 활용하여 프로젝트 흐름도를 먼저 작성한 후, 이를 컨텍스트로 제공하여 LLM에게 내용 재구성을 요청했을 때 더 우수한 결과를 얻을 수 있었다.

이력서 최적화 프로세스

MCP(멀티모달 기능)를 최대한 활용하여 개인 이력서를 효과적으로 수정하는 방법을 개발했다:

  1. YouTube MCP 기능을 활용해 경력기술서 작성 관련 영상 내용을 참고
  2. 개인 GitHub 내용을 정리하여 이력서 PDF와 함께 분석
  3. 구조적 구성을 위해 마크다운 형식으로 변환하거나 Notion에 정리 후 PDF로 변환

2024년 4월 2-3일: 든든전세 대시보드 업그레이드와 LangGraph 구현

LangGraph를 활용한 구조도 정보 추출

기존에 계획했던 방 구조도에서 정보를 추출하는 기능을 LangGraph를 활용하여 구현했다. GitHub 프로젝트에서는 다음과 같이 구현되어 있습니다:

from langgraph.graph import StateGraph, START, END
from .nodes import download_image, save_image, describe_image, check_image_description, get_default_llm
from .edges import decide_to_generate, decide_to_regenerate
from .models import GraphState
from langchain_core.language_models.chat_models import BaseChatModel
from typing import Optional, Dict, List, Any, TypedDict

# 병렬 노드 반환 타입 정의
class SaveImageBranch(TypedDict):
    """이미지 저장 작업의 병렬 분기 결과"""
    save_result: None  # 이미지 저장 결과 (상태를 변경하지 않음)

def make_workflow(llm: Optional[BaseChatModel] = None) -> StateGraph:
    """
    LLM을 인자로 받아 방 구조 분석 워크플로우를 생성하는 함수
    
    이미지 다운로드 후 저장은 병렬로 진행하고, 이미지 분석은 메인 흐름에서 진행

    Args:
        llm (BaseChatModel, optional): 사용할 LLM 모델. 기본값은 None이며, 
                                      None인 경우 기본 모델 사용

    Returns:
        StateGraph: 컴파일된 LangGraph 워크플로우
    """
    # llm이 None인 경우 기본 모델 설정
    llm_instance = llm or get_default_llm()

    # Define a new graph
    workflow = StateGraph(GraphState)

    # Define the nodes
    workflow.add_node("download_image", download_image)
    
    # 이미지 저장 병렬 노드: 상태를 변경하지 않고 별도 작업만 수행
    def save_image_branch(state: GraphState) -> Dict[str, Dict[str, Any]]:
        """다운로드된 이미지를 저장하는 병렬 노드"""
        # 이미지 저장 실행
        save_image(state)
        # 상태 변경 없이 병렬 노드 결과 반환
        return {"save_result": None}
    
    # 병렬 노드 추가: 다운로드 이미지를 계속 사용하면서 저장도 병렬로 수행
    workflow.add_node("save_image_branch", save_image_branch)
    
    # LLM을 사용하는 노드들에 llm_instance 전달
    workflow.add_node("describe_image", lambda state: describe_image(state, llm=llm_instance))
    workflow.add_node("check_image_description", lambda state: check_image_description(state, llm=llm_instance))

    # Add edges
    workflow.add_edge(START, "download_image")
    
    # 이미지 다운로드 후 병렬 처리
    # 1. 이미지 저장 브랜치
    workflow.add_edge("download_image", "save_image_branch")
    # 이미지 저장 브랜치는 결과를 반환하지만 메인 워크플로우에 영향을 주지 않음
    
    # 2. 메인 경로: 다운로드 → 이미지 설명
    workflow.add_conditional_edges(
        "download_image",
        decide_to_generate,
        {
            "end": END,
            "generate": "describe_image"
        }
    )

    workflow.add_edge("describe_image", "check_image_description")

    workflow.add_conditional_edges(
        "check_image_description",
        decide_to_regenerate,
        {
            "nextstep": END,
            "regenerate": "describe_image",
            "end": END
        }
    )

    # Compile
    graph = workflow.compile()

    return graph

이 코드는 이미지 다운로드, 저장, 분석, 검증의 전체 워크플로우를 정의하고 있습니다. 특히 병렬 처리를 통해 이미지 처리 효율성을 높이고, 조건부 엣지를 통해 처리 결과에 따라 다른 경로로 진행하는 흐름 제어를 구현했습니다.

기술적 제약사항 및 해결 방안

토큰 수 제한을 고려했을 때, 구조도를 직접 전송하는 멀티모달 방식은 OpenAI 모델 사용에 제약이 있다고 판단했다. 이에 따라 RPM(Request Per Minute) 제한을 고려한 시스템 구조를 다음과 같이 설계했다:

  1. 구조도 다운로드
  2. 병렬 처리:
    • 구조도 로컬 저장
    • 구조도에서 정보 추출
  3. LLM을 활용한 추출 정보 검증
  4. 검증 결과에 따른 반복 처리 여부 결정

방 구조도 이미지 분석 시 정확도를 높이기 위해 검증 단계를 추가했는데, 이는 GitHub의 edges.py 파일에서 다음과 같이 구현되어 있습니다:

def decide_to_regenerate(state: GraphState) -> Literal["nextstep", "regenerate", "end"]:
    """
    Determines whether to regenerate an answer, proceed to next step, or end the workflow.

    Args:
        state (GraphState): The current graph state

    Returns:
        str: Decision for next node to call
    """
    # 현재 regenerate_count 출력 (디버깅용)
    current_count = state.get('regenerate_count', 0)
    logger.info(f"현재 재생성 횟수: {current_count}")
    
    # 재시도 최대 횟수 정의
    max_regenerations = 3
    
    # 최대 재시도 횟수 초과 여부 확인 (어떤 조건에서든 우선 확인)
    if current_count >= max_regenerations:
        logger.error(f"최대 재시도 횟수({max_regenerations})에 도달했습니다. 워크플로우를 종료합니다.")
        return "end"
    
    # 에러 여부 확인
    if state.get('error'):
        logger.warning(f"에러 발생: {state['error']}")
        return "regenerate"
        
    # 검증 결과 확인
    accuracy = state.get('description_accuracy')
    if not accuracy:
        logger.warning("검증 결과가 없습니다. 재생성합니다.")
        return "regenerate"
        
    # 모든 검증 통과 확인
    if (accuracy.num_room_accuracy and 
        accuracy.num_balcony_accuracy and 
        accuracy.num_wc_accuracy and 
        accuracy.description_adequacy):
        logger.info("모든 검증이 통과되었습니다.")
        return "nextstep"
        
    # 검증 실패로 재생성 진행
    logger.warning(f"검증 실패. 재생성합니다. (시도 {current_count + 1}/{max_regenerations})")
    return "regenerate"

이 코드는 방 구조 분석 결과의 정확도를 검증하고, 필요에 따라 재분석을 결정하는 로직입니다. 최대 3번까지 재시도를 허용하며, 방의 개수, 발코니 수, 화장실 수, 그리고 전반적인 설명의 적절성을 모두 검증합니다.

현재 진행 상황과 과제

구조도 다운로드 과정이 안정적인 API가 아닌 크롤링 방식에 의존하고 있어, 웹사이트에 과도한 부하를 주지 않도록 주의하면서 작업을 진행하고 있다. 또한 응답 속도 문제로 인해 데이터 처리에 상당한 시간이 소요되고 있다.

이미지 다운로드 부분은 GitHub 프로젝트에서 다음과 같이 구현되어 있습니다:

def download_image(img_url, save_path, filename, max_retries=3, timeout=30):
    for attempt in range(max_retries):
        try:
            # SSL 검증 비활성화 및 타임아웃 설정
            response = requests.get(img_url, verify=False, timeout=timeout)
            response.raise_for_status()
            
            os.makedirs(save_path, exist_ok=True)
            file_path = os.path.join(save_path, filename)
            
            with open(file_path, 'wb') as f:
                f.write(response.content)
            return True
            
        except requests.Timeout:
            if attempt < max_retries - 1:
                print(f"타임아웃 발생 ({filename}), {attempt + 1}/{max_retries} 재시도")
                time.sleep(2)  # 타임아웃 발생 시 더 긴 대기
            else:
                print(f"최대 타임아웃 재시도 횟수 초과 ({filename})")
                return False
                
        except Exception as e:
            if attempt < max_retries - 1:
                print(f"다운로드 시도 {attempt + 1}/{max_retries} 실패: {e}")
                time.sleep(1)
            else:
                print(f"최대 재시도 횟수 초과 ({filename}): {e}")
                return False

def download_images_batch(df, batch_size=2, save_path='downloaded_images', max_retries=3, 
                         timeout=30, delay_between_batches=3):
    downloaded_paths = []
    
    # 이미지 다운로드
    for i in tqdm(range(0, df.shape[0], batch_size), desc="이미지 다운로드 중"):
        batch = df.loc[i:i + batch_size, :]
        
        for row in batch.itertuples():
            img_url = row.img
            if img_url:
                filename = f"{row.번호}.jpg"
                if download_image(img_url, save_path, filename, max_retries=max_retries, timeout=timeout):
                    downloaded_paths.append(os.path.join(save_path, filename))
                else:
                    print(f"이미지 다운로드 실패: {filename}")
        
        # 배치 처리 후 대기
        time.sleep(delay_between_batches)
    
    return downloaded_paths

이 코드는 이미지 다운로드를 배치 단위로 처리하며, 재시도 로직과 타임아웃 처리를 포함하여 견고한 다운로드 프로세스를 구현했습니다.

최근 대상 매물 수가 100개에서 500개로 크게 증가함에 따라, 현재는 코드 정리와 테스트에 집중하고 있으며, 전체 시스템의 스케일링 방안을 검토 중이다.

지하철 역 거리 및 통근시간 계산 기능

GitHub 프로젝트에서는 카카오 API를 활용하여 각 매물과 가장 가까운 지하철역까지의 거리와 예상 통근 시간을 계산하는 기능도 구현되어 있습니다:

def find_near_subway_station(x, y, max_distance = 3000):
    time.sleep(0.2)
    results = kakaomap.search_by_category('SW8', y, x, 3000) # 위경도 바꿔어서 입력
    if len(results.get('documents'))!=0:
        near_result = results.get('documents')[0]
        return near_result.get('distance'), near_result.get('place_name')
    else:
        print('no result')
def calculate_transit_time(self, origin_y, origin_x, dest_y, dest_x):
    time.sleep(0.2)
    url = "https://apis-navi.kakaomobility.com/v1/directions"
    headers = {"Authorization": f"KakaoAK {self.api_token}"}
    params = {
        "origin": f"{origin_y},{origin_x}",
        "destination": f"{dest_y},{dest_x}",
        "priority": "RECOMMEND",
        "car_fuel": "GASOLINE",
        "car_hipass": True,
        "alternatives": False,
        "road_details": False,
        "roadevent":2
    }
    
    try:
        response = requests.get(url, headers=headers, params=params)
        if response.status_code == 200:
            result = response.json()
            return result['routes'][0]['summary']['duration'] / 60, result['routes'][0]['summary']['distance']
        return None
    except:
        return None

이 코드들은 카카오맵 API를 활용하여 지리적 정보를 풍부하게 하는 기능을 구현했습니다. 각 매물로부터 가장 가까운 지하철역을 찾고, 특정 목적지(예: 회사)까지의 예상 통근 시간을 계산하여 사용자가 주택 선택 시 중요한 정보를 제공합니다.

다음 단계

향후 계획은 다음과 같다:

  1. 크롤링 프로세스 최적화를 통한 데이터 수집 시간 단축
  2. LLM 기반 정보 추출의 정확도 향상을 위한 프롬프트 엔지니어링
  3. 대량 데이터 처리를 위한 분산 처리 시스템 구축 검토
  4. 시각화 대시보드의 사용자 인터페이스 개선

이번 연구를 통해 LLM과 데이터 파이프라인을 결합한 효율적인 정보 추출 및 시각화 시스템의 가능성을 확인할 수 있었으며, 추후 더 많은 도메인에 적용할 수 있는 방안을 모색할 예정이다.

2024년 3월 25일 - Claude GitHub 연동 및 GPT 업데이트

Claude에 GitHub 연동 기능이 추가되었다. 회사에서는 데스크톱 앱 사용이 제한적이라 MCP(Multi-model Capability Protocol) 활용에 어려움이 있었는데, 이제 최신 코드 기반으로 생성 요청할 때 각 패키지의 최신 리포지토리를 포크한 후 관련 코드를 컨텍스트로 제공하니 구버전 코드로 응답하는 경우가 확실히 줄어들었다.

 

또한 OpenAI에서는 사용 공유 시 4월 10일까지 일부 모델을 무료로 사용할 수 있는 옵션이 제공되었다. 구체적으로:

  • 25만 토큰의 고급 모델
  • 2.5M의 GPT-4o-mini, o1-mini, o3-mini 모델

이 시기에 맞춰 MCP 관련 새로운 업데이트가 출시될 가능성이 있어 기대된다.

2024년 3월 26일 - MCP 연동 실습 및 학습

오늘은 Cursor를 통해 MCP 연동 실습을 진행했다. 현재 MCP 활용 방식은 크게 두 가지로 나뉘는 것 같다:

  1. Smithery와 같은 마켓플레이스에 등록된 툴을 연결해 사용하는 방법
  2. 로컬에 직접 서버를 열어 사용하는 방법

MCP 개발은 fastmcp와 같은 패키지를 활용하는 것으로 보인다. MCP 연동 시 사용하는 JSON 구조를 파악하여 다른 개발자들이 공유한 다양한 도구를 사용할 수 있는 수준까지 도달했다.

hwp-mcp와 naver-mcp를 직접 사용해보며 기본적인 이해는 갖추었지만, 몇가지 툴들은 아직 사용하기에는 무리가 있는득 하다. 앞으로는 RAG(Retrieval-Augmented Generation) 파이프라인을 MCP로 연결하는 과정을 더 깊이 파악할 계획이다.

2024년 3월 27일 - TMAP REST API 활용 및 모듈 구축

오늘은 TMAP REST API를 활용하여 새로운 프로젝트를 시작했다. 이 프로젝트는 크게 세 가지 부분으로 구성된다:

  1. 모듈 개발
  2. LLM(Large Language Model)에 사용할 tool 구현
  3. MCP 서버 구축

현재는 첫 번째 단계인 모듈 개발까지 완료했다. TMAP의 REST API를 활용하여 위치 기반 정보를 효율적으로 처리하고 활용할 수 있는 기본 모듈을 구축했다. 이 모듈은 향후 LLM에 연결할 tool과 MCP 서버 구축의 기반이 될 예정이다.

이 모듈을 통해 위치 정보, 경로 안내, POI(Point of Interest) 검색 등 TMAP API가 제공하는 다양한 기능들을 프로그래매틱하게 활용할 수 있게 되었다. 다음 단계에서는 이 모듈을 기반으로 LLM이 활용할 수 있는 tool을 개발하고, 최종적으로는 MCP 서버로 통합할 계획이다.

멀티모달 기능과 비용 고려사항

docling이나 ocr 사용시 일부 그래프의 ocr이 잘안되어 멀티모달 AI 기능을 활용해 이미지 분석으로 해당 차트들을 처리해보고 있다. 진행하면서 토큰 소비량이 역시 텍스트 분석에 비해 토큰 소비량이 매우 크게 증가하는게 확인되었다. 텍스트 분석만 할 때는 주로 API 호출 횟수가 비용의 주요 요소였지만, 이미지와 같은 멀티모달 요소를 추가하면 토큰 사용량이 급격히 증가한다.

이는 이미지를 처리하는 과정에서 필요한 추가적인 컴퓨팅 리소스와 이미지를 토큰화하는 과정에서 발생하는 비용이 반영된 결과이다. 멀티모달 AI 프로젝트를 진행할 때는 이러한 비용 증가 요소를 사전에 고려하여 예산 계획을 세워야 할 것으로 보인다.

요약 및 향후 계획

현재까지 MCP의 기본 구조와 활용 방식에 대한 이해를 갖추게 되었고, TMAP REST API를 활용한 모듈 개발까지 완료했다. 앞으로는:

  1. TMAP API 기반 LLM 툴 개발 
  2. MCP 서버 구축 및 통합
  3. RAG 파이프라인과 MCP의 연동 방식 심화 학습
  4. 멀티모달 AI 활용 시 효율적인 토큰 사용 방안 연구

GitHub 연동과 같은 최신 기능들과 TMAP API와 같은 실용적인 서비스를 결합하여 더 효율적이고 실용적인 AI 개발 환경을 구축해 나갈 계획이다.

+ Recent posts