• 캐시는 저장보다 삭제가 어렵다: NestJS 캐시 무효화 재설계

    NestJS 서비스에 Redis 캐시를 처음 붙일 때는 조회 메서드 앞에 데코레이터를 추가하는 일로 끝났습니다. 목록, 상세, 통계처럼 호출이 잦은 API의 응답 시간이 줄었고 데이터베이스 부하도 낮아졌습니다. 분산 락에서 사용한 데코레이터와 인터셉터 패턴을 캐시에도 적용했지만, 문제는 기능이 늘어난 뒤 나타났습니다. 프로젝트 이름을 수정했는데 상세 API는 새 값을…

    읽기 →

  • NestJS에서 데코레이터와 인터셉터로 분산 락 구현하기

    NestJS의 여러 endpoint에 같은 분산 락 패턴이 반복됐습니다. Redis에서 token을 얻지 못하면 429를 반환하고, 비즈니스 로직이 끝나면 소유 token을 확인해 lock을 해제하는 코드였습니다. 처음에는 service 메서드마다 이 코드를 작성했는데, 시간이 지나자 TTL과 key suffix, 오류 메시지, 해제 방식이 서로 달라졌습니다. 반복을 줄이기 위해 custom decorator와 global…

    읽기 →

  • 실시간 집계가 느려졌을 때 통계를 미리 계산하는 방법

    대시보드의 통계 API는 처음에 원본 이벤트와 결과 항목 테이블을 요청마다 집계했습니다. 정확하고 구현이 단순했지만 데이터가 늘면서 하나의 화면이 여러 개의 GROUP BY를 동시에 실행했습니다. 기간, 장비, 제품, 결함 종류 조합이 다양해 캐시 적중률도 낮았습니다. 짧은 기간은 괜찮았지만 장기 조회와 동시 사용자가 겹치면 응답 시간이 크게 흔들렸습니다.…

    읽기 →

  • Dual Write의 빈틈을 Reconciliation Job으로 복구하기

    MongoDB에 원본을 저장한 뒤 Redis Streams로 이벤트를 발행하고, Worker가 PostgreSQL 조회 모델을 만드는 구조를 운영했습니다. 전체 데이터 파이프라인의 처리 순서는 정상 흐름만 보면 단순하지만 MongoDB 저장과 Redis 발행은 하나의 트랜잭션이 아니었습니다. 저장 직후 프로세스가 종료되거나 Redis가 잠시 응답하지 않으면 원본은 존재하지만 이벤트는 없는 상태가 생깁니다. 이…

    읽기 →

  • 여러 Pod가 PostgreSQL 월별 파티션을 동시에 만들지 않게 한 방법

    감사 로그는 계속 쌓이지만 오래된 기록을 조회하는 빈도는 낮습니다. 그래서 audit_log와 audit_log_target을 occurred_at 기준의 월별 RANGE 파티션으로 나눴습니다. 데이터가 어느 파티션으로 들어갈지는 명확했지만, 다음 달 파티션을 누가 언제 만들지는 별도의 문제였습니다. PostgreSQL은 입력값을 받아 줄 파티션이 없으면 부모 테이블에 대신 보관하지 않습니다. DEFAULT 파티션을 두지 않은…

    읽기 →

  • 대규모 시계열 데이터를 PostgreSQL 파티션으로 나누기

    시간 순서로 계속 쌓이는 이벤트 데이터는 처음에는 단일 PostgreSQL 테이블로도 충분했습니다. 최근 며칠을 조회하는 API, 장비별 필터, 결함 통계 모두 적절한 복합 인덱스로 빠르게 처리됐습니다. 문제는 데이터와 인덱스가 함께 커진 뒤 나타났습니다. 조회 조건은 최근 범위였지만 조회에 사용되는 인덱스는 전체 이력을 포함했습니다. 대량 삭제로 발생한 WAL과…

    읽기 →

  • 실시간·Hot·Cold 3계층 데이터 아키텍처를 설계한 이유

    시간 순서로 쌓이는 이벤트 데이터도 시간이 지나면 접근 빈도와 갱신 방식이 달라집니다. 수집 직후에는 새 데이터가 계속 들어오고 상태가 갱신되지만, 최근 며칠 또는 몇 주의 데이터는 화면 조회와 통계에서 반복해서 읽힙니다. 수개월이 지난 데이터는 거의 조회되지 않지만 감사, 장기 통계, 파일 내보내기를 위해 보관해야 합니다. 초기에는…

    읽기 →

  • Prompt Injection으로부터 도구 실행을 보호하기

    문서와 사용자 입력을 읽는 AI가 외부 도구까지 실행하면 Prompt Injection은 잘못된 답변을 넘어 실제 상태 변경으로 이어질 수 있습니다. 처음에는 system prompt에 외부 문서의 지시를 따르지 말라고 명시하면 도구 실행도 보호할 수 있다고 생각했습니다. 하지만 검색 결과와 사용자 입력이 같은 context에 들어오자 문서 속 명령이 실제…

    읽기 →