개발일지 - 카테고리 관리페이지 수정! (AJAX + SortableJS)
개발일지 · 2026.03.24 · 조회 1
기존 카테고리는 단일 카테고리 하나만 가지고 있는 카테고리에서 이제는 하위 카테고리를 지정할 수 있도록 수정했습니다.
관리자 페이지에서 카테고리를 추가, 수정, 제거 기능을 만들었고 여기에 메인 카테고리 그리고 하단에 서브 카테고리 형식으로 만들어 프로그래밍 카테고리 밑에 개발일지 카테고리를 넣을 수 있도록 수정했습니다. 카테고리 순서나 위치 지정은 마우스로 드래그 해서 순서를 변경할 수 있도록 했고 여기에 사용된 기능은 SortableJS 와 AJAX를 사용했습니다.
<ul id="category-list">
{% for category in categories %}
<li data-id="{{ category.id }}">
{{ category.name }}
</li>
{% endfor %}
</ul>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
<script>
new Sortable(document.getElementById('category-list'), {
animation: 150,
onEnd: function () {
let order = [];
document.querySelectorAll('#category-list li').forEach((el, index) => {
order.push({
id: el.dataset.id,
position: index
});
});
fetch("/manager/category/sort/", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRFToken": "{{ csrf_token }}"
},
body: JSON.stringify({ order: order })
});
}
});
</script>SortableJS를 활용한 카테고리 프론트 설정입니다.
<!-- 카테고리 리스트 -->
<ul id="category-list">
{% for category in categories %}
<!--
data-id:
각 카테고리의 고유 ID를 HTML에 심어둔다.
→ 드래그 후 서버로 순서 저장할 때 필요
-->
<li data-id="{{ category.id }}">
{{ category.name }}
</li>
{% endfor %}
</ul>
<!-- SortableJS 라이브러리 CDN -->
<!-- 드래그 정렬 기능을 쉽게 구현하기 위한 라이브러리 -->
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
<script>
/*
Sortable 객체 생성
첫 번째 인자:
→ 드래그 대상이 될 DOM 요소 (ul)
옵션 객체:
→ 동작 커스터마이징
*/
new Sortable(document.getElementById('category-list'), {
// 드래그 이동 시 애니메이션 속도 (ms)
animation: 150,
/*
onEnd 이벤트:
드래그가 끝났을 때 실행됨
→ 순서 변경이 완료된 시점
*/
onEnd: function () {
// 최종 순서를 담을 배열
let order = [];
/*
현재 화면에 보이는 순서대로 li를 순회
index = 현재 위치 (0부터 시작)
*/
document.querySelectorAll('#category-list li').forEach((el, index) => {
/*
각 요소의 정보를 객체로 저장
id: 카테고리 ID (data-id에서 가져옴)
position: 현재 위치 (정렬된 순서)
*/
order.push({
id: el.dataset.id,
position: index
});
});
/*
서버로 정렬 결과 전송 (AJAX)
URL:
/manager/category/sort/
방식:
POST (데이터 변경이므로 GET이 아닌 POST 사용)
*/
fetch("/manager/category/sort/", {
method: "POST",
headers: {
// JSON 데이터 전송 명시
"Content-Type": "application/json",
// Django CSRF 보호를 위한 토큰
"X-CSRFToken": "{{ csrf_token }}"
},
/*
body:
order 배열을 JSON 문자열로 변환하여 전송
예시 데이터:
{
"order": [
{"id": 3, "position": 0},
{"id": 1, "position": 1},
{"id": 2, "position": 2}
]
}
*/
body: JSON.stringify({ order: order })
});
// (선택) 성공/실패 처리도 추가 가능
// .then(response => response.json())
// .then(data => console.log(data))
// .catch(error => console.error(error));
}
});
</script>다음으로 Django View에서 순서를 저장하는 기능
import json
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from .models import Category
@csrf_exempt
def category_sort(request):
if request.method == "POST":
data = json.loads(request.body)
for item in data["order"]:
Category.objects.filter(id=item["id"]).update(order=item["position"])
return JsonResponse({"status": "ok"})여기서 @csrf_exempt 사용은 fetch를 사용하기 위해 반드시 사용해야 합니다.
CSRF (Cross-Site Request Forgery) 사용자가 로그인된 상태를 악용해서 의도치 않은 요청을 서버에 보내게 만드는 공격에 대비해서 서버가 토큰을 발급하고 클라이언트가 요청에 포함하여 서버 일치 여부를 확인함으로 쿠키 기반 인증 + POST 요청에서는 필수로 CSRF를 사용하는 것이 좋다.
기능의 추가 이후에는 UX 디자인을 꾸며주기만 하면 끝납니다.