스프링과 스프링부트(Spring Boot)

웅평·2024년 5월 26일
0

스프링(Spring)이란?

스프링 프레임워크(Spring Framework)는 Java 기반 애플리케이션 개발을 지원하는 오픈소스 애플리케이션 프레임워크로 간단히 스프링(Spring)이라고도 불린다. 순수 자바 객체(POJO)만을 사용하여 복잡성을 제거하고, 단순하고 가벼운 코드로 기업용 애플리케이션을 개발하기 위한 목적으로 개발된 프레임워크(Framework)이다.

스프링의 특징

POJO 프로그래밍을 지향

  • 스프링의 가장 큰 특징은 POJO 프로그래밍을 지향한다는 것
  • POJO란, Plain Old Java Object, 즉 순수 Java만을 통해서 생성한 객체를 의미
  • 순수 Java 만을 사용한다는 것은 Java 및 Java의 스펙에 정의된 기술만 사용한다는 의미

    어떤 객체가 외부의 라이브러리나 외부의 모듈을 가져와서 사용하고 있다면, 그 객체는 POJO라고 할 수 없다. POJO는 말 그대로, 다른 기술을 사용하지 않는 순수한 Java만을 사용하여 만든 객체인 것이다

// 기본적인 POJO 예시
public class Person {
    // 필드
    private String name;
    private int age;

    // 기본 생성자
    public Person() {
    }

    // 매개변수 있는 생성자
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // getter 메서드
    public String getName() {
        return name;
    }

    // setter 메서드
    public void setName(String name) {
        this.name = name;
    }

    // getter 메서드
    public int getAge() {
        return age;
    }

    // setter 메서드
    public void setAge(int age) {
        this.age = age;
    }

    // toString 메서드 (객체 정보를 문자열로 반환)
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

POJO의 주요 특징

  1. 특정 프레임워크에 의존하지 않음: 특정 프레임워크의 클래스나 인터페이스를 구현하지 않는다.
  2. 자유롭고 단순함: 복잡한 상속 구조나 인터페이스 구현이 필요 없다.
  3. 테스트 용이: 독립적이기 때문에 단위 테스트가 용이하다.

POJO 프로그래밍을 위해 스프링이 지원하는 기술인 IoC/DI, AOP, PSA에 대해서 알아보자

IoC(Inversion of Control, 제어의 역전)

스프링은 객체의 생명 주기 및 의존성 관리를 담당하는 IoC 컨테이너를 제공한다. 개발자는 객체의 생성과 관계 설정을 스프링에 위임할 수 있으며, 스프링 컨테이너가 객체의 생명 주기를 관리하고 필요한 의존성을 주입한다. 즉 프로그램의 흐름을 개발자가 직접 제어하지 않고, 프레임워크가 대신 제어하는 개념이다

과정

  1. 객체의 생성 및 관리:
  • ApplicationContext를 사용하여 빈(Bean)을 생성하고, 관리한다.
  • 빈은 일반적으로 Spring이 제어하며, 개발자는 객체의 생성과 관리를 직접 처리하지 않는다.
  1. 의존성 관리:
  • 객체 간의 의존성을 Spring이 주입(DI)한다.
  • 객체가 필요로 하는 다른 객체를 직접 생성하거나 찾는 대신, Spring 컨테이너가 의존성을 주입해주다.
  1. 제어 흐름의 역전:
  • 개발자가 코드의 제어 흐름을 결정하지 않고, 프레임워크가 객체의 라이프사이클 및 실행 흐름을 관리한다.

DI(Dependency Injection, 의존성 주입)

스프링은 의존성 주입을 통해 객체 간의 관계를 설정한다. 의존성 주입은 애플리케이션의 결합도를 낮추고 유연성과 테스트 용이성을 향상시킨다.

DI를 사용하지 않은 경우

public class UserService {
    private UserRepository userRepository;

    public UserService() {
        this.userRepository = new UserRepository(); // 직접 생성
    }
    
    // ...
}

위의 코드에서는 'UserService'는 직접적으로 'UserRepository'를 생성하고 의존하고 있다. 이로 인해서 'UserService'와 'UserRepository' 간에 강한 결합이 형성되어 있다. 이런 경우 유지 보수성이 떨어지며, 코드 변경 시 여러 클랠스에 영향을 줄 수 있다.

DI를 사용한 경우

1. Constructor Injection
생성자를 통해 의존성을 주입하는 방식이다. 클래스의 생성자를 정의하고, 의존하는 객체를 매개변수로 받아 필드에 할당한다.

public class UserService {
    private UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    // ...
}

2. Setter Injection
Setter 메서드를 통해 의존성을 주입하는 방식이다. Setter 메서드를 정의하고, 해당 메서드를 통해 의존하는 객체를 주입한다.

public class OrderService {
    private PaymentGateway paymentGateway;

