회원가입 비즈니스 로직을 담당하는 AuthService를 구현한 과정을 기록해요.
클라이언트
↓ POST /api/auth/signup (email, password, nickname)
AuthController
↓
AuthService.signUp()
↓
이메일 중복 체크 → 중복이면 CustomException
↓
비밀번호 암호화
↓
User 엔티티 생성 후 저장
↓
200 OK
@Service
@RequiredArgsConstructor
public class AuthService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
@Transactional
public void signUp(SignUpRequestDto request) {
// 1. 이메일 중복 체크
if (userRepository.existsByEmail(request.getEmail())) {
throw new CustomException(UserErrorCode.DUPLICATE_EMAIL);
}
// 2. 비밀번호 암호화
String encodedPassword = passwordEncoder.encode(request.getPassword());
// 3. User 엔티티 생성 및 저장
User user = User.builder()
.email(request.getEmail())
.password(encodedPassword)
.nickname(request.getNickname())
.role(Role.ROLE_USER)
.provider(Provider.LOCAL)
.build();
userRepository.save(user);
}
}
DB 작업 중 에러가 나면 자동으로 롤백해줘요.
비밀번호 암호화 중 에러 발생
↓
@Transactional → 자동 롤백
↓
DB에 잘못된 데이터 저장 안 됨 ✅
DB에 데이터를 쓰는 로직에는 항상 붙여요.
if (userRepository.existsByEmail(request.getEmail())) {
throw new CustomException(UserErrorCode.DUPLICATE_EMAIL);
}
existsByEmail() → DB에 해당 이메일이 있는지 체크해요.
findByEmail()로도 중복 체크할 수 있지만, existsByEmail()은 존재 여부만 확인하기 때문에 더 가볍고 의도가 명확해요.
String encodedPassword = passwordEncoder.encode(request.getPassword());
비밀번호를 평문으로 저장하면 DB가 털렸을 때 비밀번호가 그대로 노출돼요. BCryptPasswordEncoder로 암호화해서 저장해요.
"1234" → encode() → "$2a$10$abc123..." 암호화 ✅
"$2a$10$abc123..." → decode() → ??? 복호화 불가 ✅
User user = User.builder()
.email(request.getEmail())
.password(encodedPassword) // 암호화된 비밀번호 저장
.nickname(request.getNickname())
.role(Role.ROLE_USER) // 기본 권한 설정
.provider(Provider.LOCAL) // 이메일 로그인
.build();
role과 provider는 서비스 레이어에서 직접 설정해요. 클라이언트가 임의로 설정할 수 없도록 하기 위해서예요.
회원가입은 성공 여부만 알려주면 돼요. 따로 반환할 데이터가 없어서 void로 했어요.
Controller → 요청/응답 처리만
Service → 비즈니스 로직 담당 ← 여기서 처리
Repository → DB 접근만
역할을 분리해야 코드가 깔끔하고 테스트하기 쉬워요. 실무에서 항상 지키는 패턴이에요.
JPA 엔티티 객체 생성 시 두 가지 방식을 쓸 수 있어요.
@Builder 방식
User user = User.builder()
.email(email)
.password(encodedPassword)
.role(Role.ROLE_USER)
.build();
정적 팩토리 메서드 방식
User user = User.createLocalUser(email, encodedPassword, nickname);
@Builder는 null이 들어올 수 있다는 단점이 있어요. 하지만 SignUpRequestDto에 @NotBlank 검증이 있어서 서비스까지 왔다는 건 이미 검증을 통과한 거예요. 그래서 @Builder를 그대로 써도 안전해요.
실무에서는 @Builder를 더 많이 사용해요.
다음 포스팅에서는 AuthController를 구현해서 실제 회원가입 API를 완성하는 과정을 다룰 예정이에요.