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

# Spring MVC 게시판 예제 10 : 내 프로필 페이지 구현하기

edenDev 2023. 2. 25.

본 포스팅의 예제는 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 작성 연습
7 Spring Interceptor

 

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

순서 포스팅제목
1 IntelliJ를 이용한 Spring MVC Project 생성 하기
2 Bootstrap AdminLTE Template 적용하기
3 ExceptionResovler : 예외페이지 처리
4 Spring AOP 적용하기
5 회원가입 구현하기
6 회원 유효성 검사 (Feat. JavaScript)
7 회원 유효성 검사 (Feat. Validation)
8 HttpSession을 이용한 로그인 구현하기
9 아이디 찾기 및 패스워드 찾기

1. 내 프로필 페이지 구현 하기

 

1.1 내 프로필 페이지 영속 계층(Persistence Tier) 구현 하기

 

src/기본패키지/user/persistence 패키지 안에 UserDAO 인터페이스와 UserDAOImpl 클래스 파일 두개에 아래와 같이 내용을 추가 해주세요.

//  회원정보 조회
public UserVo getUser(String user_id);

//  회원정보 수정
public void updateUserInfo(UserVo param);

//  회원정보 탈퇴
public void deleteUserInfoByUserNo(UserVo param);
//  회원정보 조회
public UserVo getUser(String user_id) {
    return sqlSession.selectOne(NAMESPACE + ".getUser", user_id);
}

//  회원정보 수정
@Override
@LogException
public void updateUserInfo(UserVo param) {
    sqlSession.update(NAMESPACE + ".updateUserInfo", param);
}

//  회원정보 탈퇴
@Override
@LogException
public void deleteUserInfoByUserNo(UserVo param) {
    sqlSession.update(NAMESPACE + ".deleteUserInfoByUserNo", param);
}

/resources/mappers/user/ 패키지 안에 UserSQLMapper.xml 파일 안에 아래와 같이 내용을 추가해주세요.

<!-- 회원정보 조회 -->
<select id="getUser" resultType="UserVo">
    select *
    from eden_user
    WHERE user_id = #{user_id}
</select>

<!-- 회원정보 수정 -->
<update id="updateUserInfo">
    update eden_user
    SET question_no     = #{question_no},
        user_nickname   = #{user_nickname},
        user_phone      = #{user_phone},
        user_email      = #{user_email},
        user_findAnswer = #{user_findAnswer}
    where user_id = #{user_id}
</update>

<!-- 회원정보 탈퇴 -->
<update id="deleteUserInfoByUserNo">
    UPDATE eden_user
    SET user_status = 'Inactive'
    WHERE user_no = #{user_no}
      AND user_pw = #{user_pw}
</update>

1.2 내 프로필 페이지 비지니스 계층 (Business Tier) 구현

 

src/기본패키지/user/service 패키지 안에 UserService 인터페이스와 UserServiceImpl 클래스 파일에 아래와 같이 내용을 추가 해주세요


// 회원정보 조회
public UserVo getUser(String user_id);

//  회원정보 업데이트
public void updateUserInfo(UserVo param);

//  회원정보 탈퇴
public void deleteUserInfoByUserNo(UserVo param);

// 회원정보 조회
public UserVo getUser(String user_id) {
    return userDAO.getUser(user_id);
}

//  회원정보 수정
@Override
@LogException
public void updateUserInfo(UserVo param) {
    userDAO.updateUserInfo(param);
}

//  회원정보 탈퇴
@Override
@LogException
public void deleteUserInfoByUserNo(UserVo param) {
    userDAO.deleteUserInfoByUserNo(param);
}

1.3 내 프로필 컨트롤러 구현

 

src/기본패키지/user/controller 패키지 안에 UserController 클래스와 RestUserController 클래스 파일의 내용을 아래와 같이 추가해주세요.


// 내정보 페이지
@GetMapping("profile")
public String profile(Model model, HttpSession session) {
    UserVo sessionUser = (UserVo) session.getAttribute("sessionUser");

    model.addAttribute("data", userService.getJoinQuestionList());

    return "user/profile";
}

//  회원정보 수정
@PostMapping("updateUserInfo")
public HashMap<String, Object> updateUserInfo(UserVo param, HttpSession session) {

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

    UserVo sessionUser = userService.getUser(param.getUser_id());

    if (sessionUser != null) {
        data.put("result", "success");
        userService.updateUserInfo(param);
    } else {
        data.put("result", "fail");
    }
    return data;
}

