ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [SpringBoot] 스프링 vs 스프링부트
    Programming/SpringBoot 2023. 12. 2. 12:35
    728x90

    Spring

    스프링의 가장 큰 특징은 POJO프로그래밍을 지향하는 것입니다.

    (POJO(Plain Old Java Object) : 순수 Java만을 통해서 생성한 객체)

     

    POJO는 순수 Java만을 사용하여 만든 객체이므로 특정 기술이나 환경에 종속되지 않습니다. 따라서, 외부 기술이나 규약의 변화에 얽매이지 않아 유연하게 변화와 확장에 대처할 수 있습니다.

    이러한 POJO를 사용하여 비즈니스 로직을 구현하면 객체지향 설계를 제한없이 적용할 수 있으며, 코드가 단순해져 테스트와 디버깅이 쉬워집니다. 

    이처럼 비즈니스 로직을 구현하는데 POJO를 적극적으로 활용하는 프로그래밍 패러다임을 POJO프로그래밍이라고 합니다.

     

    POJO프로그래밍을 위해 스프링이 지원하는 기술인 IoC, DI, AOP, PSA,가 있습니다.

    IoC

    제어의 역전

    객체의 생성 및 제어권을 사용자가 아닌 Spring이 가지고 있음.

    new를 이용해 객체를 직접 생성하는 것이 아닌 Spring Container에 의해 관리받는 객체를 사용.

     

    DI

    의존성 주입

    어플리케이션 실행 시점에 생성된 객체 인스턴스의 참조가 연결되는 것.

    생성자를 통해 참조할 객체의 정보를 알 수 있음.

     

    AOP

    관심 지향 프로그래밍

    어플리케이션 전반에 걸쳐 적용되는 공통 기능을 비즈니스 로직으로부터 분리해내는 것

     

    PSA

    일관된 서비스 추상화

    특정 기술과 관련된 서비스를 추상화하여 일관된 방식으로 사용될 수 있도록 함.

    JDBC의 경우 각 DB의 드라이버에 맞게 다르게 구현하여 사용자 입장에서 로직 변경없이 그대로 사용 가능.


    ▶ 스프링 동작 원리

    웹 애플리케이션을 개발하고 배포하려면 war방식으로 배포해야 한다.

    ● 톰캣을 별도로 설치하고 설정을 구성해야 한다.

     애플리케이션 코드를 war로 빌드해야 한다.

      빌드한 war파일을 was에 배포해야 한다.

     

    이러한 방식은 아래의 단점이 있다.

    ○ was를 별도로 설치해야 함

    ○ 개발 환경이 복잡

    ○ 배포 과정도 복잡

    ○ 톰캣의 버전을 변경하려면 톰캣을 다시 설치하고 서버를 다시 구성해야 함

     

    그래서 Spring MVC방식 + 내장 톰캣 의존성을 사용하면 일부 해결할 수 있다. (일명 Fat Jar)

     여전히 코드 및 빌드에서 톰캣을 조작해야 함

     파일명 중복을 해결할 수 없다

     

      스프링부트를 도입하면 이 문제를 해결할 수 있다!!

     

    ▶ 스프링부트 동작 원리

    @SpringBootApplication

    @SpringBootApplication 어노테이션은 크게 2가지 일을 한다.

     스프링 컨테이너를 생성한다.

      WAS(내장 톰캣)을 생성한다.

     

    스프링부트의 동작 원리를 요약하면 아래와 같다.

    1. java -jar xxx.jar

    2. MANIFEST.MF 인식

    3. JarLauncher.main() 실행

        - BOOT-INF/classes/  인식

        - BOOT-INF/lib/   인식

    4. BootApplication.main() 실행

     

    1. java -jar xxx.jar

    java -jar xxx.jar를 사용해서 jar를 풀면 아래의 파일들이 존재한다.

    [xxx.jar]
    │  
    ├─ META-INF
    │  ├─ MANIFEST.MF
    ├─ org/springframework/boot/loader
    │  ├─ JarLauncher.class // 스프링 부트 main() 실행 클래스
    ├─ BOOT-INF
    │  ├─ classes  //우리가 개발한 class파일과 리소스 파일
    │  ├─ org/example/gradlejar/GradleJarApplication.class
    │  ├─ org/example/gradlejar/controller/HelloController.class
    │  ├─ ...
    │  ├─ lib   //외부 라이브러리
    │  ├─ spring-webmvc-6.0.4.jar
    │  ├─ tomcat-embed-core-10.1.5.jar
    │  ├─ ...
    │  ├─ classpath.idx //외부 라이브러리 경로
    │  ├─ layers.idx   //스프링부트 구조 경로

     

    자세한 내용은 아래 포스팅 참고

    https://code-space.tistory.com/390

     

     

    2. MANIFEAST.MF 인식

    스프링이 만든 MANIFEST.MF파일을 보면 아래와 같다.

    Manifest-Version: 1.0
    Main-Class: org.springframework.boot.loader.launch.JarLauncher
    Start-Class: org.example.gradlejar.GradleJarApplication
    Spring-Boot-Version: 3.2.3
    Spring-Boot-Classes: BOOT-INF/classes/
    Spring-Boot-Lib: BOOT-INF/lib/
    Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
    Spring-Boot-Layers-Index: BOOT-INF/layers.idx
    Build-Jdk-Spec: 17

    Main-class는 JarLauncher 클래스를 실행한다. JarLauncher는 스프링부트가 빌드 시 생성해준다.

     

    3. JarLauncher.main() 실행

    스프링부트에서는 Jar내부에 Jar를 감싸는 형태로 동작한다. 그래서 스프링부트에서 실행을 해야 하는데 우리가 개발한 어플리케이션의 main()을 바로 실행하면 Jar안에 Jar가 있는 구조를 읽을 수 없다.

     

    그래서 일종의 Hook역할을 하는 JarLauncher가 XXXMainApplication.main()을 대신 호출한다.

    JarLauncher는 BOOT-INF에 있는 classes, lib에 있는 jar를 읽은 후 Start-Class에 지정된 main()을 실행한다.

    ( IDE를 사용할 때는 JarLauncher를 사용하지 않는다. 실행 가능한 Jar가 아니라 IDE에서 필요한 라이브러리를 모두 인식할 수 있게 도와주기 때문 )

     


    스프링 vs 스프링부트

    스프링(Spring)은 프레임워크이며, 스프링 부트(Spring boot)는 스프링을 기반으로 한 도구이다.

     

     스프링은 개발자가 직접 설정 파일을 작성하여 스프링 컨테이너를 구성하고, 필요한 빈 객체를 등록하고, 빈 객체 간의 의존성을 설정해야 한다.

    스프링은 특정한 구성을 위해 추가적인 라이브러리와 설정이 필요하다.

    반면, 스프링부트는 스프링 프레임워크를 보다 쉽게 사용할 수 있도록 만든 프레임워크이다.

    스프링부트에서는 개발자가 설정 파일을 작성할 필요 없이, 프로젝트의 설정과 라이브러리의 의존성을 자동으로 처리해주는 기능을 제공한다. 또한 스프링부트는 실행 가능한 JAR  파일을 만들 수 있다.

    스프링부트는 스프링에서 제공하는 여러 기능들을 자동으로 설정하여 개발자가 쉽게 사용할 수 있도록한다.

    예를 들어, 스프링 MVC, 스프링 Data JPA, 스프링 Security 등의 기능을 자동으로 설정하며, 개발자가 별도로 설정 파일을 작성하지 않아도 사용할 수 있다. 또한, 스프링부트는 Actuator라는 모니터링과 관리를 위한 기능을 제공하여 어플리케이션의 상태를 모니터링하고 필요한 조치를 취할 수 있도록 해준다.

     

    이러한 차이점들은 스프링과 스프링부트의 사용 목적과 방식을 크게 달리 한다.

    스프링은 개발자가 직접 설정 파일을 작성하고, 빈 객체를 등록하고, 빈 객체 간의 의존성을 설정하는 것을 요구한다.

    반면, 스프링부트는 개발자가 쉽게 스프링을 사용할 수 있도록 설정과 의존성 처리를 자동으로 처리한다.

    스프링은 스프링 프레임워크를 세밀하게 제어하고자 하는 경우에, 스프링 부트는 빠르고 간단하게 스프링 어플리케이션을 개발하고자 하는 경우에 사용된다.

     

    요약

    스프링은 프레임워크이며, 스프링부트는 스프링 프레임워크를 기반으로 한 도구이다.

    스프링은 설정파일을 작성해야 하지만, 스프링부트는 자동 설정을 제공하여 간편하게 개발 할 수 있다. 또한 스프링부트는 내장 서버를 제공하여 쉡게 웹 애플리케이션을 실행할 수 있다. 

    스프링은 스프링 프레임워크를 세밀하게 제어하고자 하는 경우에, 스프링 부트는 빠르고 간단하게 어플리케이션을 개발하고자 하는 경우에 사용된다.

     

     

     

    Spring Boot

    스프링에서 스프링 프레임워크로 개선한 사항으로는 다음과 같다.

     

    - 개발 환경 설정을 간소화 :

    스프링은 버전에 따라 동작하는 외부 라이브러리를 일일이 찾아 연동해야 한다. 하지만 스프링 부트는 미리 설정된 스타터 프로젝트로 외부 라이브러리를 최적화해 제공하므로 사용자가 직접 연동할 필요가 없다.

     

    - 웹 애플리케이션 서버를 내장:

    스프링 부트는 내부에 웹 애플리케이션 서버(WAS)인 톰캣을 가지고 있다. 따라서 웹 서비스를 jar파일로 간편하게 배포할 수 있다.

     

    스프링 부트는 자바 웹 프로그램을 더욱 쉽고 빠르게 만들기 위한 도구이고 개발자가 개발에만 더 집중할 수 있게 도와준다.

     

    스프링 부트는 스프링의 장점을 모두 가지고 있으며 다음과 같은 추가적인 기능을 제공한다.

    - 라이브러리 관리
    - Auto-configuration 및 내장형 컨테이너 제공
    - 스프링 기반의 애플리케이션을 빠르게 만들 수 있도록 하는 다양한 starter의존성 제공
    - 톰캣 기반의 편리한 배포

     

     

    라이브러리 관리

    라이브러리를 관리할 때 각 라이브러리는 호환이 잘되는 라이브러리가 있고, 호환이 잘 안되는 버전이 있다. 

    과거에는 라이브러리 버전을 선택하는데 많은 시간이 소요되었다.

    dependencies {
    	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    	implementation 'org.springframework.boot:spring-boot-starter-web'
    	compileOnly 'org.projectlombok:lombok'
    	annotationProcessor 'org.projectlombok:lombok'
    	testImplementation 'org.springframework.boot:spring-boot-starter-test'
    	implementation 'org.springframework.boot:spring-boot-starter-jdbc'
    	implementation 'mysql:mysql-connector-java:8.0.27'
    	implementation 'org.springframework.boot:spring-boot-starter-mustache'
    	testImplementation 'org.springframework.boot:spring-boot-starter-test'
    	implementation 'org.springframework.boot:spring-boot-starter-validation'
    	implementation 'org.springframework.security:spring-security-crypto:5.7.1'
    	implementation 'org.springframework.session:spring-session-core'
    	implementation 'commons-io:commons-io:2.11.0'    /* Apache commons-io */
    	implementation group: 'commons-fileupload', name: 'commons-fileupload', version: '1.4'
    }

    스프링부트는 개발자가 라이브러리를 편리하게 사용할 수 있게 다양한 기능을 제공한다.

     외부 라이브러리 버전 관리

     스프링 부트 스타터를 제공해준다.

     

    의존성 관리 플러그인만 설치해주면 스프링부트 버전에 맞는 라이브러리의 버전으로 관리해준다.

    plugins {
    	id 'java'
    	id 'org.springframework.boot' version '3.2.4'
    	id 'io.spring.dependency-management' version '1.1.4'
    }

    개발하면서 필요한 라이브러리의 버전을 명시하지 않아도 되는 것은 의존성 관리 플러그인이 동작하기 때문이다. 

    버전을 명시하지 않는다면 최신 버전을 받아오는 것이 아니라, 라이브러리를 가져오는 것에 실패하고 에러가 난다.

     

    dependency-management-plugin

    외부 라이브러리를 사용할 때 각 프로젝트의 build.gradle에서 라이브러리 의존성 버전을 일일이 동기화해줘야 하는 문제가 있는데, Gradle의 의존성 관리 플러그인을 사용하면 해결할 수 있다.

    Gradle 6.8 이상 Java 8버전 이상에서 지원된다.

     

    Auto-configuration

    스프링의 경우, configuration을 설정할 때 매우 긴 코드 작업이 필요하다. 필요한 빈들을 수동으로 등록해줘야 한다는 문제가 있다.

    스프링 부트는 이러한 문제를 해결하기 위해 자동 구성(auto configuration)이라는 필요한 빈들을 자동으로 등록해주는 기능을 제공한다. application.yml 혹은 application.properties 파일에 명세를 적으면 쉽게 configuration을 관리할 수 있다.

     

    일반적으로는 spring-boot-starter안에 있는 spring-autoconfigure라이브러리가 동작하게 된다.

    내부에는 아래 클래스들이 있다.

    ● JdbcTemplateAutoConfiguration

    ● DataSourceAutoConfiguration

    ● JacksonAutoConfiguration

    ● HttpMessageConvertersAutoConfiguration

    ● BatchAutoConfiguration

    ●  ...

     

    JdbcTemplateAutoConfiguration.class를 보자.

    @AutoConfiguration(after = DataSourceAutoConfiguration.class)
    @ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
    @ConditionalOnSingleCandidate(DataSource.class)
    @EnableConfigurationProperties(JdbcProperties.class)
    @Import({ DatabaseInitializationDependencyConfigurer.class,
            JdbcTemplateConfiguration.class,
            NamedParameterJdbcTemplateConfiguration.class })
    public class JdbcTemplateAutoConfiguration {
    }

    ● @AutoCofiguation : 자동구성을 사용하려면 이 어노테이션을 등록해야 한다.

         ○ 어노테이션 내부에 @Cofiguration이 있어서 빈 등록하는 자바 설정 파일로 사용할 수 있다.

          after = XXXAutoConfiguration.class

            자동 구성이 실행되는 순서를 지정할 수 있다. 

            JdbcTemplate은 DataSource가 필요하기 때문에 DataSourceAutoConfiguration.clas 이후에 실행되도록 설정됨

    ● @ConditonalOnClass( { DataSource.class, JdbcTemplate.class } )

          해당 클래스들이 빈으로 등록된 경우에만 빈으로 등록된다.

    ● @Import

          스프링 컨테이너에 자바 설정을 추가할 때 사용

     

    결과적으로 @Import에 포함된 DatabaseInitializationDependencyConfigurer.class, JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class가 빈으로 구성된다.

     

    @Conditional

    스프링부트의 자동 구성 및 라이브러리 등에서 자주 사용된다.

    특정 조건이 만족하는지 여부를 구분하는 어노테이션이다.

     

    예를 들어 @ConditionalOnBean 어노테이션의 구현을 보자.

    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional(OnBeanCondition.class)
    public @interface ConditionalOnBean {
    ...}

    OnBeanCondition.class는 Condition인터페이스를 구현하고 있다.

    package org.springframework.context.annotation;
    
    public interface Condition {
        boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
    }

    ConditionContext : 스프링 컨테이너, 환경 정보를 담고 있다.

    AnnotatedTypeMetadata : 어노테이션 메타 정보를 담고 있다.

     

    @Conditional 어노테이션이 붙어 있으면 스프링이 로드될 때 ConditionEvaluator를 사용해서 Condition여부를 체크하고 true일 경우 빈으로 등록한다.

     

    스프링은 개발에 필요한 대부분의 ConditionalOnXXX 메서드를 제공하고 있다.

     

    적용

    1-1. 라이브러리

    기본적으로 의존성의 경우 컴파일 과정에서 jar형태로 가져와서 사용한다.

    implementation files('libs/module.jar')

    특정 모듈의 jar를 프로젝트에 포함할 수 있다.

    라이브러리를 사용할 때 문제는 README.md, GUIDE.md 등에 사용 방법을 명확하게 명시해줘야 한다.

    ● ...Config 를 컴포넌트로 스캔해줘야 하고

    ● ... Bean을 등록해줘야 하고

    ● ..

     

    이런 처리를 모두 사용측에서 하기는 귀찮다. 그래서 라이브러리에서 대부분의 처리를 대신해 줄 수 있는 방법이 AutoConfiguration이다.

     

    1-2. AutoConfiguration

    라이브러리를 제공하는 사람이 코드 1줄을 쓰면 가져다 쓰는 사람 100만명 코드를 1줄씩 아낄 수 있다. 

    좋은 라이브러리는 의존성 추가하기만 하면 모든 구성이 자동으로 처리되어야 한다.

    // @Configuration 삭제
    @AutoConfiguration
    @ConditionalOnProperty(name = "memory", havingValue = "on")
    public class MemoryAutoConfig {
    
        @Bean
        public MemoryController memoryController() {
            return new MemoryController(memoryFinder());
        }
        @Bean
        public MemoryFinder memoryFinder() {
            return new MemoryFinder();
        }
    }

    @Configuration을 @AutoConfiguration으로 교체했다.

    그리고 환경에 따라 기능을 on/off하기 위해 @ConditionalOnProperty를 추가했다.

     

    1-3. 자동 구성 대상 지정

    이대로 프로젝트를 빌드해도 해당 @AutoConfiguration이 등록되지 않는다.

    resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 파일을 생성한다.

    그리고 해당 파일에 다음 내용을 추가한다.

    memory.MemoryAutoConfig

    이제 해당 모듈을 Import하면 해당 파일을 읽어서 AutoConfiguration이 자동으로 등록된다.

    자세하게는 다음 순서로 동작하고 있다. 

    @SpringBootApplication -> @EnableAutoConfiguration -> @Import(AutoConfigurationImportSelector.class)

     

    해당 어노테이션들을 살펴보자.

    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes =
    TypeExcludeFilter.class),
          @Filter(type = FilterType.CUSTOM, classes =
    AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {...}

     

    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {...}

     

    @EnableAutoConfiguration으로 인해 자동 구성을 활성화하게 되고, AutoConfigurationImportSelector가 동작한다.

     

    1-4. ImportSelector

    @Import는 특정 Config들을 빈으로 등록한다.

    @Import(ImportSelector)를 사용하면 ImportSelector에게 등록할 설정 빈을 선택하는 역할을 위임한다.

    package org.springframework.context.annotation;
    public interface ImportSelector {
        String[] selectImports(AnnotationMetadata importingClassMetadata);
    //...
    }

    해당 인터페이스를 구현해서 빈으로 등록할 때 클래스의 패키지 위치를 String배열로 반환하기만 하면 된다.

     

    즉, AutoConfigurationImportSelector의 경우 존재하는 모든 라이브러리의 

    org.springframework.boot.autoconfigure.AutoConfiguration.imports파일을 읽어서 String[]로 반환하므로 해당 설정들을 모두 빈으로 등록한다.

     

    2. spring.factories

    자동 구성을 구성하는 두 가지 주요 방법 중 spring.factories를 활용하는 방법도 있다.

    resources/META-INF/spring.factories에 아래 내용을 추가하면 된다.

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
        memory.MemoryAutoConfiguration

    스프링부트가 실행될 때 EnableAutoConfiguration 목록에 MemoryAutoConfiguration이 추가된다.

    spring.factories의 경우 Configuration을 등록하는 것 이외에도 다양한 구성을 추가할 수 있다.

     

    일반적으로 외부 라이브러리에서 제공하는 구성을 사용하거나, 어플리케이션에서 커스텀한 구성을 정의할 때 spring.factories를 사용한다.

    내부 라이브러리에서 제공하는 구성을 확장하거나, 특정 모듈의 자동 구성을 정의할 때는 org.springframework.boot.autoconfigure.AutoConfiguration.imports 를 주로 사용한다.

     

    org.springframework.boot.autoconfigure.AutoConfiguration.imports를 등록하고, 다수의 ApplicationListener나 ContextFactory, DB Initializer Detector등은 spring.factories로 등록한다.

     

    다양한 Starter 제공

    버전은 의존성 관리 플러그인으로 편하게 관리로 해결되지만, 여전히 수 많은 라이브러리가 필요한 문제가 있다.

    스프링에서 Spring web의존성을 추가하려면 다음과 같은 xml코드가 필요합니다.

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>5.3.5</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.5</version>
    </dependency>

     

    스프링 부트의 경우 그루비 코드 한 줄이면 설정 가능합니다. (스프링부트 스타터)

    implementation 'org.springframework.boot:spring-boot-starter-web'

     

     

    편리한 배포

    스프링의 경우 war파일을 톰캣에 직접 올려 배포해야 했습니다. 

    스프링 부트는 톰캣이 내장되어 있기 때문에 jar파일로 배포가 가능합니다.

     

    Spring.io페이지에서 생성하는 '스프링 부트 프로젝트'


    참고

    https://jaehoney.tistory.com/348

     

    https://www.inflearn.com/blogs/3315

     

    https://cafe.naver.com/gilbutitbook

    728x90

    'Programming > SpringBoot' 카테고리의 다른 글

    [SpringBoot] Lombok 롬복  (1) 2023.12.07
    [SpringBoot] JPA  (0) 2023.12.07
    [SpringBoot] MVC패턴 + REST API + JPA 프로젝트 설계  (1) 2023.12.07
    [SpringBoot] Spring Container  (0) 2023.12.02
    [SpringBoot] MVC패턴  (1) 2023.12.02

    댓글

© 2022. code-space ALL RIGHTS RESERVED.