[WebRTC] WebRTC ν™”μƒμ±„νŒ… μ‹€μŠ΅ (SpringBoot + React)

2025. 5. 30. 09:55Β·DrillDown/WebRTC

 

 

 

πŸŽ† Preview


 

 

 

 

πŸ“– κ°œμš”


λ°˜κ°‘μŠ΅λ‹ˆλ‹€.

WebRTC 이둠에 μ΄μ–΄μ„œ 화상 μ±„νŒ… μ‹€μŠ΅μ„ 해보렀고 ν•©λ‹ˆλ‹€.

 

 

[WebRTC] WebRTC 찒어버리기 - 이둠 정리

πŸ“– κ°œμš”λ°˜κ°‘μŠ΅λ‹ˆλ‹€. WebRTC κ΄€λ ¨ 글이 μ €μ˜ 첫 ν¬μŠ€νŒ…μ΄ λ˜μ—ˆλ„€μš”. 이 λ‚΄μš©μ„ μž‘μ„±ν•˜κ²Œ 된 λ°λŠ” λ‚˜λ¦„μ˜ 계기가 μžˆμŠ΅λ‹ˆλ‹€. λΆ€νŠΈμΊ ν”„μ—μ„œ νŒ€ ν”„λ‘œμ νŠΈλ₯Ό μ§„ν–‰ν•˜λ©΄μ„œ, κΈ°λŠ₯ μš”κ΅¬μ‚¬ν•­μ„ λ§Œμ‘±μ‹œν‚€κΈ°

phellinus-linteus.tistory.com

ν•΄λ‹Ή 글을 읽고 μ˜€μ‹ λ‹€λ©΄ μ‹€μŠ΅ μ½”λ“œλ₯Ό 더 μ‰½κ²Œ 이해할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

 

GitHub - BenchPress200/webrtc-tutorial: A WebRTC tutorial for learning peer-to-peer audio/video communication with custom signal

A WebRTC tutorial for learning peer-to-peer audio/video communication with custom signaling. - BenchPress200/webrtc-tutorial

github.com

μ‹€μŠ΅ μ½”λ“œλŠ” λ‘œμ»¬ν™˜κ²½μ—μ„œ μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

README와 μ†ŒμŠ€ μ½”λ“œ 주석을 μƒμ„Έν•˜κ²Œ μž‘μ„±ν–ˆμœΌλ‚˜, λ³Έ κΈ€μ—μ„œ 더 λ””ν…ŒμΌν•œ μ„€λͺ…을 μ „λ‹¬ν•©λ‹ˆλ‹€.

 

Signaling을 ν¬ν•¨ν•˜μ—¬ λͺ¨λ“  μ„œλ²„μ™€μ˜ 톡신은 WebSocket으둜 μ§„ν–‰ν•©λ‹ˆλ‹€.

κ°„λ‹¨ν•˜κ²Œ 이름 μž…λ ₯만으둜 κ°€μž…ν•˜λ©°, ν•΄λ‹Ή μ •λ³΄λŠ” λ°±μ—”λ“œ μΈ‘ λ©”λͺ¨λ¦¬μ— μ €μž₯λ©λ‹ˆλ‹€. 톡화 μš”μ²­μ„ ν†΅ν•΄μ„œ κ°€μž…ν•œ μœ μ € κ°„ ν™”μƒμ±„νŒ…μ΄ κ°€λŠ₯ν•©λ‹ˆλ‹€.

 

이번 ν¬μŠ€νŒ…μ˜ ꡬ성은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • μ‹€ν–‰ν™˜κ²½ 및 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ ꡬ쑰
  • μ‹€μŠ΅ μ½”λ“œ 따라가기
  • 마치며

 

 

 

βš™οΈ μ‹€ν–‰ν™˜κ²½ 및 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ ꡬ쑰


μ‹€μŠ΅ μ½”λ“œμ˜ μ‹€ν–‰ 흐름을 μ΄ν•΄ν•˜κΈ° μœ„ν•΄ WebRTC의 P2P μ—°κ²° 원리λ₯Ό λ˜μ§šμ–΄λ³΄κ³  ν”„λ‘ νŠΈμ—”λ“œ μ½”λ“œμ™€ λ°±μ—”λ“œ μ½”λ“œμ˜ ꡬ쑰, μ£Όμš” API의 λ™μž‘μ„ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

 

기술 μŠ€νƒ

  • ν”„λ‘ νŠΈμ—”λ“œ: TypeScript, React, Styled-component, Zustand, WebSocket, WebRTC
  • λ°±μ—”λ“œ: Java, SpringBoot, WebSocket

 

 

화상 μ±„νŒ…μ„ μœ„ν•œ P2P μ—°κ²°κ³Όμ •

두 λͺ…μ˜ μœ μ €κ°€ 있고, ν™”μƒμ±„νŒ…μ„ μœ„ν•œ P2P μ—°κ²°μˆ˜λ¦½ 과정을 λ‹€μ‹œ ν•œ 번 μ‚΄νŽ΄λ΄…μ‹œλ‹€.

