캐시딜 어드민
넛지헬스케어 | 25년 3월 - 현재 | 3분 읽기
- Next.js
- Typescript
- tailwind
- tanstack-query
- React
- FSD
- shadcn
- tanstack-table
주요 담당 업무
- 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로 부모 렌더링에 따른 불필요한 항목 렌더링을 줄인다.