[TIL] 20210901

열심히 사는 루피 🥰·2021년 9월 2일
0

데브코스 백엔드 TIL

목록 보기
19/20

1.Spring MVC - Form

  1. 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>
  1. 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

  1. 여러 서블릿이 공유가 가능한 내용을 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를 사용하게됨.

  • 로더, 리스너 내용
  • 서블릿 어플리케이션 구조부분
  1. 실습
  • 처음에 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. DispatcherServletAppConfig로 만들어주기
        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);
    }
  1. setLoadOnStartup
CRUD 명의미
0 이상ServletContext 초기화 될때
ServletContextListener 호출해서 하위 인스턴스 다 생성
-1ServletContext 초기화,
이후에 사용될때 각 서블릿 인스턴스 생성하고 초기화 진행

디폴트 값이 -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 메시지로 변환한다.

  1. 설정하지 않으면 디폴트 메시지 컨버터가 작동해 JSON으로 변환해 출력
  2. initializer에서 디폴트 메시지 컨버터를 아예 변경
 public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            //디폴트 메시지 컨버터가 싹 바뀐다.
            var messageConverter = new MarshallingHttpMessageConverter();
            var xStreamMarshaller = new XStreamMarshaller();
            messageConverter.setMarshaller(xStreamMarshaller);
            messageConverter.setUnmarshaller(xStreamMarshaller);
            converters.add(messageConverter);
        }
  1. 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()));

        }
profile
반가워_! 세상아!

0개의 댓글