카테고리 없음

코드 리팩토링 Serializer, DTO, CodeConvention

꼬락이 2024. 12. 9. 21:32

코드를 작성하는데 확인해보니

 

package entities

import (
	"time"
)

type Spot struct {
	Id         uint `json:"id" gorm:"primaryKey"`
	UserId     uint `json:"user_id"`
	User       User `gorm:"foreignKey:UserId;constraint:OnDelete:CASCADE;"`
	CategoryId *int `gorm:"default:null" json:"category_id"` // CategoryId가 null일 수가 있음
	// sqlite에서 SET NULL, mysql, postgresql에서는 SetNull
	// 배포시 아래 주석
	// sqlite 설정
	Category Category `gorm:"foreignKey:CategoryId;constraint:OnDelete:SET NULL;"`

	// 배포시 아래 주석해제
	// rds 설정
	//Category  Category  `gorm:"foreignKey:CategoryId;constraint:OnDelete:SetNull;"`
	Title     string    `json:"title"`
	Location  string    `json:"location"`
	Author    string    `json:"author"`
	Review    string    `json:"review"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`

	CoverImg string `json:"cover_img"`
}

type SpotSerializer interface {
	ListSerialize() SpotListOutputSchema
	DetailSerialize() SpotDetailOutputSchema
}

type spotSerializer struct {
	spot *Spot
	user UserSerializer
}

func (s *spotSerializer) ListSerialize() SpotListOutputSchema {

	return SpotListOutputSchema{
		Id:        int(s.spot.Id),
		User:      s.user.TinyUserSerialize(),
		Title:     s.spot.Title,
		Location:  s.spot.Location,
		Author:    s.spot.Author,
		CreatedAt: s.spot.CreatedAt,
		UpdatedAt: s.spot.UpdatedAt,
		Review:    s.spot.Review,
	}
}

func (s *spotSerializer) DetailSerialize() SpotDetailOutputSchema {
	return SpotDetailOutputSchema{
		Id:        int(s.spot.Id),
		User:      s.user.TinyUserSerialize(),
		Title:     s.spot.Title,
		Location:  s.spot.Location,
		Author:    s.spot.Author,
		CreatedAt: s.spot.CreatedAt,
		UpdatedAt: s.spot.UpdatedAt,
		Review:    s.spot.Review,
	}
}

func NewSpotSerializer(s *Spot, u UserSerializer) SpotSerializer {
	return &spotSerializer{spot: s, user: u}
}

// ============= input schema =============

type CreateSpotInputSchema struct {
	Title    string `json:"title"`
	Location string `json:"location"`
	Review   string `json:"review"`
}

type UpdateSpotSchema struct {
	Title    string `json:"title"`
	Location string `json:"location"`
	Review   string `json:"review"`
}

// ============= output schema =============

type SpotListOutputSchema struct {
	Id        int                  `json:"id"`
	User      TinyUserOutputSchema `json:"user"`
	Title     string               `json:"title"`
	Location  string               `json:"location"`
	Author    string               `json:"author"`
	CreatedAt time.Time            `json:"created_at"`
	UpdatedAt time.Time            `json:"updated_at"`
	Review    string               `json:"review"`
}

type SpotDetailOutputSchema struct {
	Id        int                  `json:"id"`
	User      TinyUserOutputSchema `json:"user"`
	Title     string               `json:"title"`
	Location  string               `json:"location"`
	Author    string               `json:"author"`
	CreatedAt time.Time            `json:"created_at"`
	UpdatedAt time.Time            `json:"updated_at"`
	Review    string               `json:"review"`
}

 

이런식으로 작성하다보니 점점 코드가 길어질 것 같은 느낌이 문득 든다. 실제로 API도 많이 붙이지도 않았는데 벌써부터 이런 거는 앞으로도 문제가 될 것 으로 보인다. 더 커지기 전에 손보자.

 

먼저 파일을 분리하자.

위의 코드를 살펴보면

1. 크게 gorm으로 Database에 입력하는 부분

2. Interface에 input으로 사용되는부분

3. serializer형태로 output을 뽑아내는 부분

 

이렇게 크게 3부분이 있기 때문에 gorm에 입력되는 부분은 그대로 두고

input 으로 사용되는 부분과 serializer부분의 파일을 분리해서 관리하도록 하겠다.

 

dto/spot.go

package dto

