웰컴스 웹 화면 뷰어 플러그인 개발

2026.05.25

# 웹 기반 원격 지원 화면 뷰어 — 기술 소개서


> 고객이 브라우저만으로 화면을 공유하고, 관리자가 실시간으로 확인하는 WebRTC 기반 원격 지원 시스템


---


# # 1. 개요


## # 1.1 목표


고객이 별도 프로그램 설치 없이 웹 브라우저에서 화면을 공유하면, 관리자가 PIN 번호만으로 해당 화면을 실시간 확인할 수 있는 기능을 WordPress 웹사이트에 추가한다.


## # 1.2 핵심 원칙


| 원칙 | 내용 |

|------|------|

| **설치 없음** | 고객은 브라우저만 열면 됨 |

| **간단한 연결** | 4자리 PIN만으로 연결 |

| **보기만** | 원격 조작 없이 화면 관찰만 |

| **관리자 인증** | 관리자 로그인 시에만 시청 가능 |


## # 1.3 대상 사용자


| 역할 | 행동 | 필요 조건 |

|------|------|-----------|

| **고객** (화면 공유자) | 사이트 접속 → 화면 공유 → PIN 관리자에게 전달 | Chrome/Edge/Firefox 브라우저 |

| **관리자** (화면 시청자) | 사이트 접속 → PIN 입력 → 고객 화면 실시간 확인 | WordPress 관리자 권한 |


---


# # 2. 시스템 아키텍처


## # 2.1 전체 구조


```mermaid

graph TB

    subgraph "고객 브라우저"

        C1["🌐 사이트/remote-support/"]

        C2["📤 getDisplayMedia()"]

        C3["🎥 WebRTC Video Track"]

        C1 -->|"화면 공유 클릭"| C2

        C2 -->|"MediaStream"| C3

    end


    subgraph "서버 (Docker)"

        subgraph "WordPress 컨테이너"

            WP["WordPress + wellcoms-remote 플러그인"]

        end

        subgraph "Signal 컨테이너"

            WS["Node.js Signaling Server<br/>WebSocket"]

            RM["Room Manager<br/>PIN ↔ Room 매칭"]

        end

    end


    subgraph "관리자 브라우저"

        A1["🌐 사이트/remote-support/"]

        A2["📥 RTCPeerConnection"]

        A3["🖥️ 화면 실시간 표시"]

        A1 -->|"PIN 입력"| A2

        A2 -->|"Video Track"| A3

    end


    C3 -->|"P2P WebRTC"| A2

    C1 -->|"1. PIN 생성"| WS

    A1 -->|"2. PIN 연결 요청"| WS

    WS -->|"3. SDP/ICE 교환"| WS

    WS -->|"4. WebRTC 연결 설정"| C3

```


## # 2.2 Docker 구성


```mermaid

graph LR

    subgraph "docker-compose.yml"

        DB["DB<br/>MariaDB"]

        WP["WordPress<br/>Apache+PHP"]

        SIG["Signal<br/>Node.js"]

        TURN["TURN<br/>coturn"]

    end


    WP ---|"DB"| DB

    WP -.->|"WebSocket"| SIG


    CLIENT["고객/관리자<br/>브라우저"] -->|"HTTPS"| WP

    CLIENT -->|"WSS"| SIG

    CLIENT -->|"TURN"| TURN

```


## # 2.3 기술 스택


| 계층 | 기술 | 이유 |

|------|------|------|

| **프론트엔드** | Vanilla JS + WebRTC API | 의존성 최소화 |

| **WordPress 플러그인** | PHP (shortcode) | 기존 아키텍처 유지, 재사용성 |

| **Signaling 서버** | Node.js + `ws` | 경량, WebSocket 지원 |

| **P2P 연결** | WebRTC (Built-in browser API) | 설치 없이 실시간 영상 전송 |

| **NAT 통과** | Google STUN + coturn TURN (자체 서버) | 모든 네트워크 환경에서 연결 보장 |

| **컨테이너** | Docker + docker-compose | 인프라 관리 |


---


# # 3. 연결 시퀀스


## # 3.1 전체 흐름


```mermaid

sequenceDiagram

    actor Customer as 고객

    participant PageC as 고객 페이지

    participant Signal as Signaling 서버

    participant PageA as 관리자 페이지

    actor Admin as 관리자


    Note over Customer,Admin: Phase 1 — PIN 생성


    Customer->>PageC: /remote-support/ 접속

    PageC->>Customer: "화면 공유 시작" 버튼 표시

    Customer->>PageC: 버튼 클릭

    PageC->>Customer: 브라우저 화면 공유 선택 팝업

    Customer->>PageC: "전체 화면" 선택

    PageC->>Signal: WebSocket 연결 + create_room

    Signal-->>PageC: PIN "4827" 생성

    PageC->>Customer: PIN 4827 표시


    Note over Customer,Admin: Phase 2 — 관리자 연결


    Admin->>PageA: /remote-support/ 접속 (로그인 상태)

    PageA->>Admin: PIN 입력 폼 표시

    Admin->>PageA: PIN 4827 입력 + "연결" 클릭

    PageA->>Signal: WebSocket 연결 + join_room {pin: "4827"}


    Note over Customer,Admin: Phase 3 — WebRTC 연결


    Signal-->>PageC: peer_joined 이벤트

    PageC->>Signal: SDP Offer 전송

    Signal->>PageA: SDP Offer 전달

    PageA->>Signal: SDP Answer 전송

    Signal->>PageC: SDP Answer 전달


    loop ICE Candidate 교환

        PageC->>Signal: ICE Candidates

        Signal->>PageA: ICE Candidates

        PageA->>Signal: ICE Candidates

        Signal->>PageC: ICE Candidates

    end


    Note over Customer,Admin: Phase 4 — 실시간 화면 시청


    Customer-->>Admin: WebRTC P2P Video Stream

    Admin->>Admin: 고객 화면 실시간 표시

```


## # 3.2 WebSocket 프로토콜


**클라이언트 → 서버**


| 타입 | 설명 |

|------|------|

| `create_room` | 방 생성 요청 |

| `join_room {pin}` | PIN으로 방 참여 |

| `sdp_offer {sdp}` | WebRTC SDP Offer |

| `sdp_answer {sdp}` | WebRTC SDP Answer |

| `ice_candidate {candidate}` | ICE Candidate |

| `leave_room` | 방 나가기 |


**서버 → 클라이언트**


| 타입 | 설명 |

|------|------|

| `room_created {pin}` | PIN 발급 |

| `peer_joined` | 상대방 입장 알림 |

| `sdp_offer {sdp}` | SDP Offer 전달 |

| `sdp_answer {sdp}` | SDP Answer 전달 |

| `ice_candidate {candidate}` | ICE Candidate 전달 |

| `peer_left` | 상대방 퇴장 알림 |

| `error {message}` | 에러 메시지 |


## # 3.3 메시지 포맷


