
create database tododatabase;가상 환경 생성(별도의 파이썬 환경 - 배포를 할 때는 가상 환경을 만들고 애플리케이션을 제작)
python -m venv myvenv
가상 환경 활성화
myvenv\Scripts\activate
필요한 패키지 설치
pip install django mysqlclient djangorestframework
프로젝트 생성
django-admin startproject todoproject .
애플리케이션 생성
python manage.py startapp todoapplication
실행
python manage.py makemigrations
python manage.py migrate
python manage.py runserver 0.0.0.0:80

application 디렉토리의 models.py 파일에 작성
from django.db import models
class Todo(models.Model):
# auto increment
id = models.AutoField(primary_key=True)
userid=models.CharField(max_length=100)
title=models.CharField(max_length=100)
done= models.BooleanField()
regdate = models.DateTimeField(auto_now_add=True)
moddate = models.DateTimeField(auto_now_add=True)
변경 내용 적용
python manage.py makemigrations
python manage.py migrate

show tablesdesc todoapplication_todo
URL 설정 : project의 urls.py
from django.contrib import admin
from django.urls import path
from todoapplication import views
urlpatterns = [
path('admin/', admin.site.urls),
# todo 요청은 todoapplication 의 views.py 파일의 TodoView 클래스가 처리
path('todo', views.TodoView.as_view())
]
application 의 views.py 파일에 url에 대한 요청을 처리하는 로직을 작성
from django.shortcuts import render
# 클래스로 요청 처리하기 위해서
from django.views import View
# JSON 리턴
from django.http import JsonResponse
# 상태 코드 사용
from rest_framework import status
# 웹 요청 처리 설정을 위한 decorator
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from datetime import datetime
import json
# models.py 에 생성한 Todo Model import
from .models import Todo
# Todo 인스턴스를 JSON 형태로 바꿔주는 메서드
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
# 요청 처리 클래스
@method_decorator(csrf_exempt, name='dispatch')
class TodoView(View):
# 삽입 요청 처리
def post(self, request):
# 클라이언트가 전송한 데이터 가져오기
# 클라이언트가 전송한 데이터가 dict로 만들어집니다.
request = json.loads(request.body)
# 데이터에서 userid 와 title 읽기
userid = request["userid"]
title = request["title"]
# 삽입할 데이터 생성
todo = Todo()
todo.userid = userid
todo.title = title
todo.done = False
# 데이터 삽입
todo.save()
# userid 에 해당하는 데이터 가져오기
if userid != None:
todos = Todo.objects.filter(userid=userid)
else:
todos = Todo.objects.all()
return JsonResponse({'list':list(todos.values())}, status=status.HTTP_200_OK)
# 조회 요청 처리
def get(self, request):
# userid 라는 파라미터 읽어오기
userid = request.GET.get("userid", None)
# userid 에 해당하는 데이터 가져오기
if userid != None:
todos = Todo.objects.filter(userid=userid)
else:
todos = Todo.objects.all()
return JsonResponse({'list':list(todos.values())}, status=status.HTTP_200_OK)
# 수정 처리
def put(self, request):
# 필요한 파라미터 읽기 - userid, id, done
request = json.loads(request.body)
userid = request["userid"]
id = request["id"]
done = request["done"]
# 수정할 데이터 찾아오기
todo = Todo.objects.get(id=id)
if todo.userid == userid:
todo.done = done
todo.moddate = datetime.today()
# 데이터 수정
todo.save()
return JsonResponse({'result':True,
'data': todoToDictionary(todo)},
status=status.HTTP_200_OK)
else:
return JsonResponse({'result':False,
'data': "수정 권한이 없음"},
status=status.HTTP_200_OK)
# 삭제 처리
def delete(self, request):
# 필요한 파라미터 읽기 - userid, id, done
request = json.loads(request.body)
userid = request["userid"]
id = request["id"]
# 수정할 데이터 찾아오기
todo = Todo.objects.get(id=id)
if todo.userid == userid:
# 데이터 삭제
todo.delete()
return JsonResponse({'result':True},
status=status.HTTP_200_OK)
else:
return JsonResponse({'result':False,
'data': "수정 권한이 없음"},
status=status.HTTP_200_OK)
POSTMAN 을 통해 POST 요청

POSTMAN 을 통해 GET 요청

POSTMAN 을 통해 PUT 요청


POSTMAN 을 통해 DELETE 요청

