bgyoonsContact

캐시딜

넛지헬스케어 | 25년 3월 - 현재 | 11분 읽기

URL - 캐시딜, 언니딜, 헬시딜

주요 담당 업무

  • 커머스 전 영역 API를 구 시스템(NHN 커머스 샵바이)에서 신규 API 마이그레이션 수행
  • Pages Router에서 App Router로의 단계적 전환 및 레거시 컴포넌트·상태 전달 구조 개선
  • 웹뷰에서만 운영되던 캐시딜을 캐시워크 홈페이지 내부 웹으로 확장하기 위해 세션 쿠키 기반 인증과 웹 프록시 연동 흐름 구현
  • 핵심 기능 개발과 상용 이슈·CS 대응 및 그라파나 기반 장애 분석
  • 테크니컬 SEO 작업 주도 - 메타데이터·JSON-LD 정비, 주요 구좌 SSR 전환, 개인화 페이지 noindex 전략 및 각 테넌트 별 협업
  • Braze 사용자 속성 적재 구조를 설계·구현하고, Firebase·GA4·AppsFlyer를 활용해 주문 퍼널 주요 단계의 이벤트 적재
  • 네이티브 팀과 앱 버전 및 배포 일정을 조율해 버전별 기능을 안정적으로 적용
  • FE 파트장으로서 파트원의 작업·PR·배포 일정을 조율

트러블 슈팅

복합 필터의 선택 순서와 취소 이전 상태가 보존되지 않는 문제

문제

  • 필터 태그가 가격 → 배송 → 기획전 → 카테고리 → 쿠폰의 고정 순서로 생성되어, 사용자가 선택한 순서가 보존되지 않았다.
  • 필터 모달에서 값을 바꾸면 전역 상태가 즉시 반영되어, 취소 시 되돌릴 기준 상태가 사라졌다.

해결

  • 선택된 태그 키를 Set으로 관리해 중복 제거와 삽입 순서 유지를 동시에 확보
  • 모달을 열기 전 현재 필터 상태를 스냅샷으로 저장하고, 적용 시 스냅샷 삭제 / 닫기 시 스냅샷으로 전역 상태 복원하는 패턴 도입하여 편집 중 상태와 확정 상태를 분리

깨달은점

  • 순서 있는 유일한 값 집합이 필요할 때는 배열보다 Set이 적합함
  • 전역 상태에서도 로컬 폼처럼 draft/commit 분리가 필요하며, 모달·폼 UI는 "즉시 반영"보다 "커밋 시 반영" 모델이 안정적이다.

하단 푸터 탭의 라우터 혼재와 도메인 통합 환경에서의 화면 전환 불안정

문제

  • 하단 푸터 탭(홈 | 숏핑 | 카테고리 | 찜 | 마이페이지)의 대상 페이지가 app router와 pages router에 혼재되어, 탭 이동이 하드 네비게이션으로 처리되며 풀 페이지 리로드가 발생했다.
  • 도메인 통합 테넌트(언파/지니어트)에서 Pages Router로 CSR 이동하면 _next/data/... 요청이 CloudFront 경로 정책과 맞지 않아 비정상 응답을 받았고, Next.js 라우터가 데이터를 처리하지 못하면서 빈 화면이 발생할 가능성이 있었습니다.

해결

  • 1차
    • 문제가 발생하는 언니의파우치와 지니어트에서 location.href를 사용해 Next.js 클라이언트 라우터와 _next/data 요청을 우회
    • 캐시워크와 캐시닥은 기존 router.push 방식을 유지해 영향 범위 최소화
  • 2차: 푸터 대상 페이지를 app router로 점진 이관해 pages router CSR 의존 구간 제거
  • 3차: 이관이 끝난 구간부터 우회 로직을 걷어내고 router.push로 재통일해 전환 UX 회복

깨달은점

  • 멀티테넌트 환경에서는 프레임워크 라우팅 전략과 인프라(CF) 동작을 분리해서 볼 수 없고 함께 설계해야 한다.
  • 운영 영향이 큰 문제는 즉시 모든 구조를 변경하기보다 안전한 우회로로 사용자 영향을 먼저 차단한 뒤, 점진적인 마이그레이션을 통해 근본 원인을 제거하는 방식이 효과적이다.

