[Spring ๊ธฐ๋ณธ] 5. ์ฑ๊ธํค ์ปจํ ์ด๋
์๋ ํ์ธ์!
์ด๋ฒ์๋ ์ฑ๊ธํค์ ๊ด๋ จํด์ ๊ณต๋ถํ๊ณ ์์ต๋๋ค!
๊ทธ๋ผ ์์ํด๋ณด๊ฒ ์ต๋๋ค.
๊ณ ๊ณ !!

1. ์น ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ์ฑ๊ธํค
์คํ๋ง์ ํ์์ด ๊ธฐ์ ์ฉ ์จ๋ผ์ธ ์๋น์ค ๊ธฐ์ ์ ์ง์ํ๊ธฐ ์ํด ํ์ํ์ต๋๋ค.
๋๋ถ๋ถ์ ์คํ๋ง ์ ํ๋ฆฌ์ผ์ด์ ์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋๋ค.
๋ชฐ๋ก ์น์ด ์๋ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ๋ ์ผ๋ง๋ ์ง ๊ฐ๋ฐํ ์ ์์ต๋๋ค.
์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณดํต ์ฌ๋ฌ ๊ณ ๊ฐ์ด ๋์์ ์์ฒญ์ ํฉ๋๋ค.
1. ํด๋ผ์ด์ธํธ A, B, C๊ฐ memberService๋ฅผ ์คํ๋งํํ ์์ฒญ์ ํฉ๋๋ค.
2. ์ฐ๋ฆฌ๊ฐ ๋ง๋ค์๋ DI ์ปจํ ์ด๋(AppConfig)๋ new๋ก memberService๋ฅผ ๋ฐํํด์ค๋๋ค.
=>A๊ฐ ์์ฒญํ๋ฉด DI ์ปจํ ์ด๋๊ฐ ๋ฐํํด์ฃผ๊ณ , B๊ฐ ์์ฒญํ๋ฉด DI ์ปจํ ์ด๋๊ฐ ๋ ๋ฐํํด์ฃผ๊ณ , C๊ฐ ์์ฒญํ๋ฉด DI ์ปจํ ์ด๋๊ฐ ๋ ๋ฐํํด์ฃผ๊ณ ..
3. ์ด๋ ๊ฒ ๋๋ฉด, ๊ณ ๊ฐ์ด 3๋ฒ ์์ฒญ์ ํ๋ฉด ๊ฐ์ฒด๊ฐ 3๊ฐ๊ฐ ์์ฑ์ด ๋๋ ๊ฒ์ ๋๋ค.
๊ทผ๋ฐ, ์ด๊ฑฐ.. ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ์ด๋ค ๋ฌธ์ ๊ฐ ์๋๋ฉด์..
์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ณ ๊ฐ์ด ๊ณ์ ์์ฒญ์ ํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋๋ค.
๊ทธ๋์, ๊ณ์ ์์ฒญ์ด ์ฌ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ๊ณ์ ๋ง๋ค์ด์ผ ํฉ๋๋ค.
์ด๊ฒ ๋ฌธ์ ์ธ ๊ฒ์ ๋๋ค.
์ง์ง ๊ณ ๊ฐ์๊ฒ ์์ฒญ์ด ์ฌ ๋๋ง๋ค ์ด๋ ๊ฒ ๋๋์?
์คํ๋ง์ด ์๋ ์์ํ DI ์ปจํ ์ด๋ ํ ์คํธ ์ฝ๋๋ก ํ์ธํด๋ณด๊ฒ ์ต๋๋ค.
์ฐ์ , test ํจํค์ง์์ hello.core์ singleton์ด๋ ํจํค์ง๋ฅผ ๋ง๋ค๊ณ ๊ทธ ์์ SingletonTest๋ผ๋ ์๋ฐ ํด๋์ค๋ฅผ ๋ง๋ค๋๋ก ํ๊ฒ ์ต๋๋ค.
public class SingletonTest {
@Test
@DisplayName("์คํ๋ง ์๋ ์์ํ DI ์ปจํ
์ด๋")
void pureContainer() {
AppConfig appConfig = new AppConfig();
//1. ์กฐํ: ํธ์ถํ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑ
MemberService memberService1 = appConfig.memberService();
//2. ์กฐํ: ํธ์ถํ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑ
MemberService memberService2 = appConfig.memberService();
//์ฐธ์กฐ๊ฐ์ด ๋ค๋ฅธ ๊ฒ์ ํ์ธ
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
}
}
AppConfig (DI์ปจํ ์ด๋)์ memberService๋ฅผ 2๋ฒ ์์ฒญํ๋ฉด ๊ณผ์ฐ ๊ฐ์ฒด๊ฐ 2๊ฐ๊ฐ ๋์ฌ๊น์?
๊ฒฐ๊ณผ ์ฌ์ง์ ๋ณด๋ฉด appConfigํํ memberService๋ฅผ ๋ฌ๋ผ๊ณ ํ ๋๋ง๋ค ๋ค๋ฅธ ๊ฐ์ฒด๊ฐ ์์ฑ๋๋ค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
๊ฒ์ฆ์ ํ ๋ฒ ํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
//๊ฒ์ฆ
//memberService1๊ณผ memberService2๋ ๋ฌ๋ผ์ผ ํ๋ค.
Assertions.assertThat(memberService1).isNotSameAs(memberService2);
์ ๋๋ค์.
์ด๋ฐ ์์ผ๋ก ์์ฒญ์ด ๋ค์ด์ฌ ๋๋ง๋ค ๊ฐ์ฒด๊ฐ ๊ณ์ ์์ฑ๋๋ ๊ฒ์ ํจ์จ์ ์ด์ง ๋ชปํ๊ฒ ์ฃ ..?
์ฐ๋ฆฌ๊ฐ ๋ง๋ค์๋ ์คํ๋ง ์๋ ์์ํ DI ์ปจํ ์ด๋์ธ AppConfig๋ ์์ฒญ์ ํ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์๋ก ์์ฑํฉ๋๋ค.
๊ณ ๊ฐ ํธ๋ํฝ์ด ์ด๋น 100์ด ๋์ค๋ฉด ์ด๋น 100๊ฐ ๊ฐ์ฒด๊ฐ ์์ฑ๋๊ณ ์๋ฉธ๋๋๋ฐ, ์ด๋ ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น๊ฐ ์ฌํฉ๋๋ค.
ํด๊ฒฐ ๋ฐฉ์์ ํด๋น ๊ฐ์ฒด๊ฐ ๋ฑ 1๊ฐ๋ง ์์ฑ๋๊ณ ๊ณต์ ํ๋๋ก ์ค๊ณํ๋ฉด ๋ฉ๋๋ค.
์ด๊ฒ์ด ์ฑ๊ธํค ํจํด!!

2. ์ฑ๊ธํค ํจํด
์ฑ๊ธํค ํจํด์ ํด๋์ค์ ์ธ์คํด์ค๊ฐ ๋ฑ 1๊ฐ๋ง ์์ฑ๋๋ ๊ฒ์ ๋ณด์ฅํ๋ ๋์์ธ ํจํด์ ๋๋ค.
๊ทธ๋์, ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ 2๊ฐ ์ด์ ์์ฑํ์ง ๋ชปํ๋๋ก ๋ง์์ผ ํฉ๋๋ค.
๋ง๋ ๋ฐฉ๋ฒ์ private ์์ฑ์๋ฅผ ์ฌ์ฉํด์ ์ธ๋ถ์์ ์์๋ก new ํค์๋๋ฅผ ์ฌ์ฉํ์ง ๋ชปํ๋๋ก ๋ง์์ผ ํฉ๋๋ค.
์ฝ๋๋ก ํ ๋ฒ ๋ด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์๊น ๋ง๋ค์๋ test์ hello.core์ singleton ํจํค์ง์ SingletonService ์๋ฐ ํด๋์ค๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
public class SingletonService {
//์๊ธฐ ์์ ์ ๋ด๋ถ์ private๋ก ํ๋ ๊ฐ์ง๊ณ ์๋๋ฐ, static์ผ๋ก ๊ฐ์ง๊ณ ์์ต๋๋ค.
//์ด๋ ๊ฒ ํ๋ฉด ํด๋์ค ๋ ๋ฒจ์ ์ฌ๋ผ๊ฐ๊ธฐ ๋๋ฌธ์ ํ๋๋ง ์กด์ฌํ๊ฒ ๋ฉ๋๋ค.
private static final SingletonService instance = new SingletonService();
//์กฐํ
public static SingletonService getInstance() {
return instance;
}
//์ด๋ ๊ฒ ์ฑ๊ธํค์ผ๋ก ํด๋ ๋ฉ์ธ ํจ์์์ ๊ฐ์ฒด๋ฅผ ์ฌ๋ฌ๊ฐ ๋ง๋ค์ด๋ฒ๋ฆด ์ ์์ต๋๋ค.
//๊ทธ๋์, private ์์ฑ์๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์์ฑ์ ๋ง์๋ฒ๋ฆฌ๋ ค๊ณ .
private SingletonService() {
}
public void logic() {
System.out.println("์ฑ๊ธํค ๊ฐ์ฒด ๋ก์ง ํธ์ถ");
}
}
- private static final SingletonService instance = new SingletonService();
- 1. static ์์ญ์ ๊ฐ์ฒด๋ฅผ ๋ฑ 1๊ฐ๋ง ์์ฑํด๋ก๋๋ค.
- ์๊ธฐ ์์ ์ ๋ด๋ถ์ private๋ก ํ๋ ๊ฐ๊ณ ์๋๋ฐ, static์ผ๋ก ๊ฐ๊ณ ์์ต๋๋ค.
์ด๋ ๊ฒ ํ๋ฉด ํด๋์ค ๋ ๋ฒจ์ ์ฌ๋ผ๊ฐ๊ธฐ ๋๋ฌธ์ ํ๋๋ง ์กด์ฌํ๊ฒ ํฉ๋๋ค.
- public static SingletonService getInstance() {
return instance;
}- 2. public์ผ๋ก ์ด์ด์ ๊ฐ์ฒด ์ธ์คํด์ค๊ฐ ํ์ํ๋ฉด ์ด static ๋ฉ์๋๋ฅผ ํตํด์๋ง ์กฐํํ๋๋ก ํ์ฉํฉ๋๋ค.
- private SingletonService() { }
- 3. ์์ฑ์๋ฅผ private๋ก ์ ์ธํด์ new ํค์๋๋ฅผ ์ฌ์ฉํ ๊ฐ์ฒด ์์ฑ์ ํ์ง ๋ชปํ๋๋ก ๋ง์ต๋๋ค.
- public void logic() {
System.out.println("์ฑ๊ธํค ๊ฐ์ฒด ๋ก์ง ํธ์ถ);
}- ์์์ ์์ฑํ ๊ฒ๋ค์ ์ถ๋ ฅํด๋ณด๋ ํจ์
<์์ธํ ์ค๋ช >
1. static ์์ญ์ ๊ฐ์ฒด instance๋ฅผ ๋ฏธ๋ฆฌ ํ๋ ์์ฑํด์ ์ฌ๋ ค๋ก๋๋ค.
2. ์ด ๊ฐ์ฒด ์ธ์คํด์ค๊ฐ ํ์ํ๋ฉด ์ค์ง getInstance() ๋ฉ์๋๋ฅผ ํตํด์๋ง ์กฐํํ ์ ์์ต๋๋ค.
์ด ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ํญ์ ๊ฐ์ ์ธ์คํด์ค๋ฅผ ๋ฐํํฉ๋๋ค.
3. ๋ฑ 1๊ฐ์ ๊ฐ์ฒด ์ธ์คํด์ค๋ง ์กด์ฌํด์ผ ํ๋ฏ๋ก, ์์ฑ์๋ฅผ private๋ก ๋ง์์ ํน์๋ผ๋ ์ธ๋ถ์์ new ํค์๋๋ก ๊ฐ์ฒด ์ธ์คํด์ค๊ฐ ์์ฑ๋๋ ๊ฒ์ ๋ง์ต๋๋ค.
์ฑ๊ธํค ํจํด์ ์ฌ์ฉํ ํ ์คํธ ์ฝ๋๋ฅผ ๋ด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์ฐ์ , singleton ํจํค์ง์ SingletonTest ํด๋์ค์์ ๋ฐ์ ๋ง์ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
@Test
@DisplayName("์ฑ๊ธํค ํจํด์ ์ ์ฉํ ๊ฐ์ฒด ์ฌ์ฉ")
void singletonServiceTest() {
new SingletonService();
}
new๋ก ์์ฑํ๋ ค๊ณ ํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?
private access ๋๋ฌธ์ ์ ๋๋ค๊ณ ์ปดํ์ผ ์ค๋ฅ๋ฅผ ๋ฑ ์ค๋ฒ๋ฆฝ๋๋ค.
๊ทธ๋ผ ์ ๋๋ก ํ ๋ฒ ์ฝ๋ ์์ฑ์ ํด๋ณด๊ฒ ์ต๋๋ค.
@Test
@DisplayName("์ฑ๊ธํค ํจํด์ ์ ์ฉํ ๊ฐ์ฒด ์ฌ์ฉ")
void singletonServiceTest() {
//์กฐํ
SingletonService singletonService1 = SingletonService.getInstance();
SingletonService singletonService2 = SingletonService.getInstance();
System.out.println("singletonService1 = " + singletonService1);
System.out.println("singletonService2 = " + singletonService2);
}
์๊น์ฒ๋ผ singletonService1,2 ์ด 2๊ฐ๋ฅผ ์กฐํํด๋ณผ๊ฑด๋ฐ์.
getInstance๋ฅผ ํตํด์๋ง ์กฐํํ ์ ์์ต๋๋ค.
๊ฒฐ๊ณผ๊ฐ ์ด๋ป๊ฒ ๋์ฌ๊น์?
์!! ์๊น ์ฑ๊ธํค ์ ์ฉ ์ ๊ณผ ๋ค๋ฅด๊ฒ ์ด๋ฒ์ ๊ฐ์ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์๋ค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
๊ฐ์ ๊ฐ์ฒด ์ธ์คํด์ค๊ฐ ๋ฐํ์ด ๋๊ฑฐ์ฃ .
๊ทธ๋ผ ๊ฒ์ฆํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
assertThat(singletonService1).isSameAs(singletonService2);
์์์๋ ๋์๋๋ฐ isSameAs์ isEqualTo์ ์ ํํ ์ฐจ์ด์ ์ ๋น๊ตํด๋ณด๊ฒ ์ต๋๋ค.
๐ฅisSameAs vs IsEqualTo๐ฅ
<isSameAs>
- ๋์ผํ ์ธ์คํด์ค์ธ์ง ๋น๊ตํฉ๋๋ค. (์๋ฐ์์ ==๋น๊ต)
- ๋ ๊ฐ์ฒด๊ฐ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์๋ฅผ ์ฐธ์กฐํ๊ณ ์๋์ง ํ์ธํฉ๋๋ค.
ex) string a = new String("hello");
string b = new String("hello");
string c = a;
assertThat(a).isSameAs(c); //โ๏ธํต๊ณผ (๊ฐ์ ๊ฐ์ฒด)
assertThat(a).isSameAs(b); //โ์คํจ (๋ค๋ฅธ ๊ฐ์ฒด์ง๋ง ๊ฐ์ ๊ฐ์)
โญ์ฆ, ๊ฐ์ฒด ์ฐธ์กฐ ๋น๊ต(==)์ ๋๋ค. ๊ฐ์ ์ธ์คํด์ค์ผ ๋๋ง ํต๊ณผํด์.โญ
<isEqualTo>
- equals() ๋ฉ์๋๋ฅผ ๊ธฐ์ค์ผ๋ก ๋น๊ตํฉ๋๋ค.
- ๋ ๊ฐ์ฒด์ ๋ด์ฉ(๊ฐ)์ด ๊ฐ์์ง๋ฅผ ํ์ธํฉ๋๋ค.
- obj1.equals(obj2)๊ฐ true์ด๋ฉด ํต๊ณผํฉ๋๋ค.
ex) string a = new String("hello");
string b = new String("hello");
assertThat(a).isEqualTo(b); //โ๏ธํต๊ณผ (๋ด์ฉ์ด ๊ฐ์)
โญ์ฆ, ๊ฐ ๋น๊ต(.equals())์ ๋๋ค. ๋ด์ฉ์ด ๊ฐ์ผ๋ฉด ํต๊ณผํด์.โญ
์ค๋ ํ ๊ฒ์ ์๊ฐํด๋ณด๋ฉด private๋ก new ํค์๋๋ฅผ ๋ง์๋์๊ณ , ํธ์ถํ ๋๋ง๋ค ๊ฐ์ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
์ฐธ๊ณ !! ์ฑ๊ธํค ํจํด์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ฌ๊ฐ์ง๊ฐ ์์ต๋๋ค. ์ฌ๊ธฐ์๋ ๊ฐ์ฒด๋ฅผ ๋ฏธ๋ฆฌ ์์ฑํด๋๋ ๊ฐ์ฅ ๋จ์ํ๊ณ ์์ ํ ๋ฐฉ๋ฒ์ ์ ํํ์ต๋๋ค.
์ฑ๊ธํค ํจํด์ ์ ์ฉํ๋ฉด ๊ณ ๊ฐ์ ์์ฒญ์ด ์ฌ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๊ฒ์ด ์๋๋ผ, ์ด๋ฏธ ๋ง๋ค์ด์ง ๊ฐ์ฒด๋ฅผ ๊ณต์ ํด์ ํจ์จ์ ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
ํ์ง๋ง, ์ฑ๊ธํค ํจํด์ ๋ค์๊ณผ ๊ฐ์ ์ ๋ง์ ๋ฌธ์ ์ ๋ค์ ๊ฐ๊ณ ์์ต๋๋ค.
<์ฑ๊ธํค ํจํด ๋ฌธ์ ์ ๐ป>
- ์ฑ๊ธํค ํจํด์ ๊ตฌํํ๋ ์ฝ๋ ์์ฒด๊ฐ ๋ง์ด ๋ค์ด๊ฐ๋๋ค.
- ์์กด ๊ด๊ณ ์ ํด๋ผ์ด์ธํธ๊ฐ ๊ตฌ์ฒด ํด๋์ค์ ์์กดํฉ๋๋ค. -> DIP ์๋ฐ!!
- ํด๋ผ์ด์ธํธ๊ฐ ๊ตฌ์ฒด ํด๋์ค์ ์์กดํด์ OCP ์์น์ ์๋ฐํ ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค.
- ํ ์คํธ ํ๊ธฐ ์ด๋ ต์ต๋๋ค.
- ๋ด๋ถ ์์ฑ์ ๋ณ๊ฒฝํ๊ฑฐ๋ ์ด๊ธฐํ ํ๊ธฐ ์ด๋ ต์ต๋๋ค.
- private ์์ฑ์๋ก ์์ ํด๋์ค๋ฅผ ๋ง๋ค๊ธฐ ์ด๋ ต์ต๋๋ค.
- ๊ฒฐ๋ก ์ ์ผ๋ก ์ ์ฐ์ฑ์ด ๋จ์ด์ง๋๋ค.
- ์ํฐ ํจํด์ผ๋ก ๋ถ๋ฆฌ๊ธฐ๋ ํฉ๋๋ค.
์ด์ , ์ฑ๊ธํค์ ๊ณต๋ถํ์ผ๋ฏ๋ก AppConfig๋ฅผ ์ฑ๊ธํค์ผ๋ก ์ซ ๋ฐ๊พธ๊ณ getInstance๋ก ๋ฐํํ๋ ์์ ์ ํด๋ณด๋ฉด ๋๊ฒ ์ฃ ?
์์ ๋จ์ ์ ๋ณด๋ฉด ์๊ฒ ์ง๋ง, ์ฌ์ค ๊ทธ๋ด ํ์ ์์ต๋๋ค.
์คํ๋ง ์ปจํ ์ด๋๋ฅผ ์ฌ์ฉํ๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก ๊ฐ์ฒด๋ฅผ ์ฑ๊ธํค์ผ๋ก ๋ง๋ค์ด์ ๊ด๋ฆฌํด์ฃผ๊ธฐ ๋๋ฌธ์ ๋๋ค.

