[회원관리] 회원가입

2023. 1. 17. 01:08프로젝트/라이프 챌린지

728x90
SMALL

앞서 Spring Security 와 JWT에 대한 설정이 1차적으로 완료되었다.

회원가입 기능부터 차근차근 완성해나가보자.

 


MemberController

import com.example.lifechallenge.controller.request.RegisterRequestDto;
import com.example.lifechallenge.controller.response.ResponseBody;
import com.example.lifechallenge.service.MemberService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RequiredArgsConstructor
@RequestMapping("/lc")
@RestController
public class MemberController {

    private final MemberService memberService;

    // 회원가입
    @PostMapping("/register")
    public ResponseEntity<ResponseBody> memberRegister(@RequestBody RegisterRequestDto registerRequestDto){
        log.info("회원가입 - 아이디 : {}, 비밀번호 : {}, 재확인 비밀번호 : {}, 닉네임 : {}, 주소 : {}",
                registerRequestDto.getMember_id(),
                registerRequestDto.getPassword(),
                registerRequestDto.getPassword_recheck(),
                registerRequestDto.getNickname(),
                registerRequestDto.getAddress());

        return memberService.memberRegister(registerRequestDto);
    }

}

컨트롤러를 생성하고, 회원가입 api를 만들어준다..

  • @RequestMapping 을 통해 "/lc" 공통된 주소를 지정해준다.
  • @RestController 를 지정해줌으로써 Json 형식으로 반환값을 받게끔 지정해주었다.
  • 회원가입은 정보가 입력되어 저장이 되어야하기 때문에 @PostMapping 메소드 형식으로 구현하였다.
  • 반환타입을 ResponseEntity<ResponseBody> 으로 지정해주었다.
    • ResponseEntity는 제네릭타입으로 반환값을 일반화시켜 반환할 수 있기 때문에 선택하였다.
    • ResponseBody는 제네릭 클래스로서 요청이 정상적으로 완료될 경우와 에러가 발생했을 경우 모두 반환받을 수 있도록 직접 만들어주었다.
  • @RequestBody 를 통해 json 형식으로 Dto 객체에 회원가입에 필요한 정보들을 한번에 담아 Service 단으로 전달해주었다.
  • @Slf4j 로 log.info를 통해 입력된 회원가입 정보들이 정상적으로 들어오는지 로그로 찍어 확인해보고자 사용하였다.

 

 

ResponseBody

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@AllArgsConstructor
@RequiredArgsConstructor
public class ResponseBody<T>{
    private final String statusCode;
    private final String status;
    private final T data;
}

api 요청이 무사히 완료되거나 완료되지 않고 에러가 발생했을 경우 제네릭 타입 클래스인 ResponseBody 클래스에 저장되어 반환된다.

따라서 최종적으로 반환되는 데이터의 허브라고 생각하면 될 것 같다.

저장되어지는 필드 값은

  • StatusCode : 커스텀하여 직접 만든 상태 코드로서, 말 그대로 상태에 따른 코드 넘버를 가지는 필드이다.
  • Status : 상태 메세지 이다. 상태에 따른 메세지 문구를 가지는 필드이다.
  • data : 반환값이 저장되는 제네릭 타입 필드이다. 배열이든, 숫자형이든, 문자열이든 아무 반환 데이터가 들어갈 수있다.

 

 

RegisterRequestDto

import lombok.Builder;
import lombok.Getter;

import javax.validation.constraints.*;

@Getter
@Builder
public class RegisterRequestDto {
    @NotBlank(message = "아이디는 공백을 포함할 수 없습니다.")
    @Email(regexp = "^[a-zA-Z0-9]+@[a-zA-Z]+.[a-z]+${4,12}$", message = "이메일 형식에 맞지 않습니다.")
    private String member_id;

    @NotNull(message = "비밀번호는 공백일 수 없습니다.")
    @Pattern(regexp="(?=.*[0-9])(?=.*[a-zA-Z])(?=.*\\W)(?=\\S+$).{7,20}",
            message = "비밀번호는 영어 대/소문자, 숫자, 특수기호가 최소한 1개씩은 포함이 되어있어야 하며, 7~20글자 이내여야 합니다.")
    private String password;

