1cm

자바 프로그래밍_Day_110_회원 정보 수정, 회원 탈퇴 본문

국비지원_Java/Java Programming_2

자바 프로그래밍_Day_110_회원 정보 수정, 회원 탈퇴

dev_1cm 2022. 2. 22. 04:24
반응형

 

2022. 01. 20

 

 

- 회원 정보 수정

 

> myPage.jsp 생성

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

<c:set var="path" value="${ pageContext.request.contextPath }"/>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>회원 정보 수정</h2>
<div id="view-container">
<form id="memberFrm" action="${ path }/member/update" method="post">
    <table>
        <tr>
            <th>아이디</th>
            <td>
                <input type="text" name="userId" id="newId" 
                    value="${ loginMember.id }" readonly required >
            </td> 	
        </tr>
        <tr>
            <th>이름</th>
            <td>
                <input type="text" name="userName" id="userName" 
                    value="${ loginMember.name }" required>				
            </td> 	
        </tr>
            <tr>
            <th>휴대폰</th>
            <td>
                <input type="tel" placeholder="(-없이)01012345678" name="phone" id="phone" 
                    value="${ loginMember.phone }" maxlength="11">
            </td>
        </tr>
        <tr>
            <th>이메일</th>
            <td>
                <input type="email" placeholder="abc@abc.com" name="email" id="email"
                    value="${ loginMember.email }">												
            </td> 	
        </tr>
        <tr>
            <th>주소</th>
            <td>
                <input type="text" name="address" id="address"
                    value="${ loginMember.address }">
            </td> 	
        </tr>
        <tr>
            <th>취미</th>
            <td>
                <label><input type="checkbox" name="hobby" id="hobby0" 
                            value="운동" ${ fn:contains(loginMember.hobby, '운동') ? 'checked' : '' }>운동</label>
                <label><input type="checkbox" name="hobby" id="hobby1" 
                            value="등산" ${ fn:contains(loginMember.hobby, '등산') ? 'checked' : '' }>등산</label>
                <label><input type="checkbox" name="hobby" id="hobby2" 
                            value="독서" ${ fn:contains(loginMember.hobby, '독서') ? 'checked' : '' }>독서</label>
                <label><input type="checkbox" name="hobby" id="hobby3" 
                            value="게임" ${ fn:contains(loginMember.hobby, '게임') ? 'checked' : '' }>게임</label>
                <label><input type="checkbox" name="hobby" id="hobby4" 
                            value="여행" ${ fn:contains(loginMember.hobby, '여행') ? 'checked' : '' }>여행</label>
            </td> 		
        </tr>
    </table>
    <button type="button" id="updatePwd">비밀번호변경</button>
    <input type="submit" value="정보수정">
    <input type="button" id="btnDelete" value="탈퇴">
</form>
</div>
<script>
$(document).ready(() => {
    $("#updatePwd").on("click", () => {
        const url = "${ pageContext.request.contextPath }/member/updatePwd";
        const status = "left=500px,top=200px,width=400px,height=200px";

        open(url, "", status);
    });

    $("#btnDelete").on("click", () => {
        if(confirm("정말로 탈퇴하시겠습니까?")) {
            location.replace("${ pageContext.request.contextPath }/member/delete");
        }
    });
});
</script>
</body>
</html>

 

 

URL 주소를 입력, <a> -> GET요청

 

 

> MemberController.java

@GetMapping("/member/myPage")
public String myPage() {

    return "member/myPage";
}

 

 

myPage -> 로그인 후에 페이지를 들어갈 수 있게 하기, 미로그인 시 접근 못하도록 처리하기

 

 

> home.jsp

<c:if test="${ !empty loginMember }">
	<a href="${ path }/member/myPage">
		${ loginMember.name }
	</a>님, 안녕하세요.
	
	<form action="${ path }/logout" method="post">
		<button type="submit">로그아웃</button>
	</form>
</c:if>

로그인 후 Anchor태그 사용해서 myPage 페이지로 이동

 

 

- 로그아웃 후 접근 시 메세지 출력 후 home으로 이동시키기

 

참고 : https://goddaehee.tistory.com/154

 

[Spring] Filter, Interceptor, AOP 차이 및 정리

[Spring] Filter, Interceptor, AOP 차이 및 정리 안녕하세요. 갓대희 입니다. 이번 포스팅은 [ [Spring] 필터, 인터셉터, AOP 정리 ] 입니다. : ) 공통 프로세스에 대한 고민 자바 웹 개발을 하다보면, 공통..

goddaehee.tistory.com

Controller로 가기 전 처리해야 될 내용을 Interceptor에서 처리

-> 스프링의 모든 Bean 접근 가능

