수업 24일차

galoo·2023년 7월 30일
0

HelloVision Dx Data School

목록 보기
24/72
post-thumbnail

✔ 프로젝트 시작!

DB 확인

Access

  • \rarr Table

MySQL

  • Database \rarr Table
  • SQLite도 동일하다.

Oracle

  • sid(service Name) \rarr User \rarr Table

둘의 작업의 단위가 다르다. Oracle로 사용하려면, User까지 확인해야 한다.

  • 서비스 만드는 것에 관심이 있다면, Oracle로 설정해보자.

DB 설정 정보를 python 코드로 만들지 않고, 별도의 파일에 Text 형태로 작성하는 이유

  • 설정 정보
    - 별도의 txt 파일이나, DB에 작성하는 경우가 많다.
    - 1. properties : 속성 : 값, 여러번 사용가능
    - 2. xml
    - 3. json
    - 4. yaml(yml) : email 표현 형식
    - yaml은 똑같은 속성 여러번 작성 x, 트리처럼 작성해야 함.(정리)

  • 소스코드
    - Compile \rarr Build \rarr EXE
    - 코드가 한 글자라도 바뀐다면 위의 과정을 전부 다시 해야한다.

  • 개발 환경에서는 운영환경의 데이터를 쓰는 것이 아닌, 자체 리소스로 개발해야 한다.
    - 이후, 이것을 운영환경으로 옮긴다. (migration)
    - 리소스 관련 코드가 소스코드에 있다면, Compile, Build를 하는데 여기서 오류가 정말 많이 발생한다.(환경이 바뀌면 문제가 생길 가능성이 존재함)
    - 그래서 환경을 하나로 묶은 DevOps
    - 그리고 등장한 개념이 일반파일 or DB

  • 이름을 dp.properties라고 하자. 그러면 소스코드에서 db.properties를 읽음. 하지만 파일을 바꿨다고 해서 소스코드가 바뀌는 것은 아니다.

  • 일반 리소스의 변화는 소스코드에 아무 변화를 주지 못한다.

결론

  • 일반 파일이나 DB에 설정 정보를 젖아하면, 설정 내용이 변경되어도 다시 컴파일&빌드를 할 필요가 없어집니다.
  • 개발 환경에서 운영 환경으로 이행 시 발생하는 오류나 운영 환경 내에서의 변경에도 오류가 발생할 가능성이 매우 낮아집니다.

ORM을 사용하는 이유

  • SQL을 이용하지 않고, 프로그래밍 언어의 함수를 이용해서 작업을 하기 때문에, SQL을 별도로 익히지 않아도 되긴 합니다.
  • SQL을 이용하지 않기 때문에, 관계형 데이터베이스를 변경할 때, 설정 정보만 변경하면 됩니다.
  • ORM을 사용할 수 없는 환경
    - NoSQL
    - 유사한 형태로 동작을 시켜주는 패키지는 존재, 하지만 ORM으로 사용불가
    - ORM은 Table과 Class가 기본적인 Mapping이기 때문입니다.

Model 생성

모델과 테이블 연결

  • 테이블을 먼저 만들고 Model 클래스와 연결하는 것이 가능하고, Model 클래스를 만들어서 자동으로 테이블을 생성하는 것도 가능
  • 테이블의 이름 : application이름_모델이름
  • 모델 이름의 첫 글자는 관계형 데이터베이스 종류에 따라 대소문자 다르게 함
  • 실무에서는 테이블을 만들고 연결하는 것이 일반적입니다.
  • 학습(공부)을 할 때는 2개가 연동되는 과정을 보거나, 모델 클래스의 필드가 데이터베이스에 어떻게 생성되는지를 확인하기 위해서, 모델 클래스를 만들어서 Migration을 합니다.

ToDo 모델을 application의 models.py에 작성하기

from django.db import models
class Todo(models.Model) : # model 클래스로부터 상속 받도록
    id=models.AutoField(primary_key=True) # Auto_increment
    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()
    #계정을 delete = 1 이렇게 해서 휴면계정을 설정할 수 있다.

변경 내용 적용

python manage.py makemigrations
python manage.py migrate

