본문 바로가기

Programming/Framework

DAY 158. Spring 회원 정보 수정

 

00. 화면 준비 myPage.jsp

 

▼ 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 }" />

 

 

01. MemberController.java

 

- 일단 myPage로 이동하는 URL에 대한 Get 처리(해당하는 URL이 ("/member/myPage") 이고, 메소드가 get 일 때 처리할 메소드를 만든다.)

 

- 디스패처 서블릿이 뷰 리졸버를 통해서 return하는 이름을 가진 view로 포워딩 시킨다.

 

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

    return "/member/myPage";
}

 

 

01-1. 로그인한 사람한테만 마이페이지 정보수정이 가능하도록 처리

- 로그인하고 나서야, 정보수정 하는 페이지로 이동시킬 수 있도록, 버튼이나 <a> 활용해서 연결하기

 

- home.jsp에 <c:if> 태그 활용

 

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

 

01-2. 로그아웃하고나서 URL 접근이 안되게 처리

 

Spring에서 제공해주는 Interceptor를 사용해보자.

 

 

https://goddaehee.tistory.com/154

 

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

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

goddaehee.tistory.com

 

 

filter와 Interceptor의 차이
-> spring 자원에 접근 가능 여부

 

 filter : wep.xml에 설정한 것, 빈 자원을 사용할 수 없이 스프링 차원에 접근할 수 없다. (사용자 요청이 서블릿으로 들어가기 전에 처리하는 것)

 

▷ Interceptor : 빈 자원을 사용하기 위해 스프링 차원에 접근할 수 있다. (컨트롤러로 넘어가기 전에 요청을 가로채는 것)

 

 

 

 

▶ Interceptor

- 디스패처 서블릿과 컨트롤러 사이에서 요청을 가로채서 컨트롤러 전에 처리해야 할 작업을 처리하는역할

 

▷ preHandle() 

- 컨트롤러가 실행되기 전에 필요한 작업을 할 수 있는 메소드

- 반환 값이 false일 경우 컨트롤러를 실행하지 않고, 이 단계에서 요청을 끝낸다.

 

 

 postHandle()

- 컨트롤러가 실행된 후에 필요한 작업을 할 수 있는 메소드

 

 

 afterCompletion()

- 컨트롤러의 처리가 끝나고 화면(View) 처리(랜더링)까지 모두 완료되면 실행되는 메소드

 

 

 afterConcurrentHandlingStarted()

- 비동기 요청(서블릿 3.0 이상) 시 postHandle, afterCompletion이 수행되지 않고 afterConcurrentHandlingStarted 메소드가 실행된다.

 

- preHandle()  -> afterConcurrentHandlingStarted() 실행

 

 

 

02. LoginCheckInterceptor.java

- com > kh > mvc > common > interceptor 밑에 클래스 생성

 

- pulic class LoginCheckInterceptor extends HandlerInterceptorAdapter를 상속하도록 한다.

 

 

 

 

Alt + Shift + S 단축키 눌러서

다음처럼 체크하고 Finish해서 메소드 생성

 

 

- 필터를 만든다고 해서 모든 요청에 대해서 서블릿 필터를 태우지 않고, filter 클래스를 만들고 xml에 filter mapping 해주거나 어노테이션을 통해서 어떤 요청, 어떤 서블릿을 수행할 때 필터를 태울지 xml 파일에 지정한 것처럼

 

- 인터셉터도 요청 사이에서 어떤 작업을 가로채서 필요한 여러 작업들을 할 때 하기 위해서 사용하게 되고, 모든 요청에 대해서 적용할 필요 없기 때문에 어떤 요청에 대해서 해당 인터셉터를 태울 것인지 지정해야 한다. 즉, 인터셉터가 적용될 요청을 지정한다.

 

 

03. servlet-context.xml


인터셉터 설정


- 인터셉터가 웹 관련 설정(in 스프링 영역)이기 때문에 root-context.xml이 아닌 servlet.context.xml에 작성한다.

 

 

<interceptors>
    <interceptor>
        <!-- 인터셉터를 적용시킬 요청(컨트롤러) 선택 -->
        <mapping path="/member/myPage"/>
        <!-- 인터셉터 등록 -->
        <beans:bean id="LoginCheckInterceptor" class="com.kh.mvc.common.interceptor.LoginCheckInterceptor"/>
    </interceptor>
</interceptors>

 

 

04. LoginCheckInterceptor.java

 

- Dispatcher Servlet에서 myPage(Controller)쪽으로 가기전에 Interceptor에서 요청을 가로채서 로그인 했는지 안했는지를 체크할 것이다.

 

- 즉, 컨트롤러를 태윅 전에 로그인 체크가 먼저여야 하는 것

 

- 로그인 체크하는 것이기 때문에 preHandle()에 작성

 

- 세션 영역에서 loginMember를 가져와서,

 

- loginMember가 null이면 컨트롤러를 태우지 않고 msg쪽으로 직접 포워딩 시킨다.

 

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

    log.info("preHandle() call..");

	// attribute에서 리턴하는 데이터 타입은 Object이기 때문에 형변환 필수
    Member loginMember = (Member) request.getSession().getAttribute("loginMember");

    if(loginMember == null) {
        // 컨트롤러를 태우는게 아니라 msg로 포워딩 시키기
        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);
}

 

 

더보기
더보기

[ 여기까지 정리 ]

 

▶ 인터셉터

- 컨트롤러에 들어오는 요청(HttpRequest)과 응답(HttpResponse)을 가로채는 역할을 한다.

- 인터셉터를 구현하기 위해서는 HandlerInterceptorAdapter 클래스를 상속하는 방법으로 구현해야 한다.

 

