8 minute read

쿠키와 세션

프로토콜

  • 표준 인터넷 규약! 웹 서비스가 준수해야하는 규칙
  1. FTP 프로토콜 ; File Transfer Protocol
    • 파일을 교환하기 위한 TCP/IP 프로토콜
    • 데이터 전송 연결과 명령 연결이 존재

-명령연결: 21번 포트를 이용

-데이터 전송 연결: 20번 포트를 이용

(1) 능동 모드 Active Mode

https://infosys.beckhoff.com/content/1033/cx8110_hw/Images/png/83035147__en-US__Web.png

Active Mode (image from https://infosys.beckhoff.com/english.php?content=../content/1033/cx8110_hw/68233610358735665675.html&id=)

  • 능동 모드는

1) 클라이언트가 1023보다 큰 포트를 명령 포트로써 이용해서 서버의 21번 포트(명령포트)로 두 번째로 접속할 포트 번호(클라이언트의 데이터 포트)를 알려준다!

2) 서버의 21번 포트는 ACK 로 클라이언트의 명령 포트로 응답해준다

3) 서버의 20번 포트(데이터 포트)는 앞서 1)에서 클라이언트가 알려주었던 데이터 포트 번호로 접속을 시도한다!

4) 클라이언트의 데이터포트가 서버의 데이터 포트로 ACK 신호를 보낸다!

이런 능동 모드는 클라이언트의 상태에 따른 비정상적인 작동 형태가 보고될 수 있다

클라이언트가 방화벽, NAT(IP 마스킹) 등을 사용하는 환경일 때에 잘 동작하지 않을 수 있는데, 이때 수동 모드를 이용하면 된다. -[wiki_FTP](https://ko.wikipedia.org/wiki/%ED%8C%8C%EC%9D%BC_%EC%A0%84%EC%86%A1_%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C#:~:text=%ED%8C%8C%EC%9D%BC%20%EC%A0%84%EC%86%A1%20%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C(File%20Transfer,%EC%9D%84%20%ED%95%98%EA%B8%B0%20%EC%9C%84%ED%95%9C%20%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%9D%B4%EB%8B%A4.)-

(2) 수동 모드 Passive Mode

https://infosys.beckhoff.com/content/1033/cx8110_hw/Images/png/83038347__en-US__Web.png

Passive Mode- image from https://infosys.beckhoff.com/english.php?content=../content/1033/cx8110_hw/68233610358735665675.html&id=

  • 수동 방식은 클라이언트가 서버에 접속할 수 없을 때 사용되는 방식
  • 서버가 클라이언트가 사용할 수 있는 서버 내의 포트를 알려주는 방식
  • 이 방식에서도 클라이언트는 데이터, 명령 포트 모두 1023 보다 큰 포트를 이용
  • 작동 과정

1) 클라이언트가 명령포트를 이용하여 서버의 명령포트(21번)로 접속을 시도하면서 PASV 신호를 보낸다

2) 서버의 21번 포트(명령 포트)는 클라이언트의 명령 포트로 서버에서 사용할 수 있는 포트 번호를 알려준다

3) 클라이언트의 데이터 포트가 2)에서 전달받은 서버 내 포트로 접속하려 시도한다

4) 서버 내의 포트(클라이언트와 접속하려는 포트)가 클라이언트의 데이터 포트로 ACK 신호를 보낸다


참고 사이트 : https://hack-cracker.tistory.com/133

https://infosys.beckhoff.com/english.php?content=../content/1033/cx8110_hw/68233610358735665675.html&id=


  1. HTTP 프로토콜; Hyper Text Transfer Protocol
  • HTML 문서의 송수신을 위해 사용하는 프로토콜
  • 비 연결(Connectionless) 지향형 통신 프로토콜

↔️ 클라이언트가 서버에 정보를 요청하면, 웹 서버가 해당 페이지를 클라이언트에게 전송(응답)한 후 연결을 끊음

  • 포트 80을 이용하여 수행됨
  • 이런 웹 서버를 “무 상태(stateless) 서버“(현재 상태를 유지하지 못하는 서버)
  • 장점 : 서버의 자원 낭비를 최소화

🌟 HTTP 상태 코드

2xx : 대부분 요청에 대한 응답이 성공하였음을 의미

HTTP 상태 코드-200번대

3xx : 리다이렉트 유도

HTTP 상태 코드-300번대

4xx : 클라이언트의 코드가 잘못된 경우

  • 유효하지 않은 자원에 대한 요청 혹은 권한이 잘못된 경우 발생

HTTP 상태 코드-400번대

5xx : 서버 에러

HTTP 상태 코드-500번대

