쇼핑몰 만들기 프로젝트 - 회원 가입(최종!)

yeom yaloo·2023년 7월 3일
0

쇼핑몰

목록 보기
12/19
post-thumbnail

[회원가입과 연관된 ERD]

  • 해당 작업에 들어가기 앞서 회원 가입이라는 로직이 성공하면 회원이 증가하게되는데 이때 연관된 테이블들 역시 함께 DB에 추가해주는 작업을 진행하였습니다.

[API Server]

CommonMemberRepository - interface

public interface CommonMemberRepository {

    Member save(Member member);
}

1. Repository

MemberRepository - interface

public interface MemberRepository extends Repository<Member, Long>, CommonMemberRepository {

}

2.Service

MemberService - 회원 가입 서비스 로직

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Slf4j
public class MemberServiceImpl implements MemberService {
    private final MemberRoleRepository memberRoleRepository;
    private final RoleCommonRepository roleRepository;
    private final MembershipRepository membershipRepository;
    private final MembershipHistoryRepository membershipHistoryRepository;

    private final QuerydslMemberRepository querydslMemberRepository;
    private final MemberRepository memberRepository;


    /**
     * 회원등록 기능
     * 회원 가입 시 회원 등급(Membership)과 회원 권한(MemberRole)을 부여해줍니다.
     * @param createRequest DTO로 넘겨받은 정보를 가지고 연관된 테이블까지 전부 정보를 저장할 수 있도록 합니다.
     * @return 해당 entity를 넘겨주는 형식이 아닌 response DTO를 두어 반환할 수 있게 합니다.
     * */
    @Transactional
    @Override
    public MemberCreateResponse createMember(MemberCreateRequest createRequest) {

        //1. 입력받은 request dto에서 해당 중복 사항이 없다면 회원 생성, 저장(연관관계에 있는 데이터들을 모두 넣어주기) 작업 계속 진행
        checkExistMember(createRequest);


        //2. member 생성, 이때 OneToOne 관계로 둘을 같이 save 해준다.
        Membership membership = Membership.createMembership();
        Member member = createRequest.toEntity(membership);
        Member savedMember = memberRepository.save(member);

        membershipRepository.save(membership);


        MembershipHistory membershipHistory = createMembershipHistory(membership,savedMember);
        membershipHistoryRepository.save(membershipHistory);


        //1 = ROLE_ADMIN, 2 = ROLE_USER .... 이미 저장된 값을 가지고 작업한다.
        Long roleId = 2L;
        Role role = roleRepository.findByRoleId(roleId).orElseThrow(NotFoundMemberRoleException::new);
        MemberRole memberRole = createMemberRole(savedMember, roleId, role);

        memberRoleRepository.save(memberRole);

        MemberCreateResponse response = MemberCreateResponse.fromEntity(savedMember, role);
        log.info("dto = {}", createRequest.getPassword());


        return response;
    }


    private MembershipHistory createMembershipHistory(Membership membership, Member member) {
        //회원가입시에만 사용하는 멤버쉽 히스토리
        return MembershipHistory.builder()
                .updateTime(LocalDateTime.now())
                .previousPaidAmount(0L)
                .membership(membership)
                .member(member)
                .build();
    }

    private MemberRole createMemberRole(Member savedMember, Long roleId, Role roleMember) {
        //회원가입시에만 사용하는 회원권한
        return MemberRole.builder()
                .memberRolePk(new MemberRolePk(savedMember.getMemberId(), roleId))
                .member(savedMember)
                .role(roleMember)
                .build();
    }