헬스체크가 307 리디렉션을 받아 배포 실패

문제

  • 도메인 통합 후 미들웨어에 / → /shop 정규화 리디렉션(307)을 추가하자, / 경로를 보던 EB/ALB 헬스체크가 307을 받아 배포가 실패했다.
  • 헬스체크는 리디렉션을 따라가지 않고 최초 응답 코드로 판정하기 때문에 정상적으로 동작하는 애플리케이션이 비정상 상태로 판단됐습니다.
  • 테넌트 호스트 요청에만 리디렉션을 적용하는 조건을 추가했지만, ALB 내부 요청의 특성에 의존하는 임시 대응이었습니다.

해결

  • 미들웨어 matcher에서 제외되는 헬스체크 전용 엔드포인트 /api/health를 신설해 항상 200을 반환하도록 하고, EB의 Health check path를 이 경로로 변경.
  • 전용 엔드포인트 적용 후 미들웨어의 임시 조건을 제거해 리디렉션 로직을 다시 단순화했습니다.

깨달은점

  • 미들웨어에 전역 리디렉션을 추가하면 사용자 트래픽뿐 아니라 인프라 트래픽까지 영향을 받는다.
  • 인프라 요청의 예외는 비즈니스 로직에 조건을 추가하기보다 전용 엔드포인트로 분리하는 것이 명확하고 유지보수하기 쉽습니다.

robots.txt 차단 이후에도 개인화 페이지가 검색 결과에 남는 문제

문제

  • 웹에서 캐시딜을 이용할 수 있게 된 이후 마이페이지·장바구니·찜 등의 개인화 페이지가 구글 검색 결과에 노출된 것을 site:unpa.me/shop/wish 검색을 통해 확인했습니다.
  • 처음에는 robots.txt의 Disallow 규칙으로 크롤링을 차단했지만, 크롤러가 페이지에 접근하지 못하면 noindex도 확인할 수 없어 이미 색인된 페이지가 검색 결과에서 제거되지 않는 구조였습니다.

해결

  • next.config.js의 headers()를 이용해 개인화 경로에 X-Robots-Tag: noindex, nofollow 응답 헤더를 적용했습니다.
  • 관련 테넌트 담당 팀에 기존 robots.txt의 Disallow 규칙 제거를 요청해 크롤러가 페이지를 다시 방문하고 noindex를 확인할 수 있도록 했습니다.

깨달은점

  • 크롤링 차단과 색인 제거는 서로 다른 문제이므로, 검색엔진의 크롤링과 색인 처리 과정을 이해하고 목적에 맞는 방법을 선택해야 합니다.
  • 멀티테넌트 환경에서는 내 레포 수정만으로 완결되지 않아 여러 담당 파트의 robots.txt까지 추적·요청·확인이 필요했다.

신규 캐시딜 출시 이후 장애 대응 체계 미흡으로 에러 누적

문제

  • 도메인 간(인증/주문/포인트/상품/클레임) 이슈가 동시에 발생했지만, 초기에는 장애 신호를 통합적으로 추적·분류하는 운영 루틴이 약했다.
  • 일부는 서버 원인, 일부는 클라이언트 중복 요청·환경 의존성(구버전·외부 브라우저) 이슈가 섞여 있어 원인 구분이 어려웠다.

해결

  • Grafana 로그를 시간대·엔드포인트·실행 환경·에러 코드별로 분류하고, 타임스탬프와 재현 조건을 포함한 근거를 유관 팀에 전달했습니다.
  • 케이스별 조치:
    • 포인트 API 502 상태코드: 특정 시간대 집중 발생 패턴 식별 → 인증 서버 증설로 해소
    • 미존재 상품 ID 오류: 상품 리스트 API 필터링 요청 → 서버 필터링 반영
    • 반품/클레임 오류: 주문 상태 불일치·중복 요청 케이스 식별 → 서버 메시지/처리 개선, 프론트는 클레임 버튼 쓰로틀링 적용
    • 클라이언트 에러: 웹뷰 환경 분석(구버전 + Samsung Internet) 후 대응 범위 제외 의사결정 지원
  • 결과: 에러 로그 약 17% 감소

