LangConnect Client: 벡터 DB 관리를 위한 직관적인 GUI 인터페이스

  • 카카오톡 공유하기
  • 네이버 블로그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 트위터 공유하기
  • 링크 복사하기

지난번 LangConnect 에 대해서 포스트하였습니다. 최근 유튜브에서 TeddyNote 님이 공개한 영상에 언급한 LangConnect 에 GUI를 추가한 Client에 대한 영상 소개가 있어서 보자마자 바로 설치 및 사용을 해봤습니다.

<TeddyNote 유튜버의 LangConnect Client 소개 영상>

LangConnect Client는 PostgreSQL과 pgvector 확장을 기반으로 한 벡터 데이터베이스를 관리하기 위한 직관적인 웹 인터페이스를 제공합니다. 이 Next.js 기반 GUI 도구는 문서 관리, 벡터 검색 기능, 그리고 Model Context Protocol(MCP)을 통한 AI 어시스턴트와의 원활한 통합을 지원합니다.

https://github.com/teddynote-lab/langconnect-client

주요 기능

컬렉션 관리

  • 커스텀 메타데이터 지원을 통한 CRUD 작업
  • 실시간 통계 및 대량 작업 지원

문서 관리

  • 다양한 형식 지원 (PDF, TXT, MD, DOCX, HTML)
  • 자동 텍스트 추출 및 청킹
  • 드래그 앤 드롭 배치 업로드
  • Metadata를 JSON 구조로 업데이트 가능

고급 검색 기능

  • 시맨틱 검색: OpenAI 임베딩을 활용한 벡터 유사성 검색
  • 키워드 검색: PostgreSQL 전체 텍스트 검색
  • 하이브리드 검색: 구성 가능한 가중치로 결합된 검색

인증 시스템

  • 자동 토큰 갱신 기능이 있는 Supabase JWT 인증
  • 역할 기반 접근 제어
  • NextAuth.js를 통한 안전한 리프레시 토큰 관리

MCP 통합

  • Claude, Cursor 등 AI 어시스턴트를 위한 9개 이상의 도구
  • stdio 및 SSE 전송 지원

직관적 UI

  • Tailwind CSS를 활용한 Next.js
  • streamlit
  • 다크/라이트 테마, 다국어 지원(영어/한국어)

아키텍처

인증 흐름

인증 시스템은 안전한 토큰 갱신 메커니즘을 구현합니다:

<출처: Lang Connect Client 아키텍처 https://github.com/teddynote-lab/langconnect-client>


주요 보안 기능:

  • 리프레시 토큰이 클라이언트에 노출되지 않음
  • 액세스 토큰 만료 시 자동 토큰 갱신
  • 향상된 보안을 위한 각 갱신 시 토큰 회전
  • httpOnly 쿠키에 암호화된 JWT 저장

시작하기

빠른 시작

MCP 구성 생성

서비스 중지

사전 요구 사항

  • Docker 및 Docker Compose
  • Node.js 20+ (MCP 인스펙터용)
  • Python 3.11+ (UV 패키지 매니저 포함)
  • Supabase 계정

설치

1. 저장소 클론

2. 환경 변수 설정

3. Supabase 구성

a. supabase.com에서 새 프로젝트 생성

b. API 자격 증명 가져오기:

  • 프로젝트 설정 → API로 이동
  • URL 및 anon public key 복사

c. .env 파일 업데이트:

4. 애플리케이션 빌드

애플리케이션 실행

모든 서비스 시작

서비스 접근

  • 🎨 프론트엔드: http://localhost:3000
  • 📚 API 문서: http://localhost:8080/docs
  • 🔍 상태 확인: http://localhost:8080/health

서비스 중지

로그 보기

MCP 통합

자동 설정

MCP 구성 생성:

이 명령은 다음을 수행합니다:

  • Supabase 자격 증명 입력 요청
  • 액세스 토큰 자동 획득
  • 토큰으로 .env 업데이트
  • mcpserver/mcp_config.json 생성
<make mcp 실행후, supabase에 생성했던 이메일과 패스워드를 입력하여 관련 파일을 생성해냅니다>

