Go에서 Content-Type 처리
HTTP 요청과 응답에서 Content-Type 헤더는 전송되는 데이터의 형식을 정의하며, 클라이언트와 서버 간 데이터 처리 방식을 결정합니다. 각 Content-Type은 특정 데이터 형식에 맞춰 처리되고 사용됩니다.
✅ 1. Content-Type: application/json
📤 요청 예시 (Request Body)
POST / HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Content-Length: 33
{
"name": "Alice",
"age": 30
}
🧑💻 net/http 코드
func jsonHandler(w http.ResponseWriter, r *http.Request) {
var data map[string]interface{}
body, _ := io.ReadAll(r.Body)
json.Unmarshal(body, &data)
fmt.Println("Received JSON:", data)
w.Write([]byte("OK"))
}
🍸 Gin 코드
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func jsonHandler(c *gin.Context) {
var user User
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
fmt.Printf("Parsed User: %+v\\n", user)
c.JSON(http.StatusOK, user)
}
🦍 gorilla/mux 코드
func jsonHandler(w http.ResponseWriter, r *http.Request) {
var data map[string]interface{}
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to read body", http.StatusBadRequest)
return
}
if err := json.Unmarshal(body, &data); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
fmt.Println("Received JSON:", data)
w.Write([]byte("OK"))
}
✅ 2. Content-Type: application/x-www-form-urlencoded
📤 요청 예시 (Request Body)
POST / HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
Content-Length: 17
name=Bob&age=25
🧑💻 net/http 코드
func formHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
name := r.FormValue("name")
age := r.FormValue("age")
fmt.Fprintf(w, "Received name=%s, age=%s", name, age)
}
🍸 Gin 코드
type FormData struct {
Name string `form:"name"`
Age int `form:"age"`
}
func formHandler(c *gin.Context) {
var data FormData
if err := c.ShouldBind(&data); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, data)
}
🦍 gorilla/mux 코드
func formHandler(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, "ParseForm error", http.StatusBadRequest)
return
}
name := r.FormValue("name")
age := r.FormValue("age")
fmt.Fprintf(w, "Received name=%s, age=%s", name, age)
}
✅ 3. Content-Type: text/plain
📤 요청 예시 (Request Body)
POST / HTTP/1.1
Host: localhost:8080
Content-Type: text/plain
Content-Length: 13
Hello, world!
🧑💻 net/http 코드
func textHandler(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
fmt.Println("Text received:", string(body))
w.Write([]byte("Text OK"))
}
🍸 Gin 코드
func textHandler(c *gin.Context) {
body, _ := io.ReadAll(c.Request.Body)
fmt.Println("Text:", string(body))
c.String(http.StatusOK, "Received: %s", string(body))
}
🦍 gorilla/mux 코드
func textHandler(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Read error", http.StatusBadRequest)
return
}
fmt.Println("Text received:", string(body))
w.Write([]byte("Text OK"))
}
✅ 4. Content-Type: multipart/form-data (파일 업로드)
📤 요청 예시 (Request Body - 간략화된 예시)
package main
import (
"bytes"
"io"
"log"
"mime/multipart"
"net/http"
"os"
)
func main() {
var requestBody bytes.Buffer
writer := multipart.NewWriter(&requestBody)
err := writer.WriteField("key1", "value1")
if err != nil {
log.Fatal(err)
}
file, err := os.Open("sample.txt") // 실제 파일 열기
if err != nil {
log.Fatal(err)
}
defer file.Close()
fileWriter, err := writer.CreateFormFile("upload", "sample.txt")
if err != nil {
log.Fatal(err)
}
_, err = io.Copy(fileWriter, file)
if err != nil {
log.Fatal(err)
}
writer.Close()
req, err := http.NewRequest("POST", "http://localhost:8080/upload", &requestBody)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
}
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="key1"
value1
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="upload"; filename="sample.txt"
Content-Type: text/plain
(sample.txt 파일 내용)
------WebKitFormBoundary7MA4YWxkTrZu0gW--
🧑💻 net/http 코드
func uploadHandler(w http.ResponseWriter, r *http.Request) {
r.ParseMultipartForm(10 << 20) // 10MB
file, handler, err := r.FormFile("upload")
if err != nil {
http.Error(w, "Error uploading file", http.StatusBadRequest)
return
}
defer file.Close()
fmt.Fprintf(w, "Uploaded File: %s (%d bytes)", handler.Filename, handler.Size)
}
🍸 Gin 코드
func uploadHandler(c *gin.Context) {
file, err := c.FormFile("upload")
if err != nil {
c.String(http.StatusBadRequest, "Upload error: %s", err)
return
}
// 저장도 가능: c.SaveUploadedFile(file, "uploads/"+file.Filename)
c.String(http.StatusOK, "Uploaded: %s (%d bytes)", file.Filename, file.Size)
}
🦍 gorilla/mux 코드
func uploadHandler(w http.ResponseWriter, r *http.Request) {
err := r.ParseMultipartForm(10 << 20)
if err != nil {
http.Error(w, "Could not parse multipart form", http.StatusBadRequest)
return
}
file, handler, err := r.FormFile("upload")
if err != nil {
http.Error(w, "Error retrieving the file", http.StatusBadRequest)
return
}
defer file.Close()
fmt.Fprintf(w, "Uploaded File: %s (%d bytes)", handler.Filename, handler.Size)
}
✅ 5. Content-Type: application/xml
📤 요청 예시 (Request Body)
POST / HTTP/1.1
Host: localhost:8080
Content-Type: application/xml
Content-Length: 46
<User>
<Name>Tom</Name>
<Age>40</Age>
</User>
🧑💻 net/http 코드
import (
"encoding/xml"
"net/http"
"io"
"fmt"
)
type User struct {
Name string `xml:"Name"`
Age int `xml:"Age"`
}
func xmlHandler(w http.ResponseWriter, r *http.Request) {
var user User
body, _ := io.ReadAll(r.Body)
if err := xml.Unmarshal(body, &user); err != nil {
http.Error(w, "XML Parse Error", http.StatusBadRequest)
return
}
fmt.Printf("Parsed User: %+v\\n", user)
w.Write([]byte("XML OK"))
}
🍸 Gin 코드
type User struct {
Name string `xml:"Name"`
Age int `xml:"Age"`
}
func xmlHandler(c *gin.Context) {
var user User
if err := c.ShouldBindXML(&user); err != nil {
c.String(http.StatusBadRequest, "XML Parse Error")
return
}
c.JSON(http.StatusOK, user)
}
🦍 gorilla/mux 코드
type User struct {
Name string `xml:"Name"`
Age int `xml:"Age"`
}
func xmlHandler(w http.ResponseWriter, r *http.Request) {
var user User
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Read error", http.StatusBadRequest)
return
}
if err := xml.Unmarshal(body, &user); err != nil {
http.Error(w, "XML Parse Error", http.StatusBadRequest)
return
}
json.NewEncoder(w).Encode(user)
}
📌 net/http에서는 XML을 직접 파싱해야 하므로 encoding/xml을 사용하여 구조체에 매핑해야 합니다.
🔄 여러 Content-Type 동시 지원 (Gin)
func smartHandler(c *gin.Context) {
contentType := c.ContentType()
switch contentType {
case "application/json":
var jsonData map[string]interface{}
if err := c.BindJSON(&jsonData); err == nil {
c.JSON(http.StatusOK, jsonData)
}
case "application/x-www-form-urlencoded":
name := c.PostForm("name")
c.String(http.StatusOK, "Form name: %s", name)
case "multipart/form-data":
file, _ := c.FormFile("upload")
c.String(http.StatusOK, "Multipart file: %s", file.Filename)
default:
c.String(http.StatusUnsupportedMediaType, "Unsupported Content-Type: %s", contentType)
}
}