3. ์ฑ๊ธํค ์ปจํ ์ด๋
์คํ๋ง ์ปจํ ์ด๋๋ ์ฑ๊ธํค ํจํด์ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๋ฉด์, ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ์ฑ๊ธํค(1๊ฐ๋ง ์์ฑ)์ผ๋ก ๊ด๋ฆฌํฉ๋๋ค.
์ง๊ธ๊น์ง์ ์คํ๋ง ๋น์ด ์ฑ๊ธํค์ผ๋ก ๊ด๋ฆฌ๋๋ ๋น์ ๋๋ค.
<์ฑ๊ธํค ์ปจํ ์ด๋>
- ์คํ๋ง ์ปจํ
์ด๋๋ ์ฑ๊ธํค ํจํด์ ์ ์ฉํ์ง ์์๋, ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ์ฑ๊ธํค์ผ๋ก ๊ด๋ฆฌํฉ๋๋ค.
- ์ด์ ์ ๋ดค๋ ์ปจํ ์ด๋ ์์ฑ ๊ณผ์ ์ ์์ธํ ๋ด๋ณด๋ฉด, ์ปจํ ์ด๋๋ ๊ฐ์ฒด๋ฅผ ํ๋๋ง ์์ฑํด์ ๊ด๋ฆฌํฉ๋๋ค.
- ์คํ๋ง ์ปจํ
์ด๋๋ ์ฑ๊ธํค ์ปจํ
์ด๋ ์ญํ ์ ํฉ๋๋ค.
์ด๋ ๊ฒ ์ฑ๊ธํค ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ๊ด๋ฆฌํ๋ ๊ธฐ๋ฅ์ ์ฑ๊ธํค ๋ ์ง์คํธ๋ฆฌ๋ผ๊ณ ํฉ๋๋ค. - ์คํ๋ง ์ปจํ
์ด๋์ ์ด๋ฐ ๊ธฐ๋ฅ ๋๋ถ์ ์ฑ๊ธํค ํจํด์ด ๋ชจ๋ ๋จ์ ์ ํด๊ฒฐํ๋ฉด์ ๊ฐ์ฒด๋ฅผ ์ฑ๊ธํค์ผ๋ก ์ ์งํ ์ ์์ต๋๋ค.
- ์ฑ๊ธํค ํจํด์ ์ํ ์ง์ ๋ถํ ์ฝ๋๊ฐ ๋ค์ด๊ฐ์ง ์์๋ ๋ฉ๋๋ค.
- DIP, OCP, ํ ์คํธ, private ์์ฑ์๋ก๋ถํฐ ์์ ๋กญ๊ฒ ์ฑ๊ธํค์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์์์ ํ๋ SingletonTest ์๋ฐ ํด๋์ค์์ ํ ์คํธ ์ฝ๋๋ฅผ ๋ง์ ์ง๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
@Test
@DisplayName("์คํ๋ง ์ปจํ
์ด๋์ ์ฑ๊ธํค")
void springContainer() {
//AppConfig appConfig = new AppConfig();
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
//1. ์กฐํ: ํธ์ถํ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑ
MemberService memberService1 = ac.getBean("memberService", MemberService.class);
//2. ์กฐํ: ํธ์ถํ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑ
MemberService memberService2 = ac.getBean("memberService", MemberService.class);
//์ฐธ์กฐ๊ฐ์ด ๊ฐ์ ๊ฒ์ ํ์ธ
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
//๊ฒ์ฆ
//memberService1๊ณผ memberService2๋ ๊ฐ์์ผ ํ๋ค.
assertThat(memberService1).isSameAs(memberService2);
}
์๊น ์์์ ํ๋ ์คํ๋ง์ ์ฌ์ฉํ์ง ์์ pureContainer์ ์ฝ๋๋ฅผ ๋น๊ตํด์ ๋ณด๋ฉด ํ์คํ ๋ ์ ๋ณด์ผ ๊ฒ์ ๋๋ค.
๊ฐ์ ๊ฐ์ฒด๋ก ๊ฐ์ ธ์จ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
์๊น ๋งจ ์ฒ์์ ๊ทธ๋ฆผ์์ ์ฑ๊ธํค ์ปจํ ์ด๋๋ฅผ ์ ์ฉํ ํ๋ฅผ ๋ด๋ณผ๊น์?
1. ํด๋ผ์ด์ธํธ A, B, C๊ฐ memberService๋ฅผ ์์ฒญํฉ๋๋ค.
2. ๊ฐ์์๊ฒ ๋์ผํ memberService๋ฅผ ๋ฐํํด์ค๋๋ค.
์คํ๋ง ์ปจํ ์ด๋ ๋๋ถ์ ๊ณ ๊ฐ์ ์์ฒญ์ด ์ฌ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๊ฒ์ด ์๋, ์ด๋ฏธ ๋ง๋ค์ด์ง ๊ฐ์ฒด๋ฅผ ๊ณต์ ํด์ ํจ์จ์ ์ผ๋ก ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ฐธ๊ณ !! ์คํ๋ง์ ๊ธฐ๋ณธ ๋น ๋ฑ๋ก ๋ฐฉ์์ ์ฑ๊ธํค์ด์ง๋ง, ์ฑ๊ธํค ๋ฐฉ์๋ง ์ง์ํ๋ ๊ฒ์ ์๋๋๋ค.
์์ฒญํ ๋๋ง๋ค ์๋ก์ด ๊ฐ์ฒด๋ฅผ ์์ฑํด์ ๋ฐํํ๋ ๊ธฐ๋ฅ๋ ์ ๊ณตํฉ๋๋ค.
์์ธํ ๋ด์ฉ์ ๋ค์ ๋น ์ค์ฝํ์์ ๋์ฌ ์์ ์ ๋๋ค.

4. ์ฑ๊ธํค ๋ฐฉ์์ ์ฃผ์์
์ฑ๊ธํค ํจํด์ด๋ , ์คํ๋ง ๊ฐ์ ์ฑ๊ธํค ์ปจํ ์ด๋๋ฅผ ์ฌ์ฉํ๋ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ํ๋๋ง ์์ฑํด์ ๊ณต์ ํ๋ ์ฑ๊ธํค ๋ฐฉ์์ ์ฌ๋ฌ ํด๋ผ์ด์ธํธ๊ฐ ํ๋์ ๊ฐ์ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ๊ณต์ ํ๊ธฐ ๋๋ฌธ์ ์ฑ๊ธํค ๊ฐ์ฒด๋ ์ํ๋ฅผ ์ ์ง(Stateful)ํ๊ฒ ์ค๊ณํ๋ฉด ์ ๋ฉ๋๋ค.
์ฆ, ๋ฌด์ํ(Stateless)๋ก ์ค๊ณํด์ผ ํฉ๋๋ค.
- ํน์ ํด๋ผ์ด์ธํธ์ ์์กด์ ์ธ ํ๋๊ฐ ์์ผ๋ฉด ์ ๋ฉ๋๋ค.
- ๊ฐ๊ธ์ ์ฝ๊ธฐ๋ง ๊ฐ๋ฅํด์ผ ํฉ๋๋ค.
- ํ๋ ๋์ ์ ์๋ฐ์์ ๊ณต์ ๋์ง ์๋ ์ง์ญ๋ณ์, ํ๋ผ๋ฏธํฐ, ThreadLocal ๋ฑ์ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
์คํ๋ง ๋น์ ํ๋์ ๊ณต์ ๊ฐ์ ์ค์ ํ๋ฉด ์์ฒญ ํฐ ์ฅ์ ๊ฐ ๋ฐ์ํ ์ ์ด์์!!ใ ใ
์ํ๋ฅผ ์ ์งํ ๊ฒฝ์ฐ ๋ฐ์ํ๋ ๋ฌธ์ ์ ์์ ์ฝ๋๋ฅผ ์์ฑํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
Test ํจํค์ง์์ hello.core์ singleton ํจํค์ง์์ StatefulService ์๋ฐ ํด๋์ค๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
public class StatefulService {
//ํ
์คํธ ๊ณผ์ ์ ํตํด 10,000์->20,000์์ผ๋ก ๋ฐ๋์ด๋ฒ๋ฆผ
private int price; //์ํ๋ฅผ ์ ์งํ๋ ๊ฐ๊ฒฉ์ด๋ผ๋ ํ๋
public void order(String name, int price) {
System.out.println("name = " + name + " price = " + price);
this.price = price; //์ฌ๊ธฐ๊ฐ ๋ฌธ์ !!
}
public int getPrice() {
return price;
}
}
์ํ๋ฅผ ์ ์งํ๋ ๊ฐ๊ฒฉ์ด๋ผ๋ ํ๋๋ฅผ ๋ง๋ค์์ต๋๋ค. ์ด ๊ฐ๊ฒฉ ํ๋๋ ๊ณต์ ํ๋ ์ ๋๋ค.
๊ทธ๋ฆฌ๊ณ , ์ฃผ๋ฌธ ํจ์๋ฅผ ๋ง๋ค์๋๋ฐ ์ฌ๊ธฐ์ this.price = price;๊ฐ ๋ฌธ์ ๊ฐ ๋ฉ๋๋ค.
์์ธ์ง๋ ํ ์คํธ ํด๋ณด๋ฉด์ ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
StatefulService์ ๋ํ ํ ์คํธ๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
์ฌ์ค ๊ทธ๋ฅ Test ํจํค์ง์ hello.core์ singleton ํจํค์ง์์ ๊ฑ ํ ์คํธ๋ฅผ ์์ฑํด๋ ๋์ง๋ง ์ ํฌ๋ ๋จ์ถํค๋ฅผ ํ ๋ฒ ์จ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
Window ๊ธฐ์ค ctrl + shift + t๋ฅผ ๋๋ฅด๋ฉด ๋ฐ์ ์ฌ์ง์ฒ๋ผ ๋์ต๋๋ค. (Mac์ command + shift + t)
์ฌ๊ธฐ์ ์๋ฌด ๊ฒ๋ ๊ฑด๋ค์ง ์๊ณ ํ์ธ์ ๋๋ฆ ๋๋ค.
class StatefulServiceTest {
@Test
void statefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
//ThreadA: A ์ฌ์ฉ์๊ฐ 10,000์ ์ฃผ๋ฌธ
statefulService1.order("userA", 10000);
//ThreadB: B ์ฌ์ฉ์๊ฐ 20,000์ ์ฃผ๋ฌธ
statefulService2.order("userB", 20000);
//ThreadA: A ์ฌ์ฉ์ ์ฃผ๋ฌธ ๊ธ์ก ์กฐํ
int price = statefulService1.getPrice();
//ThreadA: A ์ฌ์ฉ์๋ 10,000์์ ๊ธฐ๋ํ์ง๋ง ๊ธฐ๋์ ๋ค๋ฅด๊ฒ 20,000์ ์ถ๋ ฅ
System.out.println("price = " + price);
}
static class TestConfig {
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
}
}
A ์ฌ์ฉ์๊ฐ 10,000์ ์ฃผ๋ฌธ์ ํธ์ถํ๊ณ , ๋ณธ์ธ์ด ์ฃผ๋ฌธํ ๊ธ์ก์ ์กฐํํ๋ ค๊ณ ํ๋๋ฐ ๊ทธ ์ฌ์ด์ B ์ฌ์ฉ์๊ฐ 20,000์ ์ฃผ๋ฌธ์ ํด๋ฒ๋ฆฝ๋๋ค.
A ์ฌ์ฉ์๊ฐ ์ฃผ๋ฌธํ ๊ธ์ก์ ์กฐํํ๋ ค๊ณ ํ๋ฉด ์ผ๋ง๊ฐ ๋์ฌ๊น์?
์์ํ๊ฒ ์ง๋ง 20,000์์ด ๋์ต๋๋ค.
statefulService1์ด๋ statefulService2๋ ๋ค์ด๊ฐ๋ฉด ์ธ์คํด์ค๋ ๊ฐ์ ์ ๋ก ์ธ์์ ํด ๋ฒ๋ฆฌ๊ธฐ ๋๋ฌธ์ ์ค๊ฐ์ 20,000์์ผ๋ก ๊ธ์ก์ด ๋ฐ๋๋ฉด ์กฐํ๋ 20,000์์ผ๋ก ๋ฉ๋๋ค.
๊ฒ์ฆ์ ํด ๋ณด๊ฒ ์ต๋๋ค.
assertThat(statefulService1.getPrice()).isEqualTo(20000);
statefulService1์ ๋ถ๋ช 10,000์์ด์๋๋ฐ ์ฌ๊ธฐ์ 20,000์๊ณผ ๊ฐ๋ค๊ณ ๋์ค๋ฉด ์ด ํ ์คํธ๋ ์ฑ๊ธํค ๋ฐฉ์์ ์๋ชป ์ฌ์ฉํ๋ค๋ ๋ป์ ๋๋ค.
ํ ๋ฒ ํด๋ณผ๊ฒ์.
๋ค ๋งํ์ต๋๋ค. ์ด๋ ๊ฒ ์๋น์ค ์ถ์๋๋ฉด ์ ๋ผ์!
- ์ต๋ํ ๋จ์ํ๊ฒ ๋ณด๊ธฐ ์ํด, ์ค์ ์ฐ๋ ๋๋ ์ฌ์ฉํ์ง ์์์ต๋๋ค. (๋ฉํฐ ์ฐ๋ ๋ ๋๋ฌด ์ด๋ ค์ใ ใ ใ )
- ThreadA๊ฐ A ์ฌ์ฉ์ ์ฝ๋๋ฅผ ํธ์ถํ๊ณ , ThreadB๊ฐ B ์ฌ์ฉ์ ์ฝ๋๋ฅผ ํธ์ถํ๋ค๊ณ ๊ฐ์ ํด๋ณด๊ฒ ์ต๋๋ค.
=> StatefulService์ price ํ๋๋ ๊ณต์ ๋๋ ํ๋์ธ๋ฐ, ํน์ ํด๋ผ์ด์ธํธ๊ฐ ๊ฐ์ ๋ณ๊ฒฝํฉ๋๋ค.
=> A ์ฌ์ฉ์์ ์ฃผ๋ฌธ ๊ธ์ก์ 10,000์์ด ๋์ด์ผ ํ๋๋ฐ, 20,000์์ด๋ผ๋ ๊ฒฐ๊ณผ๊ฐ ๋์์ต๋๋ค.
=> ์ค๋ฌด์์ ์ด๋ฐ ๊ฒฝ์ฐ๋ฅผ ์ข ์ข ๋ณด๋๋ฐ, ์ด๋ก ์ธํด ์ ๋ง ํด๊ฒฐํ๊ธฐ ์ด๋ ค์ด ํฐ ๋ฌธ์ ๋ค์ด ํฐ์ง๋๋ค. (๋ช ๋ ์ ํ ๋ฒ์ฉ ๊ผญ ๋ง๋ฉ๋๋ค.)
=> ์ง์ง ๊ณต์ ํ๋๋ ์กฐ์ฌํด์ผ ํฉ๋๋ค. ์คํ๋ง ๋น์ ํญ์ ๋ฌด์ํ(Stateless)๋ก ์ค๊ณํด์ผ ํฉ๋๋ค!!
์์ ์ํ์ ์ง(Stateful) ์ฝ๋๋ฅผ ๋ฌด์ํ(Stateless) ์ฝ๋๋ก ๋ณ๊ฒฝํด๋ณด๊ฒ ์ต๋๋ค.
๋ฐ๋ก ํด๋์ค๋ ๋ง๋ค์ง ์๊ณ , ์ ํด๋์ค์์ ์์ ํ๋๋ก ํ๊ฒ ์ต๋๋ค.
StatefulService ์๋ฐ ํด๋์ค๋ก ์ด๋ํด๋ณด๊ฒ ์ต๋๋ค.
public class StatefulService {
//private int price; //์ํ๋ฅผ ์ ์งํ๋ ๊ฐ๊ฒฉ์ด๋ผ๋ ํ๋ (Stateful ๋ ์ฌ์ฉ)
//Stateful ๋ ์ฌ์ฉ
/*
public void order(String name, int price) {
System.out.println("name = " + name + " price = " + price);
this.price = price; //์ฌ๊ธฐ๊ฐ ๋ฌธ์ !!
}
*/
//๊ณต์ ํ๋๋ฅผ ์ ์งํ์ง ์๊ณ , ํจ์๋ฅผ void๊ฐ ์๋ int๋ก ํด์ return price๋ฅผ ํ๋ฉด ๋ฉ๋๋ค.
public int order(String name, int price) {
System.out.println("name = " + name + " price = " + price);
//this.price = price; //์ฌ๊ธฐ๊ฐ ๋ฌธ์ !!, Stateful ๋ ์ฌ์ฉ
return price;
}
//Stateful ๋ ์ฌ์ฉ
/* public int getPrice() {
return price;
} */
}
๊ณต์ ํ๋๋ฅผ ์ ์งํ์ง ์๊ณ , ํจ์๋ฅผ void๊ฐ ์๋ int ๋ก ํด์ return price๋ฅผ ํ๋ฉด ๋ฉ๋๋ค.
StatefulServiceTest ์๋ฐ ํด๋์ค๋ก ์ด๋ํด๋ณด๊ฒ ์ต๋๋ค.
class StatefulServiceTest {
@Test
void statefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
//ThreadA: A ์ฌ์ฉ์๊ฐ 10,000์ ์ฃผ๋ฌธ
//statefulService1.order("userA", 10000); //Stateful ๋ ์ฌ์ฉ
int userAPrice = statefulService1.order("userA", 10000);
//ThreadB: B ์ฌ์ฉ์๊ฐ 20,000์ ์ฃผ๋ฌธ
//statefulService2.order("userB", 20000); //Stateful ๋ ์ฌ์ฉ
int userBPrice = statefulService1.order("userB", 20000);
//ThreadA: A ์ฌ์ฉ์ ์ฃผ๋ฌธ ๊ธ์ก ์กฐํ
// int price = statefulService1.getPrice(); //Stateful ๋ ์ฌ์ฉ
//ThreadA: A ์ฌ์ฉ์๋ 10,000์์ ๊ธฐ๋ํ์ง๋ง ๊ธฐ๋์ ๋ค๋ฅด๊ฒ 20,000์ ์ถ๋ ฅ
//System.out.println("price = " + price); //Stateful ๋ ์ฌ์ฉ
System.out.println("price = " + userAPrice);
//assertThat(statefulService1.getPrice()).isEqualTo(20000);
}
static class TestConfig {
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
}
}
๋ฌด์ํ์ ์กฐ๊ฑด ์ค ํ๋๊ฐ ์ง์ญ๋ณ์ ์ฌ์ฉ์ด ์์๊ธฐ ๋๋ฌธ์ ์ง์ญ๋ณ์ userAPrice, userBPrice๋ฅผ ๋ง๋ค์ด์ค๋๋ค.
์ด ์ํ์์ userAPrice๋ฅผ ์กฐํํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?
ํ ์คํธ๊ฐ ์ ํต๊ณผ๋์์ต๋๋ค.
์ง์ญ๋ณ์๋ ๊ณต์ ๋์ง ์๊ธฐ ๋๋ฌธ์ด์ฃ .
์ค๋ฌด์์ ์ด๊ฑฐ ์ง์ง ํฌ๊ฒ ๋ฌธ์ ๊ฐ ๋๋ฉด ๋์ ๊ฒฐ์ ๋ด์ญ, ๋ก๊ทธ์ธ ์ ๋ณด๊ฐ ๋ค๋ฅธ ์ฌ๋ํํ ๋ณด์ด๊ณ ํ๋๊ฑฐ์์..ใทใท
๋ณต๊ตฌํ๋๋ฐ ๋ช ๋ฌ์ ๊ฑธ๋ฆฐ๋ค๋ค์.

