스프링 프레임워크/스프링 MVC

# Spring MVC 게시판 예제 06 - 회원가입 유효성 검사 (Feat. JavaScript)

edenDev 2023. 2. 23.

본 포스팅의 예제는 STS 또는 Eclipse를 사용하지 않고 Intellij를 통해 구현하고 있습니다.

그래서 기존의 STS(Spring Tool Studio)에서 생성된 Spring 프로젝트의 스프링 관련 설정 파일명과

프로젝트 구조가 약간 다를 수 있습니다.Intellij 스프링 mvc 프로젝트 생성 포스팅을 참고해주시면 감사하겠습니다.


Spring-MVC 기본 개념 및 테스트 예제 관련 포스팅 링크

순서 포스팅 제목
1 Intellij에서 Spring MVC Project 생성하기
2 Spring MVC - MariaDB 연결테스트
3 Spring MVC - Mybatis 설정 및 테스트
4 SpringMVC 구조
5 SpringMVC + Mybatis
6 Spring MVC Controller 작성 연습

 

Spring-MVC 게시판 예제 이전 포스팅 링크

순서 포스팅제목
1 IntelliJ를 이용한 Spring MVC Project 생성 하기
2 Bootstrap AdminLTE Template 적용하기
3 ExceptionResovler : 예외페이지 처리
4 Spring AOP 적용하기
5 회원가입 구현하기

1. 회원 정보 중복체크

1.1 회원 정보 중복 체크를 위한 영속 계증 (persistance tier) 구현

기본패키지/user/persistance 패키지안에 UserDAO 인터페이스와 UserDAOImpl 클래스를 수정해주세요

public interface UserDAO {

    //  회원가입
    public void insertUser(UserVo param);

    //  아이디 중복체크
    public int isExistId(String user_id);

    //  닉네임 중복체크
    public int isExistNickName(String user_nickname);

    //  휴대폰 중복체크
    public int isExistPhone(String user_phone);

    //  이메일 중복체크
    public int isExistEmail(String user_email);
}
@Repository
public class UserDAOImpl implements UserDAO {
    private static final String NAMESPACE = "mappers.user.UserSQLMapper";

    @Autowired
    private SqlSession sqlSession;

    //  회원가입
    @Override
    @LogException
    public void insertUser(UserVo param) {
        sqlSession.insert(NAMESPACE + ".insertUser", param);
    }

    //  아이디 중복체크
    @Override
    @LogException
    public int isExistId(String user_id) {
        return sqlSession.selectOne(NAMESPACE + ".isExistId", user_id);
    }

    //  닉네임 중복체크
    @Override
    @LogException
    public int isExistNickName(String user_nickname) {
        return sqlSession.selectOne(NAMESPACE + ".isExistNickName", user_nickname);
    }

    //  휴대폰 중복체크
    @Override
    @LogException
    public int isExistPhone(String user_phone) {
        return sqlSession.selectOne(NAMESPACE + ".isExistPhone", user_phone);
    }

    //  이메일 중복체크
    @Override
    @LogException
    public int isExistEmail(String user_email) {
        return sqlSession.selectOne(NAMESPACE + ".isExistEmail", user_email);
    }
}

/resources/mappers/user 경로 안에 UserSQLMapper.xml 를 수정해주세요

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mappers.user.UserSQLMapper">
    <!-- 회원가입 -->
    <insert id="insertUser">
        insert into eden_user(user_no, user_id, user_pw, user_nickname, user_image, user_gender, user_birth, user_phone,
                              user_email, user_status, user_join_date, user_last_connection_date)
        values (eden_user_seq.nextval,
                #{user_id},
                #{user_pw},
                #{user_nickname},
                'default-user-image.jpg',
                #{user_gender},
                #{user_birth},
                #{user_phone},
                #{user_email},
                'active',
                sysdate,
                sysdate)
    </insert>

    <!-- 아이디 중복체크 -->
    <select id = "isExistId" resultType="int">
        select count(*) from eden_user WHERE user_id = #{user_id}
    </select>

    <!-- 닉네임 중복체크 -->
    <select id="isExistNickName" resultType="int">
        select count(*) from eden_user where user_nickname = #{user_nickname}
    </select>

    <!-- 휴대폰 번호 중복체크 -->
    <select id="isExistPhone" resultType="int">
        select count(*) from eden_user where user_phone = #{user_phone}
    </select>

    <!-- 이메일 중복체크 -->
    <select id="isExistEmail" resultType="int">
        select count(*) from eden_user where user_email = #{user_email}
    </select>
