refactor: migrate lib/grpcentity to lib/grpc/basic and update path references

This commit is contained in:
2026-07-17 21:43:48 +09:00
parent 600a950806
commit d90553ecdf
9 changed files with 29 additions and 26 deletions
+6 -5
View File
@@ -29,9 +29,10 @@ grpccanary/
│ ├── main.go # 학습 예제 통합 실행 진입점 (수동 전환) │ ├── main.go # 학습 예제 통합 실행 진입점 (수동 전환)
│ ├── jsonexample/ # 1단계: JSON 데이터 다루기 실습 예제 │ ├── jsonexample/ # 1단계: JSON 데이터 다루기 실습 예제
│ ├── httpentity/ # 2단계: HTTP & Gin 웹 서버 실습 예제 (WIP) │ ├── httpentity/ # 2단계: HTTP & Gin 웹 서버 실습 예제 (WIP)
│ └── grpcentity/ # 3단계: gRPC 통신 구현 실습 예제 (서버/클라이언트 데모) │ └── grpc/
── protoapi.proto # gRPC 인터페이스 정의서 (IDL) ── basic/ # 3단계: gRPC 통신 구현 실습 예제 (서버/클라이언트 데모)
── protoapi/ # protoc로 컴파일 생성된 Go Stub 코드 ── protoapi.proto # gRPC 인터페이스 정의서 (IDL)
│ └── protoapi/ # protoc로 컴파일 생성된 Go Stub 코드
└── docs/ └── docs/
├── MANUSCRIPT.md # 학습 로드맵 및 각 단계별 심화 가이드로의 안내서 ├── MANUSCRIPT.md # 학습 로드맵 및 각 단계별 심화 가이드로의 안내서
├── JSON.md # 1단계: JSON 데이터 다루기 상세 가이드 ├── JSON.md # 1단계: JSON 데이터 다루기 상세 가이드
@@ -45,7 +46,7 @@ grpccanary/
### 2단계: HTTP & Gin 웹 서버 (`lib/httpentity`) ### 2단계: HTTP & Gin 웹 서버 (`lib/httpentity`)
* 대중적인 Go 웹 프레임워크 `gin-gonic`을 활용해 RESTful API 사양을 구축하는 방법을 이해합니다. (현재 주석 해제 후 실습하도록 설계된 Work-in-Progress 단계) * 대중적인 Go 웹 프레임워크 `gin-gonic`을 활용해 RESTful API 사양을 구축하는 방법을 이해합니다. (현재 주석 해제 후 실습하도록 설계된 Work-in-Progress 단계)
### 3단계: gRPC 통신 구현 (`lib/grpcentity`) ### 3단계: gRPC 통신 구현 (`lib/grpc/basic`)
* `.proto` 정의를 바탕으로 통신 스키마 계약을 강제하고, Go 언어로 gRPC 서버를 띄워 클라이언트가 날짜/시간, 무작위 비밀번호 및 정수 데이터를 실시간 원격 호출로 송수신하는 분산 통신 기초를 학습합니다. * `.proto` 정의를 바탕으로 통신 스키마 계약을 강제하고, Go 언어로 gRPC 서버를 띄워 클라이언트가 날짜/시간, 무작위 비밀번호 및 정수 데이터를 실시간 원격 호출로 송수신하는 분산 통신 기초를 학습합니다.
* `.proto` 정의를 바탕으로 통신 스키마 계약을 강제하고, Go 언어로 gRPC 서버를 띄워 클라이언트가 날짜/시간, 무작위 비밀번호 및 정수 데이터를 실시간 원격 호출로 송수신하는 분산 통신 기초를 학습합니다. * `.proto` 정의를 바탕으로 통신 스키마 계약을 강제하고, Go 언어로 gRPC 서버를 띄워 클라이언트가 날짜/시간, 무작위 비밀번호 및 정수 데이터를 실시간 원격 호출로 송수신하는 분산 통신 기초를 학습합니다.
@@ -106,4 +107,4 @@ Sensing Update Message: Sensing data updated successfully for device sensor-room
* **[docs/JSON.md](docs/JSON.md)**: 1단계 JSON 데이터 다루기 상세 학습 가이드 * **[docs/JSON.md](docs/JSON.md)**: 1단계 JSON 데이터 다루기 상세 학습 가이드
* **[docs/HTTP.md](docs/HTTP.md)**: 2단계 HTTP & Gin 웹 프레임워크 상세 학습 가이드 * **[docs/HTTP.md](docs/HTTP.md)**: 2단계 HTTP & Gin 웹 프레임워크 상세 학습 가이드
* **[docs/GRPC.md](docs/GRPC.md)**: 3단계 gRPC & Protobuf 상세 학습 가이드 (컴파일 절차 및 소스코드 구현체 분석 포함) * **[docs/GRPC.md](docs/GRPC.md)**: 3단계 gRPC & Protobuf 상세 학습 가이드 (컴파일 절차 및 소스코드 구현체 분석 포함)
* **[lib/grpcentity/README.md](lib/grpcentity/README.md)**: gRPC 실습 디렉토리 안내 및 `docs/GRPC.md` 심화 가이드로의 링크 * **[lib/grpc/basic/README.md](lib/grpc/basic/README.md)**: gRPC 실습 디렉토리 안내 및 `docs/GRPC.md` 심화 가이드로의 링크
+9 -9
View File
@@ -83,7 +83,7 @@ gRPC는 HTTP/2를 기반으로 구축된 구글의 고성능 오픈소스 원격
### 3.4 명세서 소스코드 및 구체적 분석 ### 3.4 명세서 소스코드 및 구체적 분석
실습 디렉토리 내에 선언된 [protoapi.proto](../lib/grpcentity/protoapi.proto) 명세서 코드는 앞서 기획한 `IoTService` 통신 구조를 수립하기 위해 다음과 같이 사양을 기재해 둡니다. 실습 디렉토리 내에 선언된 [protoapi.proto](../lib/grpc/basic/protoapi.proto) 명세서 코드는 앞서 기획한 `IoTService` 통신 구조를 수립하기 위해 다음과 같이 사양을 기재해 둡니다.
※ 이 코드는 기본 Unary RPC 3종만 발췌한 것이며, 전체 스펙(스트리밍 4종 포함)은 §7.1에서 이어집니다. ※ 이 코드는 기본 Unary RPC 3종만 발췌한 것이며, 전체 스펙(스트리밍 4종 포함)은 §7.1에서 이어집니다.
@@ -178,7 +178,7 @@ gRPC 빌드 및 코드 생성 환경을 구축하기 위해 사용되는 세 가
작성된 스키마 명세를 빌드하여 Go 소스코드를 생성하기 위해 다음 명령어를 구동합니다: 작성된 스키마 명세를 빌드하여 Go 소스코드를 생성하기 위해 다음 명령어를 구동합니다:
```bash ```bash
# 해당 실습 디렉터리로 이동 후 컴파일 실행 # 해당 실습 디렉터리로 이동 후 컴파일 실행
cd lib/grpcentity cd lib/grpc/basic
protoc --go_out=. --go-grpc_out=. protoapi.proto protoc --go_out=. --go-grpc_out=. protoapi.proto
``` ```
@@ -189,7 +189,7 @@ protoc --go_out=. --go-grpc_out=. protoapi.proto
**Go 패키지 지정 옵션과의 결합 규칙**: **Go 패키지 지정 옵션과의 결합 규칙**:
해당 출력 경로 옵션들은 단독으로 파일의 최종 위치를 고정하지 않습니다. 컴파일러는 지정된 기준 경로(예: `.`)에 `.proto` 스펙 내부의 `option go_package = "./protoapi;protoapi"` 설정값을 조합하여 최종 디렉터리 경로를 생성합니다. 해당 출력 경로 옵션들은 단독으로 파일의 최종 위치를 고정하지 않습니다. 컴파일러는 지정된 기준 경로(예: `.`)에 `.proto` 스펙 내부의 `option go_package = "./protoapi;protoapi"` 설정값을 조합하여 최종 디렉터리 경로를 생성합니다.
이에 따라 **컴파일 대상 디렉터리(`.`)**와 **상세 패키지 주소(`./protoapi`)**가 결합되어 `lib/grpcentity/protoapi/` 경로가 자동 생성되며, 그 하위에 다음 소스코드들이 정상 배치됩니다: 이에 따라 **컴파일 대상 디렉터리(`.`)**와 **상세 패키지 주소(`./protoapi`)**가 결합되어 `lib/grpc/basic/protoapi/` 경로가 자동 생성되며, 그 하위에 다음 소스코드들이 정상 배치됩니다:
* `protoapi.pb.go`: 명세에 정의된 메시지(Message) 규격을 Go 구조체로 변환한 파일입니다. * `protoapi.pb.go`: 명세에 정의된 메시지(Message) 규격을 Go 구조체로 변환한 파일입니다.
* `protoapi_grpc.pb.go`: 클라이언트와 서버 통신을 위한 원격 호출(Service) 규격을 구현한 파일입니다. * `protoapi_grpc.pb.go`: 클라이언트와 서버 통신을 위한 원격 호출(Service) 규격을 구현한 파일입니다.
@@ -199,7 +199,7 @@ protoc --go_out=. --go-grpc_out=. protoapi.proto
컴파일러를 통해 통신을 위한 스터브(Stub) 코드가 확보되었으므로, 이를 기반으로 서버와 클라이언트의 비즈니스 로직을 연결하는 상세 코드를 검토합니다. 컴파일러를 통해 통신을 위한 스터브(Stub) 코드가 확보되었으므로, 이를 기반으로 서버와 클라이언트의 비즈니스 로직을 연결하는 상세 코드를 검토합니다.
### 5.1 gRPC 서버 구현 분석 ([server.go](../lib/grpcentity/server.go)) ### 5.1 gRPC 서버 구현 분석 ([server.go](../lib/grpc/basic/server.go))
* **서버 서비스 인터페이스 매핑 구조체 (`IoTServer`)**: * **서버 서비스 인터페이스 매핑 구조체 (`IoTServer`)**:
```go ```go
@@ -243,7 +243,7 @@ protoc --go_out=. --go-grpc_out=. protoapi.proto
* **쉬운 설명**: `:8080` 포트로 통하는 소켓(전화선)을 개통하고, 기기들의 전화(접속 및 호출)가 오기를 기다리며 대기 상태로 들어가는 서버 구동 시작점입니다. * **쉬운 설명**: `:8080` 포트로 통하는 소켓(전화선)을 개통하고, 기기들의 전화(접속 및 호출)가 오기를 기다리며 대기 상태로 들어가는 서버 구동 시작점입니다.
* **상세 설명**: 지정된 포트(기본 포트 `:8080`)의 TCP 소켓 포트를 활성화하고, 클라이언트의 접속 및 RPC 서비스 호출에 대해 지속적으로 대기하는 리스너 구동의 진입점입니다. * **상세 설명**: 지정된 포트(기본 포트 `:8080`)의 TCP 소켓 포트를 활성화하고, 클라이언트의 접속 및 RPC 서비스 호출에 대해 지속적으로 대기하는 리스너 구동의 진입점입니다.
### 5.2 gRPC 클라이언트 구현 분석 ([client.go](../lib/grpcentity/client.go)) ### 5.2 gRPC 클라이언트 구현 분석 ([client.go](../lib/grpc/basic/client.go))
* **원격 서비스 클라이언트 기동 (`NewIoTServiceClient`)**: * **원격 서비스 클라이언트 기동 (`NewIoTServiceClient`)**:
```go ```go
@@ -317,7 +317,7 @@ message DownloadRequest {
``` ```
* **설계 포인트**: `stream` 키워드가 들어간 위치에 주목합니다. `UploadFile`은 입력에 `stream`이 붙어 클라이언트 스트리밍을, `DownloadFile`은 반환(returns)에 `stream`이 붙어 서버 스트리밍 채널을 개설합니다. * **설계 포인트**: `stream` 키워드가 들어간 위치에 주목합니다. `UploadFile`은 입력에 `stream`이 붙어 클라이언트 스트리밍을, `DownloadFile`은 반환(returns)에 `stream`이 붙어 서버 스트리밍 채널을 개설합니다.
### 6.2 서버 저장 및 송수신 구현 ([server.go](../lib/grpcentity/server.go)) ### 6.2 서버 저장 및 송수신 구현 ([server.go](../lib/grpc/basic/server.go))
인메모리 파일 저장소(`fileStore`)를 구현하고 목록 조회(`ListFiles`) 및 서버 다운로드 스트리밍(`DownloadFile`) 핸들러를 정의합니다: 인메모리 파일 저장소(`fileStore`)를 구현하고 목록 조회(`ListFiles`) 및 서버 다운로드 스트리밍(`DownloadFile`) 핸들러를 정의합니다:
```go ```go
@@ -427,7 +427,7 @@ func (IoTServer) DownloadFile(r *protoapi.DownloadRequest, stream protoapi.IoTSe
* **목록 조회**: 동시 접근 보호(Race condition 방지)를 위해 읽기 전용 락(`RLock`)을 획득한 후 인메모리 맵을 순회하며 메타데이터 구조체 목록을 집계해 반환합니다. * **목록 조회**: 동시 접근 보호(Race condition 방지)를 위해 읽기 전용 락(`RLock`)을 획득한 후 인메모리 맵을 순회하며 메타데이터 구조체 목록을 집계해 반환합니다.
* **파일 다운로드**: 대상 파일 쿼리 실패 시 gRPC 표준 에러(`codes.NotFound`)를 반환합니다. 검증 통과 시 루프 내에서 가상 윈도우 슬라이싱을 집행해 청크 구조체를 구성하고, `stream.Send()`로 직렬화 패킷을 클라이언트 버퍼 큐에 기입합니다. * **파일 다운로드**: 대상 파일 쿼리 실패 시 gRPC 표준 에러(`codes.NotFound`)를 반환합니다. 검증 통과 시 루프 내에서 가상 윈도우 슬라이싱을 집행해 청크 구조체를 구성하고, `stream.Send()`로 직렬화 패킷을 클라이언트 버퍼 큐에 기입합니다.
### 6.3 클라이언트 송수신 기동 ([client.go](../lib/grpcentity/client.go)) ### 6.3 클라이언트 송수신 기동 ([client.go](../lib/grpc/basic/client.go))
클라이언트는 업로드에 성공한 뒤, 서버에 파일 목록 조회를 요구하고, 다운로드 스트림을 개설해 조각 데이터를 재조립하여 무결성을 검사합니다. 클라이언트는 업로드에 성공한 뒤, 서버에 파일 목록 조회를 요구하고, 다운로드 스트림을 개설해 조각 데이터를 재조립하여 무결성을 검사합니다.
```go ```go
@@ -516,7 +516,7 @@ message AlertMessage {
} }
``` ```
#### 2. 서버 사이드 구독자 관리 및 발행 구현 ([server.go](../lib/grpcentity/server.go)) #### 2. 서버 사이드 구독자 관리 및 발행 구현 ([server.go](../lib/grpc/basic/server.go))
서버는 구독을 신청한 클라이언트들에게 메시지를 안전하게 분배하기 위해 스레드 세이프 맵과 고루틴 채널(`chan`) 구조를 구성합니다: 서버는 구독을 신청한 클라이언트들에게 메시지를 안전하게 분배하기 위해 스레드 세이프 맵과 고루틴 채널(`chan`) 구조를 구성합니다:
```go ```go
@@ -586,7 +586,7 @@ func (IoTServer) SubscribeAlerts(r *protoapi.AlertSubscription, stream protoapi.
* **구독 신청**: `SubscribeAlerts` 엔드포인트는 호출과 동시에 전용 Go 비동기 버퍼 채널을 생성하고 전역 가입 맵에 등록합니다. `stream.Context().Done()` 채널 수신이나 스트림 유실 이벤트가 포착되기 전까지 루프 대기 상태를 안전하게 고정합니다. * **구독 신청**: `SubscribeAlerts` 엔드포인트는 호출과 동시에 전용 Go 비동기 버퍼 채널을 생성하고 전역 가입 맵에 등록합니다. `stream.Context().Done()` 채널 수신이나 스트림 유실 이벤트가 포착되기 전까지 루프 대기 상태를 안전하게 고정합니다.
* **경보 발행**: 동시성 경쟁 방지 락(`subMu.Lock()`) 임계 구역 내에서 연결된 모든 채널에 데이터를 `select-default` 논블로킹 패턴으로 분배 기입하여, 특정 클라이언트의 수신 병목이 서버 전체 성능에 미치는 파급 효과를 예방합니다. * **경보 발행**: 동시성 경쟁 방지 락(`subMu.Lock()`) 임계 구역 내에서 연결된 모든 채널에 데이터를 `select-default` 논블로킹 패턴으로 분배 기입하여, 특정 클라이언트의 수신 병목이 서버 전체 성능에 미치는 파급 효과를 예방합니다.
#### 3. 클라이언트 비동기 청취 구현 ([client.go](../lib/grpcentity/client.go)) #### 3. 클라이언트 비동기 청취 구현 ([client.go](../lib/grpc/basic/client.go))
클라이언트는 메인 흐름을 방해하지 않고 알림을 백그라운드에서 실시간으로 대기 청취할 수 있도록 별도의 독자적인 비동기 고루틴 구조로 가동합니다. 클라이언트는 메인 흐름을 방해하지 않고 알림을 백그라운드에서 실시간으로 대기 청취할 수 있도록 별도의 독자적인 비동기 고루틴 구조로 가동합니다.
```go ```go
@@ -1,12 +1,12 @@
# lib/grpcentity 실습 설명서 # lib/grpc/basic 실습 설명서
본 디렉토리는 Go 언어를 활용한 gRPC 서버 및 클라이언트 실습 예제를 포함하고 있습니다. 본 디렉토리는 Go 언어를 활용한 gRPC 서버 및 클라이언트 실습 예제를 포함하고 있습니다.
## 📖 실습 상세 분석 및 가이드 안내 ## 📖 실습 상세 분석 및 가이드 안내
학습의 일관성을 위해, 이 실습의 상세 분석 및 개념 명세는 통합 교재의 gRPC 심화 가이드인 **[docs/GRPC.md](../../docs/GRPC.md)**로 모듈화되어 있습니다. 전체 학습 로드맵은 [docs/MANUSCRIPT.md](../../docs/MANUSCRIPT.md)를 참고하십시오. 학습의 일관성을 위해, 이 실습의 상세 분석 및 개념 명세는 통합 교재의 gRPC 심화 가이드인 **[docs/GRPC.md](../../../docs/GRPC.md)**로 모듈화되어 있습니다. 전체 학습 로드맵은 [docs/MANUSCRIPT.md](../../../docs/MANUSCRIPT.md)를 참고하십시오.
[docs/GRPC.md](../../docs/GRPC.md) 문서에서 다음 내용을 참고하실 수 있습니다: [docs/GRPC.md](../../../docs/GRPC.md) 문서에서 다음 내용을 참고하실 수 있습니다:
* **IDL ([protoapi.proto](./protoapi.proto)) 명세 및 필드 분석** * **IDL ([protoapi.proto](./protoapi.proto)) 명세 및 필드 분석**
* **Go에서의 `protoc` 설치 및 Stub 파일 컴파일 방법** * **Go에서의 `protoc` 설치 및 Stub 파일 컴파일 방법**
* **gRPC 서버 코드 ([server.go](./server.go)) 구현 상세 분석** * **gRPC 서버 코드 ([server.go](./server.go)) 구현 상세 분석**
@@ -1,9 +1,9 @@
package entity package basic
import ( import (
"context" "context"
"fmt" "fmt"
"grpccanary/lib/grpcentity/protoapi" "grpccanary/lib/grpc/basic/protoapi"
"io" "io"
"math/rand" "math/rand"
"time" "time"
@@ -207,11 +207,13 @@ func ClientRun(addr string) {
// 7단계: 임계값 초과 온습도 전송을 통한 Pub/Sub 실시간 알림 유발 시뮬레이션 // 7단계: 임계값 초과 온습도 전송을 통한 Pub/Sub 실시간 알림 유발 시뮬레이션
fmt.Println("Sending abnormal high-temperature sensing data (45.8°C)...") fmt.Println("Sending abnormal high-temperature sensing data (45.8°C)...")
alertRes, err := AskUpdateSensingData(context.Background(), client, "sensor-room-01", 45.8, 60.1) alertRes, err := AskUpdateSensingData(context.Background(), client, "sensor-room-01", 45.8, 60.1)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
fmt.Println("Abnormal Sensing Update Success:", alertRes.Success) fmt.Println("Abnormal Sensing Update Success:", alertRes.Success)
// 알림 이벤트가 비동기로 화면에 출력될 시간을 확보하기 위해 100ms 대기 // 알림 이벤트가 비동기로 화면에 출력될 시간을 확보하기 위해 100ms 대기
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.36.11
// protoc v5.27.2 // protoc v3.21.12
// source: protoapi.proto // source: protoapi.proto
package protoapi package protoapi
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.6.2 // - protoc-gen-go-grpc v1.6.2
// - protoc v5.27.2 // - protoc v3.21.12
// source: protoapi.proto // source: protoapi.proto
package protoapi package protoapi
@@ -1,9 +1,9 @@
package entity package basic
import ( import (
"context" "context"
"fmt" "fmt"
"grpccanary/lib/grpcentity/protoapi" "grpccanary/lib/grpc/basic/protoapi"
"io" "io"
"math/rand" "math/rand"
"net" "net"
+3 -3
View File
@@ -2,7 +2,7 @@ package main
import ( import (
"fmt" "fmt"
entity "grpccanary/lib/grpcentity" "grpccanary/lib/grpc/basic"
"grpccanary/lib/jsonexample" "grpccanary/lib/jsonexample"
"time" "time"
) )
@@ -21,11 +21,11 @@ func grpcSample() {
fmt.Println("--- starting gRPC IoT Simulation ---") fmt.Println("--- starting gRPC IoT Simulation ---")
// 1. gRPC 서버를 백그라운드 고루틴으로 구동 // 1. gRPC 서버를 백그라운드 고루틴으로 구동
go entity.ServerRun(port) go basic.ServerRun(port)
// 2. 서버 포트가 바인딩되어 통신 대기 상태에 들어갈 시간을 일시적으로 보장 // 2. 서버 포트가 바인딩되어 통신 대기 상태에 들어갈 시간을 일시적으로 보장
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
// 3. gRPC 클라이언트를 구동하여 원격 프로시저(RPC) 기동 시뮬레이션 집행 // 3. gRPC 클라이언트를 구동하여 원격 프로시저(RPC) 기동 시뮬레이션 집행
entity.ClientRun(port) basic.ClientRun(port)
} }