본문 바로가기
Study/FrontEnd

React(1)- Django + React.js Application

by 왕방개 2024. 1. 30.

**Django + React.js Application
1.Web Browser 와 Web Application Server 가 통신하는 방법
1)ajax
=>Asynchronous Javascript XML(eXtensible Markup Language)의 약자로 Javascript의 비동기 방식을 이용해서 XML을 가져오는 것
=>최근에는 데이터 포맷에 상관없이 비동기적으로 데이터를 가져오는 기술
=>동기: 하나의 요청이 발생하면 다른 요청은 전부 대기하고 요청을 처리할 때 까지 기다린 후 다음 요청을 처리하는 방식
=>비동기: 하나의 요청이 발생해서 처리 중이더라도 다른 요청을 처리할 수 있는 방식

2)구현 방법
=>XMLHttpRequest 객체 생성
=>처리 결과를 받을 이벤트 리스너를 등록(콜백을 등록한다고 하기도 합니다.)
=>서버에게 전송할 데이터(파라미터)가 있으면 데이터를 준비
=>open 메서드를 이용해서 연결 준비
=>send 메서드를 이용해서 요청을 전송

 

3)전체 데이터 가져오기 구현
=>urls.py 파일에 하나의 요청을 처리하기 위한 url을 등록

from django.contrib import admin
from django.urls import path

#url의 처리를 다른 모듈에게 위임하고자 할 때 사용하는 패키지를 import
from django.urls import include

from apiapp import views

#path("example/", include("apiapp.urls")), 문장은
#example로 시작하는 요청이 오면 apiapp 애플리케이션의 urls.py 파일에 처리를 위임
urlpatterns = [
    path("admin/", admin.site.urls),
    path("example/", include("apiapp.urls")),
    path("ajax/", views.ajax)
]
#ajax/ 라는 요청이 왔을 때 apiapp.views 파일의 ajax 함수가 처리하도록 설정



=>apiapp 의 views.py 파일에 요청을 처리하기 위한 ajax 함수를 생성하고 작성

from django.shortcuts import render
#요청이 오면 templates 디렉토리의 ajax.html을 출력
def ajax(request):
    return render(request, "ajax.html")

 

=>apiapp 디렉토리에 templates 디렉토리를 생성하고 그 안에 ajax.html 파일을 생성

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ajax</title>
</head>
<body>
    
</body>
<script>
    //ajax 요청 객체 생성
    let request = new XMLHttpRequest();
    //요청을 준비
    request.open('GET', '../example/books/')    
    //요청 - 데이터가 없는 경우는 따옴표를 대입
    request.send('')
    //서버에서 요청이 전송되면 호출될 콜백 메서드를 등록
    request.addEventListener('load', function(e){
        //데이터를 확인: 텍스트 와 JSON은 responseText
        //XML인 경우는 responseXML
        //JSON 문자열을 직접 출력
        //alert(request.responseText);
    })
</script>

</html>


=>javascript에서 JSON 데이터 사용
결과 = JSON.parse(JSON 문자열): 배열이나 객체로 변환해서 리턴
결과 = JSON.stringify(자바스크립트 객체): JSON 문자열로 변환해서 리턴

=>ajax.html 파일을 수정해서 각 데이터의 bid 와 title을 출력

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ajax</title>
</head>
<body>
    
</body>
<script>
    //ajax 요청 객체 생성
    let request = new XMLHttpRequest();
    //요청을 준비
    request.open('GET', '../example/books/')    
    //요청 - 데이터가 없는 경우는 따옴표를 대입
    request.send('')
    //서버에서 요청이 전송되면 호출될 콜백 메서드를 등록
    request.addEventListener('load', function(e){
        //데이터를 확인: 텍스트 와 JSON은 responseText
        //XML인 경우는 responseXML
        //JSON 문자열을 직접 출력
        //alert(request.responseText);

        //받아온 문자열을 JavaScript 객체로 변환
        let data = JSON.parse(request.responseText);

        //자바스크립트는 for ~ in 으로 배열을 순회하면 인덱스가 리턴됩니다.
        for (idx in data){
            alert(data[idx].bid + data[idx].title)
        }
    })
</script>

</html>



4)데이터 삽입 구현 - POST 방식에 데이터 전송
=>ajax.html 파일의 스크립트만 수정해서 확인