</mapper>

1.2 회원 정보 중복체크를 위한 비지니스 계층 (Business Tier) 구현

기본패키지/user/service 패키지안에 UserService 인터페이스와 UserServiceImpl 클래스를 아래와 같이 수정해주세요

public interface UserService {

    //  회원가입
    public void insertUser(UserVo param);

    //  아이디 중복체크
    public boolean isExistId(String user_id);

    //  닉네임 중복체크
    public boolean isExistNickName(String user_nickname);

    //  휴대폰번호 중복체크
    public boolean isExistPhone(String user_phone);

    //  이메일 중복체크
    public boolean isExistEmail(String user_email);
}
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserDAO userDAO;

    //  회원가입
    @Override
    @LogException
    public void insertUser(UserVo param) {
        userDAO.insertUser(param);
    }

    //  아이디 중복체크
    @Override
    @LogException
    public boolean isExistId(String user_id) {
        return userDAO.isExistId(user_id) > 0;
    }

    //  닉네임 중복체크
    @Override
    @LogException
    public boolean isExistNickName(String user_nickname) {
        return userDAO.isExistNickName(user_nickname) > 0;
    }

    //  휴대폰 중복체크
    @Override
    @LogException
    public boolean isExistPhone(String user_phone) {
        return userDAO.isExistPhone(user_phone) > 0;
    }

    //  이메일 중복체크
    @Override
    @LogException
    public boolean isExistEmail(String user_email) {
        return userDAO.isExistEmail(user_email) > 3;
    }
}

1.3 회원 정보 중복체크 컨트롤러 구현

기본패키지/user/controller 패키지안 에 RestUserController 클래스를 생성하고 아래와 같이 작성해주세요.

@RestController
@RequestMapping(value = "/user/*")
public class RestUserController {

    private final HashMap<String, Object> data = new HashMap<>();

    @Autowired
    private UserService userService;

    @Autowired
    private JavaMailSender javaMailSender;

    //  아이디 중복 체크
    @PostMapping(value = "isExistId")
    public HashMap<String, Object> isExistId(String user_id) {

        HashMap<String, Object> data = new HashMap<String, Object>();

        boolean exist = userService.isExistId(user_id);

        if (exist) {
            data.put("result", "fail");
        } else {
            data.put("result", "success");
        }

        return data;
    }

    //  닉네임 중복 체크
    @PostMapping(value = "isExistNickName")
    public HashMap<String, Object> isExistNickName(String user_nickname) {

        HashMap<String, Object> data = new HashMap<String, Object>();

        boolean exist = userService.isExistNickName(user_nickname);

        if (exist) {
            data.put("result", "fail");
        } else {
            data.put("result", "success");
        }
        return data;
    }

    //  휴대폰번호 중복 체크
    @PostMapping(value = "isExistPhone")
    public HashMap<String, Object> isExistPhone(String user_phone) {

        HashMap<String, Object> data = new HashMap<String, Object>();

        boolean exist = userService.isExistPhone(user_phone);

        if (exist) {
            data.put("result", "fail");
        } else {
            data.put("result", "success");
        }

        return data;
    }

    //  이메일 중복 체크
    @PostMapping(value = "isExistEmail")
    public HashMap<String, Object> isExistEmail(String user_email) {

        HashMap<String, Object> data = new HashMap<String, Object>();

        boolean exist = userService.isExistEmail(user_email);

        if (exist) {
            data.put("result", "fail");
        } else {
            data.put("result", "success");
        }

        return data;
    }

