[Spring] MVC - 1 복습 총 정리 上

bin1225·2023년 1월 28일
0

Spring

목록 보기
14/15
post-thumbnail

나는 망각곡선 경사가 수직으로 떨어지는 것 같다.
이전에 열심히 들었던 것 같은데, 남아있는 지식이 없는 것 같아서 복습을 결심했고, 이렇게 정리하고 기록하는 게 너무 괴롭지만 이렇게 해야지 조금이라도 머릿속에 남을 것 같아서 다시 정리해보기로 결심했다.
그래도 확실히 2~3번째 다시 들으니까 들을 때마다 새롭게 이해되는 내용이 있고, 이번에는 학교에서 Java 수업을 들어서 언어에 대한 이해도가 조금 생긴 상태로 들으니까 Spring이 이렇게 좋은 도구였다는 것을 새삼 느끼면서 강의를 들었다. 가끔 박수도 쳤다.

1. 웹 애플리케이션 이해

웹 애플리케이션 서버(WAS - Web Application Server)

웹 애플리케이션 서버는 Http기반으로 동작하며, 프로그램 코드를 실행해 애플리케이션 로직을 수행하는 서버이다.
정적 리소스, 동적 리소스를 모두 처리할 수 있으며, Spring에 내장되어 있는 Tomcat 서버가 그 예이다.

서블릿

클라이언트로부터 들어오는 요청 Http 정보를 꺼내서 서버는 처리한다. 그런데 이 요청을 받고 그 안에 있는 정보를 어떻게 꺼내야할지 생각하면 생각보다 번거로운 작업인데, 이러한 작업을 대신 해주는 것이 서블릿의 기능이다. 또 요청에 따라 다른 처리를 할텐데, 들어온 요청이 어디로 분류되는지를 판별하는 것도 일이다.

서블릿을 이용함으로써, 개발자는 의미있는 비즈니스 로직에만 집중해서 개발할 수 있게 된다.

@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
 @Override
 protected void service(HttpServletRequest request, HttpServletResponse response){
}

이렇게 servlet을 이용하여 url로 요청을 구분하고, HttpServletRequest, HttpServletResponse 를 이용해 요청 데이터를 처리하고 응답하는 기능을 편리하게 사용할 수 있다.

그리고 이러한 Servlet을 관리해주는 WAS를 서블릿 컨테이너라고 부른다.

서블릿은 싱글톤으로 관리되므로 공유변수 사용에 주의한다.

쓰레드

서블릿이 도구라면, 그것을 이용하는 손을 쓰레드라고 이해했다.
결국 요청이 오면 코드를 읽으면서 데이터를 처리할 무언가가 필요한데 그게 쓰레드이다. 하나의 요청당 하나의 쓰레드가 필요한데, 쓰레드는 생성비용이 비싸다. 따라서 쓰레드 풀을 이용해 미리 생성해둔 쓰레드를 보관하고 이용하는 방식으로 운영된다.
그리고 이러한 부분은 모두 WAS에서 알아서 관리해준다.

쓰레드 풀의 최댓값을 잘 설정하는 것도 중요하다고 한다.

HTML, HTTP API, CSR, SSR

- HTML 동적/ 정적
- HTTP API
주로 데이터만 주고 받아 다양한 시스템과 연동할 수 있다
ex) 앱 <-> 서버, 서버 <-> 서버

  • CSR : 클라이언트 사이드 렌더링
  • SSR : 서버 사이드 렌더링

2. 서블릿

@ServletComponentScan //서블릿 자동 등록
@SpringBootApplication
public class ServletApplication {
public static void main(String[] args) {
SpringApplication.run(ServletApplication.class, args);
}
}

애플리케이션 main에 @ServletComponentScan 어노테이션을 달아주면, 서블릿을 스캔하여 등록해준다.

HttpServletRequest

서블릿은 Http요청 메시지를 편리하게 사용할 수 있도록 메시지를 파싱하고 HttpServletRequest 객체에 담아서 제공한다.

임시 저장소 기능