    @Autowired
    public void setPaymentGateway(PaymentGateway paymentGateway) {
        this.paymentGateway = paymentGateway;
    }
    
    // ...
}

3. Field Injection
필드에 직접 의존성을 주입하는 방식이다. 주로 '@Autowired' 어노테이션을 사용하여 필드에 의존성을 주입한다.

@Service
public class ProductService {
    @Autowired
    private ProductRepository productRepository;
    
    // ...
}

4. Method Injection
메서드의 매개변수로 의존성을 주입하는 방식이다.

@Component
public class ReportGenerator {
    private EmailService emailService;

    @Autowired
    public void setEmailService(EmailService emailService) {
        this.emailService = emailService;
    }
    
    public void generateReport() {
        // Use the emailService here...
    }
}

5. @Qualifier 어노테이션 사용 (빈의 특정 구현체 주입)
같은 타입의 여러 빈 중에서 어떤 빈을 주입할지 선택할 때 사용

public class OrderService {
    private PaymentGateway paymentGateway;

    @Autowired
    public OrderService(@Qualifier("creditCardGateway") PaymentGateway paymentGateway) {
        this.paymentGateway = paymentGateway;
    }
    
    // ...
}

6. XML 설정을 통한 의존성 주입
XML 파일을 사용하여 의존성 주입을 정의할 수 있다.

<bean id="userService" class="com.example.UserService">
    <constructor-arg ref="userRepository" />
</bean>

<bean id="userRepository" class="com.example.UserRepository" />

7. Java Config
Java 설정 클래스를 사용하여 의존성 주입을 정의할 수 있다.

@Configuration
public class AppConfig {
    @Bean
    public UserService userService(UserRepository userRepository) {
        return new UserService(userRepository);
    }
    
    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }
}

AOP지원(Aspect-Oriented Programming, 관점 지향 프로그래밍)

스프링은 AOP를 지원하여 애플리케이션의 핵심 비즈니스 로직과 부가적인 기능(로깅, 트랜잭션 관리 등)을 분리하여 모듈화할 수 있다. 이는 중복 코드를 줄이고 코드의 가독성과 유지보수성을 높인다. 즉, AOP는 여러 객체에 공통으로 적용할 수 있는 기능을 구분함으로써 재사용성을 높여주는 프로그래밍 기법이다.

PSA(Portable Service Abstraction, 추상화)

PSA란 환경의 변화와 관계없이 일관된 방식의 기술로의 접근 환경을 제공하는 추상화 구조를 말한다.

스프링은 Java 백엔드 개발에 있어 핵심적인 역할을 수행하는 프레임워크이며, 백엔드 개발에서 데이터베이스는 떼어놓기 어렵습니다. 웹 서버는 데이터베이스와 소통하며 웹 클라이언트의 요청을 처리해야 하기 때문입니다. 데이터베이스의 종류는 MySQL, Oracle, Maria DB, Mongo DB 등 실로 다양합니다.
만약, 여러분이 MySQL을 사용하여 개발을 완료했는데, Maria DB로 데이터베이스를 바꿔야 하는 상황을 가정해봅시다. 이 때, 각 데이터베이스마다 사용 방법이 다르다면 어떨것 같나요? 아마 기존에 작성한 코드를 전부 지우고 새로 작성해야 하거나, 기존 데이터베이스와 새로운 데이터베이스 간에 사용 방법이 다른 코드를 모두 찾아서 일일이 수정해주어야 할 것입니다.
그러나, 스프링을 사용하면 동일한 사용방법을 유지한 채로 데이터베이스를 바꿀 수 있습니다. 이는 스프링이 데이터베이스 서비스를 추상화한 인터페이스를 제공해주기 때문에 가능합니다. 즉, 스프링은 Java를 사용하여 데이터베이스에 접근하는 방법을 규정한 인터페이스를 제공하고 있으며, 이를 JDBC(Java DataBase Connectivity)라고 합니다.
각 데이터베이스를 만든 회사들은 자신의 데이터베이스에 접근하는 드라이버를 Java 코드의 형태로 배포하는데, 이 드라이버에 해당하는 Java 코드의 클래스가 JDBC를 구현합니다. 따라서, JDBC를 기반으로 하여 데이터베이스 접근 코드를 작성해두면, 이후에 데이터베이스를 바꾸어도 기존에 작성한 데이터베이스 접근 로직을 그대로 사용할 수 있습니다.
이러한 JDBC처럼 특정 기술과 관련된 서비스를 추상화하여 일관된 방식으로 사용될 수 있도록 한 것을 PSA(Portable Service Abstraction, 일관된 서비스 추상화)라고 합니다.

