프로젝트 정리/스프링과 JPA 기반 웹 애플리케이션 개발

6 - 1 . 회원 가입 : 폼 서브밋 검증

출처 : www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-JPA-%EC%9B%B9%EC%95%B1/dashboard

 

스프링과 JPA 기반 웹 애플리케이션 개발 - 인프런

이 강좌에서 여러분은 실제로 운영 중인 서비스를 스프링, JPA 그리고 타임리프를 비롯한 여러 자바 기반의 여러 오픈 소스 기술을 사용하여 웹 애플리케이션을 개발하는 과정을 학습할 수 있습

www.inflearn.com

 

회원가입 폼 검증

  • JSR 303 애노테이션 검증
    • 값의 길이, 필수값
  • 커스텀 검증
    • 중복 이메일, 닉네임 여부 확인
  • 폼 에러 있을 시, 폼 다시 보여주기

회원 가입 처리

  • 회원 정보 저장
  • 인증 이메일 발송
  • 처리 후 첫 페이지로 리다이렉트 ( Post-Redirect-Get 패턴)

실습

  • 시작 커밋 : bc88d9e4d7f892b09146c94260dcf144b05cd1c6
  • 완료 커밋:

frontend개발자가 thymeleaf말고 다른것으로 개발한다고 가정 할 경우

<li class="nav-item">
    <a class="nav-link" href="#" th:href="@{/login}">로그인</a>
</li>
<li class="nav-item">
    <a class="nav-link" href="#" th:href="@{/signup}">가입</a>
</li>
                

ThymeLeaf로만 attribute를 던진다고 가정

<li class="nav-item">
    <a class="nav-link" th:href="@{/login}">로그인</a>
</li>
<li class="nav-item">
    <a class="nav-link" th:href="@{/signup}">가입</a>
</li>
                

회원가입 폼 검증

  • JSR 303 애노테이션 검증
    • 값의 길이, 필수값

2009년 자바 servlet2.3 JSR validation을 확장한 annotation validation 및 hibernate활용

-> devjun.tistory.com/72

 

Validation

백기선님 강의중 @Valid 어노테이션을 사용하셔서 알아보게 되었다. Validation이란 데이터가 유효한 값인지, 타당한 값인지 확인하는 것을 의미한다. 예를 들어 전화 번호 양식이 010-xxxx-xxxx인데 숫

devjun.tistory.com

  • 커스텀 검증
    • 중복 이메일, 닉네임 여부 확인
package com.studyolle.account;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

@Component
// bean과 bean들만 의존성 주입 받을 수 있기 때문에 component어노테이션
@RequiredArgsConstructor
// lombok -> private fianl에 해당하는 member variable의 생성자를 만들어줌
public class SignUpFormValidator implements Validator {

    private final AccountRepository accountRepository;    // bean

    /*
    @RequiredArgsConstructor ->
    롬복이 Annotation으로 만들어 줌 어떤 bean이 생성자가 하나만 있고
    그 생성자가 받는 parameter들이 bean으로 등록이 되어있다면
    spring 4.2이후 부터는 자동으로 bean을 주입해주기 때문에
    autowired나 inject같은 어노테이션을 쓰지않아도 의존성 주입이 된다.
    public SignUpFormValidator(AccountRepository accountRepository) {
        this.accountRepository = accountRepository;
    }*/

    @Override
    public boolean supports(Class<?> aClass) {
        return aClass.isAssignableFrom(SignUpForm.class);
    }

    @Override
    public void validate(Object o, Errors errors) {
        // TODO email, nickname 중복 여부 검사
        SignUpForm signUpForm = (SignUpForm)errors;
        if(accountRepository.existsByEmail(signUpForm.getEmail())){
            errors.rejectValue("email","invalid.email", new Object[]{signUpForm.getEmail()}, "이미 사용중인 이메일입니다.");
            // message 소스는 이번에 다루지 않고 국제화 서비스를 가정하고 메시지 다국화를 다루면서
            // 그 때 다룰 예정
            if(accountRepository.existsByNickName(signUpForm.getNickname())){
                errors.rejectValue("nickname","invalid.nickname", new Object[]{signUpForm.getNickname()}, "이미 사용중인 닉네임입니다.");
            }
        }
    }
}

실제로 DB조회 해서 custom한 validata가 있어야 함

  • 폼 에러 있을 시, 폼 다시 보여주기
package com.studyolle.account;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import javax.validation.Valid;


@Controller
@RequiredArgsConstructor
public class AccountController {

    private final SignUpFormValidator signUpFormValidator;

    @InitBinder("signUpForm")
    public void initBinder(WebDataBinder webDataBinder) {
        webDataBinder.addValidators(signUpFormValidator);

        // signUpForm데이터를 받을 때 타입의 camelCase이름을 따라감
        // @Valid SignUpForm signUpForm(mapping)

        /*
        signUpSubmit에
        validate 검증을 한번 더 하는 것을 InitBinder로
        signUpSubmit에들어 왔을때 validate 검사를 하고
        signUpFormValidator.validate(signUpForm, errors);
        if(errors.hasErrors());
            return "account/sign-up";
        }
        */
    }


    @GetMapping("/sign-up")
    public String signUpForm(Model model) {
        model.addAttribute(new SignUpForm()); // 객체 attribute의 이름이 된다.
        // camel Case 값 -> "signUpForm"문자열이 들어감
        return "account/sign-up";
    }

    @PostMapping("/sign-up")
    public String signUpSubmit(@Valid SignUpForm signUpForm, Errors errors) {
        // 복합 객체 ( 여러 값) -> ModelAttribute로 받아오는데 파라미터로 쓰일때 생략이 가능하다.
        // Data를 컨버젼하거나 binding할때 발생할 수 있는 에러를 받아주는 타입 -> Erros
        if(errors.hasErrors()){
            //SignUpForm JSR 303 Annotation에 정의한 값에 걸리면
            // Binding Errors errors에 담기고 에러가 있다고 조건문에 걸려서 form으로 돌아감
            return "account/sign-up";
        }

        // TODO 회원 가입 처리
        return "redirect:/";

    }
}

 

 

@transaction readonly = true성능 이점에 대한 관련 글

 

happyer16.tistory.com/entry/JPA-15%EC%9E%A5-%EA%B3%A0%EA%B8%89-%EC%A3%BC%EC%A0%9C%EC%99%80-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94

 

JPA 15장. 고급 주제와 성능 최적화

성능 최적화 1 - 읽기 전용 쿼리의 성능 최적화 Entity가 영속성 컨텍스트에 관리되면 다양한 혜택을 얻을 수 있다. 하지만 dirty check를 위해 snapshot 인스턴스를 보관하므로 더 많은 메모리를 사용하

happyer16.tistory.com

vladmihalcea.com/spring-read-only-transaction-hibernate-optimization/

 

Spring read-only transaction Hibernate optimization - Vlad Mihalcea

Introduction In this article, I’m going to explain how the Spring read-only transaction Hibernate optimization works. After taking a look at what the Spring framework does when enabling the readOnly attribute on the @Transactional annotation, I realized

vladmihalcea.com