정리에 참고한 사이트: https://joshua1988.github.io/web-development/http-part1/

https://developer.mozilla.org/ko/docs/Web/HTTP/Status

⭐ HTTP 요청 메서드 HTTP Verbs ⭐

  • 데이터에 대한 조회, 생성, 변경, 삭제 동작을 HTTP 요청 메서드로 정의

HTTP Methods for Restful API

  • 경우에 따라서 POST 메서드로 PUT, DELETE 동작도 수행 가능

참고 사이트 : https://joshua1988.github.io/web-development/http-part1/

https://developer.mozilla.org/ko/docs/Web/HTTP/Methods

https://www.restapitutorial.com/lessons/httpmethods.html#:~:text=The primary or most-commonly,but are utilized less frequently.


  • 정보를 서버가 아닌, 클라이언트 컴퓨터의 하드디스크에 저장하여, 서버 동작에 무리를 주지 않으면서 정보를 지속적으로 유지하고, 적절하게 사용할 수 있도록 하는 방법

▶️ 장점: 서버의 부하

▶️ 단점 : 여러 사람이 PC를 공유하는 경우, 개인 정보가 유출될 수 있어서 보안 유지가 되지 않음

저장할 수 있는 데이터의 한계 존재(1.2MB)

  • 클라이언트에 저장된 적은 양의 정보
  • 쿠키에 대한 정보를 필요로 하는 웹 페이지가 웹 서버에 요청될 때, 클라이언트에 저장되었던 쿠키에 대한 정보를 웹 서버에 다시 건내줌
  • 쿠키는 4KB 이하로 그 크기가 제한되어 있고, 최대 300 개(즉, 4KB * 300개= 1200KB= 1.2MB)까지의 데이터 정보 배열을 저장 가능

📌 서버가 클라이언트에 쿠키를 설정하기 위한 방법 📌

쿠키 객체 생성 - 쿠키 생성자 호출

Cookie(java.lang.String name, java.lang.String value)
//생성자의 인자 1. 쿠키의 이름 2. 쿠키의 값
//쿠키에 이름을 지정하는 이유: 쿠키에 저장할 값이 여러 개일 수도 있기 때문!

② 쿠키에 속성 값을 설정

response 객체의 addCookie() 메서드를 이용하여 쿠키 추가

위의 1 과정으로 쿠키가 생성되었다면 쿠키에 관련된 메서드를 사용 가능!(이번에는 2번 과정 수행 내용)

쿠키에 관련된 메서드

1) set~: 쿠키에 새로운 값 설정

2) get~: 쿠키에 설정된 값을 알아내기

쿠키에 값을 설정하는 것과 관련된 메서드

쿠키에 대한 값을 알려주는 것과 관련된 메서드

⚠️ 클라이언트가 쿠키를 저장하는 것을 거부하도록 하지는 못하기 때문에, 보안을 고려해야하는 상황이라면, 세션이나 DB 등을 이용

  • 각 운영체제별로 쿠키가 저장되는 위치는 아래와 같다!

https://ko.joecomp.com/location-of-cookies-folder-in-windows-10-8-7

뿐만아니라, 아래와 같이 쿠키 저장 경로를 설정할 수도 있다!

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<%
		response.setCharacterEncoding("UTF-8");
		//A. 쿠키를 생성해서, 속성값을 설정하고, 쿠키를 전송하는 과정을 구분하여 작성
		//1.Cookie 객체 생성
		Cookie c = new Cookie("name","떡볶이");
		//2.속성값 설정-이번에는 유효기간을 설정해보기(1년)
		c.setMaxAge(365*24*60*60);
		//쿠키 저장 경로 설정
		c.setPath("D:/(위치)");
		//3.클라이언트에 쿠키 전송
		response.addCookie(c);
		
		//B.쿠키를 클라이언트에 전송하는 줄에서 쿠키를 생성하는 과정을 한 줄에 작성
		//단점은, 한줄에 작성하게 되면 addCookie의 매개변수는 쿠키만 올수 있어서 
		//속성 지정의 불편함이 있음
		response.addCookie(new Cookie("age","26"));
		response.addCookie(new Cookie("pw","test123"));
		
		out.println(c.getPath());
		
	%>
	<h3>쿠키 설정</h3>
</body>
</html>

https://github.com/hy6219/TIL-Today-I-Learned-/blob/main/JSP%20Servlet/Basic/%EC%BF%A0%ED%82%A4.png?raw=true

📌클라이언트에 설정된 모든 쿠키 객체들을 얻어오기📌

① 쿠키 객체를 얻어오기

