13. 스프링 프레임워크 시작하기
웹 애플리케이션 개발에서 프레임워크의 역할과 스프링 프레임워크의 핵심 개념(IoC, DI, AOP)을 다룬다.
1. 프레임워크란
프레임워크(Framework)는 애플리케이션 개발에 필요한 기능을 클래스나 인터페이스로 미리 만들어 제공하는 반제품이다. 개발자는 프레임워크가 정한 구조 위에서 비즈니스 로직만 채워넣으면 된다.
라이브러리 vs 프레임워크
| 구분 | 라이브러리 | 프레임워크 |
|---|---|---|
| 제어 흐름 | 개발자가 호출 | 프레임워크가 호출 |
| 구조 결정 | 자유롭게 설계 | 정해진 구조를 따름 |
| 예시 | JDBC, Gson | Spring, Struts |
라이브러리 프레임워크
개발자 코드 프레임워크
┌──────────┐ ┌──────────┐
│ main() │ │ 흐름 제어 │
│ ┌─────┐ │ │ ┌─────┐ │
│ │ lib │ │ │ │개발자│ │
│ │호출 │ │ │ │코드 │ │
│ └─────┘ │ │ └─────┘ │
└──────────┘ └──────────┘
개발자가 제어 프레임워크가 제어프레임워크를 사용하면 일정한 기준에 따라 개발이 이루어지므로 개발 생산성과 품질이 보장된다. 팀 단위 개발에서 코드 일관성을 유지하기 쉬운 것도 장점이다.
2. 스프링 프레임워크란
스프링 프레임워크(Spring Framework)는 자바 웹 애플리케이션 개발을 위한 오픈 소스 경량 프레임워크다. 기존 EJB의 무거움과 복잡함을 개선하여 등장했다.
EJB vs 스프링
| 구분 | EJB | 스프링 |
|---|---|---|
| 무게 | 무거움 (헤비웨이트) | 가벼움 (경량) |
| 학습 곡선 | 높음 | 상대적으로 낮음 |
| 컨테이너 | EJB 전용 서버 필요 | 톰캣 등 서블릿 컨테이너로 충분 |
| POJO 지원 | 특정 인터페이스 구현 강제 | POJO 기반 개발 가능 |
| 테스트 | 어려움 | 단위 테스트 용이 |
POJO(Plain Old Java Object) 란 특정 프레임워크에 종속되지 않은 순수한 자바 객체를 말한다. 스프링은 POJO 기반으로 동작하므로, 비즈니스 로직이 프레임워크에 묶이지 않아 테스트와 유지보수가 쉽다.
스프링의 핵심 특징
| 특징 | 설명 |
|---|---|
| 경량 컨테이너 | 빈(Bean)의 생성·초기화·소멸을 스프링이 직접 관리 |
| IoC | 객체 생성 권한을 프레임워크에 위임 |
| DI | 필요한 객체를 프레임워크가 주입 |
| AOP | 공통 관심사를 분리하여 모듈성 향상 |
| 다양한 연동 | MyBatis, JPA, JDBC 등 영속성 프레임워크 연동 지원 |
3. 스프링 컨테이너와 IoC
컨테이너란
컨테이너는 객체의 생명주기를 관리하는 실행 환경이다. 톰캣이 서블릿 컨테이너로서 서블릿의 생성·초기화·소멸을 관리하듯, 스프링 컨테이너는 빈(Bean) 의 라이프사이클을 관리한다.
톰캣 (서블릿 컨테이너)
┌──────────────────┐
│ 서블릿 관리 │
│ │
│ 생성 → 초기화 │
│ → 서비스 → 소멸 │
└──────────────────┘
스프링 (빈 컨테이너)
┌──────────────────┐
│ 빈(Bean) 관리 │
│ │
│ 생성 → 의존성 주입 │
│ → 사용 → 소멸 │
└──────────────────┘IoC(제어 역행)란
기존 방식에서는 개발자가 직접 객체를 생성하고 관계를 설정했다. IoC에서는 이 제어권이 프레임워크로 역전된다.
// 기존 방식: 개발자가 직접 생성
MemberDAO dao = new MemberDAO();
MemberService service = new MemberService();
service.setDao(dao); // 직접 관계 설정
// IoC 방식: 프레임워크가 생성 + 주입
// 개발자는 설정만 하고, 객체 생성은 스프링이 담당 기존 방식 IoC 방식
개발자 스프링 컨테이너
│ │
▼ ▼
new 객체() 설정 파일 읽기
│ │
▼ ▼
관계 설정 빈 자동 생성
│ │
▼ ▼
사용 의존성 주입
│
▼
사용4. DI(의존성 주입)
DI(Dependency Injection)는 IoC의 구체적인 구현 방법이다. 객체가 필요로 하는 의존 객체를 개발자가 코드로 생성하지 않고, 스프링 컨테이너가 외부에서 주입한다.
DI가 없는 경우 vs 있는 경우
// DI 없음: 직접 생성 (강한 결합)
public class MemberService {
private MemberDAO dao = new MemberDAO();
}
// DI 적용: 외부에서 주입 (느슨한 결합)
public class MemberService {
private MemberDAO dao;
// setter를 통해 외부에서 주입
public void setDao(MemberDAO dao) {
this.dao = dao;
}
}결합도 비교
| 구분 | 직접 생성 | DI 사용 |
|---|---|---|
| 결합도 | 강한 결합 | 느슨한 결합 |
| 구현체 교체 | 코드 수정 필요 | 설정만 변경 |
| 테스트 | Mock 주입 어려움 | Mock 쉽게 주입 |
| 유연성 | 낮음 | 높음 |
강한 결합 (직접 생성)
┌──────────┐
│ Service │
│ ┌──────┐ │
│ │new │ │
│ │DAO() │ │ ← DAO가 바뀌면
│ └──────┘ │ Service도 수정
└──────────┘
느슨한 결합 (DI)
┌──────────┐ 주입 ┌──────┐
│ Service │◄──────│ 스프링 │
│ │ │컨테이너│
│ dao 필드 │ │ │
└──────────┘ └──┬───┘
│생성
┌──┴───┐
│ DAO │
└──────┘DI의 핵심은 인터페이스 기반 설계와 함께 사용할 때 극대화된다. 구체 클래스가 아닌 인터페이스에 의존하면, 구현체를 교체해도 사용하는 쪽 코드를 변경할 필요가 없다.
DI 주입 방식
스프링은 두 가지 주입 방식을 지원한다.
1. setter 주입
public class MemberService {
private MemberDAO dao;
public void setDao(MemberDAO dao) {
this.dao = dao;
}
}<bean id="dao"
class="com.example.MemberDAO" />
<bean id="service"
class="com.example.MemberService">
<property name="dao" ref="dao" />
</bean>2. 생성자 주입
public class MemberService {
private final MemberDAO dao;
public MemberService(MemberDAO dao) {
this.dao = dao;
}
}<bean id="dao"
class="com.example.MemberDAO" />
<bean id="service"
class="com.example.MemberService">
<constructor-arg ref="dao" />
</bean>| 방식 | 장점 | 단점 |
|---|---|---|
| setter 주입 | 선택적 의존성에 적합 | 불변 보장 불가 |
| 생성자 주입 | 불변 보장, 필수 의존성 강제 | 매개변수 많아지면 복잡 |
실무에서는 생성자 주입을 권장한다.
final 키워드로 불변성을 보장할 수 있고, 필수 의존성이 누락되면 컴파일 시점에 오류를 잡을 수 있다.5. 스프링의 주요 모듈
스프링 프레임워크는 여러 모듈로 구성되어 있으며, 필요한 모듈만 선택하여 사용할 수 있다.
| 모듈 | 설명 |
|---|---|
| Core | IoC 기능 제공, 설정을 분리하는 기반 모듈 |
| Context | 빈(Bean)에 대한 접근 방법 제공 |
| DAO | JDBC를 더 편리하게 사용하도록 지원 |
| ORM | Hibernate, MyBatis 등 영속성 프레임워크 연동 |
| AOP | 관점 지향 프로그래밍 기능 제공 |
| Web | 웹 애플리케이션 개발에 필요한 기능 |
| WebMVC | MVC 패턴 구현 기능 제공 |
스프링 프레임워크 모듈 구조
┌──────────────────┐
│ Web / MVC │ ← 웹 계층
├──────────────────┤
│ AOP │ ORM│ DAO │ ← 중간 계층
├──────────────────┤
│ Context (빈 관리) │ ← 기반 계층
├──────────────────┤
│ Core (IoC/DI) │ ← 핵심
└──────────────────┘6. AOP(관점 지향 프로그래밍)
AOP(Aspect-Oriented Programming)는 핵심 기능과 부가 기능을 분리하여 모듈성을 높이는 기법이다.
핵심 기능 vs 부가 기능
public void addMember(MemberVO member) {
// --- 부가 기능 (로깅) ---
log.info("회원 등록 시작");
// --- 부가 기능 (트랜잭션) ---
tx.begin();
// ★ 핵심 기능 (비즈니스 로직) ★
dao.insertMember(member);
// --- 부가 기능 (트랜잭션) ---
tx.commit();
// --- 부가 기능 (로깅) ---
log.info("회원 등록 완료");
}AOP 적용 전후 비교
AOP 적용 전
┌────────────────┐
│ 회원 등록 │
│ - 로깅 │
│ - 트랜잭션 │
│ - 핵심 로직 │
│ - 트랜잭션 │
│ - 로깅 │
└────────────────┘
┌────────────────┐
│ 회원 조회 │
│ - 로깅 │ ← 중복!
│ - 핵심 로직 │
│ - 로깅 │
└────────────────┘
AOP 적용 후
┌────────────┐
│ 로깅 Aspect │──┐
└────────────┘ │적용
┌────────────┐ ▼
│ 회원 등록 │ 핵심 로직만
└────────────┘
┌────────────┐ ▲
│ TX Aspect │──┘적용
└────────────┘AOP 주요 용어
| 용어 | 설명 |
|---|---|
| Aspect | 공통 관심사를 모듈화한 것 (로깅, 트랜잭션 등) |
| Advice | 실제 수행할 부가 기능 코드 |
| JoinPoint | Advice를 적용할 수 있는 지점 (메서드 호출 등) |
| Pointcut | Advice를 적용할 대상을 선정하는 표현식 |
| Weaving | Aspect를 핵심 코드에 적용하는 과정 |
스프링에서 AOP를 가장 자주 사용하는 곳은 트랜잭션 관리와 로깅이다.
@Transactional 애노테이션이 대표적인 AOP 활용 사례다.7. 스프링 MVC 구조 개요
스프링은 MVC(Model-View-Controller) 패턴을 기반으로 웹 애플리케이션을 개발한다.
구성 요소
| 구성 요소 | 역할 |
|---|---|
| DispatcherServlet | 모든 요청을 받는 FrontController |
| HandlerMapping | 요청 URL에 맞는 Controller 검색 |
| Controller | 비즈니스 로직 수행, Model 생성 |
| ViewResolver | 뷰 이름으로 실제 뷰 파일 탐색 |
| View | 결과를 화면에 표시 (JSP 등) |
요청 처리 흐름
스프링 MVC 요청 흐름
클라이언트
│ ① 요청
▼
┌────────────────┐
│ Dispatcher │
│ Servlet │
└──┬───┬───┬─────┘
│ │ │
│ │ │ ② 핸들러 검색
│ │ ▼
│ │ ┌──────────────┐
│ │ │HandlerMapping│
│ │ └──────────────┘
│ │
│ │ ③ 처리 위임
│ ▼
│ ┌──────────────┐
│ │ Controller │
│ │ (핵심 로직) │
│ └──────────────┘
│
│ ④ 뷰 이름 반환
▼
┌────────────────┐
│ ViewResolver │
└──────┬─────────┘
│ ⑤ 뷰 렌더링
▼
┌────────────────┐
│ View (JSP) │
└──────┬─────────┘
│ ⑥ 응답
▼
클라이언트서블릿 기반 MVC vs 스프링 MVC 비교
| 구분 | 서블릿 기반 MVC | 스프링 MVC |
|---|---|---|
| Front Controller | 직접 구현 | DispatcherServlet 제공 |
| URL 매핑 | web.xml에 수동 등록 | @RequestMapping으로 선언 |
| 요청/응답 처리 | HttpServletRequest 직접 사용 | 메서드 매개변수로 자동 바인딩 |
| 뷰 연결 | RequestDispatcher 직접 사용 | ViewResolver가 자동 처리 |
| 비즈니스 로직 | 서블릿 클래스에 혼재 | Controller에 분리 |
스프링 MVC를 사용하기 전에 서블릿 기반 MVC 패턴을 이해하는 것이 중요하다. DispatcherServlet도 결국 서블릿이며, 내부적으로 서블릿 API를 활용하기 때문이다.
요약
핵심 개념 정리
| 개념 | 한 줄 정리 |
|---|---|
| 프레임워크 | 개발에 필요한 기능을 미리 제공하는 반제품 |
| IoC | 객체의 생성과 관리 권한을 프레임워크에 위임 |
| DI | 필요한 의존 객체를 외부에서 주입 |
| AOP | 공통 관심사(로깅, 트랜잭션)를 핵심 로직에서 분리 |
| 스프링 MVC | DispatcherServlet 기반의 웹 MVC 프레임워크 |
기존 방식 vs 스프링 방식
| 기능 | 기존 방식 | 스프링 방식 |
|---|---|---|
| 객체 생성 | new 키워드 | 컨테이너가 생성 (IoC) |
| 의존성 설정 | 직접 코드로 연결 | 설정으로 자동 주입 (DI) |
| 공통 기능 | 각 메서드에 중복 작성 | Aspect로 분리 (AOP) |
| 웹 요청 처리 | 서블릿 직접 구현 | DispatcherServlet + Controller |