[Spring/스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술] 서블릿, JSP, MVC 패턴
김영한님의 인프런 [스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술]강좌를 참고하여 작성하였습니다.
스프링 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와는 다르다.
- 서버 안에서 자기들끼리 호출하여 클라이언트에게 보낸 것이다.
- 설명이 잘 되어 있어 많은 도움이 된 다른 분의 글. (https://doublesprogramming.tistory.com/63)
🍅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) 패턴을 도입해보자.