    @NotNull(message = "비밀번호는 공백일 수 없습니다.")
    private String password_recheck;

    @NotBlank(message = "닉네임은 공백을 포함할 수 없습니다.")
    @Size(max = 20, min = 4, message = "알맞은 길이의 닉네임이 아닙니다.")
    private String nickname;

    @NotNull(message = "주소는 공백일 수 없습니다.")
    private String address;
}

회원가입할 때 입력받을 정보들이 한 곳에 저장되어있는 Dto 클래스이다.

  • member_id : 아이디
    • @NotBlank로 공백포함할 수 없게끔 처리
    • @Email로 아이디는 이메일 형식을 따라야 함을 지정
  • password : 비밀번호
    • @NotNull로 공백일 수 없음을 지정
    • @Pattern으로 영어 대/소문자, 숫자, 특수기호가 최소 1개씩 포함 및 7~20 글자 사이로 지정
  • password_recheck : 재확인 비밀번호
    • @NotNull로 공백일 수 없음을 지정
  • nickname : 닉네임
    • @NotBlank로 공백을 포함할 수 없음을 지정
    • @Size로 4~20 글자 수여야 함을 명시
  • address : 주소
    • @NotNull로 주소는 공백일 수 없음을 명시

Dto를 json 화하여 Service에 넘길 예정.

 

 

 

StatusCode

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Getter
public enum StatusCode {

    OK(200, "정상 처리 완료"),

    DUPLICATE_ACCOUNT(452, "이미 존재하는 계정입니다."),
    NOT_MATCH_PASSWORD(453, "비밀번호가 재확인 비밀번호와 일치하는지 확인해주십시오.");

    private final int statusCode;
    private final String status;

}

api 요청 후 정상적인 응답을 반환 받거나 잘못된 요청으로 정상적이지 않은 응답을 에러 처리해서 반환을 받거나 모두 각자의 상태코드와 상태메세지를 부여받을 수 잇게끔 enum 타입의 StatusCode를 생성하였다.

  • 정상적인 응답값을 반환할 경우 200번 OK 상태코드와 OK 상태 메세지가 반환값과 함께 반환될 것이다.
  • 클라이언트의 잘못된 요청으로 인해 정상적이지 않은 응답값을 반환할 경우 커스텀하여 만든 400번대의 BAD_REQUEST 상태코드와 상태 메세지가 반환값과 함께 반환되거나 반환값은 null인 채로 반환될 것이다.

지금은 초기 단계라서 커스텀한 상태코드들이 3개 정도밖에 존재하지 않지만 이후 개발을 진행하면서 점차 늘려나갈 예정이다.

 

 

 

MemberService

import com.example.lifechallenge.controller.request.RegisterRequestDto;
import com.example.lifechallenge.controller.response.ResponseBody;
import com.example.lifechallenge.domain.Member;
import com.example.lifechallenge.exception.StatusCode;
import com.example.lifechallenge.repository.MemberRepository;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import static com.example.lifechallenge.domain.QMember.member;

@RequiredArgsConstructor
@Service
public class MemberService {

    private final JPAQueryFactory queryFactory;
    private final MemberRepository memberRepository;
    private final PasswordEncoder passwordEncoder;