Signaling 과정은 WebSocket으둜 μ§„ν–‰ν•œλ‹€κ³  κ°€μ •ν•˜κ² μŠ΅λ‹ˆλ‹€.

  1. λš±μ΄μ™€ μŠ€ν°μ§€λ°₯이 Signaling Server 와 WebSocket μ—°κ²°
  2. λš±μ΄κ°€ Signaling Server 둜 offer 전솑 (caller의 SDP)
    • offer 전솑과 λ™μ‹œμ—, 본인의 ICE Candidate μˆ˜μ§‘ 및 전솑
    • STUN/TURN μ„œλ²„λ₯Ό μ‚¬μš©ν•΄μ„œ ICE Candidate μˆ˜μ§‘
  3. μŠ€ν°μ§€λ°₯이 Signaling Server λ‘œλΆ€ν„° 뚱이의 offer (caller의 SDP) μˆ˜μ‹  및 μ„€μ •
    • 뚱이의 SDP κ°€ μ„€μ •λ˜λ©΄, λš±μ΄κ°€ 보내고 μžˆλŠ” ICE Candidate μΆ”κ°€ 및 ν…ŒμŠ€νŠΈ μ§„ν–‰
  4. μŠ€ν°μ§€λ°₯이 Signaling Server 둜 받은 offer에 λŒ€ν•œ answer 전솑 (callee의 SDP)
    • answer 전솑과 λ™μ‹œμ—, 본인의 ICE Candidate μˆ˜μ§‘ 및 전솑
    • STUN/TURN μ„œλ²„λ₯Ό μ‚¬μš©ν•΄μ„œ ICE Candidate μˆ˜μ§‘
  5. λš±μ΄κ°€ Signaling Server λ‘œλΆ€ν„° μŠ€ν°μ§€λ°₯의 answer(callee의 SDP) μˆ˜μ‹  및 μ„€μ •
    • μŠ€ν°μ§€λ°₯의 SDP κ°€ μ„€μ •λ˜λ©΄, μŠ€ν°μ§€λ°₯이 보내고 μžˆλŠ” ICE Candidate μΆ”κ°€ 및 ν…ŒμŠ€νŠΈ μ§„ν–‰
  6. offer와 answer, ICE Candidate κ΅ν™˜μ΄ μ™„λ£Œλ˜κ³  정상 μ—°κ²° ν…ŒμŠ€νŠΈλ₯Ό λ§ˆμ³€λ‹€λ©΄ ν™”μƒμ±„νŒ… μ€€λΉ„ μ™„λ£Œ
  7. ν™”μƒμ±„νŒ… μ‹œμž‘

 

 

WebSocket λ©”μ‹œμ§€ ꡬ쑰

{
	"type" : λ©”μ‹œμ§€μ˜ νƒ€μž…,
	"sender" : 보낸 μœ μ € λ‹‰λ„€μž„,
	"receiver" : 받을 μœ μ € λ‹‰λ„€μž„,
	"data" : any
}

μœ„ JSON은 μ›ν™œνžˆ λ©”μ‹œμ§€λ₯Ό μ£Όκ³  λ°›κΈ°μœ„ν•΄ μž„μ˜λ‘œ μ •μ˜ν•œ λ©”μ‹œμ§€ κ΅¬μ‘°μž…λ‹ˆλ‹€.

각 ν•„λ“œμ— λŒ€ν•œ μ„€λͺ…은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • type: μ–΄λ–€ λ©”μ‹œμ§€μΈμ§€ νƒ€μž…μ„ λͺ…μ‹œν•©λ‹ˆλ‹€.
    • join: νšŒμ›κ°€μž…
    • activeUsers: ν™œμ„±μœ μ € λ‹‰λ„€μž„ 리슀트 쑰회
    • call: 톡화 μš”μ²­
    • acceptCall: 걸렀온 톡화 수락
    • cancelCall: 걸렀온 톡화 거절
    • hangUp: ν†΅ν™”μ’…λ£Œ
    • offer: offer λ©”μ‹œμ§€ 전솑
    • answer: answer λ©”μ‹œμ§€ 전솑
    • candidate: ICE Candidate λ©”μ‹œμ§€ 전솑
  • sender: λ³΄λ‚΄λŠ” μœ μ €μ˜ λ‹‰λ„€μž„μ„ λͺ…μ‹œν•©λ‹ˆλ‹€.
  • receiver: λ°›λŠ” μœ μ €μ˜ λ‹‰λ„€μž„μ„ λͺ…μ‹œν•©λ‹ˆλ‹€.
  • data: 각 λ©”μ‹œμ§€ νƒ€μž…μ— λ”°λΌμ„œ ν•„μš”ν•œ 데이터λ₯Ό ν• λ‹Ήν•©λ‹ˆλ‹€.

 

 

ν”„λ‘ νŠΈμ—”λ“œ 싀행흐름

νŽ˜μ΄μ§€μ— ν•΄λ‹Ήν•˜λŠ” μ»΄ν¬λ„ŒνŠΈκ°€ μ„Έ 개 있고, WebSocket 연결은 Zustandλ₯Ό 톡해 μ „μ—­μœΌλ‘œ κ΄€λ¦¬ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