//  현재 비밀번호 체크
@PostMapping("checkPw")
@LogException
public HashMap<String, Object> checkPw(String current_password, String user_id) {

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

    UserVo userVo = userService.getUser(user_id);

    if (userVo != null) {
        if (BCrypt.checkpw(current_password, userVo.getUser_pw())) {
            data.put("result", "success");
        } else {
            data.put("result", "fail");
        }
    }

    return data;
}

//  비밀번호 수정
@PostMapping(value = "modifyPassword")
@LogException
public HashMap<String, Object> modifyPassword(UserVo userVo, HttpSession session) {

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

    UserVo sessionUser = userService.getUser(userVo.getUser_id());

    if (sessionUser != null) {

        /* 비밀번호 변경 */
        String changePassword = BCrypt.hashpw(userVo.getUser_pw(), BCrypt.gensalt());
        sessionUser.setUser_pw(changePassword);
        userService.getUserUpdatePw(sessionUser);

        /* 비밀번호 변경후 로그아웃 */
        session.invalidate();
    }

    return data;
}

//  회원탈퇴
@PostMapping(value = "deleteUserInfoByUserNo")
public HashMap<String, Object> deleteUserInfoByUserNo(UserVo vo, HttpSession session, HttpServletRequest request, HttpServletResponse response) {

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

    UserVo sessionUser = (UserVo) session.getAttribute("sessionUser");
    String password = vo.getUser_pw();

    if (!BCrypt.checkpw(vo.getUser_pw(), sessionUser.getUser_pw())) {
        data.put("result", "fail");
    } else {
        data.put("result", "success");
        userService.deleteUserInfoByUserNo(sessionUser);
        session.invalidate();
    }
    return data;
}

1.4 내 프로필 페이지 JSP 구현

 

web-inf/views/user 패키지 안에 profile.jsp 파일 생성 후 내용을 아래와 같이 작성해주세요.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%@ include file="../include/head.jsp" %>
<%@ include file="../include/user_menu.jsp" %>

<html>
<body class="hold-transition skin-green-light sidebar-mini" oncopy="return false" oncut="return false"
      onpaste="return false">
<div class="wrapper">

    <%@ include file="../include/top_menu.jsp" %>

    <%@ include file="../include/left_menu.jsp" %>

    <div class="content-wrapper">
        <section class="content container-fluid">
            <div class="row">
                <div class="col-md-5">
                    <div class="box box-primary">
                        <div class="box-body box-profile">
                            <img class="profile-user-img img-responsive img-circle"
                                 src="/resources/dist/img/profile/${sessionUser.user_image}" alt="User profile picture">

                            <h3 class="profile-username text-center">${sessionUser.user_nickname}</h3>
                            <div class="text-center">
                                <a href="#" class="btn btn-primary btn-xs" data-toggle="modal"
                                   data-target="#userPhotoModal">
                                    <i class="fa fa-photo"> 프로필사진 수정</i>
                                </a>
                            </div>
                            <ul class="list-group list-group-unbordered">
                                <li class="list-group-item">
                                    <b>아이디</b> <a class="pull-right">${sessionUser.user_id}</a>
                                </li>
                                <li class="list-group-item">
                                    <b>이메일</b> <a class="pull-right">${sessionUser.user_email}</a>
                                </li>
                                <li class="list-group-item">
                                    <b>가입일자</b>
                                    <a class="pull-right">
                                        <fmt:formatDate value="${sessionUser.user_join_date}"
                                                        pattern="yyyy-MM-dd a hh:mm"/>
                                    </a>
                                </li>
                                <li class="list-group-item">
                                    <b>최근 로그인 일자</b>
                                    <a class="pull-right">
                                        <fmt:formatDate value="${sessionUser.user_last_connection_date}"
                                                        pattern="yyyy-MM-dd a hh:mm"/>
                                    </a>
                                </li>
                                <li class="list-group-item">
                                    <b>생년월일</b>
                                    <a class="pull-right">
                                        <fmt:formatDate value="${sessionUser.user_birth}" pattern="yy년MM월dd일"/>
                                    </a>
                                </li>
                            </ul>
                        </div>
                        <div class="box-footer text-center">
                            <a href="#" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#userInfoModal">
                                <i class="fa fa-info-circle"> 회원정보 수정</i>
                            </a>
                            <a href="#" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#userPwModal">
                                <i class="fa fa-question-circle"> 비밀번호 수정</i>
                            </a>
                            <a href="#" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#userOutModal">
                                <i class="fa fa-user-times"> 회원 탈퇴</i>
                            </a>
                        </div>
                    </div>
                </div>
            </div>
        </section>
    </div>

    <%@ include file="../include/footer.jsp" %>