db에 접속해서 테이블 확인

show tables;
desc todoapplication_todo; (권장)
select * from todoapplication_todo; 

CRUD 작업을 위한 url설정

작업의 종류 : 어떤 정보를 전달해서 어떤 정보를 받아올 것인지 결정하기

  • userID를 매개변수로 받아서 userID에 해당하는 정보를 전부 가져오기
  • 데이터 삽입 작업 (userID와 title만 받아서 처리)
    - 결과는 userID에 해당하는 정보를 리턴
  • 데이터 수정 작업 (userID와 id, done값만 받아서 처리)
    - 결과는 userID에 해당하는 정보를 리턴
  • 데이터 삭제 작업 (userID와 id만 받아서 처리)
    - 결과는 userID에 해당하는 정보를 리턴

url 설정

  • 이전에는 작업을 구분하는 것을 url만으로 했지만, 최근에는 url과 method(전송 방식)으로 구분하는 것을 권장
    - CURD 하는데 url로 구분하면 4개나 됩니다.
    - 어디든 갈 때, 문이 하나인 것이 좋아보인다.

가져오기 - GET
삽입 - POST
수정하기 - (PUT : 전체 수정, 멱등성 O Update : 부분수정, 멱등성 x)
삭제하기 - DELETE

  • todo라는 URL로 method를 구분해서 4개 작업을 처리

dajngo url처리

  • urls.py 파일에 url과 처리할 veiws의 함수 또는 클래스를 연결
  • 예전에는 함수를 권장했지만, 최근에는 클래스를 권장합니다.

프로젝트 vs 앱
ulrs views
urls
Routing
요청 \rarr (Front)Controller \rarr 처리

읽기 앱 + 나머지 기능 앱
이후
읽기 앱은 mongodb나머지는 mysql
구독과 게시 kafka

"문서화" 해서 설명해두기

REST API

  • 요청한 device나 화면에 상관없이 동일한 요청은 동일한 url로 처리하도록 하자는 것으로, 서버가 클라이언트의 요청에 응답할 때, 서버에서 view를 생성하는 것이 아닌 데이터를 전송해서 클라이언트에서 랜더링하도록 하는 것

애플리케이션의 views.py에 todo 요청을 처리할 클래스 생성

  • 우선 원래 실무에서는 views.py는 controller의 역할로만 존재하게 한다.
from django.shortcuts import render
from django.views import View
#클래스의 get, post, put, delete 메서드가 각 요청 방식을 처리함
class TodoView(View):

프로젝트의 urls.py 파일에서 todo 요청은 views.py 파일의 TodoView 클래스가 처리하도록 설정하기

from django.contrib import admin
from django.urls import path
from todoapplication import views
# todo 요청은 views파일의 TodoView 클래스가 처리하도록 설정
urlpatterns = [
    path("admin/", admin.site.urls),
    path("todo", views.TodoView.as_view())
]

userid를 받아서 userid에 해당하는 모든 데이터를 조회하는 요청 처리

\rarr views.py에서

from django.views import View
#클래스의 get, post, put, delete 메서드가 각 요청 방식을 처리함
from .models import Todo
# JSON 응답을 만들기 위한 import
from django.http import JsonResponse
from rest_framework import status
class TodoView(View):
    def get(self,request):
        # 파라미터 읽어오기
        # userid가 없다면 none
        userID=request.GET.get("userID", None)
        if userID !=None:
            todos=Todo.objects.filter(userID=userID)
        else:
            todos=Todo.objects.all()
        # Json 응답, list라는 키로 검색된 데이터를 list로 전달
        return JsonResponse({'list':list(todos.values())},
        status=status.HTTP_200_OK)
  • 이후 서버 재가동 하고 :8000/todo 해보면 데이터가 없어서 {"list": []}만 보인다.
  • dbeaver에 더미 데이터를 넣고 commit하고 결과를 다시 보자.
  • http://127.0.0.1:8000/todo?userID=mino 이렇게 하면 mino userID인 것 만 나온다.
  • get 방식으로 검색한 것이다. 즉, TodoView에서 이게 get인지, post, put, delete인지 확인 가능하다.

