서블릿
웹 서버 프로그래밍을 하기 위한 자바 코드라고 할 수 있습니다.
- 서블릿의 형태 : HttpServlet 클래스를 상속한 클래스
- 서블릿의 관리 : Servlet Container 에 의해 관리,실행됩니다.
개발자는 서블릿을 가지고 무엇을 하는가?
- 웹 서버 역할 : HTTP Server + Servlet Container 를 통해 웹 서버 역할에 필요한 부분을 구현
- 개발자의 역할 : 서블릿을 만들어 HTTP 요청을 처리하고 받는 부분을 구현
대표적인 웹서버 : Tomcat
웹 애플리케이션 서버 ( WAS ) 중 하나인 톰캣은 개발자가 작성한 서블릿을 관리해주는 Servlet Container 입니다.
- 여기서 "서블릿을 관리" 해준다는 의미는?
클라이언트가 어떠한 요청을 했을 때, 어떤 서블릿을 실행할 것인지 제어해주는 것을 의미합니다.
- Tomcat 의 주요 구성인 web.xml 이란?
Servlet에 대한 정보를 쓰는 파일입니다. 최근에는 대부분 java config 파일로 자바 소스 설정을 합니다.
- 서버에서 수행하는 작업
1. 서버 TCP/IP 연결
2. HTTP 요청 메시지 파싱해서 읽기
3. POST 방식으로 URL 읽기
4. Content-Type 확인
5. HTTP Message-body 내용 파싱
6. 비지니스 로직 실행
7. HTTP Response Message 생성
8. TCP/IP에 응답 후 소켓 종료
위의 1~8번 중 6번을 제외한 부분을 WAS 가 대신 해줍니다.
대표적인 서블릿
1. DispatcherServlet
- HTTP 프로토콜로 들어오는 모든 요청을 받아, 적합한 컨트롤러에 위임해주는 프론트 컨트롤러
- 처리 과정
: 클라이언트의 요청 -> 서블릿 컨테이너가 요청 받음 ( ex. 톰캣 ) ->
DispatcherServlet -> [ 공통적인 작업을 먼저 처리한 후 해당 요청을 처리해야 하는 컨트롤러를 찾아 작업 위임 ]
2. Servlet Filter
- 서블릿 실행 전, 후에 어떤 작업을 하고자 할 때 사용됩니다.
3. Servlet Context
- 서블릿 단위로 생성되는 컨텍스트입니다.
- DispatcherServlet 과 같은 서블릿을 등록하면 해당 서블릿이 갖는 하나의 작은 컨테이너 역할을 하는 객체
4. Application Context
- Root Context로 스프링에 의해 생성되는 Bean에 대한 Spring IoC Container 입니다.
- BeanFactory 를 상속받는 Context
- 여러 서블릿에서 공통으로 사용할 Bean을 등록하는 Context
5. WebApplicationInitializer
- Servlet Context 를 프로그래밍적으로 설정하기 위한 인터페이스 ( web.xml 을 대체하기 위함 )
6. ContextLoaderListener
- 서블릿 컨테이너의 시작과 종료시에 발생하는 이벤트를 처리하는 리스너를 등록하기 위한
ServletContextListener 인터페이스의 구현체
7. AnnotationConfigWebApplicationContext
- Component class( @Configuration, @Inject, @Component ) 를 입력값으로 받는
WebApplicationContext 인터페이스의 구현체
서블릿의 형태
@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class helloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("helloServlet.service");
System.out.println("request = " + request);
System.out.println("response = " + response);
String username = request.getParameter("username");
System.out.println("username : " + username);
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
response.getWriter().write("hello" + username);
}
}
- 동작
1. "/hello" 라는 url 이 들어오면, service에 있는 코드를 실행시켜 줍니다.
2. HTTP 요청 정보 사용하기 : HttpServletRequest
3. HTTP 요청 정보 제공하기 : HttpServletResponse ( 개발자들은 여기에 데이터를 넣어준다. )
4. requset.getParameter('username")
: hello?username=park 했을 때, 입력되는 park 를 받아온다.
5. reponse에 응답 코드를 작성
6. 포스트맨으로 테스트
로그인 동작 예시
<form action="/request-param" method="post">
username: <input type="text" name="username" />
password: <input type="password" name="password" /> <button type="submit"> 전송 </button>
</form>
이러한 Form 형식이 있을 때,
버튼을 누르면 "/requset-param" 이라는 url 로 POST 를 보내게 됩니다.
@WebServlet(name = "requestParamServlet", urlPatterns = "/request-param")
public class RequestParamServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("단일 파라미터 조회 - start");
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username = " + username);
System.out.println("password = " + password);
System.out.println("단일 파라미터 조회 - end");
if(username.equals("kim") && password.equals("1234")){
response.getWriter().write("ok");
}
}
}
- 결과 : ok 가 출력되며 정상 동작함을 알 수 있습니다.
위의 서블릿으로 구현한 코드를 Spring MVC 로 구현한다면?
1. HttpServletRequest 대신 @RequestParam
위의 service 코드를 controller에 @RequestParam 을 통해 간단하게 나타낼 수 있습니다.
@PostMapping("/save")
public String process(
@RequestParam String username,
@RequestParam String password,
Model model
) {
Member member = new Member(username, password);
memberRepository.save(member);
model.addAttribute("member", member);
return "save-result";
}
2. 타임리프를 활용한 동적인 뷰 템플릿 구성
서블릿에 작성한 HTML 코드는 정적인 코드로 여러 문제점을 가지고 있습니다.
[ 기존코드 ]
@WebServlet(name = "memberSaveServlet", urlPatterns = "/save")
public class memberSaveServlet extends HttpServlet {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("MemberSaveServlet.service");
// 요청 데이터
String username = request.getParameter("username");
String password = request.getParameter("password");
// 저장
Member member = new Member(username, password);
memberRepository.save(member);
// 응답 메시지
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter writer = response.getWriter();
writer.write("<html>\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" + "</head>\n" +
"<body>\n" +
"성공\n" +
"<ul>\n" +
" <li>id="+member.getId()+"</li>\n" +
" <li>username="+member.getUsername()+"</li>\n" +
" <li>password="+member.getPassword()+"</li>\n" + "</ul>\n" +
"<a href=\"/index.html\">메인</a>\n" + "</body>\n" +
"</html>");
}
}
위의 write 부분에 작성한 html 을 아래의 타임리프를 활용한 코드로 정적으로 사용자 데이터를 관리할 수 있습니다.
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<main class="form-signin">
<form th:action="${/member/save}" method="post">
<h1 class="h3 mb-3 fw-normal">Sign in</h1>
<div class="form-floating">
<input type="text" class="form-control" id="floatingInput" name="username" placeholder="아이디를 입력하세요.">
<label for="floatingInput">ID</label>
</div>
<div class="form-floating">
<input type="password" class="form-control" id="floatingPassword" name="password" placeholder="비밀번호를 입력하세요.">
<label for="floatingPassword">Password</label>
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
</form>
</main>
</body>
</html>
타임리프의 action 코드를 통해 POST 로 사용자가 정보를 입력 후 버튼을 누르면
POST로 "member/save" 의 url 에 사용자 정보를 보냅니다.
그러면 위에 작성한 POST의 process 메서드로 비지니스 로직을 구현할 수 있게 됩니다.
이를 통해 기존에 서블릿 부분에 작성한 코드는 작성할 필요가 없게 됩니다.
'스터디' 카테고리의 다른 글
Swagger Annotation (0) | 2023.09.13 |
---|---|
CSRF (0) | 2022.12.31 |
세션과 쿠키, JWT (0) | 2022.12.31 |
로그인 페이지는 GET ? POST ? (2) (0) | 2022.12.04 |
로그인 페이지는 GET ? POST ? (1) (0) | 2022.12.01 |