Lina's Toolbox

Django MTV 사용하기 CR (Create, Read) 본문

스파르타 내일 배움 캠프 AI 웹개발 과정/Django framework

Django MTV 사용하기 CR (Create, Read)

Woolina 2024. 8. 29. 11:04

 

 

이제 우리가 배운 ORM을 쉘이 아닌 , 장고 코드 안에 직접 작성해보자!

 

실습 준비

/articles/ 로 들어오면 아래의 articles.html 템플릿이 랜더링되어 보이도록 준비

1. urls.py에 추가

path("", views.articles, name="articles"),

 

2. views.py에 추가

def articles(request):
		return render(request, "articles.html")

 

3. articles.html

{% extends 'base.html' %}

{% block content %}
		<h1>Articles</h1>
		
{% endblock content %}

 

 

조회

데이터베이스에서 모든 아티클을 조회해서 /articles/에서 볼 수 있도록 해봅시다!

 

1. view에서 model에 접근해 모든 아티클 가져오기

 

2. view에서 가져온 아티클을 template으로 넘기기

airticles의 views.py 변경

from .models import Article
...

def articles(request):
    articles = Article.objects.all()
    context = {
        "articles": articles,
    }
    return render(request, "articles.html", context)
    
 ...

from .models → . 현재 위치 의미 중에  import Article → (다 가져오는게 아니고) article 클래스를 가져온다!

 

3. template에서 넘어온 context 보여주기

articles.html 변경

{% extends "base.html" %}

{% block content %}
    <h1>Articles</h1>

    <ul>
        {% for article in articles %}
            <li>
                <div>글 번호 : {{ article.id }}</div>
                <div>글 제목 : {{ article.title }}</div>
                <div>글 내용 : {{ article.content }}</div>
                <br>
            </li>
        {% endfor %}
    </ul>

{% endblock content %}

{{ articles.id }} = {{ articles.pk }} 똑같이 작동(pk(primary key)는 기본키로 id와 동일)

 

4. view에서 템플릿을 랜더링해서 리턴

 

 

새로운 글 작성

새로운 글을 쓰고싶다면, shell을 열고 작성하면 되지만 그러면 너무 불편하겠죠?

 

클라이언트에서 서버에 데이터를 전송하여 저장하면 편해요!

⇒ form을 이용하면 됩니다!

 

/articles/new/로 들어오면 아래의 화면이 보이도록 작성해 보세요!

→ 작성된 form을 보내는곳은 /articles/create/입니다.

 

1. articles의 urls.py에 추가

from django.urls import path
from . import views

urlpatterns = [
    path("", views.articles, name="articles"),
    path("new/", views.new, name="new"),
    path("create/", views.create, name="create"),
    path("hello/", views.hello, name="hello"),
    path("data-throw/", views.data_throw, name="throw"),
    path("data-catch/", views.data_catch, name="catch"),
]

 

 

2. articles의 views.py에 추가

...
def new(request):
    return render(request, "new.html")
...

 

 

3. articles의 templates에 new.html 만들기

{% extends "base.html" %}


{% block content %}
<h1>New Article</h1>

<form action="{% url 'create' %}" method="GET">
    <label for="title">제목</label>
    <input type="text" name="title" id="title"><br><br>

    <label for="content">내용</label>
    <textarea name="content" id="content" cols="30" rows="10"></textarea><br><br>

    <button type="submit">저장</button>
</form>


{% endblock content %}

 

 

  1. create view에서 데이터 넘겨받기
  2. 넘겨받은 데이터로 새로운 데이터 생성하기
def create(request):
    # GET 방식으로 전달된 데이터를 받아서
    title = request.GET.get("title")
    content = request.GET.get("content")

    # 받은 데이터를 Article 모델을 이용해서 저장
    article = Article(title=title, content=content)
    article.save()
    context = {
        "article": article,
    }
    return render(request, "create.html", context)

 

3. 글 작성이 잘되었다는 화면(create.html) 보여주기

def create(request):
    # GET 방식으로 전달된 데이터를 받아서
    title = request.GET.get("title")
    content = request.GET.get("content")

    # 받은 데이터를 Article 모델을 이용해서 저장
    article = Article(title=title, content=content)
    article.save()
    context = {
        "article": article,
    }
    return render(request, "create.html", context)

🤔 만약 최신순으로 보여주고 싶다면 어떻게 하면 좋을까요?

created_at 을 사용하거나 pk를 사용하여 정렬을 해주면 됩니다!

 

→ order_by(기준필드) 는 오름차순이 디폴트

→ 내림차순은 기준필드 앞에 - 붙여준다. order_by(-기준필드)

 

 

HTTP Method

GET & POST

GET

  • 말그래도 원하는 리소스를 가져오는데에 사용합니다.
  • 생성할 때도 사용할 수 있지만(로직상 문제는 없지만), 리소스 조회용으로 사용하자는 개발세계에서의 약속입니다.
  • DB에 변화를 주지 않는 요청임을 의미합니다.
  • Read에 해당합니다.

POST

  • 서버로 데이터를 전송할 때 사용합니다.
  • 특정 리소스를 생성 혹은 수정하기 위해 사용합니다.
  • DB에 변화를 주는 요청임을 의미합니다.
  • Create, Update, Delete에 해당합니다.

 

get 방식은 데이터를 url에 담아 보내기 때문에(쿼리스트링)
→ 바디는 비어있다.
→ 민감한 정보라면 post로 보내는 것이 더 적절!
(그래도 암호화 되는 것은 아니라서 관리자도구를 열면 보이기는 하지만 그래도...)

body에 담아 보내면 훨씬 더 많은 정보를 담아 보낼 수 있음!

 

 

 