5. @Configuration๊ณผ ์ฑ๊ธํค
@Configuration์ ์ฑ๊ธํค์ ์ํด ์กด์ฌํ๋ ๊ฒ์ ๋๋ค.
์ฝ๋๋ฅผ ๋ด๋ณด๊ฒ ์ต๋๋ค.
AppConfig ์๋ฐ ํด๋์ค๋ก ์ด๋ํด๋ณด๊ฒ ์ต๋๋ค.
@Configuration
//์ค์ ์ ๋ณด, ๊ตฌ์ฑ ์ ๋ณด
public class AppConfig {
@Bean
//๊ฐ ๋ฉ์๋๋ง๋ค @Bean ์ ์ด์ค
//์ด๋ ๊ฒ ํ๋ฉด ์คํ๋ง ์ปจํ
์ด๋์ ์ ์ฉ๋จ
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
//return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
memberService ๋ถ๋ถ์ ๋ด๋ณด๊ฒ ์ต๋๋ค.
@Bean์ด ์๊ณ ๊ทธ ๋ค์์ memberService๋ฅผ ํ๋ฉด new๋ก MemberServiceImpl์ ํธ์ถํ๊ณ , memberRepository๋ฅผ ํธ์ถํ๋ฉด์ new MemoryMemberRepository๋ฅผ ํธ์ถํด์ฃผ๊ฒ ๋ฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ , orderService ๋ถ๋ถ์ ๋ด๋ณด๊ฒ ์ต๋๋ค.
@Bean์ด ์๊ณ ๊ทธ ๋ค์์ orderService๋ฅผ ํ๋ฉด new๋ก OrderServiceImpl์ ํธ์ถํ๊ณ , memberRepository์ discountPolicy๋ฅผ ํธ์ถํ๋ฉด์ ๋ new MemoryMemberRepository๋ฅผ ํธ์ถํด์ฃผ๊ฒ ๋ฉ๋๋ค.
์ด๋ผ? 2๋ฒ ํธ์ถ์ ํ๋ค์?
์ด๋ฌ๋ฉด ์ฑ๊ธํค์ด ๊นจ์ง๋ ๊ฒ์ด ์๋๊ฐ์?
์ง์ ํ ์คํธ๋ฅผ ํด๋ณด๊ฒ ์ต๋๋ค.
ํ ์คํธ ํ๋ ๋ฐฉ๋ฒ์ ๊ฐ๋จํฉ๋๋ค.
MemberServiceImpl์ ์๋ MemberRepository์ ๊ฐ์ ํ์ธํด๋ณด๋ฉด ๋ฉ๋๋ค.
MemberServiceImpl ์ฝ๋๋ก ์ด๋ํด๋ณด๊ฒ ์ต๋๋ค.
//ํ
์คํธ ์ฉ๋
public MemberRepository getMemberRepository() {
return memberRepository;
}
ํ ์คํธ ์ฉ๋์ด๋ฏ๋ก ์ธํฐํ์ด์ค๊น์ง ๋ง๋ค์ง๋ ์์์ต๋๋ค.
OrderServiceImpl ์ฝ๋๋ก๋ ์ด๋ํด์ ์์์ฒ๋ผ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค.
//ํ
์คํธ ์ฉ๋
public MemberRepository getMemberRepository() {
return memberRepository;
}
MemberServiceImpl๊ณผ OrderServiceImpl ์ฝ๋๋ฅผ ๋ด๋ณด๋ฉด ๋ ๋ค private final MemberRepository memberRepository;๊ฐ ์๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
ํ ์คํธ ์๋ฐ ํด๋์ค๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
test ํจํค์ง์ hello.core์ singleton ํจํค์ง์ ConfigurationSingletonTest ์๋ฐ ํด๋์ค๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
public class ConfigurationSingletonTest {
@Test
void configurationTest() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
}
}
๋ถ๋ช ๊ตฌ์ฒด ํด๋์ค๋ก ๊บผ๋ด๋ฉด ์ ์ข๋ค๊ณ ํ๋๋ฐ ์ ๊ตฌ์ฒด ํด๋์ค๋ก ๊บผ๋ด๋๋ฉด..
Impl๋ก ๊บผ๋ด์ผ ์์์ ๋ง๋ค์๋ getMemberRepository ํจ์๋ฅผ ๋ถ๋ฌ์ฌ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
๋ง์ ์์ฑ์ ํด๋ณด๊ฒ ์ต๋๋ค.
public class ConfigurationSingletonTest {
@Test
void configurationTest() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
MemberRepository memberRepository1 = memberService.getMemberRepository();
MemberRepository memberRepository2 = orderService.getMemberRepository();
System.out.println("memberService -> memberRepository = " + memberRepository1);
System.out.println("orderService -> memberRepository = " + memberRepository2);
}
}
MemberServiceImpl๊ณผ OrderServiceImpl ๋ ๋ค MemberRepository๋ฅผ ๋ถ๋ฌ์ค๊ณ , MemoryMemberRepository๋ฅผ ๋ถ๋ฌ์ค๊ธฐ ๋๋ฌธ์ ๊ตฌ๋ถ์ ์ง๊ธฐ ์ํด์ memberRepository1, memberRepository2๋ก ์ด๋ฆ์ ์ง์์ต๋๋ค.
๊ณผ์ฐ memberRepository1๊ณผ memberRepository2๋ ๋ค๋ฅธ ์ ๊ฐ ๋์ฌ๊น์?
ํ ์คํธ๋ฅผ ์คํํด๋ณด๊ฒ ์ต๋๋ค.
๊ฒฐ๊ณผ๊ฐ ๋๊ฐ๊ฒ ๋์์ต๋๋ค.
๊ฒ์ฆ์ ํ๊ธฐ ์ ์ ํ ๊ฐ์ง ๊ณผ์ ์ ๋ ํด๋ณด๊ฒ ์ต๋๋ค.
Impl์ ํตํด์ ์กฐํํ๋ ๊ฒ์ด ์๋, ์ง์ง MemberRepository๋ฅผ ์กฐํํด๋ณด๊ฒ ์ต๋๋ค.
@Bean์ผ๋ก ๋ฑ๋ก๋์ด์๊ณ , new๋ก MemoryMemberRepository๋ฅผ ๋ฆฌํดํ๊ธฐ ๋๋ฌธ์ ์๋ง ๊ฒฐ๊ณผ๋ ์์์ด ๊ฐ๋๋ฐ์.
public class ConfigurationSingletonTest {
@Test
void configurationTest() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
//์ถ๊ฐ ํ
์คํธ
MemberRepository memberRepository = ac.getBean("memberRepository", MemberRepository.class);
MemberRepository memberRepository1 = memberService.getMemberRepository();
MemberRepository memberRepository2 = orderService.getMemberRepository();
System.out.println("memberService -> memberRepository = " + memberRepository1);
System.out.println("orderService -> memberRepository = " + memberRepository2);
//์ถ๊ฐ ํ
์คํธ
System.out.println("memberRepository = " + memberRepository);
}
}
ํ ์คํธ๋ฅผ ์คํํด๋ณด๊ฒ ์ต๋๋ค.
์ญ์ 3๊ฐ์ ์คํ๋ง ๋น์ ๋ค ๋๊ฐ์ต๋๋ค.
๊ฒ์ฆ๋ ํด๋ณด๊ฒ ์ต๋๋ค.
assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
๊ฒ์ฆ๋ ์ ๋ง์ต๋๋ค.
์์์ ํ ๊ฒ๋ค์ ํ ๋ฒ ์ ๋ฆฌโ๏ธํด๋ณด๊ฒ ์ต๋๋ค.
- ํ์ธํด๋ณด๋ฉด memberRepository ์ธ์คํด์ค๋ ๋ชจ๋ ๊ฐ์ ์ธ์คํด์ค๊ฐ ๊ณต์ ๋์ด ์ฌ์ฉ๋ฉ๋๋ค. (๊ฐ์ ์ ๊ฐ 1๋ฒ ์์ฑ)
- AppConfig์ ์๋ฐ ์ฝ๋๋ฅผ ๋ณด๋ฉด ๋ถ๋ช ํ ๊ฐ๊ฐ 2๋ฒ new MemoryMemberRepository๋ฅผ ํธ์ถํด์ ๋ค๋ฅธ ์ธ์คํด์ค๊ฐ ์์ฑ๋์ด์ผ ํ๋๋ฐ ์ด๋ป๊ฒ ๋ ์ผ ์ผ๊น์? ํน์ 2๋ฒ ํธ์ถ์ด ์๋๋ ๊ฒ์ผ๊น์?
์คํ์ ํตํด ์์๋ณด๊ฒ ์ต๋๋ค.