</div>

<%@ include file="../include/plugin_js.jsp" %>
</body>
</html>

<div class="modal fade" id="userInfoModal">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span></button>
                <h4 class="modal-title">회원정보 수정</h4>
            </div>
            <div class="model-body" align="center">
                <form id="updateForm" action="../user/updateUserInfo">
                    <div class="row mt-2">
                        <div class="col">

                            <div class="row mt-1">
                                <div class="col">
                                    <label for="inputUserId">아이디</label>
                                </div>
                            </div>

                            <div class="row mt-1">
                                <div class="col">
                                    <input type="text" id="inputUserId" class="form-control" name="user_id"
                                           value="${sessionUser.user_id}" disabled="disabled">
                                </div>
                            </div>

                            <div class="row mt-1">
                                <div class="col">
                                    <label for="userNickName">닉네임</label>
                                </div>
                            </div>

                            <div class="row mt-1">
                                <div class="col">
                                    <input type="text" id="userNickName" class="form-control" name="user_nickname"
                                           value="${sessionUser.user_nickname}">
                                </div>
                            </div>

                            <div class="row mt-1">
                                <div class="col" id="alertNickName" style="font-size: 12px;"></div>
                            </div>

                            <div class="row mt-1">
                                <div class="col">
                                    <label for="userPhone">휴대폰번호</label>
                                </div>
                            </div>

                            <div class="row mt-1">
                                <div class="col">
                                    <input type="text" id="userPhone" class="form-control" name="user_phone"
                                           value="${sessionUser.user_phone}">
                                </div>
                            </div>

                            <div class="row mt-1">
                                <div class="col" id="alertPhone" style="font-size:12px"></div>
                            </div>

                            <div class="row mt-1">
                                <div class="col-md-7">
                                    <input type="text" id="userEmail" class="form-control" name="user_email"
                                           value="${sessionUser.user_email}">
                                </div>
                                <div class="col">
                                    <button type="button" id="checkEmailButton"
                                            class="btn btn-primary pwModBtn" disabled='disabled'>인증번호 발송
                                    </button>
                                </div>
                            </div>

                            <div class="row mt-1">
                                <div class="col" id="alertEmail" style="font-size:12px;"></div>
                            </div>

                            <div class="row mt-1">
                                <div class="col">
                                    <label for="checkEmail">인증번호</label>
                                </div>
                            </div>

                            <div class="row mt-1">
                                <div class="col" id="alertCertified" style="font-size:12px;"></div>
                            </div>

                            <div class="row mt-1">
                                <div class="col">
                                    <input class="form-control" id="checkEmail" type="text"
                                           placeholder="인증번호를 입력해주세요." aria-label="default input example"
                                           disabled="disabled">
                                </div>
                            </div>

                            <div class="row mt-2">
                                <div class="col">
                                    <select class="form-select" name="question_no" id="userQuestion"
                                            aria-label="Default select example">
                                        <c:forEach items="${data }" var="question">
                                            <option value="${question.question_no }">
                                                    ${question.question_content }</option>
                                        </c:forEach>

                                    </select>
                                </div>
                            </div>

                            <div class="row mt-2">
                                <div class="col">
                                    <input type="text" id="userFindAnswer" class="form-control"
                                           value="${sessionUser.user_findAnswer}">
                                </div>
                            </div>

                            <div class="row mt-1">
                                <div class="col" id="alertFindAnswer" style="font-size:12px;"></div>
                            </div>
                        </div>
                    </div>
                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-danger pull-left" data-dismiss="modal">닫기</button>
                <button type="button" class="btn btn-primary infoModBtn" id="updateInfo" disabled="disabled">수정</button>
            </div>
        </div>
    </div>
</div>

