이 가이드에서는 웹 애플리케이션에서 '세션'이 정확히 무엇을 의미하는지, 세션 관리에 Redis가 선호되는 이유는 무엇인지, Upstash Redis 데이터베이스를 설정하는 방법, 그리고 Next.js 애플리케이션에 이를 통합하는 전체 과정을 단계별로 살펴봅니다.
세션(Session)이란 무엇일까요?
웹 애플리케이션에서 세션은 일정 시간 동안 여러 HTTP 요청에 걸쳐 사용자와 애플리케이션 간의 상호작용 상태를 유지하기 위한 임시적인 서버 측 저장 메커니즘입니다.
HTTP는 상태 비저장(stateless) 프로토콜입니다. 즉, 각 요청은 서로 독립적이며 이전 상호작용을 '기억'하지 못합니다. 세션은 이러한 한계를 극복하여 서버가 인증 상태, 사용자 환경설정, 웹사이트 이용 중 발생하는 다양한 활동 같은 사용자별 데이터를 추적하고 저장할 수 있도록 해줍니다.
세션은 일반적으로 클라이언트 측(대부분 쿠키)에 고유한 세션 ID를 저장하는 방식으로 동작합니다. 서버는 사용자가 사이트와 상호작용할 때마다 이 ID를 통해 세션 스토리지(이 글에서는 Redis)에서 해당 사용자의 세션 데이터를 조회합니다.
웹 애플리케이션의 세션은 다음과 같은 사용자 상호작용과 상태 관리에 효과적으로 활용됩니다:
- 사용자 인증: 세션은 사용자를 인증하고, 페이지를 이동하거나 새로고침한 후에도 로그인 상태를 유지하는 데 도움을 줍니다.
- 개인화: 서버가 사용자 환경설정, 인증 상태 등의 데이터를 저장하고 조회할 수 있습니다.
- 보안: 적절한 세션 관리는 CSRF 공격 방어, 권한 없는 사용자의 제한 영역 접근 차단 등 애플리케이션 보안을 강화할 수 있습니다.
왜 세션 관리에 Redis를 사용할까요?
Redis를 세션 관리용 스토어로 선택해야 할 이유는 여러 가지가 있습니다. Redis 구조 자체에서 비롯되는 핵심 장점들을 살펴보겠습니다:
- 데이터 아키텍처: Redis는 인메모리 키-값(key-value) 저장소입니다. 키-값 쌍은 해시(hash) 형태로 저장할 수 있으며, 해시는 필드-값(field-value) 쌍의 모음으로 구성되는 레코드 타입입니다. 이러한 구조는 사용자 이름, 사용자 설정 등 모든 세션 데이터를 사용자 세션별 해시에 키-값 형식으로 담아두는 세션 관리에 완벽하게 부합합니다.
- 데이터 만료: Redis에는 만료된 데이터를 자동으로 삭제하는 기능이 내장되어 있습니다. 덕분에 웹 애플리케이션은 사용자 세션에 유효 기간을 부여하기가 매우 쉬워집니다.
- 낮은 지연 시간: '인메모리' 특성 덕분에 Redis는 매우 빠릅니다. 그래서 사용자 세션과 캐시 데이터 용도로 널리 사용됩니다.
- 확장성: Redis는 대용량 데이터와 높은 처리량(throughput)의 트래픽을 감당할 수 있도록 설계되었습니다.
또한 사용자 세션 데이터를 저장하는 가장 일반적인 방식 중 하나인 클라이언트 측 쿠키와 Redis를 비교해 보면, 어떤 방식을 선택해야 할지 판단하는 데 도움이 됩니다. 클라이언트 측 쿠키 대비 Redis를 세션 저장소로 사용했을 때의 장점은 다음과 같습니다:
- 사용자 변조에 더 강함: 쿠키는 사용자에게 노출되며 조작이 가능합니다. 반면 Redis를 사용하면 사용자가 데이터를 조작하는 것으로부터 보호할 수 있습니다.
- 클라이언트-서버 간 전송 데이터 최소화: 모든 세션 정보를 Redis에 저장하기 때문에 클라이언트 머신에 별도의 데이터를 저장할 필요가 없습니다.
- 클라이언트에는 '세션 ID'만 저장: Redis를 사용하면 각 요청이 어느 세션에 속하는지 파악하기 위해 세션 ID만 있으면 됩니다.
Upstash Redis 데이터베이스 생성하기
본격적인 설정에 들어가기 전에, 먼저 Upstash Redis의 장점부터 이해할 필요가 있습니다. 사용할 도구가 왜 필요한지 모른 채 도구를 쓰는 일은 바람직하지 않으니까요.
Upstash Redis를 사용할 때 얻을 수 있는 주요 이점은 다음과 같습니다:
- 서버리스(Serverless): Upstash Redis는 서버리스 데이터베이스입니다. 인프라를 관리하거나 데이터베이스 스케일링, 저수준(low-level) 설정을 신경 쓸 필요가 없습니다. 데이터베이스를 생성하고 바로 사용하면 됩니다.
- 글로벌 데이터 분산: Upstash는 사용자와 가까운 리전에 Redis 인스턴스를 배포할 수 있어 지연 시간을 더욱 줄여줍니다. 글로벌 분산 애플리케이션에서는 사용자 위치와 관계없이 빠르고 안정적인 세션 스토리지를 제공한다는 점에서 특히 유용합니다.
- 종량제(Pay as you Go): 초기 비용이 전혀 들지 않으며, 사용한 만큼만 지불하면 됩니다.
이제 Upstash Redis를 세션 관리에 사용하는 이유에 대한 의문이 해소되었다면, 실제로 데이터베이스를 생성해 보겠습니다.
Upstash 콘솔을 통해 Redis 데이터베이스를 생성합니다. Create Database 버튼을 클릭하세요. 이 튜토리얼을 따라 하면 Upstash Redis 데이터베이스를 얼마나 쉽고 빠르게 만들 수 있는지 다시 한번 확인할 수 있을 것입니다.
나타나는 모달에서 데이터베이스 이름을 지정하고 리전(region)을 선택합니다. 가급적 서비스가 배포된 곳과 같은 리전을 선택하는 것이 좋습니다.
다음 버튼을 클릭한 후 요금제를 선택합니다. 최대 예산 한도를 설정할 수 있는 종량제(Pay as you go with max budget limit) 플랜이 특히 유용합니다!
이제 Redis 데이터베이스가 준비되었습니다. Redis 대시보드에서 데이터베이스 엔드포인트, 연결용 비밀번호, 포트 정보를 확인할 수 있습니다. 웹 애플리케이션에서 데이터베이스에 연결할 때 이 정보들이 필요합니다.

