카테고리 없음
새로고침 시에 json 배열이 덮이는 문제 해결
lamarcK
2025. 4. 24. 18:39
디버깅으로 해결
리스트 아이템을 정렬하기 위해 사용한 ReactSortable이 문제였습니다.
저장된 json이 없을 경우 배열을 디폴트 배열로 보여주기 위해 설정한 부분이 새로고침, 마운트 시에 데이터가 로딩이 안된 순간과 겹쳐서 실제로 json 배열이 있었음에도 모두 날아가고 디폴트로 덮이던 현상이 있었습니다.
<List>
{/* ReactSortable로 드래그 앤 드롭 기능 구현 */}
<StyledSortable
list={todos}
setList={handleSetList}
animation={150}
ghostClass="sortable-ghost"
dragClass="sortable-drag"
handle=".drag-handle"
forceFallback={false}
>
{todos.map((todo) => (
<ListItem
key={todo.id}
data-id={todo.id}
className="drag-handle" // 전체 ListItem을 드래그 핸들로 설정
>
<CheckBox
type="checkbox"
checked={todo.isChecked}
onChange={() => {}}
onClick={(e) => handleToggle(e, todo.id)} // 체크박스 클릭 시 이벤트 전파 방지
/>
<ListItemText>{todo.text}</ListItemText>
<div
style={{
height: "100%",
display: "flex",
justifyContent: "center",
gap: "5px",
alignItems: "center",
}}
>
<CalendarButton></CalendarButton>
<EditButton onClick={(e) => handleEditClick(e)}></EditButton>
<DeleteButton onClick={(e) => handleDelete(e)}></DeleteButton>
</div>
</ListItem>
))}
</StyledSortable>
</List>
디버그 코드를 넣어서 확인해 본 결과 새로고침시에 나오는 메시지가 워닝으로 끝났습니다.
✓ Starting...
✓ Ready in 1343ms
○ Compiling / ...
✓ Compiled / in 824ms (787 modules)
0. 현재 todos 상태: []
1. 드래그 앤 드롭 후 newState: []
Warning: newState가 비어있음
GET / 200 in 1043ms
이렇게 메시지가 떴고 문제는 이게 로딩 과정에서 떴다는 겁니다. 때문에 드래그앤 드롭 부분이 문제가 아니라 로딩 자체에서 문제가 발생하는 것이고 새로고침도 로딩 딜레이가 있다는 점에서 봤을때 배열의 길이가 0일경우 디폴트를 가져오는 방식이 아니라
리로드 시에 일단 업데이트를 하고 데이터가 비어있는 경우에만 설정을 하도록 바꿨습니다.
문제 체크를 위한 방법 1
리오더 과정에서 배열의 id가 바뀌나 체크
case 'reorder':
// 실제 할일이 있는 경우에만 순서 업데이트
if (data.updatedTodos && data.updatedTodos.length > 0) {
currentData.todos = data.updatedTodos;
console.log("updatedTodos 내용:", JSON.stringify(data.updatedTodos, null, 2));
console.log("currentData 내용:", JSON.stringify(currentData, null, 2));
}
// 데이터가 비어있는 경우에만 DEFAULT_TODOS 설정
if (!currentData.todos || currentData.todos.length === 0) {
currentData.todos = DEFAULT_TODOS;
console.log("DEFAULT_TODOS 적용됨");
}
break;
id가 기존과 동일하게 유지되어 문제 없음.
currentData 내용: {
"todos": [
{
"id": "5vm8Is35vg7VKZVDb8iQG",
"text": "ㅁㄹㄴ",
"isChecked": false,
"chosen": false,
"selected": false,
"filtered": false
},
{
"id": "gNgrspyijTK4Nf9nZEBL2",
"text": "1",
"isChecked": false,
"chosen": false,
"selected": false,
"filtered": false
},
{
"id": "HsBxw-6Gq6g08tAqt54Lf",
"text": "ㅁㄴㅇ",
"isChecked": false,
"chosen": false,
"selected": false,
"filtered": false
}
]
}
updatedTodos 내용: [
{
"id": "gNgrspyijTK4Nf9nZEBL2",
"text": "1",
"isChecked": false,
"chosen": false,
"selected": false,
"filtered": false
},
{
"id": "5vm8Is35vg7VKZVDb8iQG",
"text": "ㅁㄹㄴ",
"isChecked": false,
"chosen": false,
"selected": false,
"filtered": false
},
{
"id": "HsBxw-6Gq6g08tAqt54Lf",
"text": "ㅁㄴㅇ",
"isChecked": false,
"chosen": false,
"selected": false,
"filtered": false
}
]
문제 체크를 위한 방법 2
각 과정에 콘솔로그를 넣어서 어느 부분에서 문제가 발생하나 확인
const handleSetList = async (newState: ItemInterface[]) => {
console.log("0. 현재 todos 상태:", todos);
console.log("1. 드래그 앤 드롭 후 newState:", newState);
//디버깅 용
if (newState.length === 0) {
console.log("Warning: newState가 비어있음");
return; // 빈 배열일 경우 처리 중단
}
const updatedTodos = newState.map((item) => {
const existingTodo = todos.find((todo) => todo.id === item.id);
console.log("2. 매핑 중인 item:", item);
console.log("3. 찾은 existingTodo:", existingTodo);
return {
id: item.id,
text: (item as Todo).text,
isChecked: existingTodo ? existingTodo.isChecked : false,
chosen: false,
selected: false,
filtered: false,
} as Todo;
});
console.log("4. 변환된 updatedTodos:", updatedTodos);
setTodos(updatedTodos);
// JSON 파일 업데이트
try {
console.log("5. 서버로 보내기 직전 updatedTodos:", updatedTodos);
const response = await fetch(`${API_URL}/api/todos`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'reorder',
updatedTodos: updatedTodos
}),
});
console.log("6. 서버 응답 완료");
if (!response.ok) {
throw new Error('Failed to update todos order');
}
} catch (error) {
console.error("7. 에러 발생:", error);
setTodos(todos); // 에러 발생 시 원래 순서로 되돌리기
}
};
실제로 터미널에 Warning: newState가 비어있음 부분을 반환해서 실제로 로딩 과정에서 배열이 0인 것을 확인(로딩이 덜되서)
✓ Starting...
✓ Ready in 1319ms
○ Compiling / ...
✓ Compiled / in 785ms (787 modules)
0. 현재 todos 상태: []
1. 드래그 앤 드롭 후 newState: []
Warning: newState가 비어있음
GET / 200 in 1037ms
문제 해결을 위한 방법 1
리오더 부분의 로직을 변경 디폴트 배열의 id를 체크해서 필터하지 않고 그냥 가져오는 것으로 변경
<StyledSortable
list={todos}
setList={(newState) => {
if (isInitialMount.current) { // 최초 마운트인 경우
isInitialMount.current = false; // 플래그를 false로 변경
return; // 함수 실행 중단
}
// 이후의 호출에서만 실제 로직 실행
handleSetList(newState);
}}
animation={150}
ghostClass="sortable-ghost"
dragClass="sortable-drag"
handle=".drag-handle"
forceFallback={false}
>
이후 컴포넌트 안쪽에
const isInitialMount = useRef(true); // 초기화 플래그 생성
를 사용하고 최초 호출시 false로 변경하고 return으로 동작 중지