Spring Framework는 Java 플랫폼을 위한 오픈소스 애플리케이션 프레임워크로, 엔터프라이즈급 애플리케이션 개발을 위한 포괄적인 프로그래밍 및 구성 모델을 제공한다.
Spring은 Java 엔터프라이즈 개발의 복잡성을 해결하기 위해 탄생했다. 기존에는 복잡한 설정과 무거운 컨테이너 때문에 개발자들이 실제 비즈니스 로직보다 기술적인 부분에 더 많은 시간을 투자해야 했다. Spring은 이러한 문제를 해결하여 개발자가 핵심 업무에 집중할 수 있는 환경을 제공한다.
Spring은 "침입적이지 않은(non-invasive)" 접근 방식을 추구한다. 즉, 애플리케이션 코드가 프레임워크에 종속되지 않도록 설계되었다. 이를 통해 코드의 재사용성과 테스트 용이성을 크게 향상시켰다.
제어의 역전은 Spring의 가장 핵심적인 개념이다. 전통적인 프로그래밍에서는 객체가 자신이 사용할 다른 객체를 직접 생성하고 관리했다. 하지만 IoC에서는 이러한 제어권이 외부 컨테이너로 넘어간다.
일반적으로 객체 A가 객체 B를 사용해야 할 때, A 내부에서 B를 직접 생성한다. 이 방식은 두 객체 사이에 강한 결합도를 만들어낸다. 만약 B 대신 C를 사용하고 싶다면, A의 코드를 직접 수정해야 한다. 이는 코드의 유연성을 떨어뜨리고 테스트를 어렵게 만든다.
Spring IoC 컨테이너는 객체의 생성과 생명주기를 관리한다. 개발자는 설정 정보를 통해 객체 간의 관계를 정의하고, 컨테이너가 실행 시점에 이를 해석하여 적절한 객체를 생성하고 연결해준다. 이를 통해 코드 변경 없이도 객체 간의 관계를 변경할 수 있다.
의존성 주입은 IoC를 구현하는 구체적인 방법이다. 객체가 필요로 하는 의존성을 외부에서 제공하는 방식이다.
의존성 주입을 통해 느슨한 결합도를 달성할 수 있다. 이는 코드의 재사용성을 높이고, 단위 테스트를 용이하게 만들며, 시스템의 유연성을 크게 향상시킨다.
AOP는 횡단 관심사(Cross-cutting Concerns)를 모듈화하는 프로그래밍 패러다임이다. 애플리케이션에서 핵심 비즈니스 로직과 별개로 전체에 걸쳐 적용되는 공통 기능들을 효율적으로 관리할 수 있게 해준다.
일반적인 엔터프라이즈 애플리케이션에서는 로깅, 보안, 트랜잭션 관리, 성능 모니터링 등의 기능이 여러 모듈에 걸쳐 반복적으로 나타난다. 이러한 코드들이 비즈니스 로직과 섞이면 코드의 가독성이 떨어지고 유지보수가 어려워진다.
Spring AOP는 프록시 기반으로 동작한다. 핵심 비즈니스 로직을 담은 객체를 프록시로 감싸서, 메서드 호출 전후에 공통 기능을 자동으로 실행한다. 이를 통해 비즈니스 로직에서는 순수한 업무 코드만 남게 되고, 공통 기능은 별도 모듈에서 관리할 수 있다.
POJO(Plain Old Java Object)는 특별한 제약이나 요구사항 없이 작성할 수 있는 일반적인 Java 객체를 의미한다. Spring은 POJO 기반 개발을 적극 지원한다.
POJO를 사용하면 프레임워크에 종속되지 않는 코드를 작성할 수 있다. 이는 코드의 이식성을 높이고, 단위 테스트를 쉽게 만들며, 객체지향 설계 원칙을 자유롭게 적용할 수 있게 해준다. 또한 복잡한 상속 구조나 인터페이스 구현 없이도 Spring의 기능을 활용할 수 있다.
기존 Java EE의 EJB 같은 기술들은 특정 인터페이스를 구현하거나 특정 클래스를 상속받도록 강제했다. 이는 코드를 프레임워크에 종속시키고, 테스트를 어렵게 만들었다. Spring은 이러한 제약을 제거하여 더 자유롭고 유연한 개발이 가능하게 했다.
Spring Framework는 강력한 기능을 제공했지만, 초기 설정의 복잡성이라는 큰 장벽이 있었다. 개발자들은 실제 비즈니스 로직을 구현하기 전에 수많은 설정 파일을 작성하고, 라이브러리 의존성을 관리하며, 서버 환경을 구축해야 했다.
Spring Boot는 "설정보다는 관례(Convention over Configuration)" 원칙을 따라 Spring 애플리케이션의 개발과 배포를 간소화한다.
기존 Spring 프로젝트에서는 web.xml, applicationContext.xml, servlet-context.xml 등 다양한 설정 파일이 필요했다. 각 파일은 서로 연관되어 있어 하나를 잘못 설정하면 전체 애플리케이션이 동작하지 않았다.
Maven이나 Gradle로 의존성을 관리할 때, 각 라이브러리의 호환 가능한 버전을 찾는 것이 쉽지 않았다. 특히 Spring의 여러 모듈과 서드파티 라이브러리 간의 버전 호환성을 맞추는 것은 전문적인 지식을 요구했다.
웹 애플리케이션을 배포하려면 별도의 WAS(Web Application Server)를 설치하고 설정해야 했다. 이는 개발 환경과 운영 환경 간의 차이를 만들어내고, 배포 과정을 복잡하게 만들었다.
Spring Boot의 자동 구성은 클래스패스에 있는 라이브러리들을 분석하여 적절한 설정을 자동으로 적용하는 기능이다.
자동 구성은 다양한 조건을 확인한 후 적용된다. 예를 들어, 특정 클래스가 클래스패스에 존재하는지, 특정 Bean이 이미 정의되어 있는지, 특정 프로퍼티가 설정되어 있는지 등을 확인한다. 이를 통해 개발자의 의도를 존중하면서도 합리적인 기본값을 제공한다.
Spring Boot는 개발자가 명시적으로 정의한 설정을 항상 우선시한다. 자동 구성은 개발자가 별도로 설정하지 않은 부분에 대해서만 적용되므로, 필요한 경우 언제든지 커스터마이징할 수 있다.
스타터는 특정 기능에 필요한 모든 의존성을 하나의 패키지로 묶어 제공하는 Spring Boot의 핵심 기능이다.
각 스타터는 해당 기능에 필요한 핵심 라이브러리들과 호환되는 버전의 의존성들을 포함한다. 또한 자동 구성 클래스도 함께 제공하여, 라이브러리를 추가하는 것만으로도 해당 기능을 바로 사용할 수 있게 해준다.
Spring Boot의 BOM(Bill of Materials)이 각 스타터에 포함된 라이브러리들의 버전을 관리한다. 이를 통해 개발자는 개별 라이브러리의 버전을 신경 쓸 필요 없이, Spring Boot 버전만 관리하면 된다.
Spring Boot는 Tomcat, Jetty, Undertow 등의 서블릿 컨테이너를 애플리케이션에 내장할 수 있다.
내장 서버 덕분에 Spring Boot 애플리케이션은 일반적인 Java 애플리케이션처럼 java -jar 명령어로 실행할 수 있다. 이는 별도의 서버 설치나 WAR 파일 배포 과정을 생략할 수 있게 해준다.
내장 서버 방식은 각 서비스가 독립적으로 실행될 수 있어 마이크로서비스 아키텍처에 매우 적합하다. 또한 컨테이너 기반 배포에서도 큰 장점을 제공한다.
Spring Boot Actuator는 운영 환경에서 필요한 모니터링과 관리 기능을 제공한다.
이러한 기능들은 별도의 모니터링 도구 없이도 애플리케이션의 상태를 파악하고 문제를 진단할 수 있게 해준다.