1. 스프링 개요
1.1 소개
스프링은 소프트웨어 개발 분야에 '봄'을 선사한 프레임워크로, 기존 기술들을 통합하여 사용을 쉽게 만든다.
- 2002년 초반, 인터페이스21 프레임워크를 기반으로 설계 시작
- 2004년 3월 24일, 공식 버전 1.0 발표
- 창립자: 로드 존슨 (시드니 대학교 음악학 박사, 컴퓨터 전공)
- 핵심 철학: 기존 기술을 보다 쉽게 활용할 수 있도록 하는 것
- 대표적인 기술 조합: SSM (Spring MVC + Spring + MyBatis), SSH (Struts + Spring + Hibernate)
1.2 장점
- 오픈소스이자 무료로 제공되는 경량 컨테이너 프레임워크
- 비침입적 설계, 라이브러리 종속성 최소화
- 제어 반전(IOC)과 관심사 분리(AOP) 지원
- 트랜잭션 처리 및 다양한 외부 프레임워크와의 원활한 통합
결론적으로, 스프링은 경량의 제어 반전(IOC)과 관심사 분리(AOP) 기반의 개발 프레임워크이다.
1.3 구성 요소
스프링은 여러 모듈로 구성되며, 주요 컴포넌트는 다음과 같다:
- 스프링 컨텍스트 (Context): IOC 및 빈 관리 중심
- 스프링 AOP: 가교 기능, 공통 로직 추출
- 스프링 웹 모듈 (Web, WebMvc): 웹 애플리케이션 개발 지원
- 스프링 데이터 (Data): 데이터 접근 계층 간편화
- 스프링 트랜잭션: 다중 데이터베이스 트랜잭션 관리
1.4 확장 기술
현대 자바 개발의 중심은 스프링 기반의 생태계이다.
- 스프링 부트 (Spring Boot): 빠른 설정 기반의 초기화 툴, "약속이 구성을 대체한다"는 원칙 적용
- 스프링 클라우드 (Spring Cloud): 마이크로서비스 아키텍처를 위한 도구 모음
현재 대부분의 기업은 스프링 부트를 활용해 단일 서비스를 신속하게 개발하고 있다. 이는 스프링 및 스프링 MVC의 깊은 이해가 필수라는 의미이다.
단, 장기적인 발전 과정에서 원래의 단순성 원칙이 훼손되며, 복잡한 설정으로 인해 "설정의 지옥"이라는 비판도 존재한다.
2. IOC 이론의 도출
기존 방식에서는 클래스 간 의존성이 고정되어 있어, 변경 시 코드 수정이 필수적이었다. 이를 개선하기 위한 방법을 살펴보자.
DAO 계층 정의
package com.example.dao;
public interface UserDao {
void fetchUser();
}
DAO 구현 클래스
package com.example.dao;
public class UserDaoMySQLImpl implements UserDao {
@Override
public void fetchUser() {
System.out.println("MySQL에서 사용자 정보를 가져옵니다.");
}
}
서비스 계층 인터페이스
package com.example.service;
public interface UserService {
void executeUserFetch();
}
서비스 구현 클래스 (의존성 역전 적용 전후 비교)
package com.example.service;
import com.example.dao.UserDao;
import com.example.dao.UserDaoMySQLImpl;
public class UserServiceImpl implements UserService {
// 기존 방식: 객체 생성 고정 → 유연성 없음
// private UserDao userDao = new UserDaoMySQLImpl();
// 개선된 방식: 외부에서 의존성 주입
private UserDao userDao;
// setter를 통해 동적 주입 가능
public void setUserDao(UserDao dao) {
this.userDao = dao;
}
@Override
public void executeUserFetch() {
userDao.fetchUser();
}
}
새로운 데이터베이스 구현 추가
package com.example.dao;
public class UserDaoOracleImpl implements UserDao {
@Override
public void fetchUser() {
System.out.println("Oracle에서 사용자 정보를 조회합니다.");
}
}
테스트 클래스 (의존성 주입 확인)
package com.example.test;
import com.example.service.UserService;
import com.example.service.UserServiceImpl;
import com.example.dao.UserDaoOracleImpl;
public class ApplicationTest {
public static void main(String[] args) {
UserService service = new UserServiceImpl();
// 런타임 시에 실제 구현체를 주입
((UserServiceImpl) service).setUserDao(new UserDaoOracleImpl());
service.executeUserFetch(); // 출력: "Oracle에서 사용자 정보를 조회합니다."
}
}
핵심 개념: 제어 반전 (IoC)
- 기존 방식: 개발자가 직접 객체를 생성 → 제어권은 개발자에게 있음
- 스프링 방식: 객체 생성을 외부(컨테이너)에 위임 → 개발자는 필요한 의존성만 정의하면 됨
이 변화는 시스템의 결합도를 크게 낮추고, 유지보수와 확장성을 극대화한다.
IoC의 본질
제어 반전은 객체 생성 및 관리의 책임을 프로그램 자체에서 외부 컨테이너로 옮기는 설계 원칙이다.
- DI (Dependency Injection, 의존성 주입)는 IoC를 구현하는 방법 중 하나
- XML 또는 어노테이션을 통해 빈 정의를 기술하고, 컨테이너가 빈을 생성·주입
- 어노테이션 기반 설정 시, 구현 클래스에 빈 정보를 포함하여 완전한 자동 설정 가능
스프링에서 제어 반전은 IoC 컨테이너를 통해 이루어지며, 주로 의존성 주입 방식으로 구현된다.