    //회원 가입시 중복되는 id, nickname, emailAddress, phoneNumber 은 예외를 던져 처리하고 회원 가입을 하지 못하게 한다.
    private void checkExistMember(MemberCreateRequest createRequest){
        if(querydslMemberRepository.existMemberByNickname(createRequest.getNickname())){
            throw new AlreadyExistsMember();
        }

        if(querydslMemberRepository.existMemberByEmail(createRequest.getEmailAddress())){
            throw new AlreadyExistsMember();
        }
        if (querydslMemberRepository.existMemberByPhoneNumber(createRequest.getPhoneNumber())){
            throw new AlreadyExistsMember();
        }

        if (querydslMemberRepository.existMemberByLoginId(createRequest.getId())) {
            throw new AlreadyDeletedAddressException();
        }
    }
  • 해당 작업에는 연관된 테이블에도 회원 가입과 연관된 작업들을 모두 진행해주었다.

3. Controller

MemberRestController

	@PostMapping("/sign-up")
    @ResponseStatus(HttpStatus.CREATED)
    public ResponseDto<MemberCreateResponse> signUpMember(@Valid @RequestBody MemberCreateRequest createRequest) throws URISyntaxException {

        MemberCreateResponse response = memberService.createMember(createRequest);
        log.info("dto = {}", response);

        return ResponseDto.<MemberCreateResponse>builder()
                .status(HttpStatus.CREATED)
                .success(true)
                .data(response)
                .build();

    }
  • 해당 Url로 Post 요청을 보내면 회원 가입 작업을 진행한다.
  • 이때는 @RequestBody를 사용하고 있기 때문에 해당 DTO 객체에 Setter가 따로 필요하지 않는다.

4. DTO

회원가입 Request DTO


@NoArgsConstructor
@AllArgsConstructor
@Getter
@Builder
public class MemberCreateRequest {

    @NotBlank(message = "공백과 빈값은 허용하지 않습니다. 올바르게 입력해주세요.")
    @Size(min = 8, max = 15)
    @Pattern(regexp = "^[a-zA-Z0-9]{8,15}$", message ="아이디는 영문과 숫자로만 가능하며 숫자로 시작할 수 없습니다. 올바르게 입력해주세요.")
    private String id;

    @NotBlank(message = "공백과 빈값은 허용하지 않습니다. 올바르게 입력해주세요.")
    @Size(min = 2, max = 30)
    @Pattern(regexp = "^[가-힣ㄱ-ㅎa-zA-Z0-9._-]{2,15}$", message = "닉네임은 한글과 영문만 입력해주세요.")
    private String nickname;

    @Size(min = 2, max = 50)
    @NotBlank(message = "공백과 빈값은 허용하지 않습니다. 올바르게 입력해주세요.")
    private String name;

    @NotBlank(message = "공백과 빈값은 허용하지 않습니다. 올바르게 입력해주세요.")
    private String gender;

    @NotBlank(message = "공백과 빈값은 허용하지 않습니다. 올바르게 입력해주세요.")
    @Pattern(regexp = "^[0-9]{8}", message = "ex) 1999090 숫자로만 8글자 작성해주세요")
    private String birthday;

    @NotBlank
    private String password;

    @NotBlank(message = "공백과 빈값은 허용하지 않습니다. 올바르게 입력해주세요.")
    @Size(min = 11, max = 30)
    private String phoneNumber;

    @Email
    @NotBlank(message = "공백과 빈값은 허용하지 않습니다. 올바르게 입력해주세요.")
    @Size(max = 100)
    private String emailAddress;