AppConfig ํด๋์ค๋ก ์ด๋ํด๋ณด๊ฒ ์ต๋๋ค.
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
System.out.println("call AppConfig.orderService");
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
//return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
์ด๋ ๊ฒ ํ๋ฉด ๊ฒฐ๊ณผ๋
call AppConfig.memberService -> call AppConfig.memberRepository -> call AppConfig.memberRepository -> call AppConfig.orderService -> call AppConfig.memberRepository ํธ์ถ์ด ๋์ด์ผ ํ ๊ฒ ๊ฐ์ต๋๋ค.
์ด๋ ๊ฒ memberRepository๊ฐ ์ด 3๋ฒ์ด ํธ์ถ์ด ๋์ด์ผ ํ๊ตฌ์.
๋ช ๋ฒ ํธ์ถ๋๋์ง ํ ๋ฒ ํ์ธํด๋ณด๊ฒ ์ต๋๋ค.
ConfigurationSingletonTest ๋๋ฆฌ๋ฉด ๋ฉ๋๋ค.
์ด์ํฉ๋๋ค.
๊ฒฐ๊ณผ๊ฐ call AppConfig.memberService ํธ์ถ ํ, call AppConfig.memberRepository ํธ์ถ ํ, call AppConfig.orderService๊ฐ ํธ์ถ๋์์ต๋๋ค.
memberRepository๊ฐ 3๋ฒ ํธ์ถ๋ ๊ฒ์ด ์๋.. 1๋ฒ ํธ์ถ ๋์์ต๋๋ค.
์คํ๋ง์ด ์ด๋ป๊ฒ๋ ์ฑ๊ธํค์ ๋ณด์ฅํด์ฃผ๋ ค๊ณ ํ๋ค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
์ด๊ฒ์ ๋ํ ์์ธํ ์ค๋ช ์ ๋ฐ์์ ํ๋๋ก ํ๊ฒ ์ต๋๋ค.

