caching

3 개의 포스트

@RequestCache: HTTP 요청 범위 캐싱을 위한 커스텀 애너테이션 개발기 (새 탭에서 열림)

웹 애플리케이션에서 하나의 HTTP 요청 내에 발생하는 중복된 API 호출은 성능 저하와 리소스 낭비를 초래하며, 이를 해결하기 위해 요청 범위(Request Scope) 내에서 결과를 캐싱하는 `@RequestCache` 커스텀 애너테이션을 개발했습니다. 이 기능은 Spring의 `RequestAttribute`를 활용해 요청별로 독립적인 캐시 공간을 보장하며, 요청 종료 시 자동으로 메모리가 정리되는 효율적인 생명주기 관리 구조를 가집니다. 이를 통해 복잡한 파라미터 전달이나 부적절한 TTL 설정 문제를 해결하고 시스템의 전반적인 응답 속도를 개선할 수 있습니다. ### 파라미터 전달 및 범용 캐시의 한계 * **응답 객체 전달 방식의 복잡성**: 데이터를 실제 사용하는 말단 서비스까지 객체를 넘기기 위해 중간 계층의 모든 메서드 시그니처를 수정해야 하며, 이는 코드 가독성을 떨어뜨리고 관리를 어렵게 만듭니다. * **전략 패턴의 유연성 저하**: 공통 인터페이스를 사용하는 경우, 특정 구현체에서만 필요한 데이터를 파라미터에 포함해야 하므로 인터페이스의 범용성이 훼손됩니다. * **TTL(Time To Live) 설정의 딜레마**: Redis나 로컬 캐시 사용 시 TTL이 너무 짧으면 동일 요청 내 중복 호출을 막지 못하고, 너무 길면 서로 다른 요청 간에 의도치 않은 데이터 공유가 발생하여 데이터 정합성 문제가 생길 수 있습니다. ### @RequestCache의 특징과 동작 원리 * **RequestAttribute 기반 저장소**: 내부적으로 `ThreadLocal`을 사용하는 `RequestAttribute`에 데이터를 저장하여, 스레드 간 격리를 보장하고 각 HTTP 요청마다 독립적인 캐시 인스턴스를 유지합니다. * **자동 생명주기 관리**: 캐시의 수명이 HTTP 요청의 생명주기와 일치하므로 별도의 만료 시간을 계산할 필요가 없으며, 요청 완료 시 Spring의 `FrameworkServlet`에 의해 자동으로 정리되어 메모리 누수를 방지합니다. * **AOP 기반의 간편한 적용**: 비즈니스 로직을 수정할 필요 없이 캐싱이 필요한 메서드에 `@RequestCache` 애너테이션을 선언하는 것만으로 손쉽게 중복 호출을 제거할 수 있습니다. ### @RequestScope와 프록시 메커니즘 * **프록시 패턴 활용**: `@RequestScope`로 선언된 빈은 Spring 컨테이너에 프록시 객체로 등록되며, 실제 메서드 호출 시점에 현재 요청에 해당하는 실제 인스턴스를 찾아 호출을 위임합니다. * **상태 저장 방식**: `AbstractRequestAttributesScope` 클래스를 통해 실제 객체가 `RequestAttributes` 내에 저장되며, 이를 통해 동일 요청 내에서는 같은 인스턴스를 공유하게 됩니다. 동일 요청 내에서 외부 API 호출이 잦거나 복잡한 연산이 반복되는 서비스라면, 전역 캐시를 도입하기 전 `@RequestCache`와 같은 요청 범위 캐싱을 통해 코드 순수성을 유지하면서도 성능을 최적화할 것을 권장합니다.

비하인드 더 스트 (새 탭에서 열림)

