티스토리 뷰

🌐 이전 게시글

 

[Go] Polygon ID와 websocket을 사용한 신원 인증 - 1. Polygon ID

💜 Polygon ID? Polygon ID는 자기주권신원(Self-Sovereign Identity) 및 개인정보 보호 원칙에 따라 사용자의 개인 정보를 안전하게 보호하면서도, 앱과 사용자 사이의 신뢰할 수 있는 신원 검증 절차를 제

piatoss3612.tistory.com


📫 Websocket

 웹소켓(WebSocket)은 웹 브라우저와 웹 서버 간의 양방향 통신을 제공하는 프로토콜입니다. 

 

 웹소켓의 특징과 동작 방식은 다음과 같습니다.

  1. 양방향 통신: 웹소켓은 양방향 통신을 지원하며, 클라이언트나 서버가 언제든 데이터를 보낼 수 있습니다.
  2. 지속적인 연결: 웹소켓은 연결을 생성하고 유지하는 데 있어서 계속해서 연결 상태를 유지합니다. 이는 실시간 데이터 전송에 매우 유용합니다.
  3. 낮은 오버헤드: HTTP와는 달리 웹소켓은 추가적인 헤더가 없고, 작은 프레임 크기를 가지며 오버헤드가 낮습니다.
  4. 이벤트 기반: 웹소켓은 이벤트 기반으로 동작하며, 클라이언트나 서버는 이벤트를 수신하거나 전송할 수 있습니다.

 웹소켓은 주로 실시간 애플리케이션에서 사용되며, 채팅 애플리케이션, 주식 시장 데이터 업데이트, 온라인 게임 등과 같은 경우에 활용됩니다.


🎓 Go로 서버에서 Websocket 연결 처리하기

 클라이언트 측에서 Polygon ID를 통한 신원 인증 절차를 실시간으로 확인하기 위해서 서버 측에서 Websocket 연결을 관리하고 이벤트를 전달하는 기능을 구현해야 합니다.

gorilLa/websocket

go get github.com/gorilla/websocket

 Websocket을 구현해 놓은 패키지입니다. go get 명령어를 통해 패키지를 불러와 줍니다.

Message

type MessageType string

const (
	IDMessage    MessageType = "id"
	EventMessage MessageType = "event"
)

type ID string

type Message struct {
	Type  MessageType `json:"type"`
	ID    ID          `json:"id,omitempty"`
	Event Event       `json:"event,omitempty"`
}

type Status string

const (
	InProgress Status = "IN_PROGRESS"
	Error      Status = "ERROR"
	Done       Status = "DONE"
)

type Event struct {
	Fn     string `json:"fn"`
	Status Status `json:"status"`
	Data   any    `json:"data"`
}

 서버에서 클라이언트로 전달할 메시지. 메시지에는 메시지의 타입, 클라이언트의 세션 ID, 그리고 이벤트 정보가 담겨있습니다. 

Client

type Client struct {
	hub  *Hub 
	id   ID
	conn *websocket.Conn 
	send chan Message

	mu *sync.Mutex 
}

  클라이언트의 연결 정보를 관리하는 구조체. 모든 연결 정보를 관리하는 허브의 포인터, 클라이언트의 세션 id, Websocket 연결, 메시지를 전달받을 채널, 그리고 동시에 쓰기 작업이 발생하지 않게 잠가주는 뮤텍스를 가지고 있습니다.

func (c *Client) readPump() {
	defer func() {
		c.hub.unregister <- c.id
		c.conn.Close()
	}()

	c.conn.SetReadLimit(maxMessageSize)
	c.conn.SetReadDeadline(time.Now().Add(pongWait))
	c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })

	for {
		_, _, err := c.conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				log.Printf("error: %v", err)
			}
			break
		}
	}
}

 클라이언트로부터 전달된 메시지를 읽어 들이는 메서드. 현재로서는 연결 상태를 유지하는 것 외에 클라이언트로부터 별다른 메시지를 받지 않기 때문에 갑작스럽게 연결이 종료되는 상황만 체크합니다.

