[MVC-1] 서블릿에서 JSP까지: 자바 백엔드 웹 기술의 발전과 한계

2025. 12. 30. 17:14·Spring/MVC

 자바 백엔드 기술의 역사는 '비즈니스 로직'과 '뷰(화면) 렌더링'을 어떻게 하면 깔끔하게 분리할 수 있을지에 대한 고민의 역사와 궤를 같이한다. 모든 웹 기술의 뿌리인 서블릿부터 현대적인 MVC 패턴에 이르기까지의 과정과 그 한계를 정리한다.


1. 모든 웹 기술의 뿌리, 서블릿(Servlet)

 자바에서 동적인 웹 페이지를 생성하기 위해 탄생한 표준 기술이 서블릿이다. 서블릿은 HTTP 요청 메시지를 파싱하여 개발자가 비즈니스 로직에만 집중할 수 있도록 HttpServletRequest와 HttpServletResponse 객체를 제공한다.

1.1. 서블릿 방식의 불편함: response.getWriter().write()

서블릿만으로 웹 애플리케이션을 개발하면 자바 코드 안에 HTML이 갇혀버리는 현상이 발생한다.

@WebServlet(name = "memberSaveServlet", urlPatterns = "/servlet/members/save")
public class MemberSaveServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 비즈니스 로직: 파라미터 조회 및 저장
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));
        Member member = new Member(username, age);
        memberRepository.save(member);

        // 뷰(View) 영역: 자바 코드로 HTML 작성
        response.setContentType("text/html");
        response.setCharacterEncoding("utf-8");
        PrintWriter w = response.getWriter();
        w.write("<html>\n" +
                "<body>\n" +
                "성공\n" +
                "<ul>\n" +
                "    <li>id=" + member.getId() + "</li>\n" +
                "    <li>username=" + member.getUsername() + "</li>\n" +
                "</ul>\n" +
                "</body>\n" +
                "</html>");
    }
}
  • 가독성 저하: 오타가 발생해도 컴파일 시점에 잡아낼 수 없으며, HTML 구조를 한눈에 파악하기 매우 어렵다.
  • 비효율성: 동적인 데이터를 하나 삽입하기 위해 문자열 더하기 연산을 무수히 반복해야 한다. 수천 줄의 HTML 코드를 자바 문자열로 관리하는 것은 사실상 불가능에 가깝다.

2. 템플릿 엔진의 등장과 JSP

서블릿의 가독성 문제를 해결하기 위해 등장한 것이 템플릿 엔진이다. 그중 대표적인 JSP는 자바 코드 안에 HTML을 넣는 것이 아니라, HTML 문서 안에 자바 코드를 삽입하는 역발상을 적용했다.

2.1. JSP를 통한 개선과 새로운 한계

 JSP는 서블릿보다 직관적으로 화면을 설계할 수 있게 해주었으나, 비즈니스 로직과 뷰 영역이 한 곳에 뒤섞이는 '유지보수 지옥'을 초래했다.

<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    // 비즈니스 로직
    MemberRepository memberRepository = MemberRepository.getInstance();
    String username = request.getParameter("username");
    int age = Integer.parseInt(request.getParameter("age"));
    Member member = new Member(username, age);
    memberRepository.save(member);
%>
<html>
<body>
    성공
    <ul>
        <li>id=<%=member.getId()%></li>
        <li>username=<%=member.getUsername()%></li>
    </ul>
</body>
</html>
  • 과도한 책임: 하나의 JSP가 요청 처리, DB 통신, 화면 렌더링을 모두 담당한다.
  • 변경 라이프 사이클의 충돌: UI 수정과 로직 수정은 발생 시점과 성격이 다르지만, 하나의 파일에 묶여 있어 유지보수 시 서로에게 영향을 준다.

3. MVC 패턴의 도입: 관심사의 분리

이러한 문제를 해결하기 위해 역할을 나눈 것이 바로 MVC(Model-View-Controller) 패턴이다.

  • Controller: HTTP 요청을 받아 파라미터를 검증하고 비즈니스 로직(Service 호출)을 실행한다. 결과 데이터를 모델에 담는다.
  • Model: 뷰에 출력할 데이터를 보관한다. 덕분에 뷰는 비즈니스 로직을 몰라도 렌더링에만 집중할 수 있다.
  • View: 모델의 데이터를 사용하여 화면(HTML)을 그리는 역할만 수행한다.

