코드스테이츠 BE 42일차 - Section 3 - Spring MVC - API 계층

coding infant·2022년 8월 22일

코드스테이츠BE

목록 보기
39/48

웹 애플리케이션 작성

코드 무조건 실제로 쳐보기!!

<학습 목표>

Spring MVC

Spring MVC란 무엇인지 이해할 수 있다.

Spring MVC의 동작방식과 구성요소를 이해할 수 있다.

Controller

API 엔드 포인트인 Controller의 구성 요소를 이해할 수 있다.

실제 동작하는 Controller의 기본 기능을 구현할 수 있다.

DTO(Data Transfer Object)

DTO가 무엇인지 이해할 수 있다

DTO Validation이 무엇인지 이해할 수 있다.

Controller에 DTO 클래스를 적용할 수 있다.

[샘플 프로젝트 소개]

커피를 주문하는 애플리케이션

커피 이름, 커피 이미지 등의 정보를 스마트폰 앱에 제공

서버 측 자원을 사용하는 쪽 : 클라이언트

백엔드는 클라이언트의 유형과 무관하게 공통의 정보 제공하도록 디자인되어야 (애플리케이션 범위 제한 = 애플리케이션 경계 설정)

[Spring MVC]

서블렛 API를 기반으로 클라이언트의 요청을 처리하는 모듈

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

서블릿(Servlet) : 클라이언트의 요청을 처리하도록 특정 규약에 맞추어서 Java 코드로 작성하는 클래스 파일

서블릿 컨테이너 : 서플릿들이 웹 애플리케이션으로 실행되도록 해줌 (ex. 아파치 톰캣)

Spring MVC 내부에서 서블릿을 기반으로 웹 애플리케이션이 동작한다

MVC : Model View Controller

[Model]

작업 처리 결과 데이터.

서비스계층 : 클라이언트의 요청 사항을 구체적으로 처리하는 영역

비지니스 로직 : 실제 요청 사항을 처리하기 위해 Java 코드로 구현한 것

[View]

Model 데이터를 이용해서 클라이언트 애플리케이션(ex. 웹브라우저) 화면에 보여지는 리소스를 제공하는 역할

  • HTML 페이지 출력

  • PDF, Excel 문서 형태로 출력

  • XML, JSON 등 특정 형식의 포맷으로의 변환✅ (프런트 백엔드와의 협업 가능)

  • JSON(JavaScript Object Notation)

Spring MVC에서 클라이턴트 애플리케이션과 서버 애플리케이션이 주고 받는 데이터 형식

{"속성":"값"} 형식

참고 : https://ko.wikipedia.org/wiki/JSON

public class Coffee {
   private String korName;
   private String engName;
   private int price;

   public Coffee(String korName, String engName, int price) {
       this.korName = korName;
       this.engName = engName;
       this.price = price;
   }
}

커피 클래스를 JSON 형태로 변환해서 전송해주기

public class JsonExample {
    public static void main(String[] args) {
        Coffee coffee = new Coffee("아메리카노", "Americano", 30000);
        Gson gson = new Gson();
        String jsonString = gson.toJson(coffee);    // Gson이라는 라이브러리 사용하여 Coffee 클래스 객체를 JSON으로 출력

        System.out.println(jsonString);             // {"korName":"아메리카노","engName":"Americano","price":3000}
    }
}

[Controller]

클라이언트의 요청을 직접적으로 전달받는 엔드포인트. Model과 View 중간에서 상호작용

@RestController
@RequestMapping(path = "/v1/coffee")
public class CoffeeController {
    private final CoffeeService coffeeService;

    CoffeeController(CoffeeService coffeeService) {
        this.coffeeService = coffeeService;
    }

    @GetMapping("/{coffee-id}")                        // @GetMapping 애노테이션 통해 클라이언트 측의 요청 수신
    public Coffee getCoffee(@PathVariable("coffee-id") long coffeeId) {
        return coffeeService.findCoffee(coffeeId);     // CoffeeService 클래스의 findCoffee() 메서드 호출
    }
}

리턴 받는 Coffee 가 Model 데이터가 됨

getCoffee()에서 Model 데이터 리턴 -> Spring 의 View가 전달받아서 JSON 포맷으로 변경 -> 클라이언트 측에 전달

[Spring MVC 동작 방식]

클라이언트가 요청 데이터 전송

-> Controller가 요청 데이터 수신 -> 비지니스 로직 처리 -> Model 데이터 생성

-> Controller에게 Model 데이터 전달 -> Controller가 View에게 Model 데이터 전달

-> View가 응답 데이터 생성

DispatcherServlet은 다른 구성 요소에게 위임하는 중 (Front Controller Pattern)

참고 사이트 : https://itvillage.tistory.com/entry/Spring-MVC%EC%9D%98-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D-%EC%B6%94%EA%B0%80-%EC%84%A4%EB%AA%85

Controller

애플리케이션의 경계 설정, 기능 구현을 위한 요구 사항 수집, 프로젝트에 Java 패키지 구조 잡기

