언어 및 도구들 공부/Go

Go) Unmarshal과 포인터

Dr.Mlem 2025. 4. 4. 14:19
Q1.
json.Unmarshal(ctx.PostBody(), &req) 이게 어떻게 진행되는지 알려줘
 
 
 
A1.
좋아, 이 한 줄은 Go에서 JSON을 구조체로 파싱하는 핵심 라인이야.

json.Unmarshal(...)이 어떤 식으로 동작하는지, 단계별로 완전 상세하게 설명해줄게! 🔍


✅ 먼저 전체 코드 다시 보기

json.Unmarshal(ctx.PostBody(), &req)
  • json.Unmarshal(...) → Go 표준 라이브러리 encoding/json의 함수
  • ctx.PostBody() → 클라이언트가 보낸 HTTP 요청 본문 (JSON 문자열, []byte 타입)
  • &req → JSON을 여기에 파싱해서 채워줘! 라는 구조체의 주소 (포인터)

🔁 동작 원리 단계별 설명

1. ctx.PostBody()

  • 이건 fasthttp에서 요청 본문을 가져오는 함수야.
  • 클라이언트가 보낸 JSON이 들어있어. 예를 들어 이런 값이 올 수 있어:
{
  "num1": 10,
  "num2": 5
}

→ 이게 Go에선 []byte 타입으로 들어와.


2. &req

  • req는 model.Request라는 구조체 타입의 변수야:
type Request struct {
	A int `json:"num1"`
	B int `json:"num2"`
}
  • &req는 req 변수의 메모리 주소를 넘겨주는 거야.
  • 이걸 넘겨야 Unmarshal 함수가 그 안에 값을 직접 채워넣을 수 있어.

3. json.Unmarshal(요청바디, 구조체주소)

  • 이 함수는 []byte로 된 JSON을 읽어서
  • 구조체에 있는 json:"..." 태그를 참고해서
  • 대응되는 값을 자동으로 채워줌

예:

 
{
  "num1": 10,
  "num2": 5
}

→ 이게 아래처럼 채워짐:

req.A = 10
req.B = 5

4. 에러 발생 가능성

  • JSON 형식이 잘못됐거나
  • 타입이 안 맞으면 → 에러 반환

그래서 보통 이렇게 체크해:

if err := json.Unmarshal(...); err != nil {
	// 잘못된 JSON!
}

✅ 흐름 요약 그림

클라이언트 JSON 요청
        ↓
 ctx.PostBody()        →   []byte JSON
        ↓
json.Unmarshal(..., &req)
        ↓
 req.A ← num1
 req.B ← num2

🧠 최종 요약

요소 설명
ctx.PostBody() 클라이언트가 보낸 JSON을 byte로 가져옴
&req JSON을 파싱해 채워 넣을 구조체 주소
json.Unmarshal() JSON을 Go 구조체에 자동으로 매핑
태그(json:"num1") JSON 키와 Go 필드를 연결하는 역할

필요하면 json.Marshal()도 같이 알려줄 수 있어. 그건 Go 구조체 → JSON 문자열로 바꾸는 거야!

 
Q2.
marshal(), unmarshal 함수는 무조건 채워질 데이터 구조체를 포인터 변수로 받는거야?
 
 
 
A2.
아주 좋은 질문이야!

바로 핵심만 말하자면:


✅ json.Unmarshal() → 반드시 포인터 필요!

왜냐하면:

Unmarshal은 값을 구조체에 "채워 넣는" 함수이기 때문이야.


🔍 예를 들어

var data MyStruct
err := json.Unmarshal(jsonBytes, &data) // ✅ 반드시 포인터
  • 여기서 &data를 넘기는 이유는: → 함수 안에서 JSON 값을 data의 필드에 직접 수정해서 넣어야 하니까
  • 만약 그냥 data만 넘기면? → 값을 수정할 수 없기 때문에 Unmarshal은 작동하지 않음

📛 에러:

