Java와 spring boot 게시판 프로젝트 - 회원 가입 이메일 인증

최고요·2023년 5월 9일
0

SpringBoot-Board-Project

목록 보기
3/3
post-thumbnail

회원가입 폼 만들기

회원가입을 위해 클라이언트 입장에서 정보를 입력할 폼이 필요하다.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="layout/default_layout">
<!-- 현재 화면에서만 사용하는 js -->
<head>
    <script th:inline="javascript" th:src="@{/js/memberInfoCheck.js}"></script>
</head>
<div>
    <th:block layout:fragment="content">
        <!-- 회원가입 폼 -->
        <div class="container-md shadow p-3 mb-5 mt-5 w-50 bg-body rounded">
            <form id="SignUpForm"  onsubmit="SignUpFormSumitCheck(event)" action="/member/save" method="post" >
                <h3 class="center text-center">회원가입</h3>
                <span id="warningMsg"></span>
                <div class="mb-3">
                    <label class="form-label">Email</label>
                    <div class="row">
                        <div class="col-10">
                            <input type="email" class="form-control" name="email" id="email" onblur="emailDuplicateCheck()" placeholder="이메일을 입력해주세요">
                        </div>
                        <div class="col-2">
                            <input class="btn btn-outline-primary"  name="checkEmailNumber" id="checkEmailNumber"  type="button" value="인증번호" onclick="sendEmail()">
                        </div>
                    </div>
                    <span id="checkEmail">
                 <!-- 사용가능 이메일 확인여부 출력 하기  -->
            </span>
                </div>
                <div class="mb-3">
                    <label class="form-label">인증번호</label>
                    <div class="row">
                        <div class="col-10">
                            <input type="text" class="form-control" name="inputCode" id="inputCode" maxlength="6" placeholder="인증번호를 입력해주세요" >
                        </div>
                        <div class="col-2">
                            <input class="btn btn-outline-primary"  name="check" id="check"  type="button" value="확인하기" onclick="verifyCode()">
                        </div>
                    </div>
                    <div class="col-auto">
                        <!--<span id="#" class="#">
                             일치 / 불일치 결과 출력 (미진행)
                        </span>-->
                    </div>
                    <div class="mb-3">
                        <label class="form-label" for="password">Password</label>
                        <input type="password" class="form-control" name="password" id="password" placeholder="비밀번호를 입력해주세요" aria-describedby="passwordHelpInline">
                        <span id="password-strength"></span>
                        <div class="mb-3">
                            <label class="form-label">Confirm Password</label>
                            <input type="password" class="form-control" name="confirmPassword" id="confirmPassword" placeholder="비밀번호 확인" onblur="chackPassword()">
                            <span id="confirmMsg"></span>
                        </div>
                        <div class="mb-3">
                            <label class="form-label">User name</label>
                            <!-- name속성: 서버로 전송할 때 변수 이름의 역할 -->
                            <input type="text" class="form-control" name="name" id="name" placeholder="이름을 입력해주세요">
                        </div>
                        <div class="center text-center">
                            <input class="btn btn-primary" type="submit" value="가입하기" >
                        </div>
                    </div>
                </div>
            </form>
        </div>
    </th:block>
</div>

</html>

사용자가 이메일을 입력하고 인증번호를 누르면 인증 메일이 발송되어 인증번호를 확인하여
가입이 가능 하도록했다.


이메일인증 코드 전송

Javascript

아래 코드는 이메일 인증 코드를 버튼을 클릭하면 컨트롤러로 이메일 값을 컨트롤러로 post형식으로 보내 컨트롤러는 사용자가 입력한 이메일을 받아
해당 이메일로 인증코드를 보내게된다.

/**
* 회원가입 조건 >
* 받아온 이메일이 DB에 이미 존재하는 이메일이면 안된다.
* 이메일 형식에 맞지 않으면 안된다.
* 입력한 비밀번호와 비밀번호 확인란에 기재한 문자는 같아야한다 .
* */


// 인증번호 발송
function sendEmail() {
  const email = $("#email").val(); // 사용자가 입력한 이메일 주소
  // 이메일칸이 비어있으면 알림
  if (!email || email.trim() === '') {
      alert("이메일 주소를 입력해주세요.");
      return;
  }

  $.ajax({
      url: "/infoCheck/sendEmail" ,// @PostMapping("/infoCheck/sendEmail") EmailController
      type: "POST",
      data: {
          "email": email
      },
      success: function(data) {
          // 이메일 전송 성공
          alert("인증번호가 전송되었습니다.");
      },
      error: function() {
          // 이메일 전송 실패
          alert("인증번호 전송에 실패했습니다.");
      }
  });
}

