관리 메뉴

CASSIE'S BLOG

사전과정 03 파이썬 본문

PROGRAMMING/슈퍼코딩 강의 정리

사전과정 03 파이썬

ITSCASSIE1107 2023. 6. 20. 09:50

https://www.python.org/downloads/
여기서 주의할게 있는데 add python.exe to Path 이거 꼭 해주라고하네?
 
vs code에도 python 확장앱이 있다는데 확장앱 들어가서 또 설치
 
Python Extension Pack 도 유용해서 그것도 설치하라함
 
이거를 깔아줘야 컴퓨터에 있는 파이썬이 vs 코드랑 연결이 된다고함
 
ctrl + backtick -> 터미널을 불러와준다.
 
 

이렇게 한줄한줄 바로 응답되는 언어를 인터프리터 언어 
 
파이썬 환경 나가고 싶으면 >>> exit() 
 

 
 

 
파이썬 파일 실행하면 2 뜬다.
세미콜론도 안 찍어도 되고 
변수 const a = 2; 이런 식으로 안해도된다. 
 
 
터미널에 python
꺽쇠 >>> 
오류나면 껐다 키거나 아까 exe 그거 설치안해서 그런거니까 다시 지우고 다시 다운받으라고함

파이썬은 다음줄을 앞으로 들여쓰기 함으로써 구별을 한다. if문 쓸 때
 
 

 
a = 1
print(a)


if a==1:
    print(2)
else:
    print(5)

 
a가 1이면 2를 출력해라.
a가 1이 아니면 5를 출력해라
 
객체를 쓰는 이유
우리가 이해하기 쉬운 추상화된 개념을 쓰기 위해서 씀
 


class A_가수:
    def 장르():
        print('재즈')
    def 이름():
        print('낫킹콜')
   
A_가수.이름()
       

 
def 똑같이 이용해서 함수 선언
파라미터 전달될 수 있기때문에 () 사용
들여쓰기말고 바깥에다가 한 뒤 실행해야함
 
 


def sayHello():
    print('안녕하세요')
   

sayHello()  

 
파이썬은 if True 대문자를 쓴다.
항상 참이니까 "참입니다." 출력
 



if True:
    print('참입니다')

 


for i in range (1,5):
    print(i)

5는 출력 안함 
1부터 5가 되기 까지
 
 
파이썬 프레임워크 (FastAPI) 를 통해서 직접 서버 만들어보고 거기다 요청 보내보고 응답받아보기
 


 
FastApi
공식 문서 사이트: https://fastapi.tiangolo.com/lo/

 

FastAPI

FastAPI FastAPI framework, high performance, easy to learn, fast to code, ready for production Documentation: https://fastapi.tiangolo.com Source Code: https://github.com/tiangolo/fastapi FastAPI is a modern, fast (high-performance), web framework for buil

fastapi.tiangolo.com

프레임워크 VS 라이브러리

프레임워크 : 어떤 정해진, 개발자가 정해진 룰에 맞게 적용을 해야지 쓸 수 있는 개발 키트 (공식문서 반드시 확인 必)
(조금 더 제한된 개발 툴)
라이브러리: 개발자가 자유도 높게 갖다가 쓸 수 있는 것들을 뜻하는 용어
 
FastApi를 적용하기 전에 설치를 해야하는데
Python 프레임워크들, 패키지들, pip단축키를 통해서 쉽게 다운로드 가능
컨트롤  +  백틱 - > 터미널
거기다가 그냥 pip install fastapi 하면 됨 (fastapi 소문자여야함)
그러면 FastAPI가 성공적으로 설치됐다고 표시됨
 
 
pip = 파이썬 패키지 관리자 이런 느낌
 

 
이 부분 복붙해서 이렇게 수정하면 아주 간단한 api를 만든거라고함
 

from fastapi import FastAPI

app = FastAPI()


@app.get("/hello")
def sayHello():
    return {"message": "Hello World"}

 
why FastAPI? 
-빠르고 간편
-비동기 지원
-문서화를 자동으로 해줌으로써 관리자 편함

django나 Flask도 많이 쓰임
 
 
이 api를 띄워줄 수 있는 웹서버가 필요함, [] 대괄호 주의
터미널에 pip install uvicorn[standard]
 
터미널 복잡하면 중간중간 clear 쳐서 터미널창 내용 없애면 됨

