Flask 서버에서 SocketIO를 이용하여 각기 다른 클라이언트(핸드폰)에게 개별적인 응답을 보내야 하는 상황이 있었습니다. 하지만 기본 설정으로는 broadcast 옵션을 사용하여 모든 클라이언트에게 동일한 응답이 전송되도록 설계되어 있었습니다. 이 글에서는 이 문제를 해결하기 위해 시도했던 방법들과 최종적으로 구현한 솔루션에 대해 공유하고자 합니다.
문제 상황
- 목표: Flask 서버에서 SocketIO를 통해 각 클라이언트에게 개별적인 응답을 보내야 합니다.
- 문제점: 기본적으로 broadcast 옵션을 사용하면 모든 클라이언트에게 동일한 응답이 전송됩니다.
시도해본 해결 방법
1. 서버에서 request.sid를 이용하여 sid 값을 Upload 엔드포인트에서 사용
처음에는 Flask 서버에서 request.sid를 이용하여 업로드 엔드포인트에서도 sid 값을 활용하려고 했습니다.
from flask import request
@app.route('/upload', methods=['POST'])
def upload():
sid = request.sid
# 업로드 처리 로직
문제점: 그러나 request.sid는 SocketIO에서 WebSocket 연결을 통해 생성된 세션 ID로, SocketIO 이벤트 핸들러 내에서만 접근 가능합니다. 일반적인 HTTP 요청인 /upload 엔드포인트에서는 request 객체에 sid 속성이 존재하지 않습니다.
왜 그럴까요?
- HTTP 요청과 WebSocket 연결의 차이: HTTP 요청은 클라이언트가 서버에 요청을 보내는 단방향 통신이며, 각 요청은 독립적인 연결로 처리됩니다.
- SocketIO의 sid: SocketIO의 sid는 클라이언트와 서버 간의 지속적인 WebSocket 연결을 나타내는 세션 ID입니다. 이는 SocketIO 이벤트 핸들러에서만 접근 가능합니다.
- 따라서: 일반적인 Flask HTTP 엔드포인트에서는 SocketIO의 sid에 접근할 수 없습니다.
2. 클라이언트 측에서 HTTP로 이미지 업로드할 때 sid 값을 같이 보내기
다음으로 생각한 방법은 클라이언트에서 이미지를 업로드할 때 sid 값을 함께 전송하는 것이었습니다.
문제점: 하지만 이미지를 업로드하는 화면에서는 SocketIO를 사용하지 않기 때문에 클라이언트 측에서 sid 값을 알 수 없었습니다.
- 이미지 업로드 화면: SocketIO를 초기화하지 않았으므로 sid 값이 존재하지 않음.
- 결과: 서버에 sid 값을 전달할 수 없음.
3. 앱 전역에서 SocketIO 연결 관리하기
최종적으로, 앱 전체에서 SocketIO 연결을 공유할 수 있도록 SocketProvider.js라는 컨텍스트를 생성하기로 했습니다.
구현 방법:
- SocketProvider.js 생성: 앱 전역에서 SocketIO를 관리하는 컨텍스트를 만듭니다.
// SocketProvider.jsimport React, { createContext, useEffect, useState } from 'react';import io from 'socket.io-client';
export const SocketContext = createContext();
export const SocketProvider = ({ children }) => {const [socket, setSocket] = useState(null);
useEffect(() => {const newSocket = io('http://your-server-url');setSocket(newSocket);
return () => newSocket.close();}, []);
return (<SocketContext.Provider value={socket}>{children}</SocketContext.Provider>);}; - 앱 루트에 적용: 앱의 루트 컴포넌트를 SocketProvider로 감싸서 모든 하위 컴포넌트에서 SocketIO 객체에 접근할 수 있도록 합니다
// App.jsimport React from 'react';import { SocketProvider } from './SocketProvider';
function App() {return (<SocketProvider>{/* 나머지 컴포넌트 */}</SocketProvider>); - 각 컴포넌트에서 사용: 이제 모든 컴포넌트에서 useContext를 통해 SocketIO 객체와 sid에 접근할 수 있습니다.
// UploadScreen.jsimport React, { useContext } from 'react';import { SocketContext } from './SocketProvider';
const UploadScreen = () => {const socket = useContext(SocketContext);
const handleUpload = () => {const sid = socket.id;// sid와 함께 이미지 업로드 처리};
return (// 업로드 화면 UI);};
export default UploadScreen;
장점:
- 전역 관리: 앱이 시작될 때 서버와 SocketIO 연결을 설정하므로 모든 화면에서 동일한 SocketIO 객체를 사용합니다.
- sid 공유: 모든 컴포넌트에서 socket.id를 통해 sid 값을 가져올 수 있습니다.
- 일관성: HTTP 업로드를 하는 화면과 SocketIO로 결과를 받는 화면 모두에서 동일한 SocketIO 연결을 사용하므로 통신의 일관성이 유지됩니다.
결론
SocketProvider.js라는 컨텍스트를 만들어 앱 전체에서 SocketIO 연결을 공유함으로써 다음과 같은 문제를 해결할 수 있었습니다.
- HTTP 업로드와 함께 sid를 전송: 업로드 화면에서 socket.id를 이용해 sid 값을 서버로 전송할 수 있습니다.
- SocketIO로 결과 수신: 결과를 받아야 하는 화면에서도 동일한 SocketIO 객체를 사용하여 서버로부터 개별적인 응답을 받을 수 있습니다.
'플러팅 AI > Flask Server' 카테고리의 다른 글
| # Flask-SocketIO에서 `connected_clients` 상태 관리 문제 해결 (0) | 2024.11.30 |
|---|---|
| Socket.IO 클라이언트가 메시지를 일부만 수신하는 문제 (3) | 2024.11.27 |
| flask 서버 Docker로 패키징 (2) | 2024.11.20 |
| Yolov5 docker 배포할때 windows path 문제 (3) | 2024.11.20 |
| 서버 Docker 시작전에 모델 로드하기 (0) | 2024.11.20 |