입력폼이 바뀔 일이 없으면 html로 만드는게 더 빠르다.
그러나 프로젝트/테스크는 서블릿으로 만든 이유?(TaskAddForm, ProjectAddForm)
-> html폼이 바뀌기 때문에 동적으로 ui를 해결하기 위해서이다.
로그인/글쓰기창은 입력폼이 바뀔 일이 없다. 그래서 html로 만들어도 됐음.
project-task관계는 taksadd의 경우 동적으로 이름이 바뀌기 때문에 그걸 해결하기 위해 자바서블릿으로 만들었는데, 그걸 자바크립트가 해결할 수 있다.
링크누르면 get요청, 주소창에서 바로 요청하는것도 get 요청. 두 방법에서 post를 보낼 방법이 존재하지 않음. (네이버나 구글검색에 검색하는것 모두 get요청이다.)
프론트/백 상관없이 web이라는 세계에서는 프로토콜이 특정 언어에 종속된 것이 아니고 웹(web)에 있는 기술이기 때문에 밑바닥 지식을 확실히 깔아놔야 한다.
웹브라우저가 읽어야하는, 바라보는 루트 : 서버루트
서블릿컨텍스트가 읽어야하는, 바라보는 루트 : 컨텍스트루트(어플리케이션 루트)
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (httpRequest.getServletPath().equalsIgnoreCase("/auth") ||
httpRequest.getSession().getAttribute("loginUser") != null) {
chain.doFilter(request, response);
} else {
httpResponse.sendRedirect("/auth/form.html");
//이 경로는 서버에게 보낸다. 루트 : 9999를 의미
// bitcamp-java-project-server ... ...는 어플리케이션 루트이다.
}
}
}
위의 경로는 잘못되었다. 서버가 제대로 바라볼 수 있도록 서버루트를 지정해줘야한다.
httpResponse.sendRedirect("bitcamp-java-project-server/auth/form.html");
하지만 매번 경로를 지정해주는건 너무 귀찮은 일이다.
bitcamp-java-project
이걸 컨텍스트 루트라고 한다.
최종적으로 이렇게 변한다.
//모든 요청에 대해 반드시 이 필터를 거치게 한다.
@WebFilter("/*")
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (httpRequest.getServletPath().equalsIgnoreCase("/auth") ||
httpRequest.getSession().getAttribute("loginUser") != null) {
chain.doFilter(request, response);
} else {
ServletContext servletContext = request.getServletContext();
String contextRootPath = servletContext.getContextPath();
httpResponse.sendRedirect(contextRootPath + "/auth/form.html");
}
}
}
contextRootPath를 직접 타이핑해서는 안됀다. 이름이 바뀔 수 있기 때문이다. ServletContext는 언제어디서나 호출할 수 있으므로 rootPath또한 언제어디서나 얻을 수 있다.
로그인 잠시 막아놓고 테스트 하려면 애노테이션을 주석으로 막아둔다.
실무에 가면 doget, dopost 같이 있는 경우가 있다. 어느 방법을 택해도 상관은 없지만 고객이 원하는 화면을 시간안에 빨리 만드는게 중요하다. 리팩토링은 그 후다.
init()는 서블릿 객체가 처음 생성되고 최초로 실행되는 작업이다.
Servlet의 오리지널 init()를 오버라이딩하지말고 GenericServlet의 init()를 오버라이딩 하면 Servlet의 init()가 알아서 Generic을 호출해줄것이다.
init()를 애노테이션으로 설정하는 것도 바람직하지 않다. 하드코딩을 방지하기 위해 초기화 파라미터는 즉 web.xml로 설정하는것이 좋다!
애노테이션으로 할경우
@WebServlet(
value = "/ex06/s3",
//loadOnStartup = 1, // 이것을 붙이면 클라이언트가 요청하지 않더라도 서블릿 컨테이너가 시작될 때 객체를 자동으로 만들면서 서블릿을 실행한다.
initParams = {
@WebInitParam(name = "jdbc.driver", value = "org.mariadb.jdbc.Driver"),
@WebInitParam(name = "jdbc.url", value = "jdbc:mariadb://localhost/studydb"),
@WebInitParam(name = "jdbc.username", value = "study"),
@WebInitParam(name = "jdbc.password", value = "1111")})
@SuppressWarnings("serial")
loadOnStartup
이 코드를 붙이면 무조건 서블릿이 실행되는데, loginServlet같은 경우는 무조건 실행되어야 하는 것이기 때문에 이럴 경우를 대비해서 만들어진 것이다.
<servlet>
<servlet-name>ex06.Servlet04</servlet-name>
<servlet-class>com.eomcs.web.ex06.Servlet04</servlet-class>
<init-param>
<param-name>jdbc.driver</param-name>
<param-value>org.mariadb.jdbc.Driver</param-value>
</init-param>
<init-param>
<param-name>jdbc.url</param-name>
<param-value>jdbc:mariadb://localhost:3306/studydb</param-value>
</init-param>
<init-param>
<param-name>jdbc.username</param-name>
<param-value>study</param-value>
</init-param>
<init-param>
<param-name>jdbc.password</param-name>
<param-value>1111</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
서블릿을 web.xml에서 미리 작성해둔다.
<load-on-startup>1</load-on-startup>
이 태그를 사용해서 클라이언트가 요청하지 않더라도 미리 다 만들어둔다.(짬뽕국물같은 경우)
위의 예제는 특정 서블릿에만 해당된다. 하지만 모든 서블릿에 동일하게 하고싶다면
<!-- 컨텍스트(웹 애플리케이션 환경) 초기화 파라미터
=> Servlet, Listener, Filter 에서 모두 사용할 수 있다.
-->
<context-param>
<param-name>jdbc2.driver</param-name>
<param-value>org.mariadb.jdbc.Driver</param-value>
</context-param>
<context-param>
<param-name>jdbc2.url</param-name>
<param-value>jdbc:mariadb://localhost:3306/studydb</param-value>
</context-param>
<context-param>
<param-name>jdbc2.username</param-name>
<param-value>study</param-value>
</context-param>
<context-param>
<param-name>jdbc2.password</param-name>
<param-value>1111</param-value>
</context-param>
<context-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
<context-param>
<param-name>aaa</param-name>
<param-value>hoho</param-value>
</context-param>
위 예제는 context-parameter라고 부른다.
// 컨텍스트 초기화 파라미터 값을 꺼내려면 ServletContext 객체가 있어야 한다.
// => 웹 애플리케이션 당 ServletContext객체는 한 개이다.
// => 따라서 다음 코드 모두 같은 객체를 리턴한다.
ServletContext sc = this.getServletContext();
ServletContext sc2 = req.getServletContext();
ServletContext sc3 = this.getServletConfig().getServletContext();
다양한 상황에서 서블릿 컨텍스트객체를 꺼낼 수 있도록 여러가지 메소드가 준비되어있다. 하지만 서블릿 컨텍스트는 한 앱당 하나다! 다른 컨텍스트 아님!!!
서블릿컨피그는 서블릿당 하나
리퀘스트/리스폰스는 ㄹ요청당 하나
url에서 +는 공백(space)을 의미하는 문자열이다.
버퍼가 안차면 출력되지 않는다. 어떤때 출력되는가? 서비스메서드가 호출이 끝났을때!
PrintWriter로 출력하는건 버퍼에 저장된다. 그래서 서비스가 끝날때 자동으로 출력된다.
만약에 버퍼가 꽉차서 자동출력해버린 상태라면 refresh헤더를 아래에다 둘 경우 헤더값이 전송되지 않는다.
응답 헤더에 Refresh 정보를 추가한다. 위에서 벌써 클라이언트에게 응답을 했는데 어떻게 응답 헤더를 출력할 수 있나요? => 잊지 말자! out.println()이 출력한 것은 출력스트림 버퍼에 보관되어 있다. 따라서 아직 클라이언트에게 응답한 상태가 아니다. 그래서 다음과 같이 출력을 한 후에 응답 헤더 값을 추가하거나 변경할 수 있는 것이다. 메서드 호출이 완료될 때 비로소 클라이언트로 응답헤더와 버퍼에 저장된 message-body가 출력된다. 만약 out.println()/out.printf()/out.print() 등에서 출력한 내용이 버퍼를 꽉 채웠다면 어떻게 하나요? => 그러면 자동으로 클라이언트에게 응답한다. 따라서 일단 클라이언트에게 응답을 하면 헤더를 추가하거나 변경하는 코드는 적용되지 않는다. 즉 응답을 완료한 후에 헤더 값을 변경하거나 바꿀 수 없기 때문이다. 소용이 없다.
요청 - 출력 - 응답 - 다시요청
네이버 로그인 방식 : 로그인 - 응답 - 다시요청 (출력이 없음)
refrsh-redirect : 웹 브라우저에게 보내느것 -> 서버루트(localhost:9999/)
한 브라우저 당 하나의 httpsession
크롬브라우저 탭을 여러개 띄운건 하나의 httpSession임!
시크릿탭은 완전히 다른 httpSession이라 로그인이 연동되지 않는것.
ServletRequest는 요청이 들어오면 생겼다가 사라지는 객체임
회원가입 정보들을 서로 다른 페이지에 기입했는데 마지막에 다 출력되는 이유는 HttpSession에 정보들을 넣고 마지막에 꺼냈기 때문이다. ServletRequest끼리 정보를 공유하고 싶다면 HttpSession에 넣어야 한다는 것.
service자체를 오버라이딩하면 doget, dopost를 가리지 않고 모두 에러처리를 해줄 수 있다.
BoardDetail을실행하다 에러가 난 경우 refresh를 시키는게 아니라 forward, include가 맞다. boarddetail에 관한 에러내용이 맞기 때문이다.
로그인 로그아웃은 절대 forward,include로 처리해선 안됀다!
로그인성공했으면 아무말 할 필요 없이 redirect-refresh를 해주고 에러처리는 forward로……
메타태그로 설정해야 하는 경우와
response.setHeader("Refresh", "1;url=list");
이렇게 설정하는 경우가 더 나을때도 있다. (게시글 상세조회하는데 자동으로 이동해버리면 안돼니까)
서버루트와 컨텍스트루트 너무 헷갈려..
특정한 요청이 들어올 경우 자동으로 로그인시키기
서블릿,필터,리스너 (컴포넌트)가 바뀌면 무조건 서버 리스타트.
서블릿의 3개 메서드 - init, destroy, service()