어떤 웹서버를 띄우려면 하기 내용 쳐야한다 (uvicorn이라는 웹서버 이용해서 우리 fastapi 띄우고, main.py에 있는 app이라는 객체를 실행시킨다는 말임.
uvicorn main:app --reload
 
url에 docs만 치면 이 fastAPI서버에 어떤 api들이 제공되어있는지가 보인다.

 
 

try it out - > execute 버튼 두번 누르면 response body에 우리가 설정한 메세지값이 이렇게 들어오게되는거를 확인가능

 
백엔드에게 정보를 보내는 방법들
1. Path parameter
2. Query parameter
3. Request Body
 

 
 
나이가 20살인 유저가 많을수도 있고 적을 수도 있잖아 
인덱스 부분은 왜 안되는지 잘 모르겠다. url에서 치면 문자열이라서 int으로 변환해줘야함
 

from fastapi import FastAPI

app = FastAPI()

items = ['맥북', '애플워치', '아이폰']

@app.get('/items')
def read_items():
    return items

@app.get('items/{id}')
def read_id_item(id):
    return items[int(id)]

 
skip 이라는 쿼리를 줄거구요
skip이라는 query는 즉 숫자값이고, 초기값은 0이다. 
limit, 최대 몇개까지 뽑아낼거냐를 줄건데
역시 int라는 값이고 0이다 라는 표현이 있다.

skip이라는 변수는 int고 아무것도 안 들어오면 0이라는 초기값을 갖는다. limit이라는 변수도 int고 즉 정수고, 숫자고 아무것도 안 들어오면 0의 값을 가진다.
 

from fastapi import FastAPI

app = FastAPI()

items = ['맥북', '애플워치', '아이폰']


@app.get('items/{id}')
def read_id_item(id):
    return items[int(id)]


@app.get('/items')
def read_item(skip:int=0,limit:int=10):
    return items[skip:skip+limit]

 
이거는 초기값 0, limit이 1까지

몰랐네?
 
일단 request body를 보내려면 get요청이 아니라 post 요청을 보내야해요.
 
get요청은 어떤 값을 알려주세요. 어떤 값을 조회할 때 쓰는거거든요? 
근데 그게 아니라 내가 이런 이런 값이 있는데 
이거에 대해서 서버의 데이터를 업데이트 해주세요.
아니면 이거를 새로 등록해주세요. 라는 요청을 보낼 떄는 app.post 즉 post 요청을 씁니다.
 
 
@app.post("items")
def 

여기에는 request body에 어떤 값이 담아져서 들어온거에요.
 
fastAPI에서는 이 아이템의 포맷을 미리 정해줘야한다.
 
from dydatic이라는 내장 라이브러리를 통해 import 할건데요.
 
코드: 

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    id:int
    content:str

items = ['맥북', '애플워치', '아이폰']


@app.get('items/{id}')
def read_id_item(id):
    return items[int(id)]


@app.get('/items')
def read_item(skip:int=0,limit:int=10):
    return items[skip:skip+limit]

@app.post("/items")
def post_item(item:Item):
    return '성공했습니다'
   

 
 
 

 
 
content는 string으로 되어있다. str 

지금 우리가 post요청을 보내려면 이 포맷대로 요청을 보내야한다.
이 포맷대로 요청을 보내지않으면 서버는 에러나 응답을 내보내지않을 겁니다. 

class Item(BaseModel):
id:int
content:str


request body는 url로 구분되는게 아니라 body라는 다른 정보에 담겨서 가거든요? 
이렇게 보내려면 자바스크립트로 통제 해줘야함, 
 

post는 서버의 값을 업데이트할 떄 쓴다!

 
파이썬에서 배열에다가 어떤 값 추가할 때 append라는 문법을 쓴다.

items.append(item.content)
 

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    id:int
    content:str

items = ['맥북', '애플워치', '아이폰']


@app.get('items/{id}')
def read_id_item(id):
    return items[int(id)]


@app.get('/items')
def read_item(skip:int=0,limit:int=10):
    return items[skip:skip+limit]

@app.post("/items")
def post_item(item:Item):
    items.append(item.content)
    return '성공했습니다'
   

docs 들어간뒤에, post로 서버에 값 넣고 그 다음 get들어가서 확인하면 된다. 
 
get으로 확인했음

 
 
이 파일들을 서버에서 보내줄 수 있게 한번 만들어보겠데.  
from fastapi.staticfiles라는 라이브러리를 통해 우리의 정적 파일들 있잖아요
index, html, css, javascript 이런 것들을 묶어서 정적파일 static files라고하는데
그런것들을 서버에 주입해보자
 


from fastapi import FastAPI
from pydantic import BaseModel
from fastapi.staticfiles import StaticFiles

app = FastAPI()


 
 
우리가 서버에다가 정답을 업데이트해주면 
항상 정답이 고정되어있었잖아

css, js파일들을 서버에서 보내줄 수 있게 해본다고함
구글링 해보면
정적파일들, 우리가 만들었던 정적 웹사이트들
index, html, css, js 이런 것들을 묶어서 정적 파일 staticfiles라고 하거든요?
이런 것들을 서버에 주입해본다고 함 
 
공식문서사이트: 
https://fastapi.tiangolo.com/tutorial/static-files/
 
 

from fastapi import FastAPI
from pydantic import BaseModel
from fastapi.staticfiles import StaticFiles

app = FastAPI()

app.mount("/static", StaticFiles(directory="static"), name="static")
 

하지만 static말고 루트경로가 들어갔을 떄 파일이 뜨게하고싶음
그래서 "/static" 이 아니라 "/" 이렇게 경로를 바꿔주세요. 
 
 
html=True 
그리고 뒤에 html이라는 옵션을 true로 줄거에요.
이걸하면 경로가 조금 더 깔끔해진다고함
그리고 directory 부분을 수정을 해줘야함
 
css파일, html파일, js파일 그냥 static 폴더 만들고 넣어주면 이렇게 됨
 
우리 웹서버가 우리가 만든 wordle이라는 정적리소스들을 루트경로에서 내보내주고있다.
 

 

 
정답을 확인하는 코드를 만들려면 return 정답을 알려주는
그러려면 여기서 정답이 정해져있어야겠죠?

answer='TRAIN'

@app.get('/answer') 
 def get_answer():
return answer
 

 
정답을 서버에서 하려고 비동기통신을 할 건데
await 구문은 함수 앞에 async 라는 녀석을 넣어줘야지 await 구문을 쓸 수 있다.
 
fetch는 javascript에서 서버로 요청을 보낼 때 쓰는 함수다. 
어디로 요청을 보내냐면 (answer라는 요청을 보낸다) 
요청을 보내고 나면 응답을 주는데 const 응답으로 받아준다.
응답을 await 기다려서 응답에다가 업데이트 주겠다는 뜻


    const handleEnterKey = async() => {
        let 맞은개수 = 0;
        const 응답 = await fetch('/answer') 


}
 
그 다음에 그 안에 있는 정답을 판단해야하는데요
응답 안에는 응답을 json값으로 바꿔줘야하는데
얘도 또 await 해줘야한다고 함 

이거는 서버에 이렇게 요청을 보내는구나 정도로 대략적으로 이해하기
 

    const handleEnterKey = async() => {
        let 맞은개수 = 0;
        const 응답 = await fetch('/answer');
        const 정답 = await 응답.json()
}

응답을 answer로 요청을 보내서 응답받고 그 응답을 json 이라는 포맷으로 바꾸는 것도 기다려준 다음에 그러면 거기에 정답이 있게됨
 


main.py가서 스트링을 내보내지않고 객체로 한번 내보내볼게요.
아직 스트링 상태 

from fastapi import FastAPI
from pydantic import BaseModel
from fastapi.staticfiles import StaticFiles

app = FastAPI()

answer = 'TRAIN'

@app.get('/answer')
def get_answer():
    return answer

app.mount("/", StaticFiles(directory="static", html=True), name="static")

return문에서 객체로 내보냄. 

from fastapi import FastAPI
from pydantic import BaseModel
from fastapi.staticfiles import StaticFiles

app = FastAPI()

answer = 'TRAIN'

@app.get('/answer')
def get_answer():
    return {'answer': answer}

app.mount("/", StaticFiles(directory="static", html=True), name="static")

그러면 정답_객체랑 정답이랑 나눠지게됨
 

 const handleEnterKey = async() => {
        let 맞은개수 = 0;
        const 응답 = await fetch('/answer');
        const 정답_객체 = await 응답.json()
        const 정답 = 정답_객체.answer;

 

 
이런 식으로 각각 확인 가능
 

 
shift + f5가 완전하게 새로고침 하는 거임
 
지금 서버에서 train이라는 값을 정답으로 선언을 해줬기때문에 main.py에서 해도되고

객체로 안하고 string으로 하면 index.js에서 두줄로 끝난다
const 응답 = await fetch("/answer");
const 정답 = await 응답.json();

await라는 구문은 서버에서 서버로 요청을 보낸 다음에
그거에 대한 응답이 올 떄까지 기다리는 구문이다.
fetch앞에 await 구문을 넣지않게 되면
요청을 보내고 응답이 아직 오지않았는데
그 다음 코드가 실행되버린다.

지지코드..
const 응답 = fetch("/answer");
const 정답 = 응답.json();
console.log(응답, 정답); 

콘솔에 undefined가 내려온다. 
서버한테 요청을 보냈는데 응답이 오기전에 뒤에 로직들이 실행이 되버리는거다.
 


 


지금 서버에서 train이라는 값을 정답으로 선언을 해줬기때문에 main.py에서 해도되고

객체로 안하고 string으로 하면 index.js에서 두줄로 끝난다
const 응답 = await fetch("/answer");
const 정답 = await 응답.json();

await라는 구문은 서버에서 서버로 요청을 보낸 다음에
그거에 대한 응답이 올 떄까지 기다리는 구문이다.
fetch앞에 await 구문을 넣지않게 되면
요청을 보내고 응답이 아직 오지않았는데
그 다음 코드가 실행되버린다.

지지코드..
const 응답 = fetch("/answer");
const 정답 = 응답.json();
console.log(응답, 정답); 

콘솔에 undefined가 내려온다. 
서버한테 요청을 보냈는데 응답이 오기전에 뒤에 로직들이 실행이 되버리는거다.
 


CRUD 
 
꼭 배포하면 데이터베이스 관련 오류들이 생기고
백엔드 로직에서 오류 생기고
서버를 내렸다가 다시 올리는데도 시간이 꽤 걸리거든요
보통 프론트엔드 개발자는 리소스를 미리 만들어놓고
페이지로직들을 미리 만들어놓고 선 배포를 하거나 미리 테스트해볼 수 있는 환경이 많이 있는데
백엔드는 실서버에 올리는 순간 뭐가 안되는 경우가 굉장히 많다.



 
CRUD: 데이터 처리하는 네 가지 기본작업

Create: 새로운 데이터 생성 추가
Read: 데이터 조회 및 검색
Update: 기존 데이터 변경 또는 갱신
Delete: 데이터 삭제
 
 
우리가 css 만들떄도 시멘틱한 태그를 써야한다
html에서도 header main footer 시멘틱한 태그 써야한다
이런 식으로 비슷한 개념

api에서도 rest 한 api를 만들어야한다

API: 
Representational State Transfer
특징
1. Stateless (무상태성)
2. Cacheable (캐시 가능성)
3. Client-Server (클라이언트-서버 구조)
4. Layered System (계층화된 시스템)
 
 
간단한 메모앱
우리가 어떤 문장을 입력했을 떄
그 문장이 서버에 저장되고 저장된 걸 바탕으로 보여주고
그리고 그 메모를 수정하고 어떤 특정 메모를 지우고
실제 서버랑 프론트엔드 서로 통신하면서 만들어보기
 
▶GitHub 가서 새롭게 레포지토리 만들기 그걸 로컬에 클론한 다음 폴더 열어보기
만든 후에 Set up in Desktop 
 

 
추후에 <li> 태그에 생길꺼니까 밑에 코드에서 지워주기
 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>어썸메모</title>
</head>
<body>
    <form>
        <input type="text" placeholder="여기에 메모입력해주세요" />
        <button>생성!</button>
    </form>
        <ul>
            <li>1번 항목</li>
            <li>2번 항목</li>
        </ul>
 
</body>
</html>

 
 
label for은 id를 위한거 메모를 입력하세요. 누르면 input태그랑 연결되게 
button도 type submit 주기
body 바로 직전에 src 해서 app.js 연결하고 서버아직 없어서 서버구현하기(main.py에 서버를 구현해야함) 
 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>어썸메모</title>
</head>
<body>
    <form id="memo-form">
        <label for="memo-input">메모를 입력하세요</label>
        <input type="text" id=memo-input placeholder="여기에 메모입력해주세요" required />
        <button id="button-submit" type="submit">생성!</button>
    </form>
        <ul>
           
        </ul>
  <script src="app.js"></script>
</body>
</html>

저번에 만들었던 코드 복붙해오라고 함 이거 복붙하기
 
이 코드가 루트 경로에 우리의 static파일에 있는 html을 호스팅 해주라 이런 뜻임.
그래서 static 폴더 만들어줘야함 
static폴더에 html, js 파일 넣어준다. (index.html, app.js
 

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel

app = FastAPI()

app.mount("/", StaticFiles(directory="static", html=True), name="static")
 

uvicorn main:app --reload
uvicorn main에 있는 app이라는 객체를 띄울건데 리로드 형식으로 띄워주세요.
이것만 터미널에 뿌려주면 됨. 
 
app.js에 가서 첫 번쨰로 필요한게 뭘까요?

 

shift+ f5 로 완전 새로고침을 해야지 적용된다. 

 

function createMemo(event){
    event.preventDefault();
    console.log("동작하니?");

}

const form = document.querySelector("#memo-form");
form.addEventListener("submit", createMemo);

 

이렇게 하면 동작하냐.. 콘솔이 쭉 찍히고, 

preventDefault() 하면 이벤트가 발생하는 걸 막아준다고 함 

그래서 submit하고 redirect가 안되고 그냥 쭈욱 동작하냐?가 콘솔에 찍혀있음 -> 제출이벤트가 막힌거임

 

 

function createMemo(event){
    event.preventDefault();
    const input = document.querySelector("#memo-input");
    console.log(input.value);

}

const form = document.querySelector("#memo-form");
form.addEventListener("submit", createMemo);

음하하하 찍어보는 것 



 
"우리가 만든 텍스트를 서버로 보내서 메모를 생성하는 과정이 필요합니다." 
그래서 function createMemo를 만들어본다고함.
 
항상 createMemo가 발동하면 안되니까 이벤트를 따로 걸어줘야하는거임.

Submit 이벤트는 제출과 동시에 redirect 되어서 이벤트 만들 때 쿼리셀렉터로 submit 이벤트 만들고 그 쉼표로 해서 함수 호출하면 콘솔 되면 콘솔 찍혔다가 사라짐

우리가 서버 응답을 기다릴거에요

페치라고 요청을 보낼거고

const res = await fetch (‘/memos’)

함수안에 경로를 넣는다고 함

await fetch(‘/memos’)

async function ~ 이런 식으로 적어야 쓸 수 있어 await를

왜냐하면 await는 async 라는 펑션앞에 붙어있는 이 기호를 같이 써야지 쓸 수 있는 문법이다.

기본값이 get인데 get은 값을 달라는 요청이잖아 값을 업데이트해야하는 요청을 보내야하니까 post 요청을 보내야한다.

페치로만 요청을 보내면 get요청으로 가게됨 get이 default 라서

쉼표를 주고 중괄호를 열어서 옵션을 주는거다. 

headers 라는 값을 설정해줘야함 -> 복수형 headers 

우리는 post로 request body에 넣어서 보낼거니까

Content Type이 application/json입니다.

이런 header를 넣어서 보내야해요.

필수적으로 넣어야하는 headers이기 때문에 추가해주시고요.

body안에 어떤 값을 넣을거냐? body안에도 객체를 넣을건데요.

메모가 id가 있으면 금방 그 메모가 뭔지 확 알 수 있을 것 같아요.

 

new Date라고 해서 Id를 넣어주고 content라고 해서 value를 넣어줄게요.

 

근데 문제는 body에 이 부분이 그대로 전달이 안된다. 이것을  JSON.stringfy안에다가 객체를 넣어주셔야됩니다.

 

{
            id: new Date(),
            content: value,
        }
 



json.stringfy 왜냐하면 이 값은 json 값인데
우리가 통신할 때는 문자열만 전송을 할 수가 있어요.

 

그래서 저 {} 부분을 JSON.stringfy()의 매개변수 안에 넣어줘

문자열로 body를 바꿔주고

전송을하고 다음에 받는 쪽에서 그걸 다시 json 형태로 바꿔서 다시 처리하고 다시 문자열로 바꿔서 주르륵 전송하고 이런 과정이 필요하거든요


이렇게 해서 json.response해서 res.json 

 

async function createMemo(value){
    const res = await fetch("/memos", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body:- JSON.stringify({
            id: new Date(),
            content: value,
        }),
    });
    const jsonRes = await res.json();
    console.log(jsonRes);
}



function handleSubmit(event){
    event.preventDefault();
    const input = document.querySelector("#memo-input");
    createMemo(input); //넘겨줘야지 그 값을
    input.value = ""; //계속 input에 글자있으니까 없애주기

}

const form = document.querySelector("#memo-form");
form.addEventListener("submit", createMemo);

이러면 405에러 뜬다. 

main.py에서 서버에서 받을 준비가 안되어있다 이걸로는 

 

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel

app = FastAPI()

app.mount("/", StaticFiles(directory="static", html=True), name="static")

memos라는 post요청을 보냈을 때
define값을 어떤 처리를 해줘야한다.
어떤 처리?
메모를 추가하는 처리
거기에 들어오는 request body는 memo라는 값으로 들아옴 def create_memo(memo): 이렇게

 

 

request body를 받으려면 미리 클래스로 지정해줘야한다는데? 
객체로 어떤 request body로 받으려면
Memo라는 클래스를 지정을 해주고요
BaseModel지정을 해주고 
아까 우리가 어떤 타입으로 지정을 했죠?
id는 int 숫자, 
그리고 content라는 값이 있었죠? 

javascript에서 이런 형식으로 데이터 보낸다고 했잖아.
그래서 받는 쪽에서도 정의를 해줘야한다고함

 body:- JSON.stringify({
            id: new Date(),
            content: value,

 

 

 

 

 

 

 

정의된 값을 create_memo(memo:Memo) memo라는 값으로 이렇게 받아서
memos = [] 
memos라는 배열을 만들어서 memos에 append 한다. 

entity가 맞지않는다고 뜸. 
422 unprocessable entity 

 

읽어오는 과정

 

new Date.getTime(), 해서 보기 쉽게 해볼게요.

 

function readMemo(){} 얘도 똑같이 서버로부터 response를 받는다.
서버로부터 await를 써야하니까
read는 get을 쓸까요 post를 쓸까요?

get이지 
그래서 밑에 post처럼 복잡한 이런 옵션들을 줄 필요가 없다. 
memos를 get해서 가져와주세요. 라는 요청을 보낼거예요.

 

이건 default가 get이고
받은 것은 jsonResponse로 받아야되니까
await res.json()로 바꿔주시고
그 다음 console.log로 찍어보기

 

async function readMemo(){
    const res = await fetch('/memos')
    const jsonRes = await res.json();
    console.log(jsonRes);
}

아무것도 출력안되는데...?

 

 

이렇게만 끝나는게 아니라 
서버에서도 받아줄 수 있어야한다.
memos에 대한 get요청을 받아줄 수 있어야되니까
Python으로 넘어가서 이번엔 서버치 코드 작성해야한다. 
JavaScript하는 부분은 프론트엔드 코드다. 

 

 

 

 

배열 읽어주는 @app.get부분 추가해줘야하지 된다.

 

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel

app = FastAPI()

class Memo(BaseModel):
    id:str
    content:str
   
memos = []

@app.post("/memos")
def create_memo(memo:Memo):
    memos.append(memo)
    return '메모 추가에 성공'


@app.get("/memos")
def read_memo():
    return memos

app.mount("/", StaticFiles(directory='static', html=True), name="static")

 

 

 


async function readMemo(){
    const res = await fetch("/memos");
    const jsonRes = await res.json();
    console.log(jsonRes);
}

readMemo(); //맨처음에 그냥 호출해본다함


async function createMemo(value){
    const res = await fetch("/memos", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            id: new Date(),
            content: value,
        }),
    });
    const jsonRes = await res.json();
    console.log(jsonRes);
}



function handleSubmit(event){
    event.preventDefault();
    const input = document.querySelector("#memo-input");
    createMemo(input.value); //넘겨줘야지 그 값을 input을 계속 줘서 계속 오류남. input.value임.
    input.value = ""; //계속 input에 글자있으니까 없애주기
 

}

const form = document.querySelector("#memo-form");
form.addEventListener("submit", handleSubmit);
 

빈배열이 내려와야하는데

 

 

 

 

json-response는 어떤 배열이 내려온다함
//jsonRes = [{id: 123, content: '블라블라' }] 
이 내용을 우리가 서버에서 받아온 다음에 html에 추가할 예정

불러온 내용을 html에 추가해줘야 프론트엔드 사이드에서 보이니까 함수 displayMemo() 

 

function displayMemos(memos) 
바로 여기서 받아온 메모스 값을 이러는데..? 
html에 ul안에 리스트로 추가해야니까 


***innerText는 백틱을 이용해서 대괄호 감싸주고...-> list안에 텍스트를 넣어주는 과정 
그거를 ul에다가 append 해줘야겠죠? appendChild 로 list추가하기 

 

 

function displayMemo(memos){
    const ul = document.querySelector("#memo-ul");
    const li = document.createElement("li");
    li.innerText = `[id${memo.id}] ${memo.content}`;
    ul.appendChild(li);
}



async function readMemo(){
    const res = await fetch("/memos");
    const jsonRes = await res.json();

}

readMemo(); //맨처음에 그냥 호출해본다함


async function createMemo(value){
    const res = await fetch("/memos", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            id: new Date(),
            content: value,
        }),
    });
    const jsonRes = await res.json();
}