data 삽입하는 post method 생성

    def post(self, request):
        # 파라미터 읽기
        params=json.loads(request.body)
        userID=params["userID"]
        title=params["title"]
        #삽입할 객체 생성
        todo=Todo()
        todo.userID=userID
        todo.title=title
        todo.done=False
        #regdate는 auto 현재 날짜이지만, moddate는 없으니 설정
        todo.moddate=datetime.datetime.today()
        #데이터 저장
        todo.save()
        #삽입하고 결과 처리
        #일반 적으로 삽입한 데이터만 리턴하거나, 아니면
        #전체 데이터 리턴 방식이 존재함 트래픽때문에 전체 다운로드는
        #생각을 좀 해봐야 한다.
        #나는 삽입한 데이터 하나만 리턴할래
        todos = Todo.objects.filter(userID=userID)
        # Json 응답, list라는 키로 검색된 데이터를 list로 전달
        return JsonResponse({'list': list(todos.values())}, 
        status=status.HTTP_200_OK)
  • 제대로 동작하는지 확인해보고 싶은데?

REST API 테스트 도구를 이용해서 테스트

post 확인해보기
http://127.0.0.1:8000/todo
파라미터

{
	"userID":"mino3"
    "title":"귀가하기"
}


  • 서버에서 처리를 하는 도중, 403 forbidden 에러가 발생함

CSRF

  • Cross Site Request Forgery
    - 웹 애플리케이션 취약점 중 하나로, 인터넷 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위(삽입, 삭제, 수정)를 특정 웹 사이트에 요청하게 만드는 공격
    - 하나의 애플리케이션이 서버와 클라이언트를 담당할 때는 문제 없음
    - 서버와 클라이언트가 다른 애플리케이션으로 만들어지는 경우에 이런 문제가 발생함
    - 이 경우에는 서버에서 설정을 해제해주어야 외부에서 삽입, 삭제, 갱신이 가능합니다.
  • Django에서는 Decorator로 해당 문제를 해결합니다.
    - views.py 파일의 요청 처리 클래스 위에 설정을 추가
    - python -> decorator 다른 곳 annotation
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
@method_decorator(csrf_exempt, name='dispatch')


  • 이러면 get을 날려서 post가 잘 된 것을 확인할 수 있다.

수정

  • views.py의 클래스 put 메서드 작성
 # 클라이언트의 파라미터 읽기
 # 서버에서의 처리
 # 응답 만들기
  • 이 세 가지 단계로 웹 프로그래밍은 처리한다.
  • 수정은 id, userid, done을 전송할 것이다.
  • 수정을 할 때, 수정할 데이터의 기본키값(id)은 반드시 전송되어야 합니다.
    - ORM에서 수정 작업은 기본키를 이용해서 데이터를 조회하고 그 데이터를 수정하고 save()하면 됩니다.
    def put(self, request):
        # 1단계 클라이언트의 파라미터 읽기
        params=json.loads(request.body)
        id=params["id"]
        userID=params["userID"]
        done=params["done"]
        # 2단계 서버에서의 처리
        # 여기서 데이터베이스 작업 이외의 작업을 한다면,
        # 별도의 클래스를 만들어서 처리하고 리턴받아서 다음 작업을 수행
        # id에 해당하는 데이터를 찾아서 done의 값을 수정하는 것이다.
        # get은 하나만, filter는 여러개 return
        todo=Todo.objects.get(id=id)
        todo.done=done
        # 수정한 날짜를 기록하고 싶어.
        todo.moddate=datetime.datetime.today()
        todo.save()
        # 3단계 응답 만들기
        # 1개 줄거야? 아니면 전체 다 줄거야?
        todos = Todo.objects.filter(userID=userID)
        # Json 응답, list라는 키로 검색된 데이터를 list로 전달
        return JsonResponse({'list': list(todos.values())},
        status=status.HTTP_200_OK)
  • 테스트를 동일하게 해보자.

  • 잘 바뀌었다.
  • 하지만 시간 관련 method가 우리가 원하는 그대로가 아닌 것 같다. 뭔가 손을 봐줘야 할 것 같다.