    public Member toEntity(Membership membership){
        return Member.builder()
                .membership(membership)
                .id(id)
                .nickname(nickname)
                .name(name)
                .genderCoder(GenderCode.valueOf(gender))
                .birthday(birthday)
                .password(password)
                .phoneNumber(phoneNumber)
                .emailAddress(emailAddress)
                .memberCreatedAt(LocalDateTime.now())
                .isSocialMember(false)
                .isSoftDelete(false)
                .memberSoftDeletedAt(null)
                .isSleepAccount(false)
                .build();

    }
}
  • 해당 객체엔 setter 불필요(@RequestBody를 사용한 데이터 바인딩을 하기 때문에 메세지 컨버터가 setter 없이도 이를 대신 해줄것임)

회원가입 Response DTO(여긴 맘대로 갖고 올 정보만 돌려받아 사용)

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class MemberCreateResponse {
    private Long memberId;
    private String name;
    private String nickname;
    private String id;
    private String grade;
    private String role;

    /**
     * @param
     * @return 결과로 반환된 엔티티의 일부 데이터를 사용해서 Response DTO로 반환
     *         회원 가입 시 회원의 등급은 WHITE 등급으로 고정
     *         이 DTO의 경우엔 회원 가입 기능에서만 사용된다.
     *
     * */
    public static MemberCreateResponse fromEntity(Member member, Role role){
        return new MemberCreateResponse(
                member.getMemberId(),
                member.getName(),
                member.getNickname(),
                member.getId(),
                Grade.WHITE.getName(),
                role.getRoleType().getRoleName()
        );
    }
}

[Front Server]

1.회원가입 관련 뷰페이지

회원가입 폼(Form)

 <html lang="en" xmlns:th="http://www.thymeleaf.org">

<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="keywords" content="">
    <meta name="author" content="Codescandy">
    <title>yalooStore</title>
    <!-- Favicon icon-->
    <link rel="shortcut icon" type="image/x-icon" href="../assets/images/favicon/favicon.ico">

    <!-- Libs CSS -->
    <link rel="stylesheet" href="../assets/libs/bootstrap-icons/font/bootstrap-icons.css">

    <!-- Theme CSS -->
    <link rel="stylesheet" href="../assets/css/theme.min.css">
    <!-- Bootstrap CSS CDN -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet"
          integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We"
          crossorigin="anonymous">

    <!-- Bootstrap icons-->
    <!-- Core theme CSS (includes Bootstrap)-->
    <link th:href="@{../../static/css/styles.css}" rel="stylesheet"/>
    <link href="../assets/dist/css/bootstrap.min.css" rel="stylesheet">


    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js"
            integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p"
            crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js"
            integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF"
            crossorigin="anonymous"></script>

</head>


<body class="bg-light">
<script type="text/javascript" th:src="@{/js/member/signUp.js}"></script>

<div th:replace="~{common/fragments/header-nonbtn::fragment-header-nonbtn}"></div>

<!-- sign up -->
<div class="min-vh-100 d-flex align-items-center ">
     <!--style="background:url(../assets/images/register-img.jpg)no-repeat; background-size: cover;">-->
    <div class="container ">
        <div class="row">
            <div class="offset-lg-3 col-lg-6 col-12">
                <a href="../index.html" class="mb-4 d-flex justify-content-center"><img
                        src="../assets/images/logo.svg" alt=""></a>

                <div class="bg-white p-4 p-lg-8 rounded-3 shadow-lg p-3 mb-5 bg-white rounded">

                    <form class="signup-form"
                          id="signup-form"
                          action="/members/signup"
                          method="post"
                          th:object="${member}">

                        <h1 class="mb-2 text-dark text-center pb-2 h3">Sign up</h1>
                        <p class="mb-4 text-center">Please fill in this form to create account!</p>

                        <!--이름-->
                        <div class="mb-3">
                            <label for="name" class="form-label text-dark">Name </label>
                            <div class="row">
                                <div class="col-9">
                                    <input type="text" id="name" th:name="name" class="form-control border-1 col-9" placeholder="이름을 입력해주세요."
                                           pattern="^.*(?=.*[가-힣a-z])(?=^.{2,50}).*$"
                                           required="" autocomplete="off">
                                    <p th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="fieldError"> 이름은 한글로 2~50자까지만 허용합니다 올바르게 작성해주세요 </p>
                                </div>
                                <div class="col-3">
                                </div>
                            </div>

                        </div>

