다음주 부터 활용할 컨테이너 , 스프링 컨테이너
반복적인 코드 작업하지 않도록 만듦 , 내부적 동작구조를 볼 수 없다.
내부 동작 구조를 익히는 중
스프링 프레임워크
- 웹 MVC 구조를 이용하면 어떤 장점이 있었나요? 어떤 단점이 있었나요?
장점: 확실하게 책임과 역할을 구분해서 작업을 진행할 수 있다.
단점: 여러개의 코드를 만들어야 함
2. WEB-MVC 구조(JDBC) 구현한 작성코드들에 대한 개선 사항
여러 개의 컨트롤러를 작성하는 번거로움
동일한 로직의 반복적인 사용
예외 처리의 부재 // 비정상적인 호출 발생시 속수 무책
반복적인 메소드 호출 // 리팩토링 필요 // String -> Long
3. 스프링 프레임워크
세션, 쿠키, 필터, 리스너 기본개념 이해, 동적원리 이해 & 활용
로그인 처리
HTTP 톰캣 컨테이너에서 제공
page
request
response
session
application
exception
config
웹은 과거의 상태를 유지하지 않는다. 더 많은 사용자에게 서비스하기위해 필요할때만 연결하고 끊어야함 -> 무상태 연결(stateless)
요청과 응답을 하나의 단위로 처리하면서 기존 사용자에 대한 정보를 기억하지 않는다.
무상태 특징으로 인해 기존의 방문자를 기억하기 위한 특별한 메커니즘을 사용한다.
세션(HttpSession) , 쿠키(Cookie), JWT(토큰)
로그인 유지를 위한 모든 기능을 세션트래킹이라고 한다.
1)쿠키
문자열로 만들어진 데이터 조각/ 요청과 응답사이에 주고받는 형태
서버와 브라우저 사이에서 요청이나 응답시에 주고받는 형태로 사용
(name, value)
쿠키 주고받는 시나리오
1. 브라우저가 최초로 서버를 호출하는 경우 해당 서버에서 발행한 쿠키가 없다면 브라우저는 아무것도 전송하지 않는다.
2. 서버에서는 응답 메시지를 보낼 때, 쿠키를 보내준다. (Set-Cookie) HTTP헤더를 이용
3. 브라우저는 쿠키를 받은 후, 정보를 읽고, 쿠키는 유효기간(만료기간)을 보고 메모리상에 처리할 것인지, 파일로 보관할 것인지 판단한다.
4. 브라우저가 보관하는 쿠키는 다음에 브라우저가 해당 서버에 요청을 보낼때, HTTP 헤더에 Cookie라는 헤더 이름과 함께 전달한다.
5. 서버에서는 필요에 따라 브라우저가 보낸 쿠키를 읽고 사용한다.
쿠키를 생성하는 방법
1. 서버에서 자동으로 발행 : tomcat JSESSIONID 발행
// 브라우저 메모리상에 놨둠 , 브라우저 종료하면 삭제됨
2. 개발자가 코드로 통해서 직접 발행
// 이름, 유효기간, 응답객체, 경로, 도메인 직접 지정할 수 있음, 좀더 다양한 기능으로 활용
자동으로 발행되는 쿠키 – 서버에서 지난번 방문 브라우저를 인식하기 위해서 생성되는 쿠키로 흔히 ‘세션쿠키’라고 함
• Tomcat에서는 ‘JSESSIONID’라는 이름으로 발행
• 개발자가 작성하는 쿠키 – 직접 응답(response)에 포함시켜야 함
• response.addCookie( ) // 헤더에다가 붙인다.
무상태와 세션트랙킹
웹은 기본적으로 상태를 유지하지 않는다(stateless)
• 지난번 서버에 방문한 사용자를 어떻게 추적할 것인가? – 세션 트랙킹
• 서버에서 정보를 보관하는 방식 – 세션(Session)
• 브라우저에서 정보를 보관하는 방식 – 쿠키(Cookie)
웹서버 컨테이너 서블릿 찾기 객체들한테가서 처리하고 서블릿한테 돌려주고 리스폰스해서 클라이언트한테 돌려줌
세션과 세션 유지
WAS내 웹 프로젝트가 실행되면 WAS내 독립적인 메모리 공간이 만들어진다. – ServletContext
혹은 Application(JSP)
Web Application 공간에는 세션 컨텍스트(Session Context)라는 별도의 공간을 이용해서
JSESSIONID와 같은 쿠키를 관리하는데 이를 ‘세션 저장소‘라고 한다.
자신만의 메모리영역 하나씩 가지고 있다. -> 서블릿 컨텍스트
세션저장소에 키와 값을 가지고있다
톰캣에서는 자기가 발행하는 아이디를 JSESSIONID 생성해서 브라우저한테 보내줌
세션 저장소는 기본적으로 키(key)와 값(value)의 구조로 이루어지며 맵(map) 구조로 생성
JSESSIONID가 키가 되고 이에 해당하는 별도의 저장구조가 생성된다.
Servlet에서는 HttpSession API를 이용해서 이를 접근할 수 있다.
//내가 보내준 키값으로 서버는 사용자를 구분한다.
// 처음 접근하는 브라우저는 쿠키가 없어서 아무것도 안보냄, -> 서버가 http헤더에 쿠키 보내줌
// 다음번 부터 서버에서 매칭을해서 비교할 수 있음
로그인 여부의 판단
현재 요청에서 전송한 JSESSIONID값을 가지고 세션 저장소내 공간을 조회해서 특정한
이름과 값이 있는 데이터가 있는지를 확인
존재하는 경우에는 로그인한 사용자로 판단
세션을 이용한 로그인 체크
1.사용자가 로그인에 성공하면 HttpSession을 이용해서
해당 사용자의 공간(세션 컨텍스트)에 특정한 객체를 이름과 함께 저장
2.로그인 체크가 필요한 컨트롤러에서는 현재 사용자의 공간에 지정된 이름 (key)으로 객체가 저장되어 있는지 확인한 후 객체가 존재한다면 해당 사용자는 로그인된 사용자로 간주하고 그렇지 않으면 로그인 페이지로 이동시킨다.
register 컨트롤러에 다음과 같은 if 를 추가한다.
처음 들어갔을때, 아직 로그인 안했을때를 구분한다.
todo/register로 들어가도 login 페이지로 가짐
HttpSession session = request.getSession();
if(session.isNew()){
response.sendRedirect("/login"); // 쿠키가 새로만들어진 사용자
return;
}
if(session.getAttribute("loginInfo") == null){
log.info("로그인한 정보가 없는 사용자");
response.sendRedirect("/login");
return;
}
session.isNew() 메서드는 현재 요청이 새로운 세션을 생성했는지 여부를 확인
login.jsp
아이디 , 비밀번호 submit한다.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Login Page</title>
</head>
<body>
<form action="/login" method="post">
<input type="text" name="mid">
<input type="text" name="mpw">
<button type="submit">LOGIN</button>
</form>
</body>
</html>
login 컨트롤러
doPost에서 mid + mpw 로 세션값을 만든다.
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
log.info("login get...");
request.getRequestDispatcher("/WEB-INF/todo/login.jsp").forward(request,response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
log.info("login post called");
String mid = request.getParameter("mid");
String mpw = request.getParameter("mpw");
String str = mid + mpw;
session.setAttribute("loginInfo",str);
response.sendRedirect("/todo/list");
}
HttpSession session = request.getSession();
세션 객체에 사용자의 로그인 정보를 저장
이후에 해당 세션 속성을 검색하여 사용자가 로그인되어 있는지 확인하고, 로그인된 경우에는 해당 사용자의 정보를 가져와서 필요한 작업을 수행한다.
세션값 지우고 다시들어가면 login.jsp으로 가짐
필터를 이용한 로그인 체크
로그인 처리가 필요한 모든 컨트롤러에 동일한 코드를 적용하는 대신 필터를 이용
특정한 경로로 접근할때 지정된 필터를 통해 제어
여러 개의 필터를 등록해서 사용할 수 있음
3.필터를 이용한 로그인 체크
필터를 이용하여 특정한 서블릿이나 JSP 도달하는 과정에서 필터링하는 역할을 하는 서블릿 API 객체 @WebFilter 여러 개를 적용할 수 있다.
필터 폴더를 만든다. LoginCheckFilter
todo/* 모든 경로에 대해서 안들어가짐
@WebFilter(urlPatterns = {"/todo/*"})
@Log4j2
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("Login check filter....");
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
HttpSession session = req.getSession();
if (session.getAttribute("loginInfo") == null) {
resp.sendRedirect("/login");
return;
}
chain.doFilter(request, response);
}
}
HttpServletRequest : HTTP 요청에 대한 정보를 읽거나 조작하기 위해 사용. 렇게 함으로써 이후에 HttpServletRequest에서 제공하는 메서드를 사용할 수 있다.
chain.doFilter(request, response) : 현재 필터에서 요청을 다음 필터로 전달하는 역할
현재 필터에서 처리한 요청을 다음 필터로 전달하거나, 필터 체인의 끝에 도달했을 때 서블릿으로 요청을 전달한다. 써줘야 계속 호출됨
UTF8Filter 한글깨짐방지 필터
@WebFilter(urlPatterns = {"/*"})
@Log4j2
public class UTF8Filter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws
IOException, ServletException {
log.info("UTF8 filter....");
HttpServletRequest req = (HttpServletRequest)request;
req.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);
}
}
logout 컨트롤러
@WebServlet("/logout")
@Log4j2
public class LogoutController extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
log.info("log out..................");
HttpSession session = req.getSession();
session.removeAttribute("loginInfo");
session.invalidate(); //해당 세션에 저장된 모든 속성이 삭제
resp.sendRedirect("/");
}
}
서버키면 filter
Member VO
id, pw, 이름을 가진다.
@Getter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MemberVO {
private String mid;
private String mpw;
private String mname;
}
Member DTO
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MemberDTO {
private String mid;
private String mpw;
private String mname;
}
MemberDAO
id, pw를 매개변수로 받아 해당하는 정보를 select한다.
memberVO를 반환한다.
public class MemberDAO {
public MemberVO getWithPassword(String mid, String mpw) throws Exception {
String query = "select mid, mpw, mname from tbl_member where mid =? and mpw = ?";
MemberVO memberVO = null;
@Cleanup Connection connection = ConnectionUtil.INSTANCE.getConnection();
@Cleanup PreparedStatement preparedStatement =
connection.prepareStatement(query);
preparedStatement.setString(1, mid);
preparedStatement.setString(2, mpw);
@Cleanup ResultSet resultSet = preparedStatement.executeQuery();
resultSet.next();
memberVO = MemberVO.builder()
.mid(resultSet.getString(1))
.mpw(resultSet.getString(2))
.mname(resultSet.getString(3))
.build();
return memberVO;
}
}
MemberService
login 메소드를 가진다.
id와 pw를 매개변수로 받고 dao.getWithPassword로 해당하는 MemberVO객체를 받는다.
modelMapper로 memberDTO로 변환 후 return 한다.
@Log4j2
public enum MemberService {
INSTANCE;
private MemberDAO dao;
private ModelMapper modelMapper;
MemberService() {
dao = new MemberDAO();
modelMapper = MapperUtil.INSTANCE.get();
}
public MemberDTO login(String mid, String mpw)throws Exception
{
MemberVO vo = dao.getWithPassword(mid, mpw);
MemberDTO memberDTO = modelMapper.map(vo,
MemberDTO.class);
return memberDTO;
}
}
login 컨트롤러 수정
MemberService.INSTANCE.login(mid,mpw)로 받아온 id pw 를 서비스로 넘기고 memberDTO객체를 받는다.
HttpSession session = req.getSession(); session을 받아오고
session.setAttribute("loginInfo", memberDTO); setAttribute해준다.
// 세션에 저장해 놨기때문에 세션이 살아있는동안 ${loginINfo}로 다 접근 가능
그 후 /todo/list로 redirect 한다.
catch 만약 에러가 나면 다시 로그인 페이지로 돌아가며 error 라는 값을 같이 넘겨준다.(get방식)
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
log.info("login post........");
String mid = req.getParameter("mid");
String mpw = req.getParameter("mpw");
try {
MemberDTO memberDTO = MemberService.INSTANCE.login(mid,mpw);
HttpSession session = req.getSession(); // 세션값 받아와서
session.setAttribute("loginInfo", memberDTO);
resp.sendRedirect("/todo/list");
} catch (Exception e) {
resp.sendRedirect("/login?result=error");
// 에러라는 result 값을 들고감
}
}
login.jsp
param.result로 error가 있으면 , <c:if 로그인 에러 메시지를 띄운다.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Login Page</title>
</head>
<body>
<c:if test="${param.result == 'error'}">
<%-- get방식으로 error 받음--%>
<span style="color: red"><h1 >로그인 에러입니다. 다시 시도하세요!</h1></span>
</c:if>
<form action="/login" method="post">
<input type="text" name="mid">
<input type="text" name="mpw">
<button type="submit">LOGIN</button>
</form>
</body>
</html>
session에 setAttribute해줬던 값 이렇게 적어만줘도 잘 나옴!
stateless때문에 http 한번 요청할때마다 죽었다 살았다 함 , 그래서 setAttribute해야 jsp에서 사용할 수 있는데, session에 setAttribute하면 세션 살아있는동안 접근 가능함
HttpSession session = req.getSession(); // 세션값 받아와서
session.setAttribute("loginInfo", memberDTO);
${loginInfo}
'신세계 - Spring 공부' 카테고리의 다른 글
15주차 배운점 - 퍼사드 패턴, 컨트롤러, Formatter, 예외처리, Mapper (0) | 2024.09.24 |
---|---|
15주차 배운점 - 스프링, MyBatis, 스프링 Web MVC (2) | 2024.09.23 |
14주차 배운점 - Todo프로그램, ModelMapper, log4j2 (0) | 2024.09.23 |
14주차 배운점 - DTO, DAO, JDBC, Mapper (1) | 2024.09.23 |
14주차 배운점 - 톰캣, 서블릿, JSP (2) | 2024.09.23 |