#4 ๊ด์ฌ์ฌ์ ๋ถ๋ฆฌ(SOC), ์์กด์ฑ ์ฃผ์ (DI)
by JiwonDev์ด ๊ธ์ 2021.08.01 - [Backend/Spring Core] - #3 ์คํ๋ง ํต์ฌ ์๋ฆฌ - ์์ ๋ง๋ค๊ธฐ์ ์ด์ด์ง๋ ๋ด์ฉ์ ๋๋ค.
# ํ ๊ฐ์ฒด๊ฐ ๋๋ฌด ๋ง์ ์ฑ ์์ ๊ฐ์ง๊ณ ์๋ค.
ํ์ค์์ ์ผ์ด๋๋ ๋๊ท๋ชจ ๊ณต์ฐ์ ์๊ฐํด๋ณด์. ๊ฐ๊ฐ์ ์ธํฐํ์ด์ค๋ฅผ ๋ฐฐ์ญ(์ญํ )์ด๋ผ๊ณ ์๊ฐํ ๋, ๊ทธ ๋ฐฐ์ญ์ ์ฐ๊ธฐํ๋ ๊ตฌํ์ฒด(์ฐ๊ธฐ์)๋ฅผ ์ ํํ๋ ๊ฒ์ ๋๊ฐ ๋ด๋นํ ๊น? ์ด๋ ๊ฐ๋ ์ด ๊ฒฐ์ ํ๋ ๊ฒ์ด์ง ๋ฐฐ์ฐ๋ค์ด ์ง์ ๊ฒฐ์ ํ๋ ๊ฒ์ด ์๋๋ค.
์ด์ ์ ์ฝ๋ ๋ฐฉ์์ ๋ง์น ์ฃผ์ธ๊ณต์ ์ฐ๊ธฐ์(๊ตฌํ์ฒด)๊ฐ ๋ค๋ฅธ ์ญํ ์ ๋งก์ ์ฌ๋(๊ตฌํ์ฒด)์ ์ง์ ์ด๋นํ์ฌ ๊ตฌํ๋ ๋ฐฉ์๊ณผ ๊ฐ๋ค. ์ฆ ์ฐ๊ธฐ์๋ ๊ณต์ฐ๋ ํด์ผํ๊ณ , ์์ ๊ณผ ํจ๊ป ์ฐ๊ธฐํ ๋ค๋ฅธ์ฌ๋๋ ํจ๊ป ๋ฝ๊ณ , ๊ด๋ฆฌํด์ผํ๋ค. ์ฆ, ๊ฐ๋ ์ด ์ฐ๊ธฐ๋ํ๊ณ ์ญ์ธ๋ํ๊ณ ์ฌ๋๋ค ๊ฐ์ ๊ด๊ณ๋ ์ ๋ฆฌํด์ผํ๋ ๋ค์ํ ์ฑ ์์ ํผ์ ์ง๊ณ ์๋ค.
# ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌํ์, SOC(Separation Of Concerns)
๊ณต์ฐ์์ ๋ด๋น ๋ฐฐ์ฐ(๊ตฌํ์ฒด)๊ฐ ๋ฐ๋์๋ค๊ณ ๊ณต์ฐ์ ์ฃผ์ธ๊ณต์ ํ๋(์ญํ )์ด ๋ณ๊ฒฝ๋์ง ์๋๋ค.
- ๋ฐฐ์ฐ๋ ๋ณธ์ธ์ ์ญํ ์ธ ๋ฐฐ์ญ์ ์ํํ๋ ๊ฒ์๋ง ์ง์คํด์ผ ํ๋ค.
- ์ฐ๊ธฐ์๋ ๋ค๋ฅธ ๋ฐฐ์ญ์ ๋งก๊ฒ ๋๋๋ผ๋ ๊ณต์ฐ์ ํ ์ ์์ด์ผํ๋ค.
- ๊ณต์ฐ์ ๊ตฌ์ฑํ๊ณ , ๋ด๋น ๋ฐฐ์ฐ๋ฅผ ์ญ์ธํ๋ ๊ฒ์ ๋ณ๋์ ๊ณต์ฐ ๊ธฐํ์๊ฐ ๋ด๋นํด์ผํ๋ค.
- ๋ฐฐ์ฐ์ ๊ณต์ฐ ๊ธฐํ์์ ์ฑ ์์ ํ์คํ๊ฒ ๋ถ๋ฆฌํ์.
์ฆ, ๊ณต์ฐ๊ธฐํ์๋ฅผ ๋ด๋นํ๋ AppConfig ํด๋์ค๋ฅผ ๋ง๋ค๋ฉด ํด๊ฒฐํ ์ ์๋ค.
public class AppConfig {
// MemberService๋ฅผ ๋ด๋นํ๋ ๊ธฐํ์
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
// OrderService๋ฅผ ๋ด๋นํ๋ ๊ธฐํ์
public OrderService orderService() {
return new OrderServiceImpl(
new MemoryMemberRepository(),
new FixDiscountPolicy());
}
}
AppConfig์์ ๊ตฌํ์ฒด๋ฅผ ์ฃผ์ ํ ์ ์๊ฒ ์๋์ ๊ฐ์ด ์ธํฐํ์ด์ค๋ฅผ ๋ณ๊ฒฝํ์๋ค.
public class MemberServiceImpl implements MemberService {
// [... = new ๊ตฌํ์ฒด ]์ฒ๋ผ ์ง์ ํ ๋นํด์ ๊ตฌํ์ฒด๋ฅผ ์์กดํ๋ ๋ถ๋ถ์ ์์ ์ฃผ์๋ค
public final MemberRepository memberRepository;
// ๊ทธ ๋์ AppConfig์์ ๊ตฌํ์ฒด๋ฅผ ์ฃผ์
ํ ์ ์๊ฒ ์์ฑ์๋ฅผ ๋ง๋ค์ด์ฃผ์๋ค.
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository
}
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
์ด๋ ๊ฒ ์์ฑ์๋ฅผ ์ด์ฉํด ๊ตฌํ์ฒด, ์ฆ ์์กด์ฑ์ ์ฃผ์ ํด์ฃผ๋ ์์ผ๋ก ๋ฐ๋์๋ค. ๊ตฌํ์ฒด๊ฐ ๋ฐ๋๋ค ํ๋๋ผ๋ ํด๋ผ์ด์ธํธ๋ ์ธํฐํ์ด์ค(์ญํ )์๋ง ์์กดํ๊ธฐ์ ์ ํ ์๊ด์๋ค.
- AppConfig๋ ์ฑ์ ์ค์ ๋์์ ํ์ํ ๊ตฌํ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค.
- ( MemberServiceImpl, MemoryMemberRepository, OrderServiceImpl, FixDiscountPolicy ) - AppConfig๋ ์์ฑํ ๊ฐ์ฒด์ ์ธ์คํด์ค์ ์ฐธ์กฐ๋ฅผ ์์ฑ์๋ฅผ ํตํด์ ์ฃผ์
, ์ฐ๊ฒฐํด์ค๋ค.
- MemberServiceImpl -> MemoryMemberRepositroy
- OrderServiceImpl -> MemoryMemberRepository, FixDiscountPolicy - ์ฆ, ์ธํฐํ์ด์ค์๋ง ์์กดํ๋ค. ์ฆ ์์กด๊ด๊ณ์ ๋ํ ๊ณ ๋ฏผ์ ์ธ๋ถ์ ๋งก๊ธฐ๊ณ ์คํ์๋ง ์ง์คํ๋ฉด ๋๋ค. ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌํ์ฌ ์ฝ๋๋ฅผ ๋ง๋ค ์ ์๋ค.
# DI ์ ๋ฑ์ฅ (Dependency Injection)
AppConfig ๊ฐ์ฒด๋ฅผ ํตํด์ memoryMemberRepository ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ์ฐธ์กฐ๊ฐ์ memberServiceImpl์ ์์ฑ์๋ก ์ฃผ์ ํ๋ค. ์๋์ ์ฝ๋์์ ๊ตฌํ์ฒด(memoryMemberRepository)์ ์์ค์ฝ๋๊ฐ ๋ณ๊ฒฝ๋๋ค๊ณ ํด์, ๋ฐ๋์ด์ผ ํ ๋ถ๋ถ์ด ์์๊น?
public interface MemberService {
void join(Member member);
Member findMember(Long memberId);
}
public class MemberServiceImpl implements MemberService {
// [... = new ๊ตฌํ์ฒด ]์ฒ๋ผ ์ง์ ํ ๋นํด์ ๊ตฌํ์ฒด๋ฅผ ์์กดํ๋ ๋ถ๋ถ์ ์์ ์ฃผ์๋ค
public final MemberRepository memberRepository;
// ๊ทธ ๋์ AppConfig์์ ๊ตฌํ์ฒด๋ฅผ ์ฃผ์
ํ ์ ์๊ฒ ์์ฑ์๋ฅผ ๋ง๋ค์ด์ฃผ์๋ค.
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
์ด์ฒ๋ผ ํด๋ผ์ด์ธํธ(MemberServiceImpl)์ ์ ์ฅ์์ ๋ณด๋ฉด, ๋ง์น ์์กด๊ด๊ณ๋ฅผ ์ธ๋ถ์์ ์ฃผ์ ํ๋ ๊ฒ ๊ฐ๋ค๊ณ ํ์ฌ ์ด๋ฌํ ๋ฐฉ์์ ์ค๊ณ๋ฅผ DI(Dependency Injection), ์์กด์ฑ ์ฃผ์ ์ด๋ผ๊ณ ๋ถ๋ฅด๊ฒ ๋์๋ค.
# ๊ธฐํ์, AppConfig๋ฅผ ๋ฆฌํฉํ ๋งํด๋ณด์
ํ์ฌ AppConfig๋ ๋จ์ํ๊ฒ ์์ฑํ๊ฑฐ๋ผ, ๊ฐ์ฒด์งํฅ์์ ๋งํ๋ ์ข์ ์ฝ๋๋ผ๊ณ ๋ณด๊ธฐ์๋ ์ด๋ ต๋ค. ์ฝ๋์ ์ค๋ณต์ด ์๊ณ ์ญํ ์ ๋ฐ๋ฅธ ๊ตฌํ์ ์๊ธฐ ์ด๋ ต๊ธฐ ๋๋ฌธ์ด๋ค. ์ด๋ฅผ ์ฐ๋ฆฌ๊ฐ ๋ฐฐ์ด ๋ฐฉ๋ฒ์ผ๋ก ๋ฆฌํฉํ ๋งํด๋ณด์.
// ๊ธฐ์กด์ ์ฝ๋
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService() {
return new OrderServiceImpl(
new MemoryMemberRepository(),
new FixDiscountPolicy());
}
}
/*
๊ตฌํ์ฒด ์์ฑ(memberRepository)์ ์ค๋ณต ์ฝ๋๋ฅผ ์ ๊ฑฐํ๊ณ
AppConfig ๋ง์ผ๋ก ์ญํ๊ฐ ๊ตฌํํด๋์ค๋ฅผ ์์์๊ฒ ๋ฐ๋์๋ค.
*/
public class AppConfig {
// MemberService
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
// OrderService
public OrderService orderService() {
return new OrderServiceImpl(
memberRepository(),
discountPolicy());
}
// Repository
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
// DiscountPolicy
public DiscountPolicy discountPolicy() {
return new FixDiscountPolicy();
}
}
๊ทธ๋ฆฌ๊ณ ํ ์คํธ ์ฝ๋๋ new๋ก ์์ฑํ๋๊ฒ ์๋, AppConfig๋ฅผ ์ฐ๊ฒฐํ๋ ๋ฐฉ์์ผ๋ก ๋ฐ๊ฟ์ฃผ์.
@DisplayName("OrderService ํด๋์ค์")
class OrderServiceTest {
private OrderService orderService;
private MemberService memberService;
@BeforeEach
void beforeEach() {
AppConfig appConfig = new AppConfig();
memberService = appConfig.memberService();
orderService = appConfig.orderService();
}
... ์๋ต ...
์ด๋ ๊ฒ ๊น์ง ํด์ฃผ๋ฉด, ์ฐ๋ฆฌ๋ ๋๋์ด DIP์ OCP๋ฅผ ๋ง์กฑํ๋ ์ค๊ณ๋ฅผ ํ๋ค๊ณ ๋งํ ์ ์๋ค.
- ์์กด์ฑ ์ฃผ์ ์ ํตํด ์ธํฐํ์ด์ค(์ญํ )๋ง์ ๋ณด๊ณ ๊ฐ๋ฐํ ์ ์๊ฒ ๋ฐ๋์๋ค.
- ํ์ฅ์๋ ์ด๋ ค์์ผ๋ ์์ ์๋ ๋ซํ์๋ ์ฝ๋๋ฅผ ์์ฑํ์๋ค.
# ์๋ก์ด ๊ตฌ์กฐ์ ์ ๋ ๊ธฐํ์์ ํ ์ธ์ ์ฑ ์ ์ฉ
๋ค์ ์ฒ์์ผ๋ก ๋์๊ฐ์, ์ด์ ๊ธ์์ ๋งํ๋ ์๋ก์ด ํ ์ธ์ ์ฑ ์ ์ ์ฉ์์ผ๋ณด์. ์ด๋ค ๋ถ๋ถ์ ์์ ํ๋ฉด ๋ ๊น?
public class AppConfig {
... ์๋ต ...
public DiscountPolicy discountPolicy() {
//return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
์ฆ, AppConfig๋ผ๋ ๊ฐ๋ ์ญํ ์ ๊ฐ์ฒด๊ฐ ์๊น์ผ๋ก์ ์ฑ ์์ฒด๊ฐ ์๋น์ค ์ฌ์ฉ์์ญ๊ณผ ๊ฐ์ฒด๋ฅผ ๊ตฌ์ฑ(Configuration)ํ๋ ์์ญ์ผ๋ก ๋ถ๋ฆฌ๋์์์ ์ ์ ์๋ค.
# ์ค๊ณ์ ๋ฐ์ , ๊ฐ์ฒด์งํฅ ์ ์ฉ
์ฐ๋ฆฌ๊ฐ ์ด์ ๊ธ๊ณผ ์ฌ๊ธฐ๊น์ง ํ๋ ๊ณ ๋ฏผ๋ค์, ์ค์ ์๋ฐ ๊ฐ๋ฐ์๋ค์ด ๊ณ ๋ฏผํ๋ ๋ด์ฉ๊ณผ ๋์ผํ๋ค.
1. ๊ฐ์ฒด์งํฅ ๊ฐ๋ , ๋คํ์ฑ์ ๊ตฌํ
2. ์๋ก์ด ํ ์ธ ์ ์ฑ ๊ฐ๋ฐ => ์ธํฐํ์ด์ค๋ฅผ ์ด์ฉํด์ ๋คํ์ฑ์ '๊ตฌํ ํ ๊ฒ'์ฒ๋ผ ๋ณด์.
3. ์๋ก์ด ํ ์ธ ์ ์ฑ ์ ๋ฌธ์ ์ => ๊ตฌํ์ฒด๋ฅผ ๋ณ๊ฒฝํ๋ฉด, ํด๋ผ์ด์ธํธ ์ฝ๋๋ ๋ณ๊ฒฝํด์ผํจ. DIP ์๋ฐ
4. ํด๊ฒฐ์ฑ . ๊ด์ฌ์ฌ์ ๋ถ๋ฆฌ => ๊ฐ๋ ์ญํ ์ AppConfig์ ๋ฑ์ฅ.
5. AppConfig๋ ๊ฐ์ฒด์งํฅ์ ์ผ๋ก ๋ฆฌํํ ๋ง
๋ชจ๋ฅผ ์๋ ์๊ฒ ์ง๋ง, ๋ฌธ์ ๋ฅผ ํ๋ํ๋ ํด๊ฒฐํ๋ฉด์ ์ฐ๋ฆฌ๋ ์์ฐ์ค๋ฝ๊ฒ SRP, DIP, OCP๋ฅผ ์ ์ฉ์์ผฐ๋ค.
@1. SRP ๋จ์ผ ์ฑ ์ ์์น
ํ ํด๋์ค๋ ํ๋์ ์ฑ ์๋ง ๊ฐ์ ธ์ผ ํ๋ค.
[ํ์-์ฃผ๋ฌธ]์ ํด๋ผ์ด์ธํธ ๊ฐ์ฒด์์ ์ง์ ๊ตฌํ์ฒด๋ฅผ ์์ฑ, ์ฐ๊ฒฐํ๋ ๊ณผ๋ํ ์ฑ ์์ ๊ฐ์ง๊ณ ์์๋ค. ์ด๋ฅผ SRP ์์น์ ๋ฐ๋ผ ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌํ์๊ณ , ๊ตฌํ์ฒด๋ฅผ ์์ฑํ๊ณ ์ฐ๊ฒฐํ๋ ์ฑ ์์ AppConfig๋ก ๋ถ๋ฆฌํ์๋ค. ์ด์ ํด๋ผ์ด์ธํธ ๊ฐ์ฒด๋ [ํ์-์ฃผ๋ฌธ]์๋น์ค๋ผ๋ ํ๋์ ๋ก์ง์ ์ํํ๋ ์ฑ ์๋ง ๋งก๊ฒ ๋์๋ค.
@2. DIP ์์กด๊ด๊ณ ์ญ์ ์์น
์ถ์ํ์ ์์กดํด์ผ์ง, ๊ตฌ์ฒดํ์ ์์กดํ๋ฉด ์๋๋ค.
์ฒ์์ ์ค๊ณ์์๋ ๊ฐ๊ฐ์ ๊ตฌํ์ฒด๋ค์ด ํ์ํ ๋ค๋ฅธ ์ญํ ์ '์ง์ ๋์ 'ํด์ผ ํ์๋ค. ์ฆ ๋ค๋ฅธ ๊ตฌํ์ฒด์ ์์กด์ฑ์ด ์๊ธฐ๋ฉฐ DIP ์์น์ ์งํค์ง ๋ชปํ๋ค. ํ์ง๋ง AppConfig๋ฅผ ํ ์ธ์ ์ฑ ์ด๋ ํ์์กฐํ์ ์ฅ์๊ฐ ์์ฑ์๋ฅผ ํตํด AppConfig์์ ์ฃผ์ ํ๋๋ก ๋ฐ๊พธ๋ฉด์ ์ด์ ํด๋ผ์ด์ธํธ ๊ฐ์ฒด๋ ๋ค๋ฅธ ๊ตฌํ์ฒด๋ฅผ ์ ํ ๋ชฐ๋ผ๋ ์ธํฐํ์ด์ค(์ถ์ํ)์ ์์กดํ์ฌ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๊ฒ ๋์๋ค.
@3. OCP ๊ฐ๋ฐฉ-ํ์ ์์น
์ํํธ์จ์ด ์์๋ ํ์ฅ์๋ ์ด๋ ค์์ผ๋, ๋ณ๊ฒฝ์๋ ๋ซํ์์ด์ผ ํ๋ค.
์ฒ์์๋ ๋ง๋์๋๋ ๋ง์ด๋ผ๊ณ ์๊ฐํ์ ์ ์๋ค. ์๋ฌด๋ฆฌ ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ๋ค๊ณ ํ๋ค, ๋ณ๊ฒฝ์ ์ํ๊ณ ์ด๋ป๊ฒ ํ์ฅ์ ํ ๊น? ์ฐ๋ฆฌ๋ ์ด๋ฏธ ๋ต์ ์๊ณ ์๋ค. ๋คํ์ฑ์ ์ฌ์ฉํจ์ผ๋ก์จ ํด๋ผ์ด์ธํธ๊ฐ DIP๋ฅผ ์งํค๋๋ก๋ง๋ค์๊ณ , ์ฑ์ ์ฌ์ฉ์์ญ๊ณผ ๊ตฌ์ฑ(config)์์ญ์ ๋ถ๋ฆฌ์์ผ ๊ฐ๋ฐ ํ ์ ์๊ฒ ๋์๋ค.
์ด์ ํ ์ธ์ ์ฑ ๋ณ๊ฒฝ์ด๋ ํ์ ์ ์ฅ์๊ฐ ๋ ๋ง์ ๊ธฐ๋ฅ์ ๊ฐ์ง ๊ตฌํ์ฒด๋ก ๋ฐ๋๋๋ผ๋, AppConfig ๊ตฌ์ฑ์์ญ์์ ์์กด๊ด๊ณ ์ฃผ์ ๋ง ๋ฐ๊ฟ์ฃผ๊ฒ๋๋ฉด ํด๋ผ์ด์ธํธ์ ์ฝ๋์ ์์ ์์ด ๋ฐ๊ฟ ์ ์๋ค. ์ฆ ์ํํธ์จ์ด ์์๋ฅผ ์๋กญ๊ฒ ํ์ฅ, ๋ณ๊ฒฝํ๋๋ผ๋ ์๋น์ค ์ฌ์ฉ์์ญ์ ๋ณ๊ฒฝ์ ๋ซํ์๊ฒ ๋๋ค.
์ด์ ๋ค์ ๊ธ์๋ ๊ฐ์ฒด์งํฅ ์ค๊ณ์ ๋๋ถ์ด, ์คํ๋ง์ ํต์ฌ์ธ IoC, DI ๊ทธ๋ฆฌ๊ณ ์ปจํ ์ด๋์ ๋ํด ๋ฐฐ์๋ณด๋๋ก ํฉ์๋ค.
'๐ฑ Spring Framework > Spring Core' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
#5 ์คํ๋ง์ผ๋ก ์ ํํ๊ธฐ (0) | 2021.08.02 |
---|---|
IoC, DI, ๊ทธ๋ฆฌ๊ณ ์ปจํ ์ด๋ (0) | 2021.08.02 |
#3 ์คํ๋ง ํต์ฌ ์๋ฆฌ - ๊ธฐ์กด์ ๋ถํธํ ์ค๊ณ ๋ฐฉ์ (0) | 2021.08.01 |
#2 ์คํ๋ง ํ๋ก์ ํธ ์์ฑ (0) | 2021.07.25 |
#1 ๊ฐ์ฒด์งํฅ ํ๋ก๊ทธ๋๋ฐ (0) | 2021.07.25 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
JiwonDev
JiwonDev