드래그된 화면과 저장된 순서를 구분하기
블로그의 단일 카테고리를 상위·하위 구조로 바꾸면서 관리 화면에 SortableJS 드래그와 AJAX 저장을 붙였습니다. 프로그래밍 아래에 개발일지를 두고 순서를 옮기는 기능입니다. 당시 글은 브라우저의 DOM 순서만 저장하는 예제를 반복했고, csrf_exempt가 AJAX에 필수라는 잘못된 설명도 포함했습니다.
현재 코드는 CSRF 보호를 유지하고 staff 권한과 POST 방식을 검사합니다. 이 글에서는 실제 category_manage.html과 category_reorder 뷰를 기준으로 전달 형식, 실패 응답, 저장 확인 순서를 정리합니다. 드래그 애니메이션이 끝났다는 것과 서버가 저장했다는 것은 별개의 상태입니다.
parent와 형제 순서를 함께 전달한다
현재 Category에는 parent, order 필드가 있습니다. 클라이언트의 serializeTree는 각 목록의 직접 자식만 순회하고, 하위 목록으로 내려가며 부모 ID를 전달합니다. querySelectorAll('li')로 모든 자손을 한 번에 읽으면 하위 항목까지 루트 형제처럼 취급하기 쉽습니다.
가상의 카테고리 ID 101·102·103으로 표현하면 요청 모양은 다음과 같습니다. 운영 서버에 그대로 전송할 데이터가 아닙니다.
{
"items": [
{"id": "101", "parent_id": null, "order": 1},
{"id": "102", "parent_id": "101", "order": 1},
{"id": "103", "parent_id": null, "order": 2}
]
}
101과 103은 루트의 첫째·둘째이고, 102는 101 아래의 첫째입니다. order는 전체 트리에서 유일한 번호가 아니라 각 형제 목록의 순서입니다. 실제 템플릿은 index + 1을 쓰며, 예전 글의 position과 order 배열 대신 현재는 items, parent_id, order를 전송합니다.
fetch에도 CSRF 토큰이 필요하다
현재 URL 이름은 blog:manager:category_reorder이고 경로는 /manager/categories/reorder/입니다. 예전 /manager/category/sort/를 복사하면 현재 뷰와 연결되지 않습니다. 템플릿에서는 URL 이름을 해석해 fetch 대상으로 사용합니다.
fetch("{% url 'blog:manager:category_reorder' %}", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRFToken": "{{ csrf_token }}"
},
body: JSON.stringify({ items: treeData })
});
이 발췌는 Django가 렌더링하는 템플릿 안에서, treeData를 만든 뒤 사용하는 코드입니다. 그대로 외부 정적 JS 파일에 넣으면 템플릿 태그가 처리되지 않습니다. AJAX 때문에 CSRF를 해제할 필요가 없으며, 같은 출처 요청에 토큰을 전달하는 방식은 Django CSRF 안내에서 설명합니다. 토큰 검사는 staff 권한 검사를 대신하지 않습니다.
서버가 저장 전 확인하는 것
현재 뷰에는 staff_member_required, require_POST가 적용되어 있습니다. items 배열, 필수 값과 정수 변환, 중복·존재하지 않는 ID, 자기 자신을 부모로 지정하는 경우 등을 검사합니다. 부모 관계를 검사한 뒤 transaction 안에서 parent와 order를 묶어 갱신합니다.
| 확인 사례 | 기대할 결과 |
|---|---|
| 로그인한 staff의 정상 형식·유효한 토큰 | 200, status가 ok, 다시 읽은 순서 일치 |
| staff가 GET 요청 | 405, 변경 없음 |
| staff가 토큰 없이 POST | CSRF 403, 변경 없음 |
| 중복 ID·없는 ID | 400, 변경 없음 |
| 자기 자신을 부모로 지정 | 400, 변경 없음 |
| 익명·일반 회원 | 관리자 로그인으로 이동, 변경 없음 |
같은 출처 요청에서 세션이 만료되면 fetch가 로그인 HTML을 받은 뒤 JSON 해석에 실패할 수도 있습니다. “네트워크 오류”라는 표시만 보고 저장됐다고 판단하지 말고 상태 코드와 응답 형식을 같이 확인합니다. 기존 프런트엔드는 저장 실패 안내를 보여주지만, 실패한 드래그 위치를 자동으로 원래대로 되돌리는 처리는 없습니다. 다시 불러와 서버 값을 확인해야 합니다.
반복 드래그와 복잡한 트리는 추가 검토 대상
현재 템플릿은 SortableJS 1.15.0을 명시하고 onEnd에서 저장을 호출합니다. 이벤트와 중첩 목록 옵션은 SortableJS 공식 저장소에서 확인할 수 있습니다. 라이브러리의 정렬 기능이 서버 저장의 순서나 무결성을 보장하는 것은 아닙니다.
여러 번 빠르게 움직일 때 요청 순서가 뒤바뀌는 상황, 동시 편집, 복잡한 부모 교환은 따로 검토해야 합니다. 현재 코드에 요청 버전 비교나 자동 복구가 모두 구현됐다고 설명하지 않습니다. 기본 사례는 운영 카테고리를 움직이는 대신 테스트 DB의 가상 트리로 확인하고, 화면에서는 저장 완료 표시 뒤 새로고침한 결과까지 일치하는지 봅니다.
카테고리를 지울 때의 글 보존 규칙은 정렬과 별개입니다. 현재 모델의 삭제 보호를 함께 확인하면 순서 저장과 삭제 정책을 구분할 수 있습니다.
댓글 0
로그인 없이 궁금한 점이나 의견을 남겨주세요.
아직 댓글이 없습니다. 첫 의견을 남겨주세요.