νšŒμ›κ°€μž…λΆ€ν„° 톡화 μ—°κ²°κΉŒμ§€ μˆœμ„œλŒ€λ‘œ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

  1. Join νŽ˜μ΄μ§€
    • μ›ν•˜λŠ” 이름을 μž…λ ₯ν•˜μ—¬ κ°„λ‹¨ν•˜κ²Œ κ°€μž…μ„ μ§„ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • 쀑볡 검사에 ν†΅κ³Όν•˜μ—¬ κ°€μž…μ— μ„±κ³΅ν•˜λ©΄ Home νŽ˜μ΄μ§€λ‘œ μ΄λ™ν•©λ‹ˆλ‹€.
  2. Home νŽ˜μ΄μ§€
    • κ°€μž… μœ μ € λͺ©λ‘μ„ μ‹€μ‹œκ°„μœΌλ‘œ λ°›μ•„μ˜€λ©°, 톡화쀑 μƒνƒœκ°€ μ•„λ‹Œ μœ μ €μ—κ²Œ ν†΅ν™”μš”μ²­μ„ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • λ˜ν•œ, λ‹€λ₯Έ μœ μ €μ—κ²Œ 톡화 μš”μ²­μ΄ 였면 수락 ν˜Ήμ€ κ±°μ ˆν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • 톡화λ₯Ό μˆ˜λ½ν•œλ‹€λ©΄ Call νŽ˜μ΄μ§€λ‘œ μ΄λ™ν•©λ‹ˆλ‹€.
  3. Call νŽ˜μ΄μ§€
    톡화 μš”μ²­μ„ ν•œ μœ μ €λŠ” caller, 톡화 μš”μ²­μ„ μˆ˜λ½ν•œ μœ μ €λŠ” calleeλ‘œμ„œ κ΅¬λΆ„λ©λ‹ˆλ‹€.
    Signaling을 μˆ˜ν–‰ν•˜κΈ° μœ„ν•΄ λ‹€μŒκ³Ό 같은 절차λ₯Ό λ”°λ¦…λ‹ˆλ‹€.
    1. λ―Έλ””μ–΄ 슀트림 μ„€μ • - caller callee
    2. offer 생성 및 전솑 - caller
      • offer μƒμ„±λ˜λ©΄, ICE Candidate μˆ˜μ§‘ 및 전솑
    3. offer μˆ˜μ‹  및 μ„€μ • - callee
      • offer μ„€μ •λ˜λ©΄, 받은 ICE Candidate μΆ”κ°€ μ‹œμž‘
    4. answer 생성 및 전솑 - callee
      • answer μƒμ„±λ˜λ©΄, ICE Candidate μˆ˜μ§‘ 및 전솑
    5. answer μˆ˜μ‹  및 μ„€μ • - caller
      • answer μ„€μ •λ˜λ©΄, 받은 ICE Candidate μΆ”κ°€ μ‹œμž‘
    6. κ΅ν™˜ν•˜κ³  각 ν”Όμ–΄ 정보에 μΆ”κ°€ν–ˆμ—ˆλ˜ ICE Candidate μ€‘μ—μ„œ μœ νš¨ν•˜κ³  κ°€μž₯ μ›ν™œν•œ 후보λ₯Ό μ°Ύμ•˜λ‹€λ©΄ μ—°κ²° μƒνƒœκ°€ connected, ν˜Ήμ€ completed둜 μ—…λ°μ΄νŠΈ - caller callee
    7. 화상 톡화 μ‹œμž‘

 

 

λ°±μ—”λ“œ 싀행흐름

λ°±μ—”λ“œμ—μ„œλŠ” WebSocket 연결을 μ²˜λ¦¬ν•˜κΈ° μœ„ν•œ μ„€μ •κ³Ό ν•Έλ“€λŸ¬κ°€ μ‘΄μž¬ν•©λ‹ˆλ‹€.

λ©”μ‹œμ§€λ₯Ό μ„œλ²„λ‘œ μ „μ†‘ν–ˆμ„ λ•Œ νŒŒμ‹±ν•˜μ—¬ νƒ€μž…μ„ ν™•μΈν•œ ν›„ ν•΄λ‹Ήν•˜λŠ” μ„œλΉ„μŠ€ λ‘œμ§μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

 

λ©”μ‹œμ§€ νƒ€μž…μ— λ”°λ₯Έ 둜직 μ²˜λ¦¬λŠ” λ ˆμ΄μ–΄λ“œ μ•„ν‚€ν…μ²˜λ₯Ό λ”°λ₯΄κ³  μžˆμŠ΅λ‹ˆλ‹€.

각 κ³„μΈ΅μ˜ 역할은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • presentation
    • WebSocket μ—°κ²°, ν•΄μ œ, λ©”μ‹œμ§€ νƒ€μž…μ— λ”°λ₯Έ μ„œλΉ„μŠ€ 클래슀의 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.
  • application
    • WebSocket μ—°κ²°, ν•΄μ œ, λ©”μ‹œμ§€ νƒ€μž…μ— λ”°λ₯Έ μœ μŠ€μΌ€μ΄μŠ€λ₯Ό μ²˜λ¦¬ν•©λ‹ˆλ‹€.
  • domain
    • application λ ˆμ΄μ–΄μ—μ„œ ν•„μš”λ‘œν•˜λŠ” 핡심 도메인 λ‘œμ§μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.
    • μœ μ €μ˜ 이름과 μ„Έμ…˜μ„ μ €μž₯ν•˜λŠ” ν•΄μ‹œλ§΅μ„ 클래슀 λ³€μˆ˜λ‘œ κ΄€λ¦¬ν•©λ‹ˆλ‹€.

 

 

WebRTC μ£Όμš” API

WebRTC의 P2P 연결에 ν•„μš”ν•œ μ£Όμš” APIλ₯Ό 효과적으둜 닀루기 μœ„ν•΄ μ»€μŠ€ν…€ 훅을 μž‘μ„±ν–ˆμŠ΅λ‹ˆλ‹€.

κ·Έ 쀑 일뢀 μ½”λ“œλ₯Ό ν•¨κ»˜ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

const peerConnection = new RTCPeerConnection();

peerConnection.ontrack = (event) => {}

peerConnection.onicecandidate = (event) => {}

peerConnection.oniceconnectionstatechange = (event) => {}
  • new RTCPeerConnection()
    • P2P 톡신을 μœ„ν•œ WebRTC 컀λ„₯μ…˜ 객체λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
  • ontrack
    • μƒλŒ€ peerλ‘œλΆ€ν„° μ „λ‹¬λœ λ―Έλ””μ–΄ νŠΈλž™(μ˜μƒ/μŒμ„± λ“±)을 μˆ˜μ‹ ν–ˆμ„ λ•Œ μ‹€ν–‰λ˜λŠ” 이벀트 ν•Έλ“€λŸ¬μž…λ‹ˆλ‹€.
  • onicecandidate
    • 후보λ₯Ό λΉ„λ™κΈ°μ μœΌλ‘œ μˆ˜μ§‘ν•˜κ³  ν•˜λ‚˜ 발견될 λ•Œλ§ˆλ‹€ μ‹€ν–‰λ˜λŠ” 이벀트 ν•Έλ“€λŸ¬μž…λ‹ˆλ‹€.
  • oniceconnectionstatechange
    • ICE μ—°κ²° μƒνƒœκ°€ 변경될 λ•Œλ§ˆλ‹€ ν˜ΈμΆœλ˜λŠ” 이벀트 ν•Έλ“€λŸ¬μž…λ‹ˆλ‹€.

 

