코드스테이츠 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개의 댓글