[패키지 구조 생성]

기능 기반 패키지 구조(package-by-feature) ✅ : 테스트, 리팩토링, 분리 용이

계층 기반 패키지 구조(package-by-layer)

[커피 주문 애플리케이션의 기능 요구 사항]

주인이 커피 정보를 관리 : 커피 정보 등록, 등록한 커피 정보 수정, 등록한 커피 정보 삭제, 등록한 커피 정보 조회

고객이 커피 정보를 조회 : 커피 정보 조회

고객이 커피 주문 : 커피 주문 등록, 커피 주문 취소, 커피 주문 조회

고객이 주문한 커피를 주인이 조회 : 커피 주문 조회, 고객에게 전달 완료한 커피를 주문 완료 처리

[커피 주문 애플리케이션에 필요한 리소스]

회원(고객, 주인), 커피, 주문

[엔트리포인트 클래스 작성]

package com.codestates;

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

@SpringBootApplication                                                        // (1)
public class Section3Week1Application {

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

}

(1) @SpringBootApplication

자동 구성을 활성화

애플리케이션 패키지 내에서 @Component가 붙은 클래스를 검색한 후(Scan), Spring Bean으로 등록하는 기능을 활성화

@Configuration이 붙은 클래스를 자동으로 찾아주고, 추가적으로 Spring Bean을 등록하는 기능을 활성화

(2) SpringApplication.run(Section3WeekApplication.class, args)

Spring 애플리케이션을 부트스트랩하고, 실행 (부트스트랩 : 애플리케이션이 실행되기 전에 여러가지 설정 작업을 수행하여 실행 가능한 애플리케이션으로 만드는 단계 의미)

[MemberController 구조 작성]

package com.codestates.member;

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

@RestController                      // (1)
@RequestMapping("/v1/members")       // (2)

public class MemberController {
}
 (1) @RestController

Spring MVC에서 특정 클래스에 @RestController 추가시 해당 클래스가 REST API의 리소스를 처리하기 위한 API 엔드포인트로 작동

@RestController 추가된 클래스는 애플리케이션 로딩 시, Spring Bean으로 등록

(2) @RequestMapping

@RequestMapping은 클라이언트의 요청과 클라이언트 요청을 처리하는 핸들러 메서드를 매핑해주는 역할

@RequestMapping은 Controller 클래스 레벨에 추가하여 클래스 전체에 사용되는 공통 URL 설정을 함

참고 사이트 : REST API Resource 접근 URI 작성 규칙
https://itvillage.tistory.com/35

@SpringBootApplication의 역할 https://itvillage.tistory.com/36

Spring Boot 애플리케이션의 부트스트랩 과정 https://itvillage.tistory.com/37

<핸들러 메서드>

클라이언트의 요청을 처리

[MemberController의 핸들러 메서드]

회원의 이메일 주소, 이름, 전화번호 필요


package com.codestates.member;

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

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

@RestController
@RequestMapping(value = "/v1/members", produces = {MediaType.APPLICATION_JSON_VALUE})
                                    // produces : 응답 데이터를 어떤 미디어 티압으로 클라이언트에게 전송할 것인지

public class MemberController {
    @PostMapping        // 클라이언트의 요청 데이터를 서버에 생성할 때 사용하는 애너테이션
    public String postMember(@RequestParam("email") String email,       
                             @RequestParam("name") String name,                            
                             @RequestParam("phone") String phone) {
        // postMember() : 회원정보 등록해주는 핸들러 메서드
        // postMember()의 현 리턴 타입은 String -> JSON으로 바꿔줘야
        // RequestParam : 핸들러 메서드의 파라미터 종류 중 하나 (쿼리, 폼 전달받을 때)

        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;
    }

    @GetMapping("/{member-id}")             // @GetMapping : 클라이언트가 서버에 리소스 조회할 때 사용하는 에너테이션
    public String getMember(@PathVariable("member-id")long memberId) { // @PathVariable : 핸들러 메서드의 파라미터 중 하나. 중괄호 안의 문자열과 동일해야
        System.out.println("# memberId: " + memberId);

        // not implementation
        return null;
    }

    @GetMapping
    public String getMembers() {
        System.out.println("# get Members");

        // not implementation
        return null;
    }
}

[OrderController의 핸들러 메서드 작성]

package com.codestates.order;

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

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

@RestController
@RequestMapping(value = "/v1/orders", produces = MediaType.APPLICATION_JSON_VALUE)

public class OrderController {
    @PostMapping
    public String postOrder(@RequestParam("memberId") long memberId,
               // postOrder() : 회원 고객이 주문한 커피 주문 정보를 등록해주는 핸들러 메서드
                            @RequestParam("coffeeId") long coffeeId) {
        System.out.println("# memberId: " + memberId);
        System.out.println("# memberId: " + coffeeId);

        String response =
                "{\"" +
                        "memberId\":\""+memberId+"\"," +
                        "\"coffeeId\":\""+coffeeId+"\"" +
                "}";
        return response;

    }
    @GetMapping("/{order-id}")
    public String getOrder(@PathVariable("order-id") long orderId) {
        System.out.println("# orderId: " + orderId);

        // not implementation
        return null;
    }
    @GetMapping
    public String getOrders() {
        System.out.println("# get Orders");
        // not iplementation
        return null;
    }
}

