Open skarltjr opened 4 years ago
@GetMapping("/hello")
@ResponseBody
public ResponseEntity
@RequestParam / @ModelAttribute 간단 복기
// =========== RequestParam
@GetMapping("/events2")
public String eventsForm(Model model) {
Event event = new Event();
event.setLimit(50);
event.setName("기본");
model.addAttribute("event",event);
return "events/form";
}
@PostMapping("/events2")
@ResponseBody
public Event getEvent3(@RequestParam(value = "name") String name, @RequestParam(value = "limit") Integer limit) {
Event event = new Event();
event.setName(name);
event.setLimit(limit);
return event;
}
// ===== ModelAttribute 복합타입의 객체로 받는다 .유연함
@GetMapping("/events3")
public String eventsForm2(Model model) {
Event event = new Event();
event.setLimit(50);
event.setName("기본");
model.addAttribute("event",event);
return "events/form";
}
@PostMapping("/events3")
@ResponseBody // 검증은 valid랑 validator만들어서 하면되는거고
public Event getEvent4(@Valid @ModelAttribute EventForm eventForm, Errors errors) {
// if (errors.hasErrors())
// { ~~ } 에러있으면 처리해줘야하니까
Event event = new Event();
event.setLimit(eventForm.getLimit());
event.setName(eventForm.getName());
return event;
}
@SessionAttributes("event") // model.addAttribute("event",event); -> event의 해당하는 modelattribute를 세션에도저장 ~Controller
/** @SessionAttributes = 만약 장바구니나 여러페이지에 걸쳐서 폼을 채워야한다면
modelAttribute가 이런 세션정보도 매핑해준다*/
//여기서 이름만넣도록하고
@GetMapping("/events/form/name")
public String eventForm_Name(Model model) {
model.addAttribute("event", new Event()); // 여기 이 event말하는것 맨위
return "/events/form-name";
}
// 이름만받고 세선에도 event객체가 저장 modelAttribute는 세션에있는 정보도 바인딩해준다
@PostMapping("/events/form/name")
public String eventEventName(@Valid @ModelAttribute Event event, Errors errors) {
if (errors.hasErrors()) {
return "/events/form-name";
}
return "redirect:/events/form/limit";
}
//limit을 입력받는 폼으로 넘겨주고 넘겨줄 때 세션에서 event받은= 이름만 들어있는거 넘겨준다
@GetMapping("/events/form/limit")
public String eventForm_limit(@ModelAttribute Event event, Model model) {
model.addAttribute("event", event);
return "events/form-limit";
}
// 이름만받고 세선에도 event객체가 저장 modelAttribute는 세션에있는 정보도 바인딩해준다
@PostMapping("/events/form/limit")
public String eventEventLimit(@Valid @ModelAttribute Event event, Errors errors) {
if (errors.hasErrors()) {
return "events/form-limit";
}
return "redirect:/events/list";
}
@GetMapping("/events/list")
public String getEvents(@ModelAttribute Event event, Model model, SessionStatus sessionStatus) {
System.out.println(event.getName()+ " " + event.getLimit());
List<Event> eventList = new ArrayList<>();
eventList.add(event);
model.addAttribute("eventList", eventList);
sessionStatus.setComplete();// 다 채웠으면 세션을 비워준다
return "events/list";
}
정리
-- >> get에서 모델로 정보를 담을 폼을 전달하고 post에서 폼을 채우는것을 여러 단계에 걸쳐서 하도록 해준다.
-->> 이 때 여러단계에 걸쳐서 정보를 담을 폼. 혹은 객체를 컨트롤러에 어노테이션으로 @SessionAttributes("event") 지정해준것
@PostMapping("/tags/add")
@ResponseBody
public ResponseEntity addTag(@CurrentUser Account account, @PathVariable String path,
@RequestBody TagForm tagForm) {
Study study = studyService.getStudyToUpdateTag(account, path); //설정하고자 하는 스터디 가져오고
Tag tag = tagService.findOrCreateNew(tagForm.getTagTitle()); // 태그가 존재하면 그대로가져오고 아니면 저장하고
//태그 가져왔으면
studyService.addTag(study, tag);
return ResponseEntity.ok().build();
}
@ResponseBody ->본문으로 응답하겠다
@RequestBody -> 본문에 데이터요청해서 받아오고 이를 컨버터로 변환한 객체를 받아올 수 있다.
//ResponseBody -> 아래에서 보면 리턴타입인 Event를 httpMessageConverter를 사용해서 응답본문에 담아준다. 즉 매서드에서 리턴하는 값을 컨버터를통해 응답본문에 담아준다 // RequestBody -> 반대로 받아올 때 응답본문에서 (여기서는 event)객체타입으로 메세지컨버터로 변환해서가져온다
@RestController
@RequestMapping("/api/events")
public class EventApi {
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE,produces = MediaType.APPLICATION_JSON_VALUE)
public Event createEvent(@RequestBody @Valid Event event, Errors errors) {
if (errors.hasErrors()) {
return ResponseEntity.badRequest().build();
//~~~ 에러있으면 처리하고
}
return event;
}
}
===============
@Test
public void createEvent() throws Exception {
Event event = new Event();
event.setName("kiseok");
event.setLimit(23);
mockMvc.perform(post("/api/events")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(event))) //본문내용을 json으로
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("name").value("kiseok"))
.andExpect(jsonPath("limit").value("23"));
}
==============
만약 return을 Event가 아니라 ResponseEntity
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE,produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Event> createEvent2(@RequestBody @Valid Event event, Errors errors) {
if (errors.hasErrors()) {
return ResponseEntity.badRequest().build();
//~~~ 에러있으면 처리하고
}
// new ResponseEntity<Event>(헤더 status등 설정할 수 )
//이렇게 생성자로 만들어서 리턴하거나
return ResponseEntity.ok(event);
}
Model - View -Controller
MVC 패턴의 장점
● 동시 다발적(Simultaneous) 개발 - 백엔드 개발자와 프론트엔드 개발자가 독립적으로 개발을 진행할 수 있다. ● 높은 결합도 - 논리적으로 관련있는 기능을 하나의 컨트롤러로 묶거나, 특정 모델과 관련있는 뷰를 그룹화 할 수 있다. ● 낮은 의존도 - 뷰, 모델, 컨트롤러는 각각 독립적이다. ● 개발 용이성 - 책임이 구분되어 있어 코드 수정하는 것이 편하다. ● 한 모델에 대한 여러 형태의 뷰를 가질 수 있다.
MVC 패턴의 단점
● 코드 네비게이션 복잡함 ● 코드 일관성 유지에 노력이 필요함
서블릿 : 자바진영 웹 애플리케이션 개발스펙과 API을 제공하는 데 그 중 하나가 HttpServlet , ● 요청 당 쓰레드 (만들거나, 풀에서 가져다가) 사용 ● 그 중에 가장 중요한 클래스중 하나가 HttpServlet.
톰캣,제티,언더토 등이 바로 서블릿 컨테이너
서블릿의 생명주기는 init service(doget dopost) destroy
서블릿리스너와 필터 : 어떤 요청을 처리하기위해선 당연히 요청을 감지해야한다 서블릿리스너. 서블릿에게 요청을 보낼 때 걸러낼 것을 걸러내기위해 서블릿필터. 서블릿필터는 체인구조 -스프링부트에선 스프링컨테이너 안에 서블릿컨테이너(내장톰캣서버 등) : 이를통해 ioc컨테이너 연동
FrontControllerServlet : 매 요청마다 서블릿을 만들지 않고 하나의 서블릿으로 Controller에게 보내주기
스프링의 DispatcherServlet : 스프링이제공하는 FrontControllerServlet
DispatcherServlet의 동작 : 요청을받아 분석 -> 핸들러매핑으로 핸들러찾기 -> 핸들러어뎁터로 수행 -> 예외존재시 예외처리 ->응답리턴
DispatcherServlet의 구성 : MultipartResolver(파일 업로드 요청) / LocaleResolver(클라이언트 위치정보 파악) / ThemeResolver / HandlerMapping(요청처리를 위한 핸들러찾기) /HandlerAdapter(찾아낸 핸들러를 처리) / HandlerExceptionResolver /RequestToViewNameTranslator /ViewResolver / FlashMapManager
스프링부트에선 스프링컨테이너 안에 내장 톰캣(서블릿컨테이너) 그 안에DispatcherServlet / AutoConfiguration
이로인해 컨버터,핸들러매핑 등 빈으로 등록하여 사용하는 것이 가능한것
스프링부트에서 MVC설정을 위해선 : @Configuration + Impl WebMvcConfiguerer / 빈으로 등록하여 사용하기
@EnableWebMvc는 스프링부트가 기본제공하는 설정 다 무시 주의
가령 Formatter를 설정하고자 할 때 빈으로 등록하여 사용할 수 있다.
스프링데이터JPA를 사용하면 도메인클래스 컨버터 제공
HandlerInterCeptor : 말 그대로 핸들러에 대한 인터셉터/ 인터셉터를 빈으로 등록하거나 만들어서(HandlerInterCeptor 상속받아구현) / pre = 핸들러실행하기전 서블릿필터보다 자세한 로직 post 핸들러 실행 뷰 렌더링 전 . 모델에 대한 정보가 있음 인지, after = 뷰 렌더링이후
-WebConfig로 WebMvcConfiguerer 구현해서 addInterCeptor -리소스핸들러 : 정적리소스처리를 위한 핸들러 / 디폴트서블릿 서블릿컨테이너가 기본으로 제공하는 서블릿. 정적리소스처리
정적리소스를 처리하기때문에 가장 낮은 우선순위 / 스프링부트 기본제공
메세지컨버터 : 요청본문에서 메시지를 읽거나 작성할 때 변환기능 기본제공기능이나 의존성추가로 사용 -스프링부트는 json기본제공
요청매핑에서 새로알게된 것 @RequestMapping(consumes=MediaType.APPLICATION_JSON_VALUE) // 제이슨으로만 본문데이터 받겠다 -그럼 나는 요청을 보낼 때 content-type에 json으로 보내주고 @RequestMapping(produces=MediaType.APPLICATION_JSON_VALUE) // 제이슨으로 리턴해주겠다 -나는 받을 때 accept로 json 테스트코드에선 .contentType과 accept로 확인해봤다
커스텀 어노테이션 흔히 사용하는 메타 어노테이션. 직관적으로 스스로 구성하여 편하게 사용가능 @Retention : 어노테이션 정보 유지 기간 Runtime 자주사용 @Target : 말그대로 어디에 적용할것인가 파라미터? 매서드? 등. @Documented