Spring Framework

SpringMVC - Controller

ChaeHing 2023. 4. 12. 23:25

계층형 아키텍쳐

  • 클라이언트의 요청을 직접적으로 전달 받는 계층인 API계층에 속하며
  • Handler 메서드를 통해 비즈니스로직을 처리후 응답

 

엔트리포인트(Entrypoint)

package com.codestates;

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

@SpringBootApplication
public class Section3Week1Application {

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

}

@SpringBootApplication

  • 자동 구성을 활성화
  • 애플리케이션 패키지 내에서 @Component가 붙은 클래스를 검색한 후(scan), Spring Bean으로 등록
  • @Configuration 이 붙은 클래스를 자동으로 찾아주고, 추가적으로 Spring Bean을 등록

SpringApplication.run(Section3Week1Application.class, args);

  • Spring 애플리케이션을 **부트스트랩하고, 실행하는 역할을 합니다.
  • main() 메서드 내에서 SpringApplication.run()을 호출하면 Spring Boot 기반의 애플리케이션으로 동작

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

 

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

@RestController

  • 해당 클래스가 REST API의 리소스(자원, Resource)를 처리하기 위한 API 엔드포인트로 정의
  • @RestController 가 추가된 클래스는 애플리케이션 로딩 시, Spring Bean으로 등록

@ReuquestMapping

  • 클라이언트의 요청과 클라이언트 요청을 처리하는 핸들러 메서드(Handler Method)를 매핑
  • Controller 클래스 레벨에 추가하여 클래스 전체에 사용되는 공통 URL(Base URL) 설정 
    • v1/members

 

핸들러 메서드(Handler Method) 적용

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

    @GetMapping("/{member-id}")
    public ResponseEntity getMember(@PathVariable("member-id") long memberId) {
        System.out.println("# memberId: " + memberId);

        // not implementation

        
        return new ResponseEntity<>(HttpStatus.OK);
    }

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

        // not implementation

        
        return new ResponseEntity<>(HttpStatus.OK);
    }
}
  • postMember()
    • @PostMapping
      • 클라이언트의 요청 데이터(request body)를 서버에 생성할 때 사용하는 애너테이션
      • 클라이언트 쪽에서 요청 전송 시, HTTP Method 타입을 동일하게 맞춰주어야한다 - POST, GET, DELETE, PATCH 등
    • @RequestParam
      • 핸들러 메서드의 파라미터 종류 중 하나
      • 클라이언트 쪽에서 전송하는 요청 데이터를 쿼리 파라미터(Query Parmeter 또는 Query String), 폼 데이터(form-data), x-www-form-urlencoded 형식으로 전송하면 이를 서버 쪽에서 전달 받을 때 사용하는 애너테이션
      • 요청에 body로 입력받은 email, name, phone을 각각 변수로 매칭 - String email 등
    • Map<String, String> map = new HashMap<>();
      • Map객체를 통하여 데이터 리턴
      • POST Method를 처리하는 핸들러 메서드는 데이터를 생성한 후에 클라이언트 쪽에 생성한 데이터를 리턴해주는 것이 관례 - JSON
      • Map 객체를 리턴하게 되면 내부적으로 ‘이 데이터는 JSON 형식의 응답 데이터로 변환해야 되는구나’라고 이해하고 JSON 형식으로 자동 변환
    • return new ResponseEntity<>(map, HttpStatus.CREATED);
      • ResponseEntity 객체를 리턴
      • 생성자 파라미터로 응답 데이터(map)와 HTTP 응답 상태를 함께 전달
      • Map 객체로 리턴을 해도 json형식으로 리턴할수 있지만 ResponseEntity 객체로 응답데이터를 래핑하여 조금 더 세련된 방식으로 응답데이터를 생성
      • HTTP 응답 상태를 명시적으로 함께 전달하면 클라이언트의 요청을 서버가 어떻게 처리했는지를 쉽게 알 수 있다.
      • POST Method 형식의 클라이언트 요청에 대한 응답 상태는 HttpStatus.OK보다는 HttpStatus.CREATED가 조금 더 자연스럽다. - 200 OK, 201 Created
  • @Getmapping
    • 클라이언트가 서버에 리소스를 조회할 때 사용하는 애너테이션
    • Mapping에 식별자를 넣을수 있다 - @GetMapping("/{member-id}") 
      • member-id가 식별자
      • 요청 URI - /v1/members/1  -> 1이 식별자
      • 식별자란 어떤 데이터를 식별할 수 있는 고유값을 의미 - DB에 기본키(Primary key)와 같다.
      • mapping에 식별자를 따로 설정하지 않으면 클래스 단위로 매핑
        • 요청 URI - /v1/members
    • @PathVariable
      • 핸들러 메서드의 파라미터 종류 중 하나
      • @PathVariable의 괄호 안에 입력한 문자열 값은 @GetMapping 중괄호({ }) 안의 문자열과 동일해야한다
        • @GetMapping("/{member-id}") = @PathVariable("member-id")

Post 요청 보내기

 

실습