function handleSubmit(event){
    event.preventDefault();
    const input = document.querySelector("#memo-input");
    createMemo(input.value); //넘겨줘야지 그 값을 input을 계속 줘서 계속 오류남. input.value임.
    input.value = ""; //계속 input에 글자있으니까 없애주기
 

}

const form = document.querySelector("#memo-form");
form.addEventListener("submit", handleSubmit);

 

memo 하나에 대한 디스플레이만 만들었어요.
jsonRes가 배열이 올텐데
for each라는 구문을 써가지고
jsonRes.forEach(displayMemo) 이렇게 이 안에 디스플레이 메모를 넣게되면요.

쉽게 풀면
["a", "b", "c"].forEach(func) 이렇게 함수를 넣어주게되면
"a"에 대해서 func 함수를 실행하게되고
"b"에 대해서 func 함수를 실행하고
"c"에 대해서 func 함수를 실행하게된다.

 

 

백틱

 

자바스크립트에 백틱이라는 기능이있는데 이는 파이썬의 f-string과도 비슷하다.

파이썬의 f-stirng에도 { 중괄호 }를 통해 해당 문자열에 값을 바로 삽입 할 수 있었던 것처럼

자바스크립트에도 문자열 내부에 값을 바로 삽입 할 수 있다.

자바스크립스에서는 아래와 같은 문법을 사용한다.

 

