ch04. 스프링부트_시작하기
스프링 부트 시작하기
- 왜 스프링 부트인가?
- 스프링 부트가 제공하는 기능은 무엇인가?
- 자동 설정인가?
- 스프링 부트란?
- 스프링 부트를 사용할 때 백그라운드에서는 어떤 일이 발생할까?
- 스프링 이니셜라이저를 사용해 새로운 스프링 부트 프로젝트를 어떻게 만들까?
- 스프링 부트로 어떻게 애플이케이션을 모니터링할까?
- 스프링 부트 액추에이터는 무엇일까?
- 스프링 부트로 어떻게 생산성을 높일 수 있을까?
스프링 부트를 통해 개발자는 마이크로서비스의 비즈니스 논리에 집중할 수 있다.
스프링 부트의 목표는 마이크로서비스 개발에 관련된 모든 기술적 세부사항을 관리하는 것이기 때문이다.
스프링 부트를 사용하면 스프링 기반 프로젝트를 빠르게 구축할 수 있다.
코드 생성과 많은 XML 설정의 사용을 피해라. 다양한 비기능적 특징이 있다.
- 비기능이란
- 서버와 스펙의 버전 관리 및 설정에 관한 기본 처리
- 애플리케이션 시큐리티를 위한 기본 옵션
- 애플리케이션 모니터링
- 외부 설정의 여러 옵션
- 스타터 프로젝트
- 스타터는 여러 목적으로 사용자 정의가 단순화된 의존 관계 설명자다.
- spring-boot-starter-web은 스프링 및 스프링 MVC 프레임워크를 사용해 Restful API를 포함한 웹 애플리케이션을 구축하는데 사용된다.
- spring-boot-starter-test 의존관계는 단위 테스트에 필요한 테스트 프레임워크를 제공한다.
- spring-boot-starter-tomcat은 톰캣을 임베디드 서블릿 컨테이너로 사용하기 위한 스타터다. 웹 애플리케이션을 실행하기 위해 기본적으로 포함돼 있다.
스프링 부트 애플리케이션을 시작하는 과정
- pom.xml 파일에 spring-boot-starter-parnet 구성
- 요구되는 스타터 프로젝트로 pom.xml 파일 구성
- 애플리케이션을 실행할 수 있도록 spring-boot-maven-plugin 구성
- 첫번째 스프링 부트 launch 클래스 생성
- Spring Boot Starter Parent
- 프로젝트 시작 시기에 다양한 라이브러리들을 사용하게되면 라이브러리 버전간의 충돌문제가 발생할 수 있다.
- Spring Boot의 starter가 의존성 조합을 제공해준다면 starter-parent는 해당 의존성 조합간의 충돌 문제가 없는 검증 된 버전정보 조합을 제공한다.
- spring-boot-starter-parent 버전만 설정해도 수많은 라이브러리들의 버전충돌 문제를 피할 수 있다.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
</parent>
<dependencies>
...(생략)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
특정 의존성이 버전을 변경하고자 한다면 아래와 같이 properties를 이용해서 해당 의존성 버전을 override하면 된다.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
</parent>
<properties>
<aspectj.version>1.9.4</aspectj.version>
</properties>
<dependencies>
...(생략)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
자동 설정
ApplicationContext ctx = SpringApplication.run(Application.class, args);
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for(String beanName : beanNames) {
System.out.println(beanName);
}
출력결과
application
basicErrorController
characterEncodingFilter
conventionErrorViewResolver
mvcViewResolver
...
spring-boot-starter-web에 의존관계를 추가하면 빈이 자동 설정된다.
spring boot autoconfiguration 정리
- ConditionalOnClass : ({Example.class, ...}) : 언급된 클래스 중 하나라도 클래스 패스에 있으면 자동 설정이 사용된다. 웹 스타터 프로젝트를 추가할 때 언급된 모든 클래스의 의존 관계를 가져온다.
- 스프링4에서 도입된 어노테이션으로 조건부로 Bean을 스프링컨테이너에 등록하는 역할을 합니다.
- @ConditionalOnClass : 특정 Class 파일이 존재하면 Bean 을 등록.
- @ConditionalOnBean : 특정 Bean 이 존재하면 Bean 을 등록.
- @ConditionalOnClass 는 해당 Class 가 있는지를 검사하는 조건 어노테이션 입니다.
- ConditionalOnClass를 사용하는 이유
- 이 어노테이션이 작성되는 클래스는 외부 라이브러리로 제공되는 프로젝트 이다.
- 이 어노테이션이 작성되는 클래스는 의존성에 대한 제어권을 갖지 않습니다.
- ConditionalOnMissingBean(WebMvcConfigurationSupport.class) : Bean이 존재 하지 않을 때 실행되는 어노테이션이다. bean Name이나 class, annotaion으로 설정할 수 있다.
@ConditionalOnMissingBean(name = "helloConfigSample") //helloConfigSample 존재 하지 않을 때 실행해라.
- ConditionalOnBean(ViewResolver.class) : ViewResolver.class가 클래스 패스에 있으면 빈이 생성한다.
@Configuration
public class SampleAAutoConfiguration {
@Bean
public SomeAClass SomeAClass() {
return new SomeAClass();
}
}
@Configuration
public class SampleBAutoConfiguration {
@Bean
@ConditionalOnBean(SomeAClass.class)
public SomeBClass someBClass() {
return new SomeBClass();
}
}
위 어플리케이션을 실행해보면 생성이 안되는 경우가 발생한다.
SpringBootAutoConfiguration bean이 생성될 때 의존성을 확인하고 bean 생성 순서를 재정렬하지만
@ConditionalOnBean annotation 조건에 대해서는 의존성을 확인하지 않아 재정렬이 되지 않어서이다.
따라서 위의 설정은 각 AutoConfiguration 간 순서를 지정해주어야 올바르게 동작한다.
@Configuration
public class SampleAAutoConfiguration {
@Bean
public SomeAClass SomeAClass() {
return new SomeAClass();
}
}
@Configuration
@AutoConfigureAfter(SampleAAutoConfiguration.class)
public class SampleBAutoConfiguration {
@Bean
@ConditionalOnBean(SomeAClass.class)
public SomeBClass someBClass() {
return new SomeBClass();
}
}
- @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) : 특정 자동 설정의 우선순위를 지정한다.
- ConditionalOnProperty : prefix 속성의 해당하는 프로퍼티 값이 있다면 실행된다.
@ConditionalOnProperty(prefix = "autoconfig.sample", name = "id" )
- EnableConfigurationProperties : 위 ConditionalOnProperty 같이 사용하는 어노테이션이다. 프로퍼티가 있다면 id값을 저장 해놓기 위한 class가 필요한데 그 용도로 사용한다. EnableConfigurationProperties 만 있어도 사용은 가능하지만 프로퍼티의 autoconfig.sample.id 설정하지 않았다면 null 로 나온다. 만약 사용한다면 위의 어노테이션이랑 사용하길 권장된다.
- ConditionalOnWebApplication : web인지 아닌지 판단하는 어노테이션이다. web일 경우에 실행된다.
- ConditionalOnNotWebApplication : web이 아닐경우 실행된다.
- ConditionalOnJava : 자바 버전을 설정하는 어노테이션이다. 현재 버전이 설정한 버전보다 낮으면 실행된다. ex) 현재 1.8버전이고 설정한 버전이 1.8보다 작거나 같다면 실행되고 높으면 실행되지 않는다.
@ConditionalOnJava(value = ConditionalOnJava.JavaVersion.SIX)
- ConditionalOnResource : 리소스 경로에 파일이 있으면 실행되는 어노테이션이다. 특정 경러롤 지정해 주면 된다.
@ConditionalOnResource(resources = "classpath:/META-INF/resourcesfile")
- ConditionalOnExpression : 표현식으로 나타낼 수 있는 어노테이션이다.
@ConditionalOnExpression(value = "'${spring.application.name}' == 'spring-autoconfig'")
모든 자동 설정 로직은 스프링부트 애플리케이션이 시작할 때 실행된다.
특정 의존 관계나 스타터 프로젝트의 특정 클래스를 class path에서 사용할 수 있는 경우, 자동 설정 클래스가 실행된다.
자동 설정 클래스는 이미 빈이 구성돼 있는지 확인한다.
애플리케이션 구성 외부화
애플리케이션은 일반적으로 JAR나 WAR로 한 번 빌드된 후 여러 환경에 배포된다.
서로 다른 환경 간에 변경되는 설정들은 구성 파일 또는 데이터베이스로 외부화하는 것이 좋다.
- application.properties 스프링 부트에서 구성 값이 선택되는 기본 파일이다. 스프링 부트는 클래스 패스의 어디서나 application.properties 파일을 선택할 수 있다.
- appliaction.properties에서 정의할 수 있는 설정
- 로깅 구성
- 임베디드 서버 구성 사용자 정의
- 스프링 mvc 설정
- 스프링 스타터 시큐리티 구성
- 데이터 소스, JDBC와 JPA 사용자 정의
- 프로파일
- HTTP 메시지 변환기
- 트랜잭션 관리
- 국제화 등
데이터 서비스 예제
somdataservice.url = http://abc.service.com/something
@Component
public class SomeDataService {
@Value("${somedataservice.url}")
private String url;
public String retrieveSomeData() {
//URL을 사용하고 데이터를 얻는 로직
return "data from service";
}
}
@Value("${somedataservice.url}") : somedataservice.url의 값이 url변수에 자동으로 연결된다. url 값은 빈 메소드에서 사용할 수 있다.
@Value 어노테이션은 동적 구성을 제공하지만 몇 가지 단점이 있다.
- 한 서비스에서 3개의 속성 값을 사용하려면 @Value를 사용해 세 가지 속성 값을 오토와이어링 해야 한다.
- @Value 어노테이션과 메시지 키는 애플리케이션에 분산된다. 애플리케이션에서 구성 가능한 값 리스트를 찾으려면 애플리케이션에서 @Value 어노테이션을 검색해야 한다.
ConfigurationProperties
- 사전 정의된 빈 구조에 모든 특성을 보유한다.
- 빈은 모든 애플리케이션 속성의 중앙 저장소 역할을 한다.
- 구성 빈은 애플리케이션 구성이 필요한 모든 곳에 자동 연결될 수 있다.
my.name = yuni
my.age = 25
my.fullname = ${my.name} Yang
먼저 @ConfigurationProperties 어노테이션을 붙여 클래스를 만들어 준다. key값이 my로 시작했기때문에 값은 my로 준다.
@ConfigurationProperties("my")
public class MyProperties {
}
@ConfigurationProperties 어노테이션을 붙여주면, 메타데이터를 생성해 자동완성 기능을 가능하게 해주는 의존성을 추가하라고 뜬다.
org.springframework.boot spring-boot-configuration-processor true
@Component
@ConfigurationProperties
public class MyProperties {
private String name;
private int age;
private String fullName;
public String getName() {
return name;
}
public void setName() {
this.name = name;
}
public int getAge() {
return age;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
바인딩은 자바 빈 속성 설명자를 통해 발생하므로 getter와 setter가 필요하다. class를 활성화 하려면 main에 @EnableConfigurationProperties 어노테이션을 사용하여 사용할 프로퍼티 클래스를 값으로 줘서 사용해야한다.
그렇게 하면, 만들어준 프로퍼티 클래스도 Bean으로 등록해주고 @ConfigurationProperties 어노테이션도 처리해준다.
@SpringBootApplication
@EnableConfigurationProperties(MyProperties.class)
public class YuniApplication {
public static void main(String[] args) {
SpringApplication.run(YuniApplication.class, args);
}
}
@ConfigurationProperties
@Validated
public class MyProperties {
@NotEmpty
private String name;
private int age;
private String fullName;
}
@Value 어노테이션으로 프로퍼티 값을 쓰는 것보다, 이런식으로 prefix값으로 프로퍼티를 구분하여, 클래스를 만들어 @ConfigurationProperties 어노테이션을 사용하여 프로퍼티 값을 사용하는 것이 매핑도 유연하게 할 수 있다는 장점이 있다.
기본적으로 외부에서 구성된 값을 구성 등록 정보 빈에 바인딩할 때 에러가 발생하면 서버가 시작되지 않는다.
따라서 프로덕션 환경에서 실행중인 애플리케이션이 잘못 구성 돼 발생하는 문제를 방지할 수 있다.
다른 환경에 profile 설정하기
@Profile("!prd")
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory devSqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new RefreshableSqlSessionFactoryBean();
factoryBean.setDataSource(oracleDataSource());
factoryBean.setVfs(SpringBootVFS.class);
factoryBean.setConfigLocation(applicationContext.getResource("classpath:mybatis/mybatis-config.xml"));
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resource = resolver.getResources("classpath*:mybatis/oracle/**/*.xml");
factoryBean.setMapperLocations(resource);
((RefreshableSqlSessionFactoryBean) factoryBean).setInterval(1000);
return factoryBean.getObject();
}
@Profile("prd")
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(oracleDataSource());
factoryBean.setVfs(SpringBootVFS.class);
factoryBean.setConfigLocation(applicationContext.getResource("classpath:mybatis/mybatis-config.xml"));
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resource = resolver.getResources("classpath*:mybatis/oracle/**/*.xml");
factoryBean.setMapperLocations(resource);
return factoryBean.getObject();
}
method와 class에도 Profile을 정의할 수 있다.
YAML 구성
YAML은 YAML Aint Markup Language의 줄임말이다.
spring:
profiles:
active: prod
security:
basic:
enabled: false
user:
name=user-name
password=user-password
oauth2:
client:
clientId: clientId
clientSecret : clientSecret
authorized-grant-types: authorization_code,refresh_token,password
scope:openid
application:
enableSwitchForService1: true
service1Url: http://abc-dev.service.com/somethingelse
service1Timeout: 250
YAML은 단일 구성 파일에서 여러 프로필의 구성을 저장할 수 있다는 장점이 있다.
YAML 파일은 @PropertySource 어노테이션을 사용해서 로드될 수 없습니다. 때문에 이 방법으로 값을 로드해야 하는 경우 프로퍼티 파일을 사용해야 합니다.
임베디드 서버
전통적인 자바 애플리케이션은 자바 웹 애플리케이션을 사용해 WAR나 EAR를 빌드해 서버에 배포한다.
서버에 WAR를 배포하려면 서버에 웹 서버나 애플리케이션 서버가 설치돼 있어야 한다.
애플리케이션 서버는 서버에 설치된 자바 인스턴스의 맨 위에 있다.
애플리케이션을 배포하려면 자바 및 애플리케이션이 시스템에 설치돼 있어야 한다.
스프링 부트는 웹 서버가 애플리케이션 배포 가능한 JAR의 일부인 임베디드 서버 개념을 도입한다.
- 임베디드 서버를 사용해 애플리케이션을 배포하려면 자바가 서버에 설치돼 있어야 한다.
- 스프링 부트로 모든 애플리케이션을 빌드할 때의 기본값은 JAR를 빌드하는 것이다.
- spring-boot-starter-web에서 기본 임베디드 서버는 톰캣이다.
- mvn clean을 사용해 JAR를 빌드
JAR를 사용하는 대신 기존 WAR 파일 빌드 pom.xml의 패키지를 war로 변경하면 된다.
war로 웹 스피어(webSphere)나 웹 로직(webLogic)과 같은 애플리케이션 서버나 톰캣과 같은 웹 서버에 배포할 수 있다.
스프링 부트 != 서버
Spring 프레임워크를 쉽게 사용할 수 있게 해주는 Tool일뿐, 스프링 부트 자체는 웹 서버가 아니다.
@SpringBootApplication
public class Application {
//자동 설정이 없다고 가정하고 Servlet을 만들어서 톰캣에 등록
public static void main(String[] args) {
Tomcat tomcat = new Tomcat(); // 톰캣 객체 생성
tomcat.setPort(8080); // 포트 설정
Context context = tomcat.addContext("/", "/"); // 톰캣에 컨텍스트 추가
// 서블릿 객체 생성
HttpServlet servlet = new HttpServlet() {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.println("<h1>Hello world!</h1>");
}
};
final String servletName = "myServlet";
// 톰캣에 서블릿 추가
tomcat.addServlet("/", servletName, servlet);
// 컨텍스트에 서블릿 매핑
context.addServletMappingDecoded("/home", servletName);
try {
// 톰캣 실행 및 대기
tomcat.start();
tomcat.getServer().await();
} catch (LifecycleException e) {
e.printStackTrace();
}
}
}
스프링에서는 Tomcat에 Servlet을 등록하고 실행하는 일련의 작업이 자동 설정으로 정의되어 있다. 자동 설정 덕분에 위 작업을 수행하지 않아도 된다.
ServletWebServerFactoryAutoConfiguration : 서블릿 웹 서버 생성하는 설정 파일
TomcatServletWebServerFactory를 들어가보면 톰캣을 생성하고 서블릿, 디스패처 설정을 하는 코드가 있다.
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
// ...
}
내장 웹 서버 변경
spring-boot-starter-web는 기본적으로 spring-boot-starter-tomcat 을 포함하고 있다. Tomcat 대신 Jetty를 내장 서버로 사용하도록 설정을 변경해보자.
pom.xml에서 dependencies를 아래와 같이 변경한다. tomcat의 include를 제거하기 위해 태그를 추가했다.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- Exclude the Tomcat dependency -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
</dependencies>
개발자 도구를 사용해 생산성 향상
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
- 클래스 패스의 파일이 변경될 때 자동으로 다시 시작하는 중요한 기능이 있다. 다음 시나리오에서 애플리케이션이 자동으로 다시 시작된다.
- 컨트롤러나 서비스 클래스를 변경할 때
- 속성 파일을 변경할 때
- 스프링 부트 개발자 도구의 장점
- 개발자는 매번 애플리케이션을 중지하고 시작할 필요가 없다. 애플리케이션은 변경되는 즉시 자동으로 다시 시작된다.
- 스프링부트 개발자 도구의 재시작 기능은 지능적이다. 새롭게 개발된 클래스만 리로드한다. third-party jar은 리로드 하지 않는다.
애플리케이션 모니터링에 스프링 부트 액추어터 사용하기
- 서비스가 느려지거나 다운되는 경우, 즉시 알고 싶다.
- 서버에 충분한 여유 공간이나 메모리가 있는지 확인하고 싶다.
'Study > Mastering-Spring-5-Study' 카테고리의 다른 글
ch05. 스프링 프레임워크 심화 - 병렬 프로그래밍 (0) | 2022.12.31 |
---|---|
ch05. 스프링 프레임워크 심화 - AOP (0) | 2022.12.31 |
ch03. 스프링 MVC (0) | 2022.12.31 |
ch02. 의존 관계 주입 및 단위 테스트하기 (1) | 2022.12.31 |
ch01. 스프링 환경 (0) | 2022.12.31 |
댓글