import "time"

// ============= input schema =============

type CreateSpotIn struct {
	Title    string `json:"title"`
	Location string `json:"location"`
	Review   string `json:"review"`
}

type UpdateSpotIn struct {
	Title    string `json:"title"`
	Location string `json:"location"`
	Review   string `json:"review"`
}

// ============= output schema =============

type SpotListOut struct {
	Id        int         `json:"id"`
	User      TinyUserOut `json:"user"`
	Title     string      `json:"title"`
	Location  string      `json:"location"`
	Author    string      `json:"author"`
	CreatedAt time.Time   `json:"created_at"`
	UpdatedAt time.Time   `json:"updated_at"`
	Review    string      `json:"review"`
}

type SpotDetailOut struct {
	Id        int         `json:"id"`
	User      TinyUserOut `json:"user"`
	Title     string      `json:"title"`
	Location  string      `json:"location"`
	Author    string      `json:"author"`
	CreatedAt time.Time   `json:"created_at"`
	UpdatedAt time.Time   `json:"updated_at"`
	Review    string      `json:"review"`
}

 

 

serializer/spot.go

package serializer

import (
	"camping-backend-with-go/pkg/dto"
	"camping-backend-with-go/pkg/entities"
)

type SpotSerializer interface {
	ListSerialize() dto.SpotListOut
	DetailSerialize() dto.SpotDetailOut
}

type spotSerializer struct {
	spot *entities.Spot
	user UserSerializer
}

func (s *spotSerializer) ListSerialize() dto.SpotListOut {

	return dto.SpotListOut{
		Id:        int(s.spot.Id),
		User:      s.user.TinyUserSerialize(),
		Title:     s.spot.Title,
		Location:  s.spot.Location,
		Author:    s.spot.Author,
		CreatedAt: s.spot.CreatedAt,
		UpdatedAt: s.spot.UpdatedAt,
		Review:    s.spot.Review,
	}
}

func (s *spotSerializer) DetailSerialize() dto.SpotDetailOut {
	return dto.SpotDetailOut{
		Id:        int(s.spot.Id),
		User:      s.user.TinyUserSerialize(),
		Title:     s.spot.Title,
		Location:  s.spot.Location,
		Author:    s.spot.Author,
		CreatedAt: s.spot.CreatedAt,
		UpdatedAt: s.spot.UpdatedAt,
		Review:    s.spot.Review,
	}
}

func NewSpotSerializer(s *entities.Spot, u UserSerializer) SpotSerializer {
	return &spotSerializer{spot: s, user: u}
}

 

이런식으로 파일을 분리했다. 파일이름도 조금 간소화하여 변경하였고, 모쪼록 잘 변경한 것 같다.

 

생객해보니 LocalDB가 sqlite...

생각해보니 LocalDB가 sqlite이다. 물론 비용을 최소화하기 위해 spot인스턴스로 작성하긴 했지만 로컬로 개발 땡길 수 있을 때 최대한 로컬로 하려고 한다. 그런 과정에서 개발 DB는 Mysql인데 localDB가 sqlite다 보니 생각보다 버그가 많더라..

 

그래서 간단하고 빠르게 mysqlDB를 올리기로 했다.

정말 간단히...

 

local_database/docker-compose/docker-compose.yml

version: "3.8"
services:
  db:
    build:
      context: ./mysql

    env_file:
      - ".env"

    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}

    volumes:
      - "./mysql/db_data:/var/lib/mysql"

    ports:
      - "${DB_PORT}:3306"

 

local_database/docker-compose/.env

MYSQL_ROOT_PASSWORD=test1234
MYSQL_DATABASE=ggocamping
DB_PORT=3306

 

 

local_database/docker-compose/mysql/Dockerfile

FROM mysql

 

이렇게 3개의 파일을 만들고

docker-compose.yml파일이 있는 경로에서

$ docker compose up -d

를 실행하자. 그리고 나면 docker를 이용해서 Database가 설치되게 된다.

설치가 되었으니 이제 Database에 접속을 하면 되겠다.

.env에 있는 접속 정보를 이용해서 접속을 하게 되면 된다. 혹시나 아래와 같은 에러가 나오게 되면 allowPublicKeyRetrival을 true로 설정해주자.

 

 

 

 

Database가 설치되었으니 이제 Mysql을 이용해서 개발을 진행하면 되겠다.