식별자 : 어떤 데이터를 식별할 수 있는 고유값

[핵심 포인트]

  • 클라이언트의 요청을 전달 받아서 처리하기 위해서는 요청 핸들러 메서드(Request Handler Method)가 필요하다.

  • Spring MVC에서는 HTTP Method 유형과 매치되는 @GetMapping, @PostMapping 등의 애너테이션을 지원한다.

  • @PathVariable 애너테이션을 사용하면 클라이언트 요청 URI에 패턴 형식으로 지정된 변수의 값을 파라미터로 전달받을 수 있다.

  • @RequestParam 애너테이션을 사용하면 쿼리 파라미터(Query Parmeter 또는 Query string), 폼 데이터(form-data), x-www-form-urlencoded 형식의 데이터를 파라미터로 전달 받을 수 있다.

  • @GetMapping, @PostMapping 등에서 URI를 생략하면 클래스 레벨의 URI 경로만으로 요청 URI를 구성한다.

<응답 데이터에 ResponseEntity 적용>

[레거시 코드 리뷰]


package com.codestates.member;

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

//import org.springframework.http.MediaType;
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")  // (1) produces 설정 제거됨
//@RequestMapping(value = "/v1/members", produces = {MediaType.APPLICATION_JSON_VALUE})
                                    // produces : 응답 데이터를 어떤 미디어 티압으로 클라이언트에게 전송할 것인지

public class MemberController {
    @PostMapping        // 클라이언트의 요청 데이터를 서버에 생성할 때 사용하는 애너테이션
    public String postMember(@RequestParam("email") String email,       // postMember() : 회원정보 등록해주는 핸들러 메서드
                             @RequestParam("name") String name,         // postMember()의 현 리턴 타입은 String -> JSON으로 바꿔줘야야
                             @RequestParam("phone") String phone) {
        // RequestParam : 핸들러 메서드의 파라미터 종류 중 하나 (쿼리, 폼 전달받을 때)
        System.out.println("# email : " + email);
        System.out.println("# name : " + name);
        System.out.println("# phone : " + phone);

//        코드 개선이 필요한 부분 (JSON을 수작업으로 작성중)
//        String response =
//                "{\"" +
//                        "email\":\""+email+"\"," +
//                        "\"name\":\""+name+"\",\"" +
//                        "phone\":\"" + phone+
//                "\"}";
//        return response;
//    }
        // (2) JSON 문자열 수작업을 Map 객체로 대체
        Map<String, String> map = new HashMap<>();
        map.put("email", email);
        map.put("name", name);
        map.put("phone", phone);

        // (3) 리턴 값을 ResponseEntity 객체로 변경
        return new ResponseEntity<>(map, HttpStatus.CREATED);
        // ResponseEntity 객체 생성하면서 응답데이터(map)와 HTTP 응답상태 함께 전달

    @GetMapping("/{member-id}")             // @GetMapping : 클라이언트가 서버에 리소스 조회할 때 사용하는 에너테이션
    public String getMember(@PathVariable("member-id")long memberId) { // @PathVariable : 핸들러 메서드의 파라미터 중 하나. 중괄호 안의 문자열과 동일해야
        System.out.println("# memberId: " + memberId);

        // not implementation

        // (4) 리턴 값을 ResponseEntity 객체로 변경
        return new ResponseEntity<>(HttpStatus.OK);
            // ResponseEntity 객체 리턴하면서 HTTP 응답상태 OK로 전달
    }

    @GetMapping
    public String getMembers() {
        System.out.println("# get Members");

        // not implementation

        //(5) 리턴 값을 ResponseEntity 객체로 변경
        return new ResponseEntity<>(HttpStatus.OK);
    }
}

참고사이트 : HTTP 상태 코드

https://developer.mozilla.org/ko/docs/Web/HTTP/Status

ResponseEntity https://itvillage.tistory.com/44

Rendering : 브라우저에 우리가 보는 구글 메인과 같은 화면을 만들어 내는 과정

Rendering 대상 : HTML

SSR(Server Side Rendering)

서버 쪽에서 HTML 렌더링

HTML 페이지를 클라이언트 쪽에 내려준다

HTML을 탬플릿으로 취급,동적인 데이터를 채워준다

동기적인 통신

CSR(Client Side Rendering)

Frontend 쪽에서 HTML을 렌더링

HTML에 포함되는 데이터만 서버에서 받아온다 (대부분 JSON 형태. 동적인 데이터)

Javascript based 기술로 HTML에 데이터를 채워서 화면을 만든다

프런트엔드와 백엔드가 분리되어 있는 유형

이후 수정 예정

0개의 댓글