<script>
    //ajax 요청 객체 생성
    let request = new XMLHttpRequest();

    //POST 방식에서 전송할 데이터를 생성
    let formdata = new FormData();
    formdata.append('bid', 4);
    formdata.append('title', '손자병법');
    formdata.append('author', '손무');
    formdata.append('publisheddate', '2024-01-29')

    //요청을 준비하고 전송
    request.open("POST", "../example/books/");

    //데이터 변환
    request.setRequestHeader("Content-type", 
        "application/x-www-form-urlencoded");
    let param = "";
    for(let pair of formdata.entries()){
        param += pair[0] + '=' + pair[1] + '&';
    }

    request.send(param);

    request.addEventListener("load", function(e){
        alert(request.response.Text);
    });

</script>



5)fetch API
=>ajax 는 콜백 방식을 이용하는데 fetch api 는 프로미스 기반의 API
=>콜백 방식은 별도의 함수를 만들어서 연결을 하지만 프로미스 기반에서는 .then 이나 .catch를 연달아 작성해서 처리합니다.
=>도큐먼트: https://developer.mozilla.org/en_US/docs/Web/API/fetch
=>ajax.html 파일의 스크립트를 수정해서 전체 데이터 가져오기 구현

<script>
    //첫번째 then 에서는 어떤 데이터인지만 알려주면 파싱을 해서 다음 then 에게 넘겨줍니다.
    //다음 then에서 data를 사용하면 됩니다.
    //여러 개의 문장을 수행해야 하는 경우 { }로 감싸면 됩니다.
    //예외에 대한 처리는 catch로 수행할 수 있고 catch의 매개변수는 예외 객체 입니다.
    fetch('../example/books/')
    .then((response) => response.json())
    .then((data) => console.log(data))
    .catch((error) => console.log(error));
</script>



6)axios 라이브러리
=>Promise 기반 HTTP 클라이언트 기반의 비동기 데이터 요청을 위한 라이브러리
=>동일한 코드로 node.js 기반의 서버 와 클라이언트에서 동일한 기능을 수행하는 것이 가능
=>도큐먼트: https://axios-http.com/kr/docs/intro
=>최근에 많이 사용
=>스크립트 수정

<!-- axios 라이브러리를 사용하기 위한 링크 설정 -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
    axios.get('../example/books/')
    .then((response) => console.log(response.data))
    .catch((error) => console.log(error));
</script>


=>일반적으로 ajax 보다는 fetch api 가 fetch api 보다는 axios 라이브러리를 사용하는 것이 코드가 간결합니다.
그렇지만 axios는 표준 기술이 아니기 때문에 외부에 영향을 받게 됩니다.

7)Web Socket
=>ajax 나 fetch api 는 무상태(이전의 상태를 기억하지 않음) 방식이며 연결을 유지하지도 않습니다.
ajax 나 fetch api를 이용해서는 채팅처럼 계속해서 데이터를 주고 받는 서비스를 구현하기가 어렵습니다.
ajax 나 fetch api를 이용해서 채팅을 구현하면 오버헤드가 너무 큼

8)Web Push
=>클라이언트의 요청이 없어도 서버가 클라이언트에게 데이터를 전송하는 것
=>예전에는 ajax polling을 이용해서 구현했지만 지금은 Web Push(Server Side Event)를 이용해서 구현합니다.
=>부인 봉쇄를 위해서 구현하는 경우가 있습니다.
=>모바일의 경우 APNS(Apple Push Notification Service) 나 FCM(Firebase Cloud Messaging)이 UDP로 구현되어 있기 때문에 부인봉쇄에 사용할 수 없습니다.

9)csrf
=>Cross-Site Request Forgery 의 준말로 사용자가 의도하지 않은 공격을 시도하는 행위
=>이 공격을 막는 방법은 토큰을 발급해서 토큰이 있는 사용자만 서버에게 요청을 할 수 있도록 하면 됩니다.
=>장고의 경우는 클라이언트가 외부에서 만들어진 경우 csrf 설정을 필수로 요구합니다.


10)SOP(Same Origin Policy - 동일 출처 정책)
=>자바스크립트는 기본적으로 동일 도메인 내에서만 요청이 가능
도메인이 다르면 요청을 못합니다.
=>SOP가 적용되지 않는 태그
<img>, <video>, <a> <audio>, <embed>, <object>
=>ajax 나 fetch api는 SOP가 적용되기 때문에 도메인 다른 서버에 데이터 요청이 안됩니다.
=>서버에 접속할 수 있는 클라이언트의 도메인을 등록해주거나 react 와 같은 클라이언트 사이드 애플리케이션 프레임워크에 접속할 수 있는 서버의 도메인을 등록해주어야 합니다.
근본적으로는 서버에서 작업을 해야 하지만 react의 경우는 클라이언트에 설정을 하면 node로 만들어진 서버의 프록시 설정을 이용해서 가능하도록 해줍니다.

 

