bgyoonsContact

캐시딜 어드민

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

주요 담당 업무

  • Next.js·React·TypeScript 기반 커머스 서비스 어드민 및 파트너 어드민 프론트엔드 주요 기능 개발·운영
  • FSD 구조와 OpenAPI 타입을 기반으로 복잡한 어드민 기능을 도메인별로 분리하고 반복되는 폼·테이블·서버 상태 로직을 모듈화

문제 해결

OTP QR 이미지 처리 방식

문제

  • OTP 등록 API가 qrImageUrl을 응답하고, 클라이언트가 해당 URL로 이미지를 요청하는 구조였다.
  • 이 과정에서 원본 QR 이미지 URL이 브라우저에 전달됐으며, CMS 모의해킹에서 정보 누출 항목으로 지적됐다.

해결

  • Route Handler를 BFF 프록시로 두고, 서버에서 원본 이미지를 fetch한 뒤 PNG 바이너리를 클라이언트로 전달한다. 클라이언트는 blob으로 받아 렌더링하는 구조를 생각했다.
  • 그러나 보안 이슈 때문에 프록시 로직이 불필요하게 추가되는 구조인 것 같다고 판단하여, 이미지를 제공하는 백엔드에서 응답 형식을 변경하여 전체 구조를 단순하게 유지하는 방향으로 결정했다.
  • 백엔드에 API 응답을 이미지 URL 대신 Content-Type: image/png의 바이너리 응답을 요청하여 별도 프록시 없이 원본 이미지 URL의 클라이언트 전달을 제거했다.

깨달은 점

  • 프론트에서 감싸는 우회책보다 응답 형태 자체를 바꾸는 것이 전체 시스템의 책임과 코드를 줄일 수 있으며, 보안 경계도 명확하다.

최대 30개 배너의 순서 변경을 고려한 렌더링 설계

문제

  • 배너 콘텐츠는 최대 30개까지 추가·삭제·순서 변경할 수 있었고, 각 항목에는 이미지와 여러 입력 필드가 포함됐다.
  • 실제 지연을 측정한 상태는 아니었지만, 순서를 바꿀 때마다 변경되지 않은 항목까지 다시 렌더링하면 상호작용이 느려질 수 있다고 예상했다.

해결

  • 구현 단계에서 입력 상태를 보존하고 불필요한 렌더링을 줄이는 구조를 고려했다.
  • 항목별 입력값과 업로드 상태를 로컬 상태로 관리했다. 부모는 bannerIds로 순서와 추가·삭제를 관리하고, 각 항목은 자신의 편집 상태를 소유하도록 분리했다.
  • 순서가 바뀌어도 유지되는 ID를 key로 사용했다. 위치 변경 시에도 동일한 항목으로 식별해 입력값과 로컬 상태가 유지되도록 했다.
  • React.memo로 부모의 순서 변경에 따른 항목 렌더링을 줄이도록 했다. 항목 ID와 삭제 버튼 활성화 여부를 비교해, 표시 내용이 동일한 항목의 렌더링을 생략하도록 구성했다.
  • 제출 시에만 항목별 데이터를 취합했다. 각 항목이 ref에 등록한 getter를 부모가 화면 순서대로 호출하고, 수집한 데이터를 Zod로 검증했다.

깨달은 점

  • 상태 소유권·상태 보존·렌더링 생략의 역할을 구분한다. 입력값을 항목별로 소유하면 개별 입력마다 부모 상태를 갱신하지 않아도 된다. 순서 변경 시에는 안정적인 key로 항목의 상태를 보존하고, memo로 부모 렌더링에 따른 불필요한 항목 렌더링을 줄인다.