MemberController 핸들러 메서드 구현 1

  • 구현 내용
    • memberId가 1인 회원의 회원 정보 중에서 아래 정보를 수정하는 핸들러 메서드를 구현하세요.
      • phone(휴대폰 번호) 정보를 ‘010-1111-2222’로 수정하세요.
    • ⭐ 여러분들이 아직 데이터를 실제로 데이터베이스에 저장하는 학습을 진행하지 않았기 때문에 memberId가 1인 회원이 데이터베이스에 저장되어 있지 않고,  members Map에 저장되어 있습니다. 따라서 members Map에서 memberId가 1인 회원 정보(member1)를 얻어서 요청으로 전달 받은 phone 정보를 업데이트 한 뒤에 응답으로 전송하면 됩니다.
    • members Map은 key를 memberId로 가지고, 회원 정보를 포함한 map 객체를 value로 가집니다.
  • 구현 조건
    • memberId는 URI 경로에 포함되어야 합니다.
    • 수정을 위한 휴대폰 번호는 클라이언트의 요청 데이터에 포함되어야 합니다.
    • 응답 바디(Body)로 Map 객체를 사용하고 아래 데이터를 포함해야 합니다.
      • 회원 식별자
      • 이메일
      • 이름
      • 수정된 휴대폰 번호
    • 핸들러 메서드의 리턴값은 ResponseEntity 객체여야 하며, 응답 데이터를 포함해야 합니다.
    • HTTP 응답 상태 코드(Response Status Code)는 200이여야 합니다.
    • 서비스 계층을 배우지 않았기 때문에 회원 정보를 실제로 수정하는 비즈니스 로직은 필요없습니다.
  • 실행 결과
    • Postman에서 핸들러 메서드에게 API 요청을 보냈을 때, 다음과 같이 응답 결과를 받아야 합니다.
      • HTTP Response Status가 “200 OK”로 표시되어야 합니다.
      • 수정된 휴대폰 번호인 “010-1111-2222”가 응답 바디(Body)에 포함되어 있어야 합니다.


MemberController 핸들러 메서드 구현 2

  • 구현 내용
    • memberId가 1인 회원 정보를 삭제하는 핸들러 메서드를 구현하세요.
      • 이 의미는 회원 탈퇴로 인한 회원 정보 삭제를 의미합니다.
      • ⭐ 역시 아직 데이터를 실제로 데이터베이스에 저장하는 학습을 진행하지 않았기 때문에 members Map에 포함된 memberId가 1인 회원 정보를 제거(remove)하면 됩니다.
  • 구현 조건
    • memberId는 URI 경로에 포함되어야 합니다.
    • 핸들러 메서드의 리턴 값은 ResponseEntity 객체여야 합니다.
    • 응답 바디(Response Body) 데이터는 null이어야 합니다.
    • HTTP 응답 상태 코드(Response Status Code)는 204여야 합니다.
    • 서비스 계층을 배우지 않았기 때문에 회원 정보를 실제로 삭제하는 비즈니스 로직은 필요없습니다.
  • 실행 결과
    • Postman에서 핸들러 메서드에게 API 요청을 보냈을 때, [그림 h-2]와 같이 응답 결과를 받아야 합니다.
      • HTTP Response Status가 “204 NO Content”로 표시되어야 합니다.
      • 전달받은 응답 바디(Body)가 없어야 합니다.
        • Postman에서는 응답 바디(Response Body)가 null이거나, 서버 쪽에서 에러 발생으로 응답 바디가 정상적으로 전송되지 않았을 경우 [Pretty] 탭에서 1을 표시합니다.
          • [Raw] 또는 [Preview] 탭에서는 아무것도 표시되지 않습니다.
package com.codestates.member;

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

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/v1/members")
public class MemberController {
    private final Map<Long, Map<String, Object>> members = new HashMap<>();

    @PostConstruct
    public void init() {
        Map<String, Object> member1 = new HashMap<>();
        long memberId = 1L;
        member1.put("memberId", memberId);
        member1.put("email", "hgd@gmail.com");
        member1.put("name", "홍길동");
        member1.put("phone", "010-1234-5678");

        members.put(memberId, member1);
    }

    //---------------- 여기서 부터 아래에 코드를 구현하세요! --------------------//
    // 1. 회원 정보 수정을 위한 핸들러 메서드 구현
    @PatchMapping("/{member-id}")
    public ResponseEntity patchMember(@PathVariable("member-id") long memberId,
                                      @RequestParam("phone") String phone){


        // 존재하지않는 리소스(memberid)로 접근시 404 Not Found 응답
        if(!members.containsKey(memberId)) return new ResponseEntity(HttpStatus.NOT_FOUND);

        // 로직없이
        /*
        Map<String, Object> map = new HashMap<>();
        map.put("memberId", members.get(memberId).get("memberId"));
        map.put("email", members.get(memberId).get("email"));
        map.put("name", members.get(memberId).get("name"));
        map.put("phone", phone);
         */

        // 수정로직 추가 members에서 핸드폰 번호 바꾸기
        members.get(memberId).replace("phone", phone);
        Map<String, Object> map = members.get(memberId);

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

    // 2. 회원 정보 삭제를 위한 핸들러 메서드 구현
    @DeleteMapping("/{member-id}")
    public ResponseEntity deleteMember(@PathVariable("member-id") long memberId){

        // 존재하지않는 리소스(memberid)로 접근시 404 Not Found 응답
        if(!members.containsKey(memberId)) return new ResponseEntity(HttpStatus.NOT_FOUND);

        // 삭제로직 members에서 해당 멤버 삭제
        members.remove(memberId);

        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

}

PATCH - 200 OK
없는 식별자 PATCH요청 - 404 Not Found

 

Delete - 204 No Content
없는 식별자 Delete 요청 - 404 Not Found