docs: remove Section 8 (Next Steps), move Troubleshooting to Section 7, and renumber Streaming to Section 6

This commit is contained in:
2026-07-17 20:09:51 +09:00
parent 9de3f5144d
commit 600a950806
+12 -21
View File
@@ -272,24 +272,13 @@ protoc --go_out=. --go-grpc_out=. protoapi.proto
--- ---
## 6. 트러블슈팅 (Troubleshooting) ## 6. 대용량 데이터 전송을 위한 스트리밍(Streaming) 구현
실습 구동 과정에서 마주할 수 있는 전형적인 에러 현상과 대처 방안입니다.
### 6.1 `bind: address already in use` (네트워크 소켓 포트 충돌)
* **발생 원인**: gRPC 서버 기동 시 설정한 통신 포트 `:8080`이 이미 다른 네트워크 프로세스나 이전 실습 서버의 비정상 종료 등으로 인해 점유되어 바인딩에 실패한 상태입니다.
* **조치 방법**:
- `lib/main.go`의 `port` 변수 값을 다른 유휴 포트(예: `:9090`)로 변경한 뒤 다시 실행하십시오. `ServerRun(addr)` 함수가 이 값을 메인 진입점으로부터 인자로 전달받아 TCP 리스너를 생성하므로, `main.go` 한 곳만 수정하면 서버와 클라이언트의 통신 포트가 동시에 성공적으로 변경됩니다.
---
## 7. 대용량 데이터 전송을 위한 스트리밍(Streaming) 구현
쉽게 말해, 지금까지 배운 방식(Unary)이 '궁금한 게 있을 때마다 전화를 걸어 질문 하나씩 하고 바로 끊는 것'이라면, 스트리밍은 **'전화를 한 번 걸어 끊지 않은 상태로, 준비된 데이터 조각(Chunk)들을 긴 띠처럼 끊임없이 흘려보내는 것'**입니다. 큰 파일을 한 번에 통째로 보내면 메모리가 가득 차서 전송하기 어려우므로, 전화선은 그대로 유지한 채 데이터를 작게 나누어 연속으로 전달하는 방법을 배웁니다. 쉽게 말해, 지금까지 배운 방식(Unary)이 '궁금한 게 있을 때마다 전화를 걸어 질문 하나씩 하고 바로 끊는 것'이라면, 스트리밍은 **'전화를 한 번 걸어 끊지 않은 상태로, 준비된 데이터 조각(Chunk)들을 긴 띠처럼 끊임없이 흘려보내는 것'**입니다. 큰 파일을 한 번에 통째로 보내면 메모리가 가득 차서 전송하기 어려우므로, 전화선은 그대로 유지한 채 데이터를 작게 나누어 연속으로 전달하는 방법을 배웁니다.
일반적인 단발성 요청/응답(Unary) 통신은 전송할 전체 데이터를 단일 메모리에 전부 올려 적재한 상태에서 동작하므로, 펌웨어나 대형 이미지 같은 대용량 데이터를 다룰 때 메모리 고갈(OOM)이나 네트워크 대역폭 병목을 초래하기 쉽습니다. gRPC는 HTTP/2 프로토콜의 스트림(Stream) 채널을 기본 가용하므로, 데이터를 일정 크기(Chunk) 단위로 쪼개 연속적으로 전송할 수 있는 강력한 **스트리밍(Streaming)** 기법을 지원합니다. 본 예제에서는 클라이언트가 파일을 조각내어 보내는 **클라이언트 스트리밍(Client Streaming)**, 업로드된 파일 메타 정보를 모아 한 번에 내려주는 **단일 조회(Unary RPC)**, 그리고 서버가 데이터를 쪼개어 클라이언트에게 보내는 **서버 스트리밍(Server Streaming)**까지 모두 종합 설계하여 탑재했습니다. 일반적인 단발성 요청/응답(Unary) 통신은 전송할 전체 데이터를 단일 메모리에 전부 올려 적재한 상태에서 동작하므로, 펌웨어나 대형 이미지 같은 대용량 데이터를 다룰 때 메모리 고갈(OOM)이나 네트워크 대역폭 병목을 초래하기 쉽습니다. gRPC는 HTTP/2 프로토콜의 스트림(Stream) 채널을 기본 가용하므로, 데이터를 일정 크기(Chunk) 단위로 쪼개 연속적으로 전송할 수 있는 강력한 **스트리밍(Streaming)** 기법을 지원합니다. 본 예제에서는 클라이언트가 파일을 조각내어 보내는 **클라이언트 스트리밍(Client Streaming)**, 업로드된 파일 메타 정보를 모아 한 번에 내려주는 **단일 조회(Unary RPC)**, 그리고 서버가 데이터를 쪼개어 클라이언트에게 보내는 **서버 스트리밍(Server Streaming)**까지 모두 종합 설계하여 탑재했습니다.
### 7.1 스키마 설계 (`protoapi.proto`) ### 6.1 스키마 설계 (`protoapi.proto`)
업로드와 리스트 조회, 그리고 다운로드를 위한 gRPC 메시지 규격을 명세합니다: 업로드와 리스트 조회, 그리고 다운로드를 위한 gRPC 메시지 규격을 명세합니다:
```proto ```proto
service IoTService { service IoTService {
@@ -328,7 +317,7 @@ message DownloadRequest {
``` ```
* **설계 포인트**: `stream` 키워드가 들어간 위치에 주목합니다. `UploadFile`은 입력에 `stream`이 붙어 클라이언트 스트리밍을, `DownloadFile`은 반환(returns)에 `stream`이 붙어 서버 스트리밍 채널을 개설합니다. * **설계 포인트**: `stream` 키워드가 들어간 위치에 주목합니다. `UploadFile`은 입력에 `stream`이 붙어 클라이언트 스트리밍을, `DownloadFile`은 반환(returns)에 `stream`이 붙어 서버 스트리밍 채널을 개설합니다.
### 7.2 서버 저장 및 송수신 구현 ([server.go](../lib/grpcentity/server.go)) ### 6.2 서버 저장 및 송수신 구현 ([server.go](../lib/grpcentity/server.go))
인메모리 파일 저장소(`fileStore`)를 구현하고 목록 조회(`ListFiles`) 및 서버 다운로드 스트리밍(`DownloadFile`) 핸들러를 정의합니다: 인메모리 파일 저장소(`fileStore`)를 구현하고 목록 조회(`ListFiles`) 및 서버 다운로드 스트리밍(`DownloadFile`) 핸들러를 정의합니다:
```go ```go
@@ -438,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()`로 직렬화 패킷을 클라이언트 버퍼 큐에 기입합니다.
### 7.3 클라이언트 송수신 기동 ([client.go](../lib/grpcentity/client.go)) ### 6.3 클라이언트 송수신 기동 ([client.go](../lib/grpcentity/client.go))
클라이언트는 업로드에 성공한 뒤, 서버에 파일 목록 조회를 요구하고, 다운로드 스트림을 개설해 조각 데이터를 재조립하여 무결성을 검사합니다. 클라이언트는 업로드에 성공한 뒤, 서버에 파일 목록 조회를 요구하고, 다운로드 스트림을 개설해 조각 데이터를 재조립하여 무결성을 검사합니다.
```go ```go
@@ -505,7 +494,7 @@ func AskDownloadFile(ctx context.Context, m protoapi.IoTServiceClient, fileName
* **목록 조회**: 빈 메시지(`EmptyRequest`)를 동봉해 Unary RPC 채널을 트리거하고 메타데이터 배열 결과를 동기 획득합니다. * **목록 조회**: 빈 메시지(`EmptyRequest`)를 동봉해 Unary RPC 채널을 트리거하고 메타데이터 배열 결과를 동기 획득합니다.
* **파일 다운로드**: 서버 스트리밍 엔드포인트 기동 후, `stream.Recv()` 블로킹 수신 루프에 진입합니다. 채널 해제 지점(`io.EOF`)에 도달할 때까지 메모리 버퍼 슬라이스에 청크 바이트 배열을 병합 누적하여 재조립(Reassembly)을 마친 후 반환합니다. * **파일 다운로드**: 서버 스트리밍 엔드포인트 기동 후, `stream.Recv()` 블로킹 수신 루프에 진입합니다. 채널 해제 지점(`io.EOF`)에 도달할 때까지 메모리 버퍼 슬라이스에 청크 바이트 배열을 병합 누적하여 재조립(Reassembly)을 마친 후 반환합니다.
### 7.4 실시간 알림을 위한 Pub/Sub (발행/구독) 브로드캐스팅 구현 ### 6.4 실시간 알림을 위한 Pub/Sub (발행/구독) 브로드캐스팅 구현
쉽게 말해 Pub/Sub은 '신문 구독'과 같습니다. 구독자(클라이언트)가 한 번 신청해 두면, 발행자(서버)는 새로운 소식(경보)이 생길 때마다 모든 구독자에게 알아서 배달해 줍니다. 클라이언트가 매번 '무슨 일 없어요?'라고 다시 물어볼 필요가 없다는 것이 핵심입니다. 쉽게 말해 Pub/Sub은 '신문 구독'과 같습니다. 구독자(클라이언트)가 한 번 신청해 두면, 발행자(서버)는 새로운 소식(경보)이 생길 때마다 모든 구독자에게 알아서 배달해 줍니다. 클라이언트가 매번 '무슨 일 없어요?'라고 다시 물어볼 필요가 없다는 것이 핵심입니다.
@@ -630,15 +619,17 @@ func AskSubscribeAlerts(ctx context.Context, m protoapi.IoTServiceClient, client
--- ---
## 8. 한 걸음 더 나아가기 (다음 단계) ## 7. 트러블슈팅 (Troubleshooting)
본 기초 실습을 끝마치셨다면, 아래 과제를 해결해보세요: 실습 구동 과정에서 마주할 수 있는 전형적인 에러 현상과 대처 방안입니다.
1. **약속 스펙 확장해 보기**: [protoapi.proto](../lib/grpcentity/protoapi.proto) 파일에 새로운 환경 데이터(예: 미세먼지 수치 `double Dust = 4;`)를 슬쩍 얹어본 뒤, 직접 번역기를 새로 돌리고 Go 소스코드를 고치며 확장해 봅니다. ### 7.1 `bind: address already in use` (네트워크 소켓 포트 충돌)
2. **proto 명세서 물리 분할하기 (실전 모듈화)**: 현재 단일 파일로 구성된 `protoapi.proto`를 기능 성격에 맞춰 `iot_service.proto`(Unary 제어 메시지)와 `file_transfer.proto`(스트리밍 전송 메시지) 2개의 파일로 쪼개어 구성해 봅니다. 이때 두 파일의 `option go_package` 공유 방식과 번역기(`protoc`) 명령어를 다중 타겟(또는 `*.proto`)으로 조율하여 정상 빌드해 보세요. * **발생 원인**: gRPC 서버 기동 시 설정한 통신 포트 `:8080`이 이미 다른 네트워크 프로세스나 이전 실습 서버의 비정상 종료 등으로 인해 점유되어 바인딩에 실패한 상태입니다.
* **조치 방법**:
- `lib/main.go`의 `port` 변수 값을 다른 유휴 포트(예: `:9090`)로 변경한 뒤 다시 실행하십시오. `ServerRun(addr)` 함수가 이 값을 메인 진입점으로부터 인자로 전달받아 TCP 리스너를 생성하므로, `main.go` 한 곳만 수정하면 서버와 클라이언트의 통신 포트가 동시에 성공적으로 변경됩니다.
--- ---
## 9. 참고 자료 ## 8. 참고 자료
* [gRPC와 REST의 차이점 (AWS)](https://aws.amazon.com/ko/compare/the-difference-between-grpc-and-rest/): 두 방식의 특징과 언제 어떤 기술을 선택해야 하는지 친절하게 정리된 공식 블로그 자료입니다. * [gRPC와 REST의 차이점 (AWS)](https://aws.amazon.com/ko/compare/the-difference-between-grpc-and-rest/): 두 방식의 특징과 언제 어떤 기술을 선택해야 하는지 친절하게 정리된 공식 블로그 자료입니다.