@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
private String pwd;
private String name;
private String phone;
private int gender;
private String birth;
}
테이블로 생성하고자 하는 컬럼들을 선언한다.
public interface UserRepository extends JpaRepository<User, Long> {
// 이메일로 User 찾기.
Optional<User> findByEmail(String email);
}
인터페이스 형태로 UserRepository 를 선언한다.
@RequiredArgsConstructor
@Service
public class UserService {
/** jwt 부분
@Value("${jwt.secret}")
private String secretKey;
private Long expiredMs = 1000 * 60 * 60L;
public String login(String userName, String password) {
return JwtUtil.createJwt(userName, secretKey, expiredMs);
} **/
private final UserRepository userRepository;
private final BCryptPasswordEncoder encoder;
public void join(UserJoinRequestDto requestDto) {
// email 중복 체크
userRepository.findByEmail(
requestDto.getEmail())
.ifPresent(user -> {
throw new AppException(ErrorCode.USERNAME_DUPLICATED, requestDto.getEmail() + "는 이미 존재합니다.");
});
// 저장
userRepository.save(
User.builder()
.email(requestDto.getEmail())
.pwd(encoder.encode(requestDto.getPwd()))
.phone(requestDto.getPhone())
.name(requestDto.getName())
.birth(requestDto.getBirth())
.gender(requestDto.getGender())
.build());
}
}
회원가입을 위한 서비스를 생성한다.
userRepository의 save 메소드로 저장한다.
필요한 예외처리를 해주고, User 도메인의 빌더를 통해 새로운 User 객체를 생성해서 저장한다.
암호화를 위한 BCrypPasswordEncoder 를 사용한다.
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/users")
public class UserApiController {
private final UserService userService;
@PostMapping("/join")
public ResponseEntity<String> join(@RequestBody UserJoinRequestDto requestDto) {
userService.join(requestDto);
return ResponseEntity.ok("회원가입이 완료되었습니다.");
}
}
POST 방식으로 UserJoinRequestDto 를 RequestBody 로 받아준다.
등록된 userService의 join 메소드를 통해 새로운 회원을 등록한다.
이렇게 하면 회원가입은 아주 쉽게 구현할 수 있다.
@Configuration
public class EncoderConfig {
@Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
패스워드 암호화에 필요한 BCryptPasswordEncoder 를 Bean으로 등록한다.
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig {
private final UserService userService;
/** jwt 에 필요한 부분
@Value("${jwt.secret}")
private String secretKey;
**/
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.httpBasic(AbstractHttpConfigurer::disable)
.csrf(AbstractHttpConfigurer::disable)
.cors(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(requests -> {
requests.requestMatchers("/api/users/login", "/api/users/join").permitAll();
requests.requestMatchers(HttpMethod.POST, "/api/**").authenticated();
})
.sessionManagement(
sessionManagement ->
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.addFilterBefore(new JwtFilter(userService, secretKey), UsernamePasswordAuthenticationFilter.class)
.build();
}
}
암호화를 위해 Spring Security를 implements 했기 때문에, 이에 필요한 설정들을 위와 같이 작성한다.
@RestControllerAdvice
public class ExceptionManager {
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<?> runtimeExceptionHandler(RuntimeException e) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(e.getMessage());
}
@ExceptionHandler(AppException.class)
public ResponseEntity<?> appExceptionHandler(AppException e) {
return ResponseEntity
.status(e.getErrorCode().getHttpStatus())
.body(e.getErrorCode().name() + " " + e.getMessage());
}
}
ExceptionMager를 통해서 서비스 동작 중 에러가 발생하는 부분을 처리해준다.
@AllArgsConstructor
@Getter
public class AppException extends RuntimeException{
private ErrorCode errorCode;
private String message;
}
AppException 이라는 새로운 RuntimeException을 생성한다.
@AllArgsConstructor
@Getter
public enum ErrorCode {
USERNAME_DUPLICATED(HttpStatus.CONFLICT, "");
private HttpStatus httpStatus;
private String message;
}
ErrorCode 라는 Enum 을 생성하여 관리한다.


암호화된 패스워드가 DB 에 저장된 모습.