② 쿠키 객체에 설정된 값을 알아내기

  • request객체의 getCookies()메서드를 통해서 클라이언트에 설정된 모든 쿠키 객체들을 Cookie 배열로 받아올 수 있음!

https://github.com/hy6219/TIL-Today-I-Learned-/blob/main/JSP%20Servlet/Basic/%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%EC%97%90%20%EC%A0%80%EC%9E%A5%EB%90%9C%20%EC%BF%A0%ED%82%A4%EB%93%A4%20%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0.png?raw=true

request.getCookies()

  • 쿠키객체.setMaxAge(0) : 특정 쿠키의 유효기간을 만료시킴
  • JSESSIONID : 웹 브라우저로 세션 ID를 보낼 때 쿠키 형태로 만들어 전송할 때, 세션 정보를 저장한 쿠키의 이름

세션

  • 웹 브라우저를 닫기 전까지 페이지를 이동하더라도 사용자의 정보를 잃지 않고 서버에 보관할 수 있도록 하는 객체
  • 서버 상 존재하는 객체로, 브라우저 단위당 한 개씩 존재
▶️ 장점: JSP(서버)에서만 접근 가능하기 때문에 보안 유지에 강력

저장할 수 있는 데이터에 한계가 없음

  • 세션은 서블릿 컨테이너 내부에 존재
  • 세션 객체는 항상 JSP 내장객체로 존재하지만, 구분하기 위해서 고유의 id 값을 가지고 있음

세션에서 사용되는 메서드-session 내장 객체 이용

setAttribute를 이용해서 세션을 설정하고, 세션을 확인해보자

(1) 세션을 설정

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>세션</title>
</head>
<body>
	<%
		session.setAttribute("id", "jisoo");
		session.setAttribute("pw", "test123");
		session.setAttribute("age", 26);
	%>
	<h3>세션 설정</h3>
</body>
</html>

(2) 현재 세션에 대한 정보 확인

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	String id = String.valueOf(session.getAttribute("id"));
	String pw = String.valueOf(session.getAttribute("pw"));
	int   age = Integer.valueOf(String.valueOf(session.getAttribute("age")));
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>세션  받아오기</title>
</head>
<body>
	<%="id: "+id +"<br>" %>
	<%="pw: "+pw+"<br>" %>
	<%="age: "+age+"<br>" %>
</body>
</html>

https://github.com/hy6219/TIL-Today-I-Learned-/blob/main/JSP%20Servlet/Basic/%EC%84%B8%EC%85%98%EC%84%A4%EC%A0%95.PNG?raw=true

세션 생성, 확인하기

즉, 위의 (1)과 같이 세션을 설정해둔 후, (2)와 같이 세션에 대한 정보를 확인하려고 하면, 브라우저가 에 생성된 세션 관련 정보를 확인해볼 수 있다!

JSP에서는 내장객체 session을 이용하면 되지만, 서블릿에서는 요청메서드의 파라미터인 HttpServletRequest 객체를 이용하여

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
	ServletException, IOException{

					HttpSession session = request.getSession();
					session.setAttribute("user",admin);
}

와 같이 세션 객체를 가져오고, 세션에 정보를 저장할 수 있다!

*session.getAttributeNames()

  • getAttributeNames()메서드의 리턴 타입은 Enumeration이다
  • Enumeration에서 여러 데이터를 뽑아오기 위해서는 아래와 같은 메서드를 적절하게 사용해야 함!

Enumeration에서 데이터를 추출하기 위한 메서드들

  • [나의 생각] 세션에 저장된 정보가 많은데, 이 중 사용하려는 정보가 많다면, getAttributeNames가 보다 효율적일것! 다만, 사용하려는 데이터가 많지 않거나, 많은 데이터 중 극소의 데이터만 사용한다면 getAttribute를 사용하는 것이 좋을 것!
<%@ page import="java.util.Enumeration" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>getAttributeNames</title>
</head>
<body>
	<h3>세션에 설정된 모든 값 얻어오기 getAttributeNames(Enumeration)</h3>
	<%
		Enumeration names =session.getAttributeNames();
		while(names.hasMoreElements()){
			//key-value처럼 세션에 저장된 정보의 이름들을 먼저 탐색
			String name = names.nextElement().toString();
			//탐색된 정보의 이름으로 값 찾기
			String value = session.getAttribute(name).toString();
			out.println(name+" : "+value +"<br>");
		}
	%>
</body>
</html>

https://github.com/hy6219/TIL-Today-I-Learned-/blob/main/JSP%20Servlet/Basic/%EC%84%B8%EC%85%98-Enumeration%20getAttributeNames.PNG?raw=true

