실제로는 이런 메서드들이 있어요:
| 메서드 | 하는 일 | 예시 |
|---|---|---|
| GET | 데이터 조회 | 게시글 목록 보기 |
| POST | 데이터 생성 | 게시글 작성 |
| PUT | 데이터 전체 수정 | 게시글 전체 교체 |
| PATCH | 데이터 일부 수정 | 제목만 수정 |
| DELETE | 데이터 삭제 | 게시글 삭제 |
HTML <form> 태그가 GET, POST만 지원하기 때문입니다
<!-- HTML form은 이 두 가지만 가능 -->
<form method="GET"> ✅
<form method="POST"> ✅
<form method="DELETE"> ❌ 브라우저가 지원 안 함
그래서 Django 기초 단계에서는 이렇게 우회했던 거예요:
삭제 → POST /study/3/delete/ (원래는 DELETE 써야 함)
수정 → POST /study/3/update/ (원래는 PUT/PATCH 써야 함)
그때는 모든 메서드를 제대로 사용합니다
GET /api/posts/ → 목록 조회
POST /api/posts/ → 새 글 생성
GET /api/posts/3/ → 3번 글 조회
PUT /api/posts/3/ → 3번 글 전체 수정
PATCH /api/posts/3/ → 3번 글 일부 수정
DELETE /api/posts/3/ → 3번 글 삭제
Django 기초 (HTML 렌더링)
→ GET, POST만 사용 (폼의 한계)
Django + DRF (API 서버)
→ GET, POST, PUT, PATCH, DELETE 모두 사용
→ 이미지 4번처럼 views를 두 개로 분리하는 이유가 이것!
지금 이미지에서 api_views.py 와 templates_views.py 를 나누는 것도 바로 이 차이 때문입니다. API는 모든 메서드를 쓰고, 템플릿은 GET/POST만 쓰니까요.
| GET | POST | |
|---|---|---|
| 용도 | 데이터 조회 | 데이터 전송/저장 |
| 데이터 위치 | URL에 노출 | Body에 숨겨짐 |
| 보안 | 낮음 | 높음 |
| 데이터 크기 | 제한 있음 | 제한 없음 |
| 캐싱 | 가능 | 불가능 |
# GET - URL에 데이터가 보임
https://example.com/search?keyword=python&page=1
# POST - URL에 데이터가 안 보임
https://example.com/login
(아이디/비밀번호는 숨겨져서 전송)
def my_view(request):
# GET 요청 - 데이터 조회할 때
if request.method == 'GET':
posts = Post.objects.all()
return render(request, 'list.html', {'posts': posts})
# POST 요청 - 데이터 저장할 때
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
form.save() # DB 저장
GET → 검색, 목록 조회, 상세 보기
POST → 로그인, 회원가입, 게시글 작성, 수정, 삭제
<!-- GET 방식 -->
<form method="GET">
<input type="text" name="keyword">
</form>
<!-- POST 방식 -->
<form method="POST">
{% csrf_token %} <!-- Django 보안 토큰 필수! -->
<input type="text" name="title">
</form>
💡 조회는 GET, 변경은 POST 로 기억하세요!
from django.shortcuts import get_object_or_404, redirect, render
from .forms import StudyForm
from .models import Study
def study_list(request):
studies = Study.objects.all()
total_count = studies.count()
total_minutes = sum(study.total_minutes for study in studies)
total_hours = total_minutes // 60
remain_minutes = total_minutes % 60
context = {
'studies': studies,
'total_count': total_count,
'total_hours': total_hours,
'remain_minutes': remain_minutes,
}
return render(request, 'study/study_list.html', context)
def study_create(request):
form = StudyForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
form.save()
return redirect('study:list')
return render(request, 'study/study_form.html', {
'form': form,
'page_title': '공부 기록 작성',
})
def study_detail(request, pk):
study = get_object_or_404(Study, pk=pk)
return render(request, 'study/study_detail.html', {
'study': study,
})
def study_update(request, pk):
study = get_object_or_404(Study, pk=pk)
form = StudyForm(request.POST or None, instance=study)
if request.method == 'POST' and form.is_valid():
form.save()
return redirect('study:detail', pk=study.pk)
return render(request, 'study/study_form.html', {
'form': form,
'page_title': '공부 기록 수정',
'study': study,
})
def study_delete(request, pk):
study = get_object_or_404(Study, pk=pk)
if request.method == 'POST':
study.delete()
return redirect('study:list')
return render(request, 'study/study_confirm_delete.html', {
'study': study,
})
여기에서 언존헌 석재 흐름이라고 하는데.. post가 왜 안전한 삭제 흐름이니
핵심은 "실수로 삭제되는 상황을 막는다" 는 것입니다.
# 만약 GET /study/3/delete/ 로 삭제가 된다면?
<img src="/study/3/delete/"> 같은 태그 하나로도 삭제 가능 (CSRF 공격)1단계: GET /study/3/delete/
→ "정말 삭제할까요?" 확인 페이지만 보여줌
→ 아직 아무것도 안 지워짐
2단계: POST /study/3/delete/ (버튼 클릭 시)
→ 그제야 실제 삭제 실행
POST 요청은 폼을 직접 제출해야만 발생하기 때문에:
def study_delete(request, pk):
study = get_object_or_404(Study, pk=pk)
if request.method == 'POST': # POST일 때만 삭제
study.delete()
return redirect('study:list')
# GET이면 → 확인 페이지 렌더링 (삭제 안 함)
return render(request, 'study/study_confirm_delete.html', {
'study': study,
})
즉, 의도를 한 번 더 확인하는 장치가 POST 2단계 흐름의 핵심입니다.
models.py는 Django에서 가장 먼저 작성하는 파일이에요. "DB 테이블의 설계도"라고 생각하면 됩니다. 전체 구조를 단계별로 살펴볼게요.5단계로 나눠서 볼 수 있게 만들었어요. 탭을 클릭하거나 이전/다음 버튼으로 넘겨가면서 보세요.
makemigrations / migrate는 세트다 — models.py를 수정할 때마다 반드시 두 명령어를 실행해야 DB에 반영됩니다.
python manage.py makemigrations # 변경사항을 감지해서 설계도 생성
python manage.py migrate # 실제 DB에 적용
__str__ 는 꼭 써두자 — 없으면 관리자 페이지에서 모든 객체가 Post object (1), Post object (2)... 처럼 보여서 뭐가 뭔지 구분이 안 됩니다.
필드 옵션 두 개만 기억하자 — blank=True는 폼에서 빈 값 허용, null=True는 DB에서 NULL 허용. 문자열 필드는 blank=True만, 나머지는 null=True, blank=True 함께 씁니다.
궁금한 단계가 있으면 더 깊이 파고들어 드릴게요!