백틱

 

Backtics

Console

백틱 사이에 ${ }를 사용해서 expression을 넣는다.

이때 ${ }는 place holder 라고 한다.

 

 

for each를 이용해서 배열로 떨어진 이 json response 에서 각각의 displayMemo를 해줄거다.
jsonRes.forEach(displayMemo);

 

 

맨 처음에 서버에 있는 데이터값을 한번 불러와야되니까
readMemo한번 해주시고
createMemo한 다음에도
우리 서버에만 데이터를 업데이트 했으니까
그 값을 한번 불러와야겠죠?

그래서 createMemo함수안에 readMemo() 함수 호출

createMemo 안에 내용들이 서버에 값을 업데이트 하는 거잖아
그 다음 서버에 있는 그 메모들을 한번 가져와서
쫙 뿌려주는 그런 동작을 할거에요.

이 부분

  const res = await fetch("/memos", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        }, 
        body: JSON.stringify({
            id: new Date(),
            content: value,
        }),
    });
    const jsonRes = await res.json();

 


async function createMemo(value){
    const res = await fetch("/memos", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        }, 
        body: JSON.stringify({
            id: new Date(),
            content: value,
        }),
    });
    const jsonRes = await res.json();
    readMemo();
}

여기서    const jsonRes = await res.json(); jsonRes 값이 즉 json값이 필요없다고함

 

 