-> Dispatcher Servlet과 Controller사이의 요청을 가로채서 필요한 작업을 처리한다고 보면 된다.

 

 

클래스 생성 후 특정 클래스 상속할 수 있도록 하기

 

 

 

 

> LoginCheckInterceptor.java

package com.kh.mvc.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class LoginCheckInterceptor extends HandlerInterceptorAdapter{

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// 컨트롤러가 실행되기 전에 필요한 작업을 할 수 있는 메소드이다.
		// 반환값이 false일 경우 컨트롤러를 실행하지 않는다.
		log.info("preHandle() call..");

		return super.preHandle(request, response, handler);
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		// 컨트롤러가 실행된 후에 필요한 작업을 할 수 있는 메소드이다.
		log.info("postHandle() call..");

		super.postHandle(request, response, handler, modelAndView);
	}
	
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		// 컨트롤러의 처리가 끝나고 화면(View) 처리까지 모두 완료되면 실행되는 메소드이다.
		log.info("afterCompletion() call..");
		
		super.afterCompletion(request, response, handler, ex);
	}
	
	@Override
	public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// 비동기 요청 시 postHandle, afterCompletion이 수행되지 않고 afterConcurrentHandlingStarted 메소드가 실행된다.
		log.info("afterConcurrentHandlingStarted() call..");
		
		super.afterConcurrentHandlingStarted(request, response, handler);
	}


}

 

 

 

servlet-context에서 interceptor 관리를 한다. -> 웹관련 설정이기 때문

 

 > servlet-context.xml

<!-- 
    인터셉터 설정
      - 인터셉터가 웹 관련 설정이기 때문에 root-context.xml이 아닌 servlet-context.xml에 작성한다.	
-->
<interceptors>
    <interceptor>
        <!-- 인터셉터를 적용시킬 요청(컨트롤러) 선택 
        <mapping path="/member/myPage"/>
        <mapping path="/member/update"/>

        와일드카드 /*, /**의 차이점

        /member/*
          - /member/insert (O)
          - /member/update (O)
          - /member/insert/10 (X)
          - /member/update/user (X)

        /member/**
          - /member/insert (O)
          - /member/update (O)
          - /member/insert/10 (O)
          - /member/update/user (O)

        -->
        <mapping path="/member/**"/>

        <!-- 인터셉터를 제외시킬 요청(컨트롤러) 선택 -->
        <exclude-mapping path="/member/enroll"/>
        <exclude-mapping path="/member/idCheck"/>

        <!-- 인터셉터 등록 -->
        <beans:bean id="loginCheckInterceptor" class="com.kh.mvc.common.interceptor.LoginCheckInterceptor"/>
    </interceptor>
</interceptors>

 

LoginCheckInterceptor 패키지명 변경 후 남아있는 빈 폴더 삭제

 

 

 

 

- preHandle에 로직 작성

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
    // 컨트롤러가 실행되기 전에 필요한 작업을 할 수 있는 메소드이다.
    // 반환값이 false일 경우 컨트롤러를 실행하지 않는다.
    log.info("preHandle() call..");

    Member loginMember = (Member) request.getSession().getAttribute("loginMember");

    if(loginMember == null) {
        request.setAttribute("msg", "로그인 후 이용이 가능합니다.");
        request.setAttribute("location", "/");
        request.getRequestDispatcher("/WEB-INF/views/common/msg.jsp").forward(request, response);

        return false;
    }

    return super.preHandle(request, response, handler);

}

 

 

interceptor

/*
 * 인터셉터
 *  - 컨트롤러에 들어오는 요청(HttpRequest)과 응답(HttpResponse)을 가로채는 역할을 한다.
 *  - 인터셉터를 구현하기 위해서는 HandlerInterceptorAdapter 클래스를 상속하는 방법으로 구현해야 한다.
 *  
 * 필터와의 차이점
 *  - 필터는 Servlet 수행 전에 실행된다. Spring 자원을 이용할 수 없다. (web.xml에 설정)
 *  - 인터셉터는 DispatcherServlet 수행 후 컨트롤러에 요청을 넘기기 전에 실행된다. Spring 자원을 이용할 수 있다. (Servlet-context.xml에 설정)
 *  
 */

 

요청 처리 - 필터, 인터셉터

비즈니스 로직 적용 - AOP -> 서비스 앞뒤로 감싸는 역할

 

 

 

 

 

- 회원정보 수정 로직 작성

- 요청을 받아서 처리할 메소드가 없기 때문에 404에러 발생

- post 요청 (@PostMapping)

@PostMapping("/member/update")
public String update() {


    return "member/myPage";
}

 

 

 