json: Unmarshal(non-pointer MyStruct)

✅ 반대로 json.Marshal() → 포인터든 아니든 상관 없음

왜냐하면:

Marshal은 구조체를 읽어서 JSON 문자열을 만드는 함수이기 때문이야.
값을 읽기만 하지, 바꾸지 않거든.

🔍 예시

data := MyStruct{A: 1, B: 2}

jsonBytes1, _ := json.Marshal(data)   // ✅ OK
jsonBytes2, _ := json.Marshal(&data)  // ✅ 이것도 OK

둘 다 잘 작동해. 왜냐하면 읽기만 하니까.


📦 요약 정리

함수 포인터 필요 여부 이유
Unmarshal() 반드시 포인터 값을 채워 넣어야 하니까
Marshal() ❌ 선택 가능 값을 읽기만 하니까

💡 비유하자면:

  • Unmarshal()은 “빈 컵에 음료를 따라주는 것” → 컵(구조체)에 직접 접근하려면 포인터가 필요해
  • Marshal()은 “컵에 뭐 들었는지 보고 사진 찍는 것” → 그냥 값만 있으면 됨

필요하면 slice/map도 포인터가 필요한지 여부까지 정리해줄게! 😄

 
 
 
Q3.

if err := json.Unmarshal(ctx.PostBody(), &req); err != nil { 결국 여기에서 unmarshal의 파라미터 2개는, ctx.Postbody()를 통해서 반환되는 값을 req의 구조체 값으로 "넣어버리겠다"는 말인건가?

 

 

 

A3.

정확해! 🔥

너가 말한 이 해석이 아주 잘 잡혀 있어.


✅ 코드 다시 보기

if err := json.Unmarshal(ctx.PostBody(), &req); err != nil {
	// 에러 처리
}

🔍 이 코드의 의도와 동작 요약

📌 “ctx.PostBody()로 받은 JSON 데이터를 → req라는 구조체 안에 ‘집어넣겠다’”는 뜻이야.

구성요소 설명:

  1. ctx.PostBody()
    → 클라이언트가 HTTP 요청으로 보낸 본문 (body)
    → 일반적으로 JSON 형식의 []byte 데이터가 들어있어
  2. &req
    → model.Request 타입의 구조체 주소 (포인터)
    → 이 안에 값을 직접 채워 넣기 위해 주소를 전달하는 거야
  3. json.Unmarshal(...)
    → 첫 번째 인자(바이트)로부터 JSON을 읽고
    → 두 번째 인자(포인터)의 구조체에 필드 이름이 맞는 곳에 값을 채워넣음

📦 예시 흐름

클라이언트가 이런 JSON을 보냈다고 해보자:

{
  "num1": 7,
  "num2": 3
}

이게 ctx.PostBody()로 들어오고,

req := model.Request{} 가 있고,

구조체가 이렇게 생겼다면:

type Request struct {
	A int `json:"num1"`
	B int `json:"num2"`
}

→ 그럼 json.Unmarshal(...)은 req.A = 7, req.B = 3으로 값을 채워줘.


🧠 최종 요약

요소 의미
ctx.PostBody() 요청 본문의 JSON 데이터를 가져옴
&req JSON 데이터를 채워넣을 구조체 주소
json.Unmarshal() JSON → Go 구조체로 값 복사
네가 말한 표현 ✅ 완벽해! “req에 값 넣어버리겠다”는 게 정확한 요약이야

필요하면 구조체 태그(json:"...")가 어떻게 매핑되는지도 더 자세히 풀어줄게!

'언어 및 도구들 공부 > Go' 카테고리의 다른 글

Go) go.mod, go.sum  (0) 2025.04.04
빈 식별자, blank identifier, _  (0) 2025.04.04
Go 포인터 공부 4 - 구조체 포인터로 접근하기  (0) 2025.04.03
Go 포인터 3  (0) 2025.03.29
Go 포인터 공부 2  (0) 2025.03.29