계속 막 한번에 여러개 메모 같이 추가되는 이유가
ul을 초기화시키지않고 여기다가 계속 append 하기 때문에 서버에서 받아올 때마다 계속 그 값을 
밑에다가 append child해서 넣어주기때문에 서버 데이터가 중복으로 이렇게 되는거임

 

 

readMemo()할 때 그 안에
const jsonRes = await res.json(); 

이 값을 받아와서 그 밑에
  const ul = document.querySelector("#memo-ul");
추가 한 뒤에 HTML을 초기화 시켜야한다.
ul.innerHTML = "";

 

초기화시켜준 다음 각각의 리스폰스에 맞게 디스플레이해주면 됨

 

 

function displayMemo(memo){
    const ul = document.querySelector("#memo-ul");
    const li = document.createElement("li");
    li.innerText = `[id${memo.id}] ${memo.content}`;
    ul.appendChild(li);
}



async function readMemo(){
    const res = await fetch("/memos");
    const jsonRes = await res.json();

    const ul = document.querySelector("#memo-ul");
    ul.innerHTML = "";

    jsonRes.forEach(displayMemo);

}




여기서 새로고침을 눌러도 이 값이 계속 유지되는 이유: 
앱이 세로고침될 때 이 자바스크립트가 쭉 실행되면서 맨 밑에 뭐 있죠?
readMemo() 가 호출이 되는데
이 readMemo가 서버에 있는 값을 서버에 있는 memos를 (즉 fetch("memos")) 가져와서 
ul에다가 업데이트 하고 있어요.
근데 ul 초기화시킨 다음에
가져온 값으로 업데이트 하고 있으니까 항상 새로운 값으로 선언되어있어서

 

 