func (c *Client) writePump() {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		c.conn.Close()
	}()
	for {
		select {
		case message, ok := <-c.send:
			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
			if !ok {
				c.conn.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}

			// concurrent write to websocket connection is not allowed
			// thus locking the connection before writing
			c.mu.Lock()
			err := c.conn.WriteJSON(message)
			c.mu.Unlock()

			if err != nil {
				log.Printf("error while writing message: %v", err)
				return
			}
		case <-ticker.C:
			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
			if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
				log.Printf("error while writing ping message: %v", err)
				return
			}
		}
	}
}

 클라이언트로 메시지를 전달하는 메서드. 이벤트 메시지를 JSON 형식으로 클라이언트에게 전달하거나, 주기적으로 ping 메시지를 전달하여 연결 상태를 관리합니다.

Hub

type Hub struct {
	clients    map[ID]*Client
	register   chan *Client
	unregister chan ID
	send       chan Message

	mu *sync.RWMutex
}

 모든 웹소켓 연결을 관리하는 허브. 클라이언트와 새로운 연결이 수립되면 연결 정보를 등록하고, 연결이 끊어지면 해제하는 등의 작업을 처리합니다.

func (h *Hub) run() {
	for {
		select {
		case client := <-h.register:
			// register client
			if client == nil {
				continue
			}

			id := client.id

			h.mu.Lock()
			h.clients[id] = client
			h.mu.Unlock()
		case id := <-h.unregister:
			// unregister client if client is registered
			h.mu.Lock()
			client, ok := h.clients[id]
			if ok {
				close(client.send)
				delete(h.clients, id)
			}
			h.mu.Unlock()
		case msg := <-h.send:
			// send message to client if client is registered
			h.mu.RLock()
			client, ok := h.clients[msg.ID]
			h.mu.RUnlock()

			if ok {
				select {
				case client.send <- msg:
				default:
					h.mu.Lock()
					close(client.send)
					delete(h.clients, msg.ID)
					h.mu.Unlock()
				}
			}

		}
	}
}

 for문과 select문을 사용하여 채널을 통해 받은 데이터를 바탕으로 클라이언트의 연결 정보를 등록하거나 해제하는 등의 작업을 수행하는 메서드.

func NewHub() *Hub {
	hub := &Hub{
		register:   make(chan *Client),
		unregister: make(chan ID),
		clients:    make(map[ID]*Client),
		send:       make(chan Message),
		mu:         &sync.RWMutex{},
	}

	go hub.run()

	return hub
}

 허브의 생성자 함수. 생성자 함수 안에서 goroutine을 사용해 run 메서드를 실행합니다. 이렇게 goroutine으로 실행한 run 메서드는 main goroutine과 별개로 돌아가면서 허브의 주요한 작업들을 수행합니다.

Handler

var (
	allowOriginFunc = func(r *http.Request) bool {
		return true
	}

	upgrader = websocket.Upgrader{
		HandshakeTimeout: 5 * time.Second,
		ReadBufferSize:   1024,
		WriteBufferSize:  1024,
		CheckOrigin:      allowOriginFunc,
	}
)

 HTTP 연결을 Websocket 연결로 업그레이드하기 위한 변수들.

func ServeWs(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
		return
	}

	id := uuid.New().String()

	client := &Client{
		hub:  hub,
		id:   ID(id),
		conn: conn,
		send: make(chan Message, 256),
		mu:   &sync.Mutex{},
	}

	hub.register <- client

	go client.writePump()
	go client.readPump()

	msg := Message{
		Type: IDMessage,
		ID:   ID(id),
	}

	client.send <- msg
}

 Websocket 연결을 처리하기 위한 핸들러. 먼저 HTTP 연결을 Websocket으로 업그레이드한 뒤, 새로운 세션 id와 함께 클라이언트 정보를 생성하고 허브에 등록합니다. 또한 클라이언트가 읽기, 쓰기 작업을 수행할 수 있게 goroutine으로 메서드들을 실행해줍니다. 생성된 세션 id는 메시지에 담아 클라이언트에게 전달합니다. socket.io에서 자동으로 세션 id를 생성하여 전달하는 과정을 이런 식으로 구현해 보았습니다.


🐱‍👤 전체 코드

 

GitHub - piatoss3612/polygon-id-template

Contribute to piatoss3612/polygon-id-template development by creating an account on GitHub.

github.com


📖 참고 자료

 

GitHub - gorilla/websocket: Package gorilla/websocket is a fast, well-tested and widely used WebSocket implementation for Go.

Package gorilla/websocket is a fast, well-tested and widely used WebSocket implementation for Go. - GitHub - gorilla/websocket: Package gorilla/websocket is a fast, well-tested and widely used WebS...

github.com

최근에 올라온 글
최근에 달린 댓글
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Total
Today
Yesterday
글 보관함