✔️회원 정보
✔️기능 요구사항
✔️회원 도메인
@Getter
@Setter
public class Member {
private Long id;
private String username;
private int age;
protected Member() {
}
public Member(String username, int age) {
this.username = username;
this.age = age;
}
}
✔️회원 리포지토리
public class MemberRepository {
private static Map<Long, Member> store= new HashMap<>();
private static long sequence = 0L;
public static final MemberRepository instance = new MemberRepository();
private MemberRepository() {
}
public static MemberRepository getInstance() {
return instance;
}
public Member save(Member member) {
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
public Member findById(long id) {
Member member = store.get(id);
return member;
}
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
public void clearStore() {
store.clear();
}
}
✔️회원 리포지토리 테스트 코드
class MemberRepositoryTest {
private final MemberRepository memberRepository = MemberRepository.getInstance();
// 테스트가 실행되고 난 후
@AfterEach
void afterEach() {
memberRepository.clearStore();
}
@Test
void save() {
// given
Member member = new Member("김아무개", 20);
// when
Member savedMember = memberRepository.save(member);
// then
Member findMember = memberRepository.findById(savedMember.getId());
Assertions.assertThat(findMember).isEqualTo(savedMember);
}
@Test
void findAll() {
// given
Member member1 = new Member("김아무개", 20);
Member member2 = new Member("박아무개", 20);
memberRepository.save(member1);
memberRepository.save(member2);
// when
List<Member> memberList = memberRepository.findAll();
// then
Assertions.assertThat(memberList.size()).isEqualTo(2);
}
}
✔️서블릿으로 작성한 회원 관리 웹 애플리케이션 요청 코드
@WebServlet(name = "memberFormServlet", urlPatterns = "/servlet/members/new-form")
public class MemberFormServlet extends HttpServlet {
private final 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");
}
}
✔️서블릿으로 작성한 회원 관리 웹 애플리케이션 응답 코드
@WebServlet(name = "memberSaveServlet", urlPatterns = "/servlet/members/save")
public class MemberSaveServlet extends HttpServlet {
private final MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setCharacterEncoding("UTF-8");
String username = req.getParameter("username");
int age = Integer.parseInt(req.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
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>");
}
}
✔️JSP 문서
import
사용 가능)<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="hello.servlet.domain.Member" %>
<%@ page import="hello.servlet.repository.MemberRepository" %>
✔️서블릿과 JSP의 한계
컨트롤러(Controller) : HTTP 요청을 받아서 비즈니스 로직을 실행(요청 URL 매핑) + 컨트롤러의 파라미터로 모델을 넣고 이 모델에 뷰에 전달할 결과를 담는다.
모델(Movel) : 뷰에 출력할 데이터를 담는다.
뷰(View) : 모델에 담겨있는 데이터를 사용해서 화면에 나타내는 일에 집중한다.
✔️서블릿에서 사용하는 request 내부에 데이터 저장소가 존재하여 이를 모델 대용으로 사용
/WEB-INF
: 이 경로 안에 JSP가 있으면 외부에서 JSP를 호출할 수 없다. 컨트롤러를 통해서 JSP가 호출되는 것이 목표redirect
vs forward
: 리다이렉트는 실제 클라이언트에 응답이 나갔다가 클라이언트가 redirect 경로로 다시 요청한다. 클라이언트가 인지할 수 있으며 URL 경로도 실제로 변경되지만 포워드의 경우 서버 내부에서 일어나는 호출이기 때문에 클라이언트가 인지할 수 없다.@WebServlet(name = "mvcMemberFormServlet", urlPatterns = "/servlet-mvc/members/new-form")
public class MvcMemberFormServlet extends HttpServlet {
private final MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String viewPath = "/WEB-INF/views/new-form.jsp"; // 회원 등록 폼 절대 경로
RequestDispatcher dispatcher = req.getRequestDispatcher(viewPath); // 컨트롤러에서 뷰로 이동
dispatcher.forward(req, resp); // 서블릿에서 JSP 호출
}
}
@WebServlet(name = "mvcMemberListServlet", urlPatterns = "/servlet-mvc/members/list")
public class MvcMemberListServlet extends HttpServlet {
private final MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String viewPath = "/WEB-INF/views/members.jsp";
List<Member> members = memberRepository.findAll();
// 모델에 데이터 보관
req.setAttribute("members", members);
RequestDispatcher dispatcher = req.getRequestDispatcher(viewPath);
dispatcher.forward(req, resp);
}
}
@WebServlet(name = "mvcMemberSaveServlet", urlPatterns = "/servlet-mvc/members/save")
public class MvcMemberSaveServlet extends HttpServlet {
private final MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String viewPath = "/WEB-INF/views/save-result.jsp"; // 회원 등록 폼 절대 경로
String username = req.getParameter("username");
int age = Integer.parseInt(req.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
// 모델에 데이터 보관
req.setAttribute("member", member);
RequestDispatcher dispatcher = req.getRequestDispatcher(viewPath);
dispatcher.forward(req, resp);
}
}
✔️MVC패턴 - 한계
✔️해결