```json

// 방 생성

{ "type": "create_room" }

→ { "type": "room_created", "pin": "4827" }


// 방 참여

{ "type": "join_room", "pin": "4827" }

→ { "type": "peer_joined" }


// SDP Offer

{ "type": "sdp_offer", "sdp": { "type": "offer", "sdp": "..." } }


// SDP Answer

{ "type": "sdp_answer", "sdp": { "type": "answer", "sdp": "..." } }


// ICE Candidate

{ "type": "ice_candidate", "candidate": { "candidate": "...", "sdpMid": "...", "sdpMLineIndex": 0 } }

```


---


# # 4. 컴포넌트 설계


## # 4.1 WordPress 플러그인 (v2.0.0 — Shortcode 아키텍처)


```mermaid

graph TD

    subgraph "wellcoms-remote/"

        MAIN["wellcoms-remote.php<br/>플러그인 메인 v2.0.0"]

        PAGE["templates/page-remote.php<br/>독립형 원격지원 페이지"]

        SIG["signal/<br/>Signaling 서버"]

        ASSETS["assets/<br/>css/ js/ (예약)"]

    end


    subgraph "Shortcode 모드 (권장)"

        THEME["테마 page 템플릿"]

        SC["[wellcoms_screen_share]<br/>WebRTC 전체 렌더링"]

        THEME -->|"do_shortcode()"| SC

        MAIN -->|"add_shortcode()"| SC

    end


    subgraph "독립형 모드 (다른 사이트용)"

        MAIN -->|"template_include"| PAGE

    end

```


### # 4.1.1 wellcoms-remote.php (메인)


```php

<?php

/**

 * Plugin Name: Wellcoms Remote

 * Description: 웹 기반 원격 지원 화면 뷰어 — shortcode [wellcoms_screen_share]

 * Version: 2.0.0

 */


define("WR_PATH", plugin_dir_path(__FILE__));

define("WR_URL", plugin_dir_url(__FILE__));


// 관리자 판별 (필터로 확장 가능)

function wr_is_admin() {

    $is = current_user_can("manage_options");

    return apply_filters("wr_is_admin", $is);

}


// Shortcode: [wellcoms_screen_share] → WebRTC HTML+CSS+JS 반환

add_shortcode("wellcoms_screen_share", function($atts) { /* ... */ });


// 독립형: 커스텀 템플릿 없는 페이지만 template_include로 대체

add_filter("template_include", function($template) {

    if (is_page("remote-support")) {

        $assigned = get_page_template_slug(get_queried_object_id());

        if ($assigned) return $template;

        return WR_PATH . "templates/page-remote.php";

    }

    return $template;

}, 1);

```


### # 4.1.2 라우팅 전략 (이중 모드)


```mermaid

graph LR

    REQ["/remote-support/ 요청"]

    

    REQ -->|"WordPress 페이지"| CHECK{"커스텀 템플릿<br/>지정됨?"}

    

    CHECK -->|"Yes"| THEME["테마 page 템플릿"]

    CHECK -->|"No"| STANDALONE["독립형 템플릿<br/>(templates/page-remote.php)"]

    

    THEME -->|"do_shortcode()"| SC["[wellcoms_screen_share]<br/>WebRTC 렌더링"]

    

    subgraph "shortcode 내부 분기"

        LOGGED_IN{"관리자<br/>로그인?"}

        LOGGED_IN -->|"Yes"| ADMIN_VIEW["관리자 화면<br/>PIN 입력 + 시청"]

        LOGGED_IN -->|"No"| CUSTOMER_VIEW["고객 화면<br/>화면 공유 + PIN 표시"]

    end

```


### # 4.1.3 다른 사이트에서 사용법


1. 플러그인 설치 + 활성화

2. 페이지에 `[wellcoms_screen_share]` shortcode 삽입

3. (또는 slug가 `remote-support`인 페이지 생성하면 독립형 모드 자동 적용)

4. 관리자 권한 커스터마이징: `add_filter('wr_is_admin', fn() => my_check());`


## # 4.2 Signaling 서버


```mermaid

graph TD

    subgraph "signal/server.js"

        WSS["WebSocket Server"]

        RM["RoomManager"]

        STORE["rooms Map<br/>Map&lt;pin, room&gt;"]

        

        WSS -->|"connection"| CONN["연결 핸들러"]

        CONN -->|"create_room"| RM

        CONN -->|"join_room"| RM

        CONN -->|"sdp_offer"| RELAY["메시지 릴레이"]

        CONN -->|"sdp_answer"| RELAY

        CONN -->|"ice_candidate"| RELAY

        

        RM --> STORE

        RM -->|"4자리 PIN 생성"| GEN["PIN Generator<br/>1000-9999 랜덤<br/>중복 체크"]

    end

```


### # Room 데이터 구조


```javascript

// 서버 메모리에 저장 (DB 불필요)

rooms = new Map(); // pin → room


room = {

    pin: "4827",

    host: WebSocket,          // 고객 연결

    viewer: WebSocket | null, // 관리자 연결

    createdAt: Date.now(),    // 30분 후 자동 삭제

}

```


### # 서버 로직


```mermaid

stateDiagram-v2

    [*] --> Waiting: create_room

    Waiting --> Connected: join_room (관리자 입장)

    Connected --> Signaling: WebRTC 시그널링 시작

    Signaling --> Streaming: P2P 연결 완료

    Streaming --> Waiting: 관리자 퇴장

    Streaming --> Ended: 고객 퇴장

    Waiting --> Ended: 고객 퇴장

    Ended --> [*]

    

    Waiting --> Ended: 30분 타임아웃

```


## # 4.3 프론트엔드 (WebRTC)


```mermaid

graph TD

    subgraph "WebRTC 로직"

        UI["UI Controller"]

        WS["WebSocket Client"]

        RTC["WebRTC Manager"]

        

        UI -->|"공유 시작"| SCREEN["getDisplayMedia()"]

        UI -->|"PIN 입력"| CONNECT["연결 시도"]

        

        SCREEN -->|"MediaStream"| RTC

        CONNECT -->|"join_room"| WS

        

        WS -->|"sdp_offer/answer"| RTC

        WS -->|"ice_candidate"| RTC

        

        RTC -->|"Video Track"| VIDEO["&lt;video&gt; 엘리먼트"]

    end

```


### # 고객 측 (화면 공유자)


```mermaid

sequenceDiagram

    participant C as 고객 JS

    participant WS as WebSocket

    participant PC as RTCPeerConnection


    C->>C: getDisplayMedia({video: true})

    C->>WS: create_room

    WS-->>C: room_created {pin: "4827"}

    C->>C: PIN 화면에 표시


    Note over C: 관리자 대기 중...


    WS-->>C: peer_joined

    

    C->>PC: new RTCPeerConnection(config)

    C->>PC: addTrack(screenStream)

    C->>PC: createOffer()

    PC-->>C: SDP Offer

    C->>WS: sdp_offer {sdp}

    

    WS-->>C: sdp_answer {sdp}

    C->>PC: setRemoteDescription(answer)

    

    loop ICE

        PC-->>C: onicecandidate

        C->>WS: ice_candidate

        WS-->>C: ice_candidate

        C->>PC: addIceCandidate

    end

    

    Note over C: 스트리밍 중...

```