데이터 삭제 구현

  • 삭제 구현도 수정과 동일합니다.
    - 기본키를 이용해서 데이터를 찾은 후, delete메서드를 호출하면 됩니다.
    - 기본키는 파라미터로 반드시 전송을 해야 합니다.
    def delete(self, request):
        # 파라미터 읽기
        params = json.loads(request.body)
        id = params["id"]
        # 데이터 가져오기
        todo = Todo.objects.get(id=id)
        # 데이터 삭제하기
        todo.delete()
        # 응답 만들기
        todos = Todo.objects.all()
        # Json 응답, list라는 키로 검색된 데이터를 list로 전달
        return JsonResponse({'list': list(todos.values())},
        status=status.HTTP_200_OK)
  • 이번에는 id만 받아와서 해당 id를 가진 데이터를 삭제하고, Todo의 모든 리스트를 보여주는 코드를 작성하였다.
  • "id":3을 delete 하였을 때, 전체 list에서 id는 1, 2만 보이는 것을 확인할 수 있다. 즉, 데이터가 잘 삭제되었다.

✔ 서버 (Back End)

  • URL에 해당하는 메서드 호출 방법을 알아야 합니다.
    - URL과 전송방식에 해당 하는 방법을 알아야 합니다.
  • client가 전송한 파라미터를 어떻게 읽을 것인지 알아야 합니다.
  • 유효성 검사를 해야 합니다. (일반 프로그래밍 언어 로직)
    - if userID == None : , else: 이런것도 가능
  • 데이터베이스 작업을 어떻게 할 것인지 (CRUD)
  • 결과를 JSON으로 어떻게 전송하는지 알아야 합니다.

  • CSRF 해결책
  • CORS 해결책

  • 비밀번호 암호화해서 저장하는 방법(복호화를 못하도록 해서 저장해야 함)
  • JWT Token을 사용하는 방법

  • 로깅하는 방법
  • 구독과 게시를 만드는 방법(Ops를 한다면 중요합니다.)

  • 데이터 모델링, SQL 최적화
  • 네트워크, 보안
  • 트래픽 분석
  • Cloud 배포
  • SW 공학(디자인 패턴, 아키텍처 패턴, 개발 방법론)

✔ 클라이언트 프로그램 만들기

react사용을 위한 사전 준비

  • node.js 설치
  • yarn 설치 (npm install --location=global yarn)

React 프로젝트 생성

  • npx create react-app 앱이름
  • yarn create react-app 앱이름

프로젝트 실행

  • cd 프로젝트 앱 명
  • yarn start or npm start

UI 작업을 편리하게 하기 위한 패키지 설치

material-ui 패키지

  • npm list에 있는지 확인을 하였다.

Main 화면 만들기

Main으로 사용할 컴포넌트 생성

  • 일반적인 확장자는 jsx
    - js도 가능하고, 타입스크립트를 사용하면 tsx도 가능
    - App이름\src\ToDo.jsx
// <<<Todo.jsx>>>
// react.js 파일에서 export한 객체를 React로 받아서 사용한다.
// {이름} 의 경우는 export한 객체에서 이름에 해당하는 것만 받아서 사용
// 하나로 묶여있어야 하는 것을 잊지 말자.
import React from "react";
class ToDo extends React.Component{
    render(){
        return(
            <div className="ToDo">
                <input type="checkbox" 
                    id="todo0" nmae="todo0" value="todo0"/>
                <label for="todo0">ToDo 컴포넌트 만들기</label>
            </div>
        )
    }
}
export default ToDo;

이후, App.js를 맞게 수정해주자.
entry point 는 index.js이다. App.js는 메인화면이라 부른다.

