🙌
【初心者向け/ITスクール 73日】個人プロジェクトスタート8日目
はじめに
今日は、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