WebSocket 서버와 HTTP 서버 문제 해결기

정혜인·2024년 11월 18일
0

🛠️ 문제 상황

개발 중 WebSocket 서버와 HTTP 서버가 제대로 동작하지 않는 문제가 발생했습니다.

WebSocket connection to 'ws://localhost:3001/' failed:

진짜 하루 종일 붙잡고 있었기에 정말 많은 시도를 해보았습니다….

웹 소켓을 사용하여 프로젝트를 진행해보는 것은 처음이었기에, 도저히 어디서부터 잘못된 것인지 감조차 잡을 수 없었습니다.

심지어 제가 백엔드 구현을 모두 맡아서 한 이후 프론트에서 웹소켓에 접근하려고 했던 것이었기에 백엔드에서 잘못된 건지…. 프론트에서 잘못된 것인지 조차 알 수 없어 너무 답답했습니다….

WebSocket 서버는 초기화가 정상적으로 이루어졌다는 메시지를 출력했지만, 네트워크 연결 상태에서 아무런 응답이 없었고, netstat -tuln | grep 3001 명령어를 통해 확인해도 서버가 해당 포트에서 동작하지 않는 상태였습니다.

이 문제로 인해 하루 종일 시행착오를 겪으며 해결 방법을 찾아야 했고, 방법을 찾은 지금은... 그냥 너무 기쁘고 눈물납니다....😭


🔄 해결을 위한 시도들..

문제를 해결하기 위해 아래와 같은 방법들을 시도해봤습니다.

  1. 포트 사용 여부 확인

    netstat -tuln | grep 3001

    이 명령어를 입력했을 때 아무 결과도 나오지 않았습니다. 분명 3001번 포트에서 백엔드 코드가 실행되고 있었지만 말입니다…. 하지만…… 이 때에는 다른 방법을 시도해보느라 더 깊게 파보지 못했습니다..

  2. 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');
          }
        },
      });

    하지만 이는 문제와 직접적인 관련이 없었습니다.

  3. HMR(WebSocket 핫 모듈 리플레이스먼트) 비활성화

    프로젝트가 Vite 기반이었기 때문에, WebSocket 핫 모듈 리플레이스먼트(HMR)와의 충돌을 의심해 이를 비활성화했습니다.

    찾아봤을 때 vite는 웹소켓이 연결되지 않으면 HMR을 자동으로 활성화하여 충돌이 나는 경우도 있다는 것을 알게 되었습니다. 그래서 vite.config.ts 파일도 별의 별 옵션을 다 줘가며 수정해보았습니다만….

    server: {
      hmr: false,
    }

    여러 시도 끝에도 아무것도 얻지 못했습니다.

  4. URL 시도

    ws://localhost:3001/?token와 같은 URL에서 WebSocket 서버에 접근하려고 했으나, 다양한 URL로 시도했음에도 연결되지 않았습니다.

    이건 정말 혹시나 url 주소가 잘못되진 않았을까 싶어… 시도해본 것이었지만 당연히 해결할 수 없었습니다.

    • ws://localhost:3001/
    • ws://localhost:3001/ws
    • http://localhost:3001/
  5. HTTPS 환경에서 테스트

    HTTPS 환경에서 문제가 발생했을 가능성을 배제하기 위해 Vite를 HTTPS로 임시 배포한 뒤 다시 테스트했습니다.

    이 또한….. 해결할 수 없었습니다…..

  6. 네트워크 탭 확인

    네트워크 탭에서 WebSocket 핑/퐁 메시지가 오가는 것을 확인하며, WebSocket 서버 자체는 정상적으로 동작하고 있음을 확인했습니다. 그러나 이 상태에서도 HTTP 서버와 WebSocket 서버 간의 연결은 문제였습니다.

  1. 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 명령어를 실행하면 정상적으로 포트가 점유되어 있고, 클라이언트가 소켓에 접속하면 콘솔창에 뜨는 것도 확인할 수 있었습니다.

0개의 댓글