전체 보기
📕

[자바 객체 지향] Chapter 7. 스프링 삼각형과 설정 정보

작성일자
2022/09/21
태그
SUB PAGE
프로젝트
자바 객체 지향
책 종류
1 more property
목차

1. IoC/DI - 제어의 역전/의존성 주입

1) 프로그래밍에서 의존성이란?

의사 코드로 살펴보는 의존성
운전자가 자동차를 생산한다. 자동차는 내부적으로 타이어를 생산한다.
의존성 → 자동차(전체)가 타이어(부분)에 의존함
의존성: A클래스가 B클래스의 객체를 생성해 사용하는 것.
A와 B는 의존 관계에 있다.
A는 B에 의존한다.
의존성: 다른 객체를 참조하는 것 ⇒ 객체 참조 변수

2) 의존성을 직접 해결

의존성 직접 해결 → 자동차 내부에서 타이어를 직접 생산
의사 코드
운전자가 자동차를 생산해 사용한다. 자동차가 내부적으로 타이어를 생산해 사용한다.
실제 코드 요약
Driver 클래스
Car car = new Car(); // 운전자가 자동차를 생산해 사용한다.
Java
복사
Car 클래스
Tire tire = new KoreaTire(); // 자동차가 내부적으로 타이어를 생산해 사용한다.
Java
복사
실제 코드
Tire.java
KoreaTire.java
AmericaTire.java
Car.javaTire를 생산하고 사용함: Car는 Tire에 의존함
Driver.java Car를 생산하고 사용함: Driver는 Car에 의존함
CarTest.java → Driver.java와 같은 역할
문제점 & 해결책
문제점
특정 자동차가 생산될 때 “어떤 타이어를 생산해서 장착할까”를 자동차 스스로가 결정함.
운전자는“어떤 자동차를 생산할지”만 결정 할 수 있고, 선택한 자동차에 “어떤 타이어를 장착할지” 는 결정할 수 없음(유연성 ↓)
해결책
“어떤 타이어 장착할지”는 자동차가 아니라 운전자가 결정해야 함.
운전자가 타이어를 생산함. 자동차는 운전자가 생산한 타이어를 건네받아 장착만 함.
즉, 의존성을 내부에서 직접 해결하는 게 아니라, 외부에서 주입 받아 해결해야 함.

3) 스프링 없이 의존성 주입 1 - 생성자를 통한 의존성 주입 (생성자의 인자 주입으로 의존성을 해결)

의존성 주입 → 자동차 외부에서 생산된 타이어를 자동차에 장착
생성자를 통한 의존성 주입 → 생성자의 인자를 통해 타이어 건네 받음.
주입의 장점
Car는 KoreaTire, AmericaTire…등 몰라도 됨. Car는 Tire만 알면 됨.
따라서 새로운 타이어 브랜드(ChinaTire…) 얼마든지 생겨도, Car는 알 필요도 없기에 코드를 변경할 필요 없음.(확장성 ↑)
의사 코드
운전자가 타이어를 생산한다. 운전자가 자동차를 생산하면서 타이어를 장착한다.
자동차는 외부(운전자)에서 생산한 타이어를 장착해 사용한다.
단, 타이어 장착은 자동차를 생산하는 순간에만 이뤄질 수 있다. 추후 타이어 교체 불가.
실제 코드 요약
Driver 클래스
Tire tire = new KoreaTire(); // 운전자가 타이어를 생산한다. Car car = new Car(tire); // 운전자가 자동차를 생산하면서 타이어를 장착한다.
Java
복사
실제 코드
Car.javaTire를 주입 받아 사용함.
Driver.java → Tire를 생산함. Car를 생산하면서 생산해둔 Tire를 주입시킴. Car를 사용함.
문제점 & 해결책
문제점
자동차를 생산할 때 한 번 타이어를 장착하면 더 이상 타이어를 교체할 방법이 없음
해결책
운전자가 원할 때 자동차의 타이어 교체할 수 있어야 함.
생성자가 아닌 속성을 통한 의존성 주입으로 해결해야 함.
이를 통해 자동차 생산과 타이어 장착을 한 번에 하지 않고 따로 따로 할 수 있게 분리함.

4) 스프링 없이 의존성 주입 2 - 속성을 통한 의존성 주입

속성을 통한 의존성 주입 → 속성 접근자 메서드의 인자를 통해 타이어 건네 받음.
의사 코드
운전자가 타이어를 생산한다. 운전자가 자동차를 생산한다. 운전자가 자동차에 타이어를 장착한다.
실제 코드 요약
Driver 클래스
Tire tire = new KoreaTire(); // 운전자가 타이어를 생산한다. Car car = new Car(); // 운전자가 자동차를 생산한다. car.setTire(tire); // 운전자가 자동차에 타이어를 장착한다.
Java
복사
실제 코드
Car.javaTire를 주입 받아 사용함.
Driver.java → Tire를 생산함. Car를 생산함. Car에 생산해둔 Tire를 주입시킴. Car를 사용함.
문제점 & 해결책
문제점
타이어 종류 바꾸려면 운전자 코드가 바뀌어야 함.
해결책
종합쇼핑몰 같은 곳에서 타이어를 구매해오는 형태라면 타이어 바꿀 때 종합쇼핑몰에서 구매하는 타이어의 종류만 바꾸면 되므로 종합쇼핑몰의 코드만 바뀌면 됨.

5) 스프링을 통한 의존성 주입 - XML 파일 사용

XML 파일 사용한 의존성 주입 → 종합 쇼핑몰(스프링 프레임워크)에서 생산된 타이어를 구매해 자동차에 장착
의사 코드
운전자가 종합 쇼핑몰에서 타이어를 구매한다. 운전자가 종합 쇼핑몰에서 자동차를 구매한다. 운전자가 자동차에 타이어를 장착한다.
실제 코드 요약
Driver 클래스
Tire tire = context.getBean("tire", Tire.class); // 운전자가 타이어를 쇼핑몰에서 구매한다. Car car = context.getBean("car", Car.class); // 운전자가 자동차를 쇼핑몰에서 구매한다. car.setTire(tire); // 운전자가 자동차에 타이어를 장착한다.
Java
복사
실제 코드
Car.javaTire를 주입 받아 사용함. - 변동 없음
Driver.java → 쇼핑몰에서 Tire와 Car를 구매함. Car에 Tire를 주입시킴. Car를 사용함.
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
복사
실제 코드
Car.javaTire를 주입 받아 사용함. - 변동 없음
Driver.java → 쇼핑몰에서 Car를 구매함. Car를 사용함.
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 \supset 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만 적용)