갑자기 코테가 잡혀버려서 부랴부랴 코테를 준비중이다. 너무 오랜만이라 기억이 잘 안난다..ㅎ 

문제를 봐도 "아 이거 dfs, bfs" 인데 그것만 기억나고 어떻게 푸는지 잘 생각이 안났다. 시간이 얼마 안남았으니 최대한 효율적으로 공부해야겠다.

총 4문제를 풀었고 dfs, bfs 에서 못풀었다. 이번 기간동안은 해당 문제 연습을 해야겠다. 그래도 기분 좋은 건 백트래킹 문제를 풀었따 ㅎㅎ

728x90

dune 이 무엇인가요?

공식 문서 : https://dune.build/

Dune은 OCaml 프로젝트를 위한 빌드 시스템 및 패키지 관리 도구입니다. Dune은 다음과 같은 기능을 제공합니다.

  • 프로젝트 빌드: Dune은 OCaml 코드, C 코드, Cython 코드 등을 포함한 다양한 유형의 소스 파일을 빌드할 수 있습니다.
  • 패키지 관리: Dune은 OPAM을 통해 OCaml 패키지를 설치 및 관리할 수 있습니다.
  • 테스트 실행: Dune은 OUnit을 사용하여 OCaml 테스트를 실행할 수 있습니다.
  • 문서 생성: Dune은 Haddock을 사용하여 OCaml 문서를 생성할 수 있습니다.

Dune은 OCaml 프로젝트를 빌드하고 관리하는 데 매우 유용한 도구입니다. 다음은 Dune 사용 방법의 간략한 개요입니다.

Dune 프로젝트를 만들려면 dune init 명령을 실행합니다. 이 명령은 dune 파일과 dune-project 파일을 생성합니다.

dune 파일은 프로젝트 설정을 포함합니다. 다음은 dune 파일의 예시입니다.

(executable (name my-project) (libraries ocaml) (sources "main.ml"))

이 예시에서는 my-project라는 이름의 실행 파일을 빌드합니다. 이 실행 파일은 OCaml 라이브러리를 사용하고 main.ml 파일의 소스 코드를 포함합니다.

프로젝트를 빌드하려면 dune build 명령을 실행합니다. 이 명령은 dune 파일에서 설정된 대로 실행 파일을 빌드합니다.

테스트를 실행하려면 dune test 명령을 실행합니다. 이 명령은 OUnit을 사용하여 프로젝트의 모든 테스트를 실행합니다.

문서를 생성하려면 dune doc 명령을 실행합니다. 이 명령은 Haddock을 사용하여 프로젝트의 문서를 HTML 형식으로 생성합니다.

728x90

설치 및 환경설정

나는 m2 맥북을 쓰고 있다.

강의 자료 대로 sudo apt-get install opam 하니까 command not found 가 떠서

(brew 를 통해 설치하였고, 아래 순서대로 터미널에 입력했다)

brew update → brew install opam → opam init → eval $(opam env)

brew update
brew install opam
opam init
eval $(opam env)

하고 나서 ocaml --version 와 opam --version 로 버전 확인 결과 이렇게 떴다. 각각 5.1.1, 2.1.5 인가보다.

ocaml --version
opam --version

그러고 나서 OCaml 필수 라이브러리인 core 설치를 위해

opam install core 를 입력한다. 중간에 y(yes) 눌러주면 된다. (설치가 꽤나 오래걸린다)

opam install coore

설치가 되면 dune --version 으로 버전을 확인해보자.

dune --version

실행해보기

나는 Visual Studio Code 가 편해서 코드 에디터로 Visual Studio Code를 설정하였다. 편한 걸로 진행하면 된다.

우선 파일들은 이렇게 만들어놨다.

확장 들어가서 OCaml Platform 도 설치해줬다.

dune-project 에는 이렇게

(lang dune 2.9)

dune 에는 이렇게

(include_subdirs unqualified)
(executable
(name helloworld))

helloworld.ml 예시 코드에는 이렇게 작성해준다.

Format.printf "Hello World!"

컴파일을 해야하는데 여기서 초반에 헤맸다.

파일이 존재하는 해당 폴더 경로에서 다음 명령어를 입력한다. 아까 dune 파일에서 실행파일 이름을 helloworld 라고 입력했는데 helloworld.exe 에서 helloworld 로 이름이 동일해야한다.(이름은 변경가능)

dune build helloworld.exe

.ml 실행 파일이 있는 폴더 내부에 eval $(opam env) 을 해야한다. 처음엔 저게 뭔지도 모르고 따라하다보니 다른 경로에 저 명령어를 입력했다.
그렇게 하면 dune build helloworld.exe 라는 빌드 명령어 입력시 zsh: command not found: dune 라고 뜨는 것 같다. (가상환경 설치하는 건가..?)

