ASHD Dev_Blog

RTC_1 : How to Connect Channel

이재룡
이재룡Jun 26, 2025

[ 1. 관련 Struct (Room, Message, Client) ]

type Room struct {
	Forward chan *message
	Join    chan *Client
	Leave   chan *Client
	Clients map[*Client]bool
}
type message struct {
	Name    string
	Message string
	Time    int64
}

type Client struct {
	Send chan message
	Room *Room
	Name string
	Socket *websocket.Conn
}
go

Room

  • Forward(전달) : 수신 되는 메시지를 저장하는 채널
  • Join(참여자) : Socket에 연결된 Client 채널
  • Leave(탈락자) : Socket에서 나간 Client 채널 (후처리용)
  • Clients : client를 index로 갖는 T/F map : (map[c1] = T)

Client

  • Socket *websocket.Conn : 모듈 ⇒ websocket package에서 소켓 정보를 가지는 Conn struct

Room 초기화

func NewRoom() *Room {
	return &Room{
		Forward: make(chan *message),
		Join:    make(chan *Client),
		Leave:   make(chan *Client),
		Clients: make(map[*Client]bool),
	}
}
go

👉
new( ) : 메모리 공간 할당 (초기 구조 없음)
make( ) : 공간 할당 + 초기 구조 구현
⇒ chan/map/slice는 내부 구조를 구현해줘야 사용 가능 = make

[ 2. Upgrader : HTTP를 WebSocket으로 ]

websocket.Upgrader : HTTP connection을 WebSocket으로 업그레이드

var Upgrader = &websocket.Upgrader{
	ReadBufferSize:  types.SocketBufferSize,
	WriteBufferSize: types.MessageBufferSize,
	CheckOrigin:     func(r *http.Request) bool { return true }}
go
  • CheckOrigin : 요청 Origin(출처)를 확인해서 함수 조건에 맞지 않으면 거부

    ⇒ request를 받아서 boolean형식의 리턴을 가지는 함수

    👉
    실제로는 아래와 같이 사용 (특정 도메인만 허용)
    CheckOrigin: func(r *http.Request) bool {
    	return r.Header.Get("Origin") == " https://mytrusteddomain.com " }
    go

[ 3. 소켓 통신 & Run(Switch-Case) ]

socket : HTTP request를 바탕으로 Upgrade의 결과물 : *Conn

authCookie : Request 쿠키의 auth 값

func (r *Room) SocketServe(c *gin.Context) {
	socket, err := Upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		panic(err)
	}

	authCookie, err := c.Request.Cookie("auth")
	if err != nil {
		panic(err)
	}

	client := &Client{
		Send:   make(chan *message, types.MessageBufferSize),
		Room:   r,
		Name:   authCookie.Value,
		Socket: socket,
	}

	r.Join <- client

	defer func() { r.Leave <- client }()

	//logic
}
go
  • 결국 gin.Context에서 추출한 데이터로 Socket을 만들고
  • Client정보를 만들어서
  • room의 Join에 Client 추가
  • defer 함수를 이용해서 logic이 끝난 후 SocketServe 함수를 탈출하기 직전 Leave에 Client 추가
    👉
    defer func ( ) : 함수를 탈출할 때 실행되도록 설정

[ 채널 Select-Case 문 ]

⇒ 특정 channel로 들어오는 데이터를 분기 (핵심 : 분기 지점이 채널)

func (r *Room) RunInit() {
	for {
		select {
		case client := <-r.Join:
			r.Clients[client] = true
		case client := <-r.Leave:
			r.Clients[client] = false
			close(client.Send)
			delete(r.Clients, client)
		case msg := <-r.Forward:
			for client := range r.Clients {
				client.Send <- msg
			}
		}

	}
}
go

r.Forward를 통해 msg가 들어오면

  • r.Clients의 모든 Client들의 Client.Send에 msg 추가 (Broadcasting)

⇒ client, msg는 그냥 어디에 담을지 정도, 분기의 기준은 channel (r.Join, r.Leave, r.Forward)