// 인증번호 확인
function verifyCode() {
  const inputCode = $("#inputCode").val(); // 사용자가 입력한 인증번호
  if (!inputCode || inputCode.trim() === '') { // 인증번호입력없이 확인하기를 눌렀을 경우 알림
    alert("인증번호를 입력해주세요.");
    return;
  }

  $.ajax({
    url: "/infoCheck/verifyCode",
    type: "POST",
    data: {
      "inputCode": inputCode
    },
    success: function(isCodeCorrect) {
      if (isCodeCorrect) {
        alert("인증번호가 일치합니다.");
      } else {
        alert("인증번호가 일치하지 않습니다.");
        $("#inputCode").focus();
      }
    },
    error: function() {
      alert("인증번호 확인에 실패했습니다.");
    }
  });
}

 //비밀번호 확인(span.innerHtml)
 function  chackPassword(){
       var password = $("#password");
       var confirmPassword = $("#confirmPassword");
       var confrimMsg = $("#confirmMsg");
       var correctColor = "green";	//맞았을 때 출력되는 색깔.
       var wrongColor ="red";	//틀렸을 때 출력되는 색깔
       if(password.value == confirmPassword.value){
           confirmMsg.style.color = correctColor;/* span 태그의 ID(confirmMsg) 사용  */
           confirmMsg.innerHTML ="비밀번호 일치";
       } else{
		confirmMsg.style.color = wrongColor;
		confirmMsg.innerHTML ="비밀번호 불일치";
	   }
 }

	$(document).ready(function() {
		$('#password').on('keyup', function() {
			var password = $(this).val();
			var strength = '';
			if (password.length < 7) {
				strength = 'week';
				$('#password-strength').css('color', 'red');
			} else if (password.length >= 8) {
				strength = 'good!';
				$('#password-strength').css('color', 'green');
			}
			$('#password-strength').text(strength);
		});
	});


function SignUpFormSumitCheck(event) {
  // 폼 제출을 막기 위해 event.preventDefault() 호출
  event.preventDefault();

  // 입력된 이메일, 비밀번호, 비밀번호 확인 값을 가져옴
  const email = $("#email").val(); // 이메일
  const password = $("#password").val(); // 비밀번호
  const confirmPassword = $("#confirmPassword").val(); // 비밀번호 확인
  const inputCode = $("#inputCode").val(); // 인증번호 확인

  if (!isEmailValid(email)) {
    alert("올바른 이메일 형식이 아닙니다.");
    $("#email").focus();
    return;
  }


  // 인증번호 확인 AJAX 요청
  $.ajax({
    url: "/infoCheck/verifyCode",
    type: "POST",
    data: {
      "inputCode": inputCode
    },
    success: function(isCodeCorrect) {
      if (!isCodeCorrect) {
        alert("인증번호가 일치하지 않습니다.");
        return;
      }

      // 인증번호가 일치하면 회원가입 양식 확인 AJAX 요청
      const ajaxPromise = new Promise((resolve, reject) => {
        $.ajax({
          type: "post",
          url: "/SignUpForm/SignUpFormSumitCheck",
          data: {
            "email": email,
            "password": password,
            "confirmPassword": confirmPassword,
            "inputCode": inputCode,
          },
          success: function(res) {
            resolve(res);
          },
          error: function(err) {
            reject(err);
          }
        });
      });

      ajaxPromise.then((res) => {
        if (res == "Emailno") {
          alert("이메일을 확인해주세요.");
          document.getElementById("email").focus();

        } else if (res == "PWno") {
          alert("비밀번호를 확인해주세요.");
          document.getElementById("confirmPassword").focus();

        } else {
          alert("회원가입 성공.");
          console.log("res : ", res);
          document.getElementById("SignUpForm").submit();
        }
      }).catch((err) => {
        console.log("에러발생: ", err);
      });

    },
    error: function() {
      alert("인증번호 확인에 실패했습니다.");
    }
  });
}


// 이메일 형식 확인하는 함수
function isEmailValid(email) {
  const emailRegex = /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/;
  return emailRegex.test(email);
}


