Project/Todo

[Spring Boot] Member 서비스 구현 (1)

lakelight 2022. 7. 9. 19:29
728x90
반응형

domain/Member.java

package hooyn.todo.domain;

import lombok.*;
import org.hibernate.annotations.GenericGenerator;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.UUID;

@Entity //bean 등록
@Getter @Setter //lombok의 getter와 setter를 만들어주는 annotation
@NoArgsConstructor(access = AccessLevel.PROTECTED) //무분별한 객체 생성을 막기 위해
public class Member {

    @Id @GeneratedValue(generator = "uuid2")
    @GenericGenerator(name = "uuid2", strategy = "uuid2")
    @Column(columnDefinition = "BINARY(16)")
    private UUID uuid; //UUID를 랜덤으로 받아서 자동 설정

    private String userNM; //사용자 이름
    private String userID; //사용자 ID
    private String userPW; //사용자 PW

    /*
     Todo mapping //추후에 Todo 도메인 제작 후 매핑 예정
     Memo mapping //추후에 Memo 도메인 제작 후 매핑 예정
    */

    public Member(String userNM, String userID, String userPW) { //생성자
        this.userNM = userNM;
        this.userID = userID;
        this.userPW = userPW;
    }

    /**
     * 비밀번호 암호화
     */
    public void encodePassword(PasswordEncoder passwordEncoder){
        this.userPW = passwordEncoder.encode(userPW); //비밀번호를 암호화 하기 위한 로직
    }

    /**
     * 비밀번호 확인 (복호화)
     */
    public boolean matchPassword(PasswordEncoder passwordEncoder, String password){
        return passwordEncoder.matches(password, getUserPW()); //비밀번호를 확인하기 위한 로직
    }
}

/repository/MemberRepository.java

package hooyn.todo.repository;

import com.querydsl.jpa.impl.JPAQueryFactory;
import hooyn.todo.domain.Member;
import hooyn.todo.domain.QMember;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import javax.persistence.EntityManager;
import java.util.UUID;

@Repository
@RequiredArgsConstructor //private final에 생성자를 통한 의존성 주입을 해준다.
public class MemberRepository {

    private final EntityManager em;
    private final JPAQueryFactory queryFactory;

    /* [회원 정보 저장] */
    public UUID save(Member member){
        em.persist(member);
        return member.getUuid();
    }

    /* [회원 엔티티 조회] */
    public Member findOne(String uuid){
        return em.find(Member.class, UUID.fromString(uuid));
    }

    /* [사용자 아이디에 따른 회원 조회] */
    public Member findByUserId(String userID){ //queryDSL을 사용
        return queryFactory //queryDSL 사용
                .selectFrom(QMember.member)
                .where(QMember.member.userID.eq(userID))
                .fetchOne();
    }
}

/service/MemberService.java

package hooyn.todo.service;

import hooyn.todo.domain.Member;
import hooyn.todo.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
@RequiredArgsConstructor
public class MemberService {

    private final MemberRepository memberRepository;
    private final PasswordEncoder passwordEncoder;

    /**
     * 회원 가입
     */
    @Transactional
    public UUID join(String userNM, String userID, String userPW){
        Member member = new Member(userNM, userID, userPW);
        member.encodePassword(passwordEncoder);
        return memberRepository.save(member);
    }

    /**
     * 아이디에 따른 회원 조회
     */
    @Transactional(readOnly = true)
    public Member findUser(String userID){
        return memberRepository.findByUserId(userID);
    }

    /**
     * 아이디 중복 확인
     */
    @Transactional(readOnly = true)
    public boolean checkDuplicatedID(String userID){
        Member member = memberRepository.findByUserId(userID);
        if(member!=null){
            return false; //아이디로 검색되는 회원이 있다면 중복(false)
        } else return true;
    }

    /**
     * 비밀번호 제약조건 확인
     */
    public boolean checkPasswordConstraint(String userPW){
        Pattern pattern = Pattern.compile("^(?=.*[a-zA-Z])(?=.*\\d).{8,20}$");
        return pattern.matcher(userPW).matches();
    }
}

/api/request/SignInRequest.java -> MemberController에서 사용하기 위해 생성

package hooyn.todo.api.request;

import lombok.Getter;

