[TIL] 20210901
1.Spring MVC - Form
- form 담긴 html 파일
<-- 1. POST로 해당 url에 요청 -->
<form th:action="@{/customers/new}" method="post">
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Email address</label>
<-- 2. input name을 설정해줄 것. -->
<input type="email" name="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Name</label>
<input type="text" name="name" class="form-control" id="exampleInputPassword1">
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="exampleCheck1">
<label class="form-check-label" for="exampleCheck1">Check me out</label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
- Controller에 POST 핸들러 작성해주기
@PostMapping("/customers/new")
public String addNewCustomer(CreateCustomerRequest createCustomerRequest){
customerService.createCustomer(createCustomerRequest.email(), createCustomerRequest.name());
return "redirect:/customers";
}
CreateCustomerRequest에서는 1번의 폼에서 전달해준 input의 name에 해당하는 요소를 받아 처리한다.
public record CreateCustomerRequest(String email, String name) {
}
- 파라미터를 url로 전달했을 때
- @PathVariable("custoemrId") : path에 있는 변수 찾아 매핑해줌.
@GetMapping("/customers/{customerId}")
public String findCustomer(@PathVariable("custoemrId") UUID customerId, Model model){
var maybeCustomer = customerService.getCustomer(customerId);
if(maybeCustomer.isPresent()){
model.addAttribute("customer",maybeCustomer.get());
return "views/customer-details";
}else{
return "views/404";
}
}
- 정리
- 컨트롤러에서 DTO(CreateCustomerRequest 같은)를 통해 데이터를 받아온다.- validation,http 확인 등의 작업을 수행한다.
- 그 후 받아온 데이터를 실제 로직은 서비스와 entity에서 하도록 넘겨준다.
2. WebApplicationContext
-
여러 서블릿이 공유가 가능한 내용을 Servlet Context에 저장
모든 서블릿들이 접근 가능한 루트 서블릿이 서블릿
WebApplicationContext: ApplicationContext를 상속받음. 거기에 Servlet Context접근하는 기능이 추가 되어 있음.Servlet Context: 여러 서블릿이 공유가 가능한 정보를 담고 있음.
-
여러 dispatcher servlet에서도 접근이 가능함.
-
여러 dispatcher servlet이 만들어지면 여러 WebApplicationContext이 만들어짐.
-
각 Context는 어떤 관계를 가지는가? 모든 context에 접근 가능한 Bean이 없을까?
-> 모든 ApplicationContext들이 접근 가능한 root ApplicationContext이 필요함.root ApplicationContext
-
Servlet Context가 만들어질때 root ApplicationContext가 생김.
-
Servlet Context에 attribute로 들어가있음.
-
그 후 생겨난 servlet들은 Servlet Context에 접근해 root ApplicationContext를 사용하게됨.
- 로더, 리스너 내용
- 서블릿 어플리케이션 구조부분
- 실습
-
처음에 Dispatcher Servelet의 WebApplicationContext의 parent는 없다.
-
모든 빈들이 WebApplicationContext 컨테이너에 다 등록되어 있다.
-> root ApplicationContext을 만들고, 거기에 서비스랑 데이터 access 관련된 빈들을 등록하고 관리할것임.
-> dispatcher servelet 의 WebApplicationContext에는 mvc 관련된 빈만 등록하도록 하고 루트랑 부모 자식으로 연결되도록.
@EnableWebMvc
@Configuration
//컨트롤러에 해당하는 빈만 스캔하겠다.
@ComponentScan(basePackages = "org.prgrms.kdt.customer",
includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = CustomerController.class),
useDefaultFilters = false
)
static class AppConfig implements WebMvcConfigurer, ApplicationContextAware {
//스프링의 app context
ApplicationContext applicationContext;
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp().viewNames("jsp/*");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
//컨트롤러 제외하고 다른 빈들(서비스, 데이터관련)만 스캔하겠다.
@Configuration
@ComponentScan(basePackages = "org.prgrms.kdt.customer",
excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = CustomerController.class)
)
@EnableTransactionManagement
static class RootConfig{
@Bean
public DataSource dataSource(){
return DataSourceBuilder.create().build();
}
//그 외 트랜잭션 및 jdbcTemplate 관련 Bean 설정
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//1.RootConfig로 root컨텍스트 만들어주기
var rootApplicationContext = new AnnotationConfigWebApplicationContext();
rootApplicationContext.register(RootConfig.class);
//2. 로드 리스너 설정해주기 -> 부모 자식 연결해줌
var loaderListener =new ContextLoaderListener(rootApplicationContext);
servletContext.addListener(loaderListener);
//3. DispatcherServlet은 AppConfig로 만들어주기
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(AppConfig.class);
var dispatcherServlet= new DispatcherServlet(applicationContext);
logger.info("Starting Server...");
ServletRegistration.Dynamic servletRegistration = servletContext.addServlet("test", dispatcherServlet);
servletRegistration.addMapping("/");
servletRegistration.setLoadOnStartup(1);
}
- setLoadOnStartup
CRUD 명 | 의미 |
---|---|
0 이상 | ServletContext 초기화 될때 ServletContextListener 호출해서 하위 인스턴스 다 생성 |
-1 | ServletContext 초기화, 이후에 사용될때 각 서블릿 인스턴스 생성하고 초기화 진행 |
디폴트 값이 -1로 정의되어 있음.
3. REST API
-
REST(Representational Transfer): 웹 상의 자료를 HTTP위에서 SOAP이나 쿠키를 통한 세션 트랙킹 같은
별도의 전송 계층 없이 전송하기 위한 아주 간단한 인터페이스 -
API(application programming interface) :
REST API : REST 아키텍쳐 스타일을 따르는 API
REST 아키텍쳐 스타일
균일한 인터페이스 : URI로 지정한 리소스에 대한 조작을 통일되고 한정적인 인터페이스로 수행하는 아키텍처 스타일
SOAP : 단 하나의 url에 요청을 xml에 기술하고 전달
LV 1: 하나의 리소스에 대해 다양한 형태로 표현이 가능하다
LV 2: http 메서드(get,post,put,delete)를 도입하는 것
LV 3: HATEOAS(Hypermedia as the Engine of Application State)
- 리소스와 함께 link에 해당 리소스로 할 수 있는 행위들을 제공
API 설계
- URI는 자원을 표현, 명사를 사용
- 자원을 가지고 할 행위는 http 메서드를 사용
- 슬래시는 계층관계를 나타냄
- URI 마지막에 슬래시를 포함하지 않기
- 하이픈으로 URI 가독성을 높이기
실습
스프링에서 레스트api 개발을 위해 annotation 제공
1. RequestBody 전달받은 요청 메시지를 원하는 형태로 변환.
2. ResponseBody 우리가 결과낸 모델클래스가 response로 변환.
3. RestController Controller + ResponseBody
RestController를 적용하면 하위 모든 메서드에 respnse Body가 생긴다.
Dispatch Servlet의 요청을 받아
핸들러매핑으로 핸들러 찾고, 핸들러 어댑터가 argument Resolver를 사용해서 적절히 변형해 핸들러에 전달
@RequestBody,@ResponseBody, HttpEntity를 리턴할경우, HTTP 메시지 컨버터가 동작해 결과 모델을 HTTP 메시지로 변환한다.
- 설정하지 않으면 디폴트 메시지 컨버터가 작동해 JSON으로 변환해 출력
- initializer에서 디폴트 메시지 컨버터를 아예 변경
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//디폴트 메시지 컨버터가 싹 바뀐다.
var messageConverter = new MarshallingHttpMessageConverter();
var xStreamMarshaller = new XStreamMarshaller();
messageConverter.setMarshaller(xStreamMarshaller);
messageConverter.setUnmarshaller(xStreamMarshaller);
converters.add(messageConverter);
}
- initializer에서 디폴트 메시지 컨버터를 경우에 따라 다르도록 확장
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//디폴트 메시지 컨버터에서 확장하는 방식으로 사용가능
//1.xml 방식으로 컨버팅하도록 추가
var messageConverter = new MarshallingHttpMessageConverter();
var xStreamMarshaller = new XStreamMarshaller();
messageConverter.setMarshaller(xStreamMarshaller);
messageConverter.setUnmarshaller(xStreamMarshaller);
converters.add(0, messageConverter);
//2. 날짜의 경우 변환해주는 다른 메시지 컨버터를 추가
var javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ISO_DATE_TIME));
var modules = Jackson2ObjectMapperBuilder.json().modules(javaTimeModule);
converters.add(1, new MappingJackson2HttpMessageConverter(modules.build()));
}
Author And Source
이 문제에 관하여([TIL] 20210901), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@hoit_98/TIL-20210901저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)