6. @Configuration๊ณผ ๋ฐ์ดํธ์ฝ๋ ์กฐ์์ ๋ง๋ฒ
์คํ๋ง ์ปจํ ์ด๋๋ ์ฑ๊ธํค ๋ ์ง์คํธ๋ฆฌ์ ๋๋ค.
๋ฐ๋ผ์, ์คํ๋ง ๋น์ด ์ฑ๊ธํค์ด ๋๋๋ก ๋ณด์ฅํด์ฃผ์ด์ผ ํฉ๋๋ค.
๊ทธ๋ฐ๋ฐ, ์คํ๋ง์ด ์๋ฐ ์ฝ๋๊น์ง ์ด๋ป๊ฒ ํ๊ธฐ๋ ์ด๋ ต์ต๋๋ค.
๋ถ๋ช .. ์์์ ํ ์๋ฐ ์ฝ๋๋๋ก๋ฉด memberRepository๊ฐ 3๋ฒ ํธ์ถ๋์ด์ผ ํ๋ ๊ฒ์ด ๋ง์ต๋๋ค.

๊ทธ๋์!!
์คํ๋ง์ ํด๋์ค์ ๋ฐ์ดํธ์ฝ๋๋ฅผ ์กฐ์ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
๋ชจ๋ ๋น๋ฐ์ @Configuration์ ์ ์ฉํ AppConfig์ ์์ต๋๋ค.
์์์ ๋ง๋ค์๋ ConfigurationSingletonTest ์๋ฐ ํด๋์ค๋ก ๊ฐ์ ๋ฐ์ ์ฝ๋๋ฅผ ๊ตฌํํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
@Test
void configurationDeep() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class); //์ด ์์
์ ํตํด ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก
AppConfig bean = ac.getBean(AppConfig.class); //์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก๋ AppConfig ์กฐํ
//์ถ๋ ฅ
System.out.println("bean = " + bean.getClass());
}
์ฌ์ค AnnotationConfigApplicationContext์ ํ๋ผ๋ฏธํฐ๋ก ๋๊ธด ๊ฐ์ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก๋ฉ๋๋ค.
๊ทธ๋์, AppConfig๋ ์คํ๋ง ๋น์ด ๋ฉ๋๋ค.
ํ ์คํธ๋ฅผ ์คํํด๋ณด๊ฒ ์ต๋๋ค.
์ถ๋ ฅ๋ ๊ฒ์ ๋ณด๋ฉด bean = class hello.core.AppConfig๊น์ง ์์ํ ๋๋ก ๋ง๊ฒ ์ถ๋ ฅ๋์๋๋ฐ ๊ทธ ๋ค์ ์ด์ํ๊ฒ ์๋ค์ฌ?
์์ํ ์๋ฐ ํด๋์ค๋ผ๋ฉด class hello.core.AppConfig๊น์ง ์ถ๋ ฅ๋๋๊ฒ ์ ์์ธ๋ฐ ๋ง์ ๋๋ค..
์์๊ณผ๋ ๋ค๋ฅด๊ฒ ํด๋์ค ๋ช ์ CGLIB ๊ฐ์ ๊ฒ์ด ๋ถ์ผ๋ฉด์ ์๋นํ ๋ณต์กํด์ง ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
์ด๊ฒ์ ์ ๊ฐ ๋ง๋ ํด๋์ค๊ฐ ์๋๋ผ ์คํ๋ง์ด CGLIB๋ผ๋ ๋ฐ์ดํธ์ฝ๋ ์กฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ AppConfig ํด๋์ค๋ฅผ ์์ ๋ฐ์ ์์์ ๋ค๋ฅธ ํด๋์ค๋ฅผ ๋ง๋ค๊ณ , ๊ทธ ๋ค๋ฅธ ํด๋์ค๋ฅผ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํ ๊ฒ์ ๋๋ค.
๋ง๋ก๋ ๋ณต์กํ๋ ๊ทธ๋ฆผ์ผ๋ก ํ ๋ฒ ๋ด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
<๊ทธ๋ฆผ์ ๋ํ ์ค๋ช โ๏ธ>
1. ์ผ์ชฝ - ํด๋์ค ๊ด๊ณ
- AppConfig: ์ฐ๋ฆฌ๊ฐ ์์ฑํ ์๋ ์ค์ ํด๋์ค (@Configuration์ด ๋ถ์ ํด๋์ค)
- AppConfig@CGLIB: ์คํ๋ง์ด ๋ฐํ์ ์์ CGLIB๋ฅผ ์ด์ฉํด ์์ฑํ ํ๋ก์ ํด๋์ค
๐๐ป์ด ํ๋ก์๊ฐ ์ค์ AppConfig๋ฅผ ์์ํ๊ณ , ๋ด๋ถ ์กฐ์ง์ ๊ฐ๋ก์ฑ์ ๋์์ ์ ์ดํฉ๋๋ค.
=> ํ์ดํ ๋ฐฉํฅ์ AppConfig@CGLIB๊ฐ AppConfig๋ฅผ ์์(ํ์ฅ)ํ๊ณ ์๋ค๋ ๋ป์ ๋๋ค.
2. ์ค๋ฅธ์ชฝ - ์คํ๋ง ์ปจํ ์ด๋ ๋ด๋ถ
- ์คํ๋ง ์ปจํ ์ด๋๋ ์๋ณธ AppConfig ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ์ง์ ์ฌ์ฉํ์ง ์์ต๋๋ค.
๋์ , ํ๋ก์ ๊ฐ์ฒด(AppConfig@CGLIB)๋ฅผ ๋น์ผ๋ก ๋ฑ๋กํด์ ์ฌ์ฉํฉ๋๋ค.
- ์ด ํ๋ก์ ๊ฐ์ฒด๋ ๋ฉ์๋ ํธ์ถ์ ๊ฐ๋ก์ฑ์ ์ฑ๊ธํค ๋น์ ๋ณด์ฅํ๋ ๋ฑ ๋ค์ํ ๋์์ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.
ex) ์์์๋ ํ๋ ์์์ธ๋ฐ, @Bean ๋ฉ์๋๋ฅผ ์ฌ๋ฌ ๋ฒ ํธ์ถํด๋ ๋์ผํ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๊ฒ ์ฒ๋ฆฌํ๋ ๊ฒ๋ ์ด CGLIB ๋๋ถ์ ๋๋ค.
์๋ฌดํผ, ๊ทธ ์์์ ๋ค๋ฅธ ํด๋์ค๊ฐ ๋ฐ๋ก ์ฑ๊ธํค์ด ๋ณด์ฅ๋๋๋ก ํด์ค๋๋ค.
์๋ง๋ ๋ฐ์์ฒ๋ผ ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์กฐ์ํด์ ์์ฑ๋์ด ์์ ๊ฒ์ ๋๋ค. (์ค์ ๋ก๋ CGLIB์ ๋ด๋ถ ๊ธฐ์ ์ ์ฌ์ฉํ๋๋ฐ ๋งค์ฐ ๋ณต์กํฉ๋๋ค.)
<AppConfig@CGLIB ์์ ์ฝ๋>
...java
@Bean
public MemberRepository memberRepository() {
if (memboryMemberRepository๊ฐ ์ด๋ฏธ ์คํ๋ง ์ปจํ
์ด๋์ ๋ฑ๋ก๋์ด ์์ผ๋ฉด?) {
return ์คํ๋ง ์ปจํ
์ด๋์์ ์ฐพ์์ ๋ฐํ;
}
else { //์คํ๋ง ์ปจํ
์ด๋์ ์์ผ๋ฉด
๊ธฐ์กด ๋ก์ง์ ํธ์ถํด์ MemoryMemberRepository๋ฅผ ์์ฑํ๊ณ , ์คํ๋ง ์ปจํ
์ด๋์ ๋ฑ๋ก;
return ๋ฐํ;
}
}
...
@Bean์ด ๋ถ์ ๋ฉ์๋๋ง๋ค ์ด๋ฏธ ์คํ๋ง ๋น์ด ์กด์ฌํ๋ฉด ์กด์ฌํ๋ ๋น์ ๋ฐํํ๊ณ , ์คํ๋ง ๋น์ด ์์ผ๋ฉด ์์ฑํด์ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํ๊ณ ๋ฐํํ๋ ์ฝ๋๊ฐ ๋์ ์ผ๋ก ๋ง๋ค์ด์ง๋๋ค.
๋๋ถ์ ์ฑ๊ธํค์ด ๋ณด์ฅ๋๋ ๊ฒ์ ๋๋ค. (์ด ์ด์ ๋ก MemberRepository๊ฐ 3๋ฒ ํธ์ถ๋์ง ์๊ณ , 1๋ฒ ํธ์ถ๋ ๊ฒ์ ๋๋ค.)
โ๏ธ์ฐธ๊ณ !! AppConfig@CGLIB๋ AppConfig์ ์์ ํ์ ์ด๋ฏ๋ก, AppConfig ํ์ ์ผ๋ก ์กฐํํ ์ ์์ต๋๋ค.