navigator.mediaDevices.getUserMedia()
  • getUserMedia()
    • λΈŒλΌμš°μ €μ—μ„œ λ―Έλ””μ–΄ μž₯치(카메라/마이크)λ₯Ό μ‚¬μš©μžλ‘œλΆ€ν„° κΆŒν•œμ„ μ–»μ–΄ μ ‘κ·Όν•©λ‹ˆλ‹€.
    • λΉ„λ™κΈ°λ‘œ λ™μž‘ν•˜λ©°, MediaStream 객체λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.

 

peerConnection.createOffer()

peerConnection.createAnswer()

peerConnection.setLocalDescription()

peerConnection.setRemoteDescription()

peerConnection.addIceCandidate()
  • createOffer()
    • P2P 연결을 μ‹œμž‘ν•˜λŠ” callerκ°€ μƒλŒ€μ—κ²Œ 보낼 SDPλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
    • Promise<RTCSessionDescriptionInit> 객체λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
  • createAnswer()
    • offerλ₯Ό 받은 calleeκ°€ 응닡을 보낼 SDPλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
    • λ§ˆμ°¬κ°€μ§€λ‘œ, Promise<RTCSessionDescriptionInit> 객체λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
  • setLocalDescription()
    • createOffer() λ‚˜ createAnswer() 둜 μƒμ„±ν•œ 자기 μžμ‹ μ˜ SDP 정보λ₯Ό μ„€μ •ν•©λ‹ˆλ‹€.
    • 섀정이 된 이후에 ICE Candidate μˆ˜μ§‘μ΄ μ‹œμž‘λ©λ‹ˆλ‹€.
  • setRemoteDescription()
    • μƒλŒ€λ°©μœΌλ‘œλΆ€ν„° 받은 SDP (offer λ˜λŠ” answer) 정보λ₯Ό μ„€μ •ν•©λ‹ˆλ‹€.
    • 섀정이 된 이후에 μƒλŒ€κ°€ 보낸 ICE Candidateλ₯Ό 등둝할 수 μžˆμŠ΅λ‹ˆλ‹€.
  • addIceCandidate()
    • μƒλŒ€λ°©μ΄ 보내쀀 ICE Candidate 후보λ₯Ό λ“±λ‘ν•©λ‹ˆλ‹€.

 

 

μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ ꡬ쑰λ₯Ό μ‚΄νŽ΄λ΄€μœΌλ‹ˆ, 이제 μ‹€μŠ΅μœΌλ‘œ λ„˜μ–΄κ°€ λ΄…μ‹œλ‹€ !

 

 

 

πŸš€ μ‹€μŠ΅ μ½”λ“œ 따라가기


λ“œλ””μ–΄ μ‹€μŠ΅ μ½”λ“œλ₯Ό μ‹€ν–‰ν•  μ°¨λ‘€κ°€ μ™”μŠ΅λ‹ˆλ‹€.

μ½”λ“œλ₯Ό μ‹€ν–‰ν•  수 μžˆλŠ” ν™˜κ²½μ„ κ°–μΆ”κ³ , μ΅œμ’… λͺ©μ μΈ ν™”μƒμ±„νŒ…μ΄ λ™μž‘ν•  수 μžˆλ„λ‘ λ‹¨κ³„λ³„λ‘œ 따라가 λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

πŸŒ• 사전 μ€€λΉ„

κΉƒν—ˆλΈŒμ— μžˆλŠ” μ‹€μŠ΅ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜κΈ° μœ„ν•΄μ„œ 사전에 μ€€λΉ„ν•΄μ•Όν•  사항이 λͺ‡ κ°€μ§€ μžˆμŠ΅λ‹ˆλ‹€.

  • λΈŒλΌμš°μ €(Chrome, Safari λ“±)
  • VS Code
  • node (v22.10.0 이상)
  • npm (v10.9.0 이상)
  • IntelliJ IDEA
  • JDK 17

 

 

πŸŒ• μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰

1. μ €μž₯μ†Œ 클둠

git clone https://github.com/BenchPress200/webrtc-tutorial

  • λ‘œμ»¬ν™˜κ²½μ— μ‹€μŠ΅ μ½”λ“œ μ €μž₯μ†Œλ₯Ό ν΄λ‘ ν•©λ‹ˆλ‹€.

 

2. λ¦¬μ•‘νŠΈ μ†ŒμŠ€ μ½”λ“œ μ—΄κΈ°

  • VS Codeλ₯Ό 톡해 클둠받은 webrtc-tutorial 디렉터리 ν•˜μœ„μ— μžˆλŠ” frontend 디렉터리λ₯Ό μ˜€ν”ˆν•©λ‹ˆλ‹€.

 

3. ν•„μš”ν•œ νŒ¨ν‚€μ§€ μ„€μΉ˜

npm install

  • μ˜€ν”ˆν•œ frontend λ””λ ‰ν„°λ¦¬μ—μ„œ 터미널을 μ—΄κ³  npm install 을 μž…λ ₯ν•΄μ„œ ν•„μš”ν•œ νŒ¨ν‚€μ§€λ₯Ό μ„€μΉ˜ν•©λ‹ˆλ‹€.

 

4. λ¦¬μ•‘νŠΈ μ•± μ‹€ν–‰