넷플릭스는 수천만 명의 시청자가 동시에 접속하는 라이브 이벤트 상황에서 시스템 과부하를 방지하면서도 실시간 개인화 추천을 제공하기 위해 '프리페칭(Prefetching)'과 '실시간 브로드캐스팅'이라는 2단계 전략을 도입했습니다. 이 시스템은 이벤트 시작 전 미리 데이터를 기기에 저장해 두었다가, 실제 시작 시점에는 최소한의 신호만 보내 로컬에서 추천 정보를 활성화함으로써 '천둥 번개 효과(Thundering Herd)' 문제를 효과적으로 해결합니다. 이를 통해 넷플릭스는 클라우드 자원을 무리하게 확장하지 않고도 전 세계 수억 대의 기기에 지연 없는 실시간 스트리밍 경험을 제공할 수 있게 되었습니다. **라이브 이벤트와 시동 시간의 제약** * VOD와 달리 라이브 이벤트는 모든 시청자가 특정 시점에 동시에 접속하므로, 짧은 시간 내에 수억 개의 기기에 업데이트를 전달해야 하는 기술적 난관이 존재합니다. * 단순히 서버를 증설하는 선형적 확장은 비효율적이며, 다른 핵심 서비스의 자원을 고갈시킬 위험이 있습니다. * 성공적인 실시간 추천을 위해서는 업데이트 소요 시간(Time), 서비스 처리 용량(Request Throughput), 요청의 다양성(Compute Cardinality)이라는 세 가지 제약 조건을 동시에 최적화해야 합니다. **프리페칭을 통한 트래픽 분산** * 이벤트 시작 전 사용자가 평소처럼 앱을 탐색하는 동안, 라이브 이벤트와 관련된 메타데이터, 아트워크, 개인화된 추천 리스트를 미리 기기 캐시에 저장합니다. * 이를 통해 서버 요청을 시간에 따라 자연스럽게 분산시켜, 이벤트 직전 발생하는 트래픽 스파이크를 제거하고 시스템 안정성을 확보합니다. * 서버 측에서 미리 계산된 '구체화된 추천(Materialized Recommendations)'을 제공함으로써 기기별 요청의 복잡도를 낮춥니다. **저카디널리티 실시간 브로드캐스팅** * 이벤트가 실제로 시작되거나 일정이 변경될 때, 넷플릭스의 푸시 서비스(Zuul Push)를 통해 연결된 모든 기기에 '저카디널리티(Low-cardinality)' 메시지를 전송합니다. * 이 메시지는 복잡한 데이터를 담지 않고 단순히 미리 캐싱된 데이터를 화면에 표시하라는 트리거 역할만 수행하여 네트워크 부하를 최소화합니다. * '최소 한 번(At-least-once)' 전달 방식을 채택하여 네트워크 상태가 불안정한 기기도 다시 온라인 상태가 되면 누락된 업데이트를 즉시 따라잡을 수 있도록 설계되었습니다. **데이터 기반의 동적 적응** * 라이브 이벤트의 특성상 경기 시간이 지연되거나 일정이 변동될 수 있는데, 브로드캐스팅 시스템은 이러한 실시간 제작 상황에 맞춰 전송 타이밍을 동적으로 조절합니다. * 수천만 대의 기기가 동시에 서버에 데이터를 재요청하는 대신 로컬 데이터를 활용하게 함으로써, 전 세계 모든 사용자가 동일한 순간에 일관된 추천 UI를 볼 수 있게 합니다. 라이브 이벤트와 같은 초고부하 상황에서는 무조건적인 서버 증설보다는 클라이언트의 로컬 자원을 활용하고 서버 부하를 시간적으로 분산하는 아키텍처가 필수적입니다. 실시간성이 중요한 서비스라면 모든 데이터를 실시간으로 전송하기보다, 정적인 데이터는 미리 배치하고 상태 변화를 알리는 최소한의 신호만 실시간으로 처리하는 하이브리드 접근 방식을 권장합니다.

Flutter Riverpod 200% 활용하기 (새 탭에서 열림)

Riverpod은 기존 Provider 라이브러리의 한계를 극복하고 개발자가 더욱 직관적이고 유연하게 상태를 관리할 수 있도록 설계된 Flutter 상태 관리 라이브러리입니다. 서버 데이터 처리 최적화, 자동 생명 주기 관리, 의존성 주입 등 강력한 기능을 기본으로 제공하여 코드의 복잡성을 대폭 낮춰줍니다. 결과적으로 개발 생산성을 높이고, 성능 저하나 디버깅 오류와 같은 상태 관리의 고질적인 문제들을 효과적으로 해결해 줍니다. **서버 데이터 처리 및 상태 관리 최적화** * 서버에서 데이터를 가져올 때 필수적인 로딩, 에러, 데이터 유무 상태를 별도 로직 없이 기본적으로 제공합니다. * API 호출 도중 해당 데이터가 더 이상 필요하지 않게 되면 요청을 자동으로 취소하거나, 데이터의 유효 기간 및 재사용 설정을 손쉽게 관리할 수 있습니다. * '당겨서 새로 고침(pull to refresh)'과 같은 빈번한 UI 패턴을 `ref.refresh` 기능을 통해 간결하게 구현할 수 있어 반복적인 코드 작성을 줄여줍니다. **자유로운 의존성 주입과 자동 생명 주기 관리** * 위젯 계층 구조에 묶이지 않고 어디서든 Provider에 접근할 수 있어, 복잡한 순서나 구조를 신경 쓰지 않고 데이터를 참조할 수 있습니다. * Riverpod이 Provider의 생성과 소멸 시점을 자동으로 관리하므로 메모리 누수 방지와 같은 자원 관리가 용이합니다. * 개발자는 상태를 정의하는 Model과 이를 관리하는 Provider, 데이터를 소비하는 View를 명확히 분리하여 깔끔한 아키텍처를 유지할 수 있습니다. **효율적인 데이터 연동 및 캐시 활용 기법** * **Provider 간 상태 구독:** 필터 조건이 변경되면 이를 구독 중인 목록 Provider가 자동으로 데이터를 새로 불러오도록 설정할 수 있어 수동 리빌드 로직이 필요 없습니다. * **즉각적인 사용자 경험 제공:** 상세 화면 이동 시 목록에서 미리 불러온 데이터를 즉시 노출하고, 동시에 서버에서 추가 정보를 가져오는 방식으로 로딩 지연을 최소화합니다. * **오프라인 데이터 결합:** 로컬 DB 데이터와 서버 데이터를 유연하게 결합하여 네트워크가 불안정한 환경에서도 사용자에게 끊김 없는 인터페이스를 제공할 수 있습니다. * **화면 간 동기화:** 상세 화면에서 수정된 즐겨찾기 상태나 업데이트 내역이 목록 화면에도 즉각 반영되도록 구현하여 앱 전반의 데이터 일관성을 보장합니다. Riverpod은 단순히 상태를 저장하는 도구를 넘어, 비동기 프로그래밍과 의존성 관리를 우아하게 해결해 주는 솔루션입니다. 특히 서버 통신이 많고 화면 간 데이터 동기화가 복잡한 앱을 개발할 때, Riverpod의 선언적인 코드 스타일과 리액티브한 특징을 활용하면 유지 보수성이 뛰어난 코드를 작성할 수 있습니다.