
이번 포스팅에서는 Spring Boot와 Spring Security를 함께 사용하여 구현한 회원가입 프로세스에 대해 자세히 알아보겠습니다 🚀
회원가입 구현은 다음과 같은 계층 구조로 이루어져 있습니다:
- Controller (웹 요청 처리)
- Service (비즈니스 로직)
- Repository (데이터 접근)
- Entity (데이터 모델)
@Entity
@Table(name = "tbl_user")
@Data
public class User {
@Id // Primary Key 지정
@GeneratedValue(strategy = GenerationType.IDENTITY) // Auto Increment
@Column(name = "user_pk")
private long userPk;
@Column(name = "user_id", nullable = false, unique = true)
private String userId;
@Column(name = "user_pw", nullable = false)
private String userPw;
@Column(name = "user_email", nullable = false, unique = true)
private String userEmail;
@Column(name = "user_nickname", nullable = false, unique = true)
private String userNickname;
@Column(name = "user_role")
private String userRole = "ROLE_USER"; // Spring Security의 기본 권한
}
@Entity: JPA 엔티티임을 나타냄@Table: 실제 DB 테이블 이름 지정@Data: getter/setter/toString 등 자동 생성nullable = false로 필수 입력값 지정unique = true로 중복 방지
클라이언트로부터 회원가입 데이터를 받는 DTO
@Data
public class UserCreateRequest {
private String userId;
private String userPw;
private String userEmail;
private String userNickname;
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
boolean existsByUserId(String userId);
boolean existsByUserEmail(String userEmail);
boolean existsByUserNickname(String userNickname);
Optional<User> findByUserId(String userId);
Optional<User> findByUserEmail(String userEmail);
}
- JpaRepository 상속으로 기본 CRUD 메서드 제공
Optional: 사용자 조회 시 결과가 null일 수 있는 경우 사용
@RestController
public class UserController {
private final UserService userService;
@PostMapping("/auth/join") // /auth/** 경로로 Spring Security 인증 없이 접근 가능
public ResponseEntity<?> join(@RequestBody UserCreateRequest request) {
userService.createUser(request);
return ResponseEntity.status(HttpStatus.CREATED).body("회원 가입이 완료되었습니다.");
}
}
/auth/join엔드포인트로 POST 요청 처리@RequestBody로 JSON 데이터를 DTO로 변환- 성공 시 201 Created 상태코드 반환
- 생성자 주입으로 UserService 의존성 주입
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final BCryptPasswordEncoder passwordEncoder; // Spring Security의 암호화 도구
private final ModelMapper modelMapper;
@Override
@Transactional
public void createUser(UserCreateRequest request) {
// 중복 검사
checkDuplicateUser(request);
// DTO -> Entity 변환
User user = modelMapper.map(request, User.class);
// Spring Security의 BCrypt로 비밀번호 암호화
user.setUserPw(passwordEncoder.encode(request.getUserPw()));
// Spring Security의 기본 사용자 권한 설정
user.setUserRole("ROLE_USER");
userRepository.save(user);
}
private void checkDuplicateUser(UserCreateRequest request) {
List<String> errors = new ArrayList<>();
if (userRepository.existsByUserId(request.getUserId())) {
errors.add("이미 사용 중인 아이디입니다.");
}
// ... 이메일, 닉네임 중복 검사
if (!errors.isEmpty()) {
throw new DuplicateUserException(String.join(", ", errors));
}
}
}
@Transactional로 트랜잭션 처리@RequiredArgsConstructor로 final 필드 생성자 주입- ModelMapper로 DTO -> Entity 변환 자동화
- BCryptPasswordEncoder로 비밀번호 암호화
- 중복 검사 로직을 별도 메서드로 분리
- 여러 중복 오류를 한 번에 반환하도록 구현
비밀번호 암호화
- BCryptPasswordEncoder를 사용하여 안전한 비밀번호 해싱
- 암호화된 비밀번호는 복호화가 불가능
권한 관리
- ROLE_USER와 같은 Spring Security의 권한 체계 사용
- 추후 @PreAuthorize 등을 통한 메소드 레벨 보안 적용 가능
보안 설정
- /auth/** 경로는 인증 없이 접근 가능
- 나머지 경로는 인증 필요
- 입력값 검증 (Bean Validation)
- 예외 처리 개선
- OAuth2 소셜 로그인을 위한 필드 추가