Back-End/Spring

[Spring/스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술] 서블릿, JSP, MVC 패턴

cloudmato00 2023. 5. 24. 19:16

김영한님의 인프런 [스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술]강좌를 참고하여 작성하였습니다. 

https://inf.run/NqFF

 

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의

웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., -

www.inflearn.com


회원 관리 웹 애플리케이션 요구사항

🍅위치

  • src.main.java.hello.servlet.domain.member.Member
  • src.main.java.hello.servlet.domain.member.MemberRepository

🍅src.main.java.hello.servlet.domain.member.Member

  • 클래스명 위에 lombok을 이용한 @Getter, @Setter 라이브러리를 추가해주었다. 
  • 필드는 Long 형식의 id, String 형식의 username, integer형식의 age이다. 
  • 기본 생성자와 username, age를 파라미터로 가지고 있는 생성자를 작성하였다. 

🍅src.main.java.hello.servlet.domain.member.MemberRepository

  • 싱글톤방식으로 프로그램을 설계하기 위해서는 private로 생성자를 외부로부터의 접근을 막아야 한다. 

  • 멤버를 저장할 수 있는 save 메소드를 정의하였다. 
  • 아이디를 이용하여 멤버를 조회할 수 있는 findById 메소드를 정의하였다. 
  • store에 있는 모든 values를 리스트에 넣어 반환받을 수 있는 findAll 메소드를 정의하였다. 
  • 테스트 코드 작성을 위한 clearStore 메소드를 정의하였다. 

🍅 MemberRepositoryTest 

  • 테스트 코드 작성을 위해 MemberRepository 객체를 생성할때, new를 이용한 생성은 불가능하다. 왜냐하면 싱글톤방식을 이용하고 있기 때문에 생성자에 외부에서 접근할 수 없기 때문이다. 
  • 따라서 getInstance() 메소드를 이용하여 새로운 객체를 MemberRepository 내부에서 생성해주었다. 


서블릿으로 회원 관리 웹 애플리케이션 만들기

  • 본격적으로 서블릿을 이용해 회원 관리 웹 애플리케이션을 만들어보자. 
  • 가장 먼저 서블릿으로 회원 등록 HTML 폼을 제공해보자. 

🍅 src.main.java.hello.web.servlet.MemberFormServlet.java

  • 역시 MemberRepository 객체를 생성할 때에는 new 키워드 대신 getInstence()메서드를 이용한다. 

  • 서블릿을 실행해보면 다음과 같은 form이 나타난다. 

  • Ctrl + U로 소스보기를 확인해보면 다음과 같은 HTML코드를 확인할 수 있다. 

  • 개발자도구-Form Data를 확인해보면 입력한 내용을 확인 할 수 있다. 
  • 다만 전송을 해도 오류 페이지가 드게 된다. 왜냐하면 입력해준 코드의 form태그를 살펴보면 action 속성에 입력된 데이터를 보내줄 경로의 파일이 존재하지 않기 때문이다. 

🍅src.main.java.hello.web.servlet.MemberSaveServlet.java

  • HTML Form 데이터를 전달받는 서블릿을 만들었다. 
  • 파라미터를 조회하여 Member 객체를 만든다. 
  • Member 객체를 MemberRepository를 통해서 저장한다. 
  • Member 객체를 사용해서 결과 화면용 HTML을 동적으로 만들어서 응답한다. 

  • getParameter을 통해 각 필드에 전달받은 값을 HTML코드를 이용하여 화면에 나타내주었다. 

🍅src.main.java.hello.web.servlet.MemberListServlet.java

  • 회원저장 서블릿에서 저장한 회원 리스트를 출력해 주는 서블릿 생성하였다. 
  • 저장한 회원 정보는 메모리에 저장되게 되고, 해당 서블릿의 경로로 들어가면 저장된 회원 정보가 나타난다. 
  • for문을 이용하여 members에 저장된 멤버 객체들이 동적으로 출력된다. 

  • 저장한 정보가 출력되었다. 

🍅템플릿 엔진으로

  • 이렇듯 서블릿과 자바 코드로 동적으로 원하는 HTML을 마음껏 만들 수 있으나 매우 비효율적이다. 
  • 자바 코드로 HTLM을 만들어 내는 것 보다 차라리 HTML문서에 동적으로 변경해야 하는 부분만 자바 코드를 넣는 것이 더 효율적이다.  -> 템플릿 엔진이 나온 이유. 
  • 템플릿 엔진의 예시
    • JSP, Thymeleaf, Freemarker, Velocity 등
  • 참고 : JSP는 성능과 기능면에서 다른 템플릿 엔진과의 경쟁에서 밀리는 추세이다. 
  • 강의에서는 JSP만 잠깐 만다루고 스프링과 잘 통합과는 Thymeleaf를 사용할 것이다. 

🍅Welcome파일 변경

  • index.html파일을 변경해주었다. 자세한 코드는 강의로 확인😊

JSP로 회원 관리 웹 애플리케이션 만들기

🍅 JSP 라이브러리 추가

implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
implementation 'javax.servlet:jstl'
  • build.gradle에 다음 JSP라이브러리를 추가한다. 
  • 작성자의 스프링부트는 3.0버전 미만이기에 해당 코드를 추가해주었다. 