function emailDuplicateCheck(){
     const email = $("#email").val();
     // <span id="checkResult">      </span>에 출력하기
     const checkResult = $("#checkResult");
     console.log("입력값 :" , email);

        // 형식
     if (!isEmailValid(email)) {
       console.log("올바른 이메일 형식이 아닙니다.");
       checkEmail.style.color = "red";
       checkEmail.innerHTML = "올바른 이메일 형식이 아닙니다.";
       return;
     }

     $.ajax({
         type:"post",
         url:"/SignUpForm/emailDuplicateCheck",
         data: {
             "email": email // "전송되는 파라미터 값 이름 ": 파라미터
         },
         success : function(res){
             console.log("요청성공 : ", res);
             if(res == "ok"){
                 console.log("사용가능한 이메일입니다.");
                 checkEmail.style.color = "green";
                 checkEmail.innerHTML="사용가능한 이메일입니다.";
             } else {
                 console.log("이미 사용중인 이메일입니다.");
                 checkEmail.style.color = "red";
                 checkEmail.innerHTML="이미 사용중인 이메일입니다.";
             }
         },
         error : function(err){
            console.log("에러발생: ",err);
            }

     })
}

Controller

package com.dandelion.dandelion.controller;

import com.dandelion.dandelion.service.EmailService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpSession;

@Controller
@RequiredArgsConstructor // 생성자 주입 방식 사용
public class EmailController {
    private final EmailService emailService;

    @PostMapping("/infoCheck/sendEmail")
    public ResponseEntity<Void> sendEmail(@RequestParam("email") String email, HttpSession session) {
        // 이메일 전송 및 인증번호 저장
        String verificationCode = emailService.sendVerificationCode(email);
        session.setAttribute("verificationCode", verificationCode); // 세션에 전송된 인증번호를 저장.
        return ResponseEntity.ok().build();
    }

    //    입력한 인증번호와 세션에 저장된 인증번호를 비교
    @PostMapping("/infoCheck/verifyCode")
    public ResponseEntity<Boolean> verifyCode(@RequestParam("inputCode") String inputCode, HttpSession session) {
        String storedCode = (String) session.getAttribute("verificationCode");

        if (storedCode != null && storedCode.equals(inputCode)) {
            return ResponseEntity.ok(true);
        } else {
            return ResponseEntity.ok(false);
        }
    }
}

sendEmail 메서드는 이메일 주소를 받아 EmailService를 통해 인증번호를 전송한다.
전송된 인증번호는 사용자가 입력한 인증번호와 비교하기위해 세션에 저장된다.

verifyCode 메서드는 사용자가 입력한 인증번호와 세션에저장된 인증번호를 비교하는 메서드다.
일치하는 경우 true 불일치하는 경우 false 를 응답 본문에 담아 반환한다.

Service

package com.dandelion.dandelion.service;

import lombok.RequiredArgsConstructor;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;

import java.security.SecureRandom;
import java.util.Random;


@RequiredArgsConstructor // 생성자 주입 사용하기
@Service
public class EmailService {

    private final JavaMailSender javaMailSender;

    public String sendVerificationCode(String recipient) {
        String verificationCode = generateEmailCode();
        String subject = "dandelion 인증번호";
        String message = "인증번호는 " + verificationCode + " 입니다.";

        sendEmail(recipient, subject, message);
        return verificationCode; // 세션에 저장하기위해 반환
    }

    public void sendEmail(String recipient, String subject, String text) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo(recipient); // 수신자 설정
        message.setSubject(subject); // 제목 설정
        message.setText(text); // 내용 설정
        javaMailSender.send(message); // 메일 전송
    }

    /**
     * 이메일 인증번호를 생성하는 메서드
     *
     * @return 6자리 랜덤 알파벳 인증번호
     */
    public String generateEmailCode() {
        int codeLength = 6;  // 코드자리 6자리로 설정
        String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        StringBuilder sb = new StringBuilder(codeLength);
        Random random = new SecureRandom();

        for (int i = 0; i < codeLength; i++) {
            int index = random.nextInt(alphabet.length());
            char randomChar = alphabet.charAt(index);
            sb.append(randomChar);
        }
        return sb.toString();
    }
}

sendVerificationCode 메서드는 수신자의 이메일 주소를 입력받아 인증번호를 생성하고 이메일을 전송한 후, 생성된 인증번호를 반환한다.