// <<<App.js>>>
import logo from './logo.svg';
import './App.css';
import React from "react";
import ToDo from "./ToDo";
function App() {
  return (
    <div className="App">
      <ToDo/>
    </div>
  );
}
export default App;
  • React는 CBD(Component Based Development) 컴포넌트를 기반으로 개발하는 것이다.
    - 높은 재사용성이 특징이다
  • React에서 기억할 특징 하나는 SPA(Single Page Application) 라는 것이다.
  • 이 2개만 알아도 될 정도로 매우 중요하다.
  • REACT 왜쓰냐?
    - SPA
    - 기존의 웹 페이지는 여러 페이지로 구성되어 있으며, 페이지를 로딩할 때 마다 서버에서 리소스를 전달받아와서 렌더링을 했다.
    - 데이터 정보 전송 과부화 등으로 인해 속도 저하와 같은 여러 문제점이 존재하기에 서버에서 제공하는 페이지가 1개인 SPA를 사용하게 되었다.
    - 기본 방법론은 CBD이다.
    • 변경 사항이 있다면 그 부분만 변경해서 출력 속도가 빠르다.

React에서 사용하는 변수의 개념

  • State & Props 를 기억하자.
    - state 내 안에서
    - props 상위 컴포넌트 넘겨받기(수정 불가)
    - 한 개 짜리 데이터는 바로 수정 가능(자동적으로 복제해서 사용)
    - 그 외는 바로 수정은 불가능
    • 원본데이터의 변경 위험으로 사본을 작업하고 집어넣는 형태로 동작함
  • props
    - 상위 컴포넌트에서 넘겨주는 데이터, 읽기 전용
  • state
    - 자신의 컴포넌트에서 생성한 데이터, 읽고 쓰기 가능
  • react는 props나 state(인스턴스 변수)의 원본 데이터를 수정하지 않습니다.
    복사본을 만들어 수정한 뒤에 다시 대입하는 형태로 작업을 합니다.
  • Data를 주고 받는 것은 react가 하는 것이 아닌, JS가 합니다.
  • React는 화면 출력만 담당합니다.

메인 화면에서 받은 데이터를 ToDo.jsx에 출력하기

ToDo.jsx 파일을 수정

// react.js 파일에서 export한 객체를 React로 받아서 사용한다.
// {이름} 의 경우는 export한 객체에서 이름에 해당하는 것만 받아서 사용
// 하나로 묶여있어야 하는 것을 잊지 말자.
import React from "react";
class ToDo extends React.Component{
    constructor(props){
        // 상위 컴포넌트로부터 넘겨받은 데이터를 나의 props에 저장
        super(props)
        //안에서 쓴다면 this.state={이름: 초기값, 이름: 초기값...} 
        // this 는 인스턴스 안에 숨겨진 첫 번째 매개변수
        // 왜 생성자에서 만드는거야? render 안에 넣으면 안되나? 
        // 둘이 this.state 해도 되는건데?
        // 왜냐하면, instance 호출은 constructor 이다. 
        // 얘가 아니면 다른곳에서 한다면 있는지 없는지 알 수 없음
        // python 은 def __init__(): super()겠지
        // 생성자를 만드는 이유 : 시작하자마자 무엇인가를 해야 한다면 
        // props는 읽기 전용이라 수정을 한다면, 
        // state에 복사해서 사용해야 합니다.
        this.state={item:props.item}
    }
    //밖에다가 쓴다면
    // state={이름:초기값,...}
    render(){ // render는 메서드, 
    //메서드 안에서 인스턴스를 쓴다면 this를 붙여야 한다. 
        // 안붙으면 없으면 그냥 만든다.
        return(
            <div className="ToDo">
                <input type="checkbox" 
                    id={this.state.item.id} 
                    name={this.state.item.id} 
                    value={this.state.item.done}/>
                <label id={this.state.item.done}>
                {this.state.item.title}</label>
            </div>
        )
    }
}
export default ToDo;

App.js에서 sample data를 생성해서 ToDo.jsx를 이용해서 출력하기

import './App.css';
import React from "react";
import ToDo from "./ToDo";
class App extends React.Component {
  constructor(props){
    super(props);
    // 하나의 객체를 생성해서 state에 item이라는 이름으로 저장
    this.state={item:{id:0, title:"hello React", done:true}};
  }
  render(){
    return(
      <div className="App">
        <ToDo item={this.state.item}/> {/*props*/}
      </div>
    )
  }
}
export default App;
profile
밀가루 귀여워요

0개의 댓글