articles/templates/new.html

⇒ 일단, 여기서 GET만 POST로 바꿔보면 어떻게 될까요?

⚠️ 오류가 발생합니다!

 

사이트간 요청 위조 CSRF(Cross-Site-Request-Forgery)

CSRF(Cross-Site-Request-Forgery)

  • 유저가 실수로 해커가 작성한 로직을 따라 특정 웹페이지에 수정/삭제등의 요청을 보내게 되는 것입니다.→ 좋은 사이트 서버 입장에서는 어디서 온 요청인지 구분이 필요해요. ⇒ 즉, 정말 유저가 의도한 요청인지 검증이 필요합니다!
  • → 아주나쁜사이트.com → 요청 → 좋은사이트.com

 

🤔 Q) Https 쓰면 막을 수 있지 않아요?
아닙니다 ! CSRF 공격은 인증이 된 상태에서 해커의 의도대로 서버에 요청을 보내게 되는 것으로 HTTPS가 데이터를 암호화하고 중간에서의 공격을 방지하는 것과는 다른 맥락에서 발생합니다.
즉, 이미 유저의 브라우저에서 인증된 요청을 전송하는것으로 추가적인 보안 조치가 필요합니다. 

 

  • CSRF Token을 사용해서 막을 수 있습니다.
    • 유저가 서버에 요청을 보낼 때 함께 제공되는 특별한 토큰 값으로, 이 토큰은 사용자의 세션과 연결되어 있습니다. 요청이 전송될 때, 함께 제출되며 서버는 요청을 받을 때 이 토큰을 검증하여 요청이 유효한지 확인하는 방식으로 CSRF을 방지합니다.
    • 쉽게말해 서로 알아볼 수 있는 임의의 비밀번호를 만들어서 주고 받는다고 생각해주세요!
    • 일반적으로 GET을 제외한 데이터를 변경하는 Method에 적용합니다.
    → 세션에 대해서는 뒤에서 조금 더 살펴볼 예정이에요! 👀

 

Django는 모든게 준비되어 있습니다!

  • Django에서는 쉽게 CSRF Token 방식을 구현할 수 있게 template tag로 제공합니다.

get방식으로 받고 create해도 문제는 없지만,

그냥 개발자들 사이의 약속임.

지금까지 우리 코드는 get으로 받아서 db에 create를 해주고 있으니

안좋은 코드였던 것!

 

 

 

👟 이것을 적는 것만으로 어떻게 동작하는 걸까요?
⇒ settings.py의 미들웨어를 봐주세요!

request가 들어오면 setting.py의 Middleare에 있는 것을 위에서부터 하나씩 쭉 통과하며 하나씩 다 처리하며 장고로 들어온다.

그리고 장고에서 처리한뒤 다시 미들웨어를 하나씩 쭉 다 통과하며 response가 나간다.

 

 

POST 방식으로 데이터 주고 받기

new.html

{% extends "base.html" %}


{% block content %}
<h1>New Article</h1>

<form action="{% url 'create' %}" method="POST">
    {% csrf_token %}
    <label for="title">제목</label>
    <input type="text" name="title" id="title"><br><br>

    <label for="content">내용</label>
    <textarea name="content" id="content" cols="30" rows="10"></textarea><br><br>

    <button type="submit">저장</button>
</form>


{% endblock content %}

 

articles/views.py

...
def create(request):
    # POST 방식으로 전달된 데이터를 받아서
    title = request.POST.get("title")
    content = request.POST.get("content")

    # 받은 데이터를 Article 모델을 이용해서 저장
    article = Article(title=title, content=content)
    article.save()
    context = {
        "article": article,
    }
    return render(request, "create.html", context)
...

 

 

POST 방식 vs GET 방식

  • HEADER
    HTTP 전송에 필요한 모든 부가정보를 담고있는 부분으로, 메시지 크기, 압축, 인증, 요청 클라이언트(웹 브라우저) 정보, 서버 애플리케이션 정보, 캐시 관리 정보 등등 을 포함하고 있습니다.
  • BODY
    실제 전송할 데이터가 담겨있는 부분으로, 데이터가 없다면 비어있게 됩니다.

  • HTTP Request는 어떻게 생겼을까?

 📎 GET /url/ HTTP1.1
GET(어떤 방식으로 보내는 건지)/ url(어떤 url로 보내는지)?/http(어떤 약속(프로토콜)로 보내는지)


HOST: aidenlim.dev
HOST 사이트주소(도메인)

(HEADER)


(공백)


(BODY)




  • HTTP Response

 📎 HTTP1.1 200 OK

Date: ….

(HEADER)


(공백)


{ status:200, data: “하잉” }

(BODY)


 

 

📌 요약

  • GET은 데이터를 URL에 담아보내고 POST는 BODY에 담아 보내요!
  • 따라서 GET은 데이터 전송에 한계가 있으나, POST는 그렇지 않아요!
  • POST방식으로 데이터를 전송할 때는 CSRF Token이 필요해요!

 

 

실제로 보면

CSRF 토큰이 SET 되어 있는채로 보내게 됩니다!

 

관리자도구에서 보면 우리가 설정해준 적 없는데  hidden 타입으로 crsf 들어가있다! value=에는 난수의 토큰값이 들어가있다.

우린 넣어준 적이 없는데, 서버가 넣어서 클라이언트로 보내준것!

→ 이 form, 정확히 이 사이트에서 보내준 request만 처리할 수 있게함

토큰값(value)는 그때그때 달라지기 때문에 (그때그때 서버가 보내줌) 토큰 탈취가 불가능하다. 

 보안상 안전!