Spring, java/Spring_MVC
[Web MVC] 서블릿, JSP로 회원관리 애플리케이션 만들기
재코딩
2022. 8. 30. 20:29
반응형
본 리뷰는 인프런 김영한님의 스프링 MVC 리뷰를 한 포스팅입니다.
목차
- 회원 관리 애플리케이션 요구사항
- 서블릿을 활용하여 회원 관리 웹 애플리케이션 만들기
- JSP을 활용하여 회원 관리 웹 애플리케이션 만들기
1. 회원 관리 애플리케이션 요구사항
-. 회원 정보 : 이름(username), 나이(age)
-. 기능: 회원 저장, 회원 목록 조회
Member domain
package hello.servlet.domain.member;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class Member {
//Repository에 저장하면 id가 발급되는 구조
private Long id;
private String username;
private int age;
//기본 생성자
public Member() {
}
public Member(String username, int age) {
this.username = username;
this.age = age;
}
}
Member Repository
package hello.servlet.domain.repository;
import hello.servlet.domain.member.Member;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 동시성 문제가 고려되지 않고 있다.
* 실무에서는 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;
}
//Id를 통하여 멤버 찾기
public Member findById(Long id){
return store.get(id);
}
//전체 멤버 조회
public List<Member> findAll(){
return new ArrayList<>(store.values());
}
//스토어를 비우기
public void clearStore(){
store.clear();
}
}
Member Repository Test
package hello.servlet.domain.repository;
import hello.servlet.domain.member.Member;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class MemberRepositoryTest {
MemberRepository memberRepository = MemberRepository.getInstance();
//테스트가 끝나면 초기화
@AfterEach
void afterEach(){
memberRepository.clearStore();
}
@Test
void save(){
//given
Member member = new Member("kim", 20);
//when
Member savedMember = memberRepository.save(member);
//then
Member findMember = memberRepository.findById(savedMember.getId());
assertThat(findMember).isEqualTo(savedMember);
}
@Test
void findAll(){
//given
Member member1 = new Member("member1", 20);
Member member2 = new Member("member2", 30);
memberRepository.save(member1);
memberRepository.save(member2);
//when
List<Member> all_members = memberRepository.findAll();
//then
assertThat(all_members.size()).isEqualTo(2);
assertThat(all_members).contains(member1, member2);
}
}
2. 서블릿으로 회원관리 웹 애플리케이션 만들기
회원등록 폼 HTML
package hello.servlet.web.servlet;
import hello.servlet.domain.repository.MemberRepository;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
//http://localhost:8080/servlet/members/new-form
@WebServlet(name = "memberFormServlet", urlPatterns = "/servlet/members/new-form")
public class MemberFormServlet extends HttpServlet {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter writer = response.getWriter();
writer.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
package hello.servlet.web.servlet;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.repository.MemberRepository;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet(name = "memberSaveServlet", urlPatterns = "/servlet/members/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");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
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>age="+member.getAge()+"</li>\n" +
"</ul>\n" +
"<a href=\"/index.html\">메인</a>\n" +
"</body>\n" +
"</html>");
}
}
회원 목록 HTML
package hello.servlet.web.servlet;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.repository.MemberRepository;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
@WebServlet(name = "memberListServlet", urlPatterns = "/servlet/members")
public class MemberListServlet extends HttpServlet {
MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
List<Member> members = memberRepository.findAll();
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>");
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>");
}
}
불편한 점
Java에서 Html코드를 입력해야하다보니 다소 불편한 점이 많이있다.
이를 보완하기위해서 템플릿 엔진(JSP, Thymeleaf)을 이용한다. HTML파일을 활용해서 적용!
동작 순서
1. findAll을 통해서 모든 회원을 조회한다.
2. HTML for루프를 통해서 회원 수만큼 동적으로 생성하고 응답한다.
3. JSP로 회원관리 웹 애플리케이션 만들기
-. 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 라이브러리 추가 (Build.gradle)
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
//JSP 추가
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
implementation 'javax.servlet:jstl'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
-. 회원등록 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>
-. 회원저장 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("save.jsp");
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
System.out.println("member = " + member);
memberRepository.save(member);
%>
<html>
<head>
<meta charset="UTF-8">
</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>
-. 회원목록 JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page import="hello.servlet.domain.member.MemberRepository" %>
<%@ page import="java.util.List" %>
<%
MemberRepository memberRepository = MemberRepository.getInstance();
List<Member> members = memberRepository.findAll();
%>
<html>
<head>
<meta charset="UTF-8">
<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>
서블릿과 JSP의 한계
- 서블릿으로 개발 시 뷰(View) 화면을 위한 HTML을 만드는 작업이 자바 코드에 섞여서 지저분하고 복잡하다.
- JSP를 사용한 덕분에 뷰를 생성하는 HTML 작업을 깔끔하게 가져가고, 중간중간 동적으로 변경이 필요한 부분에만 자바 코드를 적용.
- 회원 저장 JSP를 보면 코드의 상위 절반은 회원을 저장하기 위한 비즈니스 로직이고, 나머지 하위 절반만 결과를 HTML로 보여주기 위한 뷰 영역.
- JAVA 코드, 데이터를 조회하는 리포지토리 등 다양한 코드가 모두 JSP에 노출. JSP가 너무 많은 역할을 수행.
반응형