6-1. @Configuration ์ ์ฉํ์ง ์๊ณ , @Bean๋ง ์ ์ฉํ๋ฉด?
@Configuration์ ๋ถ์ด๋ฉด ๋ฐ์ดํธ์ฝ๋๋ฅผ ์กฐ์ํ๋ CGLIB ๊ธฐ์ ์ ์ฌ์ฉํด์ ์ฑ๊ธํค์ ๋ณด์ฅํ์ง๋ง, ๋ง์ฝ์ @Bean๋ง ์ ์ฉํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?

AppConfig ํด๋์ค์์ @Configuration์ ๋นผ๊ณ ConfigurationSingletonTest ํด๋์ค์ configurationDeep ํจ์๋ฅผ ์คํํด๋ณด๊ฒ ์ต๋๋ค.
์คํ๋ง ๋น์ ์ผ๋จ ๋ค ๋ฑ๋ก์ด ๋ฉ๋๋ค.
๊ทธ๋ฐ๋ฐ, ์์์ ์๊ธฐํ๋ ๋ฐ๋ก memberRepository๊ฐ 3๋ฒ ํธ์ถ๋์์ต๋๋ค. ์ฑ๊ธํค์ด ๊นจ์ง ๊ฒ์ด์ฃ .
1๋ฒ์ @Bean์ ์ํด ์คํ๋ง ์ปจํ ์ด๋์ ๋ฑ๋กํ๊ธฐ ์ํด์์ด๊ณ , 2๋ฒ์ ๊ฐ๊ฐ memberRepository()๋ฅผ ํธ์ถํ๋ฉด์ ๋ฐ์ํ ์ฝ๋์ ๋๋ค.
๊ทธ๋ฆฌ๊ณ , ์์์ ๋ณด์๋ CGLIB๊ฐ ์๋ณด์ด๋ค์. ์์ํ ์๋ฐ ์ฝ๋ ๊ทธ๋๋ก bean = class hello.core.AppConfig๋ง ์์ต๋๋ค.