                        <!--이름-->
                        <div class="mb-3">
                            <label for="id" class="form-label text-dark">Login Id </label>
                            <div class="row">
                                <div class="col-9">
                                    <input type="text" id="id" th:name="id" class="form-control border-1" placeholder="회원 아이디를 입력해주세요."
                                           required="">
                                    <p th:if="${#fields.hasErrors('id')}" th:errors="*{id}" class="fieldError"> 회원 로그인 아이디는 영문과 하나 이상의 숫자의 조합으로 작성해주세요. </p>
                                </div>
                                <div class="col-3 d-flex justify-content-around">
                                    <button id="checkLoginIdBtn" class="btn btn-light" type="button" onclick="checkLoginId()">중복 확인</button>
                                </div>
                            </div>
                        </div>
                        <div class="mb-3 mb-4">

                            <label for="password" class="form-label text-dark">Password</label>
                            <div class="row">
                                <div class="col-9">
                                    <input type="password" id="password" th:name="password" class="form-control border-1"
                                           placeholder="Password" required="">
                                    <p th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="fieldError">비밀번호는 숫자와 영문 그리고 특수기호를 하나 포함하여야 합니다. 다시 작성해주세요 </p>

                                </div>
                                <div class="col-3">

                                </div>
                            </div>

                        </div>

                        <div class="mb-3 mb-4">
                            <label for="nickname" class="form-label text-dark">Nickname </label>
                            <div class="row">
                                <div class="col-9">
                                    <input type="text" id="nickname" th:name="nickname" class="form-control border-1"
                                           placeholder="사용하실 닉네임을 입력해주세요." required="">
                                    <p th:if="${#fields.hasErrors('nickname')}" th:errors="*{nickname}" class="fieldError"> 닉네임은 영문으로 시작해야하며 영문, 숫자, 언더스코어(_)만을 허용합니다. </p>
                                </div>
                                <div class="col-3 d-flex justify-content-around">
                                    <button id="checkNicknameBth" type="button" class="btn btn-light checkNicknameBth" onclick="checkNickname()">중복 확인</button>
                                </div>
                            </div>

                        </div>
                        <div class="mb-3 mb-4">
                            <label for="gender" class="form-label text-dark">Gender </label>
                            <div class="row g-2">
                                <div class="col-9">
                                    <select th:name="gender" class="form-select" id="gender" required>
                                        <option value="">성별</option>
                                        <option value="MALE">남성</option>
                                        <option value="FEMALE">여성</option>
                                    </select>
                                    <p th:if="${#fields.hasErrors('gender')}" th:errors="*{gender}" class="fieldError"> 성별을 입력해주세요. </p>

                                </div>
                            </div>

                        </div>
                        <div class="mb-3 mb-4">
                            <label for="email" class="form-label text-dark">Email </label>
                            <div class="row">
                                <div class="col-9">
                                    <input type="email" id="email" th:name="emailAddress" class="form-control border-1" placeholder="이메일 주소를 입력해주세요."
                                                          required="">
                                    <p th:if="${#fields.hasErrors('emailAddress')}" th:errors="*{emailAddress}" class="fieldError">이메일 형식에 맞게 작성해주세요.</p>

                                </div>
                                <div class="col-3 d-flex justify-content-around">
                                    <button class="btn btn-light" type="button" id="checkEmailBth" onclick="checkEmail()">중복 확인</button>
                                </div>
                            </div>
                        </div>

                        <div class="mb-3 mb-4">
                            <label for="birthday" class="form-label text-dark">Birthday </label>
                            <div class="row">
                                <div class="col-9">
                                    <input type="text" id="birthday" class="form-control border-1"
                                           th:name="birthday"
                                           placeholder="공백과 기호 없이 입력해주세요. ex)19960320" required="">
                                    <p th:if="${#fields.hasErrors('birthday')}" th:errors="*{birthday}" class="fieldError">공백과 하이픈(-)은 허용하지 않습니다. 다시 입력해주세요 </p>

                                </div>
                                <div class="col-3">

                                </div>
                            </div>

                        </div>

