All case studies
GAIS — Government Advertising Integrated Support SystemKorea Press Foundation (KPF)Aug 2020 - Dec 2025 (5y 4m)

INFRA / SESSION

Redis-based session clustering

JEUS Standard Edition does not support session clustering, so I routed around the limitation by putting Redis in front as an external session store. The result: rolling restarts of WAS nodes became possible even on a legacy stack.

Core impact
Zero-downtime deploys

Rolling WAS restarts enabled

Session latency
Milliseconds

No perceivable UX impact

Framework
eGovFrame 3.x

Native Spring Session support

Infrastructure
Redis 7.4

Docker container

Redis 7.4Spring SessionSpring Data RedisLettuceJEUS (Standard)DockereGovFrame

Problem

Restarting a single WAS wiped out logins and in-progress input

The setup had multiple WAS nodes, but each one held its own sessions independently. Restarting even a single node logged out every user pinned to it, and any form they were filling out was lost along with it. JEUS does offer session clustering, but only in the Enterprise license — our environment was Standard, so there was no official path to fix it.

  • Restarts wiped out logins and form input

    Users routinely lost what they were typing with a single refresh

  • Zero-downtime deploys were impossible

    Either defer releases to protect sessions, or accept session drops to ship — a constant trade-off

  • No licensing path to a fix

    Session clustering ships only with JEUS Enterprise; unavailable on Standard

Approach

Route around it with an external Redis session store

A license upgrade would have been expensive, so instead I moved sessions out of the application and into an external store. Spring Session integrates with Redis out of the box, so no application code had to change — only configuration. And because Redis responds in milliseconds, I expected almost no user-facing impact.

BEFORE

세션 격리 · 재기동 시 유실

WAS 1

session

WAS 2

session

WAS 3

session

✕ WAS 간 세션 공유 없음

AFTER

세션 공유 · 무중단 재기동

WAS 1

WAS 2

WAS 3

Redis

SHARED SESSION STORE

Fast responses

Session reads and writes run in milliseconds — no UX impact

Code stays as-is

Spring Session integrates with Redis natively — config-only change

Lightweight ops

Installed via Docker, TTL managed automatically, simple topology

Process

Implementation steps

  1. 01

    Stand up Redis

    I ran Redis 7.4 in a container on a shared Docker host, wiring up a password, a persistent volume, and an auto-restart policy. The host, port, and password used by the application were extracted into a separate properties file.

    install-redis.sh
    bash
    # Install Redis (7.4.2)
    docker pull redis:7.4.2
     
    # Run the container
    docker run -d \
    --name redis \
    --restart unless-stopped \
    -p 6379:6379 \
    -v /data/redis:/data \
    redis:7.4.2 redis-server --requirepass "your-password"
    redis.properties
    properties
    # redis.properties
    redis.host = your.redis.host
    redis.port = 6379
    redis.password = your-password
    redis.database = 0
  2. 02

    Add Spring Session dependencies

    I added Spring Session, Spring Data Redis, and Lettuce to the eGovFrame project's pom.xml. This is the groundwork for delegating servlet sessions to an external store.

    pom.xml
    xml
    <!-- pom.xml - Spring Session + Redis dependencies -->
    <dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
    <version>2.7.0</version>
    </dependency>
    <dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>2.7.0</version>
    </dependency>
    <dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
    <version>6.2.0.RELEASE</version>
    </dependency>
  3. 03

    Wire up the Redis connection and session mapping

    In context-redis.xml I declared a LettuceConnectionFactory and RedisHttpSessionConfiguration. That maps the HTTP session to Redis automatically, so no application code had to change.

    context-redis.xml
    xml
    <!-- context-redis.xml - Redis connection, serialization, and session config -->
    <bean id="redisConnectionFactory"
    class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory">
    <property name="hostName" value="${redis.host}"/>
    <property name="port" value="${redis.port}"/>
    <property name="password" value="${redis.password}"/>
    <property name="database" value="${redis.database}"/>
    </bean>
     
    <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
    <property name="maxInactiveIntervalInSeconds" value="1800"/>
    </bean>

Outcome

Results and takeaways

  • Zero-downtime deploys unlocked

    Rolling WAS restarts now preserve sessions. Hotfixes and test rollouts can ship without taking the service offline.

  • Login drops eliminated

    Under failover, redeploys, and long-running operation, users no longer get kicked out or lose form input.

  • Worked around the license ceiling with an external store

    I delivered session clustering without buying the higher license tier — a firsthand lesson that platform constraints can often be solved at the architecture layer.

  • Next time, verify compatibility up front

    Because the eGovFrame version was older, validating Spring Session and Redis compatibility took more time than expected. Going forward, I will pin down compatibility before I start.

MORE

Explore other cases

Badabom

AUTH / SSO

Building an SSO Provider for Partner Sites

Implemented an SSO Provider so external partner sites (e.g., OTT) could sign in with Badabom accounts. Single-use UUID tokens stored in the database support multiple WAS nodes, and CI (Connecting Information) auto-maps accounts across both sides.

View detail

Badabom

DEVOPS / OBSERVABILITY

SSE + Cross-WAS Real-Time Log Viewer

The WAS lived in the Daejeon IDC, but network-segregation policy meant only Busan-office PCs could reach it — so pulling a log effectively meant flying to Busan. I built an SSE-based viewer inside the admin web and added a cross-WAS relay so logs from both WAS nodes stream into a single screen.

View detail

Badabom

LEGACY MIGRATION

Migrating the OTT Technology-Trade System into Badabom

Moved an Oracle + MyBatis technology-trade platform (OTT) onto PostgreSQL + iBATIS. Rewrote 87 URLs, 34 JSPs, 80+ SQL queries, and 14 tables.

View detail

GAIS — Government Advertising Integrated Support System

CI/CD

Automating the Build and Deploy Pipeline

Replaced a fully manual build-and-deploy workflow with a Jenkins + GitLab Webhook pipeline, cutting deploy time from 15–20 min down to around 4 min.

View detail

GAIS — Government Advertising Integrated Support System

SECURITY / NETWORK

Applying TLS 1.3 via an Nginx Reverse Proxy

Touching the shared WebtoB SSL felt risky, so I put Nginx in front and terminated TLS there instead. Existing services kept running untouched while TLS 1.3 was rolled out.

View detail

Freelance · Side Projects

CLIENT WORK / WEB

Pitched and Built a Postpartum Care Center Site Renewal

My wife had stayed at a postpartum care center whose website felt dated, so I mocked up a UI sample and pitched it myself. I built an Astro static site with a 192-frame scroll animation, Kakao Map, and SEO — then shipped it to their production domain.

View detail

Freelance · Side Projects

SIDE PROJECT / AI

Family-Driven Baby Naming with AI + Tournament-Style Voting

Existing naming services are designed for solo use, so I built a way for the whole family to join in. GPT-4o suggests names aligned with Saju (birth-chart) and Ohaeng (Five-Element) rules, and the family votes tournament-style to pick the final name.

View detail
Redis Session Clustering | Case Study