스프링 프레임워크란?
스프링 프레임워크는 자바 엔터프라이즈 애플리케이션 개발의 복잡성을 줄이기 위해 설계된 강력한 오픈소스 프레임워크입니다. 주요 목표는 객체 지향 원칙을 준수하며 유연하고 테스트 가능한 코드를 작성하도록 돕는 것입니다.
스프링 프레임워크의 대표적인 특징으로는 POJO(Plain Old Java Object) 기반의 경량화 설계와 유연한 아키텍처가 있습니다. 이를 통해 기존 자바 기술(JDBC, JPA 등)과의 높은 호환성을 제공하며, 대규모 애플리케이션부터 소규모 프로젝트까지 다양한 범위에서 활용됩니다.
스프링의 탄생과 철학을 알아봅시다.
자바 엔터프라이즈 애플리케이션 개발은 2000년대 초반까지 매우 복잡하고 어려운 일이었습니다. EJB(Enterprise JavaBeans)라는 무거운 프레임워크를 사용해야 했고, 개발자들은 비즈니스 로직보다 기술적인 문제에 더 많은 시간을 소비해야 했습니다. 이러한 문제를 해결하기 위해 로드 존슨(Rod Johnson)은 2004년 스프링 프레임워크를 발표했습니다.
스프링의 핵심 철학은 "자바 개발을 즐겁게 만든다(Making Java Development A Pleasure)"입니다. 이는 단순한 슬로건이 아닌, 실제로 다음과 같은 구체적인 원칙들로 구현됩니다:
스프링 프레임워크의 주요 특징
POJO(Plain Old Java Object) 기반 개발
- 순수한 자바 객체로 개발하면서도 엔터프라이즈 급의 고급 기능을 구현할 수 있어야 합니다. 예를 들어, 다음과 같은 순수한 자바 클래스도 스프링에서는 완벽하게 동작합니다.
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentService paymentService;
// 단순한 자바 생성자를 통한 의존성 주입
public OrderService(OrderRepository orderRepository, PaymentService paymentService) {
this.orderRepository = orderRepository;
this.paymentService = paymentService;
}
public void createOrder(Order order) {
// 비즈니스 로직에만 집중할 수 있는 깔끔한 코드
paymentService.processPayment(order);
orderRepository.save(order);
}
}
DI(Dependency Injection)와 IoC(Inversion of Control)의 실제적 의미
- 단순히 "의존성을 주입한다"는 표면적 의미를 넘어, 이는 객체 지향 설계의 핵심 원칙인 "고수준 모듈은 저수준 모듈에 의존해서는 안 된다"는 의존관계 역전 원칙(DIP)을 실현하는 방법입니다.
// 인터페이스를 통한 추상화
public interface PaymentGateway {
void processPayment(Order order);
}
// 구체적인 구현체
@Component
public class StripePaymentGateway implements PaymentGateway {
@Override
public void processPayment(Order order) {
// Stripe 결제 처리 로직
}
}
// 고수준 모듈은 추상화에 의존
@Service
public class OrderService {
private final PaymentGateway paymentGateway; // 인터페이스에 의존
public OrderService(PaymentGateway paymentGateway) {
this.paymentGateway = paymentGateway;
}
}
AOP(Aspect-Oriented Programming)의 실용적 적용
- 횡단 관심사(Cross-cutting Concerns)를 분리함으로써 코드의 중복을 제거하고 핵심 비즈니스 로직에 집중할 수 있게 해 줍니다.
@Aspect
@Component
public class PerformanceAspect {
@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
System.out.printf("%s 실행 시간: %d ms%n",
joinPoint.getSignature().getName(),
(endTime - startTime));
return result;
}
}
선언적 트랜잭션의 강력함
- 데이터 일관성을 유지하기 위한 복잡한 트랜잭션 관리를 단순한 선언만으로 해결할 수 있습니다.
@Service
@Transactional
public class BankingService {
private final AccountRepository accountRepository;
public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
Account from = accountRepository.findById(fromAccount);
Account to = accountRepository.findById(toAccount);
from.withdraw(amount);
to.deposit(amount);
accountRepository.save(from);
accountRepository.save(to);
// 모든 작업이 자동으로 하나의 트랜잭션으로 처리됨
}
}
'Spring > Spring 기초 지식' 카테고리의 다른 글
스프링은 Singleton 패턴을 어떻게 활용할까? (0) | 2023.08.07 |
---|---|
스프링의 제어의 역전 (IoC, Inversion of Control) (0) | 2023.08.07 |
[Spring] 의존성 주입(DI - Dependency Injection)과 결합도 낮추기 (0) | 2023.08.07 |
[@Configuration과 @Bean] 스프링 컨테이너의 동작 원리 톺아보기 (0) | 2023.08.07 |
[스프링, 스프링부트] Spring - @Bean과 @Component (0) | 2023.07.20 |