1. 개요

DoEatFit (v2.0) 프로젝트의 프론트엔드와 백엔드 통신 아키텍처 사이에서 발생했던 아주 까다로운 네트워크 오작동 사례를 해결했습니다. 브라우저의 네트워크 탭을 확인해 보면 분명히 백엔드 API 서버로부터 HTTP 200 OK 초록색 성공 신호와 함께 풍부한 결과 JSON 데이터가 잘 전달되고 있었음에도 불구하고, 정작 프론트엔드 화면에서는 “서버와의 연결이 원활하지 않습니다.” 라는 강제 에러 경고창이 점등되며 시스템이 굳어 버리는 기이한 현상이 발생했습니다. 분석 결과, 프론트엔드 공통 API 통신 모듈인 Axios Interceptor의 시스템 Envelope 판정용 status 필드와 백엔드 API에서 리턴하는 특정 도메인 비즈니스 데이터의 status 필드가 동명(同名) 충돌하여 발생한 런타임 섀도잉(Shadowing) 버그였습니다. 이 장애의 원인을 투명하게 밝히고, 네임스페이스를 완전히 격리하여 영구적인 통신 안정성을 수립한 트러블슈팅의 전말을 소개합니다.


2. 핵심 내용

2-1. 🚨 문제 현상 및 원인 분석

  • 문제 현상:
    • 식단 관리 대용량 동기화 및 동기화 상태 실시간 검출 모듈에서, 동기화 조회 API(GET /api/food/sync/status)가 돌 때마다 사용자에게 에러 알림창이 팝업되었습니다.
    • 네트워크 리스폰스 페이로드는 정상이었으나 프론트엔드 내부 JS 코드에서 예외 처리가 작동하며 동기화 상태 갱신이 일제히 가동 중단되었습니다.
  • 원인 분석:
    1. Axios Response Interceptor의 과도한 status 규격 검사: 프론트엔드는 통일성 있는 API 통신 제어를 위해 axios-instance.ts에 응답 처리 인터셉터를 운용 중이었습니다. 이 필터는 API 서버 응답 본문 JSON 바디에 status라는 키(Key)가 포착되면, 해당 값이 필히 "success"여야만 정상 데이터로 분류하고, 그렇지 않을 경우엔 비즈니스 에러로 간주해 강제로 JS 예외(throw new Error)를 던지는 규격을 고수하고 있었습니다.
    2. 비즈니스 상태 데이터 "RUNNING"과의 키 충돌: 식품 동기화 상태 조회 API는 백엔드 도메인에서 동기화 작업의 물리적인 구동 상태를 식별하기 위해 status라는 필드명을 리턴하고 있었습니다. 응답 페이로드가 { "status": "RUNNING", "progress": 42.5 } 형태로 가동 중인 찰나에, Axios 인터셉터가 이 JSON을 주시하다가 "RUNNING" 값을 포착했고, 조건식인 "RUNNING" !== "success"가 참이 되면서 200 OK였던 정상 응답을 강제 예외로 폭발시켰던 것입니다.
    3. 디버깅 단서의 블랙 아웃: 불행하게도 인터셉터가 억지로 생성한 에러 객체 내부에는 '요청이 실패했습니다.'라는 단순 텍스트만 바인딩되어 유실되었습니다. 에러 전역 전파 핸들러는 단순 텍스트만 받고 실제 네트워크 계층의 에러가 아니라는 이유로 디폴트 토스트 메시지인 *“서버와의 연결이 원활하지 않습니다.”*를 표출하여 마치 백엔드 서버나 호스트 인프라에 물리적 장애가 난 것처럼 상황을 오인하도록 왜곡했습니다.

