docs: restore missing contents and align canonical stage names across docs
This commit is contained in:
@@ -29,21 +29,25 @@ grpccanary/
|
|||||||
├── protoapi/ # protoc로 컴파일 생성된 Go Stub 코드
|
├── protoapi/ # protoc로 컴파일 생성된 Go Stub 코드
|
||||||
├── examples/
|
├── examples/
|
||||||
│ ├── main.go # 학습 예제 통합 실행 진입점 (수동 전환)
|
│ ├── main.go # 학습 예제 통합 실행 진입점 (수동 전환)
|
||||||
│ ├── jsonexample/ # [1단계] encoding/json 표준 직렬화 예제
|
│ ├── jsonexample/ # 1단계: JSON 데이터 다루기 실습 예제
|
||||||
│ ├── httpentity/ # [2단계] Gin-gonic 기반 HTTP API 서버 (WIP)
|
│ ├── httpentity/ # 2단계: HTTP & Gin 웹 서버 실습 예제 (WIP)
|
||||||
│ └── grpcentity/ # [3단계] gRPC 서비스 구현체 (서버/클라이언트 데모)
|
│ └── grpcentity/ # 3단계: gRPC 통신 구현 실습 예제 (서버/클라이언트 데모)
|
||||||
└── docs/
|
└── docs/
|
||||||
└── MANUSCRIPT.md # JSON, HTTP, gRPC에 대한 통합 상세 개념서
|
├── MANUSCRIPT.md # 학습 로드맵 및 각 단계별 심화 가이드로의 안내서
|
||||||
|
├── JSON.md # 1단계: JSON 데이터 다루기 상세 가이드
|
||||||
|
├── HTTP.md # 2단계: HTTP & Gin 웹 서버 상세 가이드
|
||||||
|
└── GRPC.md # 3단계: gRPC 통신 구현 상세 가이드
|
||||||
```
|
```
|
||||||
|
|
||||||
### 1단계: JSON 데이터 다루기 (`examples/jsonexample`)
|
### 1단계: JSON 데이터 다루기 (`examples/jsonexample`)
|
||||||
* Go 표준 라이브러리인 `encoding/json`을 활용하여 구조체(Struct)와 JSON 데이터 간의 마샬링(Serialization) 및 언마샬링(Deserialization) 기법을 학습합니다.
|
* Go 표준 라이브러리인 `encoding/json`을 활용하여 구조체(Struct)와 JSON 데이터 간의 마샬링(Serialization) 및 언마샬링(Deserialization) 기법을 학습합니다.
|
||||||
|
|
||||||
### 2단계: Gin 기반 HTTP 웹 서버 (`examples/httpentity`)
|
### 2단계: HTTP & Gin 웹 서버 (`examples/httpentity`)
|
||||||
* 대중적인 Go 웹 프레임워크 `gin-gonic`을 활용해 RESTful API 사양을 구축하는 방법을 이해합니다. (현재 주석 해제 후 실습하도록 설계된 Work-in-Progress 단계)
|
* 대중적인 Go 웹 프레임워크 `gin-gonic`을 활용해 RESTful API 사양을 구축하는 방법을 이해합니다. (현재 주석 해제 후 실습하도록 설계된 Work-in-Progress 단계)
|
||||||
|
|
||||||
### 3단계: gRPC 통신 구현 (`examples/grpcentity`)
|
### 3단계: gRPC 통신 구현 (`examples/grpcentity`)
|
||||||
* `.proto` 정의를 바탕으로 통신 스키마 계약을 강제하고, Go 언어로 gRPC 서버를 띄워 클라이언트가 날짜/시간, 무작위 비밀번호 및 정수 데이터를 실시간 원격 호출로 송수신하는 분산 통신 기초를 학습합니다.
|
* `.proto` 정의를 바탕으로 통신 스키마 계약을 강제하고, Go 언어로 gRPC 서버를 띄워 클라이언트가 날짜/시간, 무작위 비밀번호 및 정수 데이터를 실시간 원격 호출로 송수신하는 분산 통신 기초를 학습합니다.
|
||||||
|
* `.proto` 정의를 바탕으로 통신 스키마 계약을 강제하고, Go 언어로 gRPC 서버를 띄워 클라이언트가 날짜/시간, 무작위 비밀번호 및 정수 데이터를 실시간 원격 호출로 송수신하는 분산 통신 기초를 학습합니다.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -83,15 +87,22 @@ gRPC 예제 실행 시, 서버가 백업 고루틴으로 `:8080` 포트에 대
|
|||||||
```text
|
```text
|
||||||
Serving requests...
|
Serving requests...
|
||||||
Client:
|
Client:
|
||||||
Server Date and Time: 2026-07-12 20:00:00.123456789 +0900 KST
|
Server Date and Time: 2026-07-12 20:00:00.123456789 +0900 KST m=+1.000000001
|
||||||
Random Password: &c(D7f/G#s%d
|
Random Password: &c(D7f/G#s%d
|
||||||
Random Integer 1: 42
|
Random Integer 1: 42
|
||||||
Random Integer 2: 87
|
Random Integer 2: 87
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 4. 트러블슈팅
|
||||||
|
* **`module grpccanary: package ... is not in GOROOT`**:
|
||||||
|
- 모듈 로드 에러가 발생한 경우, 리포지토리 루트 경로(작업 디렉토리)에서 명령어를 올바르게 실행했는지 재확인하고, `go clean -modcache` 후 `go mod tidy`를 수행해 보십시오.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📚 추가 학습 리소스
|
## 📚 추가 학습 리소스
|
||||||
|
|
||||||
* **[docs/MANUSCRIPT.md](docs/MANUSCRIPT.md)**: JSON, HTTP, gRPC 통신의 원리와 배경지식(REST의 한계와 gRPC 도입 이유 등)을 담은 본 프로젝트 공식 개념서
|
* **[docs/MANUSCRIPT.md](docs/MANUSCRIPT.md)**: JSON, HTTP, gRPC 통신의 원리와 배경지식(REST의 한계와 gRPC 도입 이유 등)을 담은 학습 로드맵 및 가이드 안내서
|
||||||
* **[examples/grpcentity/README.md](examples/grpcentity/README.md)**: Protobuf 빌드 컴파일 가이드 및 상세 소스코드 구현체 분석 자료
|
* **[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 상세 학습 가이드 (컴파일 절차 및 소스코드 구현체 분석 포함)
|
||||||
|
* **[examples/grpcentity/README.md](examples/grpcentity/README.md)**: gRPC 실습 디렉토리 안내 및 `docs/GRPC.md` 심화 가이드로의 링크
|
||||||
|
|||||||
+38
-4
@@ -1,4 +1,6 @@
|
|||||||
# gRPC & Protocol Buffers 심화 학습
|
# 3단계: gRPC 통신 구현 상세 가이드
|
||||||
|
|
||||||
|
⬅ [학습 로드맵으로 돌아가기](MANUSCRIPT.md)
|
||||||
|
|
||||||
이 문서는 `grpccanary` 프로젝트의 **3단계: gRPC 통신 구현**에 대한 이론적 배경, 스키마 명세, 컴파일 기법 및 구체적인 소스코드 분석을 설명합니다.
|
이 문서는 `grpccanary` 프로젝트의 **3단계: gRPC 통신 구현**에 대한 이론적 배경, 스키마 명세, 컴파일 기법 및 구체적인 소스코드 분석을 설명합니다.
|
||||||
|
|
||||||
@@ -28,7 +30,7 @@ gRPC는 HTTP/2를 기반으로 구축된 구글의 고성능 오픈소스 원격
|
|||||||
|
|
||||||
## 2. 인터페이스 명세서 (`protoapi.proto`)
|
## 2. 인터페이스 명세서 (`protoapi.proto`)
|
||||||
|
|
||||||
저장소 루트의 [protoapi.proto](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/protoapi.proto) 파일은 의사 난수, 비밀번호, 날짜 데이터를 교환하는 `Random` 서비스를 제공하기 위해 아래와 같이 사양을 선언해 둡니다.
|
저장소 루트의 [protoapi.proto](../protoapi.proto) 파일은 의사 난수, 비밀번호, 날짜 데이터를 교환하는 `Random` 서비스를 제공하기 위해 아래와 같이 사양을 선언해 둡니다.
|
||||||
|
|
||||||
```proto
|
```proto
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
@@ -99,7 +101,7 @@ protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. \
|
|||||||
|
|
||||||
## 4. 실습 코드 구현 상세 분석
|
## 4. 실습 코드 구현 상세 분석
|
||||||
|
|
||||||
### 4.1 gRPC 서버 구현 ([server.go](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/examples/grpcentity/server.go))
|
### 4.1 gRPC 서버 구현 ([server.go](../examples/grpcentity/server.go))
|
||||||
|
|
||||||
* **구조체 정의**:
|
* **구조체 정의**:
|
||||||
```go
|
```go
|
||||||
@@ -118,7 +120,7 @@ protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. \
|
|||||||
server.Serve(listen)
|
server.Serve(listen)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.2 gRPC 클라이언트 구현 ([client.go](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/examples/grpcentity/client.go))
|
### 4.2 gRPC 클라이언트 구현 ([client.go](../examples/grpcentity/client.go))
|
||||||
|
|
||||||
* **연결 수립**:
|
* **연결 수립**:
|
||||||
```go
|
```go
|
||||||
@@ -129,3 +131,35 @@ protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. \
|
|||||||
`insecure.NewCredentials()`를 전달하여 TLS를 건너뛴 채 평문으로 빠르고 간단한 로컬 테스트 환경을 구축합니다.
|
`insecure.NewCredentials()`를 전달하여 TLS를 건너뛴 채 평문으로 빠르고 간단한 로컬 테스트 환경을 구축합니다.
|
||||||
* **원격 호출**:
|
* **원격 호출**:
|
||||||
`client.GetDate()`, `client.GetRandom()`, `client.GetRandomPass()`를 차례로 호출하여 매개변수와 결과를 콘솔로 확인합니다.
|
`client.GetDate()`, `client.GetRandom()`, `client.GetRandomPass()`를 차례로 호출하여 매개변수와 결과를 콘솔로 확인합니다.
|
||||||
|
|
||||||
|
### 4.3 gRPC 실습 예제 동작 흐름
|
||||||
|
예제가 구동되면 서버와 클라이언트 간에 다음과 같은 호출 시퀀스가 순차적으로 실행됩니다:
|
||||||
|
1. **날짜 조회 (`GetDate`)**: 클라이언트가 서버에 날짜 조회를 요청하고, 서버는 자체의 현재 날짜와 시간 문자열을 포맷하여 반환합니다.
|
||||||
|
2. **비밀번호 생성 (`GetRandomPass`)**: 클라이언트가 생성할 무작위 비밀번호의 길이(기본 8자)와 난수 생성 시드값을 전달하면, 서버는 지정된 사양의 임의 문자열을 작성해 반환합니다.
|
||||||
|
3. **난수 생성 (`GetRandom`)**: 서로 다른 위치(Place) 및 시드(Seed) 값을 전달하여 각각 1회씩, 총 2회의 독립적인 난수 생성(의사 난수 정수값) 결과를 반환받아 출력합니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 트러블슈팅 (Troubleshooting)
|
||||||
|
|
||||||
|
gRPC 서버 및 클라이언트 실습 과정에서 직면할 수 있는 대표적인 문제와 해결 방안입니다.
|
||||||
|
|
||||||
|
### 5.1 `listen tcp :8080: bind: address already in use`
|
||||||
|
* **원인**: 포트 `8080`이 이미 다른 백그라운드 프로세스나 기존 기동된 서버에 의해 점유되어 충돌이 난 상태입니다.
|
||||||
|
* **해결 방법**:
|
||||||
|
- `examples/main.go` 의 `port` 변수값과 [server.go](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/examples/grpcentity/server.go)의 `port` 전역 변수값을 동시에 다른 포트(예: `:9090`)로 변경하고 재시도해야 합니다.
|
||||||
|
- **주의**: `server.go` 내부의 `ServerRun(addr string)` 함수는 외부 진입점으로부터 인자 `addr`을 인가받지만, 실제 포트 리슨 코드에서는 이를 무시하고 패키지 전역 변수 `port = ":8080"`를 직접 읽어 처리하도록 하드코딩되어 있습니다. 따라서 정상적으로 포트를 바꾸기 위해서는 반드시 `server.go` 내부 전역 변수인 `port` 값을 수정해 주어야 포트 바인딩이 성공합니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 다음 단계 (Next Steps)
|
||||||
|
|
||||||
|
gRPC 통신 방식을 한층 더 깊이 탐구해 보려면 다음과 같은 후속 실습을 추천합니다:
|
||||||
|
1. **메시지 스펙 확장**: 루트 디렉토리의 [protoapi.proto](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/protoapi.proto)에 새로운 필드를 추가하거나 메서드를 정의한 뒤, Stub을 재컴파일([examples/grpcentity/README.md](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/examples/grpcentity/README.md) 컴파일 가이드 참고)해 보십시오.
|
||||||
|
2. **인터페이스 모듈 연계**: 생성된 gRPC 클라이언트 및 서버 stub 인터페이스를 활용하여, 향후 분산 AIoT 환경에서의 센서 데이터 수집이나 에이전트 간 제어 메시지 전송 로직을 설계해 보십시오.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 참고 자료
|
||||||
|
|
||||||
|
* [gRPC와 REST의 차이점 (AWS)](https://aws.amazon.com/ko/compare/the-difference-between-grpc-and-rest/)
|
||||||
|
|||||||
+5
-3
@@ -1,6 +1,8 @@
|
|||||||
# HTTP 프로토콜 & Gin 웹 프레임워크 학습
|
# 2단계: HTTP & Gin 웹 서버 상세 가이드
|
||||||
|
|
||||||
이 문서는 `grpccanary` 프로젝트의 **2단계: HTTP 서버 예제**에 대한 이론적 배경과 코드 구조를 설명합니다.
|
⬅ [학습 로드맵으로 돌아가기](MANUSCRIPT.md)
|
||||||
|
|
||||||
|
이 문서는 `grpccanary` 프로젝트의 **2단계: HTTP & Gin 웹 서버**에 대한 이론적 배경과 코드 구조를 설명합니다.
|
||||||
|
|
||||||
HTTP(Hypertext Transfer Protocol)는 클라이언트와 웹 서버가 웹에서 리소스를 교환하기 위해 정의한 규약입니다. Go 언어에서는 전통적인 `net/http` 표준 라이브러리 외에도, 성능이 우수하고 사용하기 쉬운 서드파티 웹 프레임워크인 **Gin-gonic**을 주로 사용하여 RESTful API를 신속하게 설계합니다.
|
HTTP(Hypertext Transfer Protocol)는 클라이언트와 웹 서버가 웹에서 리소스를 교환하기 위해 정의한 규약입니다. Go 언어에서는 전통적인 `net/http` 표준 라이브러리 외에도, 성능이 우수하고 사용하기 쉬운 서드파티 웹 프레임워크인 **Gin-gonic**을 주로 사용하여 RESTful API를 신속하게 설계합니다.
|
||||||
|
|
||||||
@@ -27,7 +29,7 @@ Go 진영에서 대표적으로 사랑받는 웹 프레임워크 중 하나로,
|
|||||||
|
|
||||||
## 3. 실습 코드 분석 (`examples/httpentity/server.go`)
|
## 3. 실습 코드 분석 (`examples/httpentity/server.go`)
|
||||||
|
|
||||||
저장소의 [server.go](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/examples/httpentity/server.go) 파일에는 Gin 라우터를 구성하고 API 서버와 정적 웹 서빙을 혼합하여 라우팅을 우회 처리하는 설계 패턴이 주석 상태로 존재합니다.
|
저장소의 [server.go](../examples/httpentity/server.go) 파일에는 Gin 라우터를 구성하고 API 서버와 정적 웹 서빙을 혼합하여 라우팅을 우회 처리하는 설계 패턴이 주석 상태로 존재합니다.
|
||||||
|
|
||||||
### 3.1 라우터 엔진 분리 및 통합 핸들링
|
### 3.1 라우터 엔진 분리 및 통합 핸들링
|
||||||
```go
|
```go
|
||||||
|
|||||||
+4
-2
@@ -1,4 +1,6 @@
|
|||||||
# Go 언어와 JSON 다루기
|
# 1단계: JSON 데이터 다루기 상세 가이드
|
||||||
|
|
||||||
|
⬅ [학습 로드맵으로 돌아가기](MANUSCRIPT.md)
|
||||||
|
|
||||||
이 문서는 `grpccanary` 프로젝트의 **1단계: JSON 데이터 다루기**에 대한 이론적 배경과 코드 예시를 설명합니다.
|
이 문서는 `grpccanary` 프로젝트의 **1단계: JSON 데이터 다루기**에 대한 이론적 배경과 코드 예시를 설명합니다.
|
||||||
|
|
||||||
@@ -21,7 +23,7 @@ Go 구조체로 JSON 데이터를 다룰 때 핵심적인 과정입니다.
|
|||||||
|
|
||||||
## 2. 실습 코드 분석 (`examples/jsonexample/json_parser.go`)
|
## 2. 실습 코드 분석 (`examples/jsonexample/json_parser.go`)
|
||||||
|
|
||||||
저장소의 [json_parser.go](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/examples/jsonexample/json_parser.go) 파일은 (1) `map[string]interface{}`와의 직렬화 및 (2) 구조체(`Person`)를 이용한 매핑 방식을 모두 다룹니다.
|
저장소의 [json_parser.go](../examples/jsonexample/json_parser.go) 파일은 (1) `map[string]interface{}`와의 직렬화 및 (2) 구조체(`Person`)를 이용한 매핑 방식을 모두 다룹니다.
|
||||||
|
|
||||||
### 2.1 구조체 태그(Struct Tag) 정의
|
### 2.1 구조체 태그(Struct Tag) 정의
|
||||||
```go
|
```go
|
||||||
|
|||||||
+6
-6
@@ -10,9 +10,9 @@
|
|||||||
|
|
||||||
| 단계 | 실습 주제 | 심화 학습 가이드 링크 |
|
| 단계 | 실습 주제 | 심화 학습 가이드 링크 |
|
||||||
| :--- | :--- | :--- |
|
| :--- | :--- | :--- |
|
||||||
| **1단계** | JSON 데이터 직렬화 및 구조체 매핑 | [JSON 상세 가이드 (JSON.md)](JSON.md) |
|
| **1단계** | JSON 데이터 다루기 | [JSON 상세 가이드 (JSON.md)](JSON.md) |
|
||||||
| **2단계** | HTTP 프로토콜 및 Gin 웹 서버 라우팅 | [HTTP 상세 가이드 (HTTP.md)](HTTP.md) |
|
| **2단계** | HTTP & Gin 웹 서버 | [HTTP 상세 가이드 (HTTP.md)](HTTP.md) |
|
||||||
| **3단계** | gRPC 통신, Protobuf 컴파일 및 서버/클라이언트 구현 | [gRPC 상세 가이드 (GRPC.md)](GRPC.md) |
|
| **3단계** | gRPC 통신 구현 | [gRPC 상세 가이드 (GRPC.md)](GRPC.md) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
JSON(JavaScript Object Notation)은 데이터를 구조화하여 전송하기 위해 널리 사용되는 가볍고 읽기 쉬운 텍스트 기반의 데이터 포맷입니다. 대부분의 현대 프로그래밍 언어에서 기본적으로 지원하며, 특히 HTTP 기반 REST API의 데이터 교환 규격으로 오랫동안 사랑받아 왔습니다.
|
JSON(JavaScript Object Notation)은 데이터를 구조화하여 전송하기 위해 널리 사용되는 가볍고 읽기 쉬운 텍스트 기반의 데이터 포맷입니다. 대부분의 현대 프로그래밍 언어에서 기본적으로 지원하며, 특히 HTTP 기반 REST API의 데이터 교환 규격으로 오랫동안 사랑받아 왔습니다.
|
||||||
|
|
||||||
👉 **[Go 언어와 JSON 다루기 상세 학습 가이드 (JSON.md)](JSON.md)**
|
👉 **[1단계: JSON 데이터 다루기 상세 가이드 (JSON.md)](JSON.md)**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ HTTP(Hypertext Transfer Protocol)는 웹 브라우저와 웹 서버 간에 데
|
|||||||
### `gin-gonic`을 이용한 http 서버 구현하기
|
### `gin-gonic`을 이용한 http 서버 구현하기
|
||||||
Go 언어에서는 전통적인 `net/http` 표준 라이브러리 외에도, 성능이 뛰어나고 라우팅 기능이 강력한 `gin-gonic/gin` 프레임워크를 널리 활용하여 RESTful 웹 API 서버를 구축합니다.
|
Go 언어에서는 전통적인 `net/http` 표준 라이브러리 외에도, 성능이 뛰어나고 라우팅 기능이 강력한 `gin-gonic/gin` 프레임워크를 널리 활용하여 RESTful 웹 API 서버를 구축합니다.
|
||||||
|
|
||||||
👉 **[HTTP 프로토콜 & Gin 웹 프레임워크 상세 학습 가이드 (HTTP.md)](HTTP.md)**
|
👉 **[2단계: HTTP & Gin 웹 서버 상세 가이드 (HTTP.md)](HTTP.md)**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -56,4 +56,4 @@ REST와 JSON은 단순하고 사람이 읽기 쉽다는 훌륭한 장점이 있
|
|||||||
* **바이너리 프로토콜**: 텍스트가 아닌 이진 데이터 형식을 사용하여 통신 속도가 JSON 방식에 비해 훨씬 빠르고 가볍습니다.
|
* **바이너리 프로토콜**: 텍스트가 아닌 이진 데이터 형식을 사용하여 통신 속도가 JSON 방식에 비해 훨씬 빠르고 가볍습니다.
|
||||||
* **HTTP/2 기반**: 하나의 커넥션을 다중화(Multiplexing)하여 사용하므로 네트워크 리소스 효율성이 매우 높습니다.
|
* **HTTP/2 기반**: 하나의 커넥션을 다중화(Multiplexing)하여 사용하므로 네트워크 리소스 효율성이 매우 높습니다.
|
||||||
|
|
||||||
👉 **[gRPC & Protocol Buffers 상세 학습 가이드 (GRPC.md)](GRPC.md)**
|
👉 **[3단계: gRPC 통신 구현 상세 가이드 (GRPC.md)](GRPC.md)**
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
# Go 언어와 JSON 다루기
|
|
||||||
|
|
||||||
Go 언어에서 JSON 데이터를 다루는 방법을 배워봅시다.
|
|
||||||
|
|
||||||
Go 표준 라이브러리에는 JSON 데이터를 처리하기 위한 `encoding/json` 패키지가 포함되어 있습니다. Go는 구조체 태그(Struct Tag)를 사용하여 Go 구조체와 JSON 필드를 손쉽게 매핑할 수 있는 기능을 제공합니다. 이 태그는 Go 구조체를 JSON으로 변환하거나, JSON을 Go 구조체로 변환하는 과정을 제어합니다. 이 과정을 각각 Marshaling과 Unmarshaling이라고 부릅니다.
|
|
||||||
|
|
||||||
## `Marshal()`과 `Unmarshal()` 이해하기
|
|
||||||
|
|
||||||
**Marshaling**과 **Unmarshaling**은 Go 구조체로 JSON 데이터를 다룰 때 핵심적인 과정입니다.
|
|
||||||
|
|
||||||
- **Marshaling**: Go 구조체(메모리 상의 데이터)를 JSON 문자열(텍스트 데이터)로 변환하는 과정입니다. 주로 API 응답으로 JSON을 보내거나, 데이터를 파일로 저장할 때 사용됩니다.
|
|
||||||
- **Unmarshaling**: JSON 문자열을 Go 구조체로 변환하는 과정입니다. 주로 외부 API로부터 받은 JSON 데이터를 다루거나 파일에서 데이터를 읽어올 때 사용됩니다.
|
|
||||||
|
|
||||||
> **가장 흔히 겪는 문제**: JSON과 Go 구조체 간 변환 시 가장 흔한 버그는 구조체의 필드명을 소문자로 시작하여 발생하는 문제입니다. `encoding/json` 패키지가 구조체의 필드에 접근하려면, 해당 필드는 **반드시 대문자로 시작해야 합니다 (Exported field)**. Marshaling 또는 Unmarshaling이 제대로 동작하지 않는다면, 가장 먼저 구조체 필드명이 대문자로 시작하는지 확인해 보세요.
|
|
||||||
|
|
||||||
# 코딩 예제
|
|
||||||
|
|
||||||
아래 `encodeDecode.go` 코드는 간단한 예제를 통해 JSON 레코드의 Marshaling과 Unmarshaling 과정을 보여줍니다.
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// UseAll 구조체는 JSON 데이터와 매핑됩니다.
|
|
||||||
type UseAll struct {
|
|
||||||
Name string `json:"username"`
|
|
||||||
Surname string `json:"surname"`
|
|
||||||
Year int `json:"created"`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
구조체 필드 옆의 `` `json:"..."` `` 부분을 **구조체 태그**라고 부릅니다. 이 태그는 각 필드가 JSON 데이터에서 어떤 키(key)와 매핑되는지를 명시합니다.
|
|
||||||
- `Name` 필드는 JSON에서 `username` 키와 매핑됩니다.
|
|
||||||
- `Surname` 필드는 `surname` 키와 매핑됩니다.
|
|
||||||
- `Year` 필드는 `created` 키와 매핑됩니다.
|
|
||||||
|
|
||||||
이 태그 정보는 Marshaling과 Unmarshaling 과정에서 사용되며, 이 외에는 `UseAll`을 일반적인 Go 구조체처럼 사용하면 됩니다.
|
|
||||||
|
|
||||||
```go
|
|
||||||
func main() {
|
|
||||||
// Marshaling할 구조체 인스턴스 생성
|
|
||||||
useall := UseAll{Name: "Mike", Surname: "Tsoukalos", Year: 2021}
|
|
||||||
|
|
||||||
// Go 구조체를 JSON 바이트 슬라이스로 Marshaling합니다.
|
|
||||||
t, err := json.Marshal(&useall)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`json.Marshal()` 함수는 Go 데이터(주로 구조체 포인터)를 인자로 받아, JSON으로 인코딩된 `[]byte`와 `error`를 반환합니다.
|
|
||||||
|
|
||||||
```go
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
} else {
|
|
||||||
// t는 []byte 타입이므로, 출력을 위해 문자열로 변환합니다.
|
|
||||||
fmt.Printf("Value %s\n", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshaling할 JSON 문자열 데이터
|
|
||||||
str := `{"username": "M.", "surname": "Ts", "created":2020}`
|
|
||||||
```
|
|
||||||
|
|
||||||
JSON 데이터는 보통 문자열 형태로 다루어집니다.
|
|
||||||
|
|
||||||
```go
|
|
||||||
// json.Unmarshal 함수는 바이트 슬라이스를 인자로 받으므로, 문자열을 변환합니다.
|
|
||||||
jsonRecord := []byte(str)
|
|
||||||
```
|
|
||||||
|
|
||||||
`json.Unmarshal()` 함수는 바이트 슬라이스 (`[]byte`)를 인자로 받기 때문에, 먼저 JSON 문자열을 `[]byte` 타입으로 변환해야 합니다.
|
|
||||||
|
|
||||||
```go
|
|
||||||
// 변환된 JSON 데이터를 담을 구조체 변수를 선언합니다.
|
|
||||||
var temp UseAll
|
|
||||||
// JSON 바이트 슬라이스를 Go 구조체로 Unmarshaling합니다.
|
|
||||||
err = json.Unmarshal(jsonRecord, &temp)
|
|
||||||
```
|
|
||||||
|
|
||||||
`json.Unmarshal()` 함수는 JSON 데이터가 담긴 바이트 슬라이스와, 데이터를 채워 넣을 Go 구조체 변수의 **포인터**를 인자로 받습니다. 포인터를 사용하는 이유는 함수가 `temp` 변수의 값을 직접 수정해야 하기 때문입니다. 함수가 종료된 후에도 변경된 값이 유지되려면 이처럼 변수의 메모리 주소를 전달해야 합니다.
|
|
||||||
|
|
||||||
```go
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Data type: %T with value %v\n", temp, temp)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`encodeDecode.go`를 실행하면 다음과 같은 결과가 출력됩니다.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# go run encodeDecode.go
|
|
||||||
Value {"username":"Mike","surname":"Tsoukalos","created":2021}
|
|
||||||
Data type: main.UseAll with value {M. Ts 2020}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 전체 예제 코드
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UseAll struct {
|
|
||||||
Name string `json:"username"`
|
|
||||||
Surname string `json:"surname"`
|
|
||||||
Year int `json:"created"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
useall := UseAll{Name: "Mike", Surname: "Tsoukalos", Year: 2021}
|
|
||||||
|
|
||||||
// Marshaling: Go 구조체 -> JSON
|
|
||||||
t, err := json.Marshal(&useall)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Value %s\n", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshaling할 JSON 문자열
|
|
||||||
str := `{"username": "M.", "surname": "Ts", "created":2020}`
|
|
||||||
jsonRecord := []byte(str)
|
|
||||||
|
|
||||||
// 결과를 저장할 구조체 변수
|
|
||||||
var temp UseAll
|
|
||||||
// Unmarshaling: JSON -> Go 구조체
|
|
||||||
err = json.Unmarshal(jsonRecord, &temp)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Data type: %T with value %v\n", temp, temp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
# Working with JSON
|
|
||||||
|
|
||||||
Let’s learn how to work with JSON data.
|
|
||||||
|
|
||||||
The Go standard library includes `encoding/json`, which is for working with JSON data. Additionally, Go allows us to add support for JSON fields in Go structures using tags. Tags control the encoding and decoding of JSON records to and from Go structures. But first, we should talk about marshaling and unmarshaling JSON records.
|
|
||||||
|
|
||||||
## Using `Marshal()` and `Unmarshal()`
|
|
||||||
|
|
||||||
Both the marshaling and unmarshaling of JSON data are important procedures for working with JSON data using Go structures. **Marshaling** is the process of converting a Go structure into a JSON record. We usually want that for transferring JSON data via computer networks or for saving it on disk. **Unmarshaling** is the process of converting a JSON record given as a byte slice into a Go structure. We usually want that when receiving JSON data via computer networks or when loading JSON data from disk files.
|
|
||||||
|
|
||||||
> **Note:** The number one bug when converting JSON records into Go structures and vice versa is not making the required fields of our Go structures exported. When we have issues with marshaling and unmarshaling, begin our debugging process from there.
|
|
||||||
|
|
||||||
# Coding example
|
|
||||||
|
|
||||||
The code in `encodeDecode.go` illustrates both the marshaling and unmarshaling of JSON records using hardcoded data for simplicity:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UseAll struct {
|
|
||||||
Name string `json:"username"`
|
|
||||||
Surname string `json:"surname"`
|
|
||||||
Year int `json:"created"`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
What the previous metadata tells us is that the `Name` field of the `UseAll` structure is translated to `username` in the JSON record, and vice versa; the `Surname` field is translated to `surname`, and vice versa; and the `Year` structure field is translated to `created` in the JSON record, and vice versa. This information has to do with the marshaling and unmarshaling of JSON data. Other than this, we treat and use `UseAll` as a regular Go structure.
|
|
||||||
|
|
||||||
```go
|
|
||||||
func main() {
|
|
||||||
useall := UseAll{Name: "Mike", Surname: "Tsoukalos", Year: 2021}
|
|
||||||
|
|
||||||
// Regular Structure
|
|
||||||
// Encoding JSON data -> Convert Go Structure to JSON record with fields
|
|
||||||
t, err := json.Marshal(&useall)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `json.Marshal()` function requires a pointer to a structure variable—its real data type is an empty interface variable—and returns a byte slice with the encoded information and an `error` variable.
|
|
||||||
|
|
||||||
```go
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Value %s\n", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decoding JSON data given as a string
|
|
||||||
str := `{"username": "M.", "surname": "Ts", "created":2020}`
|
|
||||||
```
|
|
||||||
|
|
||||||
JSON data usually comes as a string.
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Convert string into a byte slice
|
|
||||||
jsonRecord := []byte(str)
|
|
||||||
```
|
|
||||||
|
|
||||||
However, as `json.Unmarshal()` requires a byte slice, we need to convert that string into a byte slice before passing it to `json.Unmarshal()`.
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Create a structure variable to store the result
|
|
||||||
temp := UseAll{}
|
|
||||||
err = json.Unmarshal(jsonRecord, &temp)
|
|
||||||
```
|
|
||||||
|
|
||||||
The `json.Unmarshal()` function requires the byte slice with the JSON record and a pointer to the Go structure variable that is going to store the JSON record and returns an `error` variable.
|
|
||||||
|
|
||||||
```go
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Data type: %T with value %v\n", temp, temp)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Running `encodeDecode.go` produces the next output:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# go run encodeDecode.go
|
|
||||||
Value {"username":"Mike","surname":"Tsoukalos","created":2021}
|
|
||||||
Data type: main.UseAll with value {M. Ts 2020}
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UseAll struct {
|
|
||||||
Name string `json:"username"`
|
|
||||||
Surname string `json:"surname"`
|
|
||||||
Year int `json:"created"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
useall := UseAll{Name: "Mike", Surname: "Tsoukalos", Year: 2021}
|
|
||||||
|
|
||||||
// Regular Structure
|
|
||||||
// Encoding JSON data -> Convert Go Structure to JSON record with fields
|
|
||||||
t, err := json.Marshal(&useall)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Value %s\n", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decoding JSON data given as a string
|
|
||||||
str := `{"username": "M.", "surname": "Ts", "created":2020}`
|
|
||||||
// Convert string into a byte slice
|
|
||||||
jsonRecord := []byte(str)
|
|
||||||
// Create a structure variable to store the result
|
|
||||||
temp := UseAll{}
|
|
||||||
err = json.Unmarshal(jsonRecord, &temp)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Data type: %T with value %v\n", temp, temp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# [보고서] MAM 위임 도구의 역할(Role) 지정 옵션 누락 이슈 분석
|
||||||
|
|
||||||
|
본 문서는 멀티 에이전트 오케스트레이션 프레임워크(`multi-agent-mux`)의 핵심 CLI 도구인 `multi-agent-mux-delegate-job`에서 세션의 역할(Role)을 지정할 수 있는 옵션이 누락되어 발생하는 정합성 충돌 문제와 이에 대한 원인 분석 및 해결 방안을 정의합니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 문제가 발생한 정확한 상황 (Context)
|
||||||
|
|
||||||
|
프로젝트 개발을 오케스트레이션하는 과정에서 아래와 같은 에이전트 간 역할 분담을 적용하고자 했습니다.
|
||||||
|
* **개발 팀장 (Antigravity)**: 실제 저장소의 문서 수정 및 구현 진행 (**Worker/Implementer**)
|
||||||
|
* **리뷰 에이전트 (Claude)**: 문서 구조의 설계 및 계획안 수립 (**Planner**)
|
||||||
|
|
||||||
|
이 분담에 따라 Claude 세션(`canary-projects-grpccanary-creator-claude`)에 "문서 모듈화 계획 및 체크리스트 작성" 작업을 위임하기 위해 `multi-agent-mux-delegate-job` 도구로 비동기 작업을 요청했습니다.
|
||||||
|
|
||||||
|
그러나 자동 생성된 잡 지시서인 `.mam/jobs/<job_id>/brief.md` 파일의 메타데이터에 다음과 같이 **구현자의 역할이 `Worker`로 강제 지정**되어 나가는 상황이 발생했습니다:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# 📋 Brief: Job ed31b5fb Delegation
|
||||||
|
|
||||||
|
- **Job ID**: ed31b5fb
|
||||||
|
- **Target Agent**: claude (session: tmux:canary-projects-grpccanary-creator-claude)
|
||||||
|
- **Role**: Worker <-- [이슈 발생 지점: Planner가 아닌 Worker로 강제 지정됨]
|
||||||
|
- **Timeout**: 3600 s (Idle: 120 s)
|
||||||
|
```
|
||||||
|
|
||||||
|
이는 프로젝트 협업 규칙(`.agents/MULTI_AGENT_RULES.ko.md`)에 명시된 **"에이전트 역할 범위 준수 원칙(Role Suitability Check)"**에 위배되며, `claude`가 문서 작성이 아닌 파일 직접 수정을 시도할 위험이 있는 정합성 모순을 유발합니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 문제 사유 (Root Cause)
|
||||||
|
|
||||||
|
이 문제의 근본적인 기술적 원인은 **CLI 인수 파싱 로직 및 지시서(Brief) 생성 템플릿의 하드코딩**에 있습니다.
|
||||||
|
|
||||||
|
1. **CLI 옵션 설계 누락**:
|
||||||
|
* `multi-agent-mux-delegate-job submit` 명령어의 헬프 스펙을 확인한 결과, `--agent`, `--agent-session`, `--prompt` 등의 인수는 정의되어 있으나, 작업의 논리적 성격을 조율하는 **`--role <role_name>` 파라미터가 구현되어 있지 않습니다**.
|
||||||
|
2. **템플릿 내부의 상수 고정**:
|
||||||
|
* API를 통해 비동기 잡이 수임될 때 생성되는 `brief.md` 파일과 잡 레지스트리 JSON의 생성기 로직 내부에 `Role` 값이 **`Worker` 문자열 상수로 하드코딩**되어 동작하고 있습니다. 이로 인해 어떤 에이전트에 어떤 종류의 명령을 위임하더라도 메타데이터상으로는 항상 `Worker`로 바인딩됩니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 문제 해결 방법 (Remediation & Workarounds)
|
||||||
|
|
||||||
|
### 3.1 단기적 우회 방법 (Workaround)
|
||||||
|
프레임워크 CLI 소스코드를 수정하기 어려운 제한적 상황에서는 **프롬프트 페이로드(Prompt Payload) 하드닝** 기법을 사용하여 에이전트의 오작동을 차단합니다.
|
||||||
|
* **해결 원리**: brief.md의 메타데이터상 `Role: Worker` 지정을 덮어쓸 수 있도록, 프롬프트 문맥 내부에 **"너의 역할은 실제 문서를 수정하지 않고 계획만 수립하는 Planner이다. 절대 문서를 직접 수정하지 말라"**는 강력한 지시 제약(System-level Rule Override)을 포함하여 송신합니다.
|
||||||
|
* **효과**: AI 에이전트는 메타데이터보다 프롬프트 지시어의 행위 제약을 우선 순위로 받아들이므로, 의도한 대로 설계서 및 계획안만 수립하는 Planner 동작을 정상 수행하게 됩니다.
|
||||||
|
|
||||||
|
### 3.2 근본적인 해결 방법 (Remediation)
|
||||||
|
프레임워크의 CLI 래퍼인 `multi-agent-mux-delegate-job` 파일의 파싱 로직 및 brief.md 빌더 로직을 다음과 같이 수정합니다.
|
||||||
|
|
||||||
|
#### 1단계: CLI 인수 파서 수정 (`submit` 옵션 추가)
|
||||||
|
스크립트의 인수 파싱 영역에 `--role` 파라미터를 식별할 수 있는 변수 및 분기 로직을 선언합니다.
|
||||||
|
```bash
|
||||||
|
# 옵션 분석 루프 예시
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
--role)
|
||||||
|
DELEGATE_ROLE="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
# ... 기존 옵션 파싱 ...
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# 기본값 정의
|
||||||
|
DELEGATE_ROLE="${DELEGATE_ROLE:-Worker}"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2단계: `brief.md` 생성 템플릿 연동
|
||||||
|
잡 디렉토리 내에 `brief.md`를 기입하여 내보내는 빌더 영역(Python 혹은 쉘 스크립트 에코 영역)을 다음과 같이 동적 변수와 연결합니다.
|
||||||
|
```diff
|
||||||
|
- echo "- **Role**: Worker" >> "$BRIEF_PATH"
|
||||||
|
+ echo "- **Role**: ${DELEGATE_ROLE}" >> "$BRIEF_PATH"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3단계: 잡 레지스트리 JSON 메타데이터 갱신
|
||||||
|
동일하게 생성되는 `.mam/jobs/<job_id>.json` 파일 등의 메타데이터 생성 객체 내에 `role: DELEGATE_ROLE` 매핑 키를 추가하여, 타 모니터링 도구(예: `reconcile.sh` 및 `status.sh`)에서도 해당 에이전트의 잡 실행 역할을 정확하게 대시보드에 모니터링할 수 있도록 보완합니다.
|
||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
## 📖 실습 상세 분석 및 가이드 안내
|
## 📖 실습 상세 분석 및 가이드 안내
|
||||||
|
|
||||||
학습의 일관성을 위해, 이 실습의 상세 분석 및 개념 명세는 통합 교재 문서인 **[docs/MANUSCRIPT.md](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/docs/MANUSCRIPT.md)**로 통합되었습니다.
|
학습의 일관성을 위해, 이 실습의 상세 분석 및 개념 명세는 통합 교재의 gRPC 심화 가이드인 **[docs/GRPC.md](../../docs/GRPC.md)**로 모듈화되어 있습니다. 전체 학습 로드맵은 [docs/MANUSCRIPT.md](../../docs/MANUSCRIPT.md)를 참고하십시오.
|
||||||
|
|
||||||
[docs/MANUSCRIPT.md](file:///home/godopu16/PuKi/lab/canary_projects/grpccanary/docs/MANUSCRIPT.md) 문서의 **"5. 실습: gRPC 기반 서비스 구현 및 컴파일"** 절에서 다음 내용을 참고하실 수 있습니다:
|
[docs/GRPC.md](../../docs/GRPC.md) 문서에서 다음 내용을 참고하실 수 있습니다:
|
||||||
* **IDL (`protoapi.proto`) 명세 및 필드 분석**
|
* **IDL (`protoapi.proto`) 명세 및 필드 분석**
|
||||||
* **Go에서의 `protoc` 설치 및 Stub 파일 컴파일 방법**
|
* **Go에서의 `protoc` 설치 및 Stub 파일 컴파일 방법**
|
||||||
* **gRPC 서버 코드 ([server.go](./server.go)) 구현 상세 분석**
|
* **gRPC 서버 코드 ([server.go](./server.go)) 구현 상세 분석**
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# examples/httpentity 실습 설명서
|
||||||
|
|
||||||
|
본 디렉토리는 Go 언어 웹 프레임워크인 Gin(`gin-gonic`)을 활용한 HTTP 웹 API 서버 실습 예제를 포함하고 있습니다.
|
||||||
|
|
||||||
|
## 📖 실습 상세 분석 및 가이드 안내
|
||||||
|
|
||||||
|
이 실습에 대한 상세한 코드 구조 설명과 Gin 라우터 설계 이론은 심화 학습 문서인 **[docs/HTTP.md](../../docs/HTTP.md)**에서 상세히 기술되어 있습니다.
|
||||||
|
|
||||||
|
[docs/HTTP.md](../../docs/HTTP.md) 문서에서 다음 내용을 공부할 수 있습니다:
|
||||||
|
* **HTTP 프로토콜 및 REST API 기본 구조**
|
||||||
|
* **Gin 웹 프레임워크의 라우터 매핑 (`gin.Default()` vs `gin.New()`)**
|
||||||
|
* **API 라우터와 정적 웹 리소스 서빙 우회 설계 패턴**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 빠른 실행 방법
|
||||||
|
|
||||||
|
이 예제는 리포지토리 루트의 `examples/main.go`를 통해 실행됩니다.
|
||||||
|
|
||||||
|
1. 리포지토리 루트의 `examples/main.go`를 엽니다.
|
||||||
|
2. `main()` 함수 내에서 `httpentity` API 호출 주석을 해제합니다. (현재 주석 상태로, 추후 구현 완성을 위한 예제 뼈대 파일입니다.)
|
||||||
|
3. 리포지토리 루트에서 다음 명령어를 실행합니다:
|
||||||
|
```bash
|
||||||
|
go run ./examples
|
||||||
|
```
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# examples/jsonexample 실습 설명서
|
||||||
|
|
||||||
|
본 디렉토리는 Go 언어 표준 라이브러리(`encoding/json`)를 활용한 JSON 데이터 직렬화 및 역직렬화 실습 예제를 포함하고 있습니다.
|
||||||
|
|
||||||
|
## 📖 실습 상세 분석 및 가이드 안내
|
||||||
|
|
||||||
|
이 실습에 대한 상세한 코드 해설과 구조체 태그 매핑 원리는 심화 학습 문서인 **[docs/JSON.md](../../docs/JSON.md)**에서 상세히 기술되어 있습니다.
|
||||||
|
|
||||||
|
[docs/JSON.md](../../docs/JSON.md) 문서에서 다음 내용을 공부할 수 있습니다:
|
||||||
|
* **Go 구조체와 JSON 필드 매핑 및 필드 노출 대소문자 규칙**
|
||||||
|
* **Go `Marshal` 및 `Unmarshal` 함수 동작 메커니즘**
|
||||||
|
* **동적 맵 구조(`map[string]interface{}`)의 직렬화 실습 분석**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 빠른 실행 방법
|
||||||
|
|
||||||
|
이 예제는 리포지토리 루트의 `examples/main.go`를 통해 실행됩니다.
|
||||||
|
|
||||||
|
1. 리포지토리 루트의 `examples/main.go`를 엽니다.
|
||||||
|
2. `main()` 함수 내에서 `jsonexample.JsonParsingExample()`의 주석을 해제합니다.
|
||||||
|
3. 리포지토리 루트에서 다음 명령어를 실행합니다:
|
||||||
|
```bash
|
||||||
|
go run ./examples
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user