백엔드/Spring

[Spring] 서블릿, MVC 패턴

happy_life 2022. 6. 10. 15:26

목차

1. 서블릿으로 회원 관리 앱 만들기

2. MVC 패턴

 

 

 

 

1. 서블릿으로 회원 관리 앱 만들기

1) 개요

간단히 회원을 저장하고 출력하는 앱을 만들기를 코드를 통해 알아보자. 

 

 

2) 간단 코드 예제

Member.class

@Setter @Getter
public class Member {

    private Long id;
    private String name;
    private int age;
}

 

MemberRepository.class

public class MemberRepository {
    private static Map<Long, Member> database = new HashMap<>();
    private static long sequence = 0L;

    private static final MemberRepository instance = new MemberRepository();

    // 생성자 막아버리기
    private MemberRepository() {
    }

    // instance 꺼내는 방법 열어두기
    public static MemberRepository getInstance() {
        return instance;
    }

    // 데이터 저장하기
    public Member save(Member member) {
        member.setId(++sequence);
        database.put(member.getId(), member);
        return member;
    }

    // 데이터 꺼내기
    public Member findOne(Long memberId) {
        Member member = database.get(memberId);
        return member;
    }

    // 데이터 전체 꺼내기
    public List<Member> findAll() {
        return new ArrayList<>(database.values());
    }
}

 

servlet-exam.html(Form) 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>서블릿 실습 예제</title>
</head>
<body>
<form action="/servlet-member-save" method="post">
  <label for="name">Name:</label>
  <input type="text" id="name" name="name">
  <label for="age">Age:</label>
  <input type="text" id="age" name="age">
  <button type="submit">저장</button>
</form>
</body>
</html>

 

 

MemberSaveServlet.class(저장)

@WebServlet(name = "formServlet", urlPatterns = "/servlet-member-save")
public class MemberSaveServlet extends HttpServlet {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //1. POST로 받아온 데이터 꺼내기
        String name = req.getParameter("name");
        int age = Integer.parseInt(req.getParameter("age"));

        //2. Member 객체 만들기
        Member member = new Member();
        member.setName(name);
        member.setAge(age);

        //3. 저장하기
        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.getName()+"</li>\n" +
                " <li>age="+member.getAge()+"</li>\n" +
                "</ul>\n" +
                "<a href=\"/servlet-member-list\">목록조회</a>\n" +
                "<a href=\"/index.html\">돌아가기</a>\n" +
                "</body>\n" +
                "</html>");
    }
}

ContentType이 html의 text이므로 "text/html"을 넣어준다. Encoding은 utf-8로 넣는다.

html을 response의 Printwriter로 직접 작성해준다.

membersave에서는 request안에 담긴 값을 꺼내,  멤버 객체를 생성하고 memberRepository에 저장한다. 이후 멤버 객체에서 값을 꺼내 동적으로 html을 작성해 응답으로 보낸다.

 

MemberListServlet.class(조회)

@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 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) 서블릿 Form을 따로 만들지 않고 html로 Post로 데이터를 넘겨주게 된다.

순서 2) Form으로 넘어온 데이터로 객체를 만들어 저장하고, 응답한다.

순서 3) 목록을 조회하면, 메모리의 database에 저장되어 있는 member의 List를 돌면서 html을 동적으로 만들어 응답한다.

 

2. MVC 패턴

1) 개요

하나의 서블릿이나 JSP만으로 비즈니스 로직과 뷰 렌더링까지 모두 처리하게 되면, 너무 복잡해진다. 결과적으로는 유지 보수가 어려워질 것이다. 비즈니스 로직을 호출하는 부분에 변경이 발생해도 해당 코드를 손대야 하고, UI를 변경하기위해서도 비지니스 로직이 함께 있는 파일을 수정해야 한다. 이에 MVC 패턴이 등장하게 되었다.

 

MVC 패턴은 Model, Controller, View 3가지로 나눠 각각의 업무만을 특화해 담당하게 한 것이다. 

컨트롤러는 HTTP 요청을 받아서 파라미터를 검증하고, 비즈니스 로직을 실행한다. 그리고 뷰에 전달할 결과 데이터를 조회해서 모델에 담는다.

모델은 뷰에 출력할 데이터를 담아둔다. 뷰가 필요한 데이터를 모두 모델에 담아 전달해주는 덕분에 뷰는 비지니스 로직이나, 데이터 접근을 몰라도 되고, 화면을 렌더링 하는 업무에 집중할 수 있다.

는 모델에 담겨있는 데이터를 사용해서 화면을 그리는 일에 집중한다. 여기서는 HTML을 생성하는 부분을 말한다.

 

2) 간단 코드 예제

MvcMemberSaveServlet.class

@WebServlet(name = "MvcMemberSaveServlet", urlPatterns = "/servlet-mvc/members/save")
public class MvcMemberSaveServlet extends HttpServlet {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

        Member member = new Member(username, age);
        memberRepository.save(member);

        //Model에 데이터를 보관하기.
        request.setAttribute("member", member);

        //View로 넘기기
        String viewPath = "/WEB-INF/views/save-result.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);
    }

POST로 form의 데이터가 들어오면 request.setAttribute를 통해 새로 생성한 member 객체를 넣는다. 서블릿의 request에는 내부에 데이터 저장소를 가지고 있는데 이것이 model의 역할을 한다. 이를 아까와 마찬가지로 view의 save-result.jsp로 forward해주는 코드이다. forward의 인자로 request가 들어가니, save-result.jsp에서는 member 객체를 사용할 수 있다. 즉 뷰에서 모델의 데이터를 참조할 수 있게 되는 것이다.

 

모델의 데이터는 ${}라는 특정 문법을 사용하여 참조할 수 있다. 

코드를 비교해보면 알겠지만,MVC 패턴은 뷰와 모델, 비즈니스로직을 분리함으로써 유지보수에 용이한 코드를 제공한다.

 

3) MVC 패턴의 한계

1) View로 이동하는 코드가 항상 중복 호출된다.

	//View로 넘기기
        String viewPath = "/WEB-INF/views/save-result.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);

 

2) 공통 처리가 어렵다.

기능이 복잡해질수록 컨트롤러에서 공통으로 처리해야 하는 부분이 점점 더 많이 증가한다. 단순히 공통 기능을 메서드로 뽑으면 될 것같지만, 결과적으로 해당 메서드를 항상 호출해야 되고, 실수로 호출하지 않으면 문제가 될 것이다.

 

본 포스팅은 김영한님 인프런 강의내용을 바탕으로 복습을 위해 작성하였습니다.