<div class="modal fade" id="userPwModal">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span></button>
                <h4 class="modal-title">비밀번호 변경</h4>
            </div>
            <div class="modal-body" data-rno>
                <div class="row mt-1">
                    <div class="col">
                        <div class="row mt-1">
                            <div class="col">
                                <input type="hidden" id="uid" value="${sessionUser.user_id}">
                                <label for="currentPassword">현재 비밀번호</label>
                            </div>
                        </div>

                        <div class="row mt-1">
                            <div class="col-md-7">
                                <input type="password" class="form-control"
                                       placeholder="현재 사용중인 패스워드를 입력해주세요" id="currentPassword">
                            </div>
                            <div class="col">
                                <button type="button" class="but btn-info pwModBtn" id="checkPassword">체크하기</button>
                            </div>
                        </div>

                        <div class="row mt-1">
                            <div class="col" id="alertCurrentPassword" style="font-size:12px;"></div>
                        </div>

                        <div class="row mt-1">
                            <div class="col">
                                <label for="newPassword">새로운 비밀번호 :</label>
                            </div>
                        </div>

                        <div class="row mt-1">
                            <div class="col">
                                <input type="password" class="form-control" placeholder="새로운 패스워드를 입력해주세요"
                                       id="newPassword" disabled="disabled">
                            </div>
                        </div>

                        <div class="row mt-1">
                            <div class="col" id="alertNewPassword"></div>
                        </div>

                        <div class="row mt-1">
                            <div class="col">
                                <label for="checkingNewPassword">비밀번호 확인 : </label>
                            </div>
                        </div>

                        <div class="row mt-1">
                            <div class="col">
                                <input type="password" class="form-control"
                                       placeholder="새로운 패스워드를 한번 더 입력해주세요" id="checkingNewPassword"
                                       disabled="disabled">
                            </div>
                        </div>

                        <div class="row mt-1">
                            <div class="col-md-7" id="alertCheckingPassword"></div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-danger pull-left" data-dismiss="modal">닫기</button>
                <button type="button" class="btn btn-primary pull-right pwModBtn" id="modifyPw" disabled="disabled">
                    저장
                </button>
            </div>
        </div>
    </div>
</div>

<div class="modal fade" id="userOutModal">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span></button>
                <h4 class="modal-title">회원을 탈퇴하시겠습니까?</h4>
            </div>
            <div class="modal-body" data-rno>
                <div class="row mt-1">
                    <div class="col">
                        <div class="row mt-1">
                            <div class="col">
                                <label for="currentPassword2">현재 비밀번호</label>
                            </div>
                        </div>

                        <div class="row mt-1">
                            <div class="col-md-7">
                                <input type="password" class="form-control"
                                       placeholder="현재 사용중인 패스워드를 입력해주세요" id="currentPassword2">
                            </div>
                            <div class="col">
                                <button type="button" class="but btn-info pwModBtn" id="checkPassword2">체크하기</button>
                            </div>
                        </div>
                        <div class="row mt-1">
                            <div class="col" id="alertCurrentPassword2" style="font-size:12px;"></div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-danger pull-left" data-dismiss="modal">닫기</button>
                <button type="button" class="btn btn-primary myInfoModModalBtn" id="deleteUser" disabled="disabled">탈퇴
                </button>
            </div>
        </div>
    </div>
</div>

1.5 내 프로필 JavaScript 구현

 

webapp/resources/dist/js/user/ 패키지 안에 relatedUser.js 파일에 아래의 내용을 추가 해주세요


$("#userFindAnswer").keyup(function () {
    $("#updateInfo").attr("disabled", false);
});

$("#updateInfo").click(function () {

    if ($("#alertNickName").text() == "!  이미 사용중인 닉네임 입니다.") {
        alert("닉네임을 확인해주세요");
    } else if ($("#alertPhone").text() == "!  이미 사용중인 휴대폰번호 입니다.") {
        alert("휴대폰번호을 확인해주세요");
    } else if ($("#alertEmail").text() == "!  이메일이 이미 사용중입니다.") {
        alert("이메일주소를 확인해주세요");
    } else if ($("#alertCertified") == "! 인증번호가 일치하지 않습니다. 다시 확인해주시기 바랍니다.") {
        alert("인증번호를 확인해주세요");
    } else {
        $.ajax({
            type: "post",
            url: "../user/updateUserInfo",
            data: {
                question_no: $("#userQuestion").val(),
                user_id: $("#inputUserId").val(),
                user_nickname: $("#userNickName").val(),
                user_phone: $("#userPhone").val(),
                user_email: $("#userEmail").val(),
                user_findAnswer: $("#userFindAnswer").val()
            },
            dataType: "json",
            success: function (data) {
                if (data.result == 'fail') {
                    alert("유저 정보 확인에 실패 하였습니다 다시 확인해주세요");
                } else if (data.result == 'success') {
                    alert("유저 정보 업데이트에 성공 하였습니다");
                    location.reload();
                }
            }
        });
    }
});

