정우님 👏
그 생각 정말 잘하셨어요. 아주 현명한 접근이에요.
처음부터 완벽한 설계를 하려다 보면,
대부분은 “설계 지옥”에 빠집니다 —
코드를 쓰기 전에 너무 많은 걸 고려하다가 아무것도 안 돌아가는 상태가 되죠 😅
“먼저 돌아가게 만들고, 나중에 좋게 만든다.”
이건 마틴 파울러(Martin Fowler)가 말한 유명한 원칙이에요:
"Make it work, make it right, make it fast."
(일단 돌아가게 만들고, 올바르게 고치고, 마지막에 빠르게 한다.)
| 단계 | 목표 | 특징 |
|---|---|---|
| ① Prototype | 기능 중심으로 빠르게 돌아가게 하기 | 디렉토리 구조 단순, 코드도 직관적 |
| ② Refactor | 구조 개선, 중복 제거, 책임 분리 | handler → service → repository 계층 분리 |
| ③ Optimize | 성능, 보안, 안정성 개선 | connection pool, presigned, limit, retry 등 |
func UploadHandler(c *gin.Context) {
file, _ := c.FormFile("file")
src, _ := file.Open()
defer src.Close()
// 그냥 바로 MinIO 업로드
minioClient.PutObject(context.TODO(), "bucket", file.Filename, src, file.Size, minio.PutObjectOptions{})
c.JSON(200, gin.H{"message": "uploaded"})
}
type UploadRequest struct {
File *multipart.FileHeader `form:"file" binding:"required"`
}
func UploadHandler(c *gin.Context) {
var req UploadRequest
if err := c.ShouldBind(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
if err := service.UploadToMinio(req.File); err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "ok"})
}
이제 서비스 코드(service/upload_service.go)로 분리하고,
테스트하기도 쉬워집니다.
internal/, pkg/, service/ 같은 구조는 “나중에 옮기기 쉽게”만 생각해두세요.main.go 안에 다 때려넣고,원하신다면 지금 정우님 프로젝트 상황 기준으로
👉 “현재 구조로 시작했다가, 자연스럽게 리팩토링할 수 있는 단계별 가이드”
(예: 1단계: 단일 main.go → 2단계: service 분리 → 3단계: interface 도입)
이렇게 만들어드릴까요?