dune exec ./helloworld.exe 을 사용하면 컴파일 후 실행까지 할 수 있다.

728x90

OCaml 은 ML family 에 속하는 프로그래밍 언어이다.

ML 이란, 범용 프로그래밍 언어의 일종으로 안전한 타입 시스템이 특징이다.

OCaml 특징

  • 함수형 프로그래밍 언어이다.
    • 함수가 일등시민 !
  • Strongly typed 언어이다.
    • 모든 변수 및 표현식의 타입이 컴파일 시점에 결정됨.
  • Type inference
    • 타입은 있는데 타입을 우리가 쓸 필요없다. 컴파일러(타입 시스템)가 자동으로 표현식의 타입을 추론해줌.
  • Polymorphism 지원
    • 데이터 구조 및 알고리즘 일반적으로 구현된다.
  • Pattern mathching
    • Case 분석 지원
  • 모듈 시스템
    • 프로그램을 여러 모듈로 나누어 구현
  • Ocaml 은 Caml 에 OOP 개념 추가하여 확장한 것.

OCaml 장점

  • 함수형 언어라 간결함(깔끔함)
    • C++, Java 에서의 람다식 도입
  • 함수형 언어쓰면 알아보기 쉽다(명시적)
  • Ocaml 은 함수형 언어의 대표격이다.

기타 특징들

  • Ocaml 은 확장자로 .ml 을 쓴다.

  • Ocaml 은 main 함수가 없다. (like python)

    • c, java, c++ 은 main 있으나 ocaml 은 python 처럼 없음
  • Ocaml의 컴파일러는 ocamlc

    • C/C++(gcc,clang),Java(javac)
  • Ocaml의 빌드 시스템은 dune

    • C/C++(Bazel,CMake),Java(Maven,Gradle,Ant)
  • Ocaml 은 타입을 값의 집합으로 이해한다

    • 타입을 값의 집합으로 이해함.

    • 3 → int ( 3이라는 녀석이 int 라는 집합의 원소이다.)

    • 변수 x → char (x 에 넣는 것, 혹은 x 에서 뺐을 때 그것은 char 집합의 원소임)

    • 1+2 → int (1+2 계산 결과가 int 의 원소이기 때문에 int)

    • 함수의 리턴타입 → void (함수의 반환 값이 공집합인 void 의 원소이다)

728x90

<span style="color:grey">이 포스트는 유데미 강의 FastAPI 강의를 듣고 정리한 글입니다.'</span>


## Project2 개요 

이번 시간에는 지난번에 이어 book project2 를 진행할 계획이다.


GET, POST, PUT, DELETE 리퀘스트 메소드는 계속 사용하되

프로젝트2에서는 데이터 유효성 검사(Data Validation) , 예외 처리(Exception Handling), 상태 코드 (Status Codes), 스웨거 Configuration, 파이썬 리퀘스트 객체에 대해 추가적으로 학습할 것이다.



프로젝트1에서 만든 book 이 프로젝트2에서는 파이썬 객체가 될 것이다. 그렇가 하려면 Book 이라는 새로운 **"클래스"** 를 생성해야 한다.

우리는 이 프로젝트를 통해 이러한 book들을 이용하게 될 것이다.
book 오브젝트를 만들때는 아래 코드를 참고하면 알 수 있듯이, 생성자를 통해 생성할 수 있다.


```
class Book:
id: int
    title: str
    author: str
    description: str
    rating: int

def __init__(self, id, title, author, description, rating):
self.id = id
    self.title = title
    self.author = author
    self.description = description
    self.rating = rating
    
```


### Pydantic v1 vs Pydantic v2 이슈

> - FastAPI is now compatible with both Pydantic v1 and Pydantic v2.
 Based on how new the version of FastAPI you are using, there could be small method name changes.
The three biggest are:

> .dict() function is now renamed to .model_dump()
 schema_extra function within a Config class is now renamed to json_schema_extra
Optional variables need a =None example: id: Optional[int] = None

=> 정리하면 FastAPI 가 현재 Pydantic v1 과 v2 둘다 호환가능해서 FastAPI 버전에 따라 메소드 이름이 바뀔 수 있다는 것.
예를 들어 .dic() 함수는 .model_dump() 로 바뀌었고
schema_extra function 은 json_schema_extra 로 
바뀌었다.

## Project2 SetUP


