본문 바로가기
부트캠프 개발일기/Spring MVC

41일차: Spring MVC(API 계층, Controller)

by shyun00 2023. 4. 11.

❯ Sping MVC

Spring 모듈 중 웹 계층을 담당하는 모듈이 있다.
그 중 Servlet API를 기반으로 클라이언트 요청 처리하는 모듈을 spring-webmvc라고 한다. 이를 줄여서 Spring MVC라고 한다.

* 서블릿(Servlet): 클라이언트 요청을 처리할 수 있도록 특정 규약에 맞춰서 Java 코드로 작성한 클래스파일.
   서버에서 실행되다가 웹 브라우저가 요청을 하면 해당 기능을 수행하여 웹 브라우저에 결과를 전달한다.

* 서블릿 컨테이너: 클라이언트 요청을 받아주고 응답할 수 있도록 웹 서버와 소켓으로 통신하는것.
    대표적으로 아파치 톰캣(Apache Tomcat)이 있음

  • M(Model): 클라이언트의 요청을 처리하고 응답으로 돌려주는 결과물(결과데이터)을 의미한다.
  • V(View): Model 데이터를 이용해 클라이언트에게 보여지는 리소스를 제공하는것을 의미한다.
          HTML 페이지로 출력해주거나 PDF, Excel 등의 문서로 출력하거나, XML/JSON 등 특정 형식으로 변환해주는 것을 말한다.
          (현재 진행중인 과정에서는 Model 데이터를 JSON 프로토콜 데이터로 변환하는 방법을 배울 예정임)
  • C(Controller): 클라이언트의 요청을 직접적으로 전달받는 엔드포인트(Endpoint)이다.
          클라이언트 요청을 받고 비즈니스 로직을 거쳐서 Model 데이터가 생성되면, 이를 View 에게 전달하는 역할을 한다.

Spring MVC 동작 방식

출처 : 코드스테이츠

위 그림에서 Handler는 Controller 클래스를 의미한다. 

위와 같이 DispatcherServlet이 가장 앞단에서 다른 요소들과 상호작용하며 요청을 처리하는 패턴을 Front Controller Pattern이라고 한다.

 Controller

컨트롤러는 Spring MVC에서 클라이언트의 최종 목적지라고 할 수 있다.

컨트롤러로 사용될 클래스에 아래 두 애너테이션을 추가한다.

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/v1/members")
public class MemberController {
}

@RestController 는 해당 클래스가  REST API의 리소스를 처리하기 위한 엔드포인트임을 정의한다.

      애플리케이션 로딩 시 Spring Bean 으로 등록된다.

@RequestMapping 은 클라이언트의 요청과 이를 처리하는 핸들러 메서드를 매핑해주는 애너테이션이다.
      해당 클래스 전체에 적용되는 URL을 기재한다. (세부 경로는 각 메서드별로 지정할 수도 있다.)

Handler Method

@PostMapping, @GetMapping 등의 애너테이션을 사용해 요청별로 어떤 메서드를 적용해야하는지 작성한다.

@RequestParam으로 파라미터를 설정할 수 있으며 @PathVariable을 사용해 특정 경로를 설정할 수 있다.

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value = "/v1/members", produces = {MediaType.APPLICATION_JSON_VALUE})
public class MemberController {
    @PostMapping
    public String postMember(@RequestParam("email") String email,
                             @RequestParam("name") String name,
                             @RequestParam("phone") String phone) {
        System.out.println("# email: " + email);
        System.out.println("# name: " + name);
        System.out.println("# phone: " + phone);
        
        String response =
                "{\"" + 
                   "email\":\""+email+"\"," + 
                   "\"name\":\""+name+"\",\"" + 
                   "phone\":\"" + phone+ 
                "\"}";
        return response;
    }
}

ResponseEntity

위 코드에서 JSON 형식으로 결과물을 출력하기 위해 아래와 같이 직접 양식을 지정해주고있다.

String response =
        "{\"" +
           "email\":\""+email+"\"," +
           "\"name\":\""+name+"\",\"" +
           "phone\":\"" + phone+
        "\"}";

이렇게 작성할 경우 코드 가독성이 떨어지고 오타 등의 오류가 발생하기 쉽다.

(실제 실습하면서도 해당 부분에서 몇번의 실수가 있었다..🫠)

이 점을 개선하기 위해 사용할 수 있는 것이 ResponseEntity이다.

ResponseEntity는 HttpEntity를 상속받은 클래스로 HttpStatus, HttpHeaders, HttpBody를 포함한다.

아래와 같이 ResponseEntity를 리턴하면 JSON 형식으로 직접 구현하지 않아도 JSON 형태의 결과를 출력할 수 있다.

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/v1/members")
public class MemberController {
    @PostMapping
    public ResponseEntity postMember(@RequestParam("email") String email,
                                     @RequestParam("name") String name,
                                     @RequestParam("phone") String phone) {

        Map<String, String> map = new HashMap<>();
        map.put("email", email);
        map.put("name", name);
        map.put("phone", phone);

        return new ResponseEntity<>(map, HttpStatus.CREATED);
    }
}

Seciton 3의 첫번째 날이었다. 새롭게 시작한 날이라 그런지 다시 집중도 잘되고! 재밌게 할 수 있었다.

부디 이 마음이 한달동안 계속 이어지길...!