옆에 업데이트 버튼이 있어가지고
입력할 수 있고, 그 입력된 값을 바탕으로 서버에 업데이트하고 다시 서버에서 read해와서 화면에 업데이트 

이미 만들어진 html 그 메모 옆에 업데이트 버튼 만들기
버튼을 만들 떄는 display 메모를 할 때 같이 만들어주면 좋은 것 같다고함  

displayMemo(){} 함수 안에 
const editBtn = document.createElement("button");
 

editBtn에 이벤트 달아줘야함
editBtn.addEventListener("click", editMemo) 그 다음 editMemo() 함수 만들고 

 

중간에 console.log(memo.id) 해보니까 콘솔에 저렇게 불러올 수 있다.

function editMemo(){

}




function displayMemo(memo){
    const ul = document.querySelector("#memo-ul");
    const li = document.createElement("li");
    li.innerText = `[id${memo.id}] ${memo.content}`;


    const editBtn = document.createElement("button");
    editBtn.innerText = "수정하기";
    editBtn.addEventListener("click", editMemo)
    editBtn.dataset.id = memo.id;
    console.log(memo.id);
    ul.appendChild(li);
}

 

 

 

data-id를 자바스크립트에서도 속성 줄 수 있다.

 

 

특정 메모 값을 바꾸려면 특정메모가 id가 몇번인지를 알아야한다. 
근데 id가 몇번인지 알 수 있을까요?