    // 회원가입
    public ResponseEntity<ResponseBody> memberRegister(RegisterRequestDto registerRequestDto) {
        // 아이디 기준으로 이미 존재하는 계정이라면 중복된 계정 에러처리
        if(!(queryFactory
                .selectFrom(member)
                .where(member.member_id.eq(registerRequestDto.getMember_id()))
                .fetchOne() == null)){
            return new ResponseEntity<>(new ResponseBody(StatusCode.DUPLICATE_ACCOUNT.getStatusCode(), StatusCode.DUPLICATE_ACCOUNT.getStatus(), null), HttpStatus.BAD_REQUEST);
        }

        // 비밀번호와 재확인용 비밀번호의 값이 다를 경우 에러처리
        if(!registerRequestDto.getPassword().equals(registerRequestDto.getPassword_recheck())){
            return new ResponseEntity<>(new ResponseBody(StatusCode.NOT_MATCH_PASSWORD.getStatusCode(), StatusCode.DUPLICATE_ACCOUNT.getStatus(), null), HttpStatus.BAD_REQUEST);
        }

        // 회원가입 정보 build
        Member member = Member.builder()
                .member_id(registerRequestDto.getMember_id())
                .password(passwordEncoder.encode(registerRequestDto.getPassword()))
                .nickname(registerRequestDto.getNickname())
                .address(registerRequestDto.getAddress())
                .build();

        // 회원가입
        memberRepository.save(member);

        return new ResponseEntity<>(new ResponseBody(StatusCode.OK.getStatusCode(),StatusCode.OK.getStatus(), "회원가입이 완료되었습니다."), HttpStatus.OK);
    }

}

컨트롤러에서 넘겨받은 Dto 정보들을 가지고 회원가입 처리를 한다.

  • 중복된 계정인지, 비밀번호와 재확인용 비밀번호가 일치하는지 확인한 후, 만들어놓은 ResponseBody에 StatusCode와 반환값을 포함하여 ResponseEntity로 반환 (정상적인 응답이면 200번 코드, 아니라면 400번대 코드)
  • api 를 활용하여 입력받은 값들 중 address 값은 api를 활용하여 공식 주소로 변환시킨 다음 Member 엔티티에 build 한다.
  • Repository를 통해 저장 (회원가입)
  • JPAQueryFactory를 의존성 주입받아 Querydsl 을 사용하여 DB에 대한 동작을 수행한다.
  • Querydsl 을 사용할 때 DB에 직접적으로 반영하여 수행하는 것이 아니라 EntityManager를 인자값으로 받기 때문에 영속성 컨텍스트를 활용하여 DB 동작을 수행한다. Querydsl 은 사용하려면 Q객체라는 것을 엔티티마다 생성을 한 후 1차적으로 수행 테스트를 하고 (1차 캐시) DB에 반영한다.
  • 비밀번호를 넣을 때 PasswordEncoder를 통하여 인코딩하여 넣는다.

주소에 대한 정보는 지금은 일단 사용자가 직접 입력한 정보를 그대로 넣어주지만 나중에는 공용 api를 활용하여 공식 도로주소명으로 받아서 저장하게 될 것이다.

 

 

 

 

이제 포스트맨으로 정상적으로 결과값이 나오는지 확인해보도록 하자.

  • 컨트롤러 Method 주소 /lc/register 로 매핑된다.
  • @RequestBody로 RegisterRequestDto객체를 받기 때문에 RegisterRequestDto에 들어갈 정보들을 JSON 형식으로 넣어준다.
  • POST 형식으로 send 해주게 되면, 정상적으로 요청이 완료가 되었을 시에 위의 사진처럼 statuscode는 200, status 는 "정상 처리 완료", data 에는 "회원가입이 완료되었습니다." 라고 뽑힐 것이다. 
  • 포스트맨에서 정상적으로 처리가 완료되었다면 DB 상에 회원정보가 제대로 들어갔는지 확인해준다.

 

 

일단 회원가입에 대한 1차 구현은 완료되었다.

앞서 말했다싶이, 주소에 관한 부분은 이후 공용 api를 활용하여 공식적인 도로주소명을 받아서 넣어주고 싶기 때문에 나중에 수정이 필요할 것이다.

 

 

 

728x90
반응형
LIST

'프로젝트 > 라이프 챌린지' 카테고리의 다른 글

[회원관리] 회원탈퇴  (0) 2023.01.21
[회원관리] 로그아웃  (0) 2023.01.21
[회원관리] 로그인  (0) 2023.01.20
[회원관리] JWT 세팅  (0) 2023.01.16
라이프 챌린지 프로젝트 시작 계기  (0) 2023.01.14