5. Spring Boot 회원가입 서비스 구현 (feat. AuthService)

yoon·2026년 3월 30일

스프링 인증/인가

목록 보기
4/6

개요

회원가입 비즈니스 로직을 담당하는 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);
    }
}

코드 상세 설명

1. @Transactional

DB 작업 중 에러가 나면 자동으로 롤백해줘요.

비밀번호 암호화 중 에러 발생
    ↓
@Transactional → 자동 롤백
    ↓
DB에 잘못된 데이터 저장 안 됨 ✅

DB에 데이터를 쓰는 로직에는 항상 붙여요.

2. 이메일 중복 체크

if (userRepository.existsByEmail(request.getEmail())) {
    throw new CustomException(UserErrorCode.DUPLICATE_EMAIL);
}

existsByEmail() → DB에 해당 이메일이 있는지 체크해요.

findByEmail()로도 중복 체크할 수 있지만, existsByEmail()은 존재 여부만 확인하기 때문에 더 가볍고 의도가 명확해요.

3. 비밀번호 암호화

String encodedPassword = passwordEncoder.encode(request.getPassword());

비밀번호를 평문으로 저장하면 DB가 털렸을 때 비밀번호가 그대로 노출돼요. BCryptPasswordEncoder로 암호화해서 저장해요.

"1234" → encode() → "$2a$10$abc123..."  암호화 ✅
"$2a$10$abc123..." → decode() → ??? 복호화 불가 ✅

4. User 엔티티 생성

User user = User.builder()
        .email(request.getEmail())
        .password(encodedPassword)  // 암호화된 비밀번호 저장
        .nickname(request.getNickname())
        .role(Role.ROLE_USER)       // 기본 권한 설정
        .provider(Provider.LOCAL)   // 이메일 로그인
        .build();

roleprovider는 서비스 레이어에서 직접 설정해요. 클라이언트가 임의로 설정할 수 없도록 하기 위해서예요.

5. void 반환

회원가입은 성공 여부만 알려주면 돼요. 따로 반환할 데이터가 없어서 void로 했어요.


왜 컨트롤러에서 직접 저장 안 하냐

Controller → 요청/응답 처리만
Service    → 비즈니스 로직 담당 ← 여기서 처리
Repository → DB 접근만

역할을 분리해야 코드가 깔끔하고 테스트하기 쉬워요. 실무에서 항상 지키는 패턴이에요.


@Builder vs 정적 팩토리 메서드

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를 완성하는 과정을 다룰 예정이에요.

0개의 댓글