어노테이션 기반 설계
컨트롤러 클래스
- MVC 패턴 중 핸들러 메소드를 포함하는 컨트롤러 빈을 만드는 과정
- @Controller
- 컨트롤러 빈으로 스프링 컨테이너에 인식
- View를 반환하기 위해 사용
- 객체를 상황에 맞는 ResponseEntity로 감싸서 반환해주어야 한다.
- Spring MVC Container View 반환 과정
- Client는 URI 형식으로 웹 서비스에 요청을 보낸다.
- DispatcherServlet이 요청을 처리할 대상을 찾는다.
- HandlerAdapter을 통해 요청을 Controller로 위임한다.
- Controller는 요청을 처리한 후에 ViewName을 반환한다.
- DispatcherServlet은 ViewResolver를 통해 ViewName에 해당하는 View를 찾아 사용자에게 반환한다.
- Controller가 반환환 뷰의 이름으로부터 View를 렌더링하기 위해서는 ViewResolver가 사용되며, ViewResolver 설정에 맞게 View를 찾아 렌더링한다.
- @RestController = @ResponseBody(바디 부분을 출력) + @Controller
- Json 형태로 객체 데이터를 반환
- REST API를 개발 할 때 주로 사용하며 객체를 ResponseEntity로 감싸서 반환
- 반환 과정
- Client는 URI 형식으로 웹 서비스에 요청을 보낸다.
- DispatcherServlet이 요청을 처리할 대상을 찾는다.
- HandlerAdapter을 통해 요청을 Controller로 위임한다.
- Controller는 요청을 처리한 후에 객체를 반환한다.
- 반환되는 객체는 Json으로 Serialize되어 사용자에게 반환된다.
- 반환 과정
핸들러 메소드
- 스프링 웹서비스가 받는 URI 요청을 컨트롤러 클래스의 특정 메소드에 매핑(비즈니스 로직으로 타고 들어갈 수 있도록)하는 과정
- @RequestMapping
- Spring 개발 시 특정 URL로 요청(Request)을 보내면 Controller에서 어떠한 방식으로 처리할지 정의한다.
이때 들어온 요청을 특정 method와 매핑하기 위해 사용하는 어노테이션 - 스프링부트 애플리케이션이 실행되면 애플리케이션에서 사용할 bean들을 담을 ApplicationContext를 생성하고 초기화합니다. refresh과정에서 Spring Application 구동을 위해 많은 Bean들이 생성되고,3) Bean으로 등록된 HandlerMapping이 변수들을 찾아서 Adapter를 거쳐 실행합니다.
- 그 중 하나가 RequestMappingHandlerMapping 입니다. 이 Bean은 우리가 @RequestMapping 으로 등록한 메서드들을 가지고 있다가 요청이 들어오면 Mapping해주는 역할을 합니다.
- @RequestMapping 이 붙은 메서드들이 핸들러에 등록되는 것은 Application refresh되는 과정에서 일어납니다.
- Spring 개발 시 특정 URL로 요청(Request)을 보내면 Controller에서 어떠한 방식으로 처리할지 정의한다.
1) @PostMapping : HTTP Post Method에 해당하는 단축 표현으로 서버에 리소스를 등록(저장)할 때 사용합니다.
2) @GetMapping : HTTP Get Method에 해당하는 단축 표현으로 서버의 리소스를 조회할 때 사용합니다.
3) @DeleteMapping : 서버의 리소스를 삭제
4) @PutMapping : 서버의 리소스를 모두 수정
5) @PatchMapping : 서버의 리소스를 일부 수정
코로나 줄서기 Controller 구현
// adminController
package com.example.Get_In_Line.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("/admin") // 공통적인 맵핑을 할때 사용 -> /admin/places에서 공통적으로 admin 경로를 거치므로 RequestMapping 처리
@Controller
public class AdminController {
@GetMapping("/places")
public String adminPlaces(){
return "admin/places";
}
@GetMapping("/places/{placeId}")
public String adminPlaceDetail(@PathVariable Integer placeId){ // @PathVariable : path안의 일부 요소를 변수로 사용 -> placeId를 안에서 사용가능
return "admin/place-detail";
}
@GetMapping("/events")
public String adminEvents(){
return "admin/events";
}
@GetMapping("/events/{eventId")
public String adminEventDetail(@PathVariable Integer eventId){
return "admin/event-detail";
}
}
// AuthController
package com.example.Get_In_Line.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class AuthController {
// 핸들러 메서드
// 로그인
@GetMapping("/login")
public String login(){
return "auth/login";
}
// 회원가입
@GetMapping("/sign-up")
public String signUp(){
return "auth/sign-up";
}
}
// BaseController
package com.example.Get_In_Line.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class BaseController {
// root 페이지 설정
@GetMapping("/")
public String root(){
return "index"; // 타임리프를 통해 기본경로를 templates로 변경하여 안에 있는 index.html을 기본 페이지로 설정, static에 index.html을 생성하면 welcomepage로 인식해서 기본페이지 설정 가능
}
}
// EventController
package com.example.Get_In_Line.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("/events")
@Controller
public class EventController {
@GetMapping("/")
public String events(){
return "event/index";
}
@GetMapping("/{eventId}")
public String eventDetail(@PathVariable Integer eventId){
return "event/detail";
}
}
// placeController
package com.example.Get_In_Line.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("/places")
@Controller
public class PlaceController {
@GetMapping("/")
public String places(){
return "place/index";
}
@GetMapping("/{placeId}")
public String placeDetail(@PathVariable Integer placeId){
return "place/detail";
}
}
APIController 구현
// APIAuthController
package com.example.Get_In_Line.Controller.api;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/api")
@RestController
public class APIAuthController {
@GetMapping("/sign-up")
public String signUp(){
return "done.";
}
@GetMapping("/login")
public String login(){
return "done.";
}
}
// APIEventController
package com.example.Get_In_Line.Controller.api;
import java.util.List;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/api")
@RestController
public class APIEventController {
@GetMapping("/events")
public List<String> getEvents(){
return List.of("event1", "event2");
}
@PostMapping("/events")
public Boolean createEvent(){
return true;
}
@GetMapping("/events/{eventId}")
public String getEvent(@PathVariable Integer eventId){
return "event " + eventId;
}
@PutMapping("/events/{eventId}")
public Boolean modifyEvent(@PathVariable Integer eventId){
return true;
}
@DeleteMapping("/events/{eventId}")
public Boolean removeEvent(@PathVariable Integer eventId){
return true;
}
}
// APIPlaceController
package com.example.Get_In_Line.Controller.api;
import java.util.List;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/api")
@RestController
public class APIPlaceController {
@GetMapping("/places")
public List<String> getPlaces(){
return List.of("place1", "place2");
}
@PostMapping("/places")
public Boolean createPlace(){
return true;
}
@GetMapping("/places/{placeId}")
public String getPlace(@PathVariable Integer placeId){
return "place " + placeId;
}
@PutMapping("/places/{placeId}")
public Boolean modifyPlace(@PathVariable Integer placeId){
return true;
}
@DeleteMapping("/places/{placeId}")
public Boolean removePlace(@PathVariable Integer placeId){
return true;
}
}
Postman Test
Error Report
Fix this pattern in your application or switch to the legacy
parser implementation with 'spring.mvc.pathmatch.
matching-strategy=ant_path_matcher'.
- 원인
SpringBoot 를 2.6 이상 버전으로 업그레이드 되어있을 시 요청 경로를
ControllerHandler 에 매칭시키기 위한 것이 작동된다.
조금 더 자세하게 설명해보면,
spring.mvc.pathmatch.matching-strategy 기본 값이 ant_path_matcher
에서 path_pattern_parser 로 변경이 된다.
- 해결
application.properties 파일에 가서
spring.mvc.pathmatch.matching- strategy=ant_path_matcher
을 입력해서 해결
'개발 > Spring' 카테고리의 다른 글
MVC 패턴 - API 설계(2) (0) | 2024.02.17 |
---|---|
함수형 프로그래밍 (0) | 2024.02.16 |
MVC 패턴(1) - 요구사항 설계 (0) | 2024.02.15 |
AOP(Aspect Oriented Programming) (0) | 2024.02.06 |
스프링 빈 이벤트 라이프 사이클 (0) | 2024.02.04 |