깨달은점

  • 장애 대응은 로그를 확인하는 데서 끝나지 않고 탐지, 분류, 커뮤니케이션, 조치, 검증의 과정으로 관리해야 합니다.
  • 서버에서 발생한 문제라도 프론트엔드는 실행 환경과 재현 조건을 구체화하고, 중복 요청을 방지해 해결 속도를 높이고 사용자 영향을 줄일 수 있다.

cloneElement 레거시 패턴과 주문 액션 로직 분산으로 인한 구조적 복잡성

문제

  • Container + React.cloneElement 패턴으로 자식 컴포넌트에 props가 암묵적으로 주입되어 데이터 흐름 추적과 타입 추론이 어려웠다.
  • 주문 상태별 다음 행동 로직이 여러 UI 컴포넌트에 흩어져 있어 상태를 추가하거나 변경할 때 여러 파일을 함께 수정해야 했습니다.
  • 일부 수정이 누락되면 기존에 정상적으로 동작하던 주문 기능에도 오류가 발생할 가능성이 있었습니다.
  • 해당 패턴이 여러 화면에 사용되고 있어 단건 수정만으로는 구조적인 문제를 해결하기 어려웠습니다.

해결

  • 즉시 대규모 수정 대신, 개선 방향을 검증하기 위한 임시 PR을 작성해 기존 구조의 문제와 변경 방향을 팀에 공유했습니다.
  • 이후 useOrderNextAction 중심으로 주문 상태별 액션을 중앙화하고, 버튼 컴포넌트는 상태와 옵션을 전달받아 렌더링하는 역할만 담당하도록 분리했습니다.
  • OrderSheetObserverContainer를 제거하고 각 컴포넌트가 필요한 값을 직접 구독하도록 변경해 암묵적인 props 전달 구조를 줄였습니다.

깨달은점

  • 여러 화면에 사용되는 레거시 구조는 바로 수정하기보다 문제와 개선 방향을 먼저 공유하고 작은 범위에서 검증해야 변경 위험을 줄일 수 있습니다.
  • 상태 기반 UI에서는 화면 렌더링과 행동 결정을 분리할수록 새로운 상태를 추가하거나 기존 동작을 변경하기 쉽습니다.
  • cloneElement를 이용한 암묵적 주입보다 훅이나 컨텍스트를 통한 명시적인 상태 구독이 타입 안정성과 유지보수 측면에서 유리합니다.

홈화면 LCP 개선 과정에서 배너 데이터 갱신이 중단될 수 있는 문제

문제

  • 홈 화면 LCP 개선 과정에서 서버가 조회한 배너 데이터를 initialData로 전달하고, 데이터가 존재하면 enabled: false로 클라이언트 쿼리를 비활성화하는 구조가 제안됐습니다.
  • 초기 요청은 줄일 수 있지만 배너는 노출 시작일과 종료일이 있는 데이터이므로, 사용자가 페이지에 머무르거나 다시 포커스했을 때 재검증되지 않으면 만료된 배너가 계속 노출되거나 새로운 배너의 반영이 늦어질 수 있었습니다.

해결

  • initialData가 쿼리 캐시에 들어가면 staleTime 동안은 fresh로 간주되어 추가 호출이 없고 이후에는 백그라운드 refetch가 된다는 점을 로컬에서 직접 확인
  • 쿼리를 영구적으로 비활성화하는 enabled 대신 staleTime을 통해 요청 주기를 제어하도록 제안했습니다.
  • 서버 사전 조회를 통한 초기 렌더링 개선은 유지하면서 배너 데이터의 노출 기간도 정상적으로 반영할 수 있도록 수정했습니다.

깨달은점

  • 성능 최적화는 네트워크 요청을 최대한 제거하는 것이 아니라 사용자 경험과 데이터 신선도 사이의 적절한 기준을 정하는 작업이다.
  • 캐시 정책은 호출 횟수뿐 아니라 데이터의 변경 주기, 잘못된 정보가 노출됐을 때의 영향과 재검증 시점까지 함께 고려해야 한다.