[LangChain.js] 1. React +LangChain 환경설정, 기본 예제

LangChain.js 카테고리에 포함된 포스트들에서는 React/JS 를 이용해서 LangChain 을 구현하는 예제들을 포함하고 있습니다.

여기서는 LangChain 의 javascript 구현체를 기반으로 내용이 구성되어 있습니다. LangChain 아키텍처 자체에 대해서는 python 을 기반으로 한 강좌에서 더욱 자세히 다루고 있습니다. 아래 링크의 강좌를 참고하세요.

이번 포스트에서는 React + LangChain 예제를 테스트하기 위한 개발환경을 설정하고, 가장 기본적인 번역 예제를 실행합니다.



LangChain 소개

LangChain은 LLM 기반 애플리케이션 개발을 단순화하는 프레임워크로, LLM 개발→프로덕션화→배포 전 과정을 지원합니다.

  • 핵심 기능:
    1. LangGraph.js로 상태 유지 에이전트 및 실시간 스트리밍 시스템 구축
    2. LangSmith를 통한 체인 모니터링·최적화·배포
    3. LangGraph Cloud로 프로덕션 환경 API 변환
  • 주요 구성 요소:
    • 코어 라이브러리(@langchain/core)
    • 타사 통합(@langchain/community)
    • LLM 전용 패키지(OpenAI·Anthropic 등)
    • LangGraph.js: 그래프 기반 멀티 액터 애플리케이션 개발
    • LangSmith: 애플리케이션 성능 관리 및 평가 플랫폼



React + LangChain 개발환경 설정

React 개발환경 설정 방법은 웹 검색으로 쉽게 찾을 수 있습니다. 아래 항목을 확인하고 설치하면 됩니다.

npm install eslint prettier eslint-plugin-prettier eslint-config-prettier eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/parser @typescript-eslint/eslint-plugin --save-dev


예제소스는 github 에서 다운로드 받을 수 있습니다.




한글 -> 영어 번역 예제 실행

이번 포스트에서는 사용자가 한글로 입력한 문장을 영어로 번역해주는 간단한 예제를 실행할 예정입니다. github 에서 다운로드 받은 예제 중 01_react_basic_chat 폴더를 사용합니다.

이 예제를 실행하기 위해 필요한 패키지들은 package.json 에서 확인할 수 있습니다.

{
  "name": "langchainjs_test01",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite --port 3030",
    "build": "tsc -b && vite build",
    "lint": "eslint .",
    "preview": "vite preview",
    "start": "vite --port 3030",
    "start:debug": "vite --debug --port 3030"
  },
  "dependencies": {
    "@langchain/core": "^0.3.28",
    "@langchain/openai": "^0.3.16",
    "langchain": "^0.3.11",
    "react": "^18.3.1",
    "react-dom": "^18.3.1"
  },
  "devDependencies": {
    "@eslint/js": "^9.17.0",
    "@types/react": "^18.3.18",
    "@types/react-dom": "^18.3.5",
    "@typescript-eslint/eslint-plugin": "^8.19.1",
    "@typescript-eslint/parser": "^8.19.1",
    "@vitejs/plugin-react": "^4.3.4",
    "eslint": "^9.18.0",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-prettier": "^5.2.1",
    "eslint-plugin-react": "^7.37.3",
    "eslint-plugin-react-hooks": "^5.1.0",
    "eslint-plugin-react-refresh": "^0.4.16",
    "globals": "^15.14.0",
    "prettier": "^3.4.2",
    "typescript": "~5.6.2",
    "typescript-eslint": "^8.18.2",
    "vite": "^6.0.5"
  }
}

dependencies 항목을 보면 langchain 이 정의되어 있는 것을 알 수 있습니다. 기타 실행에 필요한 패키지들 설치를 위해 아래 커매드를 실행합니다.

npm install


이 예제는 하나의 화면에 사용자 입력창과 출력창을 표시합니다. 입력창에 번역하고 싶은 문장을 넣으면 출력창에 영어로 번역한 결과를 표시해줍니다.

소스코드의 구조와 주요 파일의 역할은 아래와 같습니다.

+ 01_react_basic_chat
  + src/
    + ai/
      - chatService.ts   // LLM service 에 연결해서 특정한 기능을 수행하는 함수들 모음
    + assets/
    - App.css
    - App.tsx      // Chat 화면 구성, 사용자 입력 처리
    ......
  - .env           // LLM service 연결을 위해 필요한 설정값을 담는 설정 파일
  - package.json   // 프로젝트 및 패키지 설정 파일


