본문 바로가기

서블릿

230418_서블릿_현재 로그인 접속자 목록 표시 실습

화면 구성

 

1) 메인 화면

메인 화면

 

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>로그인</h3> <!--html 구성 화면 - 제목 -->

<!-- input 태그들의 값을 서버로 전송하기 위한 정보를 담고 있다. -->
<!-- action의 값으로는 요청하는 컴포넌트 이름 -->
<form name="formLogin" method = "post" action="/pro10/login100402">

<!-- 사용자가 입력한 아이디 값을 userId 매개 변수 이름으로 서버로 전송 -->
<input type="text" name = "userId" id = "userId" placeholder="아이디를 입력하세요.">

<!-- 사용자가 입력한 암호 값을 userPw 매개 변수 이름으로 서버로 전송 -->
<input type="password" name = "userPw" id="userPw" placeholder="암호를 입력하세요.">

<button type="submit">로그인</button>&nbsp;

</form>
</body>
</html>

: 메인 화면을 구성하는 html 소스

: 사용자가 웹 브라우저 URL 입력창에 URI 주소를 입력하고 보이는 첫 화면의 페이지

: action 속성에 작성된 것은, 서블릿에 정의된 매핑 이름이고 이 매핑 이름이 속한 서블릿 클래스로 이동한다.

 

 

2) 로그인 시 출력 화면

첫번 째 값 입력

 

첫번 째 값 입력 후 출력 화면

 

@WebServlet(name = "LoginTest100402", urlPatterns = { "/login100402" })
public class LoginTest extends HttpServlet {
	private static final long serialVersionUID = 1L;

	private ServletContext ctx = null;
	//ServletContext : 하나의 서블릿이 서블릿 컨테이너와 통신하기 위해서 사용되어지는 메서드들을 가지고 있는 클래스
	private List<String> userList = new ArrayList<>(); //로그인 아이디 저장, 목록 정보 가져오기
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doHandle(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doHandle(request, response);
	}

	private void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	
		//요청 처리
		request.setCharacterEncoding("utf-8");

		response.setContentType("text/html; charset=utf-8");
		PrintWriter pw = response.getWriter();
		
		String data =
				"<!DOCTYPE html>"
				+ "<html>"
				+ "<head>"
				+ "<meta charset=\"UTF-8\">"
				+ "<title>Login Result</title>"
				+ "</head>"
				+ "<body>";
		
		String userId = request.getParameter("userId");
		String userPw = request.getParameter("userPw");
		
		LoginImpl loginUser = null;
		HttpSession mySession = null;
		
		if(userId != null && userId.length() != 0) { //userId의 값이 0이 아닐 때 = 로그인 페이지를 거쳤을 때
			loginUser = new LoginImpl(userId, userPw); ////현재 로그인 한 사용자 아이디와 암호를 저장한 HttpSessionListener 인터페이스의 LoginImpl 구현 객체를 생성합니다.
			
			//HttpSession 객체 생성 또는 가져옴
			mySession = request.getSession(); //요청 객체에서 session 객체 가져오기
			ctx = getServletContext(); // ServletContext 객체 가져오기
			
			if(mySession.isNew()) {//isNew : 새로 만든 객체인지 검증 (boolean 반환)
				mySession.setAttribute("loginUser", loginUser);//바인딩 걸기
				userList.add(userId); //userId 배열에 넣기
				ctx.setAttribute("userList", userList); //setAttribute : 선택한 요소(element)의 속성(attribute) 값을 정합니다.
				
			} else {// 새로고침만 할 경우 실행
				System.out.println("총 접속자 수: " + LoginImpl.totalUser);
			}
				
			data
			+= "접속 아이디 :" + loginUser.getUserId() + "<br>" //loginUser: private 이기 때문에 getter 사용
			+ "현재 접속자 수 : " + LoginImpl.totalUser + "명 <br><br>" //정적 필드는 공유 메모리 영역에 있음 -> 클래스이름.필드이름 로 호출
			+ "현재 접속자 목록<br>"
			+ "=================<br>";
		
			
			@SuppressWarnings("unchecked")
			List<String> currentUserList = (ArrayList<String>)ctx.getAttribute("userList"); //오류 : Type safety: Unchecked cast from Object to ArrayList<String>
//			오류 : Type mismatch: cannot convert from Object to List<String>
			//getAttribute 가 오브젝트 타입이므로 캐스팅 해야함
			for(String _userId : currentUserList) { //이름은 배열에 있음 -> for문으로 꺼냄
				System.out.println(_userId);
				data += _userId + "<br>";
			}
		} else { //로그아웃 페이지를 거치지 않고, 서블릿 결과 화면을 새로고침
			mySession = request.getSession();
			loginUser = (LoginImpl)mySession.getAttribute("loginUser");
			if(loginUser != null) {
				ctx = getServletContext();
				
				data
				+="접속 아이디: " + loginUser.getUserId() +"<br>"
				+ "현재 접속자 수: " + LoginImpl.totalUser + "명<br><br>"

				+ "현재 접속자 목록<br>"
				+ "=============<br>" ;
				
				@SuppressWarnings("unchecked")
				List<String> currenList = (List<String>)ctx.getAttribute("userList");
				
				for(String _userId : currenList) {
					System.out.println(_userId);
					data += _userId + "<br>";
				}
			} else {
				data
				="<script>"
				+ "    alert('로그인 페이지로 이동합니다.');"
				+ "    location.href='/pro10/login.html';"
				+ "</script>" ;
                
                		//비즈니스 로직 처리
			
			pw.write(data);
			pw.flush();
			pw.close();
			
			mySession.invalidate();
			return;
			
			}

: 한포도, 1234란 비밀번호를 입력했을 때 실행 될 코드이다.

  하지만 사용자의 행동엔 변수가 있기 때문에 크게 세가지로 나누어 if-else문으로 경우의 수를 나눴다.

 

 1) 아이디, 비밀번호 두 가지를 다 작성했을 경우

