📚 전체 맵🗺 로드맵
Complete Visual Guide

Spring Framework

컨테이너부터 AOP까지 — 연결된 흐름으로 이해하는 스프링 핵심 개념

전체 구조 한눈에 보기

스프링은 "공장(컨테이너)"이 부품(빈)을 찍어내고, 조립해서, 제공하는 공장 시스템이다. 아래 흐름이 스프링의 전부다.

Spring 핵심 흐름도
📝
Java Class
개발자가 직접 작성하는
순수 자바 클래스
🏷️
@Component 등
어노테이션 부착
@Service @Repository
@Controller @Bean
스캔 / 등록
🏭
ApplicationContext (IoC Container)
빈 정의를 읽고 → 객체를 생성하고 → 의존성을 주입하고 → 관리하는 핵심 공장
BeanFactory 의존성 주입(DI) 빈 생명주기 관리
인스턴스화 + DI
🫘
Bean A
컨테이너가
관리하는 객체
🫘
Bean B
Bean A 를
자동 주입받음
🫘
Bean C
Bean B 를
자동 주입받음
getBean() / @Autowired
🖥️
클라이언트 코드
직접 new 하지 않고
컨테이너에서 받아서 사용
핵심 개념 상세 해설

각 개념을 비유, 설명, 코드 예제와 함께 완전히 이해하자.

📐
개념 01
클래스 (Class)
Java Class
비유: 설계도면 — 집을 어떻게 지을지 그린 청사진. 아직 실체가 없다.

클래스는 객체를 만들기 위한 설계도다. 어떤 필드(상태)와 메서드(행동)를 가질지 정의하지만, 그 자체로는 메모리에 존재하지 않는다. 스프링 없이도 존재하는 순수 자바 개념.

UserService.java
// 클래스 = 설계도. 아직 실체(객체)가 없다.
public class UserService {
    private final UserRepository userRepository;

    UserService(UserRepository repo) {
        this.userRepository = repo;
    }

    public User findUser(Long id) {
        return userRepository.findById(id);
    }
}
🏠
개념 02
객체 (Object)
Instance
비유: 실제로 지어진 집 — 설계도(클래스)를 바탕으로 메모리에 실제로 만들어진 실체. new 키워드로 생성.

클래스(설계도)를 기반으로 new 키워드로 메모리에 실제 생성된 실체. 같은 클래스로 여러 객체를 만들 수 있다. 스프링이 없다면 개발자가 직접 new로 생성하고 관리해야 한다.

Main.java (스프링 없이)
// 객체 = 클래스를 new해서 실체화한 것
UserRepository repo = new UserRepository();
UserService service = new UserService(repo);
// 문제: 개발자가 직접 의존성을 연결해야 함 😫
// 객체가 늘어날수록 연결이 복잡해짐
🏭
개념 03
컨테이너 (IoC Container)
ApplicationContext
비유: 스마트 공장 — 어떤 부품(빈)이 필요한지 알고, 자동으로 만들어 조립해서 필요한 곳에 배달해주는 자동화 공장.

스프링의 핵심 심장. 빈의 생성·의존성 주입·생명주기 관리를 모두 담당한다. 개발자가 new로 직접 객체를 만들 필요가 없어진다. IoC(제어의 역전)는 "내가 객체를 만드는 것"이 아니라 "컨테이너가 만들어주는 것"을 뜻한다.

IoC (Inversion of Control, 제어의 역전)
기존: 내가 필요한 객체를 직접 만든다 (new)
IoC: 컨테이너가 알아서 만들어 주입해준다 → 제어권이 뒤집힘
ApplicationContext 사용
// 스프링 컨테이너 생성 및 시작
ApplicationContext ctx =
    new AnnotationConfigApplicationContext(AppConfig.class);

// 컨테이너가 만든 빈을 꺼내서 사용
UserService svc = ctx.getBean(UserService.class);
// → 컨테이너가 UserRepository도 알아서 주입해준 상태
🫘
개념 04
빈 (Bean)
Spring Bean
비유: 공장에서 등록·관리되는 부품 — 공장(컨테이너)의 관리 대장에 올라간 객체. 일반 객체(Object)와 달리 컨테이너가 생명주기 전체를 책임진다.

컨테이너가 관리하는 객체. 일반 자바 객체와 기술적으로 같지만, 스프링 컨테이너에 등록되어 관리 받는다는 점이 핵심 차이. 기본적으로 싱글톤(하나만 생성)이다.

두 가지 빈 등록 방법
// 방법 1: Java Config로 직접 등록
@Configuration
public class AppConfig {
    @Bean  // 이 메서드가 반환하는 객체를 빈으로 등록
    public UserService userService() {
        return new UserService(userRepository());
    }
}