2.ToDoService 구현
1)구현
=>Django Framework를 이용해서 Back End Application을 구현하고 react.js를 이용해서 Front End Application을 구현

=>구현 기능
ToDo 생성
Todo 목록 보기
ToDo 수정
ToDo 삭제

=>서버 API 테스트: postman 이용

2)데이터베이스에 접속해서 사용할 데이터베이스를 결정
=>adam 데이터베이스 이용

3)Back End Application 생성
=>가상환경 생성: python -m venv myvenv

=>가상환경 활성화: myvenv\Scripts\activate

=>필요한 패키지 설치: pip install django mysqlclient

=>프로젝트 생성: django-admin startproject todoproject .
주의할 점은 마지막에 . 을 추가하지 않으면 todoproject 라는 디렉토리가 생성되고 그 안에 todoproject 라는 프로젝트가 생성됩니다.
이 경우는 todoproject 안에서 작업을 수행해야 합니다.

=>애플리케이션 생성: python manage.py startapp todoapplication

4)기본 설정 수정 - settings.py 에서 작업
=>애플리케이션 등록

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "todoapplication"
]


=>데이터베이스 접속 정보 수정

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "NAME": "****",
        "USER": "*****",
        "PASSWORD": "*****",
        "HOST": "127.0.0.1",
        "PORT": "3306"
    }
}


=>시간 대역 설정
TIME_ZONE = "Asia/Seoul"

=>설정이 제대로 되어 있는지 확인하기 위해서 서버를 구동해보고 웹 브라우저에서 접속
python manage.py runserver 0.0.0.0:80

5)모델 생성
=>모델 설계
id: 기본키이고 자동 입력
userid: 유저 아이디로 문자열이고 50글자
title: 해야 할일로 문자열이고 100글자
done: 수행여부를 나타내는 것으로 불린으로 기본값은 False
regdate: 생성 날짜로 기본값으로 오늘 날짜를 설정
moddate: 수정 날짜로 선택적으로 입력

=>models.py 파일에 모델을 생성

from django.db import models

class Todo(models.Model):
    id = models.AutoField(primary_key=True)
    userid = models.CharField(max_length=50)
    title = models.CharField(max_length=100)
    done = models.BooleanField(default=False)
    regdate = models.DateTimeField(auto_now_add=True)
    moddate = models.DateTimeField(null=True)


=>django application 의 데이터베이스 설정 내용을 데이터베이스에 반영
python manage.py makemigrations
python manage.py migrate

=>확인
데이터베이스에 접속해서 desc 앱이름(todoapplication)_모델이름(todo);

6)Back End CRUD 처리: 하나의 클래스로 모든 요청을 처리
=>하나의 클래스로 하나의 모델에 대한 모든 처리를 수행할 때 주의해야 할 부분은 get 입니다.
get의 구현은 2가지 정도인데 하나는 전체 데이터 보기 와 상세보기 입니다.
2개의 처리를 모두 해야 하는 경우는 하나의 클래스로 처리를 하려면 함수 내에서 분기를 하거나 2개 이상의 클래스로 나누어서 처리를 합니다.
이번의 경우는 전체보기만 존재하므로 분기를 할 필요가 없습니다.

7)삽입 요청 처리
=>애플리케이션의 views.py 파일에 요청을 처리하는 클래스를 생성

#클래스형 View를 만들기 위해서 import
from django.views import View

#csrf 설정을 위한 import
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator

#데이터 모델을 가져오기 위한 import
from .models import Todo

#날짜 와 시간을 사용하기 위한 import
from datetime import datetime

#JSON으로 응답을 하기 위한 import
from django.http import JsonResponse

#클라이언트의 정보를 JSON 문자열로 만들기 위한 import
import json


#Todo 클래스의 인스턴스를 디셔너리로 변환해주는 함수
#JsonReponse로 JSON 데이터를 출력하고자 하면 빠르게 JSON 문자열로 만들때
#dict 만 가능하기 때문
#dict 는 JSON 문자열과 표현 방법이 같기 때문입니다.
#python application 개발자가 될거라면 함수를 만들 때 매개변수에 자료형을 기재하고
#return type을 기재하는 형태로 만들어주는 것이 좋습니다.
def todoToDictionary(todo:Todo) -> dict:
    result = {
        "id":todo.id,
        "userid":todo.userid,
        "title":todo.title,
        "done":todo.done,
        "regdate":todo.regdate,
        "moddate":todo.moddate
    }
    return result