### # 관리자 측 (화면 시청자)


```mermaid

sequenceDiagram

    participant A as 관리자 JS

    participant WS as WebSocket

    participant PC as RTCPeerConnection


    A->>WS: join_room {pin: "4827"}

    

    WS-->>A: sdp_offer {sdp}

    

    A->>PC: new RTCPeerConnection(config)

    A->>PC: setRemoteDescription(offer)

    A->>PC: createAnswer()

    PC-->>A: SDP Answer

    A->>WS: sdp_answer {sdp}

    

    loop ICE

        PC-->>A: onicecandidate

        A->>WS: ice_candidate

        WS-->>A: ice_candidate

        A->>PC: addIceCandidate

    end

    

    PC-->>A: ontrack (video)

    A->>A: video.srcObject = stream

    Note over A: 고객 화면 실시간 표시

```


---


# # 5. UI/UX 설계


## # 5.1 고객 화면 (비로그인)


```mermaid

graph TD

    START["고객 접속<br/>/remote-support/"] --> VIEW["안내 문구 +<br/>🔵 화면 공유 시작 버튼"]

    VIEW -->|"클릭"| PICK["브라우저 화면 선택 팝업"]

    PICK -->|"화면 선택"| LIVE["📺 화면 공유 중<br/>PIN: 4827 표시"]

    PICK -->|"취소"| VIEW

    LIVE -->|"공유 중지"| VIEW

```


**화면 공유 중 UI:**


| 요소 | 내용 |

|------|------|

| 상태 | 📺 화면 공유 중 |

| PIN | 4자리 숫자 크게 표시 (예: `4 8 2 7`) |

| 안내 | "이 번호를 상담원에게 알려주세요" |

| 버튼 | [공유 중지] |

| 하단 안내 | ℹ️ 상담원은 화면을 볼 수만 있으며 원격 조작은 하지 않습니다 |


## # 5.2 관리자 화면 (로그인)


```mermaid

graph TD

    ADMIN["관리자 접속<br/>/remote-support/"] --> FORM["PIN 입력 폼"]

    FORM -->|"PIN 입력 + 연결"| CONNECT["WebRTC 연결"]

    CONNECT -->|"연결 성공"| WATCH["🖥️ 고객 화면 실시간 표시"]

    CONNECT -->|"연결 실패"| ERROR["에러 토스트 표시"]

    WATCH -->|"연결 종료"| FORM

```


---


# # 6. 보안 설계


## # 6.1 위협 모델과 대응


```mermaid

graph LR

    subgraph "위협"

        T1["무단 시청"]

        T2["PIN 브루트포스"]

        T3["중간자 공격"]

        T4["방 해킹"]

    end

    

    subgraph "대응"

        D1["WordPress 관리자 인증"]

        D2["PIN 시도 횟수 제한"]

        D3["WSS (TLS) 암호화"]

        D4["PIN 1회용 + 30분 만료"]

        D5["1방 = 1관리자만"]

    end

    

    T1 --> D1

    T2 --> D2

    T3 --> D3

    T4 --> D4

    T4 --> D5

```


## # 6.2 보안 체크리스트


| 항목 | 구현 |

|------|------|

| Signaling 서버 | WSS (TLS) 필수 |

| 관리자 인증 | WordPress 권한 확인 (필터로 커스터마이징 가능) |

| PIN 만료 | 30분 후 자동 삭제 |

| PIN 시도 제한 | 5회 실패 시 5분 차단 |

| 방 인원 제한 | 1방 = 고객 1명 + 관리자 1명 |

| 고객 화면 선택 | `getDisplayMedia()` — 고객이 공유할 화면 직접 선택 |

| WebRTC 암호화 | SRTP — WebRTC 기본 암호화 |

| 연결 종료 | 고객이 언제든 "공유 중지" 가능 |


---


# # 7. Docker 구성


## # 7.1 docker-compose.yml


```yaml

services:

  wordpress:

    image: wordpress:latest

    volumes:

      - ./data:/var/www/html

    networks:

      - backend

      - web


  signal:

    image: wellcoms-signal:latest

    environment:

      SIGNAL_PORT: "3000"

    networks:

      - web


  turn:

    image: coturn/coturn:latest

    network_mode: host

    volumes:

      - ./turn/turnserver.conf:/etc/coturn/turnserver.conf:ro


networks:

  web:

    external: true

  backend:

    external: true

```


## # 7.2 TURN 서버 설정 (`turnserver.conf`)


```

listening-port=3478

tls-listening-port=5349

external-ip=<YOUR_PUBLIC_IP>

realm=your-domain.com

user=<TURN_USERNAME>:<TURN_PASSWORD>

log-file=stdout

fingerprint

min-port=49152

max-port=65535

no-cli

```


## # 7.3 리버스 프록시 설정 (Caddy 예시)


```

# Signaling WebSocket 프록시

handle /signal/* {

    reverse_proxy signal:3000 {

        header_up X-Real-IP {remote}

        header_up X-Forwarded-For {remote}

    }

}


# TURN 서버는 network_mode: host로 직접 포트 3478 노출

# 리버스 프록시 불필요

```


---


# # 8. 파일 구조


```

wp-wellcoms-remote/                         ← GitLab 리포

├── wellcoms-remote.php                     ← 플러그인 메인 v2.0.0 (shortcode + template_include)

├── templates/

│   └── page-remote.php                     ← 독립형 원격지원 페이지 (템플릿 없는 사이트용)

├── assets/

│   ├── js/                                 ← (예약)

│   └── css/                                ← (예약)

├── signal/                                 ← Signaling 서버

│   ├── server.js                           ← Node.js WebSocket 서버

│   ├── package.json

│   └── Dockerfile

└── README.md

```


**테마 연동 예시:**


```

themes/your-theme/

└── page-remote-support.php                 ← 원격지원 페이지 템플릿

    ├── 페이지 레이아웃, 안내 문구, 다운로드 섹션 등

    └── <?php echo do_shortcode('[wellcoms_screen_share]'); ?>  ← WebRTC 섹션

```


---


# # 9. 구현 단계


## # Phase 1: 기반 구축 ✅


| 작업 | 상태 | 비고 |

|------|------|------|

| Node.js + ws Signaling 서버 | ✅ | Docker 컨테이너 |

| Room Manager (PIN 생성/매칭) | ✅ | 4자리 PIN, 30분 타임아웃 |

| 메시지 릴레이 | ✅ | SDP/ICE 교환 |

| WordPress 페이지 템플릿 | ✅ | shortcode + 독립형 |

| Docker Compose 구성 | ✅ | signal 컨테이너 |

