캐시딜
넛지헬스케어 | 25년 3월 - 현재 | 11분 읽기
- Next.js
- Typescript
- tailwind
- styled-components
- tanstack-query
- React
- zustand
주요 담당 업무
- 커머스 전 영역 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을 통해 요청 주기를 제어하도록 제안했습니다. - 서버 사전 조회를 통한 초기 렌더링 개선은 유지하면서 배너 데이터의 노출 기간도 정상적으로 반영할 수 있도록 수정했습니다.
깨달은점
- 성능 최적화는 네트워크 요청을 최대한 제거하는 것이 아니라 사용자 경험과 데이터 신선도 사이의 적절한 기준을 정하는 작업이다.
- 캐시 정책은 호출 횟수뿐 아니라 데이터의 변경 주기, 잘못된 정보가 노출됐을 때의 영향과 재검증 시점까지 함께 고려해야 한다.