[Spring] IoC / DI / AOP 주요 핵심 개념의 이해

슈퍼대디·2024년 12월 24일

CS면접대비

목록 보기
9/13

Spring 핵심 원리 완벽 가이드

목차

  1. IoC (Inversion of Control)
  2. DI (Dependency Injection)
  3. AOP (Aspect Oriented Programming)
  4. Spring Container와 Bean
  5. 면접 예상 질문

1. IoC (Inversion of Control)

IoC의 개념

제어의 역전(IoC)은 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것입니다.

전통적인 프로그래밍:

public class UserService {
    private UserRepository userRepository = new UserRepository(); // 직접 생성
    
    public User getUser(String id) {
        return userRepository.findById(id);
    }
}

IoC 적용:

public class UserService {
    private final UserRepository userRepository; // 외부에서 주입
    
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

IoC의 장점

  1. 의존성 관리

    • 객체 생성과 의존관계 관리를 컨테이너가 담당
    • 결합도 감소, 유연성 증가
  2. 코드 재사용성

    • 컴포넌트 간의 결합도가 낮아져 재사용 용이
  3. 테스트 용이성

    • Mock 객체 등을 이용한 테스트가 쉬워짐

2. DI (Dependency Injection)

DI의 방식

  1. 생성자 주입 (권장)
@Service
public class UserService {
    private final UserRepository userRepository;
    private final EmailService emailService;
    
    // 생성자 주입
    public UserService(UserRepository userRepository, EmailService emailService) {
        this.userRepository = userRepository;
        this.emailService = emailService;
    }
}
  1. 필드 주입 (권장하지 않음)
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private EmailService emailService;
}
  1. 수정자(Setter) 주입
@Service
public class UserService {
    private UserRepository userRepository;
    private EmailService emailService;
    
    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    @Autowired
    public void setEmailService(EmailService emailService) {
        this.emailService = emailService;
    }
}

생성자 주입을 권장하는 이유

  1. 불변성 보장

    • final 키워드 사용 가능
    • 객체 생성 시점에 모든 의존성 주입 완료
  2. 순환 참조 감지

    • 컴파일 시점에 순환 참조 감지 가능
    • 런타임 에러 방지
  3. 테스트 용이성

    • 테스트 코드 작성 시 의존성 명시적 주입 가능

3. AOP (Aspect Oriented Programming)

AOP의 주요 개념

  1. Aspect

    • 공통 관심사를 모듈화한 것
    • 로깅, 트랜잭션, 보안 등
  2. Join Point

    • 메서드 실행, 필드 액세스 등 프로그램 실행 중의 특정 지점
  3. Advice

    • 특정 Join Point에서 실행되는 코드
    • Before, After, Around 등
  4. Pointcut

    • Join Point의 집합을 선별하는 표현식

AOP 구현 예시

  1. 로깅 Aspect
@Aspect
@Component
public class LoggingAspect {
    private final Logger log = LoggerFactory.getLogger(this.getClass());
    
    @Around("execution(* com.example.service.*.*(..))")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long end = System.currentTimeMillis();
        
        log.info("Method: {} executed in {} ms", 
            joinPoint.getSignature().toShortString(), (end - start));
            
        return result;
    }
}
  1. 트랜잭션 관리
@Aspect
@Component
public class TransactionAspect {
    @Around("@annotation(org.springframework.transaction.annotation.Transactional)")
    public Object handleTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            // 트랜잭션 시작
            Object result = joinPoint.proceed();
            // 트랜잭션 커밋
            return result;
        } catch (Exception e) {
            // 트랜잭션 롤백
            throw e;
        }
    }
}

4. Spring Container와 Bean

컨테이너의 역할

  1. Bean 생성 및 관리
  2. 의존관계 주입
  3. 생명주기 관리

Bean의 생명주기

@Component
public class ExampleBean implements InitializingBean, DisposableBean {
    @PostConstruct
    public void init() {
        // 초기화 작업
    }
    
    @PreDestroy
    public void cleanup() {
        // 정리 작업
    }
    
    @Override
    public void afterPropertiesSet() {
        // InitializingBean 구현
    }
    
    @Override
    public void destroy() {
        // DisposableBean 구현
    }
}

Bean의 Scope

  1. singleton (기본값)
    • 스프링 컨테이너당 하나의 인스턴스
  2. prototype
    • 요청할 때마다 새로운 인스턴스 생성
  3. request
    • HTTP 요청당 하나의 인스턴스
  4. session
    • HTTP 세션당 하나의 인스턴스

5. 면접 예상 질문

Q: IoC와 DI의 차이점은 무엇인가요?
A: IoC는 프로그램의 제어 흐름을 개발자가 아닌 외부(프레임워크)에서 관리하는 디자인 패턴이며, DI는 IoC를 구현하는 방법 중 하나입니다. DI는 객체가 필요로 하는 의존성을 외부에서 주입해주는 패턴을 의미합니다. Spring에서는 IoC 컨테이너가 Bean을 생성하고 관리하며, 필요한 의존성을 DI를 통해 주입합니다.

Q: Spring AOP와 AspectJ의 차이점은 무엇인가요?
A: Spring AOP와 AspectJ는 구현 방식과 기능 범위에서 차이가 있습니다. Spring AOP는 프록시 기반으로 동작하며, 메서드 실행 지점만을 조인 포인트로 지원합니다. 런타임 시점에 프록시 객체를 생성하여 AOP를 적용합니다. 반면 AspectJ는 컴파일 시점이나 로드 시점에 바이트 코드를 조작하여 AOP를 적용하며, 모든 조인 포인트(필드 접근, 생성자 호출 등)를 지원합니다. Spring AOP는 간단한 구현과 Spring과의 통합이 장점이며, AspectJ는 완전한 AOP 기능을 제공하지만 설정이 복잡합니다.

Q: 생성자 주입을 권장하는 이유는 무엇인가요?
A: 생성자 주입이 권장되는 주요 이유는 다음과 같습니다:
1. 불변성: final 키워드를 사용할 수 있어 객체의 불변성을 보장합니다.
2. 필수 의존성 보장: 객체 생성 시점에 필요한 모든 의존성이 주입되므로, NPE를 방지할 수 있습니다.
3. 순환 참조 감지: 컴파일 시점에 순환 참조를 감지할 수 있습니다.
4. 테스트 용이성: 의존성을 명시적으로 주입할 수 있어 단위 테스트가 용이합니다.

이러한 이유로 Spring 팀에서도 생성자 주입을 공식적으로 권장하고 있습니다.

참고 자료

profile
성장하고싶은 Backend 개발자

0개의 댓글