Go 웹 개발 완벽 가이드 | Gin·GORM·JWT·Middleware·성능 최적화

Go 웹 개발 완벽 가이드 | Gin·GORM·JWT·Middleware·성능 최적화

이 글의 핵심

Go로 고성능 웹 API를 구축하는 완벽 가이드입니다. Gin 프레임워크, GORM, JWT 인증, Middleware, 동시성, 성능 최적화까지 실전 예제로 정리했습니다.

실무 경험 공유: Node.js API를 Go로 재작성하면서, 응답 속도를 5배 향상시키고 메모리 사용량을 70% 줄인 경험을 공유합니다.

들어가며: “Node.js가 느려요”

실무 문제 시나리오

시나리오 1: CPU 집약적 작업이 느려요
Node.js는 싱글 스레드입니다. Go는 고루틴으로 병렬 처리합니다.

시나리오 2: 메모리를 너무 많이 써요
Node.js는 메모리 사용량이 큽니다. Go는 효율적입니다.

시나리오 3: 타입 안전성이 부족해요
JavaScript는 런타임 에러가 많습니다. Go는 컴파일 타임에 잡습니다.


1. Go란?

핵심 특징

Go (Golang)는 Google이 만든 시스템 프로그래밍 언어입니다.

주요 장점:

  • 빠른 성능: C/C++에 근접
  • 동시성: 고루틴과 채널
  • 간단한 문법: 배우기 쉬움
  • 빠른 컴파일: 대규모 프로젝트도 빠름
  • 단일 바이너리: 배포 간편

성능 비교:

  • Node.js: 5,000 req/sec
  • Python: 2,000 req/sec
  • Go: 20,000 req/sec

2. 설치

# Windows (Chocolatey)
choco install golang

# macOS
brew install go

# 확인
go version

3. Gin 프레임워크

설치

go mod init myapp
go get -u github.com/gin-gonic/gin

기본 서버

// main.go
package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()

	r.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "Hello World",
		})
	})

	r.GET("/users/:id", func(c *gin.Context) {
		id := c.Param("id")
		c.JSON(http.StatusOK, gin.H{
			"user_id": id,
		})
	})

	r.Run(":8080")
}

4. GORM (ORM)

설치

go get -u gorm.io/gorm
go get -u gorm.io/driver/postgres

모델 정의

// models/user.go
package models

import "gorm.io/gorm"

type User struct {
	gorm.Model
	Name  string `json:"name"`
	Email string `json:"email" gorm:"uniqueIndex"`
	Age   int    `json:"age"`
}

DB 연결

// db/db.go
package db

import (
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"myapp/models"
)

var DB *gorm.DB

func Connect() {
	dsn := "host=localhost user=postgres password=secret dbname=mydb port=5432"
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("Failed to connect to database")
	}

	db.AutoMigrate(&models.User{})
	DB = db
}

5. CRUD API

// main.go
package main

import (
	"myapp/db"
	"myapp/models"
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	db.Connect()

	r := gin.Default()

	// 모든 사용자 조회
	r.GET("/users", func(c *gin.Context) {
		var users []models.User
		db.DB.Find(&users)
		c.JSON(http.StatusOK, users)
	})

	// 단일 사용자 조회
	r.GET("/users/:id", func(c *gin.Context) {
		var user models.User
		if err := db.DB.First(&user, c.Param("id")).Error; err != nil {
			c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
			return
		}
		c.JSON(http.StatusOK, user)
	})

	// 사용자 생성
	r.POST("/users", func(c *gin.Context) {
		var user models.User
		if err := c.ShouldBindJSON(&user); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		db.DB.Create(&user)
		c.JSON(http.StatusCreated, user)
	})

	// 사용자 업데이트
	r.PUT("/users/:id", func(c *gin.Context) {
		var user models.User
		if err := db.DB.First(&user, c.Param("id")).Error; err != nil {
			c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
			return
		}

		if err := c.ShouldBindJSON(&user); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		db.DB.Save(&user)
		c.JSON(http.StatusOK, user)
	})

	// 사용자 삭제
	r.DELETE("/users/:id", func(c *gin.Context) {
		if err := db.DB.Delete(&models.User{}, c.Param("id")).Error; err != nil {
			c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
			return
		}
		c.JSON(http.StatusOK, gin.H{"message": "User deleted"})
	})

	r.Run(":8080")
}