2-2. 💡 단계별 에러 극복 및 솔루션 구현

  • 네임스페이스(Renaming) 전면 리팩토링:
    • 프레임워크와 네트워크 라이브러리 전역에 걸쳐 시스템 지시자로 널리 남용되는 status라는 단어를 비즈니스 상태 고유 단어인 **syncStatus**로 완벽하게 리네이밍해 섀도잉 간섭을 일소했습니다.
    • 백엔드(Spring Boot 3.x): FoodSyncHistoryEntity 내부의 변수명을 syncStatus로 교체하되, DB 기존 컬럼 마이그레이션 방지를 위해 @Column(name = "status")을 선언해 하위 호환성을 보존했습니다. FoodSyncHistoryDto의 변수명 또한 syncStatus로 치환하고 스프링 데이터 JPA의 쿼리 메서드명도 findBySyncStatus로 동기화 개편했습니다.
    • 프론트엔드(Next.js / TypeScript): API TypeScript 모델 명세를 syncStatus로 변경하고 React Query 훅 및 데이터 렌더링 뱃지 컴포넌트(FoodAdminPage)에 바인딩된 변수 참조를 안전하게 갱신했습니다.
  • 에러 리포팅 디버깅 상세화:
    • 향후 유사한 명칭 충돌이나 가짜 200 OK가 또 다른 모듈에서 터졌을 때 빠르게 추적할 수 있도록 use-axios-interceptor.ts에 동적인 괄호 힌트 로깅을 장착했습니다.
    // 변경 후: 실제 격발된 런타임 에러 메시지를 꼬리표 괄호에 바인딩해 가시화
    toast.error(`서버와의 연결이 원활하지 않습니다. (${error.message})`);
graph TD
    subgraph "기존 상황 (Shadowing 충돌)"
        A["백엔드 HTTP 200 OK 전송"] --> B["body.status = 'RUNNING' 포함"]
        B --> C{"Axios 응답 인터셉터 감지"}
        C -- "status가 'success'가 아님!" --> D["강제 throw new Error() 실행"]
        D --> E["토스트: '서버 연결 실패' 오탐 출력"]
    end

    subgraph "개선 상황 (Namespace 격리)"
        F["백엔드 HTTP 200 OK 전송"] --> G["body.syncStatus = 'RUNNING' 포함"]
        G --> H{"Axios 응답 인터셉터 감지"}
        H -- "시스템 키 'status' 없음! 무사통과" --> I["정상 응답 리턴"]
        I --> J["React Query에 순수한 비즈니스 데이터 수신"]
        J --> K["화면에 동기화 현황 정상 실시간 점등"]
    end

2-3. ✅ 결과 검증 및 모던 오픈소스 라이브러리 검토

  • 결과 검증:
    • 식품 DB 강제 동기화 론칭 툴을 다이얼로그에서 기동하여 네트워크 패널과 UI 피드백을 모니터링했습니다.
    • 리네이밍 작업 완료 후, 서버는 정상적으로 syncStatus: "RUNNING"을 함유한 200 OK를 내려 주었으며 Axios 인터셉터는 더 이상 이에 오작동을 일으키지 않고 안전하게 브릿지 통과시킴을 입증했습니다. 동기화 상태가 화면 뱃지에 평온하고 실시간으로 표출되며 화면 먹통 현상이 완벽하게 퇴치되었습니다.
  • 추천 오픈소스 라이브러리 검토:
    1. Zod
      • 평가: TypeScript 타입은 빌드타임 컴파일 단계에만 한정됩니다. 런타임에 서버 API 규격이 미세하게 깨지거나 필드가 꼬이는 사고를 조기에 제압하려면 프론트 통신 수문장으로 런타임 파싱 엔진 Zod를 장착해 보세요. z.object({ syncStatus: z.string() })로 스키마 파싱을 걸어 두면 유효하지 않은 속성 인입 시 투명한 Schema Error를 검출해 주어 사후 디버깅이 무척 쾌적해집니다.
    2. Sentry
      • 평가: HTTP 코드는 200이지만 프론트엔드 어플리케이션 코드 내부에서 강제로 던져버린 예외는 백엔드 인프라 로그 수집기로는 잡히지 않는 유령 장애가 됩니다. 클라이언트 브라우저 런타임 에러 스택과 Axios 요청 세부 내역을 캡처하여 즉각 개발팀 대시보드로 경보를 쏴 주는 Sentry를 연동해 보실 것을 제안합니다.