이걸로 끝입니다!
Next.js 앱에서 Upstash Redis 사용하기
먼저 웹 애플리케이션 구조 안에서 Redis가 어떻게 동작하는지 살펴보겠습니다:
- 먼저 sessionId 없이 클라이언트 브라우저로부터 요청을 받습니다.
- 요청에 sessionId가 없으므로 새 sessionId를 생성하고 Redis에 해시를 만듭니다.
- sessionId를 쿠키 형태로 응답에 담아 클라이언트 머신에 반환합니다.
- 이후 sessionId가 포함된 새 HTTP 요청을 받으면, Redis에서 필요한 세션 데이터를 조회하여 응답에 활용합니다.
다음은 프로세스를 더 잘 이해할 수 있도록 서비스와 Redis 간 연결을 나타낸 시퀀스 다이어그램입니다:

모든 준비가 되었다면, 아직 Next.js 앱이 없다면 터미널에서 다음 명령어로 생성할 수 있습니다.
npx create-next-app@latest <project-name>
이제 Upstash Redis TypeScript SDK를 설치합니다.
cd <project-name>
npm install @upstash/redis
필요한 의존성 설치를 마쳤다면, 이제 사용자 브라우저 쿠키에서 세션 ID를 관리하고, 앱의 세션 저장소인 Upstash Redis와 상호작용하기 위한 인터페이스를 만들 차례입니다.
이 인터페이스는 /app/lib/sessionManager.tsx 파일에 구현합니다.
import { Redis } from '@upstash/redis'
import { cookies } from 'next/headers'
export const redis = new Redis({
url: '<UPSTASH-REDIS-URL>',
token: 'UPSTASH-REDIS-TOKEN',
})
type SessionId = string;
type Key = 'userName' | 'sessionStatus'; // 세션 데이터의 다른 키를 추가할 수 있습니다.
export async function getSessionId(): SessionId | undefined {
const cookieStore = await cookies();
return cookieStore.get("session-id")?.value;
}
async function setSessionId(sessionId: SessionId): void {
const cookieStore = await cookies();
cookieStore.set("session-id", sessionId);
}
export async function getSessionIdAndCreateIfMissing() {
const sessionId = await getSessionId();
if (!sessionId) {
const newSessionId = crypto.randomUUID();
await setSessionId(newSessionId);
return newSessionId;
}
return sessionId;
}
export async function get(key: Key, username: string = "") {
const sessionId = await getSessionId();
if (!sessionId) {
return null;
}
return await redis.hget(`session-${username}-${sessionId}`, key);
}
export async function getAll(username: string = "") {
const sessionId = await getSessionId();
if (!sessionId) {
return null;
}
return await redis.hgetall(`session-${username}-${sessionId}`);
}
export async function set(key: Key, value: string, username: string = "") {
const sessionId = await getSessionIdAndCreateIfMissing();
await redis.hset(`session-${username}-${sessionId}`, { [key]: value });
return redis.expire(`session-${username}-${sessionId}`, 900);
}
각 함수를 하나씩 살펴보겠습니다.
- getSessionId: 세션 ID는 사용자 브라우저의 쿠키에 저장됩니다. 이 함수는 HTTP 호출 시 API로 전달된 브라우저 쿠키에서 세션 ID를 가져옵니다.
- setSessionId: 전달받은 세션 ID 값으로 세션 ID 쿠키를 설정합니다.
- getSessionIdAndCreateIfMissing:
getSessionId를 호출해 기존 세션 ID를 쿠키에서 가져오고, 사용자 브라우저에 세션 ID가 없으면 새로운 랜덤 UUID를 세션 ID로 지정합니다. 또한 새 세션 ID를 쿠키에 추가하여 브라우저로 되돌려 보냅니다. - getSessionData(
get): Redis에서 세션 데이터를 가져오는 함수입니다. Upstash Redis에서 지정된 키에 해당하는 세션 데이터를 조회해 반환합니다. - getAllSessionData(
getAll): 모든 세션 데이터를 가져옵니다. Upstash Redis에서 해당 세션 해시의 모든 키-값 쌍을 조회합니다. - setSessionData(
set): 지정된 세션의 키-값 쌍을 설정하는 함수입니다.
Upstash Redis를 통해 세션 ID와 세션 스토리지를 관리하는 유틸리티 함수가 준비되었으니, 이제 이 함수들을 활용하는 API를 구현해 보겠습니다.
API는 매우 간단합니다. 경로(path)에서 사용자 이름을 전달받습니다. 요청을 받으면 먼저 쿠키에 세션 ID가 존재하는지 확인합니다. 쿠키에 세션 ID가 없으면 새 ID를 생성해 응답의 쿠키에 설정합니다. 세션 ID가 이미 존재한다면, Upstash Redis에서 해당 세션을 나타내는 해시에 키가 있는지 확인하고, 없으면 그 해시에 키-값 쌍을 생성합니다.
그럼 코드로 만들어 보겠습니다!
API는 Next.js의 App Router를 사용해 생성합니다.
app/api/user/[username]/route.tsx 파일을 생성해야 합니다.
import * as sessionStore from '../../lib/session'
export async function GET(request: Request,{ params }: { params: Promise<{ user: string }> }){
const userName = (await params).user;
const sessionId = await sessionStore.getSessionIdAndCreateIfMissing();
const sessionStatus = await sessionStore.get('sessionStatus', userName);
if(sessionStatus == null) {
console.log('There is no active session.');
await sessionStore.set('sessionStatus', 'ACTIVE', userName);
}
return Response.json({ userName: userName, sessionId: sessionId });
}
이 라우트 파일 덕분에 Next.js는 <URL>:3000/api/user/<username>으로 전송된 요청을 파일 내부에 정의된 HTTP 메서드로 라우팅할 수 있습니다.
이제 직접 동작을 확인해 보겠습니다. 터미널에서 루트 디렉터리로 이동한 후 다음 명령어로 Next.js 앱을 실행합니다:
npm run dev
이제 브라우저에서 API 엔드포인트인 https://localhost:3000/api/user/noah를 열어볼 수 있습니다. 페이지를 열면 사용자 이름과 세션 ID가 함께 반환되는 것을 확인할 수 있습니다:
{"userName":"noah","sessionId":"804814dc-10fc-4b0a-a4c1-321f4b54d399"}
마우스 오른쪽 버튼을 클릭해 검사(inspect)를 실행한 후 'Application' 탭으로 이동하면, 쿠키에 설정된 session-id를 확인할 수 있습니다.

또한 Upstash Redis에서 세션이 데이터베이스에 실제로 생성되었는지 확인할 수도 있습니다. 이를 위해 Upstash Redis 콘솔로 돌아가 해당 Redis 데이터베이스를 연 다음 Data Browser 탭을 엽니다.
Data Browser 탭에서는 아래와 같이 세션 데이터를 확인할 수 있습니다.

결론
Upstash Redis를 활용한 Next.js 앱의 세션 관리는 확장 가능하면서도 효율적인 솔루션을 제공합니다. 이 글에서는 Next.js 앱을 생성하고, Upstash Redis를 세션 관리 저장소로 통합하는 전체 과정을 다뤘습니다.
Redis 데이터베이스를 설정하고 Next.js와 매끄럽게 통합하면, 전통적인 서버 측 복잡성 없이도 세션 스토리지를 효과적으로 처리할 수 있습니다. 이러한 접근 방식은 성능과 단순함을 동시에 원하는 개발자에게 이상적입니다.
이 가이드가 여러분의 Next.js 애플리케이션에서 Upstash Redis를 안정적인 세션 스토어로 구현하는 데 명확한 길잡이가 되기를 바랍니다.