반응형
Next.js로 주문 내역 페이지를 만들고 있었다. 왼쪽에 주문 목록을 띄우고, 클릭하면 중간 섹션(Middle Section)에 상세 정보가 나와야 하는 구조였다. 근데 주문을 클릭해도 중간 섹션이 빈 채로 남아 있었다. 콘솔을 열어보니 이런 로그가 찍혀 있었다:
page.tsx:189 Selected order in useEffect: undefined
page.tsx:227 Order not found for selectedOrderId: 164
API 호출은 잘 됐는데, 왜 데이터가 안 보이는 걸까? 하루를 이거 풀어보느라 씨름했다.
원인 파악
코드를 뜯어보니 문제는 상태 관리에 있었다.
- 상태 두 개로 나뉘어 있었다:
- sortedGroups: /api/orders/daily에서 받은 주문 데이터를 날짜별로 정리한 상태.
- dailyOrders: Zustand 스토어에서 관리하는 별도의 주문 데이터.
- 두 상태가 서로 동기화되지 않고 따로 놀았다.
- 비동기 타이밍 문제:
- handleOrderClick에서 주문을 클릭하면 setSelectedOrder로 selectedOrderId와 selectedDate를 설정하고, dailyOrders가 없으면 API를 호출해서 채웠다:
const handleOrderClick = async (order) => {
const date = new Date(order.orderedAt).toISOString().split("T")[0];
setSelectedOrder(order.orderId, date);
if (!dailyOrders[date]) {
const response = await axiosInstance.get(`/api/orders/daily`, { params: { storeId, date } });
setDailyOrders(date, response.data);
}
};
- 근데 setSelectedOrder가 실행되자마자 useEffect가 돌면서 dailyOrders[selectedDate]를 확인했는데, 이때 setDailyOrders가 아직 완료되지 않아 데이터가 비어 있었다:
useEffect(() => {
if (selectedOrderId && selectedDate && dailyOrders[selectedDate]) {
const order = dailyOrders[selectedDate].find((o) => o.orderId === selectedOrderId);
// order가 undefined
}
}, [selectedOrderId, selectedDate, dailyOrders]);
데이터 소스 불일치:
- sortedGroups에는 API 데이터가 잘 들어갔는데(예: [{date: '2025-03-07', orders: Array(2)}]), 중간 섹션은 dailyOrders만 쳐다봤다. 이중 관리 때문에 혼란이 생긴 거였다.
하루 고민 끝에 접근을 바꿨다. 상태를 단순화하고 타이밍 문제를 잡아야겠다고 결심했다.
- 상태 통합:
- dailyOrders를 없애고 sortedGroups만 남겼다. 어차피 sortedGroups에 모든 주문 데이터가 있었으니 중복 상태는 필요 없었다.
- Zustand에서 dailyOrders와 setDailyOrders를 지우고, sortedGroups를 단일 소스로 삼았다.
- useEffect 수정:
- dailyOrders 대신 sortedGroups에서 주문을 찾도록 고쳤다:
useEffect(() => {
if (selectedOrderId && selectedDate) {
const group = sortedGroups.find((g) => g.date === selectedDate);
const order = group?.orders.find((o) => o.orderId === selectedOrderId);
if (order) {
setPlaceName(order.placeName || "Unknown");
// receipt 불러오는 로직
} else {
setPlaceName("");
setReceipt(null);
}
}
}, [selectedOrderId, selectedDate, sortedGroups]);
의존성 배열에 sortedGroups를 추가해서 데이터가 업데이트될 때마다 반응하도록 했다.
- handleOrderClick 개선:
- 클릭 시 sortedGroups에 데이터가 없으면 API 호출로 추가했다:
const handleOrderClick = async (order) => {
const date = new Date(order.orderedAt).toISOString().split("T")[0];
setSelectedOrder(order.orderId, date);
const groupExists = sortedGroups.some((g) => g.date === date);
if (!groupExists) {
const response = await axiosInstance.get(`/api/orders/daily`, { params: { storeId, date } });
setSortedGroups((prev) => [
...prev,
{ date, orders: response.data },
].sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()));
}
};
렌더링 로직 통합:
- selectedOrder를 sortedGroups에서 바로 가져오게 했다:
const selectedOrder =
selectedDate && sortedGroups.length > 0
? sortedGroups
.find((g) => g.date === selectedDate)
?.orders.find((o) => o.orderId === selectedOrderId) || null
: null;
수정하고 나니 로그가 달라졌다:
page.tsx:145 API 응답: /api/orders/daily {status: 200, data: Array(2)}
page.tsx:167 sortedGroups 설정 완료: [{date: '2025-03-07', orders: Array(2)}]
page.tsx:189 Selected order in useEffect: {orderId: 164, price: 90000, ...}
- sortedGroups에 데이터가 잘 들어갔고, useEffect에서 주문을 정확히 찾았다.
- Middle Section에 placeName과 receipt 정보가 드디어 표시됐다. 하루 만에 해결한 셈이다.
회고
처음엔 API 응답이 잘못됐나, 데이터 구조가 문제인가 고민했다. 근데 로그를 하나씩 보니 상태 관리 실수가 눈에 들어왔다. dailyOrders와 sortedGroups를 왜 따로 뒀는지 모르겠다 싶었다. 이번에 코드 단순화하면서 머리도 정리된 느낌이다. 다음엔 상태 설계할 때 좀 더 신경 써야겠다고 다짐했다. 코드도 깔끔해지고 배운 것도 많은 경험이었다.
반응형
'Next.js' 카테고리의 다른 글
캐시 활용으로 누락된 서버데이터 처리 (0) | 2025.03.01 |
---|---|
React : useMemo로 성능 최적화하기 (0) | 2025.02.26 |
Next.js에서 GSAP 최적화하기: 중앙집중화로 성능 개선 (0) | 2025.01.22 |
Next.js 15에서 발생한 Hydration 오류와 해결 과정 (0) | 2025.01.20 |
Next.js 14: Pages Router에서 App Router로 마이그레이션하기 (0) | 2025.01.20 |