                        <div class="mb-3 mb-4">
                            <label for="phoneNumber" class="form-label text-dark">PhoneNumber </label>
                            <div class="row">
                                <div class="col-9">
                                    <input th:name="phoneNumber" type="text" id="phoneNumber" class="form-control border-1"
                                           oninput="autoHyphen(this)" maxlength="13" required="">
                                    <p th:if="${#fields.hasErrors('phoneNumber')}" th:errors="*{phoneNumber}" class="fieldError"> 휴대전화 번호는 010, 011 형식만을 지원합니다. 확인하여 다시 작성해주세요 </p>

                                </div>
                                <div class="col-3 d-flex justify-content-around">
                                    <button id="checkPhoneNumberBtn" class="btn btn-light" type="button" onclick="checkPhoneNumber()">중복 확인</button>
                                </div>
                            </div>

                        </div>

                        <div class="d-grid">
                            <button class="btn btn-primary" type="submit">
                                Sign up
                            </button>
                        </div>
                    </form>
                    <div class="d-flex justify-content-center">
                        <p class="mt-3 mb-3 text-muted font-14 text-dark">
                            Already have an account? <a href="/members/login"> Sign in.</a>
                        </p>

                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<p class="text-center mt-5 mb-3 text-muted pb-5"><span><a href="https://github.com/yeomyaloo"> yaloo</a><span>&copy; 2023</p>
<!-- Optional JavaScript -->
<!-- Libs JS -->
<script src="../assets/libs/jquery/dist/jquery.min.js"></script>
<script src="../assets/libs/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="../assets/libs/jquery-slimscroll/jquery.slimscroll.min.js"></script>
<!-- Bootstrap JS CDN -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script>


<!-- Theme JS -->
<script src="../assets/js/theme.min.js"></script>

</body>

</html>

회원가입 성공 시 뷰페이지

<html lang="en" xmlns:th="http://www.thymeleaf.org">

<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="keywords" content="">
    <meta name="author" content="Codescandy">
    <title>yalooStore</title>
    <!-- Favicon icon-->
    <link rel="shortcut icon" type="image/x-icon" href="../assets/images/favicon/favicon.ico">

    <!-- Libs CSS -->
    <link rel="stylesheet" href="../assets/libs/bootstrap-icons/font/bootstrap-icons.css">

    <!-- Theme CSS -->
    <link rel="stylesheet" href="../assets/css/theme.min.css">
    <!-- Bootstrap CSS CDN -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet"
          integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We"
          crossorigin="anonymous">

    <!-- Bootstrap icons-->
    <!-- Core theme CSS (includes Bootstrap)-->
    <link th:href="@{../../static/css/styles.css}" rel="stylesheet"/>
    <link href="../assets/dist/css/bootstrap.min.css" rel="stylesheet">


    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js"
            integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p"
            crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js"
            integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF"
            crossorigin="anonymous"></script>

</head>


<body class="bg-light">
<script type="text/javascript" th:src="@{/js/member/signUp.js}"></script>

<div th:replace="~{common/fragments/header-nonbtn::fragment-header-nonbtn}"></div>

<!-- sign up -->
<div class="min-vh-100 d-flex align-items-center ">
    <!--style="background:url(../assets/images/register-img.jpg)no-repeat; background-size: cover;">-->
    <div class="container ">
        <div class="row">
            <div class="offset-lg-3 col-lg-6 col-12">
                <a href="../index.html" class="mb-4 d-flex justify-content-center"><img
                        src="../assets/images/logo.svg" alt=""></a>

                <div class="bg-white p-4 p-lg-8 rounded-3 shadow-lg p-3 mb-5 bg-white rounded">

                    <form class="signup-form" id="signup-form" action="/members/signup" method="post" th:object="${member}">

                        <h1 class="mb-2 text-dark text-center pb-2 h3">Sign up</h1>
                        <p class="mb-4 text-center">Please fill in this form to create account!</p>

