[Spring] Spring Security로 회원가입 구현하기

icebox127·2026년 1월 15일

TO:DAY 서버

목록 보기
3/8

Spring Security란?

Spring Security는 Spring 기반 애플리케이션의 보안을 담당하는 강력한 프레임워크
주로 두 가지 핵심 기능인 인증권한 부여를 제공한다.

  • 인증(Authentication)
    내가 누구인지 증명. 보통 아이디와 비밀번호로 신원을 검증한다.
  • 인가(Authorization)
    특정 리소스에 접근할 권한이 있는지 확인
    (예: 관리자 계정, 일반 계정)

구현 과정

1. 의존성 추가

// Spring Security
implementation 'org.springframework.boot:spring-boot-starter-security'

2. Security 설정

Spring Security를 커스텀 설정하기 위해 SecurityConfig를 추가한다.

@EnableWebSecurity
@Configuration
public class SecurityConfig {

    private final String[] allowUris = {
			"/swagger-ui/**",
            "/swagger-resources/**",
            "/v3/api-docs/**",
            "/auth/**"
    };

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(requests -> requests
                        .requestMatchers("/admin/**").hasRole("ADMIN")
                        .anyRequest().authenticated()
                )
                // 폼로그인 비활성화
                .formLogin(AbstractHttpConfigurer::disable)
                .logout(logout -> logout
                        .logoutUrl("/logout")
                        .logoutSuccessUrl("/login?logout")
                        .permitAll()
                );

        return http.build();
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
  • @EnableWebSecurity: Spring Security 활성화 -> 기본 설정보다 이 커스텀 설정이 더 우선 적용됨
  • allowUris: 인증 없이도 접근 허용할 경로. (주로 회원가입/로그인 api 경로, 스웨거 경로, 그 외의 문서등)

SecurityFilterChain

  • authorizeHttpRequests()
    • requestMatchers() 메소드를 사용하여 특정 URL 패턴에 대한 접근 권한을 설정한다.
    • permitAll()은 인증 없이 접근 가능한 경로를 지정한다.
    • hasRole("ADMIN")은 'ADMIN' 역할을 가진 사용자만 접근 가능하도록 제한한다.
    • anyRequest().authenticated()는 그 외 모든 요청에 대해 인증을 요구한다.

내가 진행하는 프로젝트의 경우 운영자 등 별도의 권한이 부여된 계정이 없어 role 부분은 삭제했다.

  • formLogin
    • defaultSuccessUrl("/"): 성공 시 리다이렉트할 경로
    • 하지만 프론트를 따로 개발한다면 프론트 측에서 페이지 전환 등을 설정할 것이므로 필요 없는 부분이다.
    • AbstractHttpConfigurer::disable로 해두면 됨
  • logout
    • 참고 자료 따라서 작성한거긴 한데 로그아웃도 프론트 측에서 토큰 삭제하는 식으로 구현하면 굳이 쓸 필요가 없을듯하다.
  • PasswordEncoder: db에 넣을 비밀번호를 암호화하기 위한 클래스 빈 등록

3. 컨트롤러 작성

	@PostMapping("/auth/signup/email")
    public ApiResponse<Void> emailSignup(@Valid @RequestBody MemberReqDto.SignupDto dto){
        memberService.emailSignup(dto);
        return ApiResponse.success(null);
    }

4. 서비스 작성

 	@Transactional
    @Override
    public void emailSignup(MemberReqDto.SignupDto dto) {
        String email = dto.getEmail();
        checkEmailDuplicate(email);
        if (!emailCodeRepository.existsByEmailAndVerifiedIsTrue(email)){
            throw new AuthException(AuthErrorCode.INVALID_EMAIL);
        }
        while(true){
            String nickname = nicknameGenerator.generate();
            String salt = passwordEncoder.encode(dto.getPassword());
            try {
                Member member = MemberConverter.toMember(dto, salt, nickname);
                memberRepository.save(member);
                return;
            } catch (DataIntegrityViolationException e) {
                //재시도
            }
        }
    }
  • @Transactional: db에서 조회를 하기 때문에 트랜잭션 처리를 했다.

  • 가입 전 이메일 중복검사

  • 랜덤 닉네임을 가입 시 부여해야해서, NicknameGenerator 클래스를 구현하였다. 다만, 닉네임 중복도 확인해야 하는데 여러 사용자가 동시에 회원가입 중에 정말 우연히도 닉네임이 겹치게 부여되어 에러가 나는 상황을 방지하고자 가입 로직에 while문을 사용하여 혹시라도 중복된 닉네임이면 여러 번 시도하도록 구현함.
    하지만 보통은 그냥 memberRepository.save(member);만 쓰면 된다.

  • 비밀번호 암호화: 레인보우 테이블 공격을 방지하기 위해, BCrypt를 사용하여 내부적으로 랜덤 솔트를 포함한 단방향 해시로 비밀번호를 저장

실행 결과

db에 암호화된 비밀번호가 저장되었다.

profile
감자의 공부기록🥔

0개의 댓글