| 리버스 프록시 WSS 설정 | ✅ | `/signal/*` → signal:3000 |


## # Phase 2: WebRTC 연결 ✅


| 작업 | 상태 | 비고 |

|------|------|------|

| getDisplayMedia 화면 캡처 | ✅ | 고객 측 |

| RTCPeerConnection (Offer/Answer) | ✅ | SDP 교환 |

| ICE Candidate 교환 | ✅ | Signaling 서버 경유 |

| PIN 입력 UI | ✅ | 4칸 입력 + 자동 포커스 |

| Video 재생 | ✅ | `<video>` 엘리먼트 |

| ICE disconnect 처리 | ✅ | 1.5초 지연 후 재연결 시도 |


## # Phase 2A: TURN 서버 ✅


| 작업 | 상태 | 비고 |

|------|------|------|

| coturn Docker 컨테이너 | ✅ | network_mode: host |

| TURN 설정 (turnserver.conf) | ✅ | 포트 3478 |

| WebRTC iceServers 업데이트 | ✅ | TURN UDP + TCP 추가 |


## # Phase 3: UI/UX + 안정화 ✅


| 작업 | 상태 | 비고 |

|------|------|------|

| 고객/관리자 통합 페이지 | ✅ | 권한으로 관리자 구분 |

| 2컬럼 레이아웃 (좌:WebRTC / 우:프로그램 정보) | ✅ | 카카오톡 상담 버튼 포함 |

| 반응형 (모바일) | ✅ | 모바일에서도 동작 |

| 관리자 인증 | ✅ | WordPress 권한 + 필터 확장 |

| 에러 핸들링 | ✅ | toast 알림 |

| 연결 끊김 처리 | ✅ | intentionalClose 플래그 |

| 연결 유형 표시 | ✅ | P2P / TURN 릴레이 배지 |

| 모바일 전체화면 | ✅ | 핀치투줌 + 가로 회전 |


## # Phase 4: 배포 ✅


| 작업 | 상태 | 비고 |

|------|------|------|

| Docker 배포 | ✅ | signal, turn 컨테이너 |

| 리버스 프록시 설정 | ✅ | WSS 프록시 적용 |

| SSL 인증서 | ✅ | 자동 관리 |

| 테스트 | ✅ | 외부망 연결 테스트 완료 |


## # Phase 5: 플러그인 분리 리팩토링 ✅


| 작업 | 상태 | 비고 |

|------|------|------|

| 테마 템플릿에서 WebRTC 코드 분리 | ✅ | CSS/HTML/JS 약 500줄 이전 |

| 플러그인 shortcode 등록 | ✅ | `[wellcoms_screen_share]` — WebRTC 전체 렌더링 |

| 관리자 판별 함수 `wr_is_admin()` | ✅ | 권한 체크 + 필터 확장 |

| template_include 이중 모드 | ✅ | 커스텀 템플릿 있으면 shortcode 모드, 없으면 독립형 |

| 테마 템플릿 `do_shortcode()` 교체 | ✅ | 745줄 → 230줄 |

| 버전 업 | ✅ | v1.0.0 → v2.0.0 |


---


# # 10. WebRTC 설정


## # 10.1 RTCPeerConnection Config


```javascript

var rtcConfig = {

  iceServers: [

    { urls: 'stun:stun.l.google.com:19302' },

    { urls: 'stun:stun1.l.google.com:19302' },

    { urls: 'turn:your-domain.com:3478', username: '<USER>', credential: '<PASS>' },

    { urls: 'turn:your-domain.com:3478?transport=tcp', username: '<USER>', credential: '<PASS>' }

  ]

};

```


## # 10.2 getDisplayMedia 옵션


```javascript

const screenStream = await navigator.mediaDevices.getDisplayMedia({

    video: {

        displaySurface: 'monitor',  // 전체 화면 권장

        width: { ideal: 1920 },

        height: { ideal: 1080 },

        frameRate: { ideal: 15 }     // 원격지원은 15fps면 충분

    },

    audio: false  // 화면만 공유

});

```


## # 10.3 NAT 통과 시나리오


```mermaid

graph TD

    A["WebRTC 연결 시도"] --> B{"STUN으로<br/>P2P 연결?"}

    B -->|"성공 (80~90%)"| C["✅ 직접 P2P 연결"]

    B -->|"실패 (10~20%)"| D["coturn TURN 서버<br/>릴레이 연결"]

    D --> E["✅ TURN 릴레이로<br/>연결 성공"]

    

    style C fill:#4ade80

    style E fill:#4ade80

```


---


# # 11. 에러 처리


## # 11.1 시나리오별 대응


| 상황 | 감지 | 대응 |

|------|------|------|

| **고객이 공유 중지** | `stream.onended` 이벤트 | 관리자에게 "고객이 공유를 중지했습니다" 알림 |

| **관리자가 연결 종료** | `WebSocket.onclose` | 고객에게 "상담이 종료되었습니다" 알림 |

| **네트워크 끊김** | `ICE connectionState = 'disconnected'` | 1.5초 대기 후 "연결이 불안정합니다" 알림 |

| **PIN 만료** | 30분 타임아웃 | 고객에게 "PIN이 만료되었습니다. 다시 시도해주세요" 안내 |

| **잘못된 PIN** | 서버에서 room not found | "PIN이 올바르지 않습니다" |

| **브라우저 미지원** | `!navigator.mediaDevices.getDisplayMedia` | "이 브라우저는 화면 공유를 지원하지 않습니다" |

| **고객이 화면 선택 취소** | `getDisplayMedia()` reject | 아무 일도 안 함 (대기 상태 유지) |


---


# # 12. 추후 확장


```mermaid

graph TD

    P1["Phase 1<br/>화면 보기만<br/>(구현 완료)"]

    P2A["Phase 2A<br/>TURN 서버 추가<br/>(구현 완료)"]

    P2B["Phase 2B<br/>음성 채팅 추가<br/>(WebRTC Audio)"]

    P3["Phase 3<br/>원격 조작<br/>(포터블 .exe 필요)"]

    

    P1 --> P2A

    P1 --> P2B

    P2A --> P3

    P2B --> P3

```


---


# # 13. 브라우저 호환성


| 브라우저 | getDisplayMedia | WebRTC | 비고 |

|----------|----------------|--------|------|

| Chrome 94+ | ✅ | ✅ | 권장 |

| Edge 94+ | ✅ | ✅ | 권장 |

| Firefox 66+ | ✅ | ✅ | 지원 |

| Safari 15+ | ✅ | ✅ | 제한적 (모바일은 미지원 가능) |

| 모바일 Chrome | ❌ | ✅ | 화면 공유 미지원 (관리자 시청만 가능) |

| 모바일 Safari | ❌ | ✅ | 화면 공유 미지원 |


> 고객(공유자)은 **데스크톱 브라우저** 필요. 관리자(시청자)는 모바일에서도 가능.