pip freeze > requirements.txtasgiref==3.8.1
Django==5.1.1
djangorestframework==3.15.2
mysqlclient==2.2.4
sqlparse==0.5.1
tzdata==2024.2yarn create react-app client
npm install --save --legacy-peer-deps @material-ui/core
npm install --save --legacy-peer-deps @material-ui/icons
import React from "react";
class ToDo extends React.Component {
render() {
return (
<div className="ToDo">
<input type="checkbox" id="todo0" name="todo0" value="todo0" />
<lable for="todo0">ToDo 컴포넌트 만들기</lable>
</div>
);
}
}
export default ToDo;
import React from "react";
import ToDo from "./ToDo";
import './App.css';
function App() {
return (
<div className="App">
<ToDo/>
</div>
);
}
export default App;
import React from "react";
class ToDo extends React.Component {
constructor(props) {
super(props);
// 상위 컴포넌트로부터 넘겨받은 item 속성의 값을 item 이라는 이름으로 저장
this.state = { item: props.item };
}
render() {
return (
<div className="ToDo">
<input
type="checkbox"
id={this.state.item.id}
name={this.state.item.id}
checked={this.state.item.done}
/>
<lable id={this.state.item.id}>{this.state.item.title}</lable>
</div>
);
}
}
export default ToDo;
import React from "react";
import ToDo from "./ToDo";
import './App.css';
class App extends React.Component {
constructor(props){
super(props)
this.state = {item:{id:0, title:"안녕 React", done:true}}
}
render(){
return (
<div className="App">
<ToDo item={this.state.item}/>
</div>
);
}
}
export default App;

import React from "react";
class ToDo extends React.Component {
constructor(props) {
super(props);
// 상위 컴포넌트로부터 넘겨받은 item 속성의 값을 item 이라는 이름으로 저장
this.state = { item: props.item };
}
render() {
return (
<div className="ToDo">
<input
type="checkbox"
id={this.state.item.id}
name={this.state.item.id}
checked={this.state.item.done}
/>
<lable id={this.state.item.id}>{this.state.item.title}</lable>
</div>
);
}
}
export default ToDo;

import React from "react";
import { ListItem, ListItemText, InputBase, Checkbox } from "@material-ui/core";
class ToDo extends React.Component {
constructor(props) {
super(props);
// 상위 컴포넌트로부터 넘겨받은 item 속성의 값을 item 이라는 이름으로 저장
this.state = { item: props.item };
}
render() {
const item = this.state.item;
return (
<ListItem>
<Checkbox checked={item.done} />
<ListItemText>
<InputBase
inputProps={{ "aria-label": "naked" }}
type="text"
id={item.id}
name={item.id}
value={item.title}
multiline={true}
fullWidth={true}
/>
</ListItemText>
</ListItem>
);
}
}
export default ToDo;

import React from "react";
import ToDo from "./ToDo";
import "./App.css";
import { Paper, List } from "@material-ui/core";
class App extends React.Component {
constructor(props) {
super(props);
// 데이터 배열 생성
this.state = {
items: [
{ id: 0, title: "안녕 React", done: true },
{ id: 1, title: "블로그 쓰기", done: false },
{ id: 2, title: "집에 가기", done: true },
],
};
}
render() {
let todoItems = this.state.items.length > 0 && (
<Paper style={{ margin: 16 }}>
<List>
{this.state.items.map((item, idx) => (
<ToDo item={item} />
))}
</List>
</Paper>
);
return <div className="App">{todoItems}</div>;
}
}
export default App;

import React from "react";
import { TextField, Paper, Button, Grid } from "@material-ui/core";
class AddToDo extends React.Component {
constructor(props) {
super(props);
this.state = { item: { title: "" } };
}
render() {
return (
<Paper style={{ margin: 16, padding: 16 }}>
<Grid container>
<Grid xs={11} md={11} item style={{ paddingRight: 16 }}>
<TextField placeholder="title 입력" fullWidth />
</Grid>
<Grid xs={1} md={1} item style={{ paddingRight: 16 }}>
<Button fullWidth color="secondary" variant="outlined">
+
</Button>
</Grid>
</Grid>
</Paper>
);
}
}
export default AddToDo;
import React from "react";
import ToDo from "./ToDo";
import "./App.css";
// AddToDo 컴포넌트 추가
import AddToDo from "./AddToDo";
// Container 추가
import { Paper, List, Container } from "@material-ui/core";
class App extends React.Component {
constructor(props) {
super(props);
// 데이터 배열 생성
this.state = {
items: [
{ id: 0, title: "안녕 React", done: true },
{ id: 1, title: "블로그 쓰기", done: false },
{ id: 2, title: "집에 가기", done: true },
],
};
}
render() {
let todoItems = this.state.items.length > 0 && (
<Paper style={{ margin: 16 }}>
<List>
{this.state.items.map((item, idx) => (
<ToDo item={item} />
))}
</List>
</Paper>
);
return (
<div className="App">
<Container maxWidth="md">
<AddToDo />
<div>{todoItems}</div>
</Container>
</div>
);
}
}
export default App;

