go로 api를 구성하는데 최소버전으로 clean-architecture를 구성하도록 하겠다.
DB는 aws rds에서 최소버전으로 발급받았다.
teleport와 db를 연결한 후에
$ tsh proxy db ggocamping-db-v1 --port 3036 --db-user ggorockee
- handlers: fiber에서 api를 호출하면 실제로 실행되는 부분
- routes: routing에 따른 handlers를 처리
- presenter: api reposne 부분
으로 구분하여 폴더트리를 만들고 그 안에 각각에 맞게 개발을 진행하도록 한다.
DB에 저장되는 entities부터 정의하자.
pkg/entities/spot.go
package entities
import (
"gorm.io/gorm"
"time"
)
type Spot struct {
Id uint `json:"id" gorm:"primaryKey"`
Title string `json:"title"`
Author string `json:"author"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type DeleteRequest struct {
Id string `json:"id"`
}
이렇게 파일을 만들어 DB에 저장할 수 있는 파일 형태를 만들기로 했다. 여기에 나와있는 author는 나중에 로그인 했을 때 그 때 이름으로 하겠다.
pkg/spot/repository.go
package spot
import (
"camping-backend-with-go/pkg/entities"
"gorm.io/gorm"
)
type Repository interface {
CreateSpot(spot *entities.Spot) (*entities.Spot, error)
}
type repository struct {
DBConn *gorm.DB
}
func NewRepo(dbconn *gorm.DB) Repository {
return &repository{
DBConn: dbconn,
}
}
func (r *repository) CreateSpot(spot *entities.Spot) (*entities.Spot, error) {
result := r.DBConn.Create(spot)
if result.Error != nil {
return nil, result.Error
}
return spot, nil
}
- NewRepo()에서 *gorm.DB를 초기화한다.
아래와 같이 서비스를 만들어주게 되면 간단하게 pkg쪽은 완료가 된다.
pkg/spot/service.go
package spot
import (
"camping-backend-with-go/pkg/entities"
)
type Service interface {
InsertSpot(spot *entities.Spot) (*entities.Spot, error)
}
type service struct {
repository Repository
}
func NewService(r Repository) Service {
return &service{
repository: r,
}
}
// InsertSpot is a service layer that helps insert Spot in SpotShop
func (s *service) InsertSpot(spot *entities.Spot) (*entities.Spot, error) {
return s.repository.CreateSpot(spot)
}
api
먼저 api respone가 어떤형태로 나타날지 아래와 같이 짜고나서
presenter/spot.go
package presenter
import (
"camping-backend-with-go/pkg/entities"
"github.com/gofiber/fiber/v2"
)
type Spot struct {
Id uint `json:"id"`
Title string `json:"title"`
Author string `json:"author"`
}
func SpotSuccessResponse(data *entities.Spot) *fiber.Map {
spot := Spot{
Id: data.Id,
Title: data.Title,
Author: data.Author,
}
return &fiber.Map{
"status": true,
"data": spot,
"error": nil,
}
}
func SpotsSuccessResponse(data *[]Spot) *fiber.Map {
return &fiber.Map{
"status": true,
"data": data,
"error": nil,
}
}
func SpotErrorResponse(err error) *fiber.Map {
return &fiber.Map{
"status": false,
"data": "",
"error": err.Error(),
}
}
api/handlers/spot_handler.go
구체적인 handler 함수를 짠다. 처음엔 뼈대만 잡을 것이기 때문에 AddSpot 함수만 넣는다.
package handlers
import (
"camping-backend-with-go/api/presenter"
"camping-backend-with-go/pkg/entities"
"camping-backend-with-go/pkg/spot"
"errors"
"github.com/gofiber/fiber/v2"
"net/http"
)
func AddSpot(service spot.Service) fiber.Handler {
return func(c *fiber.Ctx) error {
var requestBody entities.Spot
err := c.BodyParser(&requestBody)
if err != nil {
c.Status(http.StatusBadRequest)
return c.JSON(presenter.SpotErrorResponse(err))
}
if requestBody.Author == "" || requestBody.Title == "" {
c.Status(http.StatusInternalServerError)
return c.JSON(presenter.SpotErrorResponse(errors.New(
"Please specify title and author",
)))
}
result, err := service.InsertSpot(&requestBody)
if err != nil {
c.Status(http.StatusInternalServerError)
return c.JSON(presenter.SpotErrorResponse(err))
}
return c.JSON(presenter.SpotSuccessResponse(result))
}
}
이제 이 handler를 routes에 연결한다.
api/routes/spot.go
package routes
import (
"camping-backend-with-go/api/handlers"
"camping-backend-with-go/pkg/spot"
"github.com/gofiber/fiber/v2"
)
func SpotRouter(app fiber.Router, service spot.Service) {
app.Post("/spots", handlers.AddSpot(service))
}
준비가 되었으니 이제 main.go에서 실행하기만 하면된다.
package main
import (
"camping-backend-with-go/api/routes"
"camping-backend-with-go/pkg/entities"
"camping-backend-with-go/pkg/spot"
"github.com/gofiber/fiber/v2/middleware/cors"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"log"
"os"
"github.com/gofiber/fiber/v2"
)
func main() {
db := databaseConnection()
spotRepo := spot.NewRepo(db)
spotService := spot.NewService(spotRepo)
app := fiber.New()
app.Use(cors.New())
v1 := app.Group("/v1")
routes.SpotRouter(v1, spotService)
log.Fatal(app.Listen(":3000"))
}
func databaseConnection() *gorm.DB {
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
log.Fatal("Failed to connect to database. \n", err)
os.Exit(2)
}
log.Println("connected")
err = db.AutoMigrate(&entities.Spot{})
if err != nil {
log.Println(err.Error())
}
return db
}
DB Setting은 나중에 하기로하고, 지금은 sqlite로 간단하게 진행한다.
air로 실행하자.
$ air
__ _ ___
/ /\ | | | |_)
/_/--\ |_| |_| \_ v1.52.3, built with Go go1.23.0
watching .
watching api
watching api/handlers
watching api/presenter
watching api/routes
watching pkg
watching pkg/entities
watching pkg/spot
!exclude tmp
building...
running...
2024/11/24 22:47:52 connected
┌───────────────────────────────────────────────────┐
│ Fiber v2.52.5 │
│ http://127.0.0.1:3000 │
│ (bound on host 0.0.0.0 and port 3000) │
│ │
│ Handlers ............. 2 Processes ........... 1 │
│ Prefork ....... Disabled PID ............. 92077 │
└───────────────────────────────────────────────────┘
postman에서 post요청을 날려보자.
db에서 확인하면 예쁘게 들어간 것을 확인할 수 있다.
개발의 뼈대가 만들어졌으니 이제 git에 push하고 나머지 부분은 완료하면 될 것 같다.
'devops > go' 카테고리의 다른 글
go fiber에 swagger를 붙여보자. (0) | 2024.11.27 |
---|---|
Json Web Token (0) | 2024.11.26 |
Clean Architecture with go (Feat. fiber) (1) | 2024.11.25 |
API(Get List) (0) | 2024.11.24 |
go fiber를 배워보자. (30) | 2024.11.22 |