npm start

  • νŒ¨ν‚€μ§€ μ„€μΉ˜κ°€ μ™„λ£Œλ˜μ—ˆμœΌλ©΄, npm start λ₯Ό μž…λ ₯ν•΄μ„œ λ¦¬μ•‘νŠΈ 앱을 μ‹€ν–‰ν•©λ‹ˆλ‹€.

 

  • 정상 μ‹€ν–‰λ˜μ—ˆμŒμ„ ν™•μΈν•©λ‹ˆλ‹€.

 

5. μŠ€ν”„λ§ λΆ€νŠΈ μ†ŒμŠ€ μ½”λ“œ μ—΄κΈ°

  • IntelliJλ₯Ό 톡해 클둠받은 webrtc-tutorial ν•˜μœ„μ— μžˆλŠ” backend/build.gradle 을 μ˜€ν”ˆν•©λ‹ˆλ‹€.

 

6. 둬볡 μ„€μ •

  • IntelliJμ—μ„œ 둬볡을 μΈμ‹ν•˜κ³  정상 λ™μž‘μ‹œν‚€κΈ° μœ„ν•΄ 섀정을 ν™•μΈν•©λ‹ˆλ‹€.

 

7. μŠ€ν”„λ§ λΆ€νŠΈ μ•± μ‹€ν–‰

  • 우츑 상단에 μ‹€ν–‰ λ²„νŠΌμ„ ν΄λ¦­ν•˜μ—¬ μŠ€ν”„λ§ λΆ€νŠΈ 앱이 정상 μ‹€ν–‰λ˜μ—ˆμŒμ„ ν™•μΈν•©λ‹ˆλ‹€.

 

 

πŸŒ• 화상 μ±„νŒ…

이제 λΈŒλΌμš°μ €μ˜ λ™μž‘κ³Ό μ†ŒμŠ€ μ½”λ“œλ₯Ό λ™μ‹œμ— ν™•μΈν•˜λ©΄μ„œ 화상 μ±„νŒ…κΉŒμ§€ μ–΄λ–€ 과정을 μˆ˜ν–‰ν•˜λŠ”μ§€ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

1. λ¦¬μ•‘νŠΈ μ•± μ ‘κ·Ό

  • μ‚¬μš©ν•˜λŠ” λΈŒλΌμš°μ €λ₯Ό 톡해 2개의 탭을 μ—΄κ³  localhost:3000둜 μ΄λ™ν•©λ‹ˆλ‹€.

 

2. λ―Έλ””μ–΄ μž₯치 κΆŒν•œ μ„€μ •

  • μ›ν™œν•œ 진행을 μœ„ν•΄ localhost:3000의 λ―Έλ””μ–΄ μž₯치 κΆŒν•œμ„ ν—ˆμš©ν•©λ‹ˆλ‹€.
  • 접근을 ν—ˆμš©ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄ μ—λŸ¬κ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

3. ν…ŒμŠ€νŠΈ μœ μ € κ°€μž…

  • 각 νƒ­μ—μ„œ '뚱이'와 'μŠ€ν°μ§€λ°₯'을 μž…λ ₯ν•˜κ³  join λ²„νŠΌμ„ ν΄λ¦­ν•©λ‹ˆλ‹€.

 

ν”„λ‘ λ“œμ—”λ“œ λ™μž‘ - caller callee

  • μ›ν•˜λŠ” 이름을 μž…λ ₯ν•˜κ³  join λ²„νŠΌμ„ ν΄λ¦­ν•˜λ©΄ Join.tsx νŽ˜μ΄μ§€μ— μžˆλŠ” handleJoin() ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.
  • join에 μ„±κ³΅ν•˜λ©΄ Call.tsx νŽ˜μ΄μ§€λ‘œ μ΄λ™ν•©λ‹ˆλ‹€.

 

λ°±μ—”λ“œ λ™μž‘

  1. presentation.SignalingHandler
    • handleTextMessage(): JOIN에 ν•΄λ‹Ήν•˜λŠ” application λ©”μ„œλ“œ ν˜ΈμΆœν•©λ‹ˆλ‹€.
  2. application.SignalingService
    • join() : κ°€μž… 처리λ₯Ό μœ„ν•œ domain λ©”μ„œλ“œ ν˜ΈμΆœν•©λ‹ˆλ‹€.
  3. domain.SignalingDomainService
    • existsName(): μœ μ € 이름 쀑볡검사λ₯Ό μ§„ν–‰ν•©λ‹ˆλ‹€
    • sendJoinResultMessage(): κ°€μž… κ²°κ³Ό λ©”μ‹œμ§€ μ „μ†‘ν•©λ‹ˆλ‹€.
    • broadcastUpdatedActiveUsers(): κ°€μž… 결과에 λ”°λ₯Έ μ΅œμ‹  μœ μ € λͺ©λ‘μ„ λͺ¨λ“  μœ μ €μ—κ²Œ μ „μ†‘ν•©λ‹ˆλ‹€.

 

4. μ „ν™”κ±ΈκΈ°

  • λš±μ΄κ°€ μŠ€ν°μ§€λ°₯μ—κ²Œ 톡화λ₯Ό μš”μ²­ν•©λ‹ˆλ‹€.
  • μŠ€ν°μ§€λ°₯은 뚱이의 톡화λ₯Ό μˆ˜λ½ν•©λ‹ˆλ‹€.

 

ν”„λ‘ λ“œμ—”λ“œ λ™μž‘ - caller

  • 톡화 μƒλŒ€λ₯Ό ν΄λ¦­ν•˜λ©΄ Home.tsx νŽ˜μ΄μ§€μ— μžˆλŠ” handleRequestCall() ν˜ΈμΆœλ©λ‹ˆλ‹€.
  • callerλŠ” μƒλŒ€μ˜ 톡화 μˆ˜λ½μ„ λŒ€κΈ°ν•˜λŠ” UIκ°€ λ‚˜νƒ€λ‚©λ‹ˆλ‹€.

 