세션 객체의 유효 시간, 웹 사이트에 머문 시간, 새로운 세션인지 확인하기

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
		//세션객체의 아이디(고유함)
		String id_str= session.getId();
		//새로운 웹 브라우저를 띄우지 않고, 이미 띄워져 있는
		//브라우저에서 다른 페이지로 이동했다가 돌아오는 경우,
		//사이트를 떠났을 때의 시간
		long lastTime=session.getLastAccessedTime();
		
		//세션이 생성된 시간
		long createdTime=session.getCreationTime();
		//생성된 시간부터 같은 브라우저 내에서 마지막으로 접근한 시간까지 소요된 시간
		long timeUsed = (lastTime-createdTime)/1000*60;//분단위로 변환
		
		//사용자의 다음 요청이 들어오기 전까지 유효한 세션의 시간
		long inActive = session.getMaxInactiveInterval()/60;//s->min
		
		//새로 생성된 세션인지
		boolean isNew = session.isNew();
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>세션객체의 정보 얻어오기</title>
</head>
<body>
	<h3>세션객체의 정보</h3>
	[1]세션 ID: <%=id_str%><br><hr>
	[2]당신이 웹사이트에 머문 시간은: <%=timeUsed%>분입니다<br><hr>
	[3]세션의 유효시간: <%=inActive%><br><hr>
	[4]세션이 새로 생성되었나요?: <%=isNew%><br><hr>
</body>
</html>

https://github.com/hy6219/TIL-Today-I-Learned-/blob/main/JSP%20Servlet/Basic/%EC%84%B8%EC%85%98%EA%B0%9D%EC%B2%B4%EC%9D%98%20%EC%A0%95%EB%B3%B4.PNG?raw=true

  • 세션의 유효시간은 마지막 요청으로부터 기본 30분!
  • 참고로, 톰캣의 경우, 아래와 같이 세션의 유효기간이 30분으로 설정되어있음을 확인해볼 수 있다(C:/Program Files/Apache Software Foundation/Tomcat 7.0/conf에 있는 web.xml)
<session-config>
	<session-timeout>30</session-timeout>
</session-config>
  • 다만, 세션의 유효시간을 임의로 변경하려면 setMaxInactiveInterval(초단위)로 변경할 수 있는데, 세션 객체가 사라지지 않도록 하려면 setMaxInactiveInterval(-1)로 설정해두면 됨!
  • 앞서 설명했던 것과 같이, 새로운 브라우저를 띄우게 되면, 브라우저와 세션이 1:1관계처럼 대응되기 때문에 새로운 id값이 보여진다

https://github.com/hy6219/TIL-Today-I-Learned-/blob/main/JSP%20Servlet/Basic/%EC%84%B8%EC%85%98%EA%B0%9D%EC%B2%B4%EC%9D%98%20%ED%8A%B9%EC%A7%95-%EC%83%88%EB%A1%9C%EC%9A%B4%20%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%97%90%EC%84%9C%20%EC%83%88%EB%A1%9C%EC%9A%B4%20id.PNG?raw=true

세션을 제거 - removeAttribute(), invalidate()

  • 로그아웃 등 회원 전용 페이지에서는 로그아웃 후에는 세션에 접근하지 못하도록 세션에 저장된 속성 값을 제거해주어야 함! ▶️ removeAttribute(), invalidate()
  • invalidate는 세션에 부여되었던 id를 삭제해줌(request.isRequestedSessionIdValid()로 id가 유효한지 확인 가능!)
  • removeAttribute()는 해당 name의 세션정보만 삭제

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Cookie &amp; Session</title>
</head>
<body>
<%
	out.print("<h3> >> 이전에 설정되었던 세션 정보드들은 모두 삭제 << </h3>");
	session.invalidate();
	
	out.print("세션 아이디가 유효할까요?: ");
	if(request.isRequestedSessionIdValid()){
		out.print("유효합니다");
	}else{
		out.print("유효하지 않아요");//유효하지 않아요
	}
%>
</body>
</html>
<%@ page import="java.util.Enumeration"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<%
	session.setAttribute("s_name1","저는 세션에 저장된  번째 값이에요>3<");
	session.setAttribute("s_name2","저는 세션에 저장된  번째 값이에요>3<");
	session.setAttribute("s_name3","저는 세션에 저장된  번째 값이에요>3<");
	
	out.print("<h3> >> 세션값을 삭제하기 << </h3>");
	
	Enumeration names = session.getAttributeNames();
	while(names.hasMoreElements()){
		String name = names.nextElement().toString();//obj->str
		String value = session.getAttribute(name).toString();//obj->str
		out.println(name+" : "+value+"<br>");
	}
	
	//이름을 통해서 제거
	session.removeAttribute("s_name2");
	
	out.print("<h3> >> 세션값을 삭제한 << </h3>");
	names = session.getAttributeNames();//enumeration, iterator는 같은 이름이면 
	//사용할때마다 매번 지정해주기!
	while(names.hasMoreElements()){
		String name = names.nextElement().toString();//obj->str
		String value = session.getAttribute(name).toString();//obj->str
		out.println(name+" : "+value+"<br>");
	}
	%>