▣ 마크 다운(Markdown) 문서(Mermaid 포함) 지원합니다.
글보기
제목웰컴스 웹 화면 뷰어 플러그인 개발 2026-05-25 16:55
카테고리 기술노트
작성자 Level 10

# 웹 기반 원격 지원 화면 뷰어 — 기술 소개서


> 고객이 브라우저만으로 화면을 공유하고, 관리자가 실시간으로 확인하는 WebRTC 기반 원격 지원 시스템


---


## 1. 개요


### 1.1 목표


고객이 별도 프로그램 설치 없이 웹 브라우저에서 화면을 공유하면, 관리자가 PIN 번호만으로 해당 화면을 실시간 확인할 수 있는 기능을 WordPress 웹사이트에 추가한다.


### 1.2 핵심 원칙


| 원칙 | 내용 |

|------|------|

| **설치 없음** | 고객은 브라우저만 열면 됨 |

| **간단한 연결** | 4자리 PIN만으로 연결 |

| **보기만** | 원격 조작 없이 화면 관찰만 |

| **관리자 인증** | 관리자 로그인 시에만 시청 가능 |


### 1.3 대상 사용자


| 역할 | 행동 | 필요 조건 |

|------|------|-----------|

| **고객** (화면 공유자) | 사이트 접속 → 화면 공유 → PIN 관리자에게 전달 | Chrome/Edge/Firefox 브라우저 |

| **관리자** (화면 시청자) | 사이트 접속 → PIN 입력 → 고객 화면 실시간 확인 | WordPress 관리자 권한 |


---


## 2. 시스템 아키텍처


### 2.1 전체 구조


```mermaid

graph TB

    subgraph "고객 브라우저"

        C1["🌐 사이트/remote-support/"]

        C2["📤 getDisplayMedia()"]

        C3["🎥 WebRTC Video Track"]

        C1 -->|"화면 공유 클릭"| C2

        C2 -->|"MediaStream"| C3

    end


    subgraph "서버 (Docker)"

        subgraph "WordPress 컨테이너"

            WP["WordPress + wellcoms-remote 플러그인"]

        end

        subgraph "Signal 컨테이너"

            WS["Node.js Signaling Server<br/>WebSocket"]

            RM["Room Manager<br/>PIN ↔ Room 매칭"]

        end

    end


    subgraph "관리자 브라우저"

        A1["🌐 사이트/remote-support/"]

        A2["📥 RTCPeerConnection"]

        A3["🖥️ 화면 실시간 표시"]

        A1 -->|"PIN 입력"| A2

        A2 -->|"Video Track"| A3

    end


    C3 -->|"P2P WebRTC"| A2

    C1 -->|"1. PIN 생성"| WS

    A1 -->|"2. PIN 연결 요청"| WS

    WS -->|"3. SDP/ICE 교환"| WS

    WS -->|"4. WebRTC 연결 설정"| C3

```


### 2.2 Docker 구성


```mermaid

graph LR

    subgraph "docker-compose.yml"

        DB["DB<br/>MariaDB"]

        WP["WordPress<br/>Apache+PHP"]

        SIG["Signal<br/>Node.js"]

        TURN["TURN<br/>coturn"]

    end


    WP ---|"DB"| DB

    WP -.->|"WebSocket"| SIG


    CLIENT["고객/관리자<br/>브라우저"] -->|"HTTPS"| WP

    CLIENT -->|"WSS"| SIG

    CLIENT -->|"TURN"| TURN

```


### 2.3 기술 스택


| 계층 | 기술 | 이유 |

|------|------|------|

| **프론트엔드** | Vanilla JS + WebRTC API | 의존성 최소화 |

| **WordPress 플러그인** | PHP (shortcode) | 기존 아키텍처 유지, 재사용성 |

| **Signaling 서버** | Node.js + `ws` | 경량, WebSocket 지원 |

| **P2P 연결** | WebRTC (Built-in browser API) | 설치 없이 실시간 영상 전송 |

| **NAT 통과** | Google STUN + coturn TURN (자체 서버) | 모든 네트워크 환경에서 연결 보장 |

| **컨테이너** | Docker + docker-compose | 인프라 관리 |


---


## 3. 연결 시퀀스


### 3.1 전체 흐름


```mermaid

sequenceDiagram

    actor Customer as 고객

    participant PageC as 고객 페이지

    participant Signal as Signaling 서버

    participant PageA as 관리자 페이지

    actor Admin as 관리자


    Note over Customer,Admin: Phase 1 — PIN 생성


    Customer->>PageC: /remote-support/ 접속

    PageC->>Customer: "화면 공유 시작" 버튼 표시

    Customer->>PageC: 버튼 클릭

    PageC->>Customer: 브라우저 화면 공유 선택 팝업

    Customer->>PageC: "전체 화면" 선택

    PageC->>Signal: WebSocket 연결 + create_room

    Signal-->>PageC: PIN "4827" 생성

    PageC->>Customer: PIN 4827 표시


    Note over Customer,Admin: Phase 2 — 관리자 연결


    Admin->>PageA: /remote-support/ 접속 (로그인 상태)

    PageA->>Admin: PIN 입력 폼 표시

    Admin->>PageA: PIN 4827 입력 + "연결" 클릭

    PageA->>Signal: WebSocket 연결 + join_room {pin: "4827"}


    Note over Customer,Admin: Phase 3 — WebRTC 연결


    Signal-->>PageC: peer_joined 이벤트

    PageC->>Signal: SDP Offer 전송

    Signal->>PageA: SDP Offer 전달

    PageA->>Signal: SDP Answer 전송

    Signal->>PageC: SDP Answer 전달


    loop ICE Candidate 교환

        PageC->>Signal: ICE Candidates

        Signal->>PageA: ICE Candidates

        PageA->>Signal: ICE Candidates

        Signal->>PageC: ICE Candidates

    end


    Note over Customer,Admin: Phase 4 — 실시간 화면 시청


    Customer-->>Admin: WebRTC P2P Video Stream

    Admin->>Admin: 고객 화면 실시간 표시

```


### 3.2 WebSocket 프로토콜


**클라이언트 → 서버**


| 타입 | 설명 |

|------|------|

| `create_room` | 방 생성 요청 |

| `join_room {pin}` | PIN으로 방 참여 |

| `sdp_offer {sdp}` | WebRTC SDP Offer |

| `sdp_answer {sdp}` | WebRTC SDP Answer |

| `ice_candidate {candidate}` | ICE Candidate |

| `leave_room` | 방 나가기 |


**서버 → 클라이언트**


| 타입 | 설명 |

|------|------|

| `room_created {pin}` | PIN 발급 |

| `peer_joined` | 상대방 입장 알림 |

| `sdp_offer {sdp}` | SDP Offer 전달 |

| `sdp_answer {sdp}` | SDP Answer 전달 |

| `ice_candidate {candidate}` | ICE Candidate 전달 |

| `peer_left` | 상대방 퇴장 알림 |

| `error {message}` | 에러 메시지 |


