From 17983ad775d094896b52b4a48efaa53d12f3b001 Mon Sep 17 00:00:00 2001 From: tolelom <98kimsungmin@naver.com> Date: Tue, 24 Feb 2026 14:52:45 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=20API=20=EC=B6=94=EA=B0=80=20(=EB=AA=A9=EB=A1=9D=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C,=20=EA=B6=8C=ED=95=9C=20=EB=B3=80=EA=B2=BD,=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GET /api/users - 전체 유저 목록 (admin only) - PATCH /api/users/:id/role - 권한 변경 (admin only) - DELETE /api/users/:id - 유저 삭제 (admin only) Co-Authored-By: Claude Sonnet 4.6 --- internal/auth/handler.go | 28 ++++++++++++++++++++++++++++ internal/auth/repository.go | 20 ++++++++++++++++++++ internal/auth/service.go | 12 ++++++++++++ routes/routes.go | 6 ++++++ 4 files changed, 66 insertions(+) diff --git a/internal/auth/handler.go b/internal/auth/handler.go index ec8e612..a0c8364 100644 --- a/internal/auth/handler.go +++ b/internal/auth/handler.go @@ -39,3 +39,31 @@ func (h *Handler) Logout(c *fiber.Ctx) error { h.svc.Logout(userID) return c.JSON(fiber.Map{"message": "로그아웃 되었습니다"}) } + +func (h *Handler) GetAllUsers(c *fiber.Ctx) error { + users, err := h.svc.GetAllUsers() + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "유저 목록을 불러오지 못했습니다"}) + } + return c.JSON(users) +} + +func (h *Handler) UpdateRole(c *fiber.Ctx) error { + var body struct { + Role string `json:"role"` + } + if err := c.BodyParser(&body); err != nil || (body.Role != "admin" && body.Role != "user") { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "role은 admin 또는 user여야 합니다"}) + } + if err := h.svc.UpdateRole(c.Params("id"), Role(body.Role)); err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "권한 변경에 실패했습니다"}) + } + return c.JSON(fiber.Map{"message": "권한이 변경되었습니다"}) +} + +func (h *Handler) DeleteUser(c *fiber.Ctx) error { + if err := h.svc.DeleteUser(c.Params("id")); err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "유저 삭제에 실패했습니다"}) + } + return c.SendStatus(fiber.StatusNoContent) +} diff --git a/internal/auth/repository.go b/internal/auth/repository.go index 07881f1..545d349 100644 --- a/internal/auth/repository.go +++ b/internal/auth/repository.go @@ -19,3 +19,23 @@ func (r *Repository) FindByUsername(username string) (*User, error) { func (r *Repository) Create(user *User) error { return r.db.Create(user).Error } + +func (r *Repository) FindAll() ([]User, error) { + var users []User + err := r.db.Order("created_at asc").Find(&users).Error + return users, err +} + +func (r *Repository) FindByID(id string) (*User, error) { + var user User + err := r.db.First(&user, id).Error + return &user, err +} + +func (r *Repository) UpdateRole(id string, role Role) error { + return r.db.Model(&User{}).Where("id = ?", id).Update("role", role).Error +} + +func (r *Repository) Delete(id string) error { + return r.db.Delete(&User{}, id).Error +} diff --git a/internal/auth/service.go b/internal/auth/service.go index dc91290..99f3542 100644 --- a/internal/auth/service.go +++ b/internal/auth/service.go @@ -65,6 +65,18 @@ func (s *Service) Logout(userID uint) { s.rdb.Del(context.Background(), key) } +func (s *Service) GetAllUsers() ([]User, error) { + return s.repo.FindAll() +} + +func (s *Service) UpdateRole(id string, role Role) error { + return s.repo.UpdateRole(id, role) +} + +func (s *Service) DeleteUser(id string) error { + return s.repo.Delete(id) +} + func (s *Service) EnsureAdmin(username, password string) error { if _, err := s.repo.FindByUsername(username); err == nil { return nil // 이미 존재하면 스킵 diff --git a/routes/routes.go b/routes/routes.go index 840a46d..9117c2e 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -21,6 +21,12 @@ func Register( a.Post("/login", authH.Login) a.Post("/logout", middleware.Auth, authH.Logout) + // Users (admin only) + u := api.Group("/users", middleware.Auth, middleware.AdminOnly) + u.Get("/", authH.GetAllUsers) + u.Patch("/:id/role", authH.UpdateRole) + u.Delete("/:id", authH.DeleteUser) + // Announcements ann := api.Group("/announcements") ann.Get("/", annH.GetAll)