Go, Vantage point
가까운 곳을 걷지 않고 서는 먼 곳을 갈 수 없다.
Github | https://github.com/overnew/
Blog | https://everenew.tistory.com/
티스토리 뷰
* 김영한님의 스프링 MVC 1편 강좌를 수강하며 정리한 글입니다. *
이전 글에서는 서브릿에 대해 공부하였다.
이번에는 서블릿만으로 웹 서비스를 만든다고 생각해보자.
간단히 회원 목록을 조회 요청을 했을 때, 클라이언트에게 HTML로 응답한다면 모든 HTML 테그를 일일히 다 메시지 본문에 담아주어야 한다.
PrintWriter w = response.getWriter();
w.write("<html>");
w.write("<head>");
w.write(" <meta charset=\"UTF-8\">");
w.write(" <title>Title</title>");
w.write("</head>");
w.write("<body>");
w.write("<a href=\"/index.html\">메인</a>");
w.write("<table>");
w.write(" <thead>");
w.write(" <th>id</th>");
w.write(" <th>username</th>");
w.write(" <th>age</th>");
w.write(" </thead>");
w.write(" <tbody>");
// 자바 코드 삽입으로 동적인 HTML 코드 변환이 가능
for (Member member : members) {
w.write(" <tr>");
w.write(" <td>" + member.getId() + "</td>");
w.write(" <td>" + member.getUsername() + "</td>");
w.write(" <td>" + member.getAge() + "</td>");
w.write(" </tr>");
}
// 닫는 태그들도 필요..
자바 코드로 if, for문을 통해 동적으로 HTML을 만들어 보내주더라도 , 결국 클라이언트에게 도착하는 것은 정적인 HTML문서이다.
거기에다 일일히 태그를 만들어주는 작업은 굉장히 불편한데, 이를 개선하기 위해 등장한 것이 템플릿 엔진이다.
템플릿 엔진은 HTML 문서에서 동적으로 변환이 필요한 부분만 자바코드를 적용할 수 있다.
대표적인 템플릿 엔진은 JSP와 Thymeleaf, Freemarker등이 있는데, 일단 가장 오래된 JSP를 통해 작업을 개선해보자.
JSP
jsp에서는 HMTL 코드를 사용 가능하고, <%와 %> 태그 사이에 자바 코드도 삽입할 수 있다.
(안타깝게도 IntelliJ 에서는 유료 버전만 JSP 편집을 지원한다.)
기본적으로 HTML 태그들을 그대로 작성하지만, 태그 내의 내용을 자바 코드로 동적으로 생성할 수 있다.
예를 들어 모든 멤버 목록 조회는 다음과 같이 HTML 와 출력하여 테이블을 만들 수 있다.
...
<tbody>
<%
for (Member member : members) {
out.write(" <tr>"); //멤버의 개수 만큼 테이블이 생성됨
out.write(" <td>" + member.getId() + "</td>");
out.write(" <td>" + member.getUsername() + "</td>");
out.write(" <td>" + member.getAge() + "</td>");
out.write(" </tr>");
}
%>
</tbody>
...
이러한 JSP 코드는 서버 내에서 서블릿으로 변환되기 때문에 request와 response 객체는 선언 없이도 바로 사용이 가능하다.
코드를 보면 알겠지만, HTML과 java코드가 뒤섞이면서 읽기가 쉽지 않다.
거기에 뷰 화면을 위한 코드와 비즈니스 로직이 분리되지 않아 jsp 파일이 너무 많은 역할을 담당하고 있다.
MVC(Model View Controller) 패턴
비즈니스 로직과 UI의 변경은 같이 일어나기보단 따로 일어난다. 따라서 두 역할을 같은 곳에서 담당하면 유지보수가 어려워진다.
MVC 패턴은 비즈니스 로직(서블릿과 같)과 뷰 화면(UI)을 출력하는 HTML(or jsp)을 분리해서 역할에 집중하도록 하는 것이다.
Model: 뷰가 출력할 데이터를 전달해주어, 뷰로부터 비지니스 로직의 추상화가 가능하다.
View: HTML을 생성하여 화면을 만드는 역할에 집중한다.
Controller: HTTP 요청을 받아 비지니스 로직을 실행한다. 그리고 뷰에 전달할 데이터를 모델이 담는다.
이제 서블릿을 controller, requeset와 response를 model, Jsp를 view로서 MVC 모델을 만들어 보자.
(HttpServletRequest 객체의 setAttribute()와 getAttribute로 모델처럼 사용)
Controller는 클라이언트로부터 요청을 받고, Controller를 거쳐야 뷰가 만들어진다.
여기서는 dispatcher.forward(request, response)를 통해 다른 서블릿이나 뷰(JSP 파일)로 넘어간다.
이때 뷰의 경로인 /WEB-INF/views/new-form.jsp 로 이동하는 것은 클라이언트가 아니라, 서버 내의 작업일 뿐이기 때문에 redirect(302 응답 후, 해당 url을 클라이언트가 다시 호출)가 아니다. 이러한 jsp는 외부에서 직접 경로(url)로 접근하는 것은 불가능하고 controller를 거쳐야만 한다.
(/WEB-INF 폴더 내부 파일은 직접 호출이 불가능하게 설계됨)
회원 등록 서비스를 진행한다 생각해보자.
이전에는 jsp에 자바 코드까지 삽입하여 회원 등록 비즈니스 로직을 실행했다면 이제는 controller(서블릿)에서 회원 객체를 생성해 레퍼지토리에 등록하고, 이를 Model(HttpServletRequest)에 저장한다.
HttpServletRequest 객체는 저번 글에서 정리했듯이 내부의 Map과 같은 자료구조에
setAttribute(key, value) 로 데이터를 임시 저장할 수 있다.
이제 dispatcher.forward(request,response)를 통해 jsp로 이동한 후, jsp에서 getAttribute()로 model에서 데이터를 꺼내 출력하면 된다.
이때 request 객체에서 꺼내는 atrribute는 간단히 ${atrributeName.variable}로 꺼낼 수 있는 태그을 지원한다.
이 외에도 jsp에는 루프를 작성하는 등의 여러 가지 태그가 존재한다.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
...
<tbody>
<c:forEach var="item" items="${members}">
<tr>
<td>${item.id}</td>
<td>${item.username}</td>
<td>${item.age}</td>
</tr>
</c:forEach>
</tbody>
이제 뷰 구성 요소를 바꾸려면 JSP를, 비즈니스 로직을 바꾸려면 Controller만 수정하면 된다.
하지만, 복잡한 기능을 제공할수록 서블릿마다 공통되는 메서드를 호출하게 된다.
모든 요청이 이를 호출하면 비효율적이므로 공통 기능을 처리해주는 프런트 컨트롤러가 스프링 MVC에 도입이 된다.
'개발 > Spring' 카테고리의 다른 글
[Spring] 16. Spring MVC (0) | 2022.07.11 |
---|---|
[Spring] 15. 프런트 컨트롤러 패턴, 어댑터 패턴 (0) | 2022.07.10 |
[Spring] 13. Servlet, 서블릿 (0) | 2022.07.06 |
[Spring] 12. 웹 어플리케이션 서버와 쓰레드 풀 (0) | 2022.07.04 |
[Spring] 11. 스프링 빈의 스코프(Scope) (0) | 2022.07.02 |