@Getter
public class SignInRequest { //로그인 할 때 필요한 RequestBody값 설정
    private String userID;
    private String userPW;
}

/api/request/SignupRequest.java -> MemberController에서 사용하기 위해 생성

 

/api/response/Resp

package hooyn.todo.api.request;

import lombok.Getter;

@Getter
public class SignUpRequest { //회원가입 할 때 필요한 RequestBody값 설정
    private String userNM;
    private String userID;
    private String userPW;
}

onse.java -> 전체 응답을 보내기 위해 생성

package hooyn.todo.api.response;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Response<T> {
    private boolean isSuccess; //통신 성공 여부
    private int statusCode; //통신 상태 코드
    private T data; //응답 데이터
    private String message; //응답 메시지
}

/api/controller/MemberController

package hooyn.todo.api.controller;

import hooyn.todo.api.request.SignInRequest;
import hooyn.todo.api.request.SignUpRequest;
import hooyn.todo.api.response.Response;
import hooyn.todo.domain.Member;
import hooyn.todo.service.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
@RequiredArgsConstructor
public class MemberController {
    private final MemberService memberService;
    private final PasswordEncoder passwordEncoder;

    @PostMapping("/member/signup")
    public Response signUp(@RequestBody SignUpRequest request){
        boolean checkID = memberService.checkDuplicatedID(request.getUserID());
        if(checkID){
            if(memberService.checkPasswordConstraint(request.getUserPW())){
                UUID data = memberService.join(request.getUserNM(), request.getUserID(), request.getUserPW());
                return new Response(true, HttpStatus.OK.value(), data, "회원가입이 정상적으로 처리되었습니다.");
            } else {
                // 302 에러
                return new Response(false, HttpStatus.FOUND.value(), null, "비밀번호는 영문과 숫자포함 8-20자리입니다.");
            }
        } else {
            // 301 에러
            return new Response(false, HttpStatus.MOVED_PERMANENTLY.value(), null, "이미 사용중인 아이디 입니다.");
        }
    }

    @PostMapping("/member/signin")
    public Response signUp(@RequestBody SignInRequest request){
        Member member = memberService.findUser(request.getUserID());
        if(member!=null){
            if(member.matchPassword(passwordEncoder, request.getUserPW())){
                return new Response(true, HttpStatus.OK.value(), member.getUuid(), "로그인 되었습니다.");
            } else {
                // 302 에러
                return new Response(false, HttpStatus.FOUND.value(), null, "비밀번호가 일치하지 않습니다.");
            }
        } else {
            // 301 에러
            return new Response(false, HttpStatus.MOVED_PERMANENTLY.value(), null, "등록되지 않은 아이디입니다.");
        }
    }
}

*** QueryDSL을 사용하기 위한 추가 설정 ***

1. Bean 등록

package gachonUniv.dormitory;

import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import javax.persistence.EntityManager;

@SpringBootApplication
public class DormitoryApplication {

	public static void main(String[] args) {
		SpringApplication.run(DormitoryApplication.class, args);
	}

	//queryDSL을 사용하기 위해 JPAQueryFactory 빈 등록
	@Bean
	JPAQueryFactory jpaQueryFactory(EntityManager em){
		return new JPAQueryFactory(em);
	}
}

 

2. QueryDSL compile하여 Q객체 생성 (새로운 도메인 클래스를 생성할 때 실행시켜주어 Q객체를 생성해야합니다.)

QMember가 생긴것을 확인 할 수 있습니다. 이 상태가 되면 QueryDSL을 통해 Query가 가능합니다.

결론

Member에 해당하는 API를 설계해보았습니다. 다음 포스트에서는 테스트를 작성해보겠습니다.
오늘 구현하면서 배운 점은 자바 정규 표현식을 배울 수 있었습니다.

비밀번호 제약조건 로직을 자바 정규 표현식을 통해 구현을 했습니다.

https://hbase.tistory.com/160

 

[Java] 정규표현식 사용법 및 예제 - Pattern, Matcher

자바에서 정규표현식(Regular Expression)'을 사용해보자. 1. 정규표현식(Regular Expression) 정규표현식 혹은 정규식은 특정한 규칙을 가진 문자열의 집합을 표현하는데 사용되는 언어다. 정규 표현식은

hbase.tistory.com

 

728x90
반응형