본문 바로가기

백엔드 개발/SpringMVC

세션(Session) 실습(2)

<개요>

저번 시간에 했던 내용을 복기 해보겠다.

먼저 홈화면에서 게시판 화면으로 들어가려 했는데, 이때 클라이언트로 로그인 상태인지 아닌지 체크한다. 

로그인 상태라면 바로 게시판 화면으로 넘어간다. 로그인 안 한 상태면, 로그인 창을 띄운다. 

클라이언트가 로그인을 하면 다시 홈 화면으로 이동한다. 

로직은 밑과 같다. 

우리가 이번에 하려는 것은 로그인 성공하면 다시 자기가 갈려고 했던 페이지로 다시 클라이언트를 보내는 것이다. 

난 무신사에서 옷을 살려고 했는데, 로그인 상태가 아닌 것을 까먹은 적이 있다. 로그인을 하니 다시 홈 화면으로 가서 내가 살려 했던 옷을 다시 찾아야 해 짜증 났던 적이 있다. 

이번 웹페이지 에서는 그런 애로사항이 없도록 게시판에 들어가려 했던 사용자가 로그인 할 시 그대로 게시판 페이지로 다시 가도록 만들려고 한다. 

1. 게시판 들어가려 했던 비 로그인 사용자가 로그인 시 자동으로 게시판으로 가게 하는 로직

(1) 로직 설계

from은 요청이 온 주소를 나타낸다. to는 요청이 갈려고 하는 목적지를 나타낸다. 

우리는 id/pwd가 DB에 있는지 확인하는 로그인 controller에서, 만약 첫 요청 목적지(to)가 게시판이면 로그인 성공 후 바로 게시판으로 가고, 첫 요청 목적지가 없으면 (그냥 로그인 한 경우) 홈화면으로 가도록 설정해야한다. 

어떻게 할 수 있을까?  

(2) 구현 방법

이 로직에서 처음에 갈려고 했던 목적지(to)가 board/list (게시판) 이었다는 것을 알아차릴 수 있는 방법은  첫 요청의 목적지가 ch2/board/list 였다는 것을 아는 것 뿐이다. 

하지만 서버는 현재 온 요청의 from과 to 밖에 기억하지 못한다. 

그래서 우리는 첫 요청을 할 때의 요청 목적지(to)를 값으로 받아서 id/pwd 확인하는 로그인 컨트롤러한테까지 전달 해야한다. 

전달하는 방법은 다음과 같다. 

만약 게시판 GET 요청을 한 클라이언트가 로그인 하지 않은 상태라면,(sessionScope에 id 가 null)

redirect로 login 화면으로 갈 때, 스트링 쿼리로 처음 목적지를 집어넣는다. 

현재 온 요청의 from과 to를 파라미터 값으로 받는 명령어는 다음과 같다. 

//현재 서버에 온 요청의 from정보(요청이 어디서부터 왔는지)를 뽑아내는 명령어 
// 여기서 referer가 어디서 요청했는지를 뜻한다.
request.getHeader("referer");

//현재 서버에 온 요청의 to정보(요청이 갈려고 하는 목적지가 어디인지)를 뽑아내는 명령어
request.getRequestURI();

 여기서 request.getRequestURI()를 사용해 요청의 목적지를 변수로 받은 뒤, 이걸 로그인 컨트롤러까지 보내면 된다. 

그리고 로그인 컨트롤러에서 같이 온 URL이 있으면 로그인 후 그 URL로, 없으면 그냥 Default인 홈 화면을 GET 하도록 설계하면 된다. 

이제 코드를 뜯어보며 구현해보겠다.

(3) 구현(코드리뷰)

x. 요청들의 from과 to가 어딘지 알 수 있도록 작업 전 전처리기 filter 수정

// 필터를 적용할 요청의 URL 지정 - 모든 요청에 대해 이 필터를 적용.
@WebFilter(urlPatterns="/*")
public class PerformanceFilter implements Filter {
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		// 초기화 작업
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		// 1. 전처리 작업 
		
		//2. 서블릿 또는 다음 필터 호출
		chain.doFilter(request, response);
		
		// 3. 후처리 작업 (요청의 from, to 출력 )
		
		// 3-1 그냥 Servlet으로 받은 거 Http로 형변환 
		HttpServletRequest req = (HttpServletRequest)request;
		
		// 3-1 요청 보낸 주소, 요청의 목적지, 요청의 종류 변수로 받기 
		String referer = req.getHeader("referer");
		// 요청의 종류 뽑기 (GET인지, POST인지)
		String method = req.getMethod();
		
		// 3-1 요청 끝나고 from, to랑 무슨 요청인지 뽑기
		System.out.println("["+referer+"] ->"+ method+"["+req.getRequestURI()+"]");

	}

	@Override
	public void destroy() {
		// 정리 작업
	}

}