                        <!--이름-->
                        <div class="mb-3">
                            <label for="name" class="form-label text-dark">Name </label>
                            <div class="row">
                                <div class="col-12">
                                    <input type="text" id="name" th:name="name" class="form-control border-1 col-9" disabled
                                    th:text="${response.getName()}">
                                </div>
                            </div>

                        </div>

                        <div class="mb-3">
                            <label for="id" class="form-label text-dark">Id </label>
                            <div class="row">
                                <div class="col-12">
                                    <input type="text" id="id" th:name="id" class="form-control border-1"
                                           disabled th:text="${response.getId()}">
                                </div>

                            </div>
                        </div>


                        <div class="mb-3 mb-4">
                            <label for="nickname" class="form-label text-dark">Nickname </label>
                            <div class="row">
                                <div class="col-12">
                                    <input type="text" id="nickname" th:name="nickname" class="form-control border-1"
                                    disabled th:text="${response.getNickname()}">
                                </div>
                                <div class=" d-flex justify-content-around">
                                </div>
                            </div>

                        </div>

                        <div class="mb-3 mb-4">
                            <label class="form-label text-dark">Grade </label>
                            <div class="row">
                                <div class="col-12">
                                    <input th:name="phoneNumber" type="text" id="phoneNumber" class="form-control border-1"
                                    disabled th:text="${response.getGrade()}">
                                </div>
                                <div class=" d-flex justify-content-around">
                                </div>
                            </div>

                        </div>

                    </form>
                    <div class="d-flex justify-content-center">
                        <button onclick="location.href='/members/login'"
                                class="btn mt-3 mb-3 text-muted font-14 text-dark border-0" type="button">
                            로그인하러 가기
                        </button>

                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<p class="text-center mt-5 mb-3 text-muted pb-5"><span><a href="https://github.com/yeomyaloo"> yaloo</a><span>&copy; 2023</p>
<!-- Optional JavaScript -->
<!-- Libs JS -->
<script src="../assets/libs/jquery/dist/jquery.min.js"></script>
<script src="../assets/libs/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="../assets/libs/jquery-slimscroll/jquery.slimscroll.min.js"></script>
<!-- Bootstrap JS CDN -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script>


<!-- Theme JS -->
<script src="../assets/js/theme.min.js"></script>

</body>

</html>
  • 수정 예정
  • 회원가입에 성공했다고 로그인이 되는 것이 아닌 로그인을 다시 해줄 수 있게 버튼을 달아둠

3.Service

  • Repository는 없다 당연한 얘기로 해당 Front Server에서는 DB에 대한 접근을 하지 않고 있기 때문이다.
  • 이때문에 API서버와 필요한 정보가 있을 땐 Http 통신을 해서 정보를 받아오는 것이다.
@Service
@Slf4j
@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService {

    private final PasswordEncoder passwordEncoder;
    private final RestTemplate restTemplate;
    private final GatewayConfig gatewayConfig;


    /**
     * {@inheritDoc}
     * */
    @Override
    public SignUpResponse signUp(SignUpRequest request) {
        request.setPassword(passwordEncoder.encode(request.getPassword()));

        HttpEntity<SignUpRequest> entity = getHttpEntity(request);



        ResponseEntity<ResponseDto<SignUpResponse>> response = restTemplate.exchange(
                gatewayConfig.getShopUrl() + "/api/service/members/sign-up",
                HttpMethod.POST,
                entity,
                new ParameterizedTypeReference<>() {
                }
        );

        log.info("response = {}", response.getBody().getData());
        return response.getBody().getData();
    }

    private static HttpEntity<SignUpRequest> getHttpEntity(SignUpRequest request) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<SignUpRequest> entity = new HttpEntity<>(request, headers);
        return entity;
    }
}
  • RestTemplate을 통해서 해당 API server와 통신하여 받은 Response 값을 이용한다.
  • 다른 서버에서 자원을 가져와서 비지니스 로직을 작성하는 경우에 해당한다.

