[Spring] Spring Boot

얄루얄루·2022년 12월 4일
0

Spring

목록 보기
3/14

Spring Core?

Spring Boot에 대해 알아보기 전에 어떤 기술들에 의해 그것이 쌓아올려졌는지 알아보자.

Spring Core란, Spring framework의 근본/핵심 부분으로 DI를 지원하는 기능을 담당한다.

사진에 보이는 Core Container 내의 Core가 바로 DI, 내지는 IoC를 담당하는 서브 프레임워크이다.

그러나 당연하게도, Core 혼자서 DI를 수행할 수는 없다.

구현체가 있어야 뭘 할 것 아닌가?

Spring Beans가 그 구현체를 찍어내는 일종의 공장을 구현한다.

Spring Context는 Core와 Beans를 기반으로 하며, 정의된 객체에 엑세스 할 수 있는 매개물을 제공한다. ApplicationContext 인터페이스가 Context의 핵심 부분이다.

Spring SpEL은 짧고 간단한 문법을 통해 필요한 데이터나 설정값을 얻어올 수 있게 하는 특별한 형태의 표현식에 가까운 간편한 언어이다. Spring 내의 모든 영역에서 사용 가능하다. @Value("$(config.value)")와 같은 식으로 변수 등에 특정 설정값을 주입하는데 사용할 수 있다.

결국 Core, Beans, Context, SpEL. DI를 수행하는데 필요한 기능들을 조금씩 담당하고 있는 이 4가지를 감싸고 있는 Core Container. 이 부분이 Spring Core라고 할 수도 있겠다.

DI container (= IoC container)

이전 글에서도 말했지만, DI = IoC이다. 용어에 헷갈리지 말자.

DI 컨테이너는 객체를 구성하고 관리하는 간소화된 방법을 제공하는 Spring의 핵심 기능 중 하나이다.

이 컨테이너는 정의된 Java 객체의 생명 주기를 관리하여 Spring 기반 애플리케이션의 구성도를 크게 향상시킨다. 즉, 컨테이너가 개발자를 대신하여 메소드 등 호출 시 필요한 자원을 전달한다.

IoC는 dependency injection (DI) 또는 dependency lookup 패턴을 사용하여 런타임 중에 필요한 자원을 제공함으로써 이루어진다.

DI는 Reflection이라는 기술을 기초로 구현되어 있고, 이는 런타임에 코드의 메타 데이터를 읽는 기술이다.

이때 메소드의 Signature를 읽을 수 있는데, Signiture에는 해당 메소드가 필요로 하는 매개변수 정보가 있다.

이를 토대로 메소드가 필요로 하는 자원을 런타임 중에에 제공하는 것이다.

Bean life cycle

DI 컨테이너에서 Bean의 생명 주기를 관리한다고 했다.

위 사진이 Bean의 생명 주기를 메소드에 따라 나타낸 그림이다.

생성이 되고 -> 초기화가 되고 -> 의존성 주입을 받고 -> 추가적인 커스텀 초기화를 진행하고 -> 사용 가능 상태가 된다.

이후 컨테이너가 닫히면 -> 파괴자를 실행하고 -> 커스텀 파괴자도 실행하고 -> 없어진다.

Bean 등록 방법

Bean을 등록하는 데에는 여러 방법이 있다. 시간이 흘러가며 발전되었다 보니, 뒤늦게 나온 보다 편의적인 방법도 있고, 그렇지는 않지만 여기저기서 쓰이고 있어서 없앨 수는 없기에 그냥 남아있는 방법도 있다.

XML을 통한 등록

아래처럼 쌩으로 xml을 작성해 등록하는 방법이다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  
<bean id="cardAdapter" class="com.example.demo.service.CardAdapter"/>
<bean id="moneyAdapter" class="com.example.demo.service.MoneyAdapter"/>
  
</beans>

자동완성이 지원되지 않고, 컴파일 등으로 오타를 잡기 힘들며 타이핑양이 많아진다.

XML ComponentScan을 통한 등록

작성한 클래스에 @Component 혹은 그 하위의 어노테이션을 붙여 런타임 중에 자동으로 등록시킨다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://w

<context:component-scan base-package="com.example.demo"/>
                                                           
</beans>

여전히 XML이 사용된다는 점이 짜증난다.

JavaConfig를 통한 등록

XML을 Java화 한 파일을 통해 등록하는 방법이다.

@Configuration
public class ApplicationConfig {

    @Bean
    public PaymentService paymentService(){
        return new PaymentService(
            new HashSet<>(Arrays.asList(moneyAdapter(), cardAdapter()))
        );
    }
    @Bean
    public CardAdapter cardAdapter() {
    	return new CardAdapter();
    }
    @Bean
    public MoneyAdapter moneyAdapter() {
    	return new MoneyAdapter();
    }
}

일단 무엇보다도, 자동완성과 컴파일시 정적 분석을 지원해준다는 점이 좋다.

JavaConfig ComponeneScan을 통한 등록

Spring-boot에서 default로 사용되는 방식이다.

@Configuration
@ComponentScan(basePackageClasses = MyApplication.class)
public class ApplicationConfig {
}

Bean 설정 방법

Bean의 구현체가 여러개인 경우 주입 받는 방법

  1. @Primary: 해당 빈을 최우선으로 주입한다.
  2. @Qualifier(”beanName”) : beanName으로 지정된 빈을 주입한다.
  3. Set 또는 List로 모두 받는다.
  4. 프로퍼티 이름을 빈과 동일하게 한다 (가장 흔하게 사용되는 방법).