6. JWT 인증

설치

go get -u github.com/golang-jwt/jwt/v5

JWT 생성

// auth/jwt.go
package auth

import (
	"time"

	"github.com/golang-jwt/jwt/v5"
)

var jwtSecret = []byte("your-secret-key")

type Claims struct {
	UserID uint   `json:"user_id"`
	Email  string `json:"email"`
	jwt.RegisteredClaims
}

func GenerateToken(userID uint, email string) (string, error) {
	claims := Claims{
		UserID: userID,
		Email:  email,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(jwtSecret)
}

func ValidateToken(tokenString string) (*Claims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return jwtSecret, nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(*Claims); ok && token.Valid {
		return claims, nil
	}

	return nil, err
}

Middleware

// middleware/auth.go
package middleware

import (
	"myapp/auth"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
)

func AuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		authHeader := c.GetHeader("Authorization")
		if authHeader == "" {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header required"})
			c.Abort()
			return
		}

		tokenString := strings.TrimPrefix(authHeader, "Bearer ")
		claims, err := auth.ValidateToken(tokenString)
		if err != nil {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
			c.Abort()
			return
		}

		c.Set("user_id", claims.UserID)
		c.Set("email", claims.Email)
		c.Next()
	}
}

사용

r.GET("/protected", middleware.AuthMiddleware(), func(c *gin.Context) {
	userID := c.GetUint("user_id")
	c.JSON(http.StatusOK, gin.H{
		"message": "Protected route",
		"user_id": userID,
	})
})

7. 동시성

고루틴

func processUsers(users []User) {
	for _, user := range users {
		go func(u User) {
			// 비동기 처리
			sendEmail(u.Email)
		}(user)
	}
}

채널

func worker(id int, jobs <-chan int, results chan<- int) {
	for j := range jobs {
		results <- j * 2
	}
}

func main() {
	jobs := make(chan int, 100)
	results := make(chan int, 100)

	// 3개의 워커 시작
	for w := 1; w <= 3; w++ {
		go worker(w, jobs, results)
	}

	// 작업 전송
	for j := 1; j <= 9; j++ {
		jobs <- j
	}
	close(jobs)

	// 결과 수신
	for a := 1; a <= 9; a++ {
		<-results
	}
}

8. 배포

Docker

# Dockerfile
FROM golang:1.22-alpine AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN go build -o main .

FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/main .

EXPOSE 8080
CMD [./main]
docker build -t myapp .
docker run -p 8080:8080 myapp

정리 및 체크리스트

핵심 요약

  • Go: 빠르고 간단한 시스템 언어
  • Gin: 고성능 웹 프레임워크
  • GORM: 강력한 ORM
  • JWT: 인증 구현
  • 고루틴: 동시성 처리
  • 단일 바이너리: 배포 간편

구현 체크리스트

  • Go 프로젝트 생성
  • Gin 서버 구현
  • GORM으로 DB 연결
  • CRUD API 구현
  • JWT 인증 구현
  • Middleware 작성
  • Docker 배포

같이 보면 좋은 글

  • FastAPI 완벽 가이드
  • NestJS 완벽 가이드
  • Redis 고급 가이드

이 글에서 다루는 키워드

Go, Golang, Gin, GORM, Backend, REST API, Performance

자주 묻는 질문 (FAQ)

Q. Go vs Node.js, 어떤 게 나은가요?

A. Go가 훨씬 빠르고 메모리 효율적입니다. CPU 집약적 작업은 Go, I/O 집약적 작업은 둘 다 괜찮습니다.

Q. 학습 곡선이 가파른가요?

A. 문법은 간단합니다. 하지만 포인터, 고루틴 등 새로운 개념이 있습니다.

Q. 프론트엔드 개발도 가능한가요?

A. 아니요, Go는 백엔드 전용입니다. 프론트엔드는 JavaScript/TypeScript를 사용하세요.

Q. 프로덕션에서 사용해도 되나요?

A. 네, Google, Uber, Dropbox 등 많은 기업에서 사용합니다.

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3