개발 중 WebSocket 서버와 HTTP 서버가 제대로 동작하지 않는 문제가 발생했습니다.
WebSocket connection to 'ws://localhost:3001/' failed:
진짜 하루 종일 붙잡고 있었기에 정말 많은 시도를 해보았습니다….
웹 소켓을 사용하여 프로젝트를 진행해보는 것은 처음이었기에, 도저히 어디서부터 잘못된 것인지 감조차 잡을 수 없었습니다.
심지어 제가 백엔드 구현을 모두 맡아서 한 이후 프론트에서 웹소켓에 접근하려고 했던 것이었기에 백엔드에서 잘못된 건지…. 프론트에서 잘못된 것인지 조차 알 수 없어 너무 답답했습니다….
WebSocket 서버는 초기화가 정상적으로 이루어졌다는 메시지를 출력했지만, 네트워크 연결 상태에서 아무런 응답이 없었고, netstat -tuln | grep 3001
명령어를 통해 확인해도 서버가 해당 포트에서 동작하지 않는 상태였습니다.
이 문제로 인해 하루 종일 시행착오를 겪으며 해결 방법을 찾아야 했고, 방법을 찾은 지금은... 그냥 너무 기쁘고 눈물납니다....😭
문제를 해결하기 위해 아래와 같은 방법들을 시도해봤습니다.
포트 사용 여부 확인
netstat -tuln | grep 3001
이 명령어를 입력했을 때 아무 결과도 나오지 않았습니다. 분명 3001번 포트에서 백엔드 코드가 실행되고 있었지만 말입니다…. 하지만…… 이 때에는 다른 방법을 시도해보느라 더 깊게 파보지 못했습니다..
CORS 설정 검토
CORS 관련 문제가 발생했을 가능성을 고려해 cors
설정을 재확인했습니다.
이런 식으로 프론트 도메인에서 연결을 허용하도록 수정해주었습니다.
const wss = new WebSocketServer({
server,
verifyClient: (info, done) => {
const { origin } = info;
// 특정 도메인에서만 연결을 허용하도록 설정 (CORS)
if (origin === 'http://localhost:5173') {
done(true); // 허용
} else {
done(false, 403, 'Forbidden: Origin not allowed');
}
},
});
하지만 이는 문제와 직접적인 관련이 없었습니다.
HMR(WebSocket 핫 모듈 리플레이스먼트) 비활성화
프로젝트가 Vite 기반이었기 때문에, WebSocket 핫 모듈 리플레이스먼트(HMR)와의 충돌을 의심해 이를 비활성화했습니다.
찾아봤을 때 vite는 웹소켓이 연결되지 않으면 HMR을 자동으로 활성화하여 충돌이 나는 경우도 있다는 것을 알게 되었습니다. 그래서 vite.config.ts
파일도 별의 별 옵션을 다 줘가며 수정해보았습니다만….
server: {
hmr: false,
}
여러 시도 끝에도 아무것도 얻지 못했습니다.
URL 시도
ws://localhost:3001/?token
와 같은 URL에서 WebSocket 서버에 접근하려고 했으나, 다양한 URL로 시도했음에도 연결되지 않았습니다.
이건 정말 혹시나 url 주소가 잘못되진 않았을까 싶어… 시도해본 것이었지만 당연히 해결할 수 없었습니다.
ws://localhost:3001/
ws://localhost:3001/ws
http://localhost:3001/
HTTPS 환경에서 테스트
HTTPS 환경에서 문제가 발생했을 가능성을 배제하기 위해 Vite를 HTTPS로 임시 배포한 뒤 다시 테스트했습니다.
이 또한….. 해결할 수 없었습니다…..
네트워크 탭 확인
네트워크 탭에서 WebSocket 핑/퐁 메시지가 오가는 것을 확인하며, WebSocket 서버 자체는 정상적으로 동작하고 있음을 확인했습니다. 그러나 이 상태에서도 HTTP 서버와 WebSocket 서버 간의 연결은 문제였습니다.
Vite 설정 파일(vite.config.ts) 반복 수정
WebSocket 서버와의 연결을 시도하며 설정 파일을 여러 번 수정해봤습니다.
server: {
proxy: {
'/ws': {
target: 'ws://localhost:3001',
ws: true,
},
},
}
하지만 설정 변경만으로는 문제를 해결할 수 없었습니다.
// initializeWebSocketServer
const wss = new WebSocketServer({
server, ... });
wss.on('connection', (ws, req) => {
console.log('New connection request', req.url);
// URL에서 token 추출
// TODO: 프론트 라우터 및 token 설정 완료 후 테스트
const url = new URL(req.url, `http://${req.headers.host}`);
const token = url.searchParams.get('token');
...
분명 이런 식으로 소켓이 connect
되었을 때 New connection request
라는 메시지가 떠야 하는데, 뜨지 않았습니다.
다만 아래 index.js
파일 (백엔드 프로젝트를 실행시킬 때 root로 실행시키는 파일)에서는 Websocket server initialized successfully.
문구가 제대로 떴습니다.
그래서 백엔드 코드에 조금 더 집중해보았고,
// index.js
const app = express();
app.use(express.json());
// TODO: 프론트 배포 후 origin url 변경
app.use(
cors({
origin: ['http://localhost:5173', 'http://223.130.151.43'],
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
credentials: true,
}),
);
...
// HTTP 서버 생성
const server = http.createServer(app);
// WebSocket 서버 초기화
try {
initializeWebSocketServer(server);
console.log('WebSocket server initialized successfully.');
} catch (error) {
console.error('Failed to initialize WebSocket server:', error);
}
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
결국… HTTP 서버와 WebSocket 서버의 통합 문제라는 것을 알아냈습니다. app.listen(PORT)
과 http.createServer(app)
를 동시에 사용하면서 서버가 각각 독립적으로 실행되어 있었고, 포트를 올바르게 점유하지 못했던 것이 문제였습니다.
HTTP 서버와 WebSocket 서버가 동일한 객체를 공유하지 못하면 네트워크 상에서 요청이 제대로 처리되지 않을 수 있습니다.
최종적으로, HTTP 서버와 WebSocket 서버를 하나의 server
객체로 통합하여 문제를 해결했습니다.
수정된 코드는 아래와 같습니다:
const server = http.createServer(app); // HTTP 서버와 WebSocket 서버 통합
// WebSocket 서버 초기화
try {
initializeWebSocketServer(server);
console.log('WebSocket server initialized successfully.');
} catch (error) {
console.error('Failed to initialize WebSocket server:', error);
}
// 서버 시작
// 원래 app.listen 이었음;;;
server.listen(PORT, () => {
console.log(`Server is running on <http://localhost>:${PORT}`);
});
HTTP와 WebSocket 서버가 하나의 server
객체로 통합되면서 포트가 제대로 열렸습니다.
이제 netstat -tuln | grep 3001
명령어를 실행하면 정상적으로 포트가 점유되어 있고, 클라이언트가 소켓에 접속하면 콘솔창에 뜨는 것도 확인할 수 있었습니다.