[Section3] Spring MVC - API 계층 1

2023. 6. 12. 22:38

🧑🏻‍💻 TIL(Today I Learned)


✔️ Spring MVC 아키텍처, Controller

 

1. Spring MVC란?

➡️ Spring 모듈 중에 웹 계층을 담당하는 몇 가지 모듈이 있는데 그중에서 서블릿(Servlet) API 를 기반으로 클라이언트의 요청을 처리하는 모듈을 "spring-webmvc" 라고 함 

➡️ 클라이언트의 요청을 편리하게 처리해 주는 프레임워크

스프링 프레임워크 모듈 아키텍처

 

📍서블릿(Servelt)이란?
: 클라이언트의 요청을 처리하도록 특정 규약에 맞추어서 Java 코드로 작성하는 클래스 파일 
: Spring MVC 내부에서는 서블릿을 기반으로 웹 애플리케이션이 동작함 
: 아파치 톰캣(Apache Tomcat)은 서블릿이 웹 애플리케이션으로 실행 되도록 도와주는 서블릿 컨테이너(Servlet Container) 중 하나

 

🔎 Model

➡️ Spring MVC 에서 "M"

➡️ Spring MVC 기반의 웹 애플리케이션이 클라이언트의 요청을 전달받으면 요청 사항을 처리하기 위한 작업을 하는데 이렇게 처리한 작업의 결과 데이터를 클라이언트에게 응답으로 돌려주는 작업의 처리 결과 데이터를 의미함

서비스 계층(Service Layer) : 클라이언트의 요청 사항을 구체적으로 처리하는 영역
비즈니스 로직(Business Logic) :  실제로 요청 사항을 처리하기 위해 Java 코드로 구현한 것 

 

🔎 View

➡️ Spring MVC에서 "V"

➡️ Model 데이터를 이용해서 웹 브라우저 같은 클라이언트 애플리케이션의 화면에 보이는 리소스(Resource) 제공하는 역할

➡️ 다양한 View의 형태는 다음과 같음

  1. HTML 페이지의 출력 
    - 클라이언트 애플리케이션에 보이는 HTML 페이지를 직접 렌더링 해서 클라이언트 측에 전송하는 방식 
    - 즉, 기본적인 HTML 태그로 구성된 페이지에 Model 데이터를 채워 넣은 후 최종적인 HTML 페이지 만들어서 클라이언트에게 전송
    - Spring MVC에서 지원하는 HTML 페이지 출력 기술에는 Thymleaf, FreeMaker, JSP + JSTL, Tiles 등이 있음

  2. PDF, Excel 등의 문서 형태로 출력
    - Model 데이터를 가공해서 PDF 문서나 Excel 문서를 만들어서 클라이언트 측에 전송하는 방식 
    - 문서 내에서 데이터가 동적으로 변경되어야 하는 경우 사용할 수 있는 방식 

  3. XML, JSON 등 특정 형식의 포맷으로 변환
    - Model 데이터를 특정 프로토콜 형태로 변환해서 변환된 데이터를 클라이언트 측에 전송하는 방식
    - 특정 형식의 데이터만 정송하고 프론트엔드 측에서 이 데이터를 기반으로 HTML 페이지를 만드는 방식
    - 프론트엔드 영역과 백엔드 영역이 명확하게 구분되기 때문에 개발 및 유지보수가 상대적으로 용이함
    - 프론트엔드 측에서 비동기 클라이언트 애플리케이션을 만드는 것이 가능함 
JSON(JavaScript Object Notation)이란?
: Spring MVC에서 클라이언트 애플리케이션과 서버 애플리케이션이 주고받는 데이터 형식 
: { "속성" : "값" }

 

🔎 Controller

➡️ Spring MVC에서 "C"

➡️ 클라이언트 측의 요청을 직접적으로 전달받는 엔드포인트(Endpoint)로써  Model과 View의 중간에서 상호 작용을 해주는 역할 

➡️ 즉 클라이언트의 요청을 받아서 비즈니스 로직을 거친 후에 Model 데이터가 만들어지면 Model 데이터를 View로 전달하는 역할

 

⭐️ 전체적인 MVC 동작 흐름

