본문 바로가기

Back-End 공부/Spring

코드로 보는 Servlet과 Controller의 차이 GET POST 요청

💡 Controller VS WebServlet

✅ WebServlet

클라이언트 요청을 처리하고, 요청에 대한 결과를 다시 클라이언트에게 전송하는 역할을 하는 자바 프로그램

 

- @Controller와 @RequestMapping이 합쳐진 것이다.

- WebServlet은 @WebServlet 어노테이션을 통해 클래스 단위로만 url 지정이 가능하다.

- 컨트롤러의 옛날 스타일로, Request, Response 객체를 다루는 기능이 다소 자동화되어 있지 않다.

- HttpServlet을 상속받아, doGet 혹은 doPost 함수를 오버라이딩해서 사용해야 한다.

 

📌 GET 요청

@WebServlet("/hello-servlet-rest-get")
public class HelloServletRestGet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Hello hello = new Hello();
        hello.setName("shin");
        hello.setEmail("shin@naver.cm");
        hello.setPassword("1234");

        // 직렬화 : 자바 객체 -> json
        // 객체를 데이터 스트림으로 만들어, 스트림에 객체를 출력
        ObjectMapper objectMapper = new ObjectMapper();
        String json_hello = objectMapper.writeValueAsString(hello);

        // 응답 header
        resp.setContentType("application/json");
        resp.setCharacterEncoding("UTF-8");

        // 응답 body
        PrintWriter out = resp.getWriter();
        out.print(json_hello);

        //버퍼를 통해 조립이 이루어지므로, 버퍼를 비우는 과정.
        out.flush();
    }
}
  • urlPatterns(/hello-servlet-rest-get)의 URL이 호출되면 서블릿 코드가 실행
  • HTTP 요청/응답 정보를 쉽게 사용할 수 있는 HttpServletRequest와 HttpServletResponse 객체 생성
    • request객체에서는 요청값을 쉽게 꺼내고,
    • response객체에서는 응답값에 필요한 값만 세팅해주면 http스펙에 맞게 서블릿이 간편히 처리

 

📌 POST 요청

@WebServlet("/hello-servlet-rest-post")
public class HelloServletRestPost extends HttpServlet{
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        BufferedReader br = req.getReader();
//        StringBuilder sb = new StringBuilder();
//
//        // sb에 body 데이터 전부 가져오기
//        String line = null;
//        while((line = br.readLine()) != null) {
//            sb.append(line);
//        }

        // 역직렬화 : json -> 자바 객체
        // 스트림에서 String을 읽어 와 객체로 만듬
        if(req.getContentType() == "application/json"){
            ObjectMapper mapper = new ObjectMapper();
            //Hello hello = mapper.readValue(sb.toString(), Hello.class);
            Hello hello = mapper.readValue(req.getReader(), Hello.class);
            System.out.println(hello);
        }

        // 응답 header
        resp.setContentType("text/plain");
        resp.setCharacterEncoding("UTF-8");

        // 응답 body
        PrintWriter out = resp.getWriter();
        out.print("ok");
        out.flush();
    }
}

 

 

요청 값을 꺼내거나, 응답 값을 정해주려면 HttpServletRequest 객체와 HttpServletResponse 객체가 인자로 필요하다.

서블릿으로 구현하게 되면 request  response처리를 위해 매번 위의 코드를 작성해주어야 하는데,

다른 메소드를 작성하는 경우에도 똑같은 코드들을 중복으로 작성해서 request response를 처리해야하는 불편함이 있다.

또한, 요청 정보에서 message body를 파싱하거나, 응답 header 및 body 정보를 조립하는 과정도 굉장히 번거롭다.

 

 

이에 서블릿 기술을 근간으로 두고, 내부적으로 서블릿 기능을 처리하면서 서블릿의 단점을 극복한 컨트롤러가 등장했다.

개발자는 단순히 @Controller 어노테이션을 사용함으로써, 복잡한 요청 및 응답 처리 과정을 스프링에 위임한다.

 

 

 

 Controller

- @Controller 어노테이션을 사용하며, 내부적으로 서블릿 기능을 사용하여 사용자 요청을 더 간편하게 처리한다.

- HTTP Request, Response 처리를 위해 매번 작성해줘야 했던 중복 코드들을 생략 가능하다.

- 클래스 단위(@RequestMapping) 뿐만아니라, 메서드 단위로(@GetMapping, @PostMapping)도 url 지정 가능하다.

- 현대에 많이 쓰이는 기술

 

📌 GET 요청

@Controller
@RequestMapping("hello")
public class HelloController {
    @GetMapping("json")
    @ResponseBody
    public Hello helloJson() {
        Hello hello = new Hello();
        hello.setName("kamill");
        hello.setEmail("ka@naver.com");
        hello.setPassword("1234");
        System.out.println(hello);
        return hello;
    }
}
  • HTTP response를 @ResponseBody로 String 타입의 data를 응답 메시지로 처리
  • 만약 json 객체를 리턴한다면 스프링이 알아서 json으로 변환해주는 과정, contentType을 설정하는 과정, print, flush 등의 여러 중복되는 과정들을 대신한다

 

📌 POST 요청

@Controller
@RequestMapping("hello")
public class HelloController {

    @PostMapping("form-post-handle1")
    @ResponseBody
    public String formPostHandle1(@RequestParam(value = "name") String name,
                                  @RequestParam(value = "email") String email,
                                  @RequestParam(value = "password") String password) {
        System.out.println("name = " + name);
        System.out.println("email = " + email);
        System.out.println("password = " + password);
        return "정상처리";
    }

}
  • HTTP request를 @RequestParam이 처리

 

 

 

컨트롤러를 사용하면 HTTP Request, Response 처리를 위해
매번 작성해줘야 했던 중복 코드들을 생략 가능하다는 게 무슨 말일까?

 

 

✔️ Servlet

Servlet을 사용해서 로그인과 회원가입 기능을 만들면 아래와 같은 코드로 구현할 것이다.

이때, 각 기능마다 .java 파일이 있어야 하고, 파일 안에서 doGet, doPost, request, response 코드가 계속 중복된다.

@WebServlet(urlPatterns = "/user/login")
public class loginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        // ...
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
        // ...
    }
}

   

@WebServlet(urlPatterns = "/user/signup")
public class UserSignUpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        // ...
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
        // ...
    }
}

 

 

 

✔️ Controller

반면, Controller를 이용해 로그인과 회원가입 기능을 만들면 아래와 같은 코드로 구현할 것이다.

하나의 java 파일 안에서 url로 각 기능들을 매핑이 가능해 코드 중복이 현저하게 줄어들고,

다양한 어노테이션들을 통해 내부적으로 자동화 처리된 기능들을 간편하게 가져다 쓸 수 있다!

@Controller
public class UserController {
    @GetMapping("/user/login")
    public String login() {
        // ...
    }

    @GetMapping("/user/signup")
    public String signup() {
        // ... 
    }

    @PostMapping("/user/signup")
    public String registerUser(MemberDto memberDto) {
        // ... 
    }
}