Http 요청이 시작될 때부터 끝날 때까지 유지되는 임시 저장소 기능이 있다.
저장 : request.setAttribute(name, vlaue)
조회 : request.getAttribute(name)

조회 기능

request.getXXX를 이용해 모든 Header 정보를 조회할 수 있다.

HttpServletRequest - 기본 사용법

주로 3가지 방법을 사용한다
- 쿼리 파라미터
검색, 필터, 페이징 에서 많이 사용되며 url?key=value & ... 형식이다

String username = request.getParameter("username"); //단일 파라미터 조회
Enumeration<String> parameterNames = request.getParameterNames(); //파라미터 이름들
모두 조회
Map<String, String[]> parameterMap = request.getParameterMap(); //파라미터를 Map
으로 조회
String[] usernames = request.getParameterValues("username"); //복수 파라미터 조회

값이 중복인데 단일 파라미터로 조회하면, 첫번째 값을 반환한다.

- HTML Form
POST의 HTML Form을 전송하면 웹 브라우저는 다음 형식으로 HTTP 메시지를 생성한다.
content-type : application/x-www-form-urlencoded
message body :username=hello&age=20
이 경우 쿼리 파라미터와 동일한 형식이므로 같은 방법으로 조회가 가능하다. (request.getParameter())

- HTTP message body

message body를 그대로 읽는 경우

	ServletInputStream inputStream = request.getInputStream();
 	String messageBody = StreamUtils.copyToString(inputStream,
	StandardCharsets.UTF_8);

JSON
objectMapper를 이용하여, message body를 객체에 매핑한다.

private ObjectMapper objectMapper = new ObjectMapper();
 @Override
 protected void service(HttpServletRequest request, HttpServletResponse 
response)
 throws ServletException, IOException {
 	ServletInputStream inputStream = request.getInputStream();
 	String messageBody = StreamUtils.copyToString(inputStream,
StandardCharsets.UTF_8);
 	
 	HelloData helloData = objectMapper.readValue(messageBody,
HelloData.class);
 }

HttpServletResponse - 기본 사용법

Header 설정

response.setXXX

쿠키 편의 메서드

private void cookie(HttpServletResponse response) {
 //Set-Cookie: myCookie=good; Max-Age=600;
 //response.setHeader("Set-Cookie", "myCookie=good; Max-Age=600");
 Cookie cookie = new Cookie("myCookie", "good");
 cookie.setMaxAge(600); //600초
 response.addCookie(cookie);
}

단순 텍스트 응답

writer.println("ok");

Html 응답

HTTP API - JSON 응답

어차피 응답은 Spring 기능 이용하면 다 알아서 처리해주니까 세세한 정리는 생략, 필요하면 찾아보기

3. 서블릿, JSP, MVC 패턴

서블릿을 이용하는 경우

@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 코드를 직접 w.write를 이용해 body에 작성해줘야 한다.자바 코드로 HTML을 제공하기 때문에 오류를 찾기도 힘들고, 코드 가독성도 떨어지게 된다.

JSP를 이용하는 경우

JSP : JSP(Java Server Pages)란
Java 언어를 기반으로 하는 Server Side 스크립트 언어
HTML 코드에 Java 코드를 넣어 동적인 웹 페이지를 생성하는 웹 어플리케이션 도구
출처 : https://gmlwjd9405.github.io/2018/11/03/jsp.html

서블릿을 확장한 기능으로 JSP가 작동되면 백그라운드에서 Sevlet으로 자동 변환된다고 한다.

하지만 JSP를 이용하더라도 view와 비즈니스 로직이 한 공간에 존재한다. 따라서 JSP가 너무 많은 역할을 부담하고, 유지보수가 힘들어진다.
또 JSP는 view 렌더링에 기능이 최적화되어 있기 때문에 이 부분의 업무만 담당하는 것이 효과적이다.

view와 비즈니스 로직은 병경의 라이프 사이클이 다르다.
변경의 라이프 사이클이 다른 부분을 하나의 코드로 관리하는 것은 유지보수하기 좋지 않다.