- 인터셉터는 DispatcherServlet 수행 후에 컨트롤러에 요청을 넘기기 전에 실행된다.

- 스프링 자원을 이용할 수 있다.

- Servlet-context.xml에 설정한다.

 

▶ 필터

- 필터는 Servlet 수행 전에 실행된다. 

- 스프링 자원을 이용할 수 없다.

- web.xml에 설정한다.

 

※ 언제 필터, 인터셉터, AOP를 사용하나요?

- 필터 : 요청이 넘어오기 전에 처리해야 할 사항들(인코딩, 보안 등)

- 인터셉터 : 요청이 처리되기 전에 수행하고, 스프링 자원을 사용해야할 경우

-> 어떤 요청에 대해서 처리해야 할 것이 있을 때,

 

- AOP : 비즈니스 로직을 수행하기 전, 후에 코드가 삽입되어야 할 경우

-> 어떤 비즈니스 로직에 대해서 적용되어야 할 부분이 있을 경우(Service 클래스 앞 뒤로 감싸는 역할)

 

 


 

 

여기까지 구현 되었으면, 이제 실제로 회원 정보의 값을 변경해서 완료 버튼을 누르면 /member/update로 요청을 보내고, 요청을 받아서 회원 정보를 수정하는 로직을 짜보자.

 

 

05. MemberController.java

 

@PostMapping("/member/update")
public String update(       
        @ModelAttribute Member member) {
	
	log.info(member.toString());
    
    return "member/myPage";
}

 

- 하나의 객체로 사용자가 post 날리는 정보를 받아서 잘 받아오도록 하려면 @ModelAttribute 어노테이션 사용한다. 그러면 스프링에서 자동으로 객체를 만들고, setter를 통해서 값을 주입한다.

 

- 이 때, 조건은 실제 만드는 객체의 필드의 필드명과, 사용자가 보내는 데이터의 name 속성의 값이 동일해야 한다. (동일하지 않다면 같도록 바꾼다.)

 

 

- 정보를 수정하는 것도, 로그인 되어있는 회원만이 가능하기 때문에 인터셉터를 적용할 수 있도록 만들자.

 

<interceptors>
    <interceptor>
        <!-- 인터셉터를 적용시킬 요청(컨트롤러) 선택 -->
        <mapping path="/member/myPage"/>
        <mapping path="/member/update"/>
        <!-- 인터셉터 등록 -->
        <beans:bean id="LoginCheckInterceptor" class="com.kh.mvc.common.interceptor.LoginCheckInterceptor"/>
    </interceptor>
</interceptors>

 

 

 

 

06. MemberServiceImpl.java

- update 로직을 수행하려면 member 객체의 PK인 No가 0이 아니여야 한다.

 

- 그러니까 사용자로부터 값을 가져올 때, no 값도 가져와야 한다.

 

[ 처리 방법 ]

1) myPage.jsp에서 hidden으로 no 값을 포함시켜서 보낸다.

<div id="view-container">
	<form id="memberFrm" action"${ path }/member/update" method="post">
    		<input type="hidden" name="no" value="${ loginMember.no }">
        
        이하 생략

 

07. MemberController.java

 

2) session 영역의 loginMember를 가져와서 member의 no를 찾아오기

 

@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", service.findMemberById(loginMember.getId()));
        model.addObject("msg", "회원 정보 수정을 완료했습니다.");
        model.addObject("location", "/member/myPage");
    } else {
        model.addObject("msg", "회원 정보 수정이 실패했습니다.");			
        model.addObject("location", "/member/myPage");
    }

    model.setViewName("common/msg");

    return model;
}

 

- @SessionAttribute 어노테이션 활용

 

- ModelAndView를 매개 값으로 받기(컨트롤러에서 view로 전달해줄 데이터 + view에 대한 정보를 저장한 객체 -> 그 정보들을 디스패처 서블릿한테 전달해준다.)

 

- result 결과에 따라서 업데이트 성공 여부를 판단한다. 그에 따라서 msg를 세팅한다.

 

- model에서 포워딩할 view의 이름을 지정한다. by setViewName, 그리고 리턴

 

- 업데이트 된 정보는 어디에 담아줄까요? => Model에

 

- WHY? loginMember(대신 이름이 이거여야 함)가 model에 들어가도 scope 는 session으로 확장되어 있기 때문에 session 객체를 만들 필요가 없다. 

 

 

 

08. MemberServiceImpl.java

 

- mapper에 실행하라고 할 메소드 이름은 mapper.xml에 등록한 해당 쿼리문의 id와 같아야 한다.

 

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

    if(member.getNo() != 0) {
        // update
        result = mapper.updateMember(member);
    } else {
        // insert
        member.setPassword(passwordEncoder.encode(member.getPassword()));

        result = mapper.insertMember(member);
    }

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

    return result;
}

 

 

09. MemberServiceImpl.java

- 리팩토링 해보자.

 

- 적용해야 할 URL이 굉장히 많은 경우, 일일히 적용하기 보다는 적용시키지 않을 요청을 빼서 적는게 더 효율적인 경우에 <exclude-mapping> 태그를 활용한다.

 

<interceptors>
    <interceptor>
        <!-- 인터셉터를 적용시킬 요청(컨트롤러) 선택 -->
        <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>

 

 

더보기
더보기

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

 

/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)

 

 

 

 

'Programming > Framework' 카테고리의 다른 글

DAY 160. Spring MVC 게시판 페이징  (0) 2022.01.26
DAY 159. Spring 회원 탈퇴  (0) 2022.01.25
DAY 157. Spring ID 중복 검사  (0) 2022.01.23
DAY 156. Spring 회원 가입 기능  (0) 2022.01.22
DAY 155. Spring 로그인 기능  (0) 2022.01.21