</body>
</html>

두 가지 예제를 살펴보자. 먼저 첫 번째 경우는 invalidate를 통해 이전에 설정해두었던 세션의 정보를 모두 제거해서 유효하지 않다는 문구를 띄워준다

두 번째 예제는 s_name2 요소만 제거되어 s_name1,s_name3 요소만 화면에 띄워주게 된다!


아래는 세션을 이용해서 로그인 로직을 구현해보는 예제이다

https://github.com/hy6219/TIL-Today-I-Learned-/blob/main/JSP%20Servlet/Basic/%EC%84%B8%EC%85%98%EC%9D%84%20%EC%9D%B4%EC%9A%A9%ED%95%B4%EC%84%9C%20%EB%A1%9C%EA%B7%B8%EC%9D%B8%20%EC%B2%98%EB%A6%AC%ED%95%B4%EB%B3%B4%EA%B8%B0.png?raw=true

세션을 이용한 로그인 로직

  • 먼저 로그인 폼에서 로그인 정보를 입력한다
  • 그 후 회원 인증 페이지에서 인증 절차를 밟는다
  • 인증에 성공하면 메인페이지로, 실패하면 로그인 폼 페이지로 이동한다
  • 인증에 성공한 회원은 메인페이지에서 로그아웃 페이지 이용이 가능한데, 로그아웃 페이지에서는 로그아웃 선택 시 세션 객체를 제거하고 로그인 폼으로 이동한다!
  1. 로그인폼 loginForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인 페이지</title>
</head>
<body>
	<form action="auth.jsp" method="post">
		<div>
			<label>아이디: <input type="text" name="id" placeholder="아이디" autocomplete="off"></label>
		</div>
		<div>
			<label>비밀번호 : <input type="password" name="pw" placeholder="비밀번호" autocomplete="off"></label>
		</div>
		<input type="submit" value="로그인">
	</form>
</body>
</html>
  1. 인증 페이지 auth.jsp(rename하였습니다)
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원인증 페이지</title>
</head>
<body>
	<%
	/*일치 기준*/
	String dbId="admin";
	String dbPw="1234";
	/*세션에 저장할 추가 정보*/
	String dbName="관리자";
	/*넘겨받은 값*/
	String inId=request.getParameter("id");
	String inPw=request.getParameter("pw");
	
	request.setCharacterEncoding("UTF-8");
	if((dbId.equals(inId)) && (dbPw.equals(inPw))){
		//세션에 정보 저장
		session.setAttribute("id", inId);
		session.setAttribute("name",dbName);
		//세션에 정보를 저장하고 리다이렉트하면 request나 response 객체가 소실되지 않음
		response.sendRedirect("main.jsp");
	}else{
		//인증 실패시, 로그인폼으로 이동
		response.sendRedirect("loginForm.jsp");
	}
%>
</body>
</html>
  1. 메인페이지 main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>메인 페이지</title>
</head>
<body>
	<%
		String id=session.getAttribute("id").toString();
		String name=session.getAttribute("name").toString();
		request.setCharacterEncoding("UTF-8");
		//id나 name이  비어있다면 다시 loginForm으로 이동
		if(id.equals("")||name.equals("")){
			response.sendRedirect("loginForm.jsp");
		}else{
			%>
			<%=name %> 안녕하세요!<br>
			<form action="logout.jsp" method="post">
				<input type="submit" value="로그아웃">
			</form>
		<%}
	%>
</body>
</html>
  1. 로그아웃 페이지 logout.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그아웃 페이지</title>
</head>
<body>
	<%
		session.invalidate();//세션에 저장되었던 모든 정보 삭제
	%>
	<script src="./logout.js"></script>
</body>
</html>

-로그아웃 알람 및 위치 변경을 해주는 logout.js

/**
 * logout alarm
 */
alert("로그아웃 되었습니다! 다시 로그인 폼으로 이동합니다");
location.href="loginForm.jsp";//location을 loginForm으로 변경

https://github.com/hy6219/TIL-Today-I-Learned-/blob/main/JSP%20Servlet/Basic/%EC%84%B8%EC%85%98%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%97%B0%EC%8A%B5.gif?raw=true

Updated: