본문으로 바로가기

[Spring] 스프링 부트 개념과 활용 - 2

category Spring 2021. 7. 10. 22:27

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8/

 

스프링 부트 개념과 활용 - 인프런 | 강의

스프링 부트의 원리 및 여러 기능을 코딩을 통해 쉽게 이해하고 보다 적극적으로 사용할 수 있는 방법을 학습합니다., 스프링 부트 개념과 활용 스프링부트 (https://spring.io/projects/spring-boot) 프로

www.inflearn.com

 

 

ㅁ 내장 웹 서버 응용 1부 : 컨테이너와 포트

- pom.xml 에서 tomcat을 exclusion으로 빼고, jetty 넣기

- application.properties 에 spring.main.web-application-type=none 입력하면 자동으로 프로그램으로 실행
 > server.port=7070 도 마찬가지 ( 0으로하면 random )

- application.properties 에 server.ssl 로 써서 지정

 ex)

server.ssl.key-store= keystore.p12
server.ssl.key-store-type=PKCS12
server.ssl.key-store-password=123456
server.ssl.key-alias=spring

- 커넥터를 생성하여 8080 과 8443 동시에 연결 받기

    @Bean
    public ServletWebServerFactory serverFactory() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        tomcat.addAdditionalTomcatConnectors(createStandardConnector());
        return tomcat;
    }

    private Connector createStandardConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setPort(8080);
        return connector;
    }

 

- http2 적용의 경우, tomcat은 8.5.x는 적용 어려움

- properties 통해 tomcat 버전 변경 가능

 

ㅁ 독립적으로 실행 가능한 JAR

- mvn package -DskipTests

 (테스트 생략하고 jar로 빌드)

 > 이후 jar파일 실행하면 바로 웹서비스가 진행됨

- 수많은 의존성들은 어떻게 되었을까?

  > 압축 풀면 모두 포함되어있음

 > JarLauncher 등을 사용하여 jar 파일을 읽음

- 스프링 부트는 standalone 어플리케이션을 만들수있다. ( 큰 특징 )

 

 

CH4. 스프링 부트 활용

ㅁ Spring Application

- vmOption : Ddebug

  기본적으로 info인데, debug 로 변경하면 자동으로 세팅되는것까지 다 나옴

- resource/banner.txt

   > 여러 옵션이 있다. 

  > SpringApplication 에서 setBanner , setBannerMode 등 가능

    - 다만 txt파일이 setBanner를 이긴다.

- new SpringApplicationBuilder().soruce(xxx.class).run(args) 등으로도 어플리케이션 실행 가능

- ApplicationListener<ApplicationStartingEvent> 를 implements 하여 클래스를 만들고, bean으로 등록했으나

  구동 시 이벤트가 발생하지 않는다

 > applicationContext가 만들어 진 후, 발생한 이벤트는 위 bean이 인식할 수 있으나, 그렇지 못했다.

 > 수동으로 등록해줘야한다

- webapplicationType 은 servlet -> webflux -> none 순으로 있는게 기동됨

- vm option, argument

  -> vm option은 argument가 아님 ( --Dxx 로 입력 )

- ApplicationRunner(추천), CommandLineRunner  : 애플리케이션 실행 뒤 뭔가 실행하고 싶을때

 

ㅁ 외부 설정

- application.properties 는 컨벤션 (규약 ) 임

  > 자동으로 여기 있는 속성을 읽음

  > 이것의 우선순위는 15위

- Application.properties 위치 우선 순위

 1. file:/config/

 2. file:./ 

 3. classpath:/config/

 4. classpath:/

- 프로퍼티 타입 컨버전은 융통성있게 변경됨

- Duration (초) 로 컨버전도 가능

  >25s 등과 같이 입력도 가능

- @NotEmpty,

- @ConfigurationProperties(" xxx" ) 는 

 application.properties에 있는 xxx의 속성들을 가져와서, 변수에 컨버전함  (@Value를 안써도됨) 

- @Value

 > SpEL을 사용할 수 있지만, @ConfigurationProperties로 땡겨오는건 SpEl 못씀

 

ㅁ 프로파일

- application.properties 에 spring.profiles.active 에 설정 (prod, test)

- 패키징 하여 java 실행시 argument로 주는 방법도 있음

- application-prod or test .properties가 application.properties 보다 우선순위가 높음

ㅁ 로깅

- 로깅 퍼사드는 로거 (JUL, Log4J2, Logback ) 를 원하는걸 쓸 수 있도록 바꿔줌

 > Commons Logging, SLF4j

- Commons Logging 은 클래스로더 에서 메모리 leak등의 이슈로 안쓰는 움직임이 있었음

- 스프링 부트는 Commons Logging 을 씀

 > 그러나 결과적으로 SLF4j 로깅퍼사드를 쓰게되고, SLF4j는 LogBack을 로거로 써서 로깅하게됨

 > logback 은 slf4j의 구현체

 > jul, lo4j2 => slf4j 로 전달하게 됨

- debug, trace, logging.path 등 여러 옵션이 있음

- logback-spring.xml 을 만들어서 설정파일로 관리도 가능

  ( 특정 패키지만 어느방식으로 기록 등)

 

ㅁ 테스트

- spring-boot-starter-test dependency 추가

- 아래 작성

package me.defian.springtestdemo.sample;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.test.context.junit4.SpringRunner;

import static org.assertj.core.api.Assertions.assertThat;

import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;


@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//@AutoConfigureMockMvc
public class SampleControllerTest {

//    @Autowired
//    MockMvc mockMvc;
    @Autowired
    WebTestClient webTestClient;

    @Autowired
    TestRestTemplate testRestTemplate;

    @MockBean
    SampleService mockSampleService;

    @Rule
    public OutputCapture outputCapture = new OutputCapture();

    @Test
    public void hello() throws Exception {
//        mockMvc.perform(get("/hello"))
//                .andExpect(status().isOk())
//                .andExpect( content().string("Hello HyungChul"))
//                .andDo(print());
        when(mockSampleService.getName()).thenReturn("defian");
        webTestClient.get().uri("/hello").exchange().expectStatus().isOk()
                .expectBody(String.class).isEqualTo("Hello defian");

//        String result = testRestTemplate.getForObject("/hello",String.class);
//        assertThat(result).isEqualTo("Hello defian");

        assertThat(outputCapture.toString()).contains("Holoman").contains("skip");
    }
}

- mocking이란 모방하는건데, 특정 서비스까지 가서 풀로직을 돌고싶지않을때, 모킹하여 리턴값을 간단히 설정하여 테스트한다

- 랜덤포트 쓰는걸 추천

- spring 5 부터 생긴 건데, WebTestClient는 Async하게 동작하여 테스트 체크함

 > pom.xml 에 추가하여 사용

- json : jacksonTester 등 여러 테스터가 존재한다 (@JsonTest)

- @WebMvcTest(SampleController.class) : 특정 mvc 빈만 테스트

- OutputCapture 를 통해 특정 로그 (logger , sysout 등) 이 찍히는지도 확인할 수 있다.

 

ㅁ Spring-Boot-Devtools

- 의존성 설치 필요

- springboot 실행 시  base classloader , restart classloader  두개를 실행함 

- 빌드만 하면 자동으로 코드 변경 및 브라우저도 리로딩함 ( 플러그인 설치 시 )

 

 

ㅁ 스프링 Thymeleaf

- 뷰를 만들때 쓰는 템플릿 엔진

 > FreeMarker , Groovy, Thymeleaf, Mustache

 - JSP 는 권장하지 않음 

  > jar 대신 war로 패키징 해야함 (물론 war로 패킹하여 java로 실행가능하긴 하나 권장하지 않음)

  > undertow는 jsp를 지원하지 않음

- 실제 서블릿 컨테이너 개입 없이 rendering된 화면을 볼 수 있다.

  >  mockmvc는 실제 서블릿 컨테이너가 아니고, mocking하는것임

  >  thymeleaf로 뷰의 결과까지 확인 할 수 있음

- thymeleaf 사용 

 > html 에 <html lang="en" xmlns:th="http://www.thymeleaf.org"> 추가

 > <h1 th:text="${name}">Name</h1> 으로 호출

 

ㅁ 스프링 HtmlUnit

- Html을 단위테스트 하기위한 툴

- xpath 통해서도 테스트 가능

- 어떤 단어가 포함되었는지 테스트 진행함 

 

ㅁ ExceptionHandler

- 특정 컨트롤러 안에서의 ExceptionHandler

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class SampleController {

    @GetMapping("/hello")
    public String hello(){
        throw new SampleExeption();
    }

    @ExceptionHandler(SampleExeption.class)
    public @ResponseBody AppError sampleError(SampleExeption e ){
        AppError appError = new AppError();
        appError.setMessage("error.app.key");
        appError.setReason("IDK IDK IDK");
        return appError;
    }
}

 

- @ControllerAdvice 통해서 전체 컨트롤러에서 설정 가능

- BasicErrorController.java 참고하여 커스터마이징하여 구성

- 기본으로 나오는 에러 페이지는 가장 마지막에 나옴 (BasicErrorController가 구성)

- resources/static/error 폴더에 번호.html 만들면 그게 나옴

 

ㅁ Hypermedia As The Engine Of Application State

- 연관된 링크 정보 (Relation , Hypertext Reference) 를 전달해주면 이걸 바탕으로 접근함

- ObjectMapper 와 LinkDiscovers 제공

  > Jackson 에서 제공해주는 클래스

- ObjectMapper는 자동으로 bean으로 등록이 되어있다.

 > application.properties에 spring.jackson.옵션으로 커스터마이징 가능

 

ㅁ CORS

- Origin ? URI 스키마 (http, https) / hostname / 포트

- Single-Origin Policy

- @CrossOrigin(origins="http://localhost:18080") 컨트롤러 안에 매핑에 태그 작성

- 위 어노테이션 설정이 힘들면 글로벌하게 아래 설정 작성

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:18080");
    }
}