ksundev 님의 블로그

SPA와 WebSocket: 실제론 하나의 HTML⁉️ 본문

[개발] Vue.js

SPA와 WebSocket: 실제론 하나의 HTML⁉️

ksundev 2025. 7. 17. 16:47

웹 개발을 하다 보면 실시간 기능을 구현해야 할 때가 있습니다. 채팅, 알림, 실시간 업데이트 등을 위해 WebSocket을 사용하는데, 이때 중요한 것이 바로 연결의 지속성입니다. 오늘은 SPA(Single Page Application)와 WebSocket의 조합이 왜 강력한지 알아보겠습니다.

전통적인 웹사이트 vs SPA

전통적인 웹사이트 (Multi-Page Application)

전통적인 웹사이트는 여러 개의 HTML 파일로 구성됩니다:

프로젝트 구조:
├── login.html
├── dashboard.html
├── patients.html
└── settings.html

사용자가 페이지를 이동할 때마다:

  1. 새로운 HTML 파일을 서버에서 받아옴
  2. 페이지가 완전히 새로고침됨
  3. 이전 페이지의 모든 JavaScript가 사라짐

SPA (Single Page Application)

SPA는 단 하나의 HTML 파일로 구성됩니다:

프로젝트 구조:
├── index.html (단 하나의 HTML!)
├── src/
│   ├── App.vue
│   ├── components/
│   │   ├── LoginComponent.vue
│   │   ├── DashboardComponent.vue
│   │   └── PatientsComponent.vue
│   └── router/
│       └── index.js

핵심 포인트: 우리가 보는 페이지는 처음부터 끝까지 index.html 하나뿐입니다.

SPA에서 페이지 이동이 일어나는 방식

1. 사용자가 보는 것

브라우저 주소창:
https://hospital.com/login    → 로그인 화면
https://hospital.com/dashboard → 대시보드 화면  
https://hospital.com/patients → 환자 관리 화면

2. 실제로 일어나는 것

<!-- 실제로는 계속 같은 index.html -->
<!DOCTYPE html>
<html>
<head><title>요양보호사 시스템</title></head>
<body>
  <div id="app">
    <!-- App.vue가 여기서 라우팅 담당 -->
  </div>
</body>
</html>

3. App.vue가 하는 일

<template>
  <div id="app">
    <!-- 주소에 따라 다른 컴포넌트 표시 -->
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  mounted() {
    // 앱 시작 시 WebSocket 연결 - 한 번만!
    this.socket = new WebSocket('ws://localhost:8080/ws');
  }
}
</script>

App.vue가 라우터 역할을 하면서 주소에 따라 다른 컴포넌트를 보여줍니다.

페이지 이동 시 실제 동작

전통적인 웹사이트

// login.html에서 WebSocket 연결
const socket = new WebSocket('ws://localhost:8080/ws');

// 사용자가 dashboard.html로 이동
// → 페이지 새로고침 → 모든 JavaScript 사라짐 → WebSocket 끊어짐

// dashboard.html에서 다시 WebSocket 연결 필요
const socket = new WebSocket('ws://localhost:8080/ws'); // 새로운 연결

SPA

// App.vue에서 WebSocket 연결
const socket = new WebSocket('ws://localhost:8080/ws');

// 사용자가 /login → /dashboard → /patients로 이동
// → 페이지 새로고침 없음 → App.vue 유지됨 → WebSocket 연결 유지!

핵심 개념: 같은 HTML, 다른 화면

브라우저 개발자 도구에서 확인하기

  1. Network 탭 확인:
    • 전통적인 웹사이트: 페이지 이동 시마다 HTML 파일 다운로드
    • SPA: 최초 한 번만 HTML 파일 다운로드
  2. Elements 탭 확인:
    • 전통적인 웹사이트: 페이지 이동 시 완전히 새로운 HTML 구조
    • SPA: 계속 같은 HTML 구조, 내용만 변경

실제 예시

<!-- 우리가 보는 페이지는 항상 이것! -->
<!DOCTYPE html>
<html>
<head><title>요양보호사 시스템</title></head>
<body>
  <div id="app">

    <!-- /login 주소일 때 -->
    <div class="login-component">
      <h1>로그인</h1>
      <form>...</form>
    </div>

    <!-- /dashboard 주소일 때 -->
    <div class="dashboard-component">
      <h1>대시보드</h1>
      <div>환자 현황...</div>
    </div>

    <!-- /patients 주소일 때 -->
    <div class="patients-component">
      <h1>환자 관리</h1>
      <table>...</table>
    </div>

  </div>
</body>
</html>

중요: 실제로는 한 번에 하나의 컴포넌트만 표시되지만, 모든 것이 같은 HTML 안에서 일어납니다.

WebSocket 연결이 끊기지 않는 이유

1. HTML이 새로고침되지 않음

전통적인 웹사이트:
login.html → dashboard.html (HTML 교체, 새로고침 발생)

SPA:
index.html → index.html (같은 HTML, 내용만 변경)

2. JavaScript 환경이 유지됨

// App.vue에서 생성한 WebSocket
export default {
  data() {
    return {
      socket: null
    }
  },

  mounted() {
    this.socket = new WebSocket('ws://localhost:8080/ws');
  }
}

// 페이지 이동해도 App.vue는 그대로 있음
// → this.socket도 그대로 유지됨
// → WebSocket 연결도 그대로 유지됨

3. 연결 상태 확인

// 어떤 페이지에 있든 항상 연결 상태
console.log(this.socket.readyState); // 1 (OPEN)

// 페이지 이동 후에도
this.$router.push('/patients');
console.log(this.socket.readyState); // 여전히 1 (OPEN)

실제 사용 예시

요양보호사 관리 시스템

// App.vue - 앱 전체에서 하나의 WebSocket 관리
export default {
  data() {
    return {
      socket: null
    }
  },

  mounted() {
    this.socket = new WebSocket('ws://localhost:8080/ws');

    this.socket.onmessage = (event) => {
      const data = JSON.parse(event.data);

      // 긴급 알림이 오면 어떤 페이지에 있든 표시
      if (data.type === 'emergency_alert') {
        alert(`긴급 상황: ${data.message}`);
      }
    };
  }
}

시나리오:

  1. 사용자가 로그인 페이지에서 로그인
  2. 대시보드로 이동
  3. 환자 관리 페이지로 이동
  4. 모든 페이지에서 실시간 긴급 알림 수신 가능

왜? 같은 HTML 안에서 계속 App.vue가 살아있기 때문입니다.

정리

SPA의 핵심 개념

  • 우리가 보는 페이지는 항상 index.html 하나
  • App.vue가 주소에 따라 다른 컴포넌트를 보여주는 라우터 역할
  • 페이지 이동 = HTML 교체가 아닌 JavaScript로 화면 변경

WebSocket이 끊기지 않는 이유

  • HTML 새로고침 없음
  • JavaScript 환경 유지
  • App.vue에서 생성한 WebSocket 연결 지속

결론

HTML이 새로고침되지 않으니 Socket도 끊기지 않는다

이것이 바로 SPA와 WebSocket 조합이 실시간 웹 애플리케이션에서 강력한 이유입니다. 사용자가 어떤 페이지에 있든 끊김 없는 실시간 경험을 제공할 수 있습니다.