Skip to content

첫 번째 앱

5분 안에 사용자 조회 API를 만들어봅니다.

완성된 모습

bash
curl "http://localhost:8080/users?id=1"
json
{
  "id": 1,
  "name": "Alice",
  "email": "alice@example.com"
}

1. 프로젝트 생성

bash
mkdir hello-spine && cd hello-spine
go mod init hello-spine
go get github.com/NARUBROWN/spine

2. 프로젝트 구조

hello-spine/
├── main.go
├── controller/
│   └── user_controller.go
├── service/
│   └── user_service.go
└── routes/
    └── routes.go

3. 코드 작성

main.go

go
package main

import (
    "hello-spine/controller"
    "hello-spine/routes"
    "hello-spine/service"

    "github.com/NARUBROWN/spine"
)

func main() {
    app := spine.New()

    // 생성자 등록 — 순서 상관없음
    app.Constructor(
        service.NewUserService,
        controller.NewUserController,
    )

    // 라우트 등록
    routes.RegisterRoutes(app)

    // 서버 시작
    app.Run(":8080")
}

service/user_service.go

go
package service

// UserResponse 응답 구조체
type UserResponse struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

// UserService 사용자 서비스
type UserService struct {
    // 실제로는 Repository를 주입받지만, 여기선 간단히 구현
}

func NewUserService() *UserService {
    return &UserService{}
}

// Get 사용자 조회 (하드코딩된 데이터)
func (s *UserService) Get(id int) (UserResponse, error) {
    // 실제로는 DB에서 조회
    users := map[int]UserResponse{
        1: {ID: 1, Name: "Alice", Email: "alice@example.com"},
        2: {ID: 2, Name: "Bob", Email: "bob@example.com"},
    }

    if user, ok := users[id]; ok {
        return user, nil
    }

    return UserResponse{}, nil
}

controller/user_controller.go

go
package controller

import (
    "context"

    "hello-spine/service"

    "github.com/NARUBROWN/spine/pkg/query"
)

type UserController struct {
    svc *service.UserService
}

// NewUserController 생성자 — 파라미터가 곧 의존성
func NewUserController(svc *service.UserService) *UserController {
    return &UserController{svc: svc}
}

// GetUser 사용자 조회 핸들러
// 함수 시그니처가 곧 API 스펙
func (c *UserController) GetUser(
    ctx context.Context,
    q query.Values,
) (service.UserResponse, error) {
    id := int(q.Int("id", 0))
    return c.svc.Get(id)
}

routes/routes.go

go
package routes

import (
    "hello-spine/controller"

    "github.com/NARUBROWN/spine"
)

func RegisterRoutes(app spine.App) {
    app.Route("GET", "/users", (*controller.UserController).GetUser)
}

4. 실행

bash
go run main.go
________       _____             
__  ___/__________(_)___________ 
_____ \___  __ \_  /__  __ \  _ \
____/ /__  /_/ /  / _  / / /  __/
/____/ _  .___//_/  /_/ /_/\___/ 
       /_/        
2026/01/19 14:37:59 [Bootstrap] Spine version: v0.2.1

5. 테스트

bash
# Alice 조회
curl "http://localhost:8080/users?id=1"
json
{"id":1,"name":"Alice","email":"alice@example.com"}
bash
# Bob 조회
curl "http://localhost:8080/users?id=2"
json
{"id":2,"name":"Bob","email":"bob@example.com"}

🎉 완성!

5분 만에 첫 번째 Spine 앱을 만들었습니다.

지금까지 배운 것

개념코드
앱 생성spine.New()
의존성 등록app.Constructor(...)
라우트 등록app.Route("GET", "/users", ...)
서버 시작app.Run(":8080")

핵심 포인트

  • 생성자 파라미터 = 의존성 선언 — 어노테이션 불필요
  • 함수 시그니처 = API 스펙 — 입출력이 명확
  • 라우트 한 곳에서 관리 — 흐름이 보임

다음 단계