#csrf 설정으로 클라이언트 애플리케이션을 별도로 구현하는 경우 필수 
@method_decorator(csrf_exempt, name='dispatch')
class TodoView(View):
    def post(self, request):
        #클라이언트의 데이터를 json 형식으로 가져오기
        request = json.loads(request.body)

        #userid 와 title 매개변수 값을 읽어서 저장
        #클라이언트에서 입력해주는 데이터만 읽어오면 됩니다.
        userid = request["userid"]
        title = request["title"]

        #모델 인스턴스 생성
        todo = Todo()
        todo.userid = userid
        todo.title = title

        todo.save()

        #userid 와 일치하는 데이터만 추출
        todos = Todo.objects.filter(userid=userid)

        #결과 리턴
        return JsonResponse({"list":list(todos.values())})



=>urls.py 파일에 url 과 요청 처리 클래스를 연결

from django.contrib import admin
from django.urls import path

from todoapplication import views

urlpatterns = [
    path("admin/", admin.site.urls),
    path("todo", views.TodoView.as_view())
]



=>서버 실행
rest_framework를 사용하지 않아서 GET 방식은 테스트가 가능하지만 나머지 방식은 테스트가 불가능

=>postman를 이용해서 삽입을 테스트
postman을 설치하고 로그인을 수행

POST 방식으로 127.0.0.1/todo 그리고 body 에 row에 {"userid":"userid", "title":"내용"} send를 눌러서 데이터가 전달되는지 확인


8)전체 데이터 가져오기 처리
=>views.py 의 클래스 안에 get 메서드 추가
    def get(self, request):
        #GET 방식에서 userid 라는 파라미터를 읽기
        userid = request.GET["userid"]
        todos = Todo.objects.filter(userid=userid)
        return JsonResponse({"list":list(todos.values())})

=>GET 의 경우는 브라우저에 직접 URL을 입력해도 됩니다.
127.0.0.1/todo?userid=아이디 로 확인

=>목록을 리턴해야 하는 경우 리스트를 리턴하도록 하는데 데이터가 없는 경우에는 빈 리스트를 리턴하도록 해야 합니다.
목록을 제외한 하나의 데이터를 리턴할 때는 데이터가 없는 경우 None(null)을 리턴합니다.

9)데이터 수정(PUT) 처리
=>데이터 수정을 할 때 넘겨줄 데이터: userid, id, done 을 넘겨줌

=>views.py 파일의 클래스 안에 put 메서드를 작성

    def put(self, request):
        #클라이언트의 데이터를 json 형식으로 가져오기
        request = json.loads(request.body)

        #userid 와 id 그리고 done 값을 읽어서 저장
        #클라이언트에서 입력해주는 데이터만 읽어오면 됩니다.
        userid = request["userid"]
        id = request["id"]
        done = request["done"]

        #수정할 데이터를 찾아옵니다.
        todo = Todo.objects.get(id=id)
        #수정할 내용을 대입
        todo.done = done
        #save는 기본키의 값이 있으면 수정이고 없으면 삽입입니다.
        todo.save()

        #userid 와 일치하는 데이터만 추출
        todos = Todo.objects.filter(userid=userid)

        #결과 리턴
        return JsonResponse({"list":list(todos.values())})


=>POSTMAN 에서 수정을 테스트


10)삭제 처리
=>views.py 파일의 클래스 안에 메서드 구현

   def delete(self, request):
        #클라이언트의 데이터를 json 형식으로 가져오기
        request = json.loads(request.body)

        #userid 와 id 값을 읽어서 저장
        #클라이언트에서 입력해주는 데이터만 읽어오면 됩니다.
        userid = request["userid"]
        id = request["id"]

        #수정할 데이터를 찾아옵니다.
        todo = Todo.objects.get(id=id)
      
        #user를 확인해서 삭제
        if userid == todo.userid:
            todo.delete()

        #userid 와 일치하는 데이터만 추출
        todos = Todo.objects.filter(userid=userid)

        #결과 리턴
        return JsonResponse({"list":list(todos.values())})


=>POSTMAN 에서 테스트

'Study > FrontEnd' 카테고리의 다른 글

React(3)  (0) 2024.01.31
React(2)-이벤트 처리기  (1) 2024.01.30
FrontEnd  (0) 2024.01.18
HTML,CSS을 활용한 자기 페이지 만들기  (3) 2024.01.03
JAVASCRIPT(1)  (0) 2023.12.27