    //  이메일 유효성 검사
    @PostMapping(value = "checkEmail")
    public HashMap<String, Object> checkEmail(String user_email) {

        HashMap<String, Object> data = new HashMap<String, Object>();

        Random random = new Random();
        int checkNum = random.nextInt(888888) + 111111;

        // 메일 제목, 내용
        String subject = "회원가입 인증 메일입니다.";
        String content = "홈페이지를 방문해주셔서 감사합니다." +
                "인증 번호는 " + checkNum + " 입니다." +
                "\r\n" +
                "해당 인증번호를 인증번호 확인란에 기입하여 주세요.";

        // 보내는 사람
        String from = "hanbyeols333z@gmail.com";

        try {
            // 메일 내용 넣을 객체와, 이를 도와주는 Helper 객체 생성
            MimeMessage mail = javaMailSender.createMimeMessage();
            MimeMessageHelper mailHelper = new MimeMessageHelper(mail, "UTF-8");

            // 메일 내용을 채워줌
            mailHelper.setFrom(from, "관리자");	// 보내는 사람 셋팅
            mailHelper.setTo(user_email);		// 받는 사람 셋팅
            mailHelper.setSubject(subject);	// 제목 셋팅
            mailHelper.setText(content);	// 내용 셋팅

            // 메일 전송
            javaMailSender.send(mail);

        } catch(Exception e) {
            e.printStackTrace();
        }

        data.put("code", checkNum);

        return data;
    }

 

전달된 회원 정보의 파라미터의 유효성 검증을 위하여 Ajax 를 활용하여 중복 확인 및 유효성 검사를 진행합니다 Ajax 와 이메일 전송을 위해서는 json 라이브러리와 java-mail 라이브러리 두개가 필요하므로 pom.xml 에 의존성을 추가해줍니다.

 

        <!-- json 변환 라이브러리 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.5</version>
        </dependency>

