🔑kudos to do your job!
💡 제어의 역전 (IoC, Inversion of Control)
📃 제어의 역전(IoC)은 프로그램의 흐름 제어나 객체 생성을 개발자가 아닌 프레임워크가 담당하는 설계 패턴입니다. 개발자는 객체 생성과 관리에 집중하지 않아도 되고, 애플리케이션의 비즈니스 로직에만 신경 쓰면 됩니다.
쉽게 말하면, "내가 객체를 만들고 관리하는 게 아니라, 프레임워크가 알아서 해주는 방식"입니다 그래서 Inversion은 "기존 전통적인 흐름과 반대로 작용한다"라고 할 수 있습니다
- 전통적인 방식: 개발자가 필요한 객체를 생성하고 관리.
- IoC 방식: 프레임워크(Spring 등)가 객체 생성과 관리를 담당.
💻CODE
// 옛날 방식: 필요한 객체를 직접 생성
MyService myService = new MyService();
myService.doSomething();
// IoC 방식
// Spring Framework가 객체를 생성하고 관리
@Component
public class MyService {
public void doSomething() {
System.out.println("작업 수행 중...");
}
}
@Autowired
private MyService myService; // Spring이 알아서 주입
⬆️ 프레임워크가 객체를 관리하고 애플리케이션에 적절히 주입합니다
💡 의존성 주입 (DI, Dependency Injection)
📃 의존성 주입은 객체가 필요로 하는 다른 객체(의존성)을 직접 생성하지 않고 외부에서 주입받는 설계 방식입니다. 이 방식은 코드간의 결합도를 낮추고 테스트와 유지보스를 더 쉽게 만듭니다
💻옛날 방식
public class MyService {
private MyRepository repository;
public MyService() {
this.repository = new MyRepository(); // 직접 생성
}
}
💻DI 방식
@Component
public class MyService {
private final MyRepository repository;
@Autowired
public MyService(MyRepository repository) { // 외부에서 주입
this.repository = repository;
}
}
⬆️ 두개의 방식을 예로 설명을 들었지만, 그냥 DI 방식은 자바 객체 생성자의 느낌이 아닌가?라는 생각이 들어서, 또 다른 예를 찾아봤습니다
💻 차량 색깔 예시
// 일반 생성자
Color red = new Color("RED");
Car car1 = new Car(red); // 매번 이렇게 해야함
// 스프링 DI
@Component
public class Car {
private final Color color;
@Autowired
public Car(Color color) { // 스프링이 설정에 따라 알아서 주입
this.color = color;
}
}
⬆️ 위와 같은 차량의 색을 변경하는 예시로 보았을때, 우리는 일반적으로 Car객체에서 new 생성자를 통해 새로운 car를 정의해줬었죠? 하지만 DI 방식은 @Autowired라는 방식으로 스프링이 설정에 따라서 주입하도록 설정을 해준다는 것입니다
💻AppConfig
@Configuration
public class AppConfig {
@Bean
public Color color() {
return new Color("RED"); // 여기만 BLUE로 바꾸면 모든 Car가 파란색으로!
}
}
⬆️ 그래서 위와 같은 Config 클래스에서 @Bean 어노테이션의 수정만을 통해서 유연하게 관리를 할 수 있다는 점을 기억하면 될 것 같습니다
📃 그래서 실제 업무환경에서 아래와 같이 설정을 관리하며 진행할 수 있다고 하네요
💻실무 환경에서
// 개발 환경용 설정
@Configuration
@Profile("dev")
public class DevConfig {
@Bean
public PaymentGateway paymentGateway() {
return new TestPaymentGateway(); // 실제 결제 없이 테스트
}
@Bean
public Repository repository() {
return new InMemoryRepository(); // 실제 DB 대신 메모리 사용
}
}
// 실제 운영 환경용 설정
@Configuration
@Profile("prod")
public class ProdConfig {
@Bean
public PaymentGateway paymentGateway() {
return new KakaoPayGateway(); // 실제 카카오페이 결제
}
@Bean
public Repository repository() {
return new MySQLRepository(); // 실제 MySQL DB 사용
}
}
⬆️ 이렇게 환경별로 다른 구현체를 사용할 수 있고, 애플리케이션 코드는 전혀 건드리지 않아도 됩니다. 이는 설정 파일만으로 시스템의 동작을 완전히 다르게 가져갈 수 있다는 큰 장점이 있습니다
💡 관점 지향 프로그래밍 (AOP, Aspect-Oriented Programming)
📃 관점 지향 프로그래밍(AOP)은 여러 객체나 메서드에서 공통적으로 수행되어야 하는 기능(로깅, 보안, 트랜잭션 관리 등)을 별도로 분리해 관리하는 프로그래밍 기법입니다.
쉽게 말하면, "공통된 작업(예: 로그 남기기)은 한 곳에서 관리하고, 필요한 곳에 자동으로 적용하는 방식"입니다
AOP의 장점:
- 중복 코드 감소
- 코드 간결화
- 공통 기능 관리의 일관성 향상
💻 옛날 방식
public class MyService {
public void doWork() {
System.out.println("작업 시작"); // 로깅 코드
// 작업 실행 코드...
System.out.println("작업 끝"); // 로깅 코드
}
}
💻 AOP 방식
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logStart(JoinPoint joinPoint) {
System.out.println("작업 시작: " + joinPoint.getSignature().getName());
}
@After("execution(* com.example.service.*.*(..))")
public void logEnd(JoinPoint joinPoint) {
System.out.println("작업 끝: " + joinPoint.getSignature().getName());
}
}