edit button에 data로 넣어주면 될 것 같다고 함
editBtn.dataset.id = memo.id; 이렇게 하면 된다고함

html에서
<div data-id="02"></div> 막 id 준거를 
이거를 javascript에서 넣는거다. 

dataset이라는 속성에 id라는 값에 메모의 id를 넣어줌
editMemo함수에 event 전달될거고 
console.log(event.target); 해보면

어떤 버튼이 눌렸는지 target이라는 속성이 있다.
button도 똑같이 ul.appendChild해줘야지 생기지 

 

 

function editMemo(event){
    console.log(event.target);

}




function displayMemo(memo){
    const ul = document.querySelector("#memo-ul");
    const li = document.createElement("li");
    li.innerText = `[id${memo.id}] ${memo.content}`;


    const editBtn = document.createElement("button");
    editBtn.innerText = "수정하기";
    editBtn.addEventListener("click", editMemo)
    editBtn.dataset.id = memo.id;

    ul.appendChild(li);
    ul.appendChild(editBtn);
}


 

 

 

event.target.dataset.id로도 뽑을 수 있나봄

prompt창으로 input요소 받기 

editInput를 받았으니까 서버로 요청 보내기
id값으로 요청 보낸다고 함 
컨텐츠를 어떻게 보낼까?

서버한테 정보 보내는 3가지 방법
지금 path로 특정 id에 해당되는 값을 보내려고 하니까
여기다 query를 통해 보내면 좋을까요?
request body를 넣어서 보내면 좋을까요?
쿼리는 다 노출되지않나...? 
query는 보통 정렬 or 필터링 이럴 때 쓰기때문에
그래서 query보다는 request-body를 이용해서 보내보기

 

 

 

, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        }, 
        body: JSON.stringify({
            id: new Date().getTime(),
            content: value,
        }),
    }

이부분 밑에꺼 그대로 복사해오면 되고

어떤 값을 수정할 때는 PUT이라는 메소드 쓰면 좋고
특정 값이 있을 때 이 값으로 바꿔줘 그런 메소드 

+ id: id로 그냥 수정하기 

근데 id만 적어도 됨 

content값은 우리가 바꾼 edit input값으로 넣어주기

요청을 보낸 다음에 ....?

이거에 대한 응답을 확인할 필요까지는 없을 것 같다고 함

그 다음 readMemo() 호출해보기 (제대로 수정됐는지 확인하려고) 

 

 

프론트단은 완료

 

 
async function editMemo(event){


    const id = event.target.dataset.id;
    const editInput = prompt('수정할 값을 입력하세요');
    const res = await fetch(`/memos/${id}`, {
        method: "PUT",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            id,
            content: editInput,
        }),
    });
    readMemo();

}




function displayMemo(memo){
    const ul = document.querySelector("#memo-ul");
    const li = document.createElement("li");
    li.innerText = `[id${memo.id}] ${memo.content}`;


    const editBtn = document.createElement("button");
    editBtn.innerText = "수정하기";
    editBtn.addEventListener("click", editMemo)
    editBtn.dataset.id = memo.id;

    ul.appendChild(li);
    ul.appendChild(editBtn);
}



async function readMemo(){
    const res = await fetch("/memos");
    const jsonRes = await res.json();

    const ul = document.querySelector("#memo-ul");
    ul.innerHTML = "";

    jsonRes.forEach(displayMemo);

}




async function createMemo(value){
    const res = await fetch("/memos", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            id: new Date().getTime(),
            content: value,
        }),
    });
    readMemo();
}



