ㅁ 화면
ㅁ member-mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="memberMapper">
<resultMap id="memberResultMap" type="MemberDto">
<result column="user_no" property="userNo" />
<result column="user_id" property="userId" />
<result column="user_pwd" property="userPwd" />
<result column="user_name" property="userName" />
<result column="email" property="email" />
<result column="gender" property="gender" />
<result column="phone" property="phone" />
<result column="address" property="address" />
<result column="profile_url" property="profileURL" />
<result column="signup_date" property="signupDt" />
<result column="modify_date" property="modifyDt" />
<result column="status" property="status" />
</resultMap>
<!-- 로그인(암호화 적용 전)
<select id="selectMember" resultMap="memberResultMap">
select
user_no
, user_id
, user_pwd
, user_name
, email
, gender
, phone
, address
, profile_url
, signup_date
, modify_date
, status
from member
where status in ('Y', 'A')
and user_id = #{userId}
and user_pwd = #{userPwd}
</select> -->
<select id="selectMember" resultMap="memberResultMap">
select
user_no
, user_id
, user_pwd
, user_name
, email
, gender
, phone
, address
, profile_url
, signup_date
, modify_date
, status
from member
where status in ('Y', 'A')
and user_id = #{userId}
</select>
<!-- 회원가입 -->
<insert id="insertMember">
insert
into member
(
user_no
, user_id
, user_pwd
, user_name
, email
, gender
, phone
, address
)
values
(
seq_uno.nextval
, #{userId}
, #{userPwd}
, #{userName}
, #{email}
, #{gender}
, #{phone}
, #{address}
)
</insert>
<!-- 아이디중복체크(아이디 수 조회) -->
<select id="selectUserIdCount" resultType="_int">
select
count(*)
from member
where user_id = #{checkId}
</select>
<!-- 한 개의 컬럼, 한 개의 숫자만 조회된다. 자바에서의 int로 반환되게끔 한다. -->
<!-- 회원정보수정 -->
<update id="updateMember">
update
member
set user_name = #{userName}
, email = #{email}
, phone = #{phone}
, address = #{address}
, gender = #{gender}
, modify_date = sysdate
where user_id = #{userId}
</update>
<!-- 회원탈퇴 (행을 삭제하진 않고 개인정보만 일부 지운다. 이름 첫글자만 남기고 마스킹처리. -->
<update id="deleteMember">
update
member
set email = null
, gender = null
, phone = null
, address = null
, profile_url = null
<!-- , user_name = rpad( substr(user_name, 1, 1), 최종반환글자수, 덧붙일문자열 ) -->
<!-- , user_name = rpad( substr(user_name, 1, 1), length(user_name), '*' ) -->
, user_name = substr(user_name, 1, 1) || rpad('*', length(user_name)-1, '*')
, status = 'N'
, modify_date = sysdate
where user_id = #{userId}
</update>
<!-- 회원프로필이미지 수정 -->
<update id="updateProfileImg">
update
member
set profile_url = #{profileURL}
, modify_date = sysdate
where user_no = #{userNo}
</update>
</mapper>
- rpad 안쓴다.
length가 사실 문자열의 길이가 아니고 바이트다. 한글은 바이트가 달라서 복잡하다.
특수문자는 1바이트다. 영문 숫자도.
ㅁ myinfo.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
#profileImg{
width:250px;
height:250px;
border:1px solid lightgray;
border-radius: 50%;
}
</style>
</head>
<body>
<div class="container p-3">
<!-- Header, Nav start -->
<jsp:include page="/WEB-INF/views/common/header.jsp"/>
<!-- Header, Nav end -->
<!-- Section start -->
<section class="row m-3" style="min-height: 500px">
<div class="container border p-5 m-4 rounded">
<h2 class="m-4">마이페이지</h2>
<br>
<div align="center">
<img id="profileImg" src="${contextPath}<c:out value='${loginUser.profileURL}' default='/resources/images/defaultProfile.png' />" onclick="$('#profileImgFile').click();">
<input type="file" class="file" id="profileImgFile" style="display:none;" accept="image/*">
</div>
<script>
$(document).ready(function(){
$('#profileImgFile').on("change", function(evt){
const files = evt.target.files; // FileList {File, File}
if(files.length != 0){ // 선택된 파일이 있으면 비동기식으로 파일 전송 업로드 처리하고, db에 기록한다.
let formData = new FormData();
formData.append("uploadFile", files[0]);
$.ajax({
url: '${contextPath}/member/updateProfile.do',
type: 'post',
data: formData,
processData: false,
contentType: false,
success: function(resData){
if(resData == "SUCCESS"){
location.reload();
}else{
alert("프로필 이미지 변경 실패");
}
}
})
}
})
})
</script>
<form action="${contextPath}/member/update.do" method="post" id="myinfo_form">
<div class="form-group">
<label for="userId">* ID :</label>
<input type="text" class="form-control" id="userId" name="userId" value="${ loginUser.userId }" readonly><br>
<label for="userName">* Name :</label>
<input type="text" class="form-control" id="userName" name="userName" value="${ loginUser.userName }"><br>
<label for="email"> Email :</label>
<input type="email" class="form-control" id="email" name="email" value="${ loginUser.email }"><br>
<label for="phone"> Phone :</label>
<input type="tel" class="form-control" id="phone" name="phone" value="${ loginUser.phone}"><br>
<label for="address"> Address :</label>
<input type="text" class="form-control" id="address" name="address" value="${ loginUser.address }"><br>
<label for=""> Gender : </label>
<input type="radio" name="gender" id="Male" value="M">
<label for="Male">남자</label>
<input type="radio" name="gender" id="Female" value="F">
<label for="Female">여자</label><br>
<script>
$(function(){
$("#myinfo_form :radio").filter("[value=${loginUser.gender}]").attr("checked", true);
})
</script>
</div>
<br>
<div class="btns" align="center">
<button type="submit" class="btn btn-primary">수정하기</button>
<button type="button" class="btn btn-danger" data-toggle="modal" data-target="#deleteForm">회원탈퇴</button>
</div>
</form>
</div>
</section>
<!-- Section end -->
<!-- Footer start -->
<jsp:include page="/WEB-INF/views/common/footer.jsp"/>
<!-- Footer end -->
</div>
<!-- 회원탈퇴 버튼 클릭시 보여질 Modal -->
<div class="modal" id="deleteForm">
<div class="modal-dialog">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<h4 class="modal-title">회원탈퇴</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<!-- Modal body -->
<div class="modal-body" align="center">
<b>
탈퇴 후 복구가 불가능합니다. <br>
정말로 탈퇴 하시겠습니까?
</b>
<form action="${contextPath}/member/resign.do" method="post">
비밀번호 :
<input type="password" name="userPwd" required>
<button type="submit" class="btn btn-danger">탈퇴하기</button>
</form>
</div>
</div>
</div>
</div>
<script src="${contextPath}/resources/js/fileValidate.js"></script>
</body>
</html>
- id가 deleteform인 modal이 뜨게 되어있다.
ㅁ MemberConroller
package com.br.spring.controller;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.br.spring.dto.MemberDto;
import com.br.spring.service.MemberService;
import com.br.spring.util.FileUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequestMapping("/member")
@RequiredArgsConstructor
@Controller
public class MemberController {
private final MemberService memberService;
private final BCryptPasswordEncoder bcryptPwdEncoder;
private final FileUtil fileUtil;
@PostMapping("/signin.do")
public void signin(MemberDto m, HttpServletResponse response, HttpSession session, HttpServletRequest request) throws IOException {
MemberDto loginUser = memberService.selectMember(m);
// 로그인 성공시 => 세션에 회원정보를 담고, alert와 함께 메인 페이지로 이동
// 로그인 실패시 => alert와 함께 기존에 보던 페이지 유지(아이디, 비번 입력하는 로그인 폼 유지. 작성하던 입력데이터도 그대로.)
// script문을 응답데이터로 돌려줘서 흐름제어
response.setContentType("text/html; charset=utf-8");
PrintWriter out = response.getWriter();
out.println("<script>");
if(loginUser != null && bcryptPwdEncoder.matches(m.getUserPwd(), loginUser.getUserPwd())) { // 로그인 성공
session.setAttribute("loginUser", loginUser);
out.println("alert('" + loginUser.getUserName() + "님 환영합니다~');");
out.println("location.href = '" + request.getHeader("referer") + "';"); // 메인페이지가 아닌 이전에 보던 페이지로 이동
}else { // 로그인 실패
out.println("alert('로그인에 실패하였습니다. 아이디 및 비밀번호를 다시 확인해주세요.');");
out.println("history.back();"); // modal이 띄워진 상태였으면 modal도 유지된다.
}
out.println("</script>");
}
@GetMapping("/signout.do")
public String signout(HttpSession session) {
session.invalidate();
return "redirect:/";
}
@GetMapping("/signup.do")
public void signupPage() { } //WEB-INF/views/member/signup.jsp
@ResponseBody
@GetMapping("/idcheck.do")
public String idCheck(String checkId) {
return memberService.selectUserIdCount(checkId) == 0 ? "YYYY" : "NNNN";
}
@PostMapping("/insert.do")
public String signup(MemberDto m, RedirectAttributes rdAttributes) {
log.debug("암호화 전 member: {}", m);
m.setUserPwd( bcryptPwdEncoder.encode(m.getUserPwd()) );
log.debug("암호화 후 member: {}", m);
int result = memberService.insertMember(m);
// 성공시 alert와 함께 메인페이지 이동
// 실패시 alert와 함께 기존에 작업중이던 페이지 유지
if(result > 0) {
rdAttributes.addFlashAttribute("alertMsg", "성공적으로 회원가입 되었습니다.");
}else {
rdAttributes.addFlashAttribute("alertMsg", "회원가입에 실패하였습니다.");
rdAttributes.addFlashAttribute("historyBackYN", "Y");
}
return "redirect:/";
}
@GetMapping("/myinfo.do")
public void myinfoPage() {}
@PostMapping("/update.do")
public String modify(MemberDto m
, RedirectAttributes rdAttributes
, HttpSession session) {
int result = memberService.updateMember(m);
if(result > 0) {
session.setAttribute("loginUser", memberService.selectMember(m));// 동일한 키값으로 담으면 덮어씌워진다.
rdAttributes.addFlashAttribute("alertMsg", "성공적으로 정보수정 되었습니다.");
}else {
rdAttributes.addFlashAttribute("alertMsg", "정보수정에 실패하였습니다.");
}
return "redirect:/member/myinfo.do";
}
@ResponseBody
@PostMapping("/updateProfile.do")
public String modifyProfile(MultipartFile uploadFile, HttpSession session) {
// 현재 로그인한 회원정보
MemberDto loginUser = (MemberDto)session.getAttribute("loginUser");
// 현재 로그인한 회원의 기존 프로필 url (불필요한 파일은 삭제처리하는 것이 좋다)
String originalProfileURL = loginUser.getProfileURL();
// 변경요청한 프로필 파일 업로드
Map<String, String> map = fileUtil.fileupload(uploadFile, "profile");
// 현재 로그인한 회원 객체의 profileURL 필드값을 새로운 프로필 이미지 경로로 수정
String newURL = map.get("filePath") + "/" + map.get("filesystemName"); // 새로 저장된 프로필 이미지의 url 주소.
loginUser.setProfileURL(newURL);
// db에 기록하기 위해 service 호출
int result = memberService.updateProfileImg(loginUser);
if(result > 0) {
// 성공시 기존 프로필이 존재했다면 파일을 삭제한다.
if(originalProfileURL != null) {
new File(originalProfileURL).delete();
}
return "SUCCESS";
}else {
// 실패시 변경요청시 전달된 파일을 삭제한다.
new File(loginUser.getProfileURL()).delete();
loginUser.setProfileURL(originalProfileURL); // 기존걸로 다시 변경
return "FAIL";
}
}
@PostMapping("/resign.do")
public String resign(String userPwd // 사용자가 입력한 비밀번호값(평문)이 담긴다.
, HttpSession session
, RedirectAttributes rdAttributes) {
MemberDto loginUser = (MemberDto)session.getAttribute("loginUser"); // 로그인 유저에 암호문이 담겨있다.
if(bcryptPwdEncoder.matches(userPwd, loginUser.getUserPwd())) { // 비밀번호를 맞게 입력한 경우
int result = memberService.deleteMember(loginUser.getUserId());
rdAttributes.addFlashAttribute("alertMsg", "성공적으로 탈퇴되었습니다. 그동안 이용해주셔서 감사합니다.");
session.invalidate(); // 세션만료시키기
}else { // 비밀번호를 틀린 경우
rdAttributes.addFlashAttribute("alertMsg", "비밀번호가 틀렸습니다. 다시 입력해주세요.");
rdAttributes.addFlashAttribute("historyBackYN", "Y"); // modal이 떠있는 상태로 보이게끔
}
return "redirect:/"; // 메인페이지 url 재요청 (비밀번호를 틀린 경우에는 실행되지 않는 코드)
}
}
ㅁ
- 비밀번호가 틀린 경우 다시 마이페이지로 돌아온다. modal창은 안보임.
- 비밀번호를 맞게 입력하면 메인페이지로 이동된다.
- 탈퇴한 아이디, 비번으로 로그인시 실패한다.
- 회원가입도 불가능하다. 이미 id가 db에 기록되어 있다.
ㅁ 회원서비스는 끝.
- 비밀번호 변경은 나 중에 변경문제다.
- 마이페이지에 버튼하나 둬서 비밀번호 ㅂ변경 기능 구현하는 것이 문제다.
ㅁ 게시판 서비스다.
'Spring' 카테고리의 다른 글
[웹프로젝트] 13. 게시판 검색 (0) | 2024.10.30 |
---|---|
[웹프로젝트] 12. 게시판 목록 조회 (0) | 2024.10.29 |
[웹프로젝트] 10. 마이페이지(3) 프로필이미지 변경 요청 (1) | 2024.10.29 |
[웹프로젝트] 9. 마이페이지(2) 수정 요청 (0) | 2024.10.29 |
[웹프로젝트] 8. 마이페이지(1) 조회 (1) | 2024.10.29 |