Springboot/SpringSecurity

Spring Security 3.x.x (회원가입)

코철이 2024. 2. 14. 14:35

이제 회원가입 기능 구현을 해보겠다.

 

Entity 생성

 

@Entity
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String userName;		//이름

    @Column(nullable = false, updatable = false, unique = true)
    private String userId;			//아이디

    @Column(nullable = false)
    private String password;		//암호

    @Column(nullable = false)
    @Email	
    private String email;			//이메일

    @Column(nullable = false)
    private String phoneNumber;		//전화번호

    @Column(columnDefinition = "integer default 0")
    private Integer admin;			//권한(0,1)

 

나는 회원 정보를 저장할 Member라는 이름의 Entity클래스를 하나 만들어주었다.

userId는 중복되지 않도록 unique = true를 넣어주었다.

 


 

MemberDTO 클래스 생성

아래와 같이  MemberDTO클래스를 하나 만들어준다. validation(유효성 검증)을 위해 설정한 @NotBlank, @Pattern, @Email, @Size 어노테이션이 있지만 이 부분은 밑에서 좀 더 살펴보겠다.

 

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class MemberDTO {

    private Long id;
    @NotBlank(message = "이름을 입력해주세요.")
    private String userName;			//이름
    @NotBlank(message = "아이디를 입력해주세요.")
    @Size(min = 4, max = 16,message = "4~16자리의 아이디를 입력하세요.")
    private String userId;				//아이디
    @NotBlank(message = "암호를 입력해주세요.")
    @Size(min = 6, max = 14, message = "6~14자리의 암호를 입력하세요.")
    private String password;			//비밀번호
    @Email
    private String email;				//이메일
    @Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$", message = "전화번호 양식을 지켜주세요. 01x-xxx(x)-xxxx")
    private String phoneNumber;			//전화번호
    private Integer admin;				//권한(0,1)

 


MemberRepositoy 생성

 

MemberRepositoy 인터페이스에 JpaRepository<Member, Long>을 이용하여 JpaRepository를 상속 받게 한다.

<Member, Long> 은 Member엔티티 클래스와 Member엔티티 클래스의 기본키 PK(@Id)로 등록된 타입을 써준다.

JpaRepository를 상속 받게 되면 MemberRepository를 이용하여 서비스 클래스에서 간편한 기능들을 여러가지 사용할 수 있게 된다. 

예를 들어, SQL쿼리문을 이용하여 member테이블에서 회원번호가 33번인 회원의 정보를 조회하고 싶을 때, 

select * from member where id = 33; 을 입력하여 id(회원번호)가 33번인 회원의 정보를 불러올 수 있고, 그 쿼리문을 직접 등록하고 이용하게 해줘야 하지만, JPA를 이용하는 경우에는 서비스 클래스에서 findById를 입력해서 쿼리문 등록없이 자동으로 실행 시켜준다. 더 좋은점은 Oracle DB, MySql 등의 여러가지 DB가 있을 때 사용하는 SQL문이 조금씩 차이가 있는데 JPA를 이용하면 어떤 DB로 바꾸든 SQL문의 수정없이 서비스가 정상적으로 작동한다.

public interface MemberRepository extends JpaRepository<Member, Long> {

    
}

 

일단 위의 MemberRepository를 JpaRepository에 상속 받게 하고, MemberService를 작성해본다.

 


MemberService 생성

 

@Service
@RequiredArgsConstructor
public class MemberService {

    private final MemberRepository memberRepository;

    public void regMember(MemberDTO dto){

        Member member = Member.builder()
                .userId(dto.getUserId())
                .password(dto.getPassword())
                .email(dto.getEmail())
                .phoneNumber(dto.getPhoneNumber())
                .userName(dto.getUserName())
                .build();

        memberRepository.save(member);

    }
}


MemberService 클래스는 MemberRepository 객체를 이용해서 작성해준다.

regMember메서드를 이용해서 MemberDTO값을 받아드리고 Member엔티티 클래스의 builder를 이용하여 dto에서 받아온 값을 Member엔티티에 넣고 db에 저장해준다.

이 때 memberRepository.save(member)를 써주는데, save() 메서드 또한 JpaRepository에 있는 기능이다.

save()메서드는 일단 호출되면 select 문을 먼저 날려서, 입력된 값이 DB에 존재하는지 안하는지를 확인한다.

DB에 존재하지 않는 값이 입력되었다면 DB에 insert를 날려주고,

입력된 값이 DB에 이미 존재한다면 insert가아닌 update문을 날려서 수정을 알아서 한다.

 

save() 메서드의 경우 먼저 무조건 Select 문을 날려서 입력된 값이 DB에 존재하는지 안하는지 먼저 확인 함을 알아두자.

 

MemberService를 좀 더 보자면, 지금 상황에서는 password를 "123456"으로 입력했을 때 DB에 곧이 곧대로 "123456"으로 저장이 되어 보안에 좋지 않다.

이런 경우 PasswordEncoder를 이용해서 보안을 신경 써줘야한다.

 


 

SecurityConfig로 다시 돌아와서 아래의 PasswordEncoder메서드를 추가해준다.

 

@Configuration
@EnableWebSecurity
public class SecurityConfig {

		.....filterChain생략......

	@Bean
	PasswordEncoder passwordEncoder(){
    	return new BCryptPasswordEncoder();
	}
    
}

 

passwordEncoder()메서드 안에 return new BcryptPasswordEncoder()를 반환해준다.


@Bean으로 등록한 passwordEncoder를 MemberService로 다시 돌아가서 사용해준다.

 

@Service
@RequiredArgsConstructor
public class MemberService {

    private final MemberRepository memberRepository;

    private final PasswordEncoder passwordEncoder;		//bean으로 등록한 passwordEncoder객체를 주입받는다.
    
    public void regMember(MemberDTO dto){

        Member member = Member.builder()
                .userId(dto.getUserId())
                .password(passwordEncoder.encode(dto.getPassword()))	
                //dto.getPassword로 받아온 password를 암호화하여 Member에 저장한다.
                .email(dto.getEmail())
                .phoneNumber(dto.getPhoneNumber())
                .userName(dto.getUserName())
                .build();

        memberRepository.save(member);

    }

 

위 처럼 SecurityConfig클래스에서 bean으로 등록해준 PasswordEncoder 객체를 주입받아서 입력받은 패스워드를 passwordEncoder.encode() 를 이용하여 암호화해준다.

 

이렇게 암호화를 해주었을 때와 안해주었을 때 DB에 저장되는 부분이 아래와 같이 달라진다.

 

id가 10,12,15인 경우 password 컬럼을 보면 단순하게 입력한 값으로 저장되었던 것을 확인할 수 있다.

그러나 16,17번 id의 경우에는 PasswordEncoder객체를 이용하여 15번과 같은 비밀번호 "awdaw"를 입력해도 자동으로 암호화하여 DB에서 알 수 없게 저장이 되어진다.

그렇다고 로그인 할 때도 DB에 암호화되어 저장되어진 값을 입력해야 하는 것이 아닌 회원 가입할 때 입력했던 "awdaw"를 패스워드로 입력해서 로그인 하면 된다.

 


이제 Controller를 구현해보자

 

@Controller
@RequestMapping("/member")
@RequiredArgsConstructor
public class MemberController {

    private final MemberService memberService;

    @GetMapping("/regMember")
    public String getRegMember(MemberDTO dto) {
        return "/member/regMember";
    }

 

Controller를 구현해보았다.

해당 클래스가 Controller클래스임을 명시하기위해 @Controller 어노테이션을 써주었고,

해당 url경로가 /member로 시작하게 하기 위해 @RequestMapping("/member")를 써주었다.

 

MemberService 객체를 이용하기 때문에 private final MemberService memberSerivce;를 써주었고, 이를 정상적으로 사용하기 위한 @RequiredArgsConstructor 어노테이션을 써주었다.

 

이제 회원 가입 페이지로 접속하기 위한 @GetMapping("/regMember")를 써준다.

@GetMapping("/regMember")
public String getRegMember(MemberDTO dto) {
    return "/member/regMember";
}

 

URL 경로를 localhost:8080/member/regMember로 접속하면 위의 메서드를 실행시킨다.

return "/member/regMember"라고 쓰여있는데, 이 메서드를 실행시키면 아래의 regMember.html이 웹페이지에 실행된다.

그리고 getRegMember 메서드의 매개변수로 MemberDTO dto가 있는데 이 부분은 유효성 검사를 하기위해 써주었다. 이 부분은 다음 편인 화면 구현할 때 설명을 하겠다.