🍅 회원 등록 JSP (webapp/jsp/members/new-form.jsp)

  • url창에는 webapp 그 하위디렉토리부터 /로 구분하여 입력한다. 
  • Form으로 입력받은 데이터는 "/jsp/members/save.jsp" 로 전달한다. 

  • 당장은 데이터를 보낼 경로에 해당하는 JSP가 존재하지 않기 때문에 에러페이지가 뜬다. 
  • 개발자도구로 확인했을 때 입력한 정보를 request에서 확인할 수 있다. 

🍅 회원 저장 JSP (webapp/jsp/members/save.jsp)

  • 자바 코드를 작성하려면  <% %> 태그를 사용한다. 
  • <% %>태그가 아닌 곳에서 자바 코드를 작성하려면 (값을 가져올 때) <%= %>코드를 사용한다. 

🍅 회원 목록 JSP (webapp/jsp/members.jsp)

  • <% %>태그를 이용하여 서블릿 실습때와 같이 MemberRepository 객체를 getInstance()메서드를 통해 생성해준다. 

  • out.write를 이용하여 동적으로 회원 정보를 출력해준다. 

 

🍅 서블릿과 JSP의 한계

  • 서블릿으로 개발할 때는 뷰(View)화면을 위한 HTML을 만드는 작업이 자바 코드에 섞여서 지저분하고 복잡했다. 
  • 하지만 JSP는 너무 많은 역할을 맡게 된다. -> 유지보수 지옥

🍅 MVC 패턴의 등장

  • 역할을 나눌 순 없을까..!?

MVC 패턴 - 개요

  • 너무 많은 역할
    • 하나의 서블릿이나 JSP만으로 비즈니스 로직과 뷰 렌더링까지 모두 처리하게 되면, 너무 많은 역할을 하게 되고, 결과적으로 유지보수가 어려워진다. 
  • 변경의 라이프 사이클
    • 사실상 제일 중요한 문제. 진짜 문제는 둘 사이의 변경의 라이프 사이클이 다르다는 것이다. 
  • 기능 특화
    • JSP같은 뷰 템플릿은 화면을 렌더링하는데 최적화 되어 있기 때문에, 이 부분의 업무만 담당 하는 것이 가장 효과적이다. 

🍅 MVC (Model View Controller)

  • 컨트롤러
    • HTTP요청을 받아서 파라미터를 검증하고, 비즈니스 로직을 실행한다. 
    • 뷰에 전달할 결과 데이터를 조회해서 모델에 담는다.
    • 참고
      • 컨트롤러에 비즈니스 로직을 줄 수도 있지만 이렇게 되면 컨트롤러가 너무 많은 역할을 담당한다. 
      • 그래서 일반적으로 비즈니스 로직은 서비스라는 계층을 별도로 만들어서 처리한다. 
      • 컨트롤러는 비즈니스 로직이 있는 서비스를 호출하는 역할을 담당한다. 
  • 모델
    • 뷰에 출력할 데이터를 담아둔다. 
    • 뷰가 필요한 데이터를 모두 모델에 전달해주는 덕분에 뷰는 비즈니스 로직이나 데이터 접근을 몰라도 되고, 화면을 렌더링 하는 일에 집중할 수 있다.
    • 모델에 담겨있는 데이터를 사용해서 화면을 그리는 일에 집중한다. 
    • 여기서는 HTML을 생성하는 부분을 말한다. 

컨트롤러가 비즈니스 로직 수행 후 모델에 데이터를 담은 뒤 뷰 로직을 호출하고, 뷰 로직은 모델 데이터를 참조하여 화면을 나타내는 것이다. 


MVC 패턴 - 적용

🍅 서블릿 생성(java.hello.servletmvc.MvcMemberFormServlet.java)

  • dispatcher.forward() : 다른 서블릿이나 JSP로 이동할 수 있는 기능이다. 서버 내부에서 다시 호출이 발생한다. 
  • redirect와는 다르다. 

🍅view 역할을 할 JSP파일 생성(webapp/WEB-INF/views/new-form.jsp)

  • 절대경로(/로 시작)가 아닌 상대경로(/로 시작하지 않는)를 사용하면 폼 전송시 현재 URL이 속한 계층 경로 + save가 호출된다.
  • WEB-INF 경로 안에 있다면 해당 JSP를 외부에서 직접 호출할 수 없다. 
    • 우리가 기대하는 것은 항상 컨트롤러를 통해서 JSP를 호출하는 것이다. 

  • 실행 결과이다. 아직은 오류가 난다. 

 

🍅 회원저장 - 컨트롤러 (MvcMemberSaveServlet)

🍅 회원 저장 결과 뷰 (/WEB-INF/views/save-result.jsp)

🍅 회원 목록 조회 - 컨트롤러 (MvcMemberListServlet)

🍅 회원 목록 조회 결과 뷰 (/WEB-INF/views/members.jsp)

 


MVC 패턴 - 한계

🍅 MVC 컨트롤러의 단점

  • forward 중복
    • View로 이동하는 코드가 항상 중복 호출되어야 한다. 
  • ViewPath에 중복
    • prefix: /WEB-INF/views/
    • suffix: .jsp
    • 만약 jsp가 아닌 thymeleaf같은 다른 뷰로 변경한다면 전체 코드를 다 변경해야 한다. 
  • 사용하지 않는 코드(사용할때도~ 사용하지 않을때도 있는)
    • 예시 : response 
  • 공통 처리가 어렵다. 
  • ==> 즉 공통처리에 어려움이 있음. 
  • ---> 프론트 컨트롤러(Front Controller) 패턴을 도입해보자.