sendEmail 메서드는 수신자, 제목, 내용을 입력받아 이메일을 전송하는 역할을 한다.
SimpleMailMessage 객체를 사용하여 메일을 구성하고 JavaMailSender를 사용하여 메일을 전송한다.

generateEmailCode 메서드는 6자리 랜덤 알파벳 인증번호를 생성하는 메서드다.
SecureRandom을 사용해서 무작위 인덱스를 생성하고,
해당 인덱스에 있는 알파벳 문자를 결과문자열에 추가한다.



사용자는 인증번호를 입력하고 확인하기 버튼을 누른다

url: "/infoCheck/verifyCode",

버튼을 클릭하면 post형식으로 값이 컨트롤러로 넘어가면서 세션에 저장되어있는 값과 비교하여
인증 번호 일치 여부를 alert창으로 알린다.


action="/member/save"

회원가입 버튼을 누르면 form값이 위 주소로 넘어가기전에

onsubmit="SignUpFormSumitCheck(event)"

모든 회원가입 조건이 충족하는지 체크하고 회원가입이 진행된다.

-jascript 전체 코드 위에 참고-

member컨트롤러 회원정보 save하기

package com.dandelion.dandelion.controller;

import com.dandelion.dandelion.dto.MemberDTO;
import com.dandelion.dandelion.service.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;

@RequiredArgsConstructor // 생성자 주입 사용하기

@Controller
public class MemberController {
    private final MemberService memberService;

    //    회원가입요청시 이메일 중복체크 비밀번호와 비밀번호 확인체크
    @PostMapping("/SignUpForm/SignUpFormSumitCheck")
    public @ResponseBody String SignUpFormSumitCheck(@RequestParam("email") String email, @RequestParam("password")
    String password, @RequestParam("confirmPassword")String confirmPassword){ // @ResponseBody ajax사용시 필요 어노테이션

        System.out.println("email = " + email);
        String checkresult = memberService.SignUpFormSumitCheck(email, password, confirmPassword);
        System.out.println(checkresult);

        return checkresult;

    }

    //    회원가입 이메일 입력시 innerhtml에 들어갈 메서드
    @PostMapping("/SignUpForm/emailDuplicateCheck")
    public @ResponseBody String emailDuplicateCheck(@RequestParam("email") String email){
        String emailChek = memberService.emailDuplicateCheck(email);
        return emailChek;

    }

    //  회원가입을 위한 메서드 입력받은 값을 userService에 넘긴다
    @PostMapping("/member/save") // 요청이 들어오면 메서드를 실행하겠다
    public String save(@ModelAttribute MemberDTO memberDTO){ // SignUpForm에서 입력받은 값의 name이 DTO의 멤버 명과 같을 때 세팅됨

        System.out.println("MemberController.save");
        System.out.println("memberDTO = " + memberDTO);
        memberService.save(memberDTO); // service객체에 dto객체를 넘김
        return "redirect:/member/loginForm";
    }
 }

컨트롤러로 넘어온 값은 @ModelAttribute을 사용해 MemberDTO 로 받고
memberService.save메서드를 호출하여 값을 저장한다

MemberService에서 회원정보 저장

@RequiredArgsConstructor // 생성자 주입 사용하기
@Service
public class MemberService {

    private final MemberRepository memberRepository;
    
    public void save(MemberDTO memberDTO) {
        //        1. dto -> entity 변환
        MemberEntity memberEntity = MemberEntity.toMemberEntity(memberDTO);
        //        2. repository의 save메서드 호출
        try {
            memberRepository.save(memberEntity); // jpa가 제공해주는 save 메서드
            System.out.println("회원테이블 DB저장시도");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

컨트롤러에서 넘어온 memberDTO 는 엔티티로 변환되어
MemberRepository의 save를 사용해 DB에 저장한다.

MemberRepository 인터페이스

package com.dandelion.dandelion.repository;

import com.dandelion.dandelion.entity.MemberEntity;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

// JpaRepository 상속
public interface MemberRepository extends JpaRepository<MemberEntity, Long> {
//    이메일로 회원정보 조회
    Optional<MemberEntity> findByEmail(String email);
}

MemberRepository 는 JPA에서 제공하는 JpaRepository를 확장하여
JpaRepository가 가지는 여러가지 기능을 상속받아 사용할 수 있다.
예를들면 찾기(find),삭제하기(delete),저장하기(save) 등이 있다.

profile
i'm best

0개의 댓글