🙌

【初心者向け/ITスクール 73日】個人プロジェクトスタート8日目

2023/11/01に公開

はじめに

今日は、8日目の日です! リサーチなどを終わらせ、昨日から本格的にVIEWを作成してみました。
やっと昨日を作ることになり、今日は会員登録機能を作成しました。

一番難しいところはサーバーからのValidationとPackage構造を組むことでした。

Package

domainとwebを分離することやService,Repositoryはまったく初めての領域でしたので、
相当空戦しました。
一番の難題はサービスに転送する際に、DTOをどの風にDomain(Member)に変換するのかでした。
友達からはDTOから変換すればよいと言われたのですが、当時はそれを知らず、
工夫した結果、MemberConverterというクラスを作成し、変換することにしました。

Validation sequence(validation優先順位)

一番難しいかった点:@NotBlankと@Patternエラーが一機に出る。

今までJSのみValidationをやってきましたが、実質意味がないと言われたので、初めてチャレンジしました。field errorとglobal error(正確にはDBから重複を検査するコード)を分離し、
初めてOptional classも使用してみました。

1. ValidationGroupクラス作成


package com.hyon01.minishop.web.member;

public class MemberValidationGroup {
        public interface NotBlankGroup {}
        public interface PatternGroup {}

}

2. ValidationSequenceインタフェース作成

package com.hyon01.minishop.web.member;

import javax.validation.GroupSequence;
import static com.hyon01.minishop.web.member.MemberValidationGroup.*;

@GroupSequence({NotBlankGroup.class, PatternGroup.class})
public interface MemberValidationSequence {
}

3. DTO修正(groups)

@Data
public class MemberAddForm {

    @NotBlank(message = "아이디를 입력해주세요.",groups = NotBlankGroup.class)
    @Pattern(regexp = "[a-zA-Z0-9]{5,20}", message = "잘못된 아이디 형식입니다.",groups = PatternGroup.class)
    private String memberId;

    @NotBlank(message = "비밀번호를 입력해주세요.",groups = NotBlankGroup.class)
    @Pattern(regexp = "^(?=.*\\d)(?=.*[a-zA-Z])[0-9a-zA-Z]{8,20}", message = "잘못된 비밀번호 형식입니다.",groups = PatternGroup.class)
    private String memberPw;

    @NotBlank(message = "비밀번호를 한 번 더 입력해주세요.",groups = NotBlankGroup.class)
    private String memberPw2;

    @NotBlank(message = "이름을 입력해주세요.",groups = NotBlankGroup.class)
    private String memberName;

    @NotBlank(message = "이메일을 입력해주세요.",groups = NotBlankGroup.class)
    @Email(message = "유효하지 않은 이메일 형식입니다.",groups = PatternGroup.class)
    private String memberEmail;

    @NotBlank(message = "휴대폰 번호를 입력해주세요.",groups = NotBlankGroup.class)
    @Pattern(regexp = "^[0-9]{10,11}$", message = "10자 또는 11자의 숫자만 입력 가능합니다",groups = PatternGroup.class)
    private String memberHp;

    @NotBlank(message = "우편번호를 입력해주세요.",groups = NotBlankGroup.class)
    @Pattern(regexp = "[0-9]{5,10}", message = "5자 이상 10자 이하여야 합니다.",groups = PatternGroup.class)
    private String memberAddress1;

    @NotBlank(message = "기본 주소를 입력해주세요.",groups = NotBlankGroup.class)
    private String memberAddress2;

    private String memberAddress3;

}

4 Controller修正

MemberController
package com.hyon01.minishop.web.member;

import com.hyon01.minishop.domain.member.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

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

    private final MemberConverter memberConverter;
    private final MemberService memberService;

    @GetMapping(value = "/add")
    public String goSignInPage(Model model){
        model.addAttribute("memberAddForm",new MemberAddForm());
        return "members/addForm";
    }

    @PostMapping(value = "/add")
    public String addMember(@Validated(MemberValidationSequence.class) @ModelAttribute MemberAddForm memberAddForm, BindingResult bindingResult) {
        //DTO -> Domain (MemberConverter를 통해 역할 분리)

        if (bindingResult.hasErrors()) {
            log.info("errors={}", bindingResult);
            return "members/addForm";
        }
           //글로벌 에러1 (비밀번호 불일치 처리)
        if (!memberAddForm.getMemberPw().equals(memberAddForm.getMemberPw2())) {
            bindingResult.rejectValue("memberPw2", null, "비밀번호가 일치하지 않습니다.");
            return "members/addForm";
        }

            log.info("컨트롤러 진입 : {}", memberAddForm);
            Member member = memberConverter.converToMember(memberAddForm);


            //글로벌 에러2 (중복 아이디 처리)
            try {
                memberService.join(member);
                return "success";
            } catch (IllegalStateException e) {
                bindingResult.rejectValue("memberId", null, null, e.getMessage());
                return "members/addForm";
            }
        }

    }

ID重複チェックロジック

1. RepositoryからfindById作成

MemberRepository
package com.hyon01.minishop.domain.member;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Slf4j
@Repository
@RequiredArgsConstructor
public class MyBatisMemberRepository implements MemberRepository{

    private final MemberMapper memberMapper;
    @Override
    public Member save(Member member) {
        log.info("레퍼지토리 진입 : {}", member);
        memberMapper.save(member);
        return member;
    }

    @Override
    public Optional<Member> findById(String memberId) {
        log.info("레퍼지토리 진입 : {}", memberId);
        return Optional.ofNullable(memberMapper.findById(memberId));
    }
}


2. Serviceから例外を飛ばし、Controllerから処理

MemberService
package com.hyon01.minishop.domain.member;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService{

    private final MemberRepository memberRepository;

    @Override
    public void join(Member member) {
       log.info("서비스 진입 : {}", member);
       memberRepository.findById(member.getMemberId())
                       .ifPresent(m-> {throw new IllegalStateException("이미 존재하는 회원입니다");});
       memberRepository.save(member);
    }
}

Discussion