λ°±μ—”λ“œ λ™μž‘

  1. presentation.SignalingHandler
    • handleTextMessage(): CALL에 ν•΄λ‹Ήν•˜λŠ” application λ©”μ„œλ“œ ν˜ΈμΆœν•©λ‹ˆλ‹€.
  2. application.SignalingService
    • call(): 톡화 μš”μ²­ 처리λ₯Ό μœ„ν•œ domain λ©”μ„œλ“œ ν˜ΈμΆœν•©λ‹ˆλ‹€.
  3. domain.SignalingDomainService
    • call(): μ†‘μˆ˜μ‹ μžμ˜ ν†΅ν™”μƒνƒœλ₯Ό ν™•μΈν•˜κ³  μˆ˜μ‹ μžμ—κ²Œ 톡화 μš”μ²­ λ©”μ‹œμ§€ μ „μ†‘ν•©λ‹ˆλ‹€.
    • broadcastUpdatedActiveUsers(): ν†΅ν™”μƒνƒœ 변화에 λ”°λ₯Έ μ΅œμ‹  μœ μ € λͺ©λ‘μ„ λͺ¨λ“  μœ μ €μ—κ²Œ μ „μ†‘ν•©λ‹ˆλ‹€.

 

ν”„λ‘ λ“œμ—”λ“œ λ™μž‘ - callee

  • ν†΅ν™”μš”μ²­ λ©”μ‹œμ§€λ₯Ό μˆ˜μ‹ ν•˜λ©΄ Home.tsx νŽ˜μ΄μ§€μ— μžˆλŠ” handleWebSocketMessageμ—μ„œ handleReceiveCall() λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.
  • 톡화 μš”μ²­μ΄ μ™”λ‹€λŠ” UIκ°€ ν‘œμ‹œλ©λ‹ˆλ‹€.
  • 톡화 μš”μ²­ 수락 μ•„μ΄μ½˜μ„ ν΄λ¦­ν•˜λ©΄, handleAcceptCall() λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.
  • callerμ—κ²Œ 톡화λ₯Ό μˆ˜λ½ν–ˆλ‹€λŠ” λ©”μ‹œμ§€λ₯Ό 보내고 Call.tsx νŽ˜μ΄μ§€λ‘œ μ΄λ™ν•©λ‹ˆλ‹€.

 

λ°±μ—”λ“œ λ™μž‘

  1. presentation.SignalingHandler
    • handleTextMessage(): ACCEPT_CALL에 ν•΄λ‹Ήν•˜λŠ” application λ©”μ„œλ“œ ν˜ΈμΆœν•©λ‹ˆλ‹€.
  2. application.SignalingService
    • acceptCall(): 톡화 μš”μ²­ 수락 처리λ₯Ό μœ„ν•œ domain λ©”μ„œλ“œ ν˜ΈμΆœν•©λ‹ˆλ‹€.
  3. domain.SignalingDomainService
    • acceptCall(): 톡화 μš”μ²­μ„ μˆ˜λ½ν–ˆλ‹€λŠ” λ©”μ‹œμ§€λ₯Ό callerμ—κ²Œ μ „μ†‘ν•©λ‹ˆλ‹€.

 

ν”„λ‘ νŠΈμ—”λ“œ λ™μž‘ - caller

  • 톡화 μš”μ²­ 수락 λ©”μ‹œμ§€λ₯Ό μˆ˜μ‹ ν•˜λ©΄ Home.tsx νŽ˜μ΄μ§€μ— μžˆλŠ” handleWebSocketMessageμ—μ„œ handleEnterCallRoom() λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.
  • μƒλŒ€μ˜ 수락이 ν™•μΈλ˜μ—ˆμœΌλ―€λ‘œ Call.tsx νŽ˜μ΄μ§€λ‘œ μ΄λ™ν•©λ‹ˆλ‹€.

 

5. Signaling

Call.tsx νŽ˜μ΄μ§€μ—μ„œ μ§„ν–‰ν•˜λŠ” Signaling κ³Όμ •μž…λ‹ˆλ‹€.

이해λ₯Ό 돕기 μœ„ν•΄, μ‹€μŠ΅ μ½”λ“œμ—μ„œλŠ” μœ μ €μ˜ λ™μž‘μ— 따라 Signaling이 λ‹¨κ³„μ μœΌλ‘œ μ§„ν–‰λ˜λ„λ‘ κ΅¬μ„±λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

μ‹€μ œλ‘œ κΈ°λŠ₯을 κ΅¬ν˜„ν•œλ‹€λ©΄, μœ μ €μ˜ 행동없이 톡화λ₯Ό μˆ˜λ½ν–ˆμ„ λ•Œ κ³§λ°”λ‘œ offer와 answerλ₯Ό μ£Όκ³ λ°›κ³  ICE Candidateλ₯Ό κ΅ν™˜ν•˜λ„λ‘ ν•˜λŠ” 것이 μΌλ°˜μ μž…λ‹ˆλ‹€.

βœ… SDP와 ICE Candidate λ₯Ό μ£Όκ³  λ°›λŠ” 흐름은 μ•žμ„œ μ„€λͺ…ν•œ λ‚΄μš©μ„ 톡해 μΆ©λΆ„νžˆ 이해할 수 μžˆλ‹€κ³  νŒλ‹¨ν•˜μ—¬, 이후 μ½”λ“œ 레벨의 μ„€λͺ…은 μƒλž΅ν—€μŠ΅λ‹ˆλ‹€. WebRTC연결에 μ‚¬μš©λœ 핡심 APIκ°€ κΆκΈˆν•˜λ‹€λ©΄ μ‹€μŠ΅ μ €μž₯μ†Œμ˜ useWebRTC.ts λ₯Ό μ°Έκ³ ν•˜μ‹œλ©΄ λ©λ‹ˆλ‹€.

 