### 3.3 메시지 포맷


```json

// 방 생성

{ "type": "create_room" }

→ { "type": "room_created", "pin": "4827" }


// 방 참여

{ "type": "join_room", "pin": "4827" }

→ { "type": "peer_joined" }


// SDP Offer

{ "type": "sdp_offer", "sdp": { "type": "offer", "sdp": "..." } }


// SDP Answer

{ "type": "sdp_answer", "sdp": { "type": "answer", "sdp": "..." } }


// ICE Candidate

{ "type": "ice_candidate", "candidate": { "candidate": "...", "sdpMid": "...", "sdpMLineIndex": 0 } }

```


---


## 4. 컴포넌트 설계


### 4.1 WordPress 플러그인 (v2.0.0 — Shortcode 아키텍처)


```mermaid

graph TD

    subgraph "wellcoms-remote/"

        MAIN["wellcoms-remote.php<br/>플러그인 메인 v2.0.0"]

        PAGE["templates/page-remote.php<br/>독립형 원격지원 페이지"]

        SIG["signal/<br/>Signaling 서버"]

        ASSETS["assets/<br/>css/ js/ (예약)"]

    end


    subgraph "Shortcode 모드 (권장)"

        THEME["테마 page 템플릿"]

        SC["[wellcoms_screen_share]<br/>WebRTC 전체 렌더링"]

        THEME -->|"do_shortcode()"| SC

        MAIN -->|"add_shortcode()"| SC

    end


    subgraph "독립형 모드 (다른 사이트용)"

        MAIN -->|"template_include"| PAGE

    end

```


#### 4.1.1 wellcoms-remote.php (메인)


```php

<?php

/**

 * Plugin Name: Wellcoms Remote

 * Description: 웹 기반 원격 지원 화면 뷰어 — shortcode [wellcoms_screen_share]

 * Version: 2.0.0

 */


define("WR_PATH", plugin_dir_path(__FILE__));

define("WR_URL", plugin_dir_url(__FILE__));


// 관리자 판별 (필터로 확장 가능)

function wr_is_admin() {

    $is = current_user_can("manage_options");

    return apply_filters("wr_is_admin", $is);

}


// Shortcode: [wellcoms_screen_share] → WebRTC HTML+CSS+JS 반환

add_shortcode("wellcoms_screen_share", function($atts) { /* ... */ });


// 독립형: 커스텀 템플릿 없는 페이지만 template_include로 대체

add_filter("template_include", function($template) {

    if (is_page("remote-support")) {

        $assigned = get_page_template_slug(get_queried_object_id());

        if ($assigned) return $template;

        return WR_PATH . "templates/page-remote.php";

    }

    return $template;

}, 1);

```


#### 4.1.2 라우팅 전략 (이중 모드)


```mermaid

graph LR

    REQ["/remote-support/ 요청"]

    

    REQ -->|"WordPress 페이지"| CHECK{"커스텀 템플릿<br/>지정됨?"}

    

    CHECK -->|"Yes"| THEME["테마 page 템플릿"]

    CHECK -->|"No"| STANDALONE["독립형 템플릿<br/>(templates/page-remote.php)"]

    

    THEME -->|"do_shortcode()"| SC["[wellcoms_screen_share]<br/>WebRTC 렌더링"]

    

    subgraph "shortcode 내부 분기"

        LOGGED_IN{"관리자<br/>로그인?"}

        LOGGED_IN -->|"Yes"| ADMIN_VIEW["관리자 화면<br/>PIN 입력 + 시청"]

        LOGGED_IN -->|"No"| CUSTOMER_VIEW["고객 화면<br/>화면 공유 + PIN 표시"]

    end

```


#### 4.1.3 다른 사이트에서 사용법


1. 플러그인 설치 + 활성화

2. 페이지에 `[wellcoms_screen_share]` shortcode 삽입

3. (또는 slug가 `remote-support`인 페이지 생성하면 독립형 모드 자동 적용)

4. 관리자 권한 커스터마이징: `add_filter('wr_is_admin', fn() => my_check());`


### 4.2 Signaling 서버


```mermaid

graph TD

    subgraph "signal/server.js"

        WSS["WebSocket Server"]

        RM["RoomManager"]

        STORE["rooms Map<br/>Map&lt;pin, room&gt;"]

        

        WSS -->|"connection"| CONN["연결 핸들러"]

        CONN -->|"create_room"| RM

        CONN -->|"join_room"| RM

        CONN -->|"sdp_offer"| RELAY["메시지 릴레이"]

        CONN -->|"sdp_answer"| RELAY

        CONN -->|"ice_candidate"| RELAY

        

        RM --> STORE

        RM -->|"4자리 PIN 생성"| GEN["PIN Generator<br/>1000-9999 랜덤<br/>중복 체크"]

    end

```


#### Room 데이터 구조


```javascript

// 서버 메모리에 저장 (DB 불필요)

rooms = new Map(); // pin → room


room = {

    pin: "4827",

    host: WebSocket,          // 고객 연결

    viewer: WebSocket | null, // 관리자 연결

    createdAt: Date.now(),    // 30분 후 자동 삭제

}

```


#### 서버 로직


```mermaid

stateDiagram-v2

    [*] --> Waiting: create_room

    Waiting --> Connected: join_room (관리자 입장)

    Connected --> Signaling: WebRTC 시그널링 시작

    Signaling --> Streaming: P2P 연결 완료

    Streaming --> Waiting: 관리자 퇴장

    Streaming --> Ended: 고객 퇴장

    Waiting --> Ended: 고객 퇴장

    Ended --> [*]

    

    Waiting --> Ended: 30분 타임아웃

```


### 4.3 프론트엔드 (WebRTC)


```mermaid

graph TD

    subgraph "WebRTC 로직"

        UI["UI Controller"]

        WS["WebSocket Client"]

        RTC["WebRTC Manager"]

        

        UI -->|"공유 시작"| SCREEN["getDisplayMedia()"]

        UI -->|"PIN 입력"| CONNECT["연결 시도"]

        

        SCREEN -->|"MediaStream"| RTC

        CONNECT -->|"join_room"| WS

        

        WS -->|"sdp_offer/answer"| RTC

        WS -->|"ice_candidate"| RTC

        

        RTC -->|"Video Track"| VIDEO["&lt;video&gt; 엘리먼트"]

    end

```


#### 고객 측 (화면 공유자)


```mermaid

sequenceDiagram

    participant C as 고객 JS

    participant WS as WebSocket

    participant PC as RTCPeerConnection


    C->>C: getDisplayMedia({video: true})

    C->>WS: create_room

    WS-->>C: room_created {pin: "4827"}

    C->>C: PIN 화면에 표시


    Note over C: 관리자 대기 중...


    WS-->>C: peer_joined

    

    C->>PC: new RTCPeerConnection(config)

    C->>PC: addTrack(screenStream)

    C->>PC: createOffer()

    PC-->>C: SDP Offer

    C->>WS: sdp_offer {sdp}

    

    WS-->>C: sdp_answer {sdp}

    C->>PC: setRemoteDescription(answer)

    

    loop ICE

        PC-->>C: onicecandidate

        C->>WS: ice_candidate

        WS-->>C: ice_candidate

        C->>PC: addIceCandidate

    end

    

    Note over C: 스트리밍 중...

```


