본문 바로가기

Study/Python

[Python] Flask를 사용하여 RESTful API 만들고 통신하기

개요


Python으로 웹서버를 띄울 때 Flask와 django를 많이 사용한다.

 

본래 Spring Boot로 REST API를 제작해서 많이 사용해왔으나 이는 안정적인 대신 프로젝트 구조가 복잡하고 배우는데 많은 시간이 걸린다.

 

Spring Boot로 REST API를 만든다고 하면 보통 다음과 같은 로직으로 구성된다.

 

 

Flask는 Controller, Service, DAO를 포괄하여 하나의 함수로 잡는 개념처럼 보인다. 물론 ORM은 별도로 구성해야 한다.

 

 

Flask 사용 전 설치할 패키지


Flask를 사용하기 위해 패키지 설치가 필요하다.

 

$ pip3 install flask
$ pip3 install flask_cors

 

flask_cors는 브라우저의 CORS 정책을 우회(?)하기 위하여 사용한다. 크롬으로 화면을 띄워 API를 통해 정보를 던져주면 CORS 정책에 의해 에러가 발생한다. 에러가 발생하게 하지 않기 위해 접근 권한을 풀어줄 이유로 사용한다.

 

 

Flask를 사용하여 RESTful API 구축하기


화면단에서 두개의 input을 파라미터로 넘겨 Flask를 통해 넘겨 받은 값의 일치여부를 판단한 뒤 return한 값을 다시 화면으로 가져오는 과정이다.

 

우선 API를 구축하기 위해 모듈을 import 해준다.

 

from flask import Flask, request
from flask_cors import CORS

 

Flask 사용을 위해 다음을 추가한다.

app = Flask(__name__)

 

CORS 모듈을 사용하여 접근권한을 설정한다. 모든 url 패턴을 허용한다.

CORS(app, resources={r'*': {'origins': '*'}})

 

그리고 모든 IP 대역폭을 허용해주어야 한다. 이 코드는 맨 마지막에 들어가야 한다.

if __name__ == "__main__":
    app.run(host="0.0.0.0")

 

첫번째로 "/"로 접속할 경우(서버 주소만 입력하여 접속하는 경우)에 해당하는 API이다.

@app.route("/")
def hello_world():
    print("hello!")
    return "Hello, world!"

 

"/"로 접속할 때 hello_world라는 함수가 실행되며 콘솔에 "hello!"라고 찍힐 것이고, "Hello, world!"라는 문자열을 return할 것이다.

실제로 서버주소:포트번호만 입력하고 들어갔을 때 브라우저에 바로 보인다.

 

처리되는 메소드를 따로 입력하지 않았을 때 GET 방식으로 받는 듯하다.. 그래서 POST 방식으로 데이터를 주고받는 경우 method를 선언해주어야 한다.

 

@app.route("/login", methods=["POST"])
def login():
    user_id = request.args.get("userId")
    user_pw = request.args.get("userPw")
    print(user_id)
    print(user_pw)
    if user_id == "admin" and user_pw == "1q2w3e4r":
        return "success"
    else:
        return "false"

 

보통 Spring Boot로 REST API를 만들게 되면 화면단에서 던져주는 값을 함수의 파라미터로 받도록 하지만, Flask에서는 좀 다르다.

 

"/login"으로 접속하는 API를 사용하기 위해 다음과 같이 화면을 구성하였다.

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <title>Document</title>
</head>
<body>
    <input type="text" placeholder="아이디" id="userId" />
    <input type="password" placeholder="비밀번호" id="userPw" />
    <button type="button" id="loginBtn">로그인</button>

    <script>
        const loginBtn = document.getElementById("loginBtn");

        loginBtn.addEventListener("click", () => {
            const userIdVal = document.getElementById("userId").value;
            const userPwVal = document.getElementById("userPw").value;

            axios({
                method: "POST",
                url: "http://192.168.0.29:5000/login",
                params: {
                    userId: userIdVal,
                    userPw: userPwVal,
                },
                header: {
                    "Content-Type": "application/json",
                }
            }).then((res) => {
                if (res.data === "success") {
                    alert(`로그인에 성공하였습니다.`)
                } else {
                    alert(`로그인에 실패하였습니다.`)
                }
            })
            .catch((err) => {
                console.log(err)
            })
        })
        
    </script>
</body>
</html>

 

로그인 버튼을 클릭했을 때 클릭 이벤트 리스너에 등록된 함수가 실행된다.

 

두개 input의 값을 params로 던져주는데 분명 Flask에서는 인자로 받는 값이 없음에도 해당 로직은 작동한다.

 

"/login" API를 다시 살펴보면

 

@app.route("/login", methods=["POST"])
def login():
    user_id = request.args.get("userId")
    user_pw = request.args.get("userPw")
    print(user_id)
    print(user_pw)
    if user_id == "admin" and user_pw == "1q2w3e4r":
        return "success"
    else:
        return "false"

 

Flask 모듈로부터 함께 import 했던 request를 사용하고 있다. request를 통해 화면단에서 던져준 객체의 key값으로 데이터에 접근한다.

 

콘솔에 찍어보면 값이 잘 넘어옴을 알 수 있다.

 

두개의 넘겨받은 input을 변수에 저장하고 값을 비교했을 때 조건과 일치하면 "success"를 반환하고, 그렇지 않으면 "false"를 반환한다.

 

해당 값은 비동기 처리 후 성공여부(then 메소드)를 통해 넘어온 값에서 data에 해당하는 값을 조회하여 로그인 성공/실패 여부를 가려주게 된다.