λ―Έλ””μ–΄ 슀트림 μ„€μ •

  • λš±μ΄μ™€ μŠ€ν°μ§€λ°₯은 각각 λ―Έλ””μ–΄ 슀트림 μ„ΈνŒ… λ²„νŠΌμ„ ν΄λ¦­ν•©λ‹ˆλ‹€.
    • λš±μ΄λŠ” offerλ₯Ό 보낼 수 μžˆλŠ” μƒνƒœκ°€ λ©λ‹ˆλ‹€.
    • μŠ€ν°μ§€λ°₯은 뚱이의 offerλ₯Ό κΈ°λ‹€λ¦¬λŠ” μƒνƒœκ°€ λ©λ‹ˆλ‹€.
  • 같은 μž₯치λ₯Ό μ‚¬μš©ν•˜λŠ” 두 λͺ…μ˜ μœ μ €λ₯Ό μ‹œκ°μ μœΌλ‘œ λͺ…ν™•νžˆ κ΅¬λΆ„ν•˜κΈ° μœ„ν•΄μ„œ 아이폰 연속성을 ν™œμš©ν–ˆμŠ΅λ‹ˆλ‹€.

 

offer 전솑 및 μ„€μ •

  • λš±μ΄κ°€ Create and Send Offer λ²„νŠΌμ„ ν΄λ¦­ν•©λ‹ˆλ‹€.
    • μžμ‹ μ˜ SDP(offer)κ°€ μƒμ„±λ˜κ³  μ„€μ •λ˜λ©΄ ICE Candidate μˆ˜μ§‘ 및 전솑을 μ‹œμž‘ν•©λ‹ˆλ‹€.
    • μŠ€ν°μ§€λ°₯μ—κ²Œ offer 전솑이 μ™„λ£Œλ˜λ©΄ answerλ₯Ό κΈ°λ‹€λ¦¬λŠ” μƒνƒœκ°€ λ©λ‹ˆλ‹€.
  • μŠ€ν°μ§€λ°₯은 뚱이의 offerλ₯Ό κΈ°λ‹€λ Έλ‹€κ°€ Set Offer λ²„νŠΌμ„ ν΄λ¦­ν•©λ‹ˆλ‹€.
    • 뚱이의 SDP(offer)κ°€ μ„€μ •λ˜λ©΄, answer 생성 및 전솑 λ²„νŠΌμ΄ ν™œμ„±ν™”λ©λ‹ˆλ‹€.
    • 뚱이의 SDP(offer)κ°€ μ„€μ •λ˜λ©΄, λ³„λ„μ˜ μœ μ € λ™μž‘μ—†μ΄ 받은 ICE Candidateλ₯Ό μžλ™μœΌλ‘œ μΆ”κ°€ν•©λ‹ˆλ‹€.

 

answer 전솑 및 μ„€μ •

  • μŠ€ν°μ§€λ°₯이가 Create and Send Answer λ²„νŠΌμ„ ν΄λ¦­ν•©λ‹ˆλ‹€.
    • μžμ‹ μ˜ SDP(answer)κ°€ μƒμ„±λ˜κ³  μ„€μ •λ˜λ©΄ ICE Candidate μˆ˜μ§‘ 및 전솑을 μ‹œμž‘ν•©λ‹ˆλ‹€.
  • λš±μ΄μ€ μŠ€ν°μ§€λ°₯의 answerλ₯Ό κΈ°λ‹€λ Έλ‹€κ°€ Set Answer λ²„νŠΌμ„ ν΄λ¦­ν•©λ‹ˆλ‹€.
    • μŠ€ν°μ§€λ°₯의 SDP(answer)κ°€ μ„€μ •λ˜λ©΄, λ³„λ„μ˜ μœ μ € λ™μž‘μ—†μ΄ ICE Candidateλ₯Ό μžλ™μœΌλ‘œ μΆ”κ°€ν•©λ‹ˆλ‹€.
  • μ„œλ‘œ κ΅ν™˜ν•œ ICE Candidateλ₯Ό μΆ”κ°€ν–ˆμ„ λ•Œ, μœ νš¨ν•œ 연결이 μƒμ„±λœλ‹€λ©΄ ν†΅ν™”μ‹œμž‘ κ°€λŠ₯ μƒνƒœκ°€ λ©λ‹ˆλ‹€.

 

6. 화상 μ±„νŒ… μ‹œμž‘

  • λš±μ΄μ™€ μŠ€ν°μ§€λ°₯이 μ •μƒμ μœΌλ‘œ SDP와 ICE Candidateλ₯Ό κ΅ν™˜ν•˜κ³  ν™”μƒμ±„νŒ…μ„ μ‹œμž‘ν•©λ‹ˆλ‹€.

 

βœ… λ‘œμ»¬ν™˜κ²½μ˜ λ―Έλ””μ–΄ 슀트림과 peer의 λ―Έλ””μ–΄ μŠ€νŠΈλ¦Όμ€ useWebRTC μ»€μŠ€ν…€ ν›…μ—μ„œ MediaStream νƒ€μž…μœΌλ‘œ μƒνƒœκ΄€λ¦¬λ₯Ό ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.
ν•΄λ‹Ή 객체λ₯Ό video νƒœκ·Έμ˜ ref속성에 ν• λ‹Ήν•΄μ£Όλ©΄ μ •μƒμ μœΌλ‘œ λ―Έλ””μ–΄ 슀트림이 λ Œλ”λ§ λ©λ‹ˆλ‹€. video νƒœκ·Έμ— μ •ν™•νžˆ μ–΄λ–»κ²Œ μ „λ‹¬ν•˜κ³ , μ–΄λ–€ 속성이 μΆ”κ°€μ μœΌλ‘œ ν•„μš”ν•œμ§€λŠ” frontend/src/components νŒ¨ν‚€μ§€μ— μžˆλŠ” λΉ„λ””μ˜€ κ΄€λ ¨ μ»΄ν¬λ„ŒνŠΈμ—μ„œ ν™•μΈν•˜μ‹€ 수 μžˆμŠ΅λ‹ˆλ‹€.

 

 

 