위 예제는 번역 기능을 수행하기 위해 LLM 서비스의 API 를 사용합니다. LLM 서비스는 다양한 모델들을 제공하며, 모델마다 성능과 사용요금이 다릅니다. 예제에서는 OpenAI 에서 제공하는 모델 중 GPT 4o-mini 모델을 사용합니다.

이를 위해 프로젝트 폴더 아래에 .env 파일을 생성하고 본인의 API Key 를 입력해야 합니다.

VITE_OPENAI_API_KEY=<your_open_ai_api_key>


아직 Open AI 에서 API Key 를 발급받지 않았다면 아래 링크의 가이드를 따라 발급받으면 됩니다.


다른 LLM 서비스가 제공하는 API 를 사용하고 싶다면 유사한 방법으로 API key 를 발급받아 사용할 수 있지만 일부 코드가 수정되어야 합니다.

.env 파일을 생성하고 API key 를 입력했다면 아래 커맨드를 react 서비스를 실행합니다

$ npm run start

  VITE v6.0.7  ready in 378 ms

  ➜  Local:   http://localhost:3030/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

브라우저를 열어 출력된 http://localhost:3030/ 경로에 접속해서 화면을 확인합니다.

브라우저에 위와 같이 화면이 출력될겁니다. 입력창에 한글로 원하는 문장을 입력하고 Send 버튼을 누르면 잠시 후 결과가 아래에 한번에 표시됩니다. Stream 버튼을 누르면 Chat GPT 같은 서비스처럼 stream 형식으로 출력 문자열이 실시간 업데이트 됩니다.



LangChain 코드 확인

번역 서비스가 수행되는 과정을 살펴보기 위해 화면을 구성하는 App.tsx 파일부터 보겠습니다. 이 파일은 화면을 구성하고 사용자가 입력 후 버튼을 누르면 LLM 서비스를 호출하는 역할을 합니다.