로그인이 되어 있어야 update가 가능하기 때문에 interceptor를 탈 수 있게 아래 코드를 작성해준다.

<mapping path="/member/update"/>

 

 

 

Controller에서 수정하기 눌렀을 때 수정한 데이터가 잘 넘어올 수 있게 Model 객체로 받아와야 한다.

이 과정에서 Set을 해줄 때 name속성과 필드명이 동일해야 적용이 가능하다.

그리고 save에서 update 로직을 수행할 시 member객체의 no값이 0이 아니어야 한다. -> 따로 안받아왔기 때문

처리 방법 : 1. no값을 hidden으로 숨겨서 같이 보내기

 

 

2. SessionAttribute 활용

@PostMapping("/member/update")
public ModelAndView update(
        ModelAndView model,
        @SessionAttribute(name="loginMember") Member loginMember,
        @ModelAttribute Member member) {
    int result = 0;

    member.setNo(loginMember.getNo());

    result = service.save(member);

    if(result > 0) {
        model.addObject("loginMember", member);
        model.addObject("msg", "회원정보 수정을 완료했습니다.");
        model.addObject("location", "/member/myPage");
    } else {
        model.addObject("msg", "회원정보 수정에 실패했습니다.");
        model.addObject("location", "/member/myPage");
    }

    model.setViewName("common/msg");

    return model;
}

 

 

 

> MemberServiceImpl.java

@Override
@Transactional
public int save(Member member) {
    int result = 0;

    if(member.getNo() != 0) {
        // update
        result = mapper.updateMember(member);
    } else {
        // 패스워드 암호화
        member.setPassword(passwordEncoder.encode(member.getPassword()));

        // insert
        result = mapper.insertMember(member);
    }

//		if(true) {
//			throw new RuntimeException();
//		}

    return result;

}

 

 

 

> MemberMapper.java

@Mapper
public interface MemberMapper {
//	@Select("select * from member")
//	List<Member> findAll();
	
	// 아래 메소드를 호출 시 실제 mapper.xml에 있는 쿼리문을 수행한 다음 결과를 리턴해주는 인터페이스의 구현체가 만들어진다.
	Member findMemberById(@Param("id") String id);
	
	int insertMember(Member member);

	int updateMember(Member member);
	
}

 

메소드도 추가 시켜줌

 

 

- session 갱신

-> service에서 찾아서 넣어주기 (MemberController.java)

model.addObject("loginMember", service.findMemberById(loginMember.getId()));

 

 

> MemberService.java

package com.kh.mvc.member.model.service;

import org.springframework.transaction.annotation.Transactional;

import com.kh.mvc.member.model.vo.Member;

//@Transactional
public interface MemberService {

	Member findMemberById(String id);
	
	Member login(String id, String password);

	int save(Member member);

	Boolean isDuplicateID(String userId);


}

 

 

> MemberServiceImpl.java

package com.kh.mvc.member.model.service;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.kh.mvc.member.model.dao.MemberMapper;
import com.kh.mvc.member.model.vo.Member;

//@Service("빈 ID")
@Service
//@Transactional
public class MemberServiceImpl implements MemberService {
	
	@Autowired
	private MemberMapper mapper;
	
	@Autowired
	private BCryptPasswordEncoder passwordEncoder;
	
//	@Autowired
//	private SqlSession session;
	

	@Override
	public Member findMemberById(String id) {
		
		return mapper.findMemberById(id);
	}
	
	@Override
	public Member login(String id, String password) {
		Member member = null;

//		member = dao.findMemberById(session, id);
		member = this.findMemberById(id);
		
//		System.out.println(mapper.findAll());
		
//		System.out.println(passwordEncoder.encode(password));
//		System.out.println(member.getPassword());
//		System.out.println(passwordEncoder.matches(password, member.getPassword()));
			
	//	if (member != null && member.getPassword().equals(passwordEncoder.encode(password))) {
	//		return member;			
	//	} else {
	//		return null;
	//	}
		
		return member != null && 
				passwordEncoder.matches(password, member.getPassword()) ? member : null;

	}

	@Override
	@Transactional
	public int save(Member member) {
		int result = 0;
		
		if(member.getNo() != 0) {
			// update
			result = mapper.updateMember(member);
		} else {
			// 패스워드 암호화
			member.setPassword(passwordEncoder.encode(member.getPassword()));

			// insert
			result = mapper.insertMember(member);
		}
		
//		if(true) {
//			throw new RuntimeException();
//		}
	
		return result;

	}

	@Override
	public Boolean isDuplicateID(String id) {

		return this.findMemberById(id) != null;
	}


}

-> findMemberById 수정

 

 

 

 