![](https://velog.velcdn.com/images/aengzu/post/5f6e8cdd-d9d8-479c-a633-9f5da123fef4/image.png)
프로젝트 2 는 새로운 파이썬 파일로 작성할 것이기 때문에 books2.py 를 새롭게 생성한 후, 초기 설정은 그대로 진행한다.

```
from fastapi import FastAPI
# fastapi 에서 FastAPI 임포트하기

app = FastAPI()
#FastAPI 객체 생성 후 app 에 담기

@app.get('/books')
async def read_all_books():
return BOOKS
# books 경로 진입시 모든 책을 GET 하는 함수를 정의한다.
```

다음은 books2.py 에서는 BOOK 클래스를 통해 객체를 생성할 것이기 때문에 app 밑에 BOOK 클래스를 정의한다. 

```
class Book:
    id: int
    title: str
    author: str
    description: str
    rating: int

    def __init__(self, id, title, author, description, rating):
        self.id = id
        self.title = title
        self.author = author
        self.description  = description
        self.rating = rating

```
![](https://velog.velcdn.com/images/aengzu/post/6722969f-e72a-4a43-99f4-9948e1eff7e1/image.png)


BOOK 오브젝트들은 강의 내용에 나온대로 작성하였다.
```

BOOKS = [
    Book(1, 'Computer Science Pro', 'codingwithroby', 'A very nice book', 5),
    Book(2, 'Be Fast with FastAPI', 'codingwithroby', 'A very great book', 5),
    Book(3, 'Master Endpoints', 'codingwithroby', 'A awesome book', 5),
    Book(4, 'HP1', 'Author 1', 'Book Description', 2),
    Book(5, 'HP2', 'Author 2', 'Book Description', 3),
    Book(6, 'HP3', 'Author 3', 'Book Description', 1),

]

```


## Project2 SetUP

728x90

유데미 FastAPI - The Complete Course 2023 강의를 듣고 정리한 내용입니다.

WHAT FAST API IS

Fast API 란 API 를 빌딩하기 위한 파이썬 웹 프레임워크이다.

 
 

API 는 "Application Programming Interface"의 약자로, 소프트웨어 애플리케이션들이 서로 상호작용하기 위한 규약이나 인터페이스를 의미한다.
우리는 코드를 짤 때 하나의 프로그램만을 사용하지 않는다. 외부 서비스의 기능을 활용하기도 하고,내부 시스템들 간 연결해서 사용하기도 한다.

 
 
 

예를 들어 웹 페이지를 html 로 만들었다고 할 때, 생기는 페이지는 정적인 페이지이다. 이는 user1, user2 가 입장했을 때 같은 화면을 보게 됨을 의미한다. 그러나 실제로는 user1 에 대한 프로필과 user2 에 대한 프로필 화면은 다를 것이다. 이때 사용자들의 정보를 저장하는 서버가 필요하게 되고, 서버와 웹페이지간의 소통할 수 있는 규칙들을 API 로 작성하는 것이다.
 
 
 

웹 프레임워크는 웹 애플리케이션을 개발하기 위한 도구나 라이브러리의 집합이다. 이러한 프레임워크들은 웹 애플리케이션을 구축하는 데 필요한 기본 구조, 기능, 도구들을 제공하여 개발자들이 보다 쉽게 웹 애플리케이션을 만들 수 있도록 도와준다. 그렇다면, "웹 프레임워크를 왜 사용할까요? 내가 직접 개발하면 되지 않나요?" 라는 질문이 나올 수 있다. 미리 개발되어 있는 웹 프레임워크를 사용하면 빠른 개발을 위한 단순화된 방법을 이용할 수 있다. 이것은 수년에 걸친 개발의 결과이고, 이를 따르는 편이 효율적이기 때문에 사용한다.

 

그럼 API 프레임워크 중 우리가 공부할 Fast API 의 장점을 알아보자.

  • Fast API 에서 Fast 가 의미하는 것은 Performance(수행능력)이 빠르다는 의미와 Development (개발)이 빠르다는 의미를 포함한다.
  • Fast API 사용시 버그가 줄어든다.
  • Fast API 는 빠르고 쉽다.
  • Fast API 는 안정성과 유연성을 제공한다.
  • Fast API 는 개발 표준이 존재한다. 오픈 API 표준과 JSON 스키마를 사용한다.

 

관련 문서: https://fastapi.tiangolo.com

 

다음은 웹페이지와 FastAPi 서버가 소통하는 과정을 대략적으로 알아보자.

웹 페이지는 유저와 상호작용을 한다. 이때, FastAPI 서버가 웹 페이지를 위한 비즈니스 로직을 처리하게 된다.
이해하기 쉽게 설명해면, 사용자가 웹페이지와 상호 작용할 때 우리는 웹 서버에 데이터를 "요청" 혹은 "입력"하게 된다. (내 프로필 페이지를 요청하거나, 회원가입을 통해 내 데이터를 입력하게 됨)
이 때 웹 페이지와 상호작용할 때 사용자가 올바른 데이터를 확보할 수 있도록 하는 것이 Fast API 가 하는 일 이다.
이렇게 Fast API 는 비즈니스 로직을 처리하게 되는데, 그 뿐 아니라 웹 페이지도 렌더링한다.

Fast API 는 추가적 도구를 활용하여 풀 스택 응용 프로그램을 만들 수도 있다.

 
 
 
그렇다면 대체 누가 Fast API 를 이용할까?

바로 넷플릭스, 우버, 마이크로소프트와 같은 세계적 기업이 Fast API 를 이용하고 있다.

 
 
 

Fast API Setting

Fast Api 실습 진행을 위해 가상 환경 세팅이 필요하다.
파이썬 가상 환경이란 내 기기(맥북)에 있는 다른 파이썬 환경들과는 분리되어있는 환경을 의미한다.

아래 명령 python3 -m venv fastapienv 으로 가상환경을 생성할 수 있는데 아직 활성화과 된 상태는 아니다. 활성화를 위해선 활성화 명령어를 통한 활성화가 필요하다.

mkdir fastapi-tutorial     // fastapi-tutorial 폴더를 생성한다.
python3 -m venv fastapienv  // fastapienv 라는 이름의 가상환경을 생성한다.

 

활성화를 해보자. 활성화를 위해선

 source [폴더]/bin/activate

명령어를 통해 fastapi 환경을 활성화 시켜야한다.

 
비활성화를 하려면 활성화된 가상 환경에서 deactivate 를 입력하면 된다.
 

이제 가상환경에 Fast API를 설치해보자. 가상환경에 진입한 후, pip install fastapi 를 입력하여 설치하면 된다.

pip install fastapi

그리고 나중 웹 서버를 열기 위해선 uvicorn 을 설치해야한다.

  pip install "uvicorn[standard]"

를 입력한다. 표준 버전의 유비콘을 설치한다는 의미이다.

Fast API Request Method Logic

Fast API 요청 메소드 로직을 공부하기 위해 하나의 작은 프로젝트로 공부를 진행할 예정이다.

BOOK PROGECT

BOOKS 책 목록들은 key : value 값을 부여받는다. title 은 책의 제목을 의미하는 카테고리이고, value 에 실제 인스턴스의 책 제목 value 가 들어간다. author 는 작가를 의미하는 카테고리이고 value 에는 실제 인스턴스의 작가 value 가 들어간다. category 는 책의 장르를 의미하는 카테고리이고, value 에는 실제 인스턴스의 카테고리 value 가 들어간다. 이것을 하나로 묶어서 하나의 책 요소가 되고 이 책들이 모여있는게 BOOKS(책 리스트)이다.

이 책들에 대해 CRUD 연산을 진행할 것이다.
C : Create
R : Read
U : Update
D : Delete
를 의미한다.

웹 페이지는 Fast API 와 상호작용한다. 웹페이지가 FastAPI 에 요청을 보내면 FastAPI 가 응답하게 된다. 예를 들어 웹 페이지가 Fast API 에 Book 2 에 대한 정보를 요청하면 Fast API 가 해당 정보를 전송한다.
 
웹 페이지가 Fast API 에 정보를 요청할 때는 HTTP Request Method 를 사용하여 요청하게 된다.
HTTP 요청 메서드는 웹 페이지가 서버에 통신할 수 있는 방법이다.
 
CRUD 작업에 대해 Fast API 에서는 스웨거 UI 가 이미 구현하고 있다.

각각의 CRUD 작업에 대해 매치되는 HTTP 리퀘스트를 표로 나타내면 이렇다.

CRUD HTTP Request
Create POST
Read GET
Update PUT
Delete DELETE

1. GET Request Method

  • books.py 작성
from fastapi import FastAPI

  app = FastAPI()

  @app.get('/api-endpoint')
  async def first_api():
      return {'message' : 'Hello Eric!'}

 

우리는 api-endpoint 에 접속하면 해당하는 데이터에 접근가능하도록 하고싶다. 엔드포인트는 네트워크에서 흔히 호스트의 의미로 여기서는 서버에 접속한 고객으로 생각하면 된다. 이를 위해 비동기 함수 first_api() 를 작성한다. 우리가 만드는 모든 함수에는 명시적으로 async 를 사용할 것이다.
비동기 함수란 함수를 호출했을 때 실행이 완료 되지 않더라도 호출자에게 리턴, 즉, 제어권을 넘기고 자기 혼자 백그라운드로 작업을 계속 한다. 그리고 어느 순간 작업이 완료 되면 호출자에게 작업이 완료 되었음을 '통보' 해주는 함수이다. 지금은 그냥 비동기 함수를 사용하는 구나 하고 이해하고 넘어가자.
 
 
파이썬에서는 데이터를 Read 하기 위한 Get 함수을 포함한다.이건 HTTP 요청 메소드로 간주된다. 이 함수는 API 엔드포인트를 추가해야한다.함수가 실행될 경로를 지정해야한다는 것을 의미한다.
경로 지정은 함수 위에 데코레이터를 추가해서 할 수 있다.
@app.get('/api-endpoint')
다음 같은 형식을 데코레이터라고 한다. 이렇게 데코레이터를 추가하여 어플리케이션을 시작하면
fast API 로 서버를 연 후, url 127.0.0.1:8000/api-endpoint 를 실행하면 fastAPI 로 부터 first_api() 함수에 대한 응답을 받게 된다.
 

 
우리가 작성한 FastAPI 서버를 열기 위해선 우선 터미널을 연 후, uvicorn 을 실행해야한다. uvicorn 실행을 위해선 아래 명령어를 터미널에 입력한다.

uvicorn books:app --reload 

uvicorn 은 fastAPI 을 설치할 웹 서버를 의미한다. 여기서 books 는 파이썬 파일을 의미한다. 우리가 해당 파일을 books.py 로 지정했기 때문에 books 이지 만약 main.py 로 지정한다면 main:app 으로 했을 것이다. 그 다음 나오는 app 은 FastAPI 이다. app = FastAPI() 와 같은 코드를 위에서 봤을 것이다. 그때 생성된 FastAPI 를 의미한다. 이것 또한 만약 app 이 아니라 merong = FastAPI() 로 생성했다면 uvicorn books:merong --reload 의 형식으로 서버를 열면 된다. 뒤에 붙는 옵션 --reload 는 코드 변화가 있을 때마다 앱을 재부팅하는 것을 의미한다.

이 명령어를 입력하면 URL : 127.0.0.0:8000 으로 서버가 열린다.

 
다음은 모든 책의 정보를 볼 수 있도록 코드를 작성해보자.
모든 책의 정보를 보려면 방금처럼 endpoint 로 url 에 접속해서는 안될 것이다. 예를들어 고객1이 모든 고객의 정보를 볼 수 있다면 아주 큰 문제가 될 것이다. 따라서 데코레이터로 경로를 수정한다.
127.0.0.0:8000/books 에 접속하면
모든 책 리스트를 볼 수 있도록 다음같은 함수를 추가하였다.
 

@app.get('/books')
 async def read_all_books():
      return BOOKS

--reload 모드로 서버를 열었으므로 재구동할 필요 없이 바로 사이트에서 확인할 수 있다.

 

Swagger UI

FastAPI는 기본적으로 Swagger UI를 자동으로 제공하여 개발자가 API를 쉽게 이해하고 테스트할 수 있는 환경을 제공한다.
스웨거 UI 에 진입하면 생성한 모든 API 의 endpoint 를 볼 수 있게 해준다.
 
스웨거 UI 에는 url 에서 endpoint 를 지우고 /docs 로 입장하면 볼 수 있다.

 

Path Parameters

경로 매개 변수란 위치에 근거한 정보를 찾는 방법으로 정의된다.
 
만약 우리가 폴더에서 /Users/codingwithroby/Document/python/fastapi/section1 의 경로에 간다면 섹션1 폴더를 보게 될 것이다.
 
만약 아래 함수가 있다고 가정하자.

@app.get('/books')
 async def read_all_books():
      return BOOKS

웹페이지에 127.0.01:8000/books 과 같은 요청이 있을 때,
우리가 데코레이터를 통해 입력한 경로의 위치에 해당하는 함수를 실행하게 된다.
 
이때 이 /books 와 같은 경로를 정적(static) 경로라고 한다. FastAPI 애플리케이션 내의 책들은 변경될 수 없기 때문이다.
 
 
하지만 FastAPI 에는 동적(dynamic) 경로 매개변수를 만들 수 있다.
예를 들어 127.0.0.1:8000/books/book_one 과 같은 요청이 있을 때,
book_one 과 같은 입력은 사용자가 입력하는 것이다. 우리는 사용자가 입력하는 모든 book1,2,3,4,5, 에 대해 endpoint 를 만들지 않는다.
이를 위해선 사용자가 전달하는 모든 정보를 리턴하는 동적 경로 매개 변수를 사용할 수 있어야 한다. => 이 말은 즉슨, 사용자가 어떤 걸 요청할 지 미리 알 수 없고, 요청하면 해당 데이터를 리턴해주는 동적 경로 매개 변수를 (요청 데이터에 따라 반응하는) 정의해야한다는 것을 의미한다.
 
 
이론으로는 이해하기 어려우니 아래 예시를 살펴보자.
동적 경로 매개 변수 정의를 위해 아래처럼 코드를 작성한다.

@app.get('/books/{dynamic_param}')
async def read_all_books(dynamic_param):
      return {'dynamic_param': dynamic_param}

 
{dynamic_param}가 만약 book_one 로 설정된다면 '/books/book_one' 을 요청하게 될 것이다. 이때 dynamic_param 의 부분에 book_one 이 들어간다면 book_one 에 대한 함수가 호출되고 이에 따라 book_one 정보를 리턴할 수 있을 것이다.
book_two, book_three 여도 마찬가지다.

 

아래 사진을 보고 결과를 예측해보자. 만약 /books/mybook 의 경로로 들어가서 요청한다면 어떻게 될까? http://127.0.0.1:8000/books/mybook 로 들어가면 {'book_title': 'My Faborite Book'} 이 아닌, {'dynamic_param': mybook} 이 리턴되는 것을 알 수 있다.
그 이유로는 우선 /books/mybook 의 경로에 대한 요청은 첫번째 /books/{dynamic_param} 에 포함되므로 만약 mybook 으로 요청을 하더라도 두번째 함수가 호출될 일은 없는 것이다 (첫번째 함수에서 다 처리되기 때문!)
 
그렇기 때문에 항상 정적(static) 또는 작은 API 를 앞에 둬야한다. (큰것에 포함되면 안되게!)

 
 
그렇다면 /books/mybook 과 /books/{dynamic_param} 의 위치를 변경하면 어떤 결과로 나타날까?
아래 사진은 위치를 바꾼 후 mybook 으로 접속했을 때의 화면이다. 우리가 원하는 결과대로 출력된 것을 확인할 수 있다. 이것이 가능한 이유는 만약 위치를 바꾼다면 mybook 에 대한 요청은 위 함수로 먼저 처리된 후, 아래 {dynamic_param} 에 대한 요청은 mybook 이 아닌 요청에 대해 처리할 수 있게 되기 때문이다. ( 정적 매개 변수가 먼저 요청을 잡아냄 )

Query Parameters

쿼리 매개 변수는 물음표 다음에 추가된 매개 변수를 요청하는 것이다.
쿼리 매개 변수는 이름=value 페어의 형태를 띈다.
예를 들어 127.0.0.1:8000/books/?category=math 라는 url 이 있을 때,
books 중 category=math 인 애들을 요청할 수 있다.

만약 127.0.0.1:8000/books/author%20four/?category=science 라는 url 이 있다면
과학 카테고리에 속하는 저자를 찾는 요청이다.
코드로 이해해보자.
/books/ 에 대해 원하는 카테고리의 데이터만 출력하기 위해선, 만약 book에서 'category'의 값이 내가 요청한 쿼리의 category: str 값과 일치하면 배열에 해당 book을 담은 후 한번에 리턴하게 된다.

@app.get('/books/')
async def read_category_by_query(category: str):
    books_to_return = []
    for book in BOOKS:
        if book.get('category').casefold() == category.casefold():
            books_to_return.append(book)
        return books_to_return

작성한 코드를 스웨거 UI 에서 실행하면 다음같은 출력이 나온다. 'books/?category-science' URL 에 대한 GET 요청을 보내서 카테고리 중 science 인 애들에 대해 필터링을 맞게 하는 것을 확인할 수 있다.

 
 

Query Parameters + Path Parameters

이번에는 {book_author} 중에서 category 가 science 인 book 만 출력해보자.(쿼리매개변수
'/books/' 에서의 쿼리 요청 함수를 작성하는 것이 아닌 '/books/{book_author}/' 의 동적 패쓰에서 쿼리 요청을 해야 한다.따라서 아까 동적 매개 변수 함수 작성했듯이 URL 을 설정하고
함수에는 매개변수로 book_author: str 과 category: str 을 전달하면 된다.

@app.get('/books/{book_author}/')
async def read_author_category_by_query(book_author: str, category: str):
    books_to_return = []
    for book in BOOKS:
        if book.get('author').casefold() == book_author.casefold() and \
        book.get('category').casefold() == category.casefold():
            books_to_return.append(book)
    return books_to_return

스웨거 UI 에서 실행하면 다음과 같다.

2. POST Request Method

데이터를 생성하기 위한 요청이다. POST 방식은 GET 은 가지지 않은 추가적인 정보인 body 를 가질 수 있다. 만약 사용자가 Body 를 전송하면 해당 데이터의 생성이 가능하게 한다.

우리의 book 프로젝트에서 바디는 
{'title':'Title Seven' , 'author':'Author Two', 'category':'math} 의 형태를 가진다.

127.0.0.1:8000/books/create_book 과 같은 요청이 있을 때

처리하기 위한 함수로 create_book(new_book=Body()) 를 정의한다.
데코레이터에는 /books/create_book 의 경로를 적는다.
그럼 body 값을 BOOKS 에 append(추가) 할 수 있다.
이 때 주의할 점은 위에 
from fastapi import Body 
코드를 추가하여 Body 를 fastapi 에서 import 해와야한다.
@app.post('/books/create_book')
    async def create_book(new_book=Body()):
        BOOKS.append(new_book)


스웨커 UI 에서 실행하면 이렇다.

주의할 점은 GET 은 Body 를 가질 수 없다. 예를 들어 아래 코드를 실행하면 오류가 뜨게 된다.

@app.get('/books/{book_title}')
async def read_book(book_title: str, new_book=Body()):
    for book in BOOKS:
        if book.get('title').casefold()==book_title.casefold():
            return book

3. PUT Request Method

PUT 메소드는 데이터를 업데이트하는데 사용된다.
PUT 은 추가적 정보인 바디를 가진다.

코드로 살펴보면 예를들어 127.0.0.1:8000/books/update_book 요청이 있을 때
아래 함수에선 body 의 정보를 받아와서 title 이 같은 BOOKS 요소에 대해 해당 바디값으로 책 정보를 변경(업데이트)하는 것을 의미한다.

@app.get('/books/update_book')
async def update_book(updated_book=Body()):
    for i in range(len(BOOKS)):
        if BOOKS[i].get('title').casefold() == updated_book.get('title').casefold():
            BOOKS[i] = updated_book

스웨거 UI 에서 실행해보았다. PUT 메소드에서 Title Six 의 카테고리를 math 에서 history 로 바꿔서 실행하였다. 그 결과 모든 책을 GET 했을 때 변경돼서 나오는 것을 알 수 있다.

4. DELETE Request Method

DELETE 명령어는 data 를 삭제하는데 사용된다.
코드로는 다음처럼 표현한다.
127.0.0.1:8000/books/delete_book/{book_title} 의 URL 로 접근하면 {book_title} 의 book 이 삭제된다.

@app.delete('/books/delete_book/{book_title}')
async def delete_book(book_title:str):
    for i in range(len(BOOKS)):
        if BOOKS[i].get('title').casefold() == book_title.casefold():
            BOOKS.pop(i)
            break

스웨거 UI 에서 실행해보면,

정상적으로 삭제된 것을 확인할 수 있다.

전체 코드

이렇게 기본 메소드 GET, PUT, POST, DELETE 들을 가벼운 book 프로젝트로 익히는 시간을 가졌다.

GET : 데이터 요청
PUT : 데이터 업데이트
POST : 데이터 생성
DELETE : 데이터 삭제

전체 코드는 이렇다.

from fastapi import Body,FastAPI

app = FastAPI()

BOOKS = [
    {'title' : 'Title one', 'author': 'Author One', 'category':'science'},
{'title' : 'Title Two', 'author': 'Author Two', 'category':'science'},
{'title' : 'Title Three', 'author': 'Author Three', 'category':'history'},
{'title' : 'Title Four', 'author': 'Author Four', 'category':'math'},
{'title' : 'Title Five', 'author': 'Author Five', 'category':'math'},
{'title' : 'Title Six', 'author': 'Author Six', 'category':'math'},
]
@app.get('/books')
async def Read_All_Books():
    return BOOKS


@app.get('/books/{book_title}')
async def read_book(book_title: str, new_book=Body()):
    for book in BOOKS:
        if book.get('title').casefold()==book_title.casefold():
            return book

#쿼리 매개변수는 데이터에 대해 필터링을 하는 것.
@app.get('/books/')
async def read_category_by_query(category: str):
    books_to_return = []
    for book in BOOKS:
        if book.get('category').casefold() == category.casefold():
            books_to_return.append(book)
        return books_to_return


@app.get('/books/{book_author}/')
async def read_author_category_by_query(book_author: str, category: str):
    books_to_return = []
    for book in BOOKS:
        if book.get('author').casefold() == book_author.casefold() and \
        book.get('category').casefold() == category.casefold():
            books_to_return.append(book)
    return books_to_return

@app.post('/books/create_book')
async def create_book(new_book=Body()):
    BOOKS.append(new_book)


@app.put('/books/update_book')
async def update_book(updated_book=Body()):
    for i in range(len(BOOKS)):
        if BOOKS[i].get('title').casefold() == updated_book.get('title').casefold():
            BOOKS[i] = updated_book

@app.delete('/books/delete_book/{book_title}')
async def delete_book(book_title: str):
    for i in range(len(BOOKS)):
        if BOOKS[i].get('title').casefold() == book_title.casefold():
            BOOKS.pop(i)
            break
728x90

이번 시간에는 수학적 함수들을 공부할 것이다.

Mathematical Function

아래 공식 문서를 참고하면 수학적인 함수 및 연산자들을 확인할 수 있다.
https://www.postgresql.org/docs/current/functions-math.html

생각할 수 있는 거의 모든 수학 연산자는 PostgreSQL 에서 사용할 수 있다.
절대값, 반올림, 로그, 파이, 삼각함수 등 다양한 수학 연산이 가능하다. 위 사이트를 참고해서 사용하면 된다.

String Function

String을 다루기 위한 Function 들도 있다.
이또한 문서에서 확인할 수 있다.

https://www.postgresql.org/docs/9.1/functions-string.html

728x90


### Timestamps and Extract

시간과 날짜 정보를 보고하는 명령어와 함수를 살펴볼 예정이다.
데이터베이스를 쿼리할 떄보다 자체 데이터베이스를 만들 때 이러한 함수가 유리해진다.

Postgre는 다음과 같은 날짜 시간 정보를 가질 수 있다. 
- TIME(시간정보), DATE(날짜정보), TIMESTAMP(시간과날짜정보), TIMESTAMPTZ(시간, 날짜, 시간대 정보)

시간 데이터 유형을 선택할 때 신중하게 고려해야한다. 상황에 따라 날짜, 시간 및 시간대가 필요할 수도 필요하지 않을 수도 있기 때문이다. 직원이 근무한 시간을 계싼할 때는 시간이 필요하지만 시간대는 필요없을 것이다. 그러면 TIMESTAMPTZ는 필요하지 않다.

이번에는 이러한 데이터 유형과 관련된 함수를 배울 것이다.
- TIMEZONE, NOW, TIMEOFDAY, CURRENT_TIME, CURRENT_DATE


### NOW

NOW() 를 사용하면 현재 시간대, 날짜, 시간 정보를 볼 수 있다. 현재 내가 있는 시간을 timestamp 형식으로 표시한다. 

![](https://velog.velcdn.com/images/aengzu/post/26dfdb55-6476-4004-bd2d-86ad99c83540/image.png)

### TIMEOFDAY
TIMEOFDAY() 는 text 의 형태로 날짜, 시간을 가져온다.
![](https://velog.velcdn.com/images/aengzu/post/173285ba-9e70-468d-897a-5207801a9f09/image.png)


### CURRENT_DATE

CURRENT_DATE()는 현재 날짜를 date 형식으로 가져온다.

![](https://velog.velcdn.com/images/aengzu/post/682680e0-128b-43db-82c9-7fa8ce936450/image.png)


### EXTRACT()
날짜 값의 하위 구성요소 출력하거나 할 때 사용한다. YEAR, MONTH, DAY, WEEK, QUARTER 과 같은 데이터값들을 갖는다.
다음 문법으로 사용할 수 있다.
```sql
EXTRACT(YEAR FROM date_col)
```

### AGE()
타임스탬프가 지정된 현재 age 를 계산후 리턴한다.

```sql
AGE(date_col)
```
로 사용하고 

다음과 같은 형식의 결과를 리턴할 것이다.
```sql
13 year 1 mon 5 days 01:34:13.003423
```
### TO_CHAR()

TO_CHAR() 은 데이터 타입을 텍스트로 변환한다. timestamp 를 포매팅할 때 유용하다.

```sql
TO_CHAR(date_col, 'mm-dd-yyyy')
```



## 실습
payment 테이블 속 payment_date 칼럼은 timestamp 형식으로 되어있다. 여기서 연도를 추출해보자.

```sql
SELECT EXTRACT(YEAR FROM payment_date) FROM payment
```
![](https://velog.velcdn.com/images/aengzu/post/dd454573-751b-4d66-9e67-db27fa2b358b/image.png)


![](https://velog.velcdn.com/images/aengzu/post/ff3ad2fc-9534-4322-98ef-ca4040f3d25e/image.png)


TO_CHAR() 은 포매팅할 때 유용하게 사용할 수 있다. 
TIMESTAMP 를 원하는 형식으로 출력할 수 있다.
포매팅 방법은 아래 사이트를 참고하면된다.
[documentation](https://www.postgresql.org/docs/current/functions-formatting.html)

```sql
SELECT TO_CHAR(payment_date, 'MONTH-YYYY')
FROM payment
```
![](https://velog.velcdn.com/images/aengzu/post/2c0c2657-3bee-43bf-a2ac-fac2d4009b26/image.png)


payment 테이블에서 월요일에 결제된 주문 수를 계산하려면 어떻게 할까?
Timestamp 에서 EXTRACT 를 통해 원하는 field 를 추출할 수 있다. 요일이 궁금하므로 "dow" 를 사용하면 일요일부터 토요일까지 요일을 0-6의 숫자로 나타낸다. 따라서 payment_date 라는 timestamp 칼럼에서 요일이 1(=월요일)일 때의 행의 개수를 COUNT 하면 된다. 

```sql
SELECT COUNT(payment_date)
FROM payment
WHERE EXTRACT(dow FROM payment_date) = 1
```

728x90

+ Recent posts