웹 애플리케이션에서 실시간 기능은 사용자 경험을 크게 향상시킵니다. 채팅, 실시간 알림, 라이브 대시보드 등 다양한 기능을 구현하기 위해서는 웹소켓이 필수적입니다. 이 글에서는 Flask와 Flask-SocketIO를 사용하여 실시간 기능을 구현하는 방법을 알아보겠습니다.
웹소켓이란?
웹소켓은 클라이언트와 서버 간에 지속적인 양방향 연결을 제공하는 통신 프로토콜입니다. 기존 HTTP 통신과 달리 한 번 연결이 수립되면 양쪽에서 자유롭게 데이터를 주고받을 수 있어 실시간 애플리케이션에 이상적입니다.
기존 HTTP 통신과 웹소켓의 차이점:
- HTTP: 요청-응답 모델, 클라이언트가 요청해야만 서버가 응답
- 웹소켓: 양방향 통신, 서버가 클라이언트에게 능동적으로 데이터 전송 가능

출처: https://www.scaleway.com/en/blog/iot-hub-what-use-case-for-websockets/
Flask-SocketIO 소개
Flask-SocketIO는 Flask 애플리케이션에 웹소켓 기능을 쉽게 통합할 수 있게 해주는 확장 라이브러리입니다. 이 라이브러리는 Socket.IO 프로토콜을 구현하여 모든 브라우저에서 실시간 기능을 지원합니다.
환경 설정하기
먼저 필요한 패키지를 설치합니다:
pip install flask flask-socketio eventlet
eventlet은 비동기 네트워킹 라이브러리로, Flask-SocketIO의 성능을 향상시키는 데 사용됩니다.
기본 Flask-SocketIO 애플리케이션 구조
간단한 실시간 채팅 애플리케이션을 만들어 보겠습니다. 먼저 기본 애플리케이션 구조를 설정합니다:
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
socketio.run(app, debug=True)
실시간 채팅 기능 구현하기
이제 웹소켓 이벤트 핸들러를 추가하여 채팅 기능을 구현해 보겠습니다:
@socketio.on('connect')
def handle_connect():
print('클라이언트가 연결되었습니다.')
@socketio.on('disconnect')
def handle_disconnect():
print('클라이언트가 연결을 종료했습니다.')
@socketio.on('message')
def handle_message(data):
print('받은 메시지:', data)
# 모든 클라이언트에게 메시지 브로드캐스트
emit('message', data, broadcast=True)
프론트엔드 구현
이제 클라이언트 측 HTML과 JavaScript를 작성해 보겠습니다. ‘templates’ 폴더에 ‘index.html’ 파일을 생성합니다:
<!DOCTYPE html>
<html>
<head>
<title>Flask-SocketIO 채팅</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style>
#messages {
height: 300px;
overflow-y: scroll;
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 10px;
}
</style>
</head>
<body>
<h1>Flask-SocketIO 실시간 채팅</h1>
<div id="messages"></div>
<input type="text" id="username" placeholder="사용자 이름" />
<input type="text" id="message" placeholder="메시지 입력" />
<button id="send">전송</button>
<script>
$(document).ready(function() {
// 소켓 연결
var socket = io();
// 연결 이벤트
socket.on('connect', function() {
$('#messages').append('<p><i>서버에 연결되었습니다.</i></p>');
});
// 메시지 수신 이벤트
socket.on('message', function(data) {
$('#messages').append('<p><strong>' + data.username + '</strong>: ' + data.message + '</p>');
// 스크롤을 항상 아래로 유지
$('#messages').scrollTop($('#messages')[0].scrollHeight);
});
// 메시지 전송
$('#send').click(function() {
var username = $('#username').val() || '익명';
var message = $('#message').val();
if (message) {
socket.emit('message', {
username: username,
message: message
});
$('#message').val('');
}
});
// Enter 키로 메시지 전송
$('#message').keypress(function(e) {
if(e.which == 13) {
$('#send').click();
}
});
});
</script>
</body>
</html>
실시간 알림 시스템 구현하기
채팅 외에도 실시간 알림 시스템을 구현해 보겠습니다. 이는 새로운 이벤트가 발생했을 때 사용자에게 즉시 알림을 보내는 기능입니다.
서버 측 코드에 알림 이벤트 핸들러를 추가합니다:
@socketio.on('notification')
def handle_notification(data):
# 특정 사용자에게만 알림 전송
emit('notification', data, to=data['user_id'])
# 서버에서 알림을 보내는 함수 (다른 라우트에서 호출 가능)
def send_notification(user_id, message):
socketio.emit('notification', {
'user_id': user_id,
'message': message,
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}, to=user_id)
룸(Room) 기능 활용하기
Socket.IO의 룸 기능을 사용하면 특정 그룹의 사용자에게만 메시지를 전송할 수 있습니다. 이는 그룹 채팅이나 특정 주제별 채팅방을 구현할 때 유용합니다.
from flask_socketio import join_room, leave_room
@socketio.on('join')
def on_join(data):
username = data['username']
room = data['room']
join_room(room)
emit('message', {'username': 'System', 'message': username + '님이 ' + room + '방에 입장했습니다.'}, to=room)
@socketio.on('leave')
def on_leave(data):
username = data['username']
room = data['room']
leave_room(room)
emit('message', {'username': 'System', 'message': username + '님이 ' + room + '방을 나갔습니다.'}, to=room)
@socketio.on('room_message')
def handle_room_message(data):
emit('message', data, to=data['room'])
대규모 애플리케이션을 위한 확장
실제 프로덕션 환경에서는 더 많은 사용자와 메시지를 처리해야 할 수 있습니다. 이를 위한 몇 가지 팁을 소개합니다:
- Redis를 메시지 브로커로 사용: 여러 서버 인스턴스 간 메시지 동기화에 유용
- 비동기 모드 사용: eventlet이나 gevent를 사용하여 비동기 처리
- 로드 밸런싱: 여러 서버에 부하 분산
# Redis를 메시지 브로커로 사용하는 예
from flask import Flask
from flask_socketio import SocketIO
app = Flask(__name__)
socketio = SocketIO(app, message_queue='redis://')
보안 고려사항
실시간 애플리케이션을 구현할 때 보안은 매우 중요합니다:
- 사용자 인증 및 권한 확인
- 입력 데이터 검증
- CORS(Cross-Origin Resource Sharing) 설정
- Rate limiting 구현
# CORS 설정 예시
socketio = SocketIO(app, cors_allowed_origins="*")
# 더 엄격한 CORS 설정
socketio = SocketIO(app, cors_allowed_origins=["https://example.com", "https://subdomain.example.com"])
실제 애플리케이션 예시: 실시간 협업 도구 , 구글 Doc, 웨어라유
이러한 기술을 활용하여 실시간 협업 도구를 만들 수 있습니다. 예를 들어, 여러 사용자가 동시에 문서를 편집하고 변경 사항을 실시간으로 볼 수 있는 기능을 구현할 수 있습니다. 대표적인 케이스가 구글 Doc입니다.
채팅 로직을 이용해서 GPS 정보를 실시간 주고 받으면서 위치정보 공유하는 위치공유채팅을 할 수도 있습니다. (웨어라유: https://choonzang.com/geo )
결론
Flask와 Flask-SocketIO를 사용하면 실시간 웹 애플리케이션을 비교적 쉽게 구현할 수 있습니다. 채팅, 알림, 라이브 업데이트 등 다양한 실시간 기능을 통해 사용자 경험을 크게 향상시킬 수 있습니다.
웹소켓 기술은 현대 웹 애플리케이션에서 점점 더 중요해지고 있으며, Flask와 같은 가벼운 프레임워크에서도 이러한 기능을 쉽게 구현할 수 있다는 것은 큰 장점입니다. 이 글에서 소개한 기본 개념과 예제를 바탕으로 여러분만의 실시간 애플리케이션을 개발해 보시기 바랍니다.
답글 남기기