초기화와 전처리는 우리가 진짜 행하려는 수행 전에 일어나는 것이다. 

후처리와 정리 작업은 본 작업을 행하고 난 뒤 일어나는 것이다. 본 작업은 dofilter 부분에서 우리가 작동 시키려는 servlet 가서 진행된다. (여기서 다른 필터로 한번 더 가서 필터링 작업을 n번 더 할 수도 있다.)

 

후처리 작업에서 우리는 요청의 종류, from,to를 출력하므로서 어떤 작업이 이루어졌고, 요청의 흐름이 어떻게 되는지 한 눈에 알 수 있다. 

그러면 직접 저 위의 로직대로 웹 페이지를 움직여 보겠다.

해당 흐름을 설명하면 다음과 같다. 

ㄱ. 홈 화면에서 게시판으로 조회 요청을 했지만, 로그인이 안되어 있는 상태라, BoarderController가 로그인 화면으로 URL 재조정을 했고, 브라우저는 자동으로 /ch2/login/login으로 Get 요청을 했다. 

ㄴ. 클라이언트가 로그인 화면에서 로그인 정보를 치고 로그인 버튼을 눌렀다. (서버 쪽에 내용 쓰는 POST 요청)

ㄷ. LoginController에 의해 클라이언트가 친 로그인 비번의 DB 일치여부를 확인했고, 일치해서 Home화면으로 redirect 했다.

 

아직 로직 구현을 안 했기에 최종적으로 HOME으로 간다.

a. 먼저 게시판 조회(GET) 요청이 왔을 시 로그인 여부를 check하는 BoardController 바꾸기

@Controller
@RequestMapping("/board")
public class BoardController{
		//1. /board/list mapping
	@GetMapping("/list")
	public String list(HttpServletRequest request) {
		//2. 로그인체크 - 안했으면 로그인 화면으로 이동, 
		//			했으면 게시판 JSP View 보여주기
		// ** 추가: StringQuery로 원 요청이 원하던 목적지 URL도 redirect URL에 추가해준다. 
		
		if(!(logincheck(request))) {
			return "redirect:/login/login?toURL="+request.getRequestURL();
		}
		
		return "boardList";
	}


	//3.  로그인 체크하는 매소드 구현
	// 	request에 적혀 있는 Session id를 통해 우리가 쓰는 세션 객체를 찾아 맵핑
	// 	그 세션 객체안에 id 값이 있는지 확인. 없으면 로그인 안한 것, 있으면 로그인 한 것.
	private boolean logincheck(HttpServletRequest request) {
		// 세션을 얻어서
		HttpSession session = request.getSession();
		// 세션에 id가 있는지 확인, 있으면 true를 반환
		return session.getAttribute("id") != null;
	}
}

스트링 쿼리로 원 요청이 가고자 한 목적지 URL을 보냈으니, JSP에서 이 내용을 받아서 어딘가에 저장해둬야 한다. 

하지만 이 정보는 굳이 클라이언트한테 보여줄 필요가 없다. 따라서 hidden으로 설정해 로그인 컨트롤러로 전달은 되지만 보이지는 않게 만든다.

b. 로그인 화면에서 URL 값을 받아서 post 시 다른 값들과 같이 전달하도록, loginForm 바꾸기

        <input type="hidden" name ="toURL" value ="${param.toURL}">
        
        <!-- loginFOrm.jsp에 이 줄을 추가하면 된다.
        
        	 input이 toURL 이라는 이름의 변수를 request객체에 값과 함께 맵 형태로 써준다.
        
        	*처음엔 type = "text"로 두고 컨트롤러에서 제대로 도착하는지 체크해보고,
        	 된다면 hidden 으로 바꾸자. 
        	 param.A 은 요청의 body 혹은 스트링쿼리에 적힌 A라는 이름의 파라미터의 값이다. -->

c. 로그인 시 처음에 갈려고 했던 화면으로 가도록 (갈려고 한데 없으면 HOME 화면) LoginCotroller 바꾸기

@Controller
@RequestMapping("/login")
public class LoginController {
	
	//조회만 할시엔 로그인 입력창을 보여줘라
	@GetMapping("/login")
	public String loginform() {
		return "loginForm";
	}
	
	//로그아웃 시 세션 종료하고 홈 화면으로 URL 재조정
	@GetMapping("/logout")
	public String logout(HttpSession session) {
		// 1. 세션을 종료
		session.invalidate();
		// 2. 홈으로 이동
		return "redirect:/";
	}
	