Client가 요청 데이터 전송 -> Controller가 요청 데이터 수신 -> 비즈니스 로직 처리 -> Model 데이터 생성 
-> Controller에게 Model 데이터 전달 -> Controller가 View에게 Model 데이터 전달 -> View가 응답 데이터 생성 

 

 

2. Spring MVC의 동작 방식과 구성 요소 

Spring MVC 동작 방식 및 구성 요소

  • 클라이언트의 요청을 제일 먼저 전달받는 구성요소는 DispatcherServlet
  • DispatcherServlet은 HandlerMapping 인터페이스에게 Controller의 검색을 위임
  • DispatcherServlet은 검색된 Controller 정보를 토대로 HandlerAdapter 인터페이스에게 Controller 클래스 내에 있는 Handler 메서드의 호출을 위임
  • HandlerAdapter 인터페이스는 Controller 클래스의 Handler 메서드를 호출
  • DispatcherServlet은 ViewResolver에게 View의 검색을 위임
  • DispatcherServlet은 View에게 Model 데이터를 포함한 응답 데이터 생성을 위임
  • DispatcherServlet은 최종 응답 데이터를 클라이언트에게 전달
DispatcherServlet이 애플리케이션의 가장 앞단에 배치되어 다른 구성요소들과 상호작용하면서 클라이언트의 요청을 처리하는 패턴
-> Front Controller Pattern

 

 

3. [실습]Controller 클래스 설계 및 구조 생성

API 계층

➡️ 오늘 배운 API 계층은 클라이언트의 요청을 직접적으로 받는 계층

➡️ Controller 클래스가 Spring MVC에서 클라이언트 요청의 최종 목적지

 

✔️  Java 패키지 구조

  • 기능 기반 패키지 구조(pakage-by-feature)
    - 애플리케이션의 패키지를 애플리케이션에서 구현해야 하는 기능을 기준으로 패키지를 구성하는 것 
    - 나누어진 패키지 안에는 하나의 기능을 완성하기 위한 계층별(API, 서비스, 데이터 엑세스) 클래스들이 모여있음
    - 예를 들어 회원을 관리하기 위한 회원 기능과 상품을 관리하기 위한 상품 기능을 각각 패키지로 나누는 것

  • 계층 기반 패키지 구조(package-by-layer)
    - 패키지를 하나의 계층으로 보고 클래스들을 계층별로 묶어서 관리하는 구조 
    - 예를 들어 controller, dto(API 계층), model, service(비즈니스 계층), respository(데이터 액세스 계층) 나누는 것
두 가지 패키지 구조는 애플리케이션의 요구 사항이나 특성에 따라 상황에 맞게 적절하게 사용하면 됨
-> 하지만 SpringBoot에서는 테스트와 리팩토링이 용이하고 나중에 마이크로 서비스 시스템으로의 분리가 상대적으로 편리한 기능 기반 구조 사용을 권장함

 

📍Controller 설계 
: 제일 먼저 '클라이언트로부터 발생할 요청에는 어떤 것들이 있을까' 고민해 보자
: 즉 '클라이언트 요청을 처리할 서버 애플리케이션의 기능으로 뭐가 필요할까'

📍 애플리케이션에 필요한 리소스 
: REST API 기반의 애플리케이션에서는 일반적으로 애플리케이션이 제공해야 될 기능을 리소스(Resource,자원)으로 분류 
: 만약 커피 주문 애플리케이션을 만든다면  회원, 커피, 주문 등과 같은 리소스가 필요할 것 

➡️ 즉, 해당 리소스에 해당하는 Controller를 만들면 됨

 

🔎 엔트리포인트(Entrypoint) 클래스

➡️ SpringBoot 기반의 애플리케이션이 정상적으로 실행되기 위해서 가장 먼저 해야할 일은 main() 메서드가 포함된 애플리케이션의 엔트리 포인트 즉, 애플리케이션의 시작점을 작성하는것 

➡️ Spring Initializr 를 통해 생성한 프로젝트에는 이미 작성되어 있음 

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication   // (1)
public class Section3Week1Application {