MVC 패턴을 이용하는 경우

MVC (Model View Controller) : 쉽게 말해 컨트롤러(요청을 컨트롤)와 뷰를 분리한 패턴이다. Model을 이용해 뷰와 컨트롤러 사이에 데이터를 주고 받는다.

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

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

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


 @Override
 protected void service(HttpServletRequest request, HttpServletResponse 
response) throws ServletException, IOException {
 		String viewPath = "/WEB-INF/views/new-form.jsp";
 		RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
 		dispatcher.forward(request, response);
 }

이렇게 viewPath를 설정해주고 forward해준다. request에 데이터를 담아 model로 이용한다.

한계 : 먼저 공통 처리가 어렵다, 지금만 봐도 중복되는 코드 ( dispatcher 선언 및 forward)가 많다. 또 view path도 의미없이 반복되는 부분 (/WEB-INF, .jsp)가 있다.

이러한 한계를 해결하기 위해 Front Controller 패턴을 도입한다.

4. MVC 프레임워크 만들기

프론트 컨트롤러

- 프론트 컨트롤러 도입 후
프론트 컨트롤러 도입 후

Spring Web MVC의 핵심도 FrontController이다.
DispatcherServlet = FrontController

프론트 컨트롤러 요약

  1. 요청 url 정보를 바탕으로 해당하는 handler를 찾는다.
  2. hadler를 처리할 수 있는 Adapter를 찾는다.
    -> 추상화에 의존해서 요청이나 반환 형태에 따라 Adapter를 구현해놓고 이용하기 때문에, 유연한 형태로 컨트롤러를 구성할 수 있다.
    3,4. handler Adapter를 이용해서 요청을 처리한다.
  3. ModelAndView를 Adapter로 부터 반환받는다.
    -> view = view의 논리적 이름, Model = 데이터
    6,7. viewResolver를 호출하여 view의 풀 네임을 가져온다.
  4. Model의 데이터와 viewName을 이용하여 응답한다.

이렇게 Spring Controller의 Front Controller가 유사한 형태로 작동하며, 다양한 처리를 지원해주고 있었던 것이다!

5. MVC 구조 이해

mvc 구조

FrontController ->DispatcherServlet
스프링 MVC의 프론트 컨트롤러가 바로 디스패처 서블릿(DispatcherServlet)이다.
그리고 이 디스패처 서블릿이 바로 스프링 MVC의 핵심이다.

  • 요청흐름
  1. 핸들러 조회: 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회한다.

  2. 핸들러 어댑터 조회: 핸들러를 실행할 수 있는 핸들러 어댑터를 조회한다.

  3. 핸들러 어댑터 실행: 핸들러 어댑터를 실행한다.

  4. 핸들러 실행: 핸들러 어댑터가 실제 핸들러를 실행한다.

  5. ModelAndView 반환: 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해서
    반환한다.

  6. viewResolver 호출: 뷰 리졸버를 찾고 실행한다.
    JSP의 경우: InternalResourceViewResolver 가 자동 등록되고, 사용된다.

  7. View 반환: 뷰 리졸버는 뷰의 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 뷰 객체를
    반환한다.
    JSP의 경우 InternalResourceView(JstlView) 를 반환하는데, 내부에 forward() 로직이 있다.

  8. 뷰 렌더링: 뷰를 통해서 뷰를 렌더링 한다.

컨트롤러에 @XXXMapping("url") 으로 핸들러를 등록한다.
그리고 해당 url로 요청이 들어오면 해당 메소드를 실행하는데,
이 메소드의 형식(매개변수, return type)이 자유로운 것은 Handler Adapter가 작동하면서 해당 타입별로 알아서 잘 변형해주기때문이라고 이해했다.

요약하면, Dispatcher Servlet이 자동으로 요청 변수에 request에 들어있는 데이터를 추출해 알맞게 담아주고 (ex @requestParam) return type에 따라서 알맞게 view를 렌더링 해준다.

0개의 댓글