From ffbb365c41c470b26f8665f73990a1c9a6c4a55a Mon Sep 17 00:00:00 2001 From: ByungCheol Date: Sat, 11 Jul 2026 08:49:19 +0900 Subject: [PATCH] =?UTF-8?q?feat(local):=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EA=B0=9C=EB=B0=9C=EC=9E=90=20=EB=A1=9C=EA=B7=B8=EC=9D=B8(dev-l?= =?UTF-8?q?ogin)=20+=20=EA=B4=80=EB=A6=AC=EC=9E=90=20=EC=8B=9C=EB=93=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DevAuthController: @Profile("local") 전용 /api/auth/dev-login (이메일로 세션 발급) — 관리자 콘솔 로컬 확인용 - AdminSeedData: admin@dog.com(ADMIN) + 더미 회원 시드. 앱 시드(LocalSeedData)와 분리, @Order로 선실행 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../dog/dognation/config/AdminSeedData.java | 39 +++++++++++++ .../dognation/config/DevAuthController.java | 57 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 src/main/java/com/dog/dognation/config/AdminSeedData.java create mode 100644 src/main/java/com/dog/dognation/config/DevAuthController.java diff --git a/src/main/java/com/dog/dognation/config/AdminSeedData.java b/src/main/java/com/dog/dognation/config/AdminSeedData.java new file mode 100644 index 0000000..6f0cad8 --- /dev/null +++ b/src/main/java/com/dog/dognation/config/AdminSeedData.java @@ -0,0 +1,39 @@ +package com.dog.dognation.config; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.context.annotation.Profile; +import org.springframework.core.annotation.Order; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +/** + * local 프로파일 전용 관리자 시드 (앱 시드 {@code LocalSeedData} 와 분리). + * 관리자 콘솔을 개발자 로그인(admin@dog.com)으로 확인하기 위한 관리자 + 더미 회원. + */ +@Component +@Profile("local") +@Order(1) // 앱 시드(LocalSeedData)보다 먼저 관리자/회원을 채워둔다 +public class AdminSeedData implements CommandLineRunner { + + private final JdbcTemplate jdbc; + + public AdminSeedData(JdbcTemplate jdbc) { + this.jdbc = jdbc; + } + + @Override + public void run(String... args) { + insertUser("admin@dog.com", "관리자", "PREMIUM", "ADMIN"); + // 회원관리 표에 보일 더미 회원 + insertUser("dol@dog.com", "돌이", "FREE", "USER"); + insertUser("byeol@dog.com", "별이", "AD_FREE", "USER"); + System.out.println("[admin seed] admin@dog.com(ADMIN) + 더미 회원 준비 완료"); + } + + private void insertUser(String email, String nickname, String tier, String role) { + jdbc.update(""" + INSERT INTO users(email, nickname, subscription_tier, status, role, provider, created_at, updated_at) + VALUES (?, ?, ?, 'ACTIVE', ?, 'GOOGLE', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) + """, email, nickname, tier, role); + } +} diff --git a/src/main/java/com/dog/dognation/config/DevAuthController.java b/src/main/java/com/dog/dognation/config/DevAuthController.java new file mode 100644 index 0000000..f912255 --- /dev/null +++ b/src/main/java/com/dog/dognation/config/DevAuthController.java @@ -0,0 +1,57 @@ +package com.dog.dognation.config; + +import com.dog.dognation.auth.dto.LoginResponse; +import com.dog.dognation.auth.dto.MemberResponse; +import com.dog.dognation.common.exception.ApiException; +import com.dog.dognation.domain.auth.AuthSession; +import com.dog.dognation.domain.auth.AuthSessionRepository; +import com.dog.dognation.domain.user.User; +import com.dog.dognation.domain.user.UserRepository; +import org.springframework.context.annotation.Profile; +import org.springframework.http.HttpStatus; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.Duration; +import java.time.OffsetDateTime; +import java.util.Map; +import java.util.UUID; + +/** + * 개발자 로그인 — local 프로파일 전용. 구글/애플 없이 이메일로 세션을 발급한다. + * 관리자 콘솔을 로컬에서 확인하기 위한 용도(예: admin@dog.com). 운영 빌드에는 등록되지 않는다. + */ +@Profile("local") +@RestController +@RequestMapping("/api/auth") +public class DevAuthController { + + private static final Duration TTL = Duration.ofDays(30); + + private final UserRepository userRepository; + private final AuthSessionRepository sessionRepository; + + public DevAuthController(UserRepository userRepository, AuthSessionRepository sessionRepository) { + this.userRepository = userRepository; + this.sessionRepository = sessionRepository; + } + + /** 이메일로 세션 발급 (검증 없음, local 전용). */ + @PostMapping("/dev-login") + @Transactional + public LoginResponse devLogin(@RequestBody Map body) { + String email = body.get("email"); + if (email == null || email.isBlank()) { + throw new ApiException(HttpStatus.BAD_REQUEST, "email 이 필요합니다."); + } + User user = userRepository.findByEmail(email) + .orElseThrow(() -> new ApiException(HttpStatus.NOT_FOUND, "회원을 찾을 수 없습니다: " + email)); + + String token = UUID.randomUUID().toString().replace("-", ""); + sessionRepository.save(AuthSession.issue(token, user, true, OffsetDateTime.now().plus(TTL))); + return new LoginResponse(token, TTL.getSeconds(), MemberResponse.from(user)); + } +}