import React from "react";
import ToDo from "./ToDo";
import "./App.css";
// AddToDo 컴포넌트 추가
import AddToDo from "./AddToDo";
// Container 추가
import { Paper, List, Container } from "@material-ui/core";
class App extends React.Component {
constructor(props) {
super(props);
// 데이터 배열 생성
this.state = {
items: [
{ id: 0, title: "안녕 React", done: true },
{ id: 1, title: "블로그 쓰기", done: false },
{ id: 2, title: "집에 가기", done: true },
],
};
}
// 데이터 추가를 위한 함수 : item 1개를 넘겨받아서 items에 추가하는 함수
add = (item) => {
// react의 state와 props는 불변의 객체
// 수정 작업을 할 때는 다른 곳에 복사를 한 후 수정하고 다시 state 나 props 에 설정
// 기존의 state에 있는 items를 thisItems에 복사
const thisItems = this.state.items;
// 새로운 item에 데이터를 추가 설정
item.id = "ID-" + thisItems.length;
item.done = false;
// thisItems에 데이터를 추가
thisItems.push(item);
// state의 값을 thisItems로 변경
this.setState({ items: thisItems });
};
render() {
let todoItems = this.state.items.length > 0 && (
<Paper style={{ margin: 16 }}>
<List>
{this.state.items.map((item, idx) => (
<ToDo item={item} />
))}
</List>
</Paper>
);
return (
<div className="App">
<Container maxWidth="md">
<AddToDo add={this.add} />
<div>{todoItems}</div>
</Container>
</div>
);
}
}
export default App;
import React from "react";
import { TextField, Paper, Button, Grid } from "@material-ui/core";
class AddToDo extends React.Component {
constructor(props) {
super(props);
this.state = { item: { title: "" } };
// 상위 컴포넌트로부터 넘겨받은 함수 가져오기
this.add = props.add;
}
/* 이벤트 처리를 위한 함수 */
// Input의 내용이 변경되었을 때 호출되는 함수
onInputChange = (e) => {
// Input 의 내용이 변경되면 데이터의 title 을 Input 으로 수정
const thisItem = this.state.item;
thisItem.title = e.target.value;
this.setState({ item: thisItem });
};
// + 버튼을 눌렀을 때 호출되는 함수
onButtonClick = (e) => {
this.add(this.state.item);
this.setState({ item: { title: "" } });
};
// Enter 를 눌렀을 때 호출되는 함수
enterKeyEventHandler = (e) => {
if (e.key === "Enter") {
this.onButtonClick();
}
};
render() {
return (
<Paper style={{ margin: 16, padding: 16 }}>
<Grid container>
<Grid xs={11} md={11} item style={{ paddingRight: 16 }}>
<TextField
placeholder="title 입력"
fullWidth
value={this.state.item.title}
onChange={this.onInputChange}
onKeyPress={this.enterKeyEventHandler}
/>
</Grid>
<Grid xs={1} md={1} item style={{ paddingRight: 16 }}>
<Button
fullWidth
color="secondary"
variant="outlined"
onClick={this.onButtonClick}
>
+
</Button>
</Grid>
</Grid>
</Paper>
);
}
}
export default AddToDo;
+ 버튼을 누르면 리스트에 추가

import React from "react";
import ToDo from "./ToDo";
import "./App.css";
// AddToDo 컴포넌트 추가
import AddToDo from "./AddToDo";
// Container 추가
import { Paper, List, Container } from "@material-ui/core";
class App extends React.Component {
constructor(props) {
super(props);
// 데이터 배열 생성
this.state = { items: [] };
}
// 컴포넌트가 메모리에 로드가 되면 호출되는 메서드
componentDidMount() {
const requestoptions = {
method: "GET",
Headers: { "Content-Type": "application/json" },
};
fetch("http://localhost/todo", requestoptions)
.then((response) => response.json())
.then(
(response) => {
this.setState({ items: response.list });
},
(error) => {
console.log(error);
}
);
}
// 데이터 추가를 위한 함수 : item 1개를 넘겨받아서 items에 추가하는 함수
add = (item) => {
// react의 state와 props는 불변의 객체
// 수정 작업을 할 때는 다른 곳에 복사를 한 후 수정하고 다시 state 나 props 에 설정
// 기존의 state에 있는 items를 thisItems에 복사
const thisItems = this.state.items;
// 새로운 item에 데이터를 추가 설정
item.id = "ID-" + thisItems.length;
item.done = false;
// thisItems에 데이터를 추가
thisItems.push(item);
// state의 값을 thisItems로 변경
this.setState({ items: thisItems });
};
render() {
let todoItems = this.state.items.length > 0 && (
<Paper style={{ margin: 16 }}>
<List>
{this.state.items.map((item, idx) => (
<ToDo item={item} />
))}
</List>
</Paper>
);
return (
<div className="App">
<Container maxWidth="md">
<AddToDo add={this.add} />
<div>{todoItems}</div>
</Container>
</div>
);
}
}
export default App;

가상 환경에 패키지를 설치
django-cors-headerspip install django-cors-headerssettings.py 파일을 수정
INSTALLED_APPS 에 'corsheaders' 추가
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'todoapplication',
'corsheaders'
]
MIDDLEWARE 의 최상단에 'corsheaders.middleware.CorsMiddleware' 추가
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
settings.py 파일에 허용할 URL 을 설정
CORS_ORIGIN_WHITELIST = ['http://localhost:3000']
CORS_ALLOW_CREDENTIALS = True
브라우저에서 다시 확인


