feat: 회원가입 API 구현 (#56)
- POST /v1/in/auth/signup 엔드포인트 추가 (AllowAnonymous) - SignupRequestDto/SignupResponseDto 생성 - AuthService.SignupAsync 구현 (이메일 중복검사, AdminCode 생성, BCrypt 해싱) - ApiResponse<T>.Success(data, msg) 오버로드 추가
This commit is contained in:
parent
b5b015255e
commit
16550dbff3
|
|
@ -19,6 +19,20 @@ public class AuthController : ControllerBase
|
||||||
_authService = authService;
|
_authService = authService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("signup")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[SwaggerOperation(
|
||||||
|
Summary = "회원가입",
|
||||||
|
Description = "새로운 관리자 계정을 생성합니다. 이메일 인증이 필요합니다.")]
|
||||||
|
[SwaggerResponse(200, "회원가입 성공", typeof(ApiResponse<SignupResponseDto>))]
|
||||||
|
[SwaggerResponse(400, "잘못된 요청")]
|
||||||
|
[SwaggerResponse(409, "이미 사용 중인 이메일")]
|
||||||
|
public async Task<IActionResult> SignupAsync([FromBody] SignupRequestDto request)
|
||||||
|
{
|
||||||
|
var result = await _authService.SignupAsync(request);
|
||||||
|
return Ok(ApiResponse<SignupResponseDto>.Success(result, "회원가입 완료. 이메일 인증이 필요합니다."));
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("login")]
|
[HttpPost("login")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[SwaggerOperation(
|
[SwaggerOperation(
|
||||||
|
|
|
||||||
22
SPMS.Application/DTOs/Auth/SignupRequestDto.cs
Normal file
22
SPMS.Application/DTOs/Auth/SignupRequestDto.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace SPMS.Application.DTOs.Auth;
|
||||||
|
|
||||||
|
public class SignupRequestDto
|
||||||
|
{
|
||||||
|
[Required(ErrorMessage = "이메일은 필수입니다.")]
|
||||||
|
[EmailAddress(ErrorMessage = "올바른 이메일 형식이 아닙니다.")]
|
||||||
|
public string Email { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "비밀번호는 필수입니다.")]
|
||||||
|
[MinLength(8, ErrorMessage = "비밀번호는 8자 이상이어야 합니다.")]
|
||||||
|
public string Password { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "이름은 필수입니다.")]
|
||||||
|
[StringLength(50, ErrorMessage = "이름은 50자 이내여야 합니다.")]
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "전화번호는 필수입니다.")]
|
||||||
|
[StringLength(20, ErrorMessage = "전화번호는 20자 이내여야 합니다.")]
|
||||||
|
public string Phone { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
12
SPMS.Application/DTOs/Auth/SignupResponseDto.cs
Normal file
12
SPMS.Application/DTOs/Auth/SignupResponseDto.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SPMS.Application.DTOs.Auth;
|
||||||
|
|
||||||
|
public class SignupResponseDto
|
||||||
|
{
|
||||||
|
[JsonPropertyName("admin_code")]
|
||||||
|
public string AdminCode { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("email")]
|
||||||
|
public string Email { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ namespace SPMS.Application.Interfaces;
|
||||||
|
|
||||||
public interface IAuthService
|
public interface IAuthService
|
||||||
{
|
{
|
||||||
|
Task<SignupResponseDto> SignupAsync(SignupRequestDto request);
|
||||||
Task<LoginResponseDto> LoginAsync(LoginRequestDto request);
|
Task<LoginResponseDto> LoginAsync(LoginRequestDto request);
|
||||||
Task<TokenRefreshResponseDto> RefreshTokenAsync(TokenRefreshRequestDto request);
|
Task<TokenRefreshResponseDto> RefreshTokenAsync(TokenRefreshRequestDto request);
|
||||||
Task LogoutAsync(long adminId);
|
Task LogoutAsync(long adminId);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ using SPMS.Application.DTOs.Auth;
|
||||||
using SPMS.Application.Interfaces;
|
using SPMS.Application.Interfaces;
|
||||||
using SPMS.Application.Settings;
|
using SPMS.Application.Settings;
|
||||||
using SPMS.Domain.Common;
|
using SPMS.Domain.Common;
|
||||||
|
using SPMS.Domain.Entities;
|
||||||
|
using SPMS.Domain.Enums;
|
||||||
using SPMS.Domain.Exceptions;
|
using SPMS.Domain.Exceptions;
|
||||||
using SPMS.Domain.Interfaces;
|
using SPMS.Domain.Interfaces;
|
||||||
|
|
||||||
|
|
@ -27,6 +29,46 @@ public class AuthService : IAuthService
|
||||||
_jwtSettings = jwtSettings.Value;
|
_jwtSettings = jwtSettings.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<SignupResponseDto> SignupAsync(SignupRequestDto request)
|
||||||
|
{
|
||||||
|
// 1. 이메일 중복 검사
|
||||||
|
if (await _adminRepository.EmailExistsAsync(request.Email))
|
||||||
|
{
|
||||||
|
throw new SpmsException(
|
||||||
|
ErrorCodes.Conflict,
|
||||||
|
"이미 사용 중인 이메일입니다.",
|
||||||
|
409);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. AdminCode 생성 (UUID 12자)
|
||||||
|
var adminCode = Guid.NewGuid().ToString("N")[..12].ToUpper();
|
||||||
|
|
||||||
|
// 3. Admin 엔티티 생성
|
||||||
|
var admin = new Admin
|
||||||
|
{
|
||||||
|
AdminCode = adminCode,
|
||||||
|
Email = request.Email,
|
||||||
|
Password = BCrypt.Net.BCrypt.HashPassword(request.Password),
|
||||||
|
Name = request.Name,
|
||||||
|
Phone = request.Phone,
|
||||||
|
Role = AdminRole.User,
|
||||||
|
EmailVerified = false,
|
||||||
|
CreatedAt = DateTime.UtcNow,
|
||||||
|
IsDeleted = false
|
||||||
|
};
|
||||||
|
|
||||||
|
// 4. 저장
|
||||||
|
await _adminRepository.AddAsync(admin);
|
||||||
|
await _unitOfWork.SaveChangesAsync();
|
||||||
|
|
||||||
|
// 5. 응답 반환
|
||||||
|
return new SignupResponseDto
|
||||||
|
{
|
||||||
|
AdminCode = admin.AdminCode,
|
||||||
|
Email = admin.Email
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<LoginResponseDto> LoginAsync(LoginRequestDto request)
|
public async Task<LoginResponseDto> LoginAsync(LoginRequestDto request)
|
||||||
{
|
{
|
||||||
// 1. 이메일로 관리자 조회
|
// 1. 이메일로 관리자 조회
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,9 @@ public class ApiResponse<T> : ApiResponse
|
||||||
public static ApiResponse<T> Success(T data)
|
public static ApiResponse<T> Success(T data)
|
||||||
=> new() { Result = true, Code = ErrorCodes.Success, Data = data };
|
=> new() { Result = true, Code = ErrorCodes.Success, Data = data };
|
||||||
|
|
||||||
|
public static ApiResponse<T> Success(T data, string msg)
|
||||||
|
=> new() { Result = true, Code = ErrorCodes.Success, Data = data, Msg = msg };
|
||||||
|
|
||||||
public new static ApiResponse<T> Fail(string code, string msg)
|
public new static ApiResponse<T> Fail(string code, string msg)
|
||||||
=> new() { Result = false, Code = code, Msg = msg };
|
=> new() { Result = false, Code = code, Msg = msg };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user