// 방법 2: 어노테이션으로 자동 등록 (더 많이 씀)
@Service  // 이 클래스를 빈으로 자동 등록!
public class UserService { ... }
싱글톤 기본 컨테이너 소유 생명주기 관리
🏷️
개념 05
컴포넌트 (@Component)
Component Scan
비유: 공장 출입증 — 클래스에 출입증(@Component 등)을 붙이면, 공장(컨테이너)이 자동으로 스캔해서 부품(빈)으로 등록해준다. 출입증 없으면 들어갈 수 없다.

@Component는 "이 클래스를 스프링 빈으로 등록해줘"라는 마커 어노테이션. 컨테이너가 시작할 때 지정된 패키지를 스캔(Component Scan)하여 어노테이션이 붙은 클래스를 자동으로 빈으로 등록한다.

어노테이션 계층 구조
// @Component의 특화된 자식들
@Component    // 일반 컴포넌트 (기반)
@Service      // 비즈니스 로직 레이어 (= @Component)
@Repository   // 데이터 접근 레이어 (예외 변환 추가)
@Controller   // 웹 요청 처리 레이어
@RestController // @Controller + @ResponseBody

// 사용 예시
@Service
public class OrderService {
    // 스프링이 자동으로 빈 등록 완료!
}
💉
개념 06
의존성 주입 (DI)
Dependency Injection
비유: 배달 서비스 — 내가 마트(new)에 가서 식재료를 사오는 대신, 컨테이너가 내 집(클래스)에 필요한 재료(의존 객체)를 배달해준다. 나는 요리(비즈니스 로직)에만 집중!

객체가 필요로 하는 의존 객체를 직접 생성하지 않고 외부(컨테이너)에서 주입받는 패턴. 결합도를 낮추고 테스트를 쉽게 만든다. 3가지 방식이 있으며 생성자 주입이 권장된다.

DI 3가지 방식
// ✅ 권장: 생성자 주입 (불변, 테스트 용이)
@Service
public class OrderService {
    private final PaymentService paymentService;

    public OrderService(PaymentService ps) {
        this.paymentService = ps; // 컨테이너가 주입
    }
}

// ⚠️ 필드 주입 (편하지만 비권장)
@Autowired
private PaymentService paymentService;

// ⚠️ 수정자 주입 (선택적 의존성에 사용)
@Autowired
public void setPaymentService(PaymentService ps) {
    this.paymentService = ps;
}
🔪
개념 07
AOP (관점 지향 프로그래밍)
Aspect Oriented Programming
비유: 고속도로 톨게이트 — 모든 차(메서드 호출)가 지나갈 때 자동으로 요금 징수(로깅/트랜잭션). 각 차에 직접 요금 내는 코드를 심지 않아도 된다.

로깅, 트랜잭션, 보안 등 여러 곳에 반복되는 공통 관심사를 핵심 비즈니스 로직과 분리하는 기법. @Transactional이 대표적 AOP 활용 사례.

AOP 예시
// AOP 없이: 모든 메서드마다 로그 코드 복붙 😫
public Order createOrder(...) {
    log.info("메서드 시작");
    // 비즈니스 로직
    log.info("메서드 끝");
}

// AOP로: 어노테이션 하나면 끝 ✅
@Aspect
@Component
public class LoggingAspect {
    @Around("execution(* com.example.service.*.*(..))")
    public Object log(ProceedingJoinPoint pjp) throws Throwable {
        log.info("시작: {}", pjp.getSignature());
        Object result = pjp.proceed(); // 실제 메서드 실행
        log.info("완료");
        return result;
    }
}
🔢
개념 08
빈 스코프 (Bean Scope)
Singleton · Prototype · Request
비유: 카페 컵 — 싱글톤은 "매장 공용 컵(한 개만 존재)", 프로토타입은 "일회용 컵(요청마다 새 것)", Request는 "한 손님의 주문 처리 동안만 쓰는 컵".

빈이 몇 개의 인스턴스로 존재할지, 얼마나 살아있을지 결정하는 설정.

singleton
기본값. 컨테이너당 1개만 존재. 모든 요청에 같은 객체 반환.
prototype
요청할 때마다 새 객체 생성. 이후 관리는 클라이언트가.
request
HTTP 요청 당 하나. 요청 완료시 소멸. (웹 전용)
session
HTTP 세션 당 하나. (웹 전용)
Scope 설정
@Component
@Scope("prototype") // 기본은 singleton
public class MyPrototypeBean { ... }
🔄
개념 09
빈 생명주기
Bean Lifecycle
비유: 직원 채용과 퇴직 — 스프링이 직원을 채용(생성)하고, 입사 교육(초기화 콜백)을 하고, 일을 시키다가, 퇴직 처리(소멸 콜백)를 해준다.

