13. 스프링 프레임워크 시작하기

13. 스프링 프레임워크 시작하기

웹 애플리케이션 개발에서 프레임워크의 역할과 스프링 프레임워크의 핵심 개념(IoC, DI, AOP)을 다룬다.


1. 프레임워크란

프레임워크(Framework)는 애플리케이션 개발에 필요한 기능을 클래스나 인터페이스로 미리 만들어 제공하는 반제품이다. 개발자는 프레임워크가 정한 구조 위에서 비즈니스 로직만 채워넣으면 된다.

라이브러리 vs 프레임워크

구분라이브러리프레임워크
제어 흐름개발자가 호출프레임워크가 호출
구조 결정자유롭게 설계정해진 구조를 따름
예시JDBC, GsonSpring, 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. 스프링의 주요 모듈

스프링 프레임워크는 여러 모듈로 구성되어 있으며, 필요한 모듈만 선택하여 사용할 수 있다.

모듈설명
CoreIoC 기능 제공, 설정을 분리하는 기반 모듈
Context빈(Bean)에 대한 접근 방법 제공
DAOJDBC를 더 편리하게 사용하도록 지원
ORMHibernate, MyBatis 등 영속성 프레임워크 연동
AOP관점 지향 프로그래밍 기능 제공
Web웹 애플리케이션 개발에 필요한 기능
WebMVCMVC 패턴 구현 기능 제공
  스프링 프레임워크 모듈 구조

  ┌──────────────────┐
  │     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실제 수행할 부가 기능 코드
JoinPointAdvice를 적용할 수 있는 지점 (메서드 호출 등)
PointcutAdvice를 적용할 대상을 선정하는 표현식
WeavingAspect를 핵심 코드에 적용하는 과정
스프링에서 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공통 관심사(로깅, 트랜잭션)를 핵심 로직에서 분리
스프링 MVCDispatcherServlet 기반의 웹 MVC 프레임워크

기존 방식 vs 스프링 방식

기능기존 방식스프링 방식
객체 생성new 키워드컨테이너가 생성 (IoC)
의존성 설정직접 코드로 연결설정으로 자동 주입 (DI)
공통 기능각 메서드에 중복 작성Aspect로 분리 (AOP)
웹 요청 처리서블릿 직접 구현DispatcherServlet + Controller