티스토리 뷰
해당 글은 김영한님의 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의를 수강하고 정리한 게시글입니다.
서블릿, JSP를 통해 회원 관리 웹 만들기
이번에는 서블릿과 JSP를 통해 간단하게 회원 가입을 하고 가입된 회원 목록을 볼 수 있는 웹을 만들어 보자.
시작 전 회원 도메인 모델과 회원을 저장할 저장소를 만들어 주자.
[회원 도메인 모델]
@Getter @Setter
@NoArgsConstructor
public class Member {
private Long id;
private String username;
private int age;
public Member(String username, int age) {
this.username = username;
this.age = age;
}
}
회원의 경우 Key값으로 사용될 id와 이름, 나이만 변수로 만들어 주었고 Getter, Setter, 인자가 없는 생성자는 Lombok 라이브러리로 간단하게 처리하였다.
다음은 회원을 저장할 저장소를 다음과 같이 만들어주자.
[회원 저장소]
/**
* 동시성 문제가 고려되어 있지 않음, 실무에서는 ConcurrentHashMap, AtomicLong 사용 고려
*/
public class MemberRepository {
private static Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L;
private static final MemberRepository instance = new MemberRepository();
public static MemberRepository getInstance() {
return instance;
}
private MemberRepository() {
}
public Member save(Member member) {
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
public Member findById(Long id) {
return store.get(id);
}
public List<Member> findAll() {
return new ArrayList<>(store.values()); // store 자체를 보호하기 위해 감싸줌
}
public void clearStore() {
store.clear();
}
}
’store’ 변수는 실제 회원 객체들이 들어가는 Map으로 Key, 객체 타입으로 선언해주었다.
MemberRepository는 어디서 호출하여도 동일한 결과를 얻어야하기 때문에 싱글톤으로 만들어주었다.(기본 생성자는 private으로 막고 객체를 사용하기 위해서는 getInstance로 객체를 받아야한다)
clearStore() 메서드는 회원 저장소를 테스트할 때 사용할 용도로 만들어 두었다.
참고:
저장소의 경우 학습용으로 사용하는 것이기 때문에, 동시성 문제는 고려하지않고 설계되었다.
이제 이를 가지고 순수 서블릿과 JSP를 통해 회원 관리 웹 어플리케이션을 만들어보자.
서블릿을 통해 회원 관리 웹 만들기
이제 회원가입과 가입된 회원 목록을 만들어 볼 차례이다. 간단한 회원 관리 시스템이기 때문에 회원을 등록하고 등록된 회원을 보여주기만 하면된다.
이제 다음 순서로 각 서블릿을 만들어 보자.
1. 회원 등록 폼
@WebServlet(name = "memberFormServlet", urlPatterns = "/servlet/members/new-form")
public class MemberFormServlet extends HttpServlet {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
PrintWriter w = resp.getWriter();
w.write("<!DOCTYPE html>\n" +
"<html>\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>Title</title>\n" +
"</head>\n" +
"<body>\n" +
"<form action=\"/servlet/members/save\" method=\"post\">\n" +
" username: <input type=\"text\" name=\"username\" />\n" +
" age: <input type=\"text\" name=\"age\" />\n" +
" <button type=\"submit\">전송</button>\n" + "</form>\n" +
"</body>\n" +
"</html>\n");
}
}
서블릿으로 만든 회원 등록 폼은 HTML Form 데이터를 POST방식으로 “/servlet/members/save”에 넘겨주는 역할을 한다.
이는 회원 저장 서블릿에서 데이터를 받아 회원 저장소에 저장시켜줄 것이다.
2. 회원 저장
@WebServlet(name = "memberSaveServlet", urlPatterns = "/servlet/members/save")
public class MemberSaveServlet extends HttpServlet {
MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("MemberSaveServlet.service");
String username = req.getParameter("username");
int age = Integer.parseInt(req.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
PrintWriter w = resp.getWriter();
w.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>age="+member.getAge()+"</li>\n" + "</ul>\n" +
"<a href=\"/index.html\">메인</a>\n" + "</body>\n" +
"</html>");
}
}
위에서 받은 데이터를 “req” 변수에서 꺼내 회원 객체로 저장하고 저장한 회원의 정보를 동적으로 html을 만들어서 응답하는 서블릿이다.
3. 회원 목록
@WebServlet(name = "memberListServlet", urlPatterns = "/servlet/members")
public class MemberListServlet extends HttpServlet {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
List<Member> members = memberRepository.findAll();
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
PrintWriter w = resp.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>");
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>");
}
w.write(" </tbody>");
w.write("</table>");
w.write("</body>");
w.write("</html>");
}
}
회원 목록 서블릿은 저장된 회원들을 회원 저장소에서 findAll()
메서드를 통해 resp 객체로 html 형식으로 화면에 동적으로 생성하고 응답한다.
이렇게 서블릿과 자바 코드로 HTML을 만들어 보았는데 서블릿 덕분에 동적으로 원하는 HTML을 만들 수 있지만, 이는 HTML 문법 오류등을 일으킬 수 있다는 문제점이 있다.
이를 보완하기 위해 HTML 문서에 필요한 곳만 코드를 적용해서 동적으로 사용하는 JSP, Thymeleaf등을 이용해서 변경할 수 있다.
위의 작업을 JSP로 한번 진행해보자.
참고 :
현재는 JSP 보다는 성능과 기능면에서 우수한 Thymeleaf를 사용한다.
먼저 JSP를 사용해보고 나중에 스프링과 잘 통합되는 Thymeleaf로도 만들어보자.
JSP를 통해 회원 관리 웹 만들기
앞으로 서블릿, JSP, MVC 패턴등을 이용해서 계속해서 현재의 문제점들을 개선해 나갈 것이다.
그럼으로 편리하게 참고할 수 있도록 welcome페이지를 다음과 같이 생성해주자.
[main/webapp/index.html 추가]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li><a href="basic.html">서블릿 basic</a></li>
<li>서블릿
<ul>
<li><a href="/servlet/members/new-form">회원가입</a></li>
<li><a href="/servlet/members">회원목록</a></li>
</ul>
</li>
<li>JSP
<ul>
<li><a href="/jsp/members/new-form.jsp">회원가입</a></li>
<li><a href="/jsp/members.jsp">회원목록</a></li>
</ul>
</li>
<li>서블릿 MVC
<ul>
<li><a href="/servlet-mvc/members/new-form">회원가입</a></li>
<li><a href="/servlet-mvc/members">회원목록</a></li>
</ul>
</li>
<li>FrontController - v1
<ul>
<li><a href="/front-controller/v1/members/new-form">회원가입</a></li>
<li><a href="/front-controller/v1/members">회원목록</a></li>
</ul>
</li>
<li>FrontController - v2
<ul>
<li><a href="/front-controller/v2/members/new-form">회원가입</a></li>
<li><a href="/front-controller/v2/members">회원목록</a></li>
</ul>
</li>
<li>FrontController - v3
<ul>
<li><a href="/front-controller/v3/members/new-form">회원가입</a></li>
<li><a href="/front-controller/v3/members">회원목록</a></li>
</ul>
</li>
<li>FrontController - v4
<ul>
<li><a href="/front-controller/v4/members/new-form">회원가입</a></li>
<li><a href="/front-controller/v4/members">회원목록</a></li>
</ul>
</li>
<li>FrontController - v5 - v3
<ul>
<li><a href="/front-controller/v5/v3/members/new-form">회원가입</a></li>
<li><a href="/front-controller/v5/v3/members">회원목록</a></li>
</ul>
</li>
<li>FrontController - v5 - v4
<ul>
<li><a href="/front-controller/v5/v4/members/new-form">회원가입</a></li>
<li><a href="/front-controller/v5/v4/members">회원목록</a></li>
</ul>
</li>
<li>SpringMVC - v1
<ul>
<li><a href="/springmvc/v1/members/new-form">회원가입</a></li>
<li><a href="/springmvc/v1/members">회원목록</a></li>
</ul>
</li>
<li>SpringMVC - v2
<ul>
<li><a href="/springmvc/v2/members/new-form">회원가입</a></li>
<li><a href="/springmvc/v2/members">회원목록</a></li>
</ul>
</li>
<li>SpringMVC - v3
<ul>
<li><a href="/springmvc/v3/members/new-form">회원가입</a></li>
<li><a href="/springmvc/v3/members">회원목록</a></li>
</ul>
</li>
</ul>
</body>
</html>
이제 JSP를 사용하기전 gradle에 다음 라이브러리를 추가해주자.
//JSP 추가 시작
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
implementation 'javax.servlet:jstl'
//JSP 추가 끝
1. 회원 등록 폼
[main/webapp/jsp/members/new-form.jsp 추가]
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/jsp/members/save.jsp" method="post">
username: <input type="text" name="username" />
age: <input type="text" name="age" />
<button type="submit">전송</button>
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
이 첫 줄은 JSP 문서라는 뜻을 의미한다.(이렇게 시작해야함)
회원 등록폼은 첫 줄을 제외하면 HTML과 똑같다.
참고:
url에 실행시 .jsp까지 함께 적어주어야 한다.
예시) http://localhost:8080/jsp/members/new-form.jsp
2. 회원 저장
[main/webapp/jsp/members/save.jsp 추가]
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page import="hello.servlet.domain.member.MemberRepository" %>
<%
//request, response 는 그냥 사용 가능
MemberRepository memberRepository = MemberRepository.getInstance();
System.out.println("MemberSaveServlet.service");
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
%>
<html>
<head>
<title>Title</title>
</head>
<body>
성공
<ul>
<li>id=<%=member.getId()%></li>
<li>username=<%=member.getUsername()%></li>
<li>age=<%=member.getAge()%></li>
</ul>
<a href="/index.html">메인</a>
</body>
</html>
<%@ page import ~ %>
부분은 자바의 import문과 동일하다.
<% ~~ %>
부분에는 자바 코드를 입력할 수 있고 request와 response부분은 그냥 사용이 가능하다.
<%= ~~ %>
에는 HTML에 그대로 출력할 자바 코드를 입력할 수 있다.
3. 회원 목록
[main/webapp/jsp/members.jsp 추가]
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page import="java.util.List" %>
<%@ page import="hello.servlet.domain.member.MemberRepository" %>
<%
MemberRepository memberRepository = MemberRepository.getInstance();
List<Member> members = memberRepository.findAll();
%>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="/index.html">메인</a>
<table>
<thead>
<th>id</th>
<th>username</th>
<th>age</th>
</thead>
<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>
</table>
</body>
</html>
회원 목록 부분에는 자바 코드를 이용해 members에 담아두고
원하는 HTML 부분에 ‘’’ 태그의 반복으로 출력하고 있다.
서블릿과 JSP의 한계
서블릿으로 개발할 때는 뷰(View)화면을 위한 HTML을 만드는 작업이 자바 코드에 섞여서 지저분하고 복잡했다.
이를 JSP를 사용하여 뷰를 생성하는 HTML 작업을 깔끔하게 가져가고, 중간중간 동적으로 변경이 필요한 부분에만 자바 코드를 적용했다.
하지만, 회원 저장 JSP를 보자. 코드의 상위 절반은 회원을 저장하기 위한 비즈니스 로직이고, 나머지 하위 절반만 결과를 HTML로 보여주기 위한 뷰 영역이다. (회원 목록의 경우에도 마찬가지다)
즉, 코드를 잘 보면, JAVA 코드, 데이터를 조회하는 리포지토리 등등 다양한 코드가 모두 JSP에 노출되어 있다. (JSP가 너무 많은 역할을 한다) 이는 곧 유지보수의 헬을 의미한다..
다음에는 이를 보완한 MVC 패턴을 알아보고 코드에 적용해보자.
'Backend > Spring' 카테고리의 다른 글
[Spring MVC] 10. 프론트 컨트롤러를 적용한 회원 관리 웹 (0) | 2022.03.18 |
---|---|
[Spring MVC] 09. MVC 패턴을 적용한 회원 관리 웹 (0) | 2022.03.16 |
[Spring MVC] 07. HTTP 응답 데이터 실습 (0) | 2022.03.12 |
[Spring MVC] 06. HTTP 요청 데이터 실습 (0) | 2022.03.11 |
[Spring MVC] 05. 서블릿 실습 환경 설정 (0) | 2022.03.11 |