AI Engineering Academy · 강의

Redis와 PostgreSQL에 채팅 기록 저장하기

RedisChatMessageHistory와 PostgresChatMessageHistory를 사용해 대화 기록을 외부에 저장함으로써 애플리케이션을 다시 시작해도 메모리가 유지되고 여러 인스턴스로 확장되도록 합니다.

레슨 4/413개 단계

Redis와 PostgreSQL에 채팅 기록 저장하기은(는) CoddyKit의 무료 AI Engineering Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Engineering Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

대화 기록을 외부에 저장해야 하는 이유

메모리에 저장된 대화 기록은 애플리케이션이 다시 시작되거나 여러 인스턴스로 확장될 때 사라집니다. 외부 저장은 세 가지 문제를 해결합니다. 기록이 충돌 후에도 유지되고, 여러 서버 인스턴스가 같은 기록을 공유하며, 재방문 사용자에게 이전 세션을 불러올 수 있습니다. Redis와 PostgreSQL은 이를 위한 가장 널리 사용되는 두 백엔드입니다.

LangChain 대화 메시지 기록 인터페이스

LangChain은 BaseChatMessageHistory 인터페이스를 정의하며, 여기에는 add_message(message)와 messages(속성)라는 두 가지 메서드가 있습니다. 이 인터페이스를 구현하는 모든 저장소 백엔드는 LangChain의 모든 체인에서 메모리 저장소로 바로 사용할 수 있습니다. Redis와 PostgreSQL 구현은 모두 이 인터페이스를 따릅니다.

from langchain_community.chat_message_histories import RedisChatMessageHistory

# Each unique session_id gets its own history namespace
history = RedisChatMessageHistory(
    session_id='user:alice:session:42',
    url='redis://localhost:6379'
)

# Add messages
history.add_user_message('What is RAG?')
history.add_ai_message('RAG stands for Retrieval-Augmented Generation...')

# Retrieve stored messages
for msg in history.messages:
    print(msg.type, ':', msg.content[:50])

Redis 기록을 체인에 연결하기

체인을 RunnableWithMessageHistory로 감싸고, 각 세션에 맞는 기록 객체를 반환하는 팩터리 함수를 제공하십시오. LangChain은 각 호출 전에 기록을 자동으로 불러오고, 각 응답 후에 새 메시지를 저장합니다.

from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

llm = ChatOpenAI(model='gpt-4o-mini')
prompt = ChatPromptTemplate.from_messages([
    ('system', 'You are a helpful assistant.'),
    MessagesPlaceholder(variable_name='history'),
    ('human', '{input}')
])

chain = prompt | llm

chain_with_history = RunnableWithMessageHistory(
    chain,
    lambda session_id: RedisChatMessageHistory(
        session_id=session_id,
        url='redis://localhost:6379'
    ),
    input_messages_key='input',
    history_messages_key='history'
)

세션 ID로 체인 호출하기

메시지 기록으로 감싼 체인을 호출할 때는 config 딕셔너리의 configurable 아래에 세션 ID를 전달하십시오. LangChain은 각 호출을 올바른 기록 저장소로 자동 라우팅하므로 Alice와 Bob의 대화 기록은 완전히 분리됩니다.

# First turn for session alice-001
response = chain_with_history.invoke(
    {'input': 'My name is Alice.'},
    config={'configurable': {'session_id': 'alice-001'}}
)
print(response.content)

# Second turn — the chain remembers Alice's name from Redis
response = chain_with_history.invoke(
    {'input': 'What is my name?'},
    config={'configurable': {'session_id': 'alice-001'}}
)
print(response.content)  # Should say 'Your name is Alice.'

대화 기록을 위한 Redis 키 설계

Redis는 각 세션의 메시지를 세션 ID에서 파생된 키 아래에 목록으로 저장합니다. 애플리케이션 간 키 충돌을 방지하고 사용자별로 효율적으로 검색하려면 chat:<app>:<user_id>:<session_id>와 같은 구조화된 키 스키마를 사용하십시오. 오래된 세션 키에는 TTL을 설정하여 저장 공간이 무제한으로 늘어나는 것을 방지하십시오.

import redis

r = redis.Redis.from_url('redis://localhost:6379')

# List all chat sessions for user alice
keys = r.keys('message_store:alice-*')
print('Active sessions:', len(keys))

# Set a 30-day TTL on a session to auto-expire old history
for key in keys:
    r.expire(key, 60 * 60 * 24 * 30)

PostgreSQL 대화 기록 테이블 설계

SQL 기반 영속성을 사용하려면 세션 ID, 메시지 역할, 내용 및 타임스탬프를 저장할 열이 있는 테이블을 생성하십시오. 대화 기록을 조회하거나 감사하거나 사용자 계정 데이터와 조인해야 한다면 PostgreSQL이 적합합니다. langchain_postgres 패키지는 바로 사용할 수 있는 구현을 제공합니다.