컨테이너가 빈을 관리하는 전체 과정. 초기화/소멸 시점에 커스텀 로직(DB 연결, 자원 해제 등)을 끼워 넣을 수 있다.

생명주기 콜백
@Component
public class DatabaseConnector {
    @PostConstruct  // 빈 생성 + DI 완료 후 호출
    public void init() {
        // DB 커넥션 풀 초기화
        System.out.println("초기화 완료!");
    }

    @PreDestroy  // 컨테이너 종료 전 호출
    public void destroy() {
        // DB 연결 종료, 자원 반납
        System.out.println("자원 반납!");
    }
}
빈 생명주기 흐름

스프링 컨테이너 시작부터 종료까지 빈이 어떤 과정을 거치는지 순서대로 따라가 보자.

STEP 01
컨테이너 시작
설정 파일/
어노테이션 로드
STEP 02
빈 정의 등록
BeanDefinition
메타데이터 저장
STEP 03
빈 인스턴스화
new로 객체
생성 (리플렉션)
STEP 04
의존성 주입
@Autowired
필드/생성자 주입
STEP 05
@PostConstruct
초기화
콜백 실행
STEP 06
빈 사용
실제 서비스
로직 수행
STEP 07
@PreDestroy
소멸 전
콜백 실행
STEP 08
빈 소멸
컨테이너 종료,
GC 처리
개념 비교 정리표

헷갈리는 개념들을 한 표로 비교해서 차이를 확실히 잡자.

개념 무엇인가 스프링과의 관계 핵심 키워드
Class 설계도 순수 Java 개념. 스프링과 무관하게 존재 설계, 청사진, 타입
Object 설계도로 만든 실체 new로 생성. 컨테이너 없이도 존재 가능 인스턴스, new, 메모리
Bean 컨테이너가 관리하는 객체 컨테이너에 등록된 특별한 객체. 싱글톤 기본 등록, 관리, 싱글톤
@Component 빈 자동 등록 마커 클래스를 빈으로 자동 등록하는 어노테이션 어노테이션, 스캔, 자동화
Container 빈 공장 + 관리자 스프링의 핵심. 빈 생성·DI·생명주기 전담 IoC, ApplicationContext
DI 의존 객체 자동 배달 컨테이너가 빈 간의 의존성을 자동 연결 @Autowired, 결합도 낮춤
AOP 공통 관심사 분리 로깅·트랜잭션을 핵심 로직과 분리하는 기법 @Aspect, @Around, 프록시
전체 흐름 실전 코드

회원가입 시나리오로 클래스 → 컴포넌트 → 빈 → DI → 서비스 전 과정을 코드로 따라가보자.

실전 시나리오: 회원가입
Spring IoC Container
@Controller
UserController
웹 요청 처리
@Service
UserService
비즈니스 로직
@Repository
UserRepository
DB 접근
↑ 컨테이너가 각 빈을 생성하고 화살표 방향으로 DI(의존성 주입) 자동 처리
실전 코드 — 3 레이어 전체 흐름
/* ─── 1. Repository: DB 접근 담당 ─── */
@Repository  // @Component + 예외 변환
public class UserRepository {
    public void save(User user) {
        // JPA / JDBC로 DB 저장
    }
}

/* ─── 2. Service: 비즈니스 로직 ─── */
@Service  // @Component의 특화 버전
public class UserService {
    private final UserRepository userRepository;

    // 생성자 주입 (권장 방식)
    // @Autowired 생략 가능 (생성자 1개면 자동)
    public UserService(UserRepository repo) {
        this.userRepository = repo;
    }

    @Transactional  // AOP! 트랜잭션 자동 처리
    public void join(User user) {
        // 비즈니스: 중복 검사 등
        userRepository.save(user);
        // 예외 발생 시 자동 rollback
    }
}

/* ─── 3. Controller: 웹 요청 처리 ─── */
@RestController
@RequestMapping("/users")
public class UserController {
    private final UserService userService;

    public UserController(UserService svc) {
        this.userService = svc; // 컨테이너가 주입
    }

    @PostMapping
    public ResponseEntity join(@RequestBody User user) {
        userService.join(user); // AOP로 트랜잭션 자동!
        return ResponseEntity.ok("가입 완료");
    }
}

/* 개발자가 신경쓸 필요 없는 것들:
   - UserRepository 객체 생성 (new) → 컨테이너가
   - UserService에 Repository 주입 → 컨테이너가
   - UserController에 Service 주입 → 컨테이너가
   - 트랜잭션 시작/커밋/롤백 → AOP(@Transactional)가
   ✅ 개발자는 비즈니스 로직에만 집중! */
📚 전체 맵🗺 로드맵