백앤드 개발일지/스프링부트

Servlet과 Controller(feat. Serializable)

giron 2021. 7. 22. 11:09
728x90

Servlet (서블릿)

  • 자바를 사용하여 웹페이지를 동적으로 생성하는 서버 측 프로그램 혹은 그 사양을 말함

Controller

  • 스프링 서버 개발자 입장에서는 시작점과 끝점으로 보이지만, 사실 스프링이 사용자의 요청 (Request)과 응답 (Response)을 처리해 주고 있습니다.

mvc동작 방식

그럼 controller를 사용하지 않은 코드와 controller를 사용한 코드는 무엇이 다른지 코드로 확인해 보겠다.

 

/*
Servelte 코드
*/
@WebServlet(urlPatterns = "/api/products/*", loadOnStartup = 1)
public class AllInOneServlet extends HttpServlet {
    // 신규 상품 등록
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 요청 Body 의 JSON -> 자바 객체
        ProductRequestDto requestDto = null;
        StringBuffer jb = new StringBuffer();
        String line = null;
        try {
            BufferedReader reader = request.getReader();
            while ((line = reader.readLine()) != null)
                jb.append(line);

            ObjectMapper objectMapper = new ObjectMapper();
            requestDto = objectMapper.readValue(jb.toString(), ProductRequestDto.class);
        } catch (Exception e) { /*report an error*/ }

        // 관심 상품 DB 에 저장
				// ...

        // 자바 객체 -> JSON 으로 변환
        ObjectMapper objectMapper = new ObjectMapper();
        String productJson = objectMapper.writeValueAsString(product);

        // 응답 보내기
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();
        out.print(productJson);
        out.flush();
    }
}

중간에 잠깐 첨언을 하겠다..(흐름 끊길 수도 있겠지만 여기서 궁금한 점이 있을 수 있기 때문에)

예전에 serialization를 잠깐 알아볼 때, 외부와 통신할 때 자바 객체를 바이트화 시켜서 통신하는 것으로 알고 있었는데 여기서 servlet도 외부 통신이기에 HttpServeletRequest 코드 속을 파보면 serialization이 상속되어있을 줄 알았는데 아니었다...🤔🤔

HttpServletRequest
servletRequest

잠깐 좀 많이 당황하고 잘못 알고 있었나 싶었는데 위에 친절한 설명들을 읽으면,

Defines an object to provide client request information to a servlet. The servlet container creates a ServletRequest object and passes it as an argument to the servlet's service method. A ServletRequest object provides data including parameter name and values, attributes, and an input stream. Interfaces that extend ServletRequest can provide additional protocol-specific data (for example, HTTP data is provided by javax.servlet.http.HttpServletRequest. See Also: javax.servlet.http.HttpServletRequest

--> 서블릿에 클라이언트 요청 정보를 제공하는 개체를 정의합니다. 서블릿 컨테이너는 ServletRequest 객체를 생성하고 이를 서블릿의 서비스 메소드에 인수로 전달합니다. ServletRequest 객체는 매개변수 이름과 값, 속성, 입력 스트림을 포함한 데이터를 제공합니다. ServletRequest를 확장하는 인터페이스는 추가 프로토콜별 데이터를 제공할 수 있습니다(예: HTTP 데이터는 javax.servlet.http.HttpServletRequest에서 제공됩니다.

이런 설명을 볼 수 있다. 이와 관련하여 추가 자료들을 찾아보니 더 많이 알 수 있었다.

 

클라이언트에서 요청이 들어오면 서블릿 컨테이너는 http 프로토콜의 header와 body에서 정보를 가져와
HttpServletRequest, HttpServletResponse 객체에 담는다.
그리고 서블릿 컨테이너는 무조건 HttpServlet 클래스의 service(ServletRequest req, ServletResponse res) 메소드를 실행한다.

즉 Http 통신할 때 HttpServlet으로 받아도 서블릿 컨테이너로 처리하기 위해 들어오면 HttpServlet클래스를 이용해서 ServletRequest, ServletResponse를 처리한다는 결론이다. 그럼 바~로 HttpServlet을 파헤쳐 봤다.

HttpServlet

다양한 http프로토콜들의 메서드들을 볼 수 있었다..(생각보다 다양해서 놀람) 아직 Serializable이 안 나왔기 때문에 그 위에 GenericServlet을 보았다.

편-안

드디어 Serializable의 집착이 끝났다... 역시 공부했던 내용이 맞았다. 결국 통신을 위해 직렬화를 하고 있었다. 복습 겸 점검을 마치고 계속 이어가 보자!

 

/*
Controller 코드
*/
@RestController // JSON으로 데이터를 주고받음을 선언합니다.
public class AllInOneController {
    // 신규 상품 등록
    @PostMapping("/api/products")
    public Product createProduct(@RequestBody ProductRequestDto requestDto) throws SQLException {
        // 관심 상품 DB 에 저장
				// ...

        // 응답 보내기
        return product;
    }
}

 

controller를 사용하는 이유는 이러한 복잡한 과정을 위의 코드처럼 간단히 작성할 수 있기 때문이다.

그 과정을 간략히 살펴보자면 아래 그림과 같다.

MVC 동작 방식

그렇다면 JSON으로 응답할 때는 어떠한 과정을 거치는지 알아보자

Json 응답

일반적인 Controller를 사용한 코드는 아래와 같다.

@Controller
class ExamController {
	@Autowird
	private ProductRepository productRepository

	@GetMapping("/product/list")
	public @ResponseBody List<Product> getProducts() {
		return productRepository.findAll();
	}
}

매핑할 함수 앞에 @ResponseBody를 붙여주면 된다. 하지만 이러한 과정 또한 매번 반복적으로 하면 불편하기 때문에 

@ResponseBody + @Controller를 @RestController로 바꿔주기만 하면 된다. 그리고 headr의 content-type을 세팅해주면 되는데 주로 application/json로 세팅하면 된다.

@RestController
class ExamController {
	@Autowird
	private ProductRepository productRepository

	@GetMapping("/product/list")
	public List<Product> getProducts() {
		return productRepository.findAll();
	}
}

차이점 비교하다가 중간에 잠깐 샛길로 들어선 다음 다시 이어갔는데 그래도 다시 확신할 수 있어서 좋았다. 직렬화는 절대 안 까먹을 것 같은 느낌적인 느낌!

Reference

 

서블릿 컨테이너의 이해

1. Servlet Class Diagram 서블릿 컨테이너에서 사용되는 클래스들의 관계를 나타낸 클래스 다이어그램 큰 그림으로 이해하면 Servlet 인터페이스를 추상 클래스인 GenericServlet 이 구현, 그리고 이 GenericSe

tinkerbellbass.tistory.com

 

728x90