-- SQL table for storing chat messages
CREATE TABLE chat_messages (
    id          SERIAL PRIMARY KEY,
    session_id  TEXT NOT NULL,
    role        TEXT NOT NULL,   -- 'human' or 'ai'
    content     TEXT NOT NULL,
    created_at  TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_chat_session ON chat_messages (session_id, created_at);

LangChain의 PostgresChatMessageHistory

langchain_postgres의 PostgresChatMessageHistory 클래스는 Redis 버전과 거의 동일하게 작동하지만 메시지를 PostgreSQL 데이터베이스에 저장합니다. 연결 문자열과 세션 ID를 전달하면 테이블 생성과 메시지 직렬화를 자동으로 처리합니다.

from langchain_postgres import PostgresChatMessageHistory
import psycopg

CONNECTION_STRING = 'postgresql://user:pass@localhost:5432/mydb'

# Create history object for a session
history = PostgresChatMessageHistory(
    table_name='chat_messages',
    session_id='alice-001',
    connection=psycopg.connect(CONNECTION_STRING)
)

history.add_user_message('Tell me about embeddings.')
history.add_ai_message('Embeddings are dense vector representations...')

print(f'Stored {len(history.messages)} messages')

기존 사용자를 위한 이전 세션 불러오기

사용자가 앱을 닫은 후 다시 돌아오면 Redis 또는 PostgreSQL에서 기록을 불러와 마지막 세션을 복원할 수 있습니다. 사용자가 어디까지 진행했는지 요약해서 보여 주거나, 저장된 기록을 다음 프롬프트에 주입하여 중단 없이 계속 진행할 수 있습니다.

def get_or_create_history(user_id: str, session_id: str):
    history = RedisChatMessageHistory(
        session_id=f'{user_id}:{session_id}',
        url='redis://localhost:6379'
    )
    if history.messages:
        print(f'Resuming session with {len(history.messages)} messages.')
    else:
        print('Starting a new session.')
    return history

데이터베이스에서 기록 다듬기

장기간 실행되는 세션에는 수천 개의 메시지가 쌓일 수 있습니다. 기준 시점보다 오래된 메시지를 삭제하거나 데이터베이스에 최근 N개의 메시지만 유지하는 다듬기 전략을 구현하십시오. 이렇게 하면 기록을 불러올 때 검색 속도를 유지하고 컨텍스트 창이 넘치는 것을 방지할 수 있습니다.

# Keep only the last 20 messages per session in PostgreSQL
CLEAN_SQL = '''
    DELETE FROM chat_messages
    WHERE session_id = %s
    AND id NOT IN (
        SELECT id FROM chat_messages
        WHERE session_id = %s
        ORDER BY created_at DESC
        LIMIT 20
    )
'''

def trim_session(conn, session_id: str, keep_last: int = 20):
    with conn.cursor() as cur:
        cur.execute(CLEAN_SQL, (session_id, session_id))
    conn.commit()

Redis와 PostgreSQL 중 선택하기

두 백엔드 모두 운영 환경에 사용할 수 있지만 강점이 서로 다릅니다. Redis는 1밀리초 미만의 읽기 속도를 제공하므로 지연 시간이 가장 중요한 실시간 대화에 적합합니다. PostgreSQL은 완전한 SQL 조회 기능, ACID 트랜잭션, 그리고 분석 및 감사 목적으로 기존 사용자 데이터베이스와 쉽게 통합할 수 있는 기능을 제공합니다. 많은 운영 시스템에서는 Redis를 기본 캐시로, PostgreSQL을 영구 보관용 저장소로 사용합니다.

보안: 다중 테넌트 기록 격리

다중 테넌트 애플리케이션에서는 사용자가 서로의 기록에 접근할 수 없도록 해야 합니다. 항상 세션 키에 사용자 ID를 포함하고, 기록을 불러오기 전에 해당 세션 ID가 인증된 사용자에게 속하는지 확인하며, 클라이언트 측 URL이나 쿠키에 원시 세션 키를 절대 노출하지 마십시오.

  • 좋은 키: chat:app1:user:42:session:abc
  • 나쁜 키: session:abc(추측할 수 있고 사용자 범위가 없음)

빠른 확인

대화 기록을 외부에 영속화하는 방법에 대한 이해도를 확인해 보십시오.

레슨 요약

이 레슨에서는 다음을 배웠습니다. RedisChatMessageHistory는 세션별 키를 사용하여 Redis에 대화 기록을 저장합니다. PostgresChatMessageHistory는 SQL로 조회할 수 있는 영구 저장소를 제공합니다. 또한 RunnableWithMessageHistory는 session_id 팩터리를 통해 모든 백엔드를 체인에 연결합니다. 다음으로는 추론 및 행동 에이전트를 구축하기 위한 ReAct 프레임워크를 살펴봅니다.

무료로 시작

AI 튜터와 함께 Python을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
30
레슨
120

자주 묻는 질문

“Redis와 PostgreSQL에 채팅 기록 저장하기” 강의는 무료인가요?

네 — “Redis와 PostgreSQL에 채팅 기록 저장하기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Engineering Academy 강의 전체를 잠금 해제할 수 있습니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“Redis와 PostgreSQL에 채팅 기록 저장하기”에서 뭘 배우나요?

RedisChatMessageHistory와 PostgresChatMessageHistory를 사용해 대화 기록을 외부에 저장함으로써 애플리케이션을 다시 시작해도 메모리가 유지되고 여러 인스턴스로 확장되도록 합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Engineering Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

AI Engineering Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 AI Engineering Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“Redis와 PostgreSQL에 채팅 기록 저장하기” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 AI Engineering Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 AI Engineering Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 상태 비저장 LLMs에 외부 메모리가 필요한 이유
  2. 버퍼 및 윈도우 메모리
  3. 요약 메모리와 토큰 인식형 잘라내기
  4. Redis와 PostgreSQL에 채팅 기록 저장하기
← AI Engineering Academy(으)로 돌아가기