목차
1. IoC/DI - 제어의 역전/의존성 주입
1) 프로그래밍에서 의존성이란?
•
의사 코드로 살펴보는 의존성
운전자가 자동차를 생산한다.
자동차는 내부적으로 타이어를 생산한다.
◦
의존성 → 자동차(전체)가 타이어(부분)에 의존함
•
의존성: A클래스가 B클래스의 객체를 생성해 사용하는 것.
◦
A와 B는 의존 관계에 있다.
◦
A는 B에 의존한다.
•
의존성: 다른 객체를 참조하는 것 ⇒ 객체 참조 변수
2) 의존성을 직접 해결
•
의존성 직접 해결 → 자동차 내부에서 타이어를 직접 생산
•
의사 코드
운전자가 자동차를 생산해 사용한다.
자동차가 내부적으로 타이어를 생산해 사용한다.
•
실제 코드 요약
◦
Driver 클래스
Car car = new Car(); // 운전자가 자동차를 생산해 사용한다.
Java
복사
◦
Car 클래스
Tire tire = new KoreaTire(); // 자동차가 내부적으로 타이어를 생산해 사용한다.
Java
복사
•
•
문제점 & 해결책
◦
문제점
▪
특정 자동차가 생산될 때 “어떤 타이어를 생산해서 장착할까”를 자동차 스스로가 결정함.
▪
운전자는“어떤 자동차를 생산할지”만 결정 할 수 있고, 선택한 자동차에 “어떤 타이어를 장착할지” 는 결정할 수 없음(유연성 ↓)
◦
해결책
▪
“어떤 타이어 장착할지”는 자동차가 아니라 운전자가 결정해야 함.
▪
운전자가 타이어를 생산함. 자동차는 운전자가 생산한 타이어를 건네받아 장착만 함.
▪
즉, 의존성을 내부에서 직접 해결하는 게 아니라, 외부에서 주입 받아 해결해야 함.
3) 스프링 없이 의존성 주입 1 - 생성자를 통한 의존성 주입 (생성자의 인자 주입으로 의존성을 해결)
•
의존성 주입 → 자동차 외부에서 생산된 타이어를 자동차에 장착
•
생성자를 통한 의존성 주입 → 생성자의 인자를 통해 타이어 건네 받음.
•
주입의 장점
◦
Car는 KoreaTire, AmericaTire…등 몰라도 됨. Car는 Tire만 알면 됨.
◦
따라서 새로운 타이어 브랜드(ChinaTire…) 얼마든지 생겨도, Car는 알 필요도 없기에 코드를 변경할 필요 없음.(확장성 ↑)
•
의사 코드
운전자가 타이어를 생산한다.
운전자가 자동차를 생산하면서 타이어를 장착한다.
◦
자동차는 외부(운전자)에서 생산한 타이어를 장착해 사용한다.
◦
단, 타이어 장착은 자동차를 생산하는 순간에만 이뤄질 수 있다. 추후 타이어 교체 불가.
•
실제 코드 요약
◦
Driver 클래스
Tire tire = new KoreaTire(); // 운전자가 타이어를 생산한다.
Car car = new Car(tire); // 운전자가 자동차를 생산하면서 타이어를 장착한다.
Java
복사
•
•
문제점 & 해결책
◦
문제점
▪
자동차를 생산할 때 한 번 타이어를 장착하면 더 이상 타이어를 교체할 방법이 없음
◦
해결책
▪
운전자가 원할 때 자동차의 타이어 교체할 수 있어야 함.
▪
생성자가 아닌 속성을 통한 의존성 주입으로 해결해야 함.
▪
이를 통해 자동차 생산과 타이어 장착을 한 번에 하지 않고 따로 따로 할 수 있게 분리함.
4) 스프링 없이 의존성 주입 2 - 속성을 통한 의존성 주입
•
속성을 통한 의존성 주입 → 속성 접근자 메서드의 인자를 통해 타이어 건네 받음.
•
의사 코드
운전자가 타이어를 생산한다.
운전자가 자동차를 생산한다.
운전자가 자동차에 타이어를 장착한다.
•
실제 코드 요약
◦
Driver 클래스
Tire tire = new KoreaTire(); // 운전자가 타이어를 생산한다.
Car car = new Car(); // 운전자가 자동차를 생산한다.
car.setTire(tire); // 운전자가 자동차에 타이어를 장착한다.
Java
복사
•
•
문제점 & 해결책
◦
문제점
▪
타이어 종류 바꾸려면 운전자 코드가 바뀌어야 함.
◦
해결책
▪
종합쇼핑몰 같은 곳에서 타이어를 구매해오는 형태라면 타이어 바꿀 때 종합쇼핑몰에서 구매하는 타이어의 종류만 바꾸면 되므로 종합쇼핑몰의 코드만 바뀌면 됨.
5) 스프링을 통한 의존성 주입 - XML 파일 사용
•
XML 파일 사용한 의존성 주입 → 종합 쇼핑몰(스프링 프레임워크)에서 생산된 타이어를 구매해 자동차에 장착
•
의사 코드
운전자가 종합 쇼핑몰에서 타이어를 구매한다.
운전자가 종합 쇼핑몰에서 자동차를 구매한다.
운전자가 자동차에 타이어를 장착한다.
•
실제 코드 요약
◦
Driver 클래스
Tire tire = context.getBean("tire", Tire.class); // 운전자가 타이어를 쇼핑몰에서 구매한다.
Car car = context.getBean("car", Car.class); // 운전자가 자동차를 쇼핑몰에서 구매한다.
car.setTire(tire); // 운전자가 자동차에 타이어를 장착한다.
Java
복사
•
실제 코드
expert002.xml → Tire와 Car 상품을 쇼핑몰에 등록함
•
문제점 & 해결책
◦
문제점
▪
운전자는 쇼핑몰에서 car와 tire를 사와서 car에 tire를 장착하는데,
▪
쇼핑몰에서 파는 tire는 한 종류(KoreaTire)밖에 없음.
◦
해결책
▪
쇼핑몰에서 tire를 장착한 car를 팔면 됨. 쇼핑몰 내엔 여러 tire 있으니 americaTire, koreaTire 등 여러 종류 타이어 장착 가능.
6) 스프링을 통한 의존성 주입 - 스프링 설정 파일(XML)에서 속성 주입
•
XML 파일에서 속성 주입 → 종합 쇼핑몰에서 타이어가 장착되어 판매되는 차를 구매해 사용
•
의사 코드
운전자가 종합 쇼핑몰에서 자동차를 구매 요청한다.
종합 쇼핑몰은 자동차를 생산한다.
종합 쇼핑몰은 타이어를 생산한다.
종합 쇼핑몰은 자동차에 타이어를 장착한다.
종합 쇼핑몰은 운전자에게 자동차를 전달한다.
•
실제 코드 요약
◦
Driver 클래스
Car car = context.getBean("car", Car.class); // 운전자가 타이어 장착된 자동차를 쇼핑몰에서 구매한다.
Java
복사
◦
XML
<bean id="koreaTire" class="expert002.KoreaTire"></bean>
<bean id="americaTire" class="expert002.AmericaTire"></bean>
<bean id="car" class="expert002.Car">
<property name="tire" ref="koreaTire"></property>
// 기존에 Driver클래스에서 하던 타이어 구매하고 타이어를 자동차에 장착하던 일을 대신함
</bean>
Java
복사
•
실제 코드
expert002.xml → Tire와 Car 상품을 쇼핑몰에 등록함. Car에 Tire를 주입시킴
7) 스프링을 통한 의존성 주입 - @Autowired를 통한 속성 주입
•
@Autowired를 통한 속성 주입 →
•
의사 코드 (이전과 동일)
운전자가 종합 쇼핑몰에서 자동차를 구매 요청한다.
종합 쇼핑몰은 자동차를 생산한다.
종합 쇼핑몰은 타이어를 생산한다.
종합 쇼핑몰은 자동차에 타이어를 장착한다.
종합 쇼핑몰은 운전자에게 자동차를 전달한다.
2. AOP - Aspect? 관점? 핵심 관심사? 횡단 관심사?
1) 스프링에서 AOP란?
•
정의)
◦
공통 관심 사항(cross-cutting concern)과 핵심 관심 사항(core concern) 분리한 후, 분리한 공통 관심 사항을 원하는 곳에 적용
▪
자바의 AOP 구현체: AspectJ, 스프링 AOP
◦
관점 지향 프로그래밍(Aspect-oriented Programming)
▪
어떤 로직을 기준으로 관점으로 나누어 보고, 그 관점을 기준으로 각각 모듈화하는 프로그래밍 기법
ex) 그림 - 공통 관심사를 모듈로 분리한 후, 후에 원하는 곳에 공통관심사를 적용함
•
용어)
◦
Concern
▪
관심사
▪
코드 = 공통 관심사(횡단 관심사) + 핵심 관심사
◦
Aspect
▪
관점
▪
흩어진 공통 관심사를 묶어서 모듈화한 하나의 모듈
▪
Aspect = Point Cut들 + Advice들
◦
Target
▪
Aspect가 가지고 있는 Advice가 적용되는 대상(클래스, 메서드)
◦
Point Cut
▪
Aspect 적용 위치 지정자
▪
스프링 → 공통 관심사를 적용할 타깃 클래스의 타깃 메서드 지정자
◦
Join Point
▪
Aspect 적용이 가능한(Point cut의 후보가 되는) 모든 지점
▪
Join Point Point cut
◦
Advice
▪
조언, 충고
▪
Pointcut에 언제, 무엇을 적용할지 정의한 메서드
•
장점)
◦
핵심 관심사항과 공통 관심 사항을 분리함
▪
핵심 관심 사항을 깔끔하게 유지할 수 있음.
▪
원하는 적용 대상을 선택할 수 있음
▪
변경이 필요하면 공통 로직 하나만 변경하면 됨
•
단일 책임 원칙 적용
2) AOP가 필요한 상황
•
예시
◦
상황) 모든 메서드의 호출 시간을 측정하고 싶음
코드) 각 메서드에 시간 측정 코드 넣음
◦
문제)
▪
회원 가입, 회원 조회에 시간을 측정하는 기능은 핵심 관심 사항이 아님
▪
시간을 측정하는 로직은 공통 관심 사항임.
▪
시간을 측정하는 로직과 핵심 비즈니스 로직이 섞여 있어 유지 보수가 어려움
▪
시간을 측정하는 로직을 별도의 공통 로직으로 만들기 매우 어려움
▪
시간을 측정하는 로직을 변경할 때 모든 로직을 찾아가면서 변경해야 함
◦
해결) AOP(Aspect Oriented Programming) 사용
▪
공통 관심 사항(cross-cutting concern) vs 핵심 관심 사항(core concern) 분리
3) AOP 적용
1.
AOP 패키지 만들기 hello.hellospring.aop
2.
TimeTraceAop 클래스 만들기
TimeTraceAop.java
3.
@Aspect 붙이기
•
에러) @Aspect 임포트가 안됨
•
해결)
◦
aop 의존성 추가하기 위해 build gradle에 implementation 'org.springframework.boot:spring-boot-starter-aop' 추가
◦
추가하고 build.gradle을 수정한 후 reload버튼을 클릭
reload 버튼 위치
4.
코드 작성
main/java/hello.hellospring.aop/TimeTraceAop.java
5.
스프링 빈으로 등록하기
1) @Component 붙여서 컴포넌트 스캔
main/java/hello.hellospring.aop/TimeTraceAop.java
2) 스프링 빈에 등록해서 쓰기
main/java/hello.hellospring/SpringConfig
6.
공통 관심사를 어디에 적용할지 타겟팅 @Around
main/java/hello.hellospring.aop/TimeTraceAop.java
•
적용 대상 변경
◦
ex) service에만 적용
@Around("execution(* hello.hellospring.service..*(..))")
7.
기존에 적어둔 시간 측정 코드 주석 처리하고 원래대로 되돌리기
main/java/hello.hellospring.service/MemberService.jav
8.
회원 목록 들어가서 시간 측정 결과 확인
터미널 (전체 적용)
터미널 (service만 적용)