3.Controller

  • 해당 작업은 RestController가 아닌 Contoller를 사용한다.
  • 실제 뷰페이지 관련된 작업을 진행할 땐 Contoller를 사용해야 한다.

@Slf4j
@Controller
@RequiredArgsConstructor
@RequestMapping("/members")
public class MemberAuthWebController {


    private final MemberService memberService;


    /**
     * 회원가입 화면으로 이동합니다.
     * */
    @GetMapping("/signup")
    public String signupForm(@ModelAttribute(name = "member") SignUpRequest request){
        return "auth/signup-form";
    }


    @PostMapping("/signup")
    public String signup(@Valid @ModelAttribute(name ="member") SignUpRequest request,
                         BindingResult bindingResult,
                         Model model){

        if(bindingResult.hasErrors()){
            log.info("binding result : {}",bindingResult);
            log.info(bindingResult.getObjectName());
            return "redirect:/members/signup";
        }

        SignUpResponse response = memberService.signUp(request);

        model.addAttribute("response", response);

        return "auth/signup-success";

    }

-@Valid @ModelAttribute(name ="member") SignUpRequest request : 해당 객체를 데이터 바인딩하고 바인딩한 데이터를 검증한다.

  • Model model: 응답 객체 뷰페이지로 넘겨주기 위한 작업
  • BindingResult bindingResult: 검증 결과

4. DTO

@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Setter
public class SignUpRequest {

    @NotBlank(message = "공백과 빈값은 허용하지 않습니다. 올바르게 입력해주세요.")
    @Size(min = 2, max = 20)
    @Pattern(regexp = "^[a-zA-Z0-9]{8,15}$", message ="아이디는 영문과 숫자로만 가능하며 숫자로 시작할 수 없습니다. 올바르게 입력해주세요.")
    private String id;

    @NotBlank(message = "공백과 빈값은 허용하지 않습니다. 올바르게 입력해주세요.")
    @Size(min = 2, max = 30)
    @Pattern(regexp = "^[가-힣ㄱ-ㅎa-zA-Z0-9._-]{2,15}$", message = "닉네임은 한글과 영문만 입력해주세요.")
    private String nickname;

    @Size(min = 2, max = 50)
    @NotBlank(message = "공백과 빈값은 허용하지 않습니다. 올바르게 입력해주세요.")
    @Pattern(regexp = "^[가-힣a-zA-Z]{2,50}$")
    private String name;

    @NotBlank(message = "공백과 빈값은 허용하지 않습니다. 올바르게 입력해주세요.")
    private String gender;

    @NotBlank(message = "공백과 빈값은 허용하지 않습니다. 올바르게 입력해주세요.")
    @Pattern(regexp = "^[0-9]{8}", message = "ex) 1999090 숫자로만 8글자 작성해주세요")
    private String birthday;

    @NotBlank
    @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,20}$",
            message = "비밀번호는 최소 8자 최대 20자, 하나 이상의 문자와 하나의 숫자 및 하나의 특수 문자가 들어가야 합니다.")
    private String password;

    @NotBlank(message = "공백과 빈값은 허용하지 않습니다. 올바르게 입력해주세요.")
    @Size(min = 11, max = 30)
    private String phoneNumber;

    @Email
    @NotBlank(message = "공백과 빈값은 허용하지 않습니다. 올바르게 입력해주세요.")
    @Size(min = 2, max = 100)
    private String emailAddress;
}
  • @Setter를 달아주어야 회원이 회원가입 폼에서 작성해 넘겨 받은 값을 제대로 바인딩 할 수 있다.
  • Setter가 없으면 해당 input창에 아무리 값을 넣어도 null값이 들어온다.

[해당 작업의 흐름]

[해당 작업의 더 자세한 코드]

profile
즐겁고 괴로운 개발😎

0개의 댓글