	public static void main(String[] args) {
     	// (2)
	  SpringApplication.run(Section3Week1Application.class, args);
	}

}
  1. @SpringBootApplication
    - 자동 구성 활성화
    - 애플리케이션 패키지 내에서 @Component가 붙은 클래스를 검색한 후, Spring Bean으로 등록하는 기능 활성화
    - @Configuration이 붙은 클래스 자동으로 찾아주고 Spring Bean을 등록하는 기능 
  2. SpringApplication.run(Section3Week1Application.class, args);
    -  Spring 애플리케이션을 부트스트랩하고 실행하는 역할
부트스트랩(Bootstrap)이란?
: 애플리케이션이 실행되기 전에 여러가지 설정 작업을 수행하여 실행 가능한 애플리케이션으로 만드는 단계

 

🔎 Controller 구조 작성

MemberController

  1. @RestController
    - 해당 클래스가  REST API의 리소스(자원, Resource)를 처리하기 위한 API 엔드포인트로 동작함을 의미 
    - @RestController가 추가된 클래스는 애플리케이션 로딩 시 Spring Bean으로 등록해 줌
  2. @RequestMapping
    - 클라이언트 요청과 클라이언트 요청을 처리하는 핸들러 메서드(Handler Method)를 매핑해 주는 역할 
    - 클래스 전체에 사용되는 공통 URL 설정을 함
❓ 엔드포인트(EndPoint)
: 해당 API를 호출할 때의 URL
: 즉 해당 API를 호출하였을 때 어떤 로직이 실행되는지 그 위치를 의미하는 URL

 

 

4. [실습]핸들러 메서드(Handler Method)

➡️ Controller 안에  Handler Method를 작성해야 클라이언트의 요청을 처리할 수 있음 

클라이언트 요청을 위해 "Postman" 이라는 툴을 사용함

 

🔎 핸들러 메서드 적용

package com.codestates.member;

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 = // JSON 형식에 맞추어 문자열 작성
                "{\"" + 
                   "email\":\""+email+"\"," + 
                   "\"name\":\""+name+"\",\"" + 
                   "phone\":\"" + phone+ 
                "\"}";
        return response;
    }

    @GetMapping("/{member-id}")
    public String getMember(@PathVariable("member-id")long memberId) { // 특정 회원 정보 조회
        System.out.println("# memberId: " + memberId);

        // not implementation
        return null;
    }

    @GetMapping
    public String getMembers() { // 모든 회원 정보
        System.out.println("# get Members");

        // not implementation
        return null;
    }
}
  • @RequestMapping(value = "/v1/members", produces = {MediaType.APPLICATION_JSON_VALUE})
    → produces 속성은 응답 데이터를 어떤 미디어 타입으로 클라이언트에게 전송할지 설정함 
        이 코드에서는 JSON 형식의 데이터를 응답 데이터로 전송하겠다는 의미 
        이 값을 설정하지 않으면 JSON 형식의 데이터를 응답으로 전송하지 않고 문자열 자체를 전송함
  • @PostMapping
    → 클라이언트의 요청 데이터(request body)를 서버에 생성할 때 사용하는 애너테이션 
  • @RequestParam
    → 핸들러 메서드의 파라미터 종류 중 하나
        클라이언트 쪽에서 전송하는 요청 데이터를 쿼리 파라미터 (Query Parameter)또는 폼 데이터(form-data), x-www-form-urlencoded 형식으로 전송하면 이를 서버 쪽에서 전달받을 때 사용하는 애너테이션 
❓ 쿼리 파라미터(Query Parameter 또는 QueryString)
: 요청 URL에서 "?"를 기준으로 붙는 key/value 쌍의 데이터 
ex) http://localhost:8080/coffees/1?page=1&size=10
  • @GetMapping
    : 클라이언트가 서버에 리소스를 조회할 때 사용하는 애너테이션 
클라이언트 쪽에서 getMember() 핸들러 메서드에 요청을 보낼 경우 최종 URI 형태
: /v1/members/{member-id}
: {member-id}는 회원 식별자를 의미하며 클라이언트가 요청을 보낼 때 URI로 어떤 값을 지정하느냐에 따라서 동적으로 바뀌는 값
  • @PathVariable
    : 핸들러 메서드의 파라미터 종류 중 하나 
    : 이 애너테이션의 괄호 안에 입력한 문자열 값은 @GetMapping("/{member-id}")의 중괄호 안의 문자열과 동일해야 함 
    : 만약 다르면 에러 발생

 


 

BELATED ARTICLES

more