스프링 MVC - 1편 - 서블릿, JSP, MVC 패턴 - 회원 관리 웹 애플리케이션 만들기
인프런 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술편을 학습하고 정리한 내용 입니다.
회원 관리 웹 애플리케이션 요구사항
회원 정보
이름 : username
나이 : age
기능 요구 사항
- 회원 저장
- 회원 목록 조회
먼저 도메인을 설계하고 테스트 해보자.
hello.servlet.domain.member.Member
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Getter @Setter
public class Member {
private Long id;
private String username;
private int age;
private Member() {
}
public Member(String username, int age) {
this.username = username;
this.age = age;
}
}
간단하게 식별자, 이름, 나이로 만들었다.
이제 리포지토리 클래스를 만들자.
hello.servlet.domain.member.MemberRepository
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 동시성 문제가 고려되어 있지 않음, 실무에서는 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());
}
public void clearStore() {
store.clear();
}
}
멤버리포지토리 클래스는 싱글톤 객체로 만들었다.
private MemberRepository() {} 기본 생성자 막아주는게 중요!
그다음은 뭐 save, findById, findAll 정도 기능 만들어 주고 테스트를 위해 clearStore도 만들어 줬다. 이건 DB 안 쓰고 메모리에서만 동작할 거기 떄문에 HashMap 사용했다..
테스트를 해보자.
MemberRepositoryTest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class MemberRepositoryTest {
MemberRepository memberRepository = MemberRepository.getInstance();
@AfterEach
void afterEach() {
memberRepository.clearStore();
}
@Test
void save() throws Exception {
//given
Member member = new Member("hello", 20);
//when
Member savedMember = memberRepository.save(member);
//then
Member findMember = memberRepository.findById(savedMember.getId());
assertThat(findMember).isEqualTo(savedMember);
}
@Test
void findAll() throws Exception {
//given
Member member1 = new Member("member1", 20);
Member member2 = new Member("member2", 30);
memberRepository.save(member1);
memberRepository.save(member2);
//when
List<Member> result = memberRepository.findAll();
//then
assertThat(result.size()).isEqualTo(2);
assertThat(result).contains(member1, member2);
}
}
MemberRepository memberRepository = MemberRepository.getInstance();
싱글톤 객체기 때문에 new 가 안된다.
1
2
3
4
@AfterEach
void afterEach() {
memberRepository.clearStore();
}
테스트 하나가 끝날 때 마다 map 초기화.
첫 번째 save() 테스트는 저장 후, 반환 객체로 찾는 테스트,
두 번째 findAll() 테스트는 미리 저장한 데이터를 findAll() 로 찾아서 비교하는 테스트
결과는

잘 통과 됐다.
이제 서블릿으로 한번 애플리케이션을 만들어 보자.
서블릿으로 회원 관리 웹 애플리케이션 만들기
이제 본격적으로 서블릿으로 회원 관리 웹 애플리케이션을 만들어 보자.
가장 먼저 서블릿으로 회원 등록 HTML 폼을 제공해 보자.
회원 등록 폼폼
hello.servlet.web.servlet.MemberFormServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@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 w = response.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 을 만들어 주면 된다..
1
2
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
이거만 잊지 말고, 오타만 안 나면

이렇게 폼이 나온다.
이제 /servlet/members/save/ 로 form 액션을 지정해 놨기 때문에 저장 서블릿을 만들어 보자.
회원 저장 완료 서블릿릿
hello.servlet.web.servlet.MemberSaveServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@WebServlet(name = "memberSaveServlet", urlPatterns = "/servlet/members/save")
public class MemberSaveServlet extends HttpServlet {
public 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 w = response.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>");
}
}
자.. 잘 오타 안내고 따라와 보자..
먼저
1
2
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
request.getParameter(...) 을 이용해서 원하는 데이터를 뽑아오고
1
2
Member member = new Member(username, age);
memberRepository.save(member);
도메인 설계한 대로 객체 만들어서 저장까지 했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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>");
이제 member.getId() 등을 이용해서 동적으로 데이터를 만들어 주면 된다.
결과를 보자. 이름은 spring, 나이는 20살을 전송해 보겠다.


잘 나온다. 한번 더 해보자. 이번엔 이름 servlet, 나이 30 으로 해보겠다.

아이디도 하나 증가하고, 원하는 대로 동적으로 나왔다..
마지막으로 멤버 리스트 조회 서블릿을 만들어 보자.
회원 리스트 서블릿
hello.servlet.web.servlet.MemberListServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@WebServlet(name = "memberListServlet", urlPatterns = "/servlet/members")
public class MemberListServlet extends HttpServlet {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Member> members = memberRepository.findAll();
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
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>");
}
}
여기도 뭐 크게 다를 것 없이, 오타 안나는게 중요하고,
1
List<Member> members = memberRepository.findAll();
이걸로 모든 멤버를 가져오고
1
2
3
4
5
6
7
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>");
}
for문을 돌려서 멤버들을 동적으로 만들어 주면 된다.
결과를 보자.

아까 만든 사람들이 잘 리스트로 나온 걸 볼 수 있다.
물론 메모리에서만 있는 거라 서버 끄면 다 사라진다.
서블릿 결론
템플릿 엔진으로 지금까지 서블릿과 자바 코드만으로 HTML을 만들어 보았다. 서블릿 덕분에 동적으로 원하는 HTML을 마음껏 만들 수 있다. 정적인 HTML 문서라면 화면이 계속 달라지는 회원의 저장 결과 라던가, 회원 목록 같은 동적인 HTML을 만드는 일은 불가능 할 것이다.
그런데, 코드에서 보듯이 이것은 매우 복잡하고 비효율 적이다. 자바 코드로 HTML을 만들어 내는 것 보다 차라리 HTML 문서에 동적으로 변경해야 하는 부분만 자바 코드로 넣을 수 있다면 더 편리할 것이다.
이것이 바로 템플릿 엔진이 나온 이유다.
템플릿 엔진을 사용하면 HTML 문서에서 필요한 곳만 코드를 적용해서 동적으로 변경할 수 있다.
템플릿 엔진에는 JSP, Thymeleaf, Freemarker, Velocity 등이 있다. 다음 시간에는 JSP로 동일 작업을 해보자.
참고 JSP는 성능과 기능 면에서 다른 템플릿 엔진과의 경쟁에서 밀리면서, 점점 사장되어 가는 추세이다.
템플릿 엔진들은 각각 장단점이 있는데, 강의에서는 JSP는 앞부분에서 잠깐 다루고,
스프링과 잘 통합되는 Thymeleaf를 사용한다.
댓글남기기