MCP SSE 실행

만약 Stdio가 아니라, SSE 로 실행하고자 하면, 아래의 명령을 실행합니다.

mcp server를 sse로 실행할 경우, 인증 후 세션이 1시간으로 제한되는 문제가 있습니다. 어플리케이션으로 Token만료를 체크하고 갱신하도록 하기 위해서는 별도의 개발이 필요한 상황입니다. 이 부분을 해결하기 위해 부득이 n8n의 도움을 받았습니다.

n8n에서 아래와 같이 MCP Server를 생성하고, MCP Server용 sse 경로를 생성하고, workflow를 통해서 인증 여부를 체크한 다음 accessToken이 없을 경우, 인증을 있을 경우, API를 통해 문서를 요청하도록 추가하였습니다.

<n8n을 통해 MCP Server를 생성 인증하고, LangConnect API를 연결>

위 설정에 따른 json 파일은 아래와 같습니다. (n8n 사용시 참고 바랍니다.)

{
  "name": "LangConnect-RAG",
  "nodes": [
    {
      "parameters": {
        "workflowInputs": {
          "values": [
            {
              "name": "query"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "typeVersion": 1.1,
      "position": [
        0,
        0
      ],
      "id": "2e7b72a6-6ed2-420d-acbf-a1d2e35718e9",
      "name": "When Executed by Another Workflow"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://langConnect-API-Server/collections/38b03cad-7589-421c-9e1f-b05e569d2e3a/documents/search",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "collection_id",
              "value": "38b03cad-7589-421c-9e1f-b05e569d2e3a"
            }
          ]
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.accessToken }}"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "query",
              "value": "={{ $('When Executed by Another Workflow').item.json.query }}"
            },
            {
              "name": "limit",
              "value": "10"
            },
            {
              "name": "search_type",
              "value": "hybrid"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        700,
        -160
      ],
      "id": "b9729fae-8e48-4d35-aac5-b3585336e1a0",
      "name": "HTTP Request"
    },
    {
      "parameters": {
        "path": "f48bc9f6-c957-481e-bea6-ab87e4a9ffa5"
      },
      "type": "@n8n/n8n-nodes-langchain.mcpTrigger",
      "typeVersion": 2,
      "position": [
        -420,
        -160
      ],
      "id": "82606c12-d711-4327-a21e-5df67fbbd045",
      "name": "MCP Server Trigger",
      "webhookId": "f48bc9f6-c957-481e-bea6-ab87e4a9ffa5"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "001a843a-c350-49ed-9cd3-39c8af2c96e4",
              "name": "accessToken",
              "value": "={{ $json.access_token }}",
              "type": "string"
            },
            {
              "id": "8c0a5bf5-9709-4e73-96e0-0eee34bc0511",
              "name": "query",
              "value": "={{ $json.query }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        220,
        0
      ],
      "id": "75a56dea-f775-404d-bd62-ac3747a1f622",
      "name": "Edit Fields"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "8bff8f31-0680-41b0-8279-33d33daeeb53",
              "leftValue": "={{ $json.accessToken }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        440,
        0
      ],
      "id": "479e5f3b-6270-4ec7-8463-4f3b1b10cf80",
      "name": "If"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://langConnect-API-Server/auth/signin",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "email",
              "value": "(supabase ID)"
            },
            {
              "name": "password",
              "value": "(supabase password)"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        660,
        100
      ],
      "id": "39a92bf0-b8b3-4b33-9a45-5255d37b81cf",
      "name": "Auth"
    },
    {
      "parameters": {
        "description": "RAG에 대한 정보를 제공하는 RAG 시스템인 도구입니다.",
        "workflowId": {
          "__rl": true,
          "value": "agbWAy2l1Ui5ydaR",
          "mode": "list",
          "cachedResultName": "LangConnect-RAG"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {},
          "matchingColumns": [],
          "schema": [
            {
              "id": "query",
              "displayName": "query",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.2,
      "position": [
        -340,
        60
      ],
      "id": "3ee9120c-b447-4338-89d3-764aae3a334e",
      "name": "LangConnect-RAG"
    }
  ],
  "pinData": {},
  "connections": {
    "When Executed by Another Workflow": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        []
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Auth",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Auth": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LangConnect-RAG": {
      "ai_tool": [
        [
          {
            "node": "MCP Server Trigger",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "e957d4f8-599f-480b-b80f-fc7b07d380ad",
  "meta": {
    "instanceId": "c800c01c11791c282fbf19f9904400fc4b5efa68352e5e18fe37e9d569fc00f5"
  },
  "id": "agbWAy2l1Ui5ydaR",
  "tags": []
}

AI 어시스턴트와의 통합

Claude Desktop용:

  • mcpserver/mcp_config.json의 내용 복사
  • Claude Desktop의 MCP 설정에 붙여넣기

Cursor용:

  • MCP 구성 복사
  • Cursor 설정의 MCP 통합에 추가

사용 가능한 MCP 도구

  • search_documents – 시맨틱/키워드/하이브리드 검색 수행
  • list_collections – 모든 컬렉션 나열
  • get_collection – 컬렉션 세부 정보 가져오기
  • create_collection – 새 컬렉션 생성
  • delete_collection – 컬렉션 삭제
  • list_documents – 컬렉션의 문서 나열
  • add_documents – 텍스트 문서 추가
  • delete_document – 문서 삭제
  • get_health_status – API 상태 확인
  • multi_query – 단일 질문에서 여러 검색 쿼리 생성

환경 변수

LangConnect Client를 설정하기 위한 주요 환경 변수:

  • OPENAI_API_KEY: 임베딩을 위한 OpenAI API 키 (필수)
  • SUPABASE_URL: Supabase 프로젝트 URL (필수)
  • SUPABASE_KEY: Supabase anon public key (필수)
  • NEXTAUTH_SECRET: NextAuth.js 비밀 키 (필수)
  • NEXTAUTH_URL: NextAuth URL (기본값: http://localhost:3000) (필수)
  • NEXT_PUBLIC_API_URL: 프론트엔드용 공개 API URL (필수)
  • POSTGRES_HOST: PostgreSQL 호스트 (기본값: postgres)
  • POSTGRES_PORT: PostgreSQL 포트 (기본값: 5432)
  • POSTGRES_USER: PostgreSQL 사용자 (기본값: teddynote)
  • POSTGRES_PASSWORD: PostgreSQL 비밀번호
  • POSTGRES_DB: PostgreSQL 데이터베이스 이름
  • SSE_PORT: MCP SSE 서버 포트 (기본값: 8765)

결론

LangConnect Client는 벡터 데이터베이스 관리와 AI 어시스턴트 통합을 위한 강력하고 직관적인 솔루션을 제공합니다. GUI, 고급 검색 기능, 그리고 MCP 통합을 통해 문서 관리와 RAG(Retrieval-Augmented Generation) 시스템 구축을 간소화합니다. 오픈 소스 프로젝트로서, TeddyNote LAB에서 개발한 이 도구는 AI 기반 애플리케이션 개발자와 데이터 관리자에게 빠른 RAG 시스템 빌드를 도와줄수 있습니다.

다만, MCP 연결하는 측면에서 사전 Instruction 설정하는 방법은 다소 복잡해보이는데, Claude 연결을 위해 부득이 그렇게 진행된 것으로 보입니다. 아마도 괜찮은 MCP Client와 연결된다거나, LangConnect Client 자체에 MCP Client 기능을 구현하고 Instrcution이나 MCP연결을 자유롭게 해줄수 있다면, 더욱 활용성이 높지 않을까 생각해봅니다.

최근 개인적으로 RAG에 관심이 많아서 이것저것 해보면서 우연히 찾은 어플리케이션인데, 설치도 쉽고, UI도 예뻐서 간단하게 이용해보았습니다. 이후에도 좀더 개선되는 모습 기대해볼만 합니다.

관련 링크

LangConnect: LangChain 기반의 효율적인 RAG 서비스


게시됨

카테고리

,

작성자

댓글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다