#### 관리자 측 (화면 시청자)


```mermaid

sequenceDiagram

    participant A as 관리자 JS

    participant WS as WebSocket

    participant PC as RTCPeerConnection


    A->>WS: join_room {pin: "4827"}

    

    WS-->>A: sdp_offer {sdp}

    

    A->>PC: new RTCPeerConnection(config)

    A->>PC: setRemoteDescription(offer)

    A->>PC: createAnswer()

    PC-->>A: SDP Answer

    A->>WS: sdp_answer {sdp}

    

    loop ICE

        PC-->>A: onicecandidate

        A->>WS: ice_candidate

        WS-->>A: ice_candidate

        A->>PC: addIceCandidate

    end

    

    PC-->>A: ontrack (video)

    A->>A: video.srcObject = stream

    Note over A: 고객 화면 실시간 표시

```


---


## 5. UI/UX 설계


### 5.1 고객 화면 (비로그인)


```mermaid

graph TD

    START["고객 접속<br/>/remote-support/"] --> VIEW["안내 문구 +<br/>🔵 화면 공유 시작 버튼"]

    VIEW -->|"클릭"| PICK["브라우저 화면 선택 팝업"]

    PICK -->|"화면 선택"| LIVE["📺 화면 공유 중<br/>PIN: 4827 표시"]

    PICK -->|"취소"| VIEW

    LIVE -->|"공유 중지"| VIEW

```


**화면 공유 중 UI:**


| 요소 | 내용 |

|------|------|

| 상태 | 📺 화면 공유 중 |

| PIN | 4자리 숫자 크게 표시 (예: `4 8 2 7`) |

| 안내 | "이 번호를 상담원에게 알려주세요" |

| 버튼 | [공유 중지] |

| 하단 안내 | ℹ️ 상담원은 화면을 볼 수만 있으며 원격 조작은 하지 않습니다 |


### 5.2 관리자 화면 (로그인)


```mermaid

graph TD

    ADMIN["관리자 접속<br/>/remote-support/"] --> FORM["PIN 입력 폼"]

    FORM -->|"PIN 입력 + 연결"| CONNECT["WebRTC 연결"]

    CONNECT -->|"연결 성공"| WATCH["🖥️ 고객 화면 실시간 표시"]

    CONNECT -->|"연결 실패"| ERROR["에러 토스트 표시"]

    WATCH -->|"연결 종료"| FORM

```


---


## 6. 보안 설계


### 6.1 위협 모델과 대응


```mermaid

graph LR

    subgraph "위협"

        T1["무단 시청"]

        T2["PIN 브루트포스"]

        T3["중간자 공격"]

        T4["방 해킹"]

    end

    

    subgraph "대응"

        D1["WordPress 관리자 인증"]

        D2["PIN 시도 횟수 제한"]

        D3["WSS (TLS) 암호화"]

        D4["PIN 1회용 + 30분 만료"]

        D5["1방 = 1관리자만"]

    end

    

    T1 --> D1

    T2 --> D2

    T3 --> D3

    T4 --> D4

    T4 --> D5

```


### 6.2 보안 체크리스트


| 항목 | 구현 |

|------|------|

| Signaling 서버 | WSS (TLS) 필수 |

| 관리자 인증 | WordPress 권한 확인 (필터로 커스터마이징 가능) |

| PIN 만료 | 30분 후 자동 삭제 |

| PIN 시도 제한 | 5회 실패 시 5분 차단 |

| 방 인원 제한 | 1방 = 고객 1명 + 관리자 1명 |

| 고객 화면 선택 | `getDisplayMedia()` — 고객이 공유할 화면 직접 선택 |

| WebRTC 암호화 | SRTP — WebRTC 기본 암호화 |

| 연결 종료 | 고객이 언제든 "공유 중지" 가능 |


---


## 7. Docker 구성


### 7.1 docker-compose.yml


```yaml

services:

  wordpress:

    image: wordpress:latest

    volumes:

      - ./data:/var/www/html

    networks:

      - backend

      - web


  signal:

    image: wellcoms-signal:latest

    environment:

      SIGNAL_PORT: "3000"

    networks:

      - web


  turn:

    image: coturn/coturn:latest

    network_mode: host

    volumes:

      - ./turn/turnserver.conf:/etc/coturn/turnserver.conf:ro


networks:

  web:

    external: true

  backend:

    external: true

```


### 7.2 TURN 서버 설정 (`turnserver.conf`)


```

listening-port=3478

tls-listening-port=5349

external-ip=<YOUR_PUBLIC_IP>

realm=your-domain.com

user=<TURN_USERNAME>:<TURN_PASSWORD>

log-file=stdout

fingerprint

min-port=49152

max-port=65535

no-cli

```


### 7.3 리버스 프록시 설정 (Caddy 예시)


```

# Signaling WebSocket 프록시

handle /signal/* {

    reverse_proxy signal:3000 {

        header_up X-Real-IP {remote}

        header_up X-Forwarded-For {remote}

    }

}


# TURN 서버는 network_mode: host로 직접 포트 3478 노출

# 리버스 프록시 불필요

```


---


## 8. 파일 구조


```

wp-wellcoms-remote/                         ← GitLab 리포

├── wellcoms-remote.php                     ← 플러그인 메인 v2.0.0 (shortcode + template_include)

├── templates/

│   └── page-remote.php                     ← 독립형 원격지원 페이지 (템플릿 없는 사이트용)

├── assets/

│   ├── js/                                 ← (예약)

│   └── css/                                ← (예약)

├── signal/                                 ← Signaling 서버

│   ├── server.js                           ← Node.js WebSocket 서버

│   ├── package.json

│   └── Dockerfile

└── README.md

```


**테마 연동 예시:**


```

themes/your-theme/

└── page-remote-support.php                 ← 원격지원 페이지 템플릿

    ├── 페이지 레이아웃, 안내 문구, 다운로드 섹션 등

    └── <?php echo do_shortcode('[wellcoms_screen_share]'); ?>  ← WebRTC 섹션

```


---


## 9. 구현 단계


### Phase 1: 기반 구축 ✅


| 작업 | 상태 | 비고 |

|------|------|------|

| Node.js + ws Signaling 서버 | ✅ | Docker 컨테이너 |

| Room Manager (PIN 생성/매칭) | ✅ | 4자리 PIN, 30분 타임아웃 |

| 메시지 릴레이 | ✅ | SDP/ICE 교환 |

| WordPress 페이지 템플릿 | ✅ | shortcode + 독립형 |

| Docker Compose 구성 | ✅ | signal 컨테이너 |