        <!-- Mail 관련 라이브러리 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4.7</version>
        </dependency>

이메일 인증을 위한 메일 전송 기능을 사용하기 위해 mailSender 라는 빈을 추가 해주어야 합니다. applicationContext.xml 을 열어서 아래 내용을 추가해주세요

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
    <property name="host" value="smtp.gmail.com"/>
    <property name="port" value="587"/>
    <property name="username" value="hanbyeols333z@gmail.com"/>
    <property name="password" value="wuolscmvwxrndrxz"/>
    <!-- email 요청시는 SMTP -->
    <property name="javaMailProperties">
        <props>
            <prop key="mail.transport.protocol">smtp</prop>
            <prop key="mail.smtp.auth">true</prop>
            <prop key="mail.smtp.starttls.enable">true</prop>
            <prop key="mail.debug">true</prop>
            <prop key="mail.smtp.ssl.trust">smtp.gmail.com</prop>
            <prop key="mail.smtp.ssl.protocols">TLSv1.2</prop>
        </props>
    </property>
</bean>

1.4 유효성 검증을 위한 자바 스크립트 작성

/webapp/resources/dist/js/user 패키지안에 relatedUser.js 파일을 생성 해 주시고 아래와 같이 작성해주세요

window.addEventListener("DOMContentLoaded", function () {
    $("#joinIdInput").keyup(function () {
        var value = $(event.target).val();
        var num = value.search(/[0-9]/g);
        var eng = value.search(/[a-z]/ig);

        if (value.length < 5 || value.length > 10) {
            $("#alertId").css({
                "color": "red",
                "font-size": "10px"
            });
            $("#alertId").text("!  아이디는 5자리이상 10자리 이하여야 합니다.")
        } else if (value.replace(/\s| /gi, "").length == 0) {
            $("#alertId").css({
                "color": "red",
                "font-size": "10px"
            });
            $("#alertId").text("! 아이디에 공백은 사용할 수 없습니다.")
        } else if (num < 0 || eng < 0) {
            $("#alertId").css({
                "color": "red",
                "font-size": "10px"
            });
            $("#alertId").text("!  아이디는 영어+숫자로 이루어져야 합니다.")
        } else {
            $.ajax({
                type: "post",
                url: "./isExistId",
                data: {
                    user_id: $("#joinIdInput").val()
                },
                dataType: "json",
                //contentType : "application/x-www-form-urlencoded", // post
                success: function (data) {
                    if (data.result == "fail") {
                        $("#alertId").css({
                            "color": "red"
                        });
                        $("#alertId").text("! 이미 사용중인 아이디입니다.")
                    } else {
                        $("#alertId").css({
                            "color": "black"
                        });
                        $("#alertId").text("✔  사용 가능한 아이디입니다.")
                    }
                }
            });
        }
    });

    $("#changePassword").keyup(function () {
        var value = $(event.target).val();

        var num = value.search(/[0-9]/g);
        var eng = value.search(/[a-z]/ig);
        var spe = value.search(/[`~!@@#$%^&*|₩₩₩'₩";:₩/?]/gi);

        if (value.length < 8 || value.length > 30) {
            $("#alterPassword").css({
                "color": "red",
                "font-size": "10px"
            });
            $("#alterPassword").text("!  비밀번호는 8자리이상 30자리 이하여야 합니다.")
        } else if (value.replace(/\s| /gi, "").length == 0) {
            $("#alterPassword").css({
                "color": "red",
                "font-size": "10px"
            });
            $("#alterPassword").text("!  비밀번호에 공백은 사용할 수 없습니다.")
        } else if (num < 0 || eng < 0 || spe < 0) {
            $("#alterPassword").css({
                "color": "red",
                "font-size": "10px"
            });
            $("#alterPassword").text("!  비밀번호는 영어+숫자+특수문자로 이루어져야 합니다.")
        } else {
            $("#alterPassword").css({
                "color": "black",
                "font-size": "10px"
            });
            $("#alterPassword").text("✔  사용가능한 비밀번호입니다.");
        }
    });

    $("#confirmPassword").keyup(function () {
        var value = $("#confirmPassword").val();


        if (value != $("#changePassword").val()) {
            $("#alterPassword2").css({
                "color": "red",
                "font-size": "12px"
            });
            $("#alterPassword2").text("!  비밀번호가 일치하지 않습니다.")
            return;
        }
        ;
        $("#alterPassword2").css({
            "color": "black",
            "font-size": "10px"
        });
        $("#alterPassword2").text("✔  비밀번호가 일치합니다.");
    });

    $("#userNickName").keyup(function () {
        var value = $(event.target).val();
        var txt = value.search(/[가-힣]/g);

        if (value.length < 1 || value.length > 10) {
            $("#alertNickname").css({
                "color": "red",
                "font-size": "10px"
            });
            $("#alertNickName").text("!  닉네임은 1자리이상 10자리 이하여야 합니다.")
        } else if (value.replace(/\s| /gi, "").length == 0) {
            $("#alertNickname").css({
                "color": "red",
                "font-size": "10px"
            });
            $("#alertNickname").text("!  닉네임에 공백은 사용 할 수 없습니다.")
        } else if (txt < 0) {
            $("#alertNickname").css({
                "color": "red",
                "font-size": "10px"
            });
            $("#alertNickname").text("!  닉네임은 한글만 입력 가능합니다.")
        } else {
            $.ajax({
                type: "post",
                url: "./isExistNickName",
                data: {
                    user_nickname: $("#userNickName").val()
                },
                dataType: "json",
                success: function (data) {
                    if (data.result == "fail") {
                        $("#alertNickName").css({
                            "color": "red",
                            "font-size": "10px"
                        });
                        $("#alertNickName").text("!  이미 사용중인 닉네임 입니다.")
                    } else {
                        $("#alertNickName").css({
                            "color": "black",
                            "font-size": "10px"
                        });
                        $("#alertNickName").text("✔  사용 가능한 닉네임입니다.")
                    }
                }
            });
        }
    });

    $("#userPhone").keyup(function () {
        var value = $(event.target).val();
        var phone = $("#userPhone").val();
        var regex = new RegExp("^01([0|1|6|7|8|9])-?([0-9]{3,4})-?([0-9]{4})$");
        if (value.length < 13 || value.length > 13) {
            $("#alertPhone").css({
                "color": "red",
                "font-size": "10px"
            });
            $("#alertPhone").text("!  휴대폰번호는 하이폰포함 13글자여야 됩니다.");
        } else if (!regex.test(phone)) {
            $("#alertPhone").css({
                "color": "red",
                "font-size": "10px"
            });
            $("#alertPhone").text("!  휴대폰번호 정규식에 맞게끔 작성해주세요");
        } else {
            $.ajax({
                type: "post",
                url: "./isExistPhone",
                data: {
                    user_phone: $("#userPhone").val()
                },
                dataType: "json",
                success: function (data) {
                    if (data.result == "fail") {
                        $("#alertPhone").css({
                            "color": "red",
                            "font-size": "10px"
                        });
                        $("#alertPhone").text("!  이미 사용중인 휴대폰 번호 입니다.")
                    } else {
                        $("#alertPhone").css({
                            "color": "black",
                            "font-size": "10px"
                        });
                        $("#alertPhone").text("✔  사용 가능한 휴대폰번호입니다.");
                    }
                }
            });
        }
    });

    $("#userEmail").keyup(function () {
        var value = $(event.target).val();
        var email = $("#userEmail").val();
        var regex = new RegExp("^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$");
        if (!regex.test(email)) {
            $("#alertEmail").css({
                "color": "red",
                "font-size": "10px"
            });
            $("#alertEmail").text("!  이메일의 정규식에 맞게끔 작성 부탁드립니다. abc@gmail.com");
        } else {
            $.ajax({
                type: "post",
                url: "./isExistEmail",
                data: {
                    user_email: email
                },
                dataType: "json",
                success: function (data) {
                    if (data.result == "fail") {
                        $("#alertEmail").css({
                            "color": "red",
                            "font-size": "10px"
                        });
                        $("#alertEmail").text("!  이메일이 이미 사용중입니다.");
                    } else {
                        $("#alertEmail").css({
                            "color": "black",
                            "font-size": "10px"
                        });
                        $("#alertEmail").text("✔  사용 가능한 이메일주소입니다.");
                        $("#checkEmailButton").attr("disabled", false);
                    }
                }
            });
        }
    });

    var code = "";
    $("#checkEmailButton").click(function () {
        $.ajax({
            type: "post",
            url: "./checkEmail",
            data: {
                user_email: $("#userEmail").val()
            },
            dataType: "json",
            success: function (data) {
                if (data.result == "error") {
                    alert("서버와 통신중 에러가 발생했습니다.");
                    $("#alertCertified").css({
                        "color": "rad",
                        "font-size": "10px"
                    });
                    $("#alertCertified").text("!  서버 통신중 에러가 발생 하였습니다.");
                } else {
                    alert("인증번호 발송이 완료되었습니다. 입력한 이메일에서 인증번호 확인을 해주세요.");
                    $("#alertCertified").text("! 인증번호를 입력해주세요.")
                    $("#alertCertified").css({
                        "color": "red",
                        "font-size": "10px"
                    });
                    code = data.code;
                    $("#checkEmail").attr("disabled", false);
                    $("#checkEmailButton").attr("disabled", true);
                }
            }
        });
    });

    $("#checkEmail").keyup(function() {
        if ($("#checkEmail").val().length != 6) {
            $("#alertCertified").text("! 인증번호가 일치하지 않습니다. 다시 확인해주시기 바랍니다.")
            $("#alertCertified").css({
                "color": "red",
                "font-size": "10px"
            });
        } else if ($("#checkEmail").val() == code) {
            $("#alertCertified").text("✔ 메일인증이 완료되었습니다.")
            $("#alertCertified").css({
                "color": "green",
                "font-size": "10px"
            });
            $("#checkEmail").attr("disabled", true);
            $("#joinButton").attr("disabled", false);
            $("#userEmail").attr("disabled", false);
            $("#updateInfo").attr("disabled", false);
        }
    });

    $("#joinButton").click(function() {
        if ($("#alertId").text() != "✔  사용 가능한 아이디입니다.") {
            alert("아이디 중복 확인을 해주세요.");
            return;
        }

        if ($("#alertNickName").text() != "✔  사용 가능한 닉네임입니다.") {
            alert("닉네임 중복확인을 해주세요");
            return;
        }

        if ($("#alertPhone").text() != "✔  사용 가능한 휴대폰번호입니다.") {
            alert("휴대폰번호 중복 확인을 해주세요.");
            return;
        }

        if ($("#alertEmail").text() != "✔  사용 가능한 이메일주소입니다.") {
            alert("이메일 중복 확인을 해주세요");
            return;
        }

        if($("#alertCertified").text() != "✔ 메일인증이 완료되었습니다.") {
            alert("메일 인증을 해주세요");
            return;
        }

        $("#insertForm").submit();

        alert("회원가입이 완료 되었습니다.");
    });
});

위 소스코드를 보면 각 필드에서 키보드를 입력하면 유효성을 체크하고 그 이후 유효성 검증을 통과하게 되면 ajax를 호출하여 데이터 중복 확인을 하는 것을 확인 할 수 있습니다.


2. 중복확인 처리 확인

 

댓글