πŸ‘‹πŸ» 마치며


이전에 νŒ€ ν”„λ‘œμ νŠΈλ₯Ό μ§„ν–‰ν•˜λ©΄μ„œ, λ―Έλ””μ–΄ 쀑계 μ„œλ²„μΈ Kurentoλ₯Ό μ‚¬μš©ν•˜μ—¬ NλŒ€ N ν™”μƒμ±„νŒ… μ„œλΉ„μŠ€λ₯Ό κ΅¬ν˜„ν–ˆμ—ˆμŠ΅λ‹ˆλ‹€.

WebRTC APIλ₯Ό ν•œ 번 감싼 Kurento API, λ‹€μˆ˜μ˜ peerκ°€ μ€‘λ³΅μœΌλ‘œ Signaling μˆ˜ν–‰, λͺ…ν™•ν•˜μ§€ μ•Šμ€ μƒνƒœ 관리, WebSocket μ„Έμ…˜μ΄ μ˜λ„μΉ˜ μ•Šκ²Œ λ‹«ν˜”μ„ λ•Œμ˜ λ™μž‘, Nginx의 WebSocket μ—°κ²° λ¦¬λ²„μŠ€ ν”„λ‘μ‹œ 문제, coturn 배포 λ“± 정말 λ§Žμ€ 문제λ₯Ό λ§Œλ‚¬μ—ˆμŠ΅λ‹ˆλ‹€.

 

λ¬Έμ œκ°€ λ°œμƒν•œ 근본적인 원인은 WebRTC 의 μ—°κ²° 방식을 μ •ν™•νžˆ μ΄ν•΄ν•˜μ§€ λͺ»ν•˜κ³  μžˆλ‹€κ³  νŒλ‹¨ν–ˆμŠ΅λ‹ˆλ‹€.

κ·Έλž˜μ„œ λ‹€μ‹œ ν•™μŠ΅ν•˜κ³  μ‹€μŠ΅ μ½”λ“œλ₯Ό 직접 μž‘μ„±ν•˜λ©΄μ„œ 이해도λ₯Ό 높일 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ, μ΄λ ‡κ²Œ κΈ€λ‘œ λ‚¨κΈΈμˆ˜λ„ 있게 λ˜μ—ˆκ΅¬μš”.

WebRTC κΈ°μˆ μ— λŒ€ν•΄ ν˜ΈκΈ°μ‹¬ ν˜Ήμ€ ν•„μš”ν•˜μ‹  λΆ„μ—κ²Œ 도움이 λ˜μ—ˆμœΌλ©΄ μ’‹κ² μŠ΅λ‹ˆλ‹€.

 

μ½μ–΄μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€. κΆκΈˆν•œ 점에 λŒ€ν•œ 질문 ν™˜μ˜ν•©λ‹ˆλ‹€!

λΆ€μ‘±ν•œ, 잘λͺ»λœ μ„€λͺ…이 μžˆλ‹€λ©΄ μ•Œλ €μ£Όμ‹œλ©΄ κ°μ‚¬ν•˜κ² μŠ΅λ‹ˆλ‹€ πŸ™πŸ»

'DrillDown > WebRTC' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

[WebRTC] WebRTC 찒어버리기 - 이둠 정리  (6) 2025.05.30
'DrillDown/WebRTC' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€
  • [WebRTC] WebRTC 찒어버리기 - 이둠 정리
ian_0430
ian_0430
개발 λΈ”λ‘œκ·Έ
  • ian_0430
    BenchPress200
    ian_0430
  • 전체
    였늘
    μ–΄μ œ
    • λΆ„λ₯˜ 전체보기 (8)
      • 회고 (0)
      • λ°±μ€€ & ν”„λ‘œκ·Έλž˜λ¨ΈμŠ€ (1)
      • DrillDown (5)
        • Spring (2)
        • MySQL (1)
        • Java (0)
        • WebRTC (2)
        • SystemDesign (0)
      • Computer Science (1)
        • Network (0)
        • DataStructure (0)
        • Algorithm (0)
        • Database (1)
        • OperatingSystem (0)
      • 기타 (1)
  • 인기 κΈ€

  • νƒœκ·Έ

    μŠ€ν”„λ§λΆ€νŠΈ
    ν†°μΊ£ μ“°λ ˆλ“œ
    μ›Ήμ•Œν‹°μ”¨
    격리 μˆ˜μ€€
    react
    Spring
    max-connections
    μŠ€ν”„λ§ λΆ€νŠΈ μ“°λ ˆλ“œ
    accept-count
    νŒ¬ν…€λ¦¬λ“œ
    μŠ€ν”„λ§
    λ°±μ€€ 11659번
    λ¦¬μ•‘νŠΈ
    11659
    Isolation level
    min-spare
    REPEATABLE READ
    11659번
    webrtc
    κ²©λ¦¬μˆ˜μ€€
  • 졜근 λŒ“κΈ€

  • hELLOΒ· Designed Byμ •μƒμš°.v4.10.3
ian_0430
[WebRTC] WebRTC ν™”μƒμ±„νŒ… μ‹€μŠ΅ (SpringBoot + React)
μƒλ‹¨μœΌλ‘œ

ν‹°μŠ€ν† λ¦¬νˆ΄λ°”