์ธ์คํด์ค๊ฐ ๊ฐ์์ง ํ ์คํธ๋ฅผ ํ ๋ฒ ํด๋ณด๊ฒ ์ต๋๋ค.
์์์ ํ ์คํธ ํ ํด๋์ค์์ configurationTest ํจ์๋ฅผ ๋๋ ค๋ณด๋ฉด ๋ฉ๋๋ค.
์ด๋ป๊ฒ ๋์ฌ๊น์?
์์์๋ ๋ค๋ฅด๊ฒ @ ๋ค์ ๋ถ๋ถ์ด ์ ๋ค ๋ค๋ฅธ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
๋น์ฐํ ์ธ์คํด์ค๊ฐ ๊ฐ์์ง ํ ์คํธ ํ๋ ์ฝ๋๋ ์คํจํ๊ณ , ๊ฐ๊ฐ ๋ค ๋ค๋ฅธ MemoryMemberRepository ์ธ์คํด์ค๋ฅผ ๊ฐ๊ณ ์๋ ๊ฒ์ ๋๋ค.
๊ทธ๋ฆฌ๊ณ , ์ด๋ ๊ฒ ๋๋ฉด ์คํ๋ง ์ปจํ ์ด๋๊ฐ ๊ด๋ฆฌํ๋ ๊ฒ์ด ์๋ ์ ๊ฐ ์ง์ new๋ก ํด์ ๋ฃ์ ๊ฒ๊ณผ ๋๊ฐ์ต๋๋ค.
(๋ฌธ์ ํด๊ฒฐํ๋ ค๋ฉด ๋ค์์ ํ ๊ฑด๋ฐ @Autowired MemberRepository memberRepository;๋ฅผ ํด์ฃผ๋ฉด ๋ฉ๋๋ค.)
=> ์์กด๊ด๊ณ ์ฃผ์ ๋์ด๋ค์ด๋ ๊ฑฐ์์ฉ
์ด์ฐ๋์๋ ํ์ธ์ด ๋๋ฌ์ผ๋ฉด @Configuration์ด ๋์ํ๋๋ก ๋ค์ ๋๋ ค๋์์ผ ํฉ๋๋ค!!
<์ ๋ฆฌโ๏ธ>
- @Bean๋ง ์ฌ์ฉํด๋ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก๋์ง๋ง, ์ฑ๊ธํค์ ๋ณด์ฅํ์ง๋ ์์ต๋๋ค.
๐๐ปmemberRepository()์ฒ๋ผ ์์กด๊ด๊ณ ์ฃผ์ ์ด ํ์ํด์ ๋ฉ์๋๋ฅผ ์ง์ ํธ์ถํ ๋ ์ฑ๊ธํค์ ๋ณด์ฅํ์ง ์์ต๋๋ค.
- ์ฆ, ํฌ๊ฒ ๊ณ ๋ฏผํ ๊ฒ์ด ์์ต๋๋ค. ์คํ๋ง ์ค์ ์ ๋ณด๋ ํญ์ @Configuration์ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค!!

์ค๋ ๊ณต๋ถ๋ ์ด๋ ๊ฒ ๋์ด ๋ฌ์ต๋๋ค.
๋ค์ ์น์ ์์๋ ์ปดํฌ๋ํธ ์ค์บ์ ๊ด๋ จํด์ ํ ์์ ์ ๋๋ค.
๊ทธ๋ผ ์๋ฝ!!
