-
Notifications
You must be signed in to change notification settings - Fork 0
feat(signup): 관리자 회원가입 기능 구현 #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The head ref may contain hidden characters: "feature/#20-\uD68C\uC6D0\uAC00\uC785\uAD6C\uD604"
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package com.example.gtable.global.config; | ||
|
|
||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.data.redis.connection.RedisConnectionFactory; | ||
| import org.springframework.data.redis.core.RedisTemplate; | ||
| import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; | ||
| import org.springframework.data.redis.serializer.StringRedisSerializer; | ||
|
|
||
| @Configuration | ||
| public class RedisConfig { | ||
| @Bean | ||
| public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { | ||
| RedisTemplate<String, Object> template = new RedisTemplate<>(); | ||
| template.setConnectionFactory(connectionFactory); | ||
| template.setKeySerializer(new StringRedisSerializer()); | ||
| template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); | ||
| return template; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| package com.example.gtable.user.dto; | ||
|
|
||
| import com.example.gtable.user.entity.Role; | ||
| import com.example.gtable.user.entity.SocialType; | ||
| import com.example.gtable.user.entity.User; | ||
|
|
||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import jakarta.validation.constraints.Email; | ||
| import jakarta.validation.constraints.NotBlank; | ||
| import jakarta.validation.constraints.Pattern; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Getter | ||
| @NoArgsConstructor | ||
| public class ManagerSignupRequestDto { | ||
|
|
||
| @NotBlank | ||
| @Email(regexp = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@" | ||
| + "[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$") | ||
| @Schema(description = "이메일", example = "abc@gmail.com") | ||
| private String email; | ||
|
|
||
| @NotBlank | ||
| @Pattern(regexp = "(?=.*[0-9])(?=.*[a-zA-Z])(?=.*\\W)(?=\\S+$).{8,20}") | ||
| @Schema(description = "비밀번호", example = "1234568!@") | ||
| private String password; | ||
|
|
||
| @NotBlank | ||
| @Pattern(regexp = "^[a-zA-Z가-힣]{2,12}$") | ||
| @Schema(description = "닉네임", example = "가십이") | ||
| private String nickname; | ||
|
|
||
| public User toEntity() { | ||
| return User.builder() | ||
| .email(email) | ||
| .password(password) | ||
| .nickname(nickname) | ||
| .socialType(SocialType.KAKAO) | ||
| .role(Role.MANAGER) | ||
| .build(); | ||
|
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| package com.example.gtable.user.dto; | ||
|
|
||
| import com.example.gtable.user.entity.Role; | ||
| import com.example.gtable.user.entity.User; | ||
|
|
||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Builder; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Getter | ||
| @NoArgsConstructor | ||
| @Builder | ||
| @AllArgsConstructor | ||
| public class ManagerSignupResponseDto { | ||
| @Schema(description = "id", example = "1") | ||
| private Long id; | ||
| @Schema(description = "이메일", example = "abc@gmail.com") | ||
| private String email; | ||
| @Schema(description = "닉네임", example = "무한이") | ||
| private String nickname; | ||
| @Schema(description = "역할", example = "MANAGER") | ||
| private Role role; | ||
|
|
||
| public static ManagerSignupResponseDto fromEntity(User user) { | ||
| return ManagerSignupResponseDto.builder() | ||
| .id(user.getId()) | ||
| .email(user.getEmail()) | ||
| .nickname(user.getNickname()) | ||
| .role(user.getRole()) | ||
| .build(); | ||
|
|
||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| package com.example.gtable.user.entity; | ||
|
|
||
| import org.springframework.security.crypto.password.PasswordEncoder; | ||
|
|
||
| import jakarta.persistence.Column; | ||
| import jakarta.persistence.Entity; | ||
| import jakarta.persistence.EnumType; | ||
|
|
@@ -24,6 +26,8 @@ public class User { | |
| @Column(nullable = false, unique = true) | ||
| private String email; // 카카오 이메일 | ||
|
|
||
| private String password; // 관리자 패스워드 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion password 필드에 대한 데이터베이스 제약조건 고려가 필요합니다 관리자 계정의 password 필드는 보안상 중요한 데이터입니다. 현재 어떤 제약조건도 없이 선언되어 있는데, 다음 사항들을 고려해보시는 것이 좋겠습니다:
- private String password; // 관리자 패스워드
+ @Column(nullable = false)
+ private String password; // 관리자 패스워드🤖 Prompt for AI Agents |
||
|
|
||
| @Column(nullable = false) | ||
| private String nickname; | ||
|
|
||
|
|
@@ -37,8 +41,9 @@ public class User { | |
| private Role role; | ||
|
|
||
| @Builder | ||
| public User(String email, String nickname, String profileImage, SocialType socialType, Role role){ | ||
| public User(String email,String password, String nickname, String profileImage, SocialType socialType, Role role){ | ||
| this.email = email; | ||
| this.password = password; | ||
| this.nickname = nickname; | ||
| this.profileImage = profileImage; | ||
| this.socialType = socialType; | ||
|
|
@@ -62,4 +67,7 @@ public static User createUserWithId(Long userId, String email, String nickname, | |
| public void updateNickname(String nickname){ | ||
| this.nickname = nickname; | ||
| } | ||
| public void encodePassword(PasswordEncoder passwordEncoder) { | ||
| password = passwordEncoder.encode(password); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,14 +1,44 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| package com.example.gtable.user.service; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.security.crypto.password.PasswordEncoder; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.stereotype.Service; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.transaction.annotation.Transactional; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.example.gtable.user.dto.ManagerSignupRequestDto; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.example.gtable.user.dto.ManagerSignupResponseDto; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.example.gtable.user.entity.User; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.example.gtable.user.repository.UserRepository; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.extern.slf4j.Slf4j; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| @Service | ||||||||||||||||||||||||||||||||||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||||||||||||||||||||||||||||||||||
| @Slf4j | ||||||||||||||||||||||||||||||||||||||||||||||||||
| public class UserService { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| private final UserRepository userRepository; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| private final PasswordEncoder passwordEncoder; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| @Transactional | ||||||||||||||||||||||||||||||||||||||||||||||||||
| public ManagerSignupResponseDto signup(ManagerSignupRequestDto managerSignupRequestDto) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| validateEmailDuplicated(managerSignupRequestDto); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| validateNickNameDuplicated(managerSignupRequestDto.getNickname()); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| User user = managerSignupRequestDto.toEntity(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| user.encodePassword(passwordEncoder); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| return ManagerSignupResponseDto.fromEntity(userRepository.save(user)); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| private void validateEmailDuplicated(ManagerSignupRequestDto managerSignupRequestDto) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| userRepository.findByEmail(managerSignupRequestDto.getEmail()).ifPresent(member -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new IllegalArgumentException(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| private void validateNickNameDuplicated(String nickName) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| userRepository.findByNickname(nickName).ifPresent(member -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new IllegalArgumentException(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+32
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 중복 검증 로직에서 예외 메시지 개선이 필요합니다 현재 private void validateEmailDuplicated(ManagerSignupRequestDto managerSignupRequestDto) {
userRepository.findByEmail(managerSignupRequestDto.getEmail()).ifPresent(member -> {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("이미 사용 중인 이메일입니다: " + managerSignupRequestDto.getEmail());
}
);
}
private void validateNickNameDuplicated(String nickName) {
userRepository.findByNickname(nickName).ifPresent(member -> {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("이미 사용 중인 닉네임입니다: " + nickName);
}
);
}이렇게 하면 API 응답에서도 더 명확한 오류 메시지를 제공할 수 있습니다. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exception 처리 관련 정책이 명확하게 결정되면 수정 예정 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @HyemIin 좋은 접근 방식이네요! 예외 처리 정책을 먼저 수립한 후 일관성 있게 적용하는 것이 코드베이스 전체의 품질과 유지보수성에 더 도움이 될 것 같습니다. 정책이 결정되면 그때 함께 개선해보시면 되겠습니다. 👍 |
||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
보안과 설계 관점에서 몇 가지 중요한 개선사항이 있습니다!
현재 구현에서 발견된 주요 이슈들:
다음과 같은 개선을 제안드립니다:
public User toEntity() { return User.builder() .email(email) - .password(password) + .password(password) // 서비스 레이어에서 인코딩 처리 .nickname(nickname) - .socialType(SocialType.KAKAO) + .socialType(SocialType.NONE) // 또는 적절한 관리자용 타입 .role(Role.MANAGER) .build(); }비밀번호 인코딩은 서비스 레이어에서 처리하는 것이 더 안전하고 적절합니다.
📝 Committable suggestion
🤖 Prompt for AI Agents