Go에서 소켓 프로그래밍 마스터하기
Grace Collins
Solutions Engineer · Leapcell

소켓 프로그래밍의 기본 원리
소켓 프로그래밍은 네트워크 프로그래밍에서 소켓 인터페이스를 사용하여 애플리케이션을 개발하는 과정을 말합니다. 소켓은 네트워크 통신의 기본적인 개념입니다. 이는 네트워크에서 엔드포인트를 설명하는 데 사용되는 추상적인 데이터 구조입니다. 소켓은 IP 주소, 포트 번호 및 프로토콜 유형과 같은 정보를 포함하며, 네트워크에서 특정 프로세스 또는 장치를 식별하는 데 사용됩니다.
소켓 프로그래밍은 클라이언트와 서버라는 두 가지 개념을 기반으로 합니다. 클라이언트는 요청을 시작하는 당사자이고, 서버는 서비스를 제공하는 당사자입니다. 네트워크를 통해 연결을 설정함으로써 클라이언트는 서버에 요청을 보낼 수 있으며, 서버는 요청을 처리하여 클라이언트에 응답을 반환합니다.
소켓 프로그래밍에는 데이터 형식 및 상호 작용 규칙 집합을 정의하는 특수한 프로토콜이 필요합니다. 오늘날 가장 일반적으로 사용되는 소켓 프로토콜은 TCP와 UDP입니다. TCP 프로토콜은 안정적인 데이터 전송을 보장하는 연결 지향 프로토콜입니다. 반면에 UDP 프로토콜은 연결이 없는 프로토콜로 더 빠르지만 신뢰할 수 없습니다.
- TCP (Transmission Control Protocol): 연결 지향적, 신뢰성 있음, 순서대로 전달, 바이트 스트림 기반.
- UDP (User Datagram Protocol): 연결 없음, 신뢰성 없음, 순서 없음, 데이터그램 기반.
Go 언어는 강력한 net
패키지를 제공하여 소켓 프로그래밍을 단순화합니다. 이를 통해 개발자는 낮은 수준의 시스템 호출을 직접 조작하지 않고도 네트워크 통신을 구현할 수 있습니다.
TCP 소켓 프로그래밍
TCP는 연결 지향 프로토콜로, 안정적인 데이터 전송이 필요한 시나리오에 적합합니다.
서버 측 구현
다음은 간단한 TCP 서버 예제입니다. 지정된 포트에서 수신 대기하고, 클라이언트 연결을 수락하고, 수신된 메시지를 다시 에코합니다.
package main import ( "bufio" "fmt" "net" ) func handleConnection(conn net.Conn) { defer conn.Close() reader := bufio.NewReader(conn) for { // 클라이언트가 보낸 데이터 읽기 message, err := reader.ReadString('\n') if err != nil { fmt.Println("데이터 읽기 오류:", err) break } fmt.Printf("수신된 메시지: %s", message) // 메시지를 클라이언트에 다시 에코 conn.Write([]byte("Echo: " + message)) } } func main() { // 로컬 포트 8080에서 수신 대기 listener, err := net.Listen("tcp", ":8080") if err != nil { fmt.Println("포트에서 수신 대기 실패:", err) return } defer listener.Close() fmt.Println("서버가 포트 8080에서 수신 대기 중...") for { // 클라이언트 연결 수락 conn, err := listener.Accept() if err != nil { fmt.Println("연결 수락 실패:", err) continue } // 연결 처리 (여러 개를 동시에 처리 가능) go handleConnection(conn) } }
설명:
net.Listen
을 사용하여 지정된 주소와 포트에서 수신 대기합니다.listener.Accept
를 사용하여 클라이언트 연결을 수락하고net.Conn
객체를 반환합니다.- 각 연결에 대해 새 고루틴을 시작하여 동시에 처리합니다.
클라이언트 측 구현
다음은 간단한 TCP 클라이언트 예제입니다. 서버에 연결하고 메시지를 보냅니다.
package main import ( "bufio" "fmt" "net" "os" ) func main() { // 서버에 연결 (localhost:8080) conn, err := net.Dial("tcp", "localhost:8080") if err != nil { fmt.Println("서버에 연결 실패:", err) return } defer conn.Close() reader := bufio.NewReader(os.Stdin) for { // 표준 입력에서 사용자 입력 읽기 fmt.Print("메시지 입력: ") message, _ := reader.ReadString('\n') // 서버에 메시지 보내기 _, err := conn.Write([]byte(message)) if err != nil { fmt.Println("메시지 보내기 실패:", err) return } // 서버에서 에코된 메시지 받기 response, err := bufio.NewReader(conn).ReadString('\n') if err != nil { fmt.Println("메시지 받기 실패:", err) return } fmt.Print("서버 에코: " + response) } }
설명:
net.Dial
을 사용하여 서버에 연결합니다.- 표준 입력에서 사용자 입력을 읽고 서버로 보냅니다.
- 서버에서 에코된 메시지를 받아 표시합니다.
UDP 소켓 프로그래밍
UDP는 연결이 없는 프로토콜로, 안정성보다 실시간 성능이 더 중요한 시나리오에 적합합니다. 아래에서는 Go를 사용하여 UDP 서버와 클라이언트를 구현하는 방법을 소개합니다.
서버 측 구현
다음은 간단한 UDP 서버 예제입니다. 지정된 포트에서 수신 대기하고, 클라이언트로부터 데이터를 수신하고, 수신된 메시지를 다시 에코합니다.
package main import ( "fmt" "net" ) func main() { // UDP를 사용하여 로컬 포트 8081에서 수신 대기 addr, err := net.ResolveUDPAddr("udp", ":8081") if err != nil { fmt.Println("주소 확인 실패:", err) return } conn, err := net.ListenUDP("udp", addr) if err != nil { fmt.Println("UDP에서 수신 대기 실패:", err) return } defer conn.Close() fmt.Println("UDP 서버가 포트 8081에서 수신 대기 중...") buffer := make([]byte, 1024) for { // 클라이언트가 보낸 데이터 읽기 n, clientAddr, err := conn.ReadFromUDP(buffer) if err != nil { fmt.Println("데이터 읽기 오류:", err) continue } message := string(buffer[:n]) fmt.Printf("수신된 메시지 (발신자: %s): %s", clientAddr, message) // 메시지를 클라이언트에 다시 에코 _, err = conn.WriteToUDP([]byte("Echo: "+message), clientAddr) if err != nil { fmt.Println("메시지 보내기 실패:", err) } } }
설명:
net.ResolveUDPAddr
을 사용하여 UDP 주소를 확인합니다.net.ListenUDP
를 사용하여 UDP 포트에서 수신 대기합니다.ReadFromUDP
를 사용하여 데이터를 수신하고 클라이언트 주소를 가져옵니다.WriteToUDP
를 사용하여 데이터를 클라이언트에 다시 보냅니다.
클라이언트 측 구현
다음은 간단한 UDP 클라이언트 예제입니다. 서버에 메시지를 보내고 에코된 응답을 받습니다.
package main import ( "bufio" "fmt" "net" "os" ) func main() { // 서버 주소 확인 serverAddr, err := net.ResolveUDPAddr("udp", "localhost:8081") if err != nil { fmt.Println("서버 주소 확인 실패:", err) return } // UDP 연결 만들기 (실제로는 연결 없음) conn, err := net.DialUDP("udp", nil, serverAddr) if err != nil { fmt.Println("UDP 서버에 연결 실패:", err) return } defer conn.Close() reader := bufio.NewReader(os.Stdin) for { // 표준 입력에서 사용자 입력 읽기 fmt.Print("메시지 입력: ") message, _ := reader.ReadString('\n') // 서버에 메시지 보내기 _, err := conn.Write([]byte(message)) if err != nil { fmt.Println("메시지 보내기 실패:", err) return } // 서버에서 에코된 메시지 받기 buffer := make([]byte, 1024) n, err := conn.Read(buffer) if err != nil { fmt.Println("메시지 받기 실패:", err) return } fmt.Print("서버 에코: " + string(buffer[:n])) } }
설명:
net.ResolveUDPAddr
을 사용하여 서버 주소를 확인합니다.net.DialUDP
를 사용하여 UDP 연결을 만듭니다 (본질적으로는 연결이 없지만).- 데이터를 보내고 받는 것은 TCP 클라이언트와 유사합니다.
Leapcell에서 Go 프로젝트를 호스팅하십시오.
Leapcell은 웹 호스팅, 비동기 작업 및 Redis를 위한 차세대 서버리스 플랫폼입니다.
다중 언어 지원
- Node.js, Python, Go 또는 Rust로 개발하십시오.
무제한 프로젝트를 무료로 배포
- 사용량에 대해서만 지불하십시오. 요청도, 요금도 없습니다.
탁월한 비용 효율성
- 유휴 요금 없이 사용한 만큼만 지불하십시오.
- 예: $25로 평균 응답 시간 60ms에서 694만 건의 요청을 지원합니다.
간소화된 개발자 경험
- 간편한 설정을 위한 직관적인 UI.
- 완전 자동화된 CI/CD 파이프라인 및 GitOps 통합.
- 실행 가능한 통찰력을 위한 실시간 메트릭 및 로깅.
손쉬운 확장성 및 고성능
- 고도의 동시성을 쉽게 처리하기 위한 자동 확장.
- 운영 오버헤드가 제로입니다. 구축에만 집중하십시오.
문서에서 자세히 알아보십시오!
X에서 팔로우하세요: @LeapcellHQ