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