체인의정석

Go Gin으로 API 만들기: POST/GET 기본 구조 + Swagger 작성법 본문

개발/backend(go)

Go Gin으로 API 만들기: POST/GET 기본 구조 + Swagger 작성법

체인의정석 2025. 4. 25. 13:58
728x90

1. POST 요청 (생성)

기본 흐름

  • 클라이언트가 JSON 데이터 보냄
  • 서버가 JSON 파싱해서 구조체에 매핑
  • 데이터를 저장하거나 처리
  • 성공하면 200 OK, 실패하면 에러 응답
// CreateExample
// @Summary Example 데이터 생성
// @Description 새로운 Example 데이터를 생성합니다.
// @Tags example
// @Accept json
// @Produce json
// @Param body body CreateExampleRequest true "Example 생성 요청 데이터"
// @Success 200 {object} RespHeader
// @Failure 400 {object} RespHeader
// @Failure 500 {object} RespHeader
// @Router /v1/examples [post]
func (h *Handler) CreateExample(c *gin.Context) {
	var req CreateExampleRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		RespError(c, http.StatusBadRequest, "Invalid request body")
		return
	}

	// 비즈니스 로직 처리 (예시)
	if err := insertExampleToDB(req); err != nil {
		RespError(c, http.StatusInternalServerError, err.Error())
		return
	}

	RespOK(c, NewRespHeader(Success))
}

2. GET 요청 (조회)

기본 흐름

  • 클라이언트가 조회 요청
  • 필요한 경우 Query 파라미터 받음
  • 서버가 데이터 조회 후 응답
  • 성공하면 200 OK 리스트 반환
// GetAllExamples
// @Summary Example 리스트 조회
// @Description Example 데이터를 상태값에 따라 조회합니다.
// @Tags example
// @Accept json
// @Produce json
// @Param status query string false "Example 상태 (예: active, inactive)"
// @Success 200 {object} GetExamplesResponse
// @Failure 500 {object} RespHeader
// @Router /v1/examples [get]
func (h *Handler) GetAllExamples(c *gin.Context) {
	status := c.Query("status")
	if status == "" {
		status = "active" // 기본값
	}

	// 비즈니스 로직 처리 (예시)
	examples, err := getExamplesFromDB(status)
	if err != nil {
		RespError(c, http.StatusInternalServerError, err.Error())
		return
	}

	resp := GetExamplesResponse{
		RespHeader: NewRespHeader(Success),
		Examples:   examples,
	}

	RespOK(c, resp)
}

📌 Swagger 주석 작성시 주의할 점

항목주의사항
@Summary 한 줄 요약 (짧고 직관적으로)
@Description 상세 설명 (필요하면 한두 줄)
@Tags Swagger 카테고리 메뉴 분류
@Accept / @Produce json이면 그냥 둘 다 "json"
@Param body 또는 query 명시 정확히 (특히 GET 쿼리)
@Success / @Failure 응답 타입, HTTP 코드 반드시 맞게
@Router API 엔드포인트 + HTTP 메서드 지정

*응답값과 요청 body값은 미리 객체 형태로 만들어서 관리

아래와 같이 정의하면 되며 binding, required와 같은 요구조건등을 통해서 필수 값을 따로 지정할 수도 있다.

type ExampleRequest struct {
	NickName      string `json:"nick_name" binding:"required"`
	WalletAddress string `json:"wallet_address" binding:"required"`
    Team          string `json:"team"`
}

DB와 상호 작용하는 부분은 따로 빼서 관리를 하였는데

먼저 DB에 대한 entitiy 부분을 따로 정의해서 빼 준 후에

type UserInfo struct {
	Id            primitive.ObjectID `json:"id" bson:"_id, omitempty"`
	NickName      string             `json:"nick_name" bson:"nick_name"`
	ExampleAddress string             `json:"example_address" bson:"example_address"`
	Team          string             `json:"team" bson:"team"`
}

다음과 같이 mongo db 모듈을 써서 crud를 진행하였다.

func (a *AccountDB) FindUser(wallet string, out *entity.UserInfo) error {
	filter := bson.M{"example_address": wallet}
	err := a.userInfo.FindOne(context.TODO(), filter).Decode(out)
	if err == mongo.ErrNoDocuments {
		return errors.New("user not found")
	}
	return err
}

https://www.mongodb.com/docs/manual/reference/method/db.collection.findOne/

 

db.collection.findOne() - Database Manual v8.0 - MongoDB Docs

If the query plan changes to use a different index, the method may return a different document. If your use case requires that a particular record is chosen consistently, you must use the options document to specify a sort. For details on using findOne() w

www.mongodb.com

공식문서에 따라서 db에 맞는 문법을 넣어서 리턴해 주었다.
전체 조회 등의 경우 다음처럼 옵션을 주는 방법도 있다. projection 그리고 cursor등을 사용하면 mongodb에서 select all 처럼 사용이 가능하다.

func (a *AccountDB) FindUserByWalletAll() ([]string, error) {
	ctx := context.TODO()

	// projection으로 wallet_address만 가져오기
	opts := options.Find().SetProjection(bson.M{"example_address": 1})

	cursor, err := a.userInfo.Find(ctx, bson.M{}, opts)
	if err != nil {
		return nil, err
	}
	defer cursor.Close(ctx)

	var addresses []string
	for cursor.Next(ctx) {
		var result struct {
			ExampleAddress string `bson:"example_address"`
		}
		if err := cursor.Decode(&result); err != nil {
			return nil, err
		}
		addresses = append(addresses, result.ExampleAddress)
	}

	return addresses, nil
}




에러나 응답 리턴의 경우 따로 에러를 만들었으며 API 응답에 대한 부분만 Utils로 따로 빼서 해당 에러 메세지를 담아서 최종적으로 전달하였다.

// RespOK - 성공 응답 헬퍼
func RespOK(c *gin.Context, body interface{}) {
	c.JSON(http.StatusOK, body)
}

// RespError - 실패 응답 헬퍼
func RespError(c *gin.Context, status int, msg string) {
	c.JSON(status, protocol.NewRespHeader(protocol.Failed, msg))
	c.Abort()
}

이렇게 응답의 경우 공통으로 묶은 후에 자체적으로 규칙을 정해서 통일된 형태로 내려주면 된다.

728x90
반응형
Comments