| 리버스 프록시 WSS 설정 | ✅ | `/signal/*` → signal:3000 |


### Phase 2: WebRTC 연결 ✅


| 작업 | 상태 | 비고 |

|------|------|------|

| getDisplayMedia 화면 캡처 | ✅ | 고객 측 |

| RTCPeerConnection (Offer/Answer) | ✅ | SDP 교환 |

| ICE Candidate 교환 | ✅ | Signaling 서버 경유 |

| PIN 입력 UI | ✅ | 4칸 입력 + 자동 포커스 |

| Video 재생 | ✅ | `<video>` 엘리먼트 |

| ICE disconnect 처리 | ✅ | 1.5초 지연 후 재연결 시도 |


### Phase 2A: TURN 서버 ✅


| 작업 | 상태 | 비고 |

|------|------|------|

| coturn Docker 컨테이너 | ✅ | network_mode: host |

| TURN 설정 (turnserver.conf) | ✅ | 포트 3478 |

| WebRTC iceServers 업데이트 | ✅ | TURN UDP + TCP 추가 |


### Phase 3: UI/UX + 안정화 ✅


| 작업 | 상태 | 비고 |

|------|------|------|

| 고객/관리자 통합 페이지 | ✅ | 권한으로 관리자 구분 |

| 2컬럼 레이아웃 (좌:WebRTC / 우:프로그램 정보) | ✅ | 카카오톡 상담 버튼 포함 |

| 반응형 (모바일) | ✅ | 모바일에서도 동작 |

| 관리자 인증 | ✅ | WordPress 권한 + 필터 확장 |

| 에러 핸들링 | ✅ | toast 알림 |

| 연결 끊김 처리 | ✅ | intentionalClose 플래그 |

| 연결 유형 표시 | ✅ | P2P / TURN 릴레이 배지 |

| 모바일 전체화면 | ✅ | 핀치투줌 + 가로 회전 |


### Phase 4: 배포 ✅


| 작업 | 상태 | 비고 |

|------|------|------|

| Docker 배포 | ✅ | signal, turn 컨테이너 |

| 리버스 프록시 설정 | ✅ | WSS 프록시 적용 |

| SSL 인증서 | ✅ | 자동 관리 |

| 테스트 | ✅ | 외부망 연결 테스트 완료 |


### Phase 5: 플러그인 분리 리팩토링 ✅


| 작업 | 상태 | 비고 |

|------|------|------|

| 테마 템플릿에서 WebRTC 코드 분리 | ✅ | CSS/HTML/JS 약 500줄 이전 |

| 플러그인 shortcode 등록 | ✅ | `[wellcoms_screen_share]` — WebRTC 전체 렌더링 |

| 관리자 판별 함수 `wr_is_admin()` | ✅ | 권한 체크 + 필터 확장 |

| template_include 이중 모드 | ✅ | 커스텀 템플릿 있으면 shortcode 모드, 없으면 독립형 |

| 테마 템플릿 `do_shortcode()` 교체 | ✅ | 745줄 → 230줄 |

| 버전 업 | ✅ | v1.0.0 → v2.0.0 |


---


## 10. WebRTC 설정


### 10.1 RTCPeerConnection Config


```javascript

var rtcConfig = {

  iceServers: [

    { urls: 'stun:stun.l.google.com:19302' },

    { urls: 'stun:stun1.l.google.com:19302' },

    { urls: 'turn:your-domain.com:3478', username: '<USER>', credential: '<PASS>' },

    { urls: 'turn:your-domain.com:3478?transport=tcp', username: '<USER>', credential: '<PASS>' }

  ]

};

```


### 10.2 getDisplayMedia 옵션


```javascript

const screenStream = await navigator.mediaDevices.getDisplayMedia({

    video: {

        displaySurface: 'monitor',  // 전체 화면 권장

        width: { ideal: 1920 },

        height: { ideal: 1080 },

        frameRate: { ideal: 15 }     // 원격지원은 15fps면 충분

    },

    audio: false  // 화면만 공유

});

```


### 10.3 NAT 통과 시나리오


```mermaid

graph TD

    A["WebRTC 연결 시도"] --> B{"STUN으로<br/>P2P 연결?"}

    B -->|"성공 (80~90%)"| C["✅ 직접 P2P 연결"]

    B -->|"실패 (10~20%)"| D["coturn TURN 서버<br/>릴레이 연결"]

    D --> E["✅ TURN 릴레이로<br/>연결 성공"]

    

    style C fill:#4ade80

    style E fill:#4ade80

```


---


## 11. 에러 처리


### 11.1 시나리오별 대응


| 상황 | 감지 | 대응 |

|------|------|------|

| **고객이 공유 중지** | `stream.onended` 이벤트 | 관리자에게 "고객이 공유를 중지했습니다" 알림 |

| **관리자가 연결 종료** | `WebSocket.onclose` | 고객에게 "상담이 종료되었습니다" 알림 |

| **네트워크 끊김** | `ICE connectionState = 'disconnected'` | 1.5초 대기 후 "연결이 불안정합니다" 알림 |

| **PIN 만료** | 30분 타임아웃 | 고객에게 "PIN이 만료되었습니다. 다시 시도해주세요" 안내 |

| **잘못된 PIN** | 서버에서 room not found | "PIN이 올바르지 않습니다" |

| **브라우저 미지원** | `!navigator.mediaDevices.getDisplayMedia` | "이 브라우저는 화면 공유를 지원하지 않습니다" |

| **고객이 화면 선택 취소** | `getDisplayMedia()` reject | 아무 일도 안 함 (대기 상태 유지) |


---


## 12. 추후 확장


```mermaid

graph TD

    P1["Phase 1<br/>화면 보기만<br/>(구현 완료)"]

    P2A["Phase 2A<br/>TURN 서버 추가<br/>(구현 완료)"]

    P2B["Phase 2B<br/>음성 채팅 추가<br/>(WebRTC Audio)"]

    P3["Phase 3<br/>원격 조작<br/>(포터블 .exe 필요)"]

    

    P1 --> P2A

    P1 --> P2B

    P2A --> P3

    P2B --> P3

```


---


## 13. 브라우저 호환성


| 브라우저 | getDisplayMedia | WebRTC | 비고 |

|----------|----------------|--------|------|

| Chrome 94+ | ✅ | ✅ | 권장 |

| Edge 94+ | ✅ | ✅ | 권장 |

| Firefox 66+ | ✅ | ✅ | 지원 |

| Safari 15+ | ✅ | ✅ | 제한적 (모바일은 미지원 가능) |

| 모바일 Chrome | ❌ | ✅ | 화면 공유 미지원 (관리자 시청만 가능) |

| 모바일 Safari | ❌ | ✅ | 화면 공유 미지원 |


> 고객(공유자)은 **데스크톱 브라우저** 필요. 관리자(시청자)는 모바일에서도 가능.


댓글
자동등록방지
(자동등록방지 숫자를 입력해 주세요)