<!-- 
    인터셉터 설정
      - 인터셉터가 웹 관련 설정이기 때문에 root-context.xml이 아닌 servlet-context.xml에 작성한다.	
-->
<interceptors>
    <interceptor>
        <!-- 인터셉터를 적용시킬 요청(컨트롤러) 선택
        <mapping path="/member/myPage"/>
        <mapping path="/member/update"/>

        와일드카드 /*, /**의 차이점

        * : 한 스텝에 대해서만 어떤 값이 와도 성공, 그 다음 스텝이 추가되면 실패함
        /member/*
          - /member/insert (O)
          - /member/update (O)
          - /member/insert/10 (X)
          - /member/update/user (X)

        ** : 현재 스텝 뿐만 아니라 그 하위 스텝까지도 허용
        /member/**
          - /member/insert (O)
          - /member/update (O)
          - /member/insert/10 (O)
          - /member/update/user (O)


        아래는 전체 적용
        -->
        <mapping path="/member/**"/>

        <!-- 
        인터셉터를 제외시킬 요청(컨트롤러) 선택 
         : ex. member로 시작하는 요청들 중 제외하고 싶은 요청만 뺄 수 있다.
        -->
        <exclude-mapping path="/member/enroll"/>
        <exclude-mapping path="/member/idCheck"/>

        <!-- 인터셉터 등록 -->
        <beans:bean id="loginCheckInterceptor" class="com.kh.mvc.common.interceptor.LoginCheckInterceptor"/>
    </interceptor>
</interceptors>

 

 

* 인터셉터를 통한 로그인체크

 

 

 

 

- 회원 탈퇴

 

@GetMapping("/member/delete")
public String delete() {

    return "member/myPage";
}

 

interceptor에 작업을 해놨기 때문에

<mapping path="/member/**"/>

로그인체크가 적용이 된다.

 

 

 

 

- 비즈니스 로직 작성

 

> MemberController.java

@GetMapping("/member/delete")
public ModelAndView delete(ModelAndView model,
        @SessionAttribute(name="loginMember") Member loginMember) {
    int result = 0;

    result = service.delete(loginMember.getNo());

    if(result > 0) {
        model.addObject("msg", "정상적으로 탈퇴되었습니다.");
        model.addObject("location", "/logout");
    } else {
        model.addObject("msg", "회원 탈퇴에 실패하였습니다.");
        model.addObject("location", "/member/myPage");			
    }
    
    model.setViewName("common/msg");
    
    return model;
}

-> 탈퇴 후 logout, 실패 시 myPage로 넘겨준다.

 

 

> MemberService.java

package com.kh.mvc.member.model.service;

import org.springframework.transaction.annotation.Transactional;

import com.kh.mvc.member.model.vo.Member;

//@Transactional
public interface MemberService {

	Member findMemberById(String id);
	
	Member login(String id, String password);

	int save(Member member);

	Boolean isDuplicateID(String userId);

	int delete(int no);


}

-> delete추가

 

 

 > MemberServiceImpl.java 에서 delete 정의된 메소드 추가

@Override
public int delete(int no) {

    return 0;
}

 

mapper를 통해서 쿼리문을 실행시켜줄 것이기 때문에 return값 수정

@Override
public int delete(int no) {

    return mapper.deleteMember(no);
}

 

 

> member-mapper.xml

<delete id="deleteMember" parameterType="_int">
    UPDATE MEMBER
    SET
        STATUS = 'N'
    WHERE
        NO = #{no}
</delete>

 

 

> MemberMapper.java 에 deleteMember 추가

package com.kh.mvc.member.model.dao;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import com.kh.mvc.member.model.vo.Member;

@Mapper
public interface MemberMapper {
//	@Select("select * from member")
//	List<Member> findAll();
	
	// 아래 메소드를 호출 시 실제 mapper.xml에 있는 쿼리문을 수행한 다음 결과를 리턴해주는 인터페이스의 구현체가 만들어진다.
	Member findMemberById(@Param("id") String id);
	
	int insertMember(Member member);

	int updateMember(Member member);

	int deleteMember(int no);
	
}

 

 

logout을 GetMapping으로 수정

> home.jsp

<form action="${ path }/logout" method="get">
    <button type="submit">로그아웃</button>
</form>

 

> MemberController.java

// 로그아웃 처리 (SessionStatus 객체 사용)
@GetMapping("/logout")
public String logout(SessionStatus status) {

    log.info("status.isComplete() : {}", status.isComplete());

    // SessionStatus 객체의 setComplete() 메소드로 세션 스코프로 확장된 객체들을 지워준다.
    status.setComplete();

    log.info("status.isComplete() : {}", status.isComplete());

    return "redirect:/";
}
반응형
Comments