$("#checkPassword").click(function () {
    $.ajax({
        type: "post",
        url: "../user/checkPw",
        data: {
            user_id: $("#uid").val(),
            current_password: $("#currentPassword").val()
        },
        dataType: "json",
        success: function (data) {
            if (data.result == 'fail') {
                $("#alertCurrentPassword").css({
                    "color": "red",
                    "font-size": "12px"
                });
                $("#alertCurrentPassword").text("!  현재 비밀번호와 일치 하지 않습니다. 다시 확인해주세요");
            } else {
                $("#alertCurrentPassword").css({
                    "color": "green",
                    "font-size": "12px"
                });
                $("#alertCurrentPassword").text("✔  현재 비밀번호와 일치 합니다.");
                $("#currentPassword").attr("disabled", true);
                $("#newPassword").attr("disabled", false);
            }
        }
    });
});

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

$("#checkingNewPassword").keyup(function () {
    var value = $("#newPassword").val();
    if (value != $("#checkingNewPassword").val()) {
        $("#alertCheckingPassword").css({
            "color": "red",
            "font-size": "12px"
        });
        $("#alertCheckingPassword").text("!  비밀번호가 일치하지 않습니다.")
        return;
    }
    ;
    $("#alertCheckingPassword").css({
        "color": "green",
        "font-size": "10px"
    });
    $("#alertCheckingPassword").text("✔  비밀번호가 일치합니다.");
    $("#newPassword").attr("disabled", true);
    $("#modifyPw").attr("disabled", false);
});

$("#modifyPw").click(function () {
    $.ajax({
        type: "post",
        url: "../user/modifyPassword",
        data: {
            user_id: $("#uid").val(),
            user_pw: $("#checkingNewPassword").val()
        },
        dataType: "json",
        success: function (data) {
            if (data.result == 'fail') {
                alert("비밀번호 변경에 실패 하였습니다. 다시 확인해주세요");
            } else {
                alert("비밀번호 변경에 성공하였습니다");
                location.reload();
            }
        }
    });
});

$("#checkPassword2").click(function () {
    $.ajax({
        type: "post",
        url: "../user/checkPw",
        data: {
            user_id: $("#uid").val(),
            current_password: $("#currentPassword2").val()
        },
        dataType: "json",
        success: function (data) {
            if (data.result == 'fail') {
                $("#alertCurrentPassword2").css({
                    "color": "red",
                    "font-size": "12px"
                });
                $("#alertCurrentPassword2").text("!  현재 비밀번호와 일치 하지 않습니다. 다시 확인해주세요");
            } else {
                $("#alertCurrentPassword2").css({
                    "color": "green",
                    "font-size": "12px"
                });
                $("#alertCurrentPassword2").text("✔  현재 비밀번호와 일치 합니다.");
                $("#deleteUser").attr("disabled", false);
            }
        }
    });
});

$("#deleteUser").click(function () {
    $.ajax({
        type: "post",
        url: "../user/deleteUserInfoByUserNo",
        data: {
            user_id: $("#uid").val(),
            user_pw: $("#currentPassword2").val()
        },
        dataType: "json",
        success: function (data) {
            if (data.result == 'fail') {
                alert("비밀번호을 확인해주세요");
            } else {
                alert("회원탈퇴에 성공 하였습니다.");
                location.reload();
            }
        }
    });
});

1.6 내 프로필 페이지 화면 확인


2. 정리

 

이번에는 내 프로필 페이지와 회원정보 변경 회원탈퇴 비밀번호 변경에 대해서 구현 해보았습니다. 다음에는 인터셉터를 활용한 권한 체크를 구현해보도록 하겠습니다.

댓글