function App() {
  const [userMessage, setUserMessage] = useState('')
  const [chatResponse, setChatResponse] = useState('')

  const handleSendMessage = async () => {
    const response = await getChatResponse(userMessage)
    setChatResponse(response) // Assuming 'text' is the string property of AIMessageChunk
  }

  const handleStreamMessage = async () => {
    setChatResponse('') // Clear previous response
    await streamChatResponse(userMessage, (message) => {
      setChatResponse((prev) => prev + message)
    })
  }

  return (
    <>
      ......

Send 버튼과 Stream 버튼의 액션을 처리하기 위해 2개의 비동기 함수가 마련되어 있습니다. handleSendMessage 와 handleStreamMessage 함수는 버튼을 눌렀을 때 chatService.ts 에 정의된 함수들을 호출합니다.

chatService.ts 에서는 LangChain 을 이용해서 LLM 서비스의 API 를 호출하고 원하는 작업을 실행합니다.

Send 버튼을 눌렀을 때 번역이 수행되는 과정은 아래와 같습니다. (chatService.ts – getChatResponse())

// 1. OpenAI API 키 가져오기
const apiKey = import.meta.env.VITE_OPENAI_API_KEY
if (!apiKey) {
  throw new Error('OpenAI API key is not defined in environment variables')
}

// 2. ChatOpenAI 모델 생성
const model = new ChatOpenAI({ model: 'gpt-4o-mini', apiKey })

/**
 * 사용자 입력 메시지를 ChatOpenAI 모델에 전달하고, 응답 받기
 *
 * @param userMessage   사용자 입력 메시지
 * @returns
 */
export async function getChatResponse(userMessage: string) {
  // 3. Prompt 설정
  const messages = [
    new SystemMessage('Translate the following from Korean into English'),
    new HumanMessage(userMessage),
  ]
  // 3-1. 아래의 두 가지 형식도 가능
  // const simpleMessage = 'Hello, how are you?'
  // const roleMessage = [{ role: 'user', content: simpleMessage }]

  // 4. ChatOpenAI 모델에 메시지 전달 후 응답 받기
  const response = await model.invoke(messages)
  // const response2 = await model.invoke(simpleMessage)
  // const response3 = await model.invoke(roleMessage)
  console.log(response.content)

  // 5. 응답 메시지 반환
  return parseContent(response.content)
}

코드를 순서대로 살펴보면

  1. OpenAI API 키 가져오기
    • .env 파일에 기록해 둔 환경설정 변수 VITE_OPENAI_API_KEY 를 가져옵니다.
  2. ChatOpenAI 모델 생성
    • Open AI 에서 제공하는 API 를 사용할 수 있도록 LangChain 이 제공하는 ChatOpenAI 를 생성합니다. 이때 API key 와 어떤 LLM 모델을 사용할지 입력합니다.
    • 예제에서는 gpt-4o-mini 를 사용합니다. 이 모델이 저렴하고 가성비가 좋기 때문에 테스트 용도로 적합합니다.
  3. 버튼을 눌렀을 때 실행되는 getChatResponse() 함수에서는 먼저 프롬프트(prompt)를 설정합니다.
    • 프롬프트는 LLM 서비스를 사용하기 위해 LLM 에 전달하는 텍스트입니다.
    • 프롬프트는 단순히 사용자의 질문만을 포함하는 것이 아니라 LLM 서비스가 어떤 역할을 맡을지, 어떤 맥락 아래에서 사용자의 질문이 들어오는지 등 LLM 이 질문을 처리하기 전에 알아야 할 다양한 정보들을 기술합니다.
    • 그래서 예제에서는 SystemMessage 와 HumanMessage 를 프롬프트에 설정합니다.
      • SystemMessage : LLM 이 사용자의 질문을 처리하기 전 알아야 할 정보, LLM 이 해야할 역할 등을 기술
      • HumanMessage : 사용자의 질문
    • 보통 LLM 서비스는 채팅 UI 형태로 제공되는데, 이때 앞서 주고 받았던 채팅의 내용 같은 정보도 프롬프트에 포함할 수 있습니다. LangChain 에서 프롬프트를 작성하는 방법이 다양한 것은 이 때문입니다.
  4. 작성된 프롬프트를 미리 생성한 ChatOpenAI 에 전달합니다.
    • model.invoke 로 호출하는 경우 LLM 서비스로부터 응답 전체를 받을 때 까지 blocking 됩니다.
  5. 수신한 응답 메시지에서 화면에 출력할 내용만 추출해서 리턴합니다.


Stream 버튼을 눌렀을 때 호출되는 streamChatResponse() 함수도 위 코드와 유사하게 작동하지만, 실시간으로 조금씩 빠르게 stream 형태로 들어오는 응답을 처리해서 전형적인 ChatGPT 의 대화창 같은 효과를 구현합니다.

/**
 * 사용자 입력 메시지를 ChatOpenAI 모델에 전달하고, 스트림으로 받은 메시지를 처리
 *
 * @param userMessage   사용자 입력 메시지
 * @param onMessage     스트림으로 받은 메시지를 처리하는 콜백 함수
 */
export async function streamChatResponse(
  userMessage: string,
  onMessage: (message: string) => void,
) {
  // 3. Prompt 설정
  const messages = [
    new SystemMessage('Translate the following from Korean into English'),
    new HumanMessage(userMessage),
  ]

  // 4. ChatOpenAI 모델에 메시지 전달 후 스트림으로 응답 받기
  const stream = await model.stream(messages)

  // 5. 스트림으로 받은 메시지 처리
  const chunks = []
  for await (const chunk of stream) {
    const content = parseContent(chunk.content)
    chunks.push(content)
    // 6. 메시지를 callback 함수로 전달
    onMessage(content)
  }
  console.log(chunks)
}
  1. getChatResponse() 와 동일
  2. getChatResponse() 와 동일
  3. getChatResponse() 와 동일
  4. stream 형태로 응답을 받아서 처리하기 위해 model.stream() 을 호출합니다.
  5. stream 에서 chunk 단위로 데이터를 받아서 callback 함수로 전달합니다.
    • callback 함수에서 chunk 데이터를 기존 출력에 붙여서 출력합니다.
    • 이 과정을 반복하면 전형적인 ChatGPT 스타일의 UI 를 구현할 수 있습니다.


LangChain 은 이처럼 다양한 LLM 서비스를 손쉽게 사용할 수 있도록 추상화하고 필요한 툴들을 제공해줍니다. 여기서 프롬프트을 고도화 하기만 해도 다양한 기능/서비스를 구현할 수 있지만, LangChain 은 이보다 더 강력하고 고차원적인 능력들도 포함하고 있습니다.

이어지는 포스트에서는 LangChain 의 기능들을 조금씩 확장해 나갈 것입니다.



참고자료

You may also like...

답글 남기기

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

이 사이트는 Akismet을 사용하여 스팸을 줄입니다. 댓글 데이터가 어떻게 처리되는지 알아보세요.