Spring의 문제점

설정의 복잡성

스프링은 강력한 기능을 제공하기 위해 많은 설정과 구성이 필요합니다. 이는 초기 설정의 복잡성을 증가시킬 수 있고, 초보자에게는 어렵게 느껴질 수 있습니다. 개발자들이 애플리케이션 컨텍스트 설정, 빈 정의, 다양한 컴포넌트 구성 등을 위해 많은 설정 코드를 작성해야 합니다.

높은 초기 학습 난이도

스프링은 다른 프레임워크에 비해 학습이 필요한 부분이 많습니다. 다양한 개념과 기능을 이해하고 사용하기 위해 시간과 노력이 필요할 수 있습니다.

의존성 관리 문제

전통적인 스프링 프레임워크에서는 여러 의존성과 그들의 버전을 관리하는 것이 복잡할 수 있습니다. 스프링 레거시에서는 의존성 주입(Dependency Injection)을 구현하기 위해 XML 설정 파일에 많은 수의 빈(Bean)을 등록해야 합니다. 이는 코드의 가독성을 떨어뜨리고, 의존성 관리가 어려워질 수 있습니다

별도 WAS 서버 구성의 번거로움

스프링을 웹상에서 사용하기 위해서는 별도의 Web Application Server(WAS)를 설치하고, 설정해야 합니다. 또한, 애플리케이션을 서비스하기 위해서 별도의 서버에 수동으로 배포해야 하는 번거로움이 있습니다.

스프링부트(Spring Boot)란?

스프링은 기존 기술의 복잡성을 크게 줄인 프레임워크이지만, 그럼에도 불구하고 스프링을 사용하기 위해서는 여러 가지의 사항들을 설정해주어야 한다. 스프링 부트를 사용하면 설정 정보를 간략하게 줄일 수 있다. 스프링 부트는 스프링으로 애플리케이션을 만들 때에 필요한 설정을 간편하게 처리해주는 별도의 프레임워크이다.

Spring Boot의 특징

간결한 설정

스프링 부트는 번거로운 XML 설정이 필요 없으며, 최소한의 설정으로 Spring을 사용할 수 있고, 기본적인 설정을 자동으로 처리하므로 개발자가 많은 설정 작업을 하지 않아도 됩니다. 설정으로 인한 불편사항을 해소하여 개발자는 애플리케이션 개발에 집중할 수 있다.

내장 서버

스프링 부트는 내장된 서버(내장 Tomcat, Jetty, Undertow)를 제공하여 별도의 서버 설정 없이 애플리케이션을 실행할 수 있다. 배포를 위해 War 파일을 생성해서 Tomcat에 배포할 필요 없으며, JAR 파일에는 모든 의존성 라이브러리가 포함되어 있어 외부 서버 없이도 애플리케이션을 실행할 수 있다. 이는 애플리케이션의 배포와 관리를 간편하게 만들어 준다.

의존성 관리 간소화

스프링 부트는 여러 3rd party 라이브러리를 사용할 때, 발생하는 라이브러리 버전 충돌로 인한 문제를 해결하기 위해서, 이미 테스트된 여러 라이브러리들의 묶음 패키지를 제공한다.

3rd party 의존성 관리를 용이하게 하기 위한 ‘starter’ 의존성 통합 모듈을 제공하여 Maven/Gradel 설정 시 버전 관리가 간편하다. 스프링 부트에서 제공하는 의존성 세트를 통해 개발자는 버전 충돌이나 복잡한 의존성 설정에 대해 걱정하지 않고 필요한 의존성을 쉽게 지정할 수 있다.

운영 편의성

스프링 부트는 애플리케이션의 상태 모니터링, 로깅, 보안 설정 등 운영에 필요한 기능들을 제공한다. 이를 통해 애플리케이션의 운영과 관리가 편리해지고 안정성이 향상된다.

출처
https://www.codestates.com/blog/content/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8
https://velog.io/@alghrksl/Spring-Spring-%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80
https://velog.io/@jinyeong-afk/%EA%B8%B0%EC%88%A0-%EB%A9%B4%EC%A0%91-Spring-IoC-Inversion-of-Control%EC%99%80-DI-Dependency-Injection%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC

0개의 댓글