 2) 로그인 후, 로그아웃을 하지 않고 새로고침만 할 경우

 3) 아이디, 비밀번호 두 가지 다 작성하지 않을 경우

 

사용자에게 전달 받은 값이 없는 경우, alert로 경고창을 띄우며 진행 페이지로 안내하던가 현 상황을 변함 없이 유지시켜 출력하는 등의 결과를 이끌어내도록 했다.

 

 

3) 로그아웃 시 출력 화면

로그아웃 시 출력 화면

		data
		+="=============<br>"
		+ "<a href=\"/pro10/logout100402?userId=" + loginUser.getUserId() +"\"><button type='button'>로그아웃</button></a>"
		//로그아웃 클릭 시 logout100402 서블릿으로 로그아웃 하는 접속자ID를 전송해 로그아웃 합니다. 
		+ "</body>"
		+ "</html>" ;

: 해당 실습에선 첫번째 아이디로 "한포도"를 입력해 캡쳐 상단의 링크 끝이 '?userId=한포도' 로 변경 되었다.

 

//요청 처리
		request.setCharacterEncoding("utf-8");
		
		String userId = request.getParameter("userId");
		
		HttpSession mysSession = request.getSession();
		mysSession.invalidate();
		
		ctx = getServletContext();
		
		@SuppressWarnings("unchecked")
		List<String> userList = (ArrayList<String>)ctx.getAttribute("userList");
	
		userList.remove(userId);
		
		ctx.removeAttribute("userList");
		ctx.setAttribute("userList", userList);
		
		
		
		//응답 처리
		response.setContentType("text/html; charset=utf-8");
		PrintWriter pw = response.getWriter();
		
		String data = "<script>"
			    + "    alert('로그아웃 되었습니다.\\n현재 접속인원수:" 
			    +            LoginImpl.totalUser + "');"
			    + "    location.href='/pro10/login.html';"
			    + "</script>" ;
		pw.write(data) ;
		
		pw.flush() ;
		pw.close() ;
	
	}

: 로그아웃 진행 시, 그동안 입력했던 값들의 배열을 불러와 지우는 일을 한다.

: 값을 지운 후 로그아웃이 되었단 팝업이 뜸과 동시에 다시 로그인 페이지로 돌아가게 만든다.

 

 

 

4) 브라우저별 동시 실행 결과

: 한 사이트에 한 사람만 가입하여 로그인 하는 경우는 극히 드물다.

그렇다면, 실습처럼 한 브라우저에서 하나의 아이디로만 로그인하는 것이 아닌 여러 명이 로그인 하고 로그아웃까지 하는 경우를 따져봐야 한다.

 

하나의 브라우저에서 로그인 하는 경우엔, 로그인 후 로그아웃을 하지 않고 되돌아가지 못하므로 항상 하나의 아이디로 로그인과 로그아웃을 반복하는 수밖에 없었다.

 

그렇다면 각각 다른 브라우저로 진행 시엔 어떤 결과일까 진행해 보았다.

 

(1) 크롬
2) 엣지
3) 웨일

: 시간 순으로 로그인한 아이디가 목록에 차곡차곡 쌓였다.

 

 

: 세번째 아이디가 로그아웃 했을 경우 나타나는 창이다.

: 로그인 시와 마찬가지로 현재 접속 인원 수에도 변동이 있다.