3.1. MVC 패턴의 적용 (Servlet + JSP)

서블릿을 컨트롤러로, JSP를 뷰로 사용하여 역할을 분리한다. 이때 /WEB-INF 경로를 활용하여 사용자가 직접 JSP를 호출하지 못하도록 보호하고, 반드시 컨트롤러를 거치도록 설계한다.

 

Controller (Servlet)

String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response); // 서버 내부에서 호출 이동

View (JSP)

<%-- JSP는 이제 모델(request attributes)에서 데이터를 꺼내 출력만 한다 --%>
<li>id=${member.id}</li>
<li>username=${member.username}</li>

4. MVC 패턴의 한계와 새로운 고민

MVC 패턴을 적용하여 역할 분리에는 성공했지만, 여전히 실무적인 관점에서는 중복이라는 문제가 남는다.

  1. 포워드 중복: 모든 컨트롤러에서 request.getRequestDispatcher(viewPath).forward() 코드가 반복된다.
  2. ViewPath 중복: /WEB-INF/views/와 같은 접두사와 .jsp와 같은 접미사가 매번 직접 입력되어야 한다.
  3. 공통 처리의 어려움: 로그인 체크나 권한 검증 등 모든 요청에서 공통으로 처리해야 하는 기능을 각 컨트롤러마다 작성하는 것은 비효율적이다.
정리하자면,  컨트롤러를 호출하기 전 '수문장' 역할을 하는 공통 입구가 필요해진 것이다.

결론: 프론트 컨트롤러(Front Controller)를 향해

 MVC 패턴은 웹 개발의 구조를 혁신했으나, 컨트롤러마다 발생하는 중복 코드는 여전히 개발자의 생산성을 저해한다. 이 문제를 해결하기 위해 도입된 것이 바로 프론트 컨트롤러 패턴이며, 이것이 곧 스프링 MVC의 핵심이 된다. 다음 포스팅에서는 이 중복을 어떻게 제거하고, 직접 MVC 프레임워크를 구축하며 스프링의 구조에 다가가는지 살펴본다.

'Spring > MVC' 카테고리의 다른 글

[MVC-6] CSR 환경에서 @RequestBody만 쓸까? (feat. 파일 업로드와 @ModelAttribute)  (0) 2026.02.18
[MVC-5] 스프링 MVC 기본 기능  (0) 2025.12.30
[MVC-4] 스프링 MVC 시작하기: 애노테이션 기반 컨트롤러  (0) 2025.12.30
[MVC-3] Spring MVC 구조 이해  (0) 2025.12.30
[MVC-2] 직접 만드는 MVC 프레임워크  (0) 2025.12.30
'Spring/MVC' 카테고리의 다른 글
  • [MVC-5] 스프링 MVC 기본 기능
  • [MVC-4] 스프링 MVC 시작하기: 애노테이션 기반 컨트롤러
  • [MVC-3] Spring MVC 구조 이해
  • [MVC-2] 직접 만드는 MVC 프레임워크
h6bro
h6bro
백엔드 개발자의 기술 블로그
  • h6bro
    Jun's Tech Blog
    h6bro
  • 전체
    오늘
    어제
    • 분류 전체보기 (250) N
      • Java (18)
        • Core (9)
        • Design Pattern (9)
      • Spring (80)
        • Core (24)
        • MVC (6)
        • DB (10)
        • JPA (26)
        • Monitoring (3)
        • Security (11)
        • WebSocket (0)
      • Database (33)
        • Redis (15)
        • MySQL (18)
      • MSA (25) N
        • MSA 기본 (11)
        • MSA 아키텍처 (14) N
      • Kafka (30)
        • Core (18)
        • Connect (12)
      • ElasticSearch (11)
        • Search (11)
        • Logging (0)
      • Test (4)
        • k6 (4)
      • Docker (9)
      • CI&CD (10)
        • GitHub Actions (6)
        • ArgoCD (4)
      • Kubernetes (18)
        • Core (12)
        • Ops (6)
      • Cloud Engineering (4)
        • AWS Infrastructure (3)
        • AWS EKS (1)
        • Terraform (0)
      • Project (8)
        • LinkFolio (1)
        • Secondhand Market (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

    • Cloud Engineering 포스팅 정리
  • 인기 글

  • 태그

    ㅈ
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
h6bro
[MVC-1] 서블릿에서 JSP까지: 자바 백엔드 웹 기술의 발전과 한계
상단으로

티스토리툴바