	// 내용을 쓰고 로그인 요청(POST)를 했을시, 
	@PostMapping("/login")
	public String login(String id, String pwd, String toURL, boolean rememberId, 
			HttpServletRequest request ,HttpServletResponse response ) throws Exception {

		
		// 1. id와 pwd를 확인
		if(!loginCheck(id,pwd)) {
			String msg = URLEncoder.encode("id 혹은 패스워드가 올바르지 않습니다.", "utf-8");

			// 2-1 일치하지 않으면 loginform 으로 redirect 
            // 스트링 쿼리로 적어서 화면에 보일 메세지도 같이 보냄.
			return "redirect:/login/login?msg="+msg;			
		}
		
		
		// 2-2 id와 pwd가 일치하면 세션 객체 얻어와서 그 안에 id를 저장.
		
		// 	세션 객체 얻어오기
		HttpSession session = request.getSession();
		
		//	세션 객체에 id를 저장
		session.setAttribute("id", id);
		
		// 2-3 rememberId 클라이언트가 체크 했을 시 id 이름과 값을 쿠키에 넣기
		//		체크 풀었을 시 쿠키에 있는 id 지우기
		if(rememberId) {
		// 1. 쿠키를 생성 
			Cookie cookie = new Cookie("id", id);
		// 2. 응답에 저장
			response.addCookie(cookie);
		}else {
		// 1. 쿠키를 삭제
			Cookie cookie = new Cookie("id", id );
			cookie.setMaxAge(0);
		// 2. 응답에 저장
			response.addCookie(cookie);
		}
		
		
		

		// 3. 홈으로 이동	
		// 3-1 toURL이 없으면 홈 주소를, 있으면 toURL 주소를 URL 넣는 변수에 대입.
		toURL = toURL==null || toURL.equals("")? "/" : toURL;
		
		
		
		// 3-2 id/pwd 일치하는 경우의 redirect
			return "redirect:"+toURL;
	}

2. 세션 생성 제어 구문

(1) 필요성

사실 위의 그림과 같이 홈 화면이나 첫 Login 화면 자체에서는 Session이 필요 없다. 

왜냐하면 해당 브라우저와 대응되는 Session에 id가 있냐 없냐(로그인 했는지 체크)는 boarderController 단계에서부터 진행하기 때문이다. 그 전에 로그인 화면으로 바로 들어오거나, 홈 화면으로 바로 들어온 경우는 Session을 쓰지 않는다. 

 

하지만 세션은 default로 어떤 브라우저로 부터 첫 요청이 온 순간에 생성된다. 

서버에는 클라이언트 별로 세션이 하나씩 존재하고, 클라이언트가 많아질수록 세션이 차지하는 용량이 증가해 부담이 커진다. 그래서 서버가 건강하려면, 세션의 수명을 최대한으로 줄이는 것이 좋다. 

이를 위해서 Session이 필요 없는 jsp에서는 요청이 들어와도 Session이 생기지 않도록 하는 제어문을 써야한다.

 

(2) 문법

<%@ page session="false" %>

<!--	Session을 새로 만들지 않는 명령어
    	기존에 존재하던 Session을 없애는 명령어는 아니다. -->

해당 제어문을 세션이 필요없는 jsp에 쓰면, 해당 jsp를 view 사용하는 요청이 들어와도 세션을 만들지 않는다. 

하지만 여기서 주의해야할 것이 있다. 해당 제어문은 특정 브라우저에 대응하는 세션이 없을 때 '새로' 만들지 않는다. 는 것이다. 이미 세션이 있는 상태에서 저 제어문이 적힌 jsp를 이용한다고 해서 기존 세션이 삭제되는 것은 아니다. 

따라서 위의 화면에서 앞의 두 경우 (바로 홈으로 들어오는 요청 , 바로 로그인 화면 들어오는 요청)에는 세션이 만들어지지 않는다. 하지만 BoarderController를 이용하는 시점부터 로그인 했는지 여부를 따지기 위해 세션이 만들어지므로, 그 뒤로부터는 세션이 쭉 존재한다. 

4. 스스로 해보기 (BoardController, loginForm, LoginController)

request.getRequestURL();은 type이 Stringbuffer로서 String으로 쓰려면 toString 형변환을 해줘야 한다. 

매개변수로 String toURL로 받길래 첨부터 String인 줄 알았더니 SpringFrameWork가 자동 형변환 해준 것 이었다.

StringBuffer sb = new StringBuffer();

String result = sb.toString();

input에 value 속성을 써서 파라미터 값을 받아줘야지, 스트링 쿼리의 내용을 input이 받아서 request 객체에 같이 넣어준다. 

위에는 type 속성을 "hidden"이 아니라 "text"를 해서 내용이 보인다. test 할 때는 "text"를 해서 보이게 하고 마지막에 안 보이게 "hidden"으로 바꿔준다.

 

toURL 형태가 http부터 다 적혀있는데도 되는구나 처음 알았다.

'백엔드 개발 > SpringMVC' 카테고리의 다른 글

Spring에서 예외 처리하는 방법(2)  (0) 2023.03.12
Spring에서 예외 처리하는 방법(1)  (0) 2023.03.11
Session 이용 실습(1)  (0) 2023.03.09
Session- 이론  (0) 2023.03.09
쿠키란?  (0) 2023.03.05