Bean의 Scope

  1. 싱글톤 : 일반적 방법으로 하나만 만들어서 계속 재활용한다.
  2. Prototype : 매번 새로 만드는 방법이다 (데이터를 클렌징 해야할 때 유용).
    • Request : 요청에 따라 계속 새로 만든다.
    • Session : 세션 마다 계속 새로 만든다.

Spring의 환경 설정 (Profile)

환경을 다양하게 하여 특정 환경에서만 동작하는 Bean을 만들 수 있다.

  • "dev" "test" "production" 등의 이름으로 개발 단계에 따라 환경을 나눌 수도 있다.

  • 클래스단위에 적용하거나 메서드 단위에 적용 가능하다

    • 클래스 단위
      • @Configuration @Profile(”test”)
      • @Component @Profile(”test”)
    • 메서드 단위
      • @Bean @Profile(”test”)
  • -Dspring.profiles.active=sandbox, beta, production

  • 프로파일 표현식

    • @Profile(”!production”)
    • ! (not), & (and), | (or)

Bean vs Component

Spring으로 작성된 애플리케이션의 코드를 보면 @Bean도 있고 @Component도 있다. 그 외에도 @Controller, @Service 등도 있지만 얘네는 별로 중요하지 않다.

왜?

보는 바와 같이 위의 4종류는 Component의 자식격이다.

결국 고유하게 남는 것은 Bean과 Component 뿐이라는 것.

검색해보면 이러쿵저러쿵 차이점이 있다고 하는데,

핵심은 어차피 둘 다 Bean을 생성하기 위해 사용된다는 것이다.

그러니 하나만 기억하도록 하자.

@Bean은 메소드에,
@Component는 클래스에,

사용한다.

추가로 @Bean이 붙은 메소드가 있는 클래스는 반드시 @Configuration이나 @Component가 붙어야 한다.

아무것도 없이 돌리면 돌아는 가는데, 내부적으로 @Component가 붙어서 돌아가는 것이다.

그런데 Bean 관리 측면에서 봤을 때 @Configuration을 붙이는 것이 싱글톤 패턴을 유지하거나, 여러 성능 측면에서 @Component 보다 효율적이라는 Spring 개발자의 말이 있었다고 한다.

그러니 @Bean이 붙은 메소드가 있다면 @Configuration 붙여주도록 하자.

Spring Boot

Spring Framework가 뭔지는 대충 알겠다.

근데 Spring Boot는 또 뭐냐? 하위의 프레임워크 중 하나인가?

비유하자면 Node.js와 express.js의 관계와 유사하다.

단순히 말하면, Spring Core를 중심으로 하여 Spring Framework 내의 몇몇 모듈과 서브 프레임워크를 사용해 기능을 확장하고, 가장 대중적인 설정을 자동으로 적용해서 애플리케이션을 일단 만들어 낸 것이다.

컴퓨터를 키는 걸 Booting 한다고 하지 않는가.

그 때 내부 장치를 유저가 일일이 조율하지 않는다. 버튼만 누르면 컴퓨터가 켜질 뿐.

Spring Boot가 딱 그런 느낌이다. Spring Framework를 이용해서 만들 수 있는 애플리케이션을 보다 손쉽게 만들어내는 프레임워크인 것이다.

이를 위해 Spring Boot는 몇 가지 편의성을 가지고 있다.

  • 손쉬운 빌드 구성을 위한 starter 종속성을 제공한다.

    • 아래가 Spring Framework를 이용해 직접 앱 구성을 할 때에 반드시 추가해줘야 하는 최소한도의 종속성이다.
      <dependencies>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-web</artifactId>
          <version>5.3.9.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>5.3.9.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>tomcat-embed-core</artifactId>
          <version>1.4.9.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-bean</artifactId>
          <version>2.5.9.RELEASE</version>
      </dependency>
      </dependencies>
    • 아래는 Spring Boot를 이용할 때의 종속성이다.
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>
        </dependencies>
  • 써드 파티 라이브러리 자동 구성

    • 외부 라이브러리 사용 시 서로 호환되는 버전을 일일이 찾지 않아도 된다.
  • 자동 설정

    • Dispatcher Servlet 구성, Data Source 구성, Error Page 구성, 내장 Tomcat 서버 구성 등을 관례에 따라 자동으로 만들어 준다.

    • 변경하고 싶은 설정이 있다면, 추후 변경하면 된다.

    • 이 기능이 없다면, 아래의 코드를 매번 직접 넣어야 한다.

      <web-app...>
      
         <!-------- DispatcherServlet configuration here----->
         ....
         <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/myWeb-servlet.xml</param-value>
         </context-param>
      
         <listener>
            <listener-class>
               org.springframework.web.context.ContextLoaderListener
            </listener-class>
         </listener>
      
      </web-app>
      
      <bean
       class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
               id="entityManagerFactory">
               <property name="persistenceUnitName" value="hsql_pu" />
                <property name="dataSource" ref="dataSource" />
      </bean>
      <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
                <property name="entityManagerFactory" ref="entityManagerFactory" />
                <property name="dataSource" ref="dataSource" />
      </bean>
      <tx:annotation-driven transaction-manager="transactionManager"/>

다음 글에서는 Spring Web MVC, AOP, ORM 등의 중요한 모듈들에 대해 알아보자. 이번엔 진짜 딴길로 안 센다

References

profile
시간아 늘어라 하루 48시간으로!

0개의 댓글