티스토리 뷰

이제 백엔드 시스템에서 비동기처리는 대세가 되었다.

python진영 백엔드는 오래전 부터 Django와 Flask가 다수를 차지 하고 있었지만, 처리속도에 장점이 있는 비동기서버에 대한 관심과 요구가 커지면서 지금은 Sanic이나 FastAPI와 같은 백엔드프레임워크를 사용하는 곳이 늘어 나고 있다.

 

이번에 개인적으로 웹소켓을 이용해서 간단한 채팅서비스를 만들어 보고자 기술검토를 하던 중 백엔드 시스템을 Node.js를 사용할까 하다 javascript보다는 python이 좀더 익숙하고 사용하기 편하기에 Node.js의 성능에 버금가는 비동기 처리 프레임워크인 FastAPI에 대해서 알아 보기로 하였다.  

이미 Flask로 여러 프로젝를 진행한 경험이 있기때문에 Flask와 구문이나 사용성 면에서 유사하고 성능면에서는 더 나은, 그리고 이미 많은 곳에서 사용하고 있는 FastAPI가 가장 좋아 보였다.

FastAPI 설치

우선 프로젝트 폴더를 만든 후 python virtualenv를 설정해보자

% mkdir myproject
% cd myproject
% python3 -m venv ./venv   # virtualevn생성

myproject라는 프로젝트와 폴더를 만들고 venv라는 폴더에 virtualenv를 설정하였다.

이제 virtuanenv를 활성화 한 후 필요한 라이브러리를 설치해보자

% vim requirements.txt # 라이브러리 관리를 위한 requirements파일 생성
# === requirements.txt
fastapi   # fastAPI
uvicorn[standard] # fastAPI를 실행할 uvicorn, standard로 설치해야 websocket등이 정상동작함
jinja2 # thml템필릿
# ====

% source ./venv/bin/activate # viratualenv활성화
(venv) % pip install -r requirements.txt # 라이브러리 설치

unicorn[standard]로 설치해야 websocket등을 정상적으로 사용 가능하다

 

 

WebSocket

이제 프로젝트 기본설정이 끝났으니 간단한 샘플소스를 만들어보자 FastAPI-Websockets 샘플사이트에서 샘플 소스를 확인 할 수 있다. 아래 소스는 이 사이트에서 받을 수 있는 소스를 일부 가공한 후 주석을 추가 하였다.

 

/main.py

실행파일인 main.py파일로 FastAPI app을 생성한 후 테스트용 클라이언트 웹페이지와 웹소켓 연결을 추가한다.

from fastapi import FastAPI, WebSocket, Request

from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from fastapi.logger import logger
​
app = FastAPI()
# html파일을 서비스할 수 있는 jinja설정 (/templates 폴더사용)
templates = Jinja2Templates(directory="templates")
​
# 웹소켓 연결을 테스트 할 수 있는 웹페이지 (http://127.0.0.1:8000/client)
@app.get("/client")
async def client(request: Request):
    # /templates/client.html파일을 response함
    return templates.TemplateResponse("client.html", {"request":request})
​
# 웹소켓 설정 ws://127.0.0.1:8000/ws 로 접속할 수 있음
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    print(f"client connected : {websocket.client}")
    await websocket.accept() # client의 websocket접속 허용
    await websocket.send_text(f"Welcome client : {websocket.client}")
    while True:
        data = await websocket.receive_text()  # client 메시지 수신대기
        print(f"message received : {data} from : {websocket.client}")
        await websocket.send_text(f"Message text was: {data}") # client에 메시지 전달
​
# 개발/디버깅용으로 사용할 앱 구동 함수
def run():
    import uvicorn
    uvicorn.run(app)
​
# python main.py로 실행할경우 수행되는 구문
# uvicorn main:app 으로 실행할 경우 아래 구문은 수행되지 않는다.
if __name__ == "__main__":
    run()

 

/templates/client.html

웹소켓 클라이언트를 테스트할 html페이지로 http://locahost:8000/client로 접속하면 보이는 웹화면의 html 소스로 websocket서버와 연결을 테스트하고 메시지를 보내고 받을수 있다.

<!DOCTYPE html>

<html>
<head>
<title>Chat</title>
</head>
<body>
<h1>WebSocket Chat</h1>
<form action="" onsubmit="sendMessage(event)">
<input type="text" id="messageText" autocomplete="off"/>
<button>Send</button>
</form>
<ul id='messages'>
</ul>
<script>
var ws = new WebSocket("ws://localhost:8000/ws");
ws.onmessage = function(event) {
var messages = document.getElementById('messages')
var message = document.createElement('li')
var content = document.createTextNode(event.data)
message.appendChild(content)
messages.appendChild(message)
};
function sendMessage(event) {
var input = document.getElementById("messageText")
ws.send(input.value)
input.value = ''
event.preventDefault()
}
</script>
</body>
</html>

 

myproject 프로젝트 폴더의 파일 구조는 다음과 같이 된다.

/venv
/templates/client.html
/main.py
/requirements.txt

 

실행

위 과정을 완료 한 후 작성한 파일을 실행해 보자

% source venv/bin/activite   # virtualenv활성화

(venv) % python main.py  # main.py 실행
INFO:     Started server process [1412]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit

main.py가 정상실행될 경우 위와 같은 메시지를 확인할 수 있다.

 

웹 클라이언트 접속 테스트

위 로그에 보이는 http://127.0.0.1:8000 페이지에 접속해보자. 아마 "{"detail":"Not Found"}" 메시지가 나올것이다. 우리가 만든 클라이언트 웹페이지는 http://127.0.0.1:8000/client 이다. 클라이언트 테스트 페이지에 접속하면 다음과 같은 화면을 볼수 있다. 웹 페이지가 로드되면 즉시 웹소켓에 연결하게 되고 웹소켓에 정상적으로 접속된경우 "Welcome client"라는 메시지를 서버로 부터 수신받는다. 그 후 서버에 메시지를 보낸경우 보낸메시지를 다시 "Message text was : {전송한메시지}" 형태로 다시 수신받게 된다.

 

서버로그

서버가 정상 동작한다면 서버에서는 다음과 같은 로그를 확인 할 수 있을 것이다.

client connected : Address(host='127.0.0.1', port=55369)

INFO:     ('127.0.0.1', 55369) - "WebSocket /ws" [accepted]
message received : 안녕! from : Address(host='127.0.0.1', port=55369)

 

 

결론

FastAPI를 이용한 간단한 webSocket서버를 만들어 테스트 해보았다. webSocket을 서비스할 수 있는 서버프레임워크는 다양하다. node.js나 spring으로도 사용이 가능하다. 만약 웹소켓을 사용하는 체팅서버를 만들어야 한다면 서버의 성능을 최대한 활용할 수 있도록 비동기처리가 가능한 서버를 선택할것이고 아마 node.js나 fastAPI중 하나가 되지 않을까? spring에서는 아직 테스트를 해보지 못했지만 아직 비동기처리가 가능한 웹소켓부분의 정보가 많지 않아서 활용하기 힘들것 같다.

그리고 python이라는 강력한 언어적인 장점이 있고, async/await구문으로 비동기 처리가 가능하기 때문에 코딩하기도 쉽고, 이해하기도 쉽다. 아직 좀더 다양한 테스트가 필요할것 같지만 채팅서버로 충분히 쓸만하지 않을까 생각된다.

FastAPI_Websocket.zip
0.00MB

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함