function handleSubmit(event){
    event.preventDefault();
    const input = document.querySelector("#memo-input");
    createMemo(input.value); //넘겨줘야지 그 값을 input을 계속 줘서 계속 오류남. input.value임.
    input.value = ""; //계속 input에 글자있으니까 없애주기
 

}

const form = document.querySelector("#memo-form");
form.addEventListener("submit", handleSubmit);

readMemo(); //맨처음에 그냥 호출해본다함

 


서버단에서 받을 준비하기

 

memos라는 array를 쭉 돌면서 for문을 돌릴거다. 
for memo in memos 

request body를 받으려면 def create_memo(memo:Memo) 이것 처럼 이런 식으로 받아야한다고함
기존에 있는 m 아이디랑 여기온 id가 같을 떄 

if m.id==id: 여기서 오른쪽에 id는 
@app.put("memo/{id}") 의 id다. 

request로 온 memo의 컨텐츠로 바꿔라

for m in memos:에서 memos는 memos 리스트에 있는 모든 요소를 반복하는 데 사용됩니다. 각 반복에서 현재 요소는 m 변수에 할당됩니다. 따라서 m은 memos 리스트의 각 요소를 대표하는 변수입니다.

 

 

따라서 프론트엔드에서 요청하는 경로가 /memo/${id}로 되어 있고, 백엔드 코드에서 해당 경로를 처리하기 위해서는 @app.put("/memo/{memo_id}")와 같이 경로 패턴을 정확히 일치시켜야 합니다. 슬래시(/)의 여부는 일치하는지 여부와는 상관이 없습니다.

 

 

백단

@app.put("/memos/{memo_id}")
def put_memo(req_memo:Memo):
    for memo in memos:
        if memo.id==req_memo.id:
            memo.content=req_memo.content
            return '성공했습니다.'
    return '그런메모는 없습니다.'    

async function editMemo(event){


    const id = event.target.dataset.id;
    const editInput = prompt('수정할 값을 입력하세요');
    const res = await fetch(`/memos/${id}`, {
        method: "PUT",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            id,
            content: editInput,
        }),
    });
    readMemo();

}




function displayMemo(memo){
    const ul = document.querySelector("#memo-ul");
    const li = document.createElement("li");
    li.innerText = `[id${memo.id}] ${memo.content}`;


    const editBtn = document.createElement("button");
    editBtn.innerText = "수정하기";
    editBtn.addEventListener("click", editMemo)
    editBtn.dataset.id = memo.id;

    ul.appendChild(li);
    ul.appendChild(editBtn);
}



async function readMemo(){
    const res = await fetch("/memos");
    const jsonRes = await res.json();//jsonRes가 어떤 배열이들어올거라 합니다.

    const ul = document.querySelector("#memo-ul");
    ul.innerHTML = "";

    jsonRes.forEach(displayMemo);

}




async function createMemo(value){
    const res = await fetch("/memos", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            id: new Date().getTime(),
            content: value,
        }),
    });
    readMemo();
}



function handleSubmit(event){
    event.preventDefault();
    const input = document.querySelector("#memo-input");
    createMemo(input.value); //넘겨줘야지 그 값을 input을 계속 줘서 계속 오류남. input.value임.
    input.value = ""; //계속 input에 글자있으니까 없애주기
 

}

const form = document.querySelector("#memo-form");
form.addEventListener("submit", handleSubmit);

readMemo(); //맨처음에 그냥 호출해본다함


readMemo에서...
//jsonRes = [{id:123, content:'블라블라'}]
["a", "b", "c"].forEach(func);


자 그러면 append child하기 전에 한번 ul을 초기화시켜줘야겠죠?

 

 

우리가 메모장 앱은 서버가 붙어있는 정적 웹사이트가 아니에요.

서버 배포가 필요하다는게 무슨 말일까요?

당근마켓클론, 워들같은 앱은 서버가 안 붙어있는 정적웹사이트였는데

즉 html, css, javascript, 만 어떤 플랫폼에 올려주기만하면

새로운 배포플랫폼인데 그마저도 굉장히 쉽게 해주는 툴이 있단다.

Deta Space

 

https://deta.space/

Enable Developer Mode 하기

 

Dos>Setting up the CLI  > 그 window 부분 복사> ctrl + Backtick > 복사한거 붙여넣기

 

 

iwr https://deta.space/assets/space-cli.ps1 -useb | iex

 

그 다음 space login 입력해준다.

 

https://deta.space/docs/en/build/fundamentals/space-cli/


 

access 검색

 

 

pip freeze > requirements.txt
이걸 해주면 우리가 쓰고 있는 파이썬 패키지들이 이제 다 들어가있게된다고함.

 

이거를 해줘야지 배포를 했을 때 오류가 없다고함

이게 배포하는 과정임

space new

새로운 스페이스를 만들겁니다.

그러면 what's your project's name? 물어봄

그러면 awesome-memo다.

 

 

제일 마지막에 space push

 

 

✓ Successfully pushed your code and updated your Builder instance!
Builder instance: https://awersomememo-1-b7043718.deta.app
PS D:\personal\awersome-memo> 

 

 

수정사항 생기면 다 수정하고 그냥 space push 다시 누르면 된다.

 

 

반응형