1. κ°μ
μ΄μ¬ν κ°λ°νμ¬ λ°μΉν μ¬μ΄λ νλ‘μ νΈ μλΉμ€κ° μ μλ¬Έμ νμ μ΄μ©μ μκ° νλ°μ μΌλ‘ μ¦κ°νκ³ μλ€κ³ μκ°ν΄λ³΄μ. μμ²λ νΈλν½μ κ°λΉν΄μΌ νλ μν©. μ€νλ§μ μ΄λ»κ² μ΄λ¬ν μλ§μ λμ μμ²μ κ°λΉν μ μμκΉ? μΉ μ ν리μΌμ΄μ μ μν΄ νμνλ μ€νλ§μ΄ μ·¨νλ μμ‘΄ μ λ΅μ 무μμ΄μμκΉ? λ³΄ν΅ μ무리 λμμΈ ν¨ν΄μ κ΄μ¬μ΄ μλ κ°λ°μλΌλ, μ€νλ§μ μ¬μ©νκ³ μλ€λ©΄ νλ²μ―€ λ€μ΄λ³Έ μ±κΈν€ ν¨ν΄, κ·Έλ¦¬κ³ λ λμκ°μ μ€νλ§μ΄ μ΄λ ν λ°©μμΌλ‘ μ±κΈν€μ μ μ§νλμ§ μ±κΈν€ 컨ν μ΄λκΉμ§ νμ΅ν΄λ³΄λλ‘ νμ.
2. μ±κΈν€μ κ°μ
μ±κΈν€ ν¨ν΄μ΄λ μ΄λ¦μμ μ μΆν μ μλ―, ν΄λμ€μ μΈμ€ν΄μ€κ° νκ°λ§ μμ±λλλ‘ λ³΄μ₯νλ λμμΈ ν¨ν΄μ΄λ€. κΈ°λ³Έ μμ±μλ₯Ό private μΌλ‘ μ ννκ³ μ€νν± μμμ μμ±ν΄λ νλμ μΈμ€ν΄μ€λ₯Ό ν΅ν΄ κ°μ²΄λ₯Ό μ¬μ©νκ² ν¨μΌλ‘μ¨ νλμ κ°μ²΄λ₯Ό κ³μν΄μ μ¬μ©νκ² νλ€. μμ£Ό μ λͺ ν λμμΈν¨ν΄μ΄λ―λ‘ μ½κ² μμ μ½λλ₯Ό ν΅ν΄ μ§μ νμΈν΄λ³΄μ.
public class SingletonService {
// 1. static μμμ λΆλ³ κ°μ²΄λ₯Ό 미리 νκ° μμ±νλ€.
private static final SingletonService instance = new SingletonService();
// 2. public μ κ·Ό λ©μλλ₯Ό ν΅ν΄ μ€μ§ μ΄ λ©μλλ₯Ό ν΅ν΄μλ§ κ°μ²΄μ μ κ·Ό κ°λ₯νλλ‘ νλ€.
public static SingletonService getInstance() {
return instance;
}
// 3. μμ±μλ₯Ό private μ κ·Όμ μ΄μλ‘ λ§μμΌλ‘μ¨ μΈλΆμμ new ν€μλλ₯Ό ν΅ν κ°μ²΄ μμ±μ λ°©μ§νλ€.
private SingletonService() {};
public void singletonObjectCall() {
System.out.println("["+this+"] Singleton Object Call");
}
}
κ·Έλ λ€λ©΄ μ±κΈν€ ν¨ν΄μ ν΅ν΄ μ λ§ νλμ κ°μ²΄λ§μ μ΄μ©νλμ§ ν μ€νΈμ½λλ‘ νμΈν΄λ³΄μ.
public class SingletonServiceTest {
@Test
@DisplayName("[ν
μ€νΈ] μλ° μ±κΈν€ ν
μ€νΈ")
void singletonTest() {
// private κΈ°λ³Έμμ±μλ₯Ό ν΅ν΄ λ€μκ³Ό κ°μ΄ μ κ· κ°μ²΄λ₯Ό μ μΈν μ μλ€.
// SingletonService singletonService = new SingletonService();
// λ€μκ³Ό κ°μ΄ λκ°μ μ±κΈν€ μλΉμ€ κ°μ²΄λ₯Ό μ μΈνλ€κ³ κ°μ νμ.
SingletonService singletonServiceA = SingletonService.getInstance();
SingletonService singletonServiceB = SingletonService.getInstance();
// μ μΈν΄ λ λ©μλλ₯Ό ν΅ν΄ μ°Έμ‘°κ°λ μ€μ λ‘ νμΈν΄λ³΄μ.
singletonServiceA.singletonObjectCall();
singletonServiceB.singletonObjectCall();
// isSameAs λ©μλλ₯Ό ν΅ν΄ μ°Έμ‘°κ°μ΄ κ°μμ§ ν
μ€νΈνλ€.
assertThat(singletonServiceA).isSameAs(singletonServiceB);
}
}
ν μ€νΈ μ½λλ₯Ό ν΅ν΄ νμΈν κ²°κ³Ό, μ±κΈν€ ν¨ν΄μ ν΅ν΄ νλμ μ°Έμ‘°κ°μ κ°μ§λ μ¬λ¬ κ°μ²΄λ₯Ό μμ±ν μ μμμ νμΈνλ€. νμ§λ§ μ΄λ κ² JAVA μ±κΈν€ ν¨ν΄λ§μ μ΄μ©νλ κ²μ μ¬λ¬κ°μ§ λ¨μ μ κ°μ§λ€.
μμ μ±κΈν€ ν¨ν΄μ λ¨μ
1. ꡬνμ΄ λ²κ±°λ‘λ€.
λ€λ₯Έ κ°μ²΄λ€κ³Ό λ¬λ¦¬ μ±κΈν€ ν¨ν΄ μ€κ³λ₯Ό μν΄μλ§ static μμ μ΄κΈ°ν, private κΈ°λ³Έμμ±μ, μΈμ€ν΄μ€ νλ λ©μλ λ± μ¬λ¬ λΆνμ΄ νμνλ€.
2. κ°μ²΄μ§ν₯μ μΈ μ€κ³κ° μ΄λ ΅λ€.
μ λ² νΈμ λ΄μ©μμ μ΄ν΄λ³΄μλ―, κ°μ²΄μ§ν₯μ μΈ μ€κ³λ₯Ό μν΄μλ ꡬν체 ν΄λμ€μ μμ‘΄νλ©΄ μλλ€. νμ§λ§ μμ μ°λ¦¬κ° μμ±ν μ±κΈν€ ν¨ν΄μ, μΈμ€ν΄μ€λ₯Ό μ»λ κ³Όμ μμ ꡬν체 ν΄λμ€μ μμ‘΄νκ³ μλ€.
3. μ μ°μ±μ΄ λ¨μ΄μ§λ€.
μ±κΈν€ ν¨ν΄μ private μ κ·Όμ νμλ₯Ό μ΄μ©νμ¬ κΈ°λ³Έ μμ±μλ₯Ό μ ννλ€. μ΄λ μμμ λΆκ°λ₯ νκ² νλ©°, static μμμ μ΄κΈ°νκ° μ΄λ£¨μ΄μ§κΈ° λλ¬Έμ, νλμ κ°μ μ΄κΈ°ν νκ±°λ μμ νλ κ²μ΄ μ΄λ €μ΄ ꡬ쑰μ΄λ€.
4. ν μ€νΈκ° μ΄λ ΅λ€.
3λ²κ³Ό μ°κ³λλ λ΄μ©μ΄μ§λ§, λμ μΌλ‘ κ°μ²΄λ₯Ό μ£Όμ ν μ μκ³ λ¬΄μ‘°κ±΄ μ ν΄μ§ λ°©μλλ‘ μΈμ€ν΄μ€λ₯Ό νλν΄μΌ νκΈ° λλ¬Έμ λ€μν ν μ€νΈμ μ΄λ €μμ΄ μλ€.
λ€μκ³Ό κ°μ λ¨μ λ€λ‘ μΈν΄ μ±κΈν€μ μν°ν¨ν΄μ΄ λ μλ μλ€.
μν°ν¨ν΄ : μ΅κ΄μ μΌλ‘ λ§μ΄ μ¬μ©λμ§λ§ μμ°μ±μ΄ λ¨μ΄μ§λ ν¨ν΄μ μλ―Ένλ€.
νμ§λ§ μ€νλ§μ μ€νλ§ μ»¨ν μ΄λλ₯Ό ν΅ν΄ μ±κΈν€ ν¨ν΄μ μ μ§νλ©΄μλ μμ κΈ°μ ν λ¨μ μ 극볡ν μ μκ² ν΄μ€λ€.
3. μ€νλ§ μ±κΈν€
μΉ μ ν리μΌμ΄μ μμλ λμμ λ§μ νΈλν½μ΄ λ°μν μ μκ³ , λ§μ κ°μ²΄κ° μ¬μ¬μ© λ κ°λ₯μ±μ΄ λλ€. μλ₯Ό λ€μ΄, νΉμ μΉ νμ΄μ§λ₯Ό νΈμΆνκΈ° μν΄ μ¬μ©νλ 컨νΈλ‘€λ¬ κ°μ²΄λ ν΄λΉ νμ΄μ§μ λν λ°©λ¬Έμ΄ λ°μν λ λ§λ€ μμ±νκ³ μ κ±°νκΈ°μλ λ무 μμκ° μ¬ν μ±κ²©μ κ°μ²΄μ΄λ€.
νμ§λ§ μ€νλ§ μ»¨ν μ΄λμ κ°μ²΄λ₯Ό λ±λ‘ν κ²½μ° μ€νλ§ μ»¨ν μ΄λλ ν΄λΉ κ°μ²΄λ₯Ό νκ°λ§ 보μ νμ¬ μμ²μ΄ μμλλ§λ€ μ¬μ©μμκ² λ°νν΄μ€λ€.
μμ μμ±ν Config κ°μ²΄λ₯Ό νμ©νμ¬ μ λ§ μ»΄ν¬λνΈ μ€μΊμ ν΅ν΄ μ€νλ§ μ»¨ν μ΄ν°μ λ±λ‘ λ κ°μ²΄λ€μ΄ λμΌν μ°Έμ‘°κ°μ κ°μ§κ³ μλμ§ ν μ€νΈλ₯Ό ν΄λ³΄μ.
public class ConfigurationSingletonTest {
@Test
@DisplayName("[ν
μ€νΈ] μ€νλ§ μ»¨ν
μ΄λ μ±κΈν€ ν
μ€νΈ")
void autoConfigurationSingletonTest() {
// given
AnnotationConfigApplicationContext ac
= new AnnotationConfigApplicationContext(MemberAutoConfig.class);
MemberService memberServiceA = ac.getBean(MemberService.class);
MemberService memberServiceB = ac.getBean(MemberService.class);
// then
System.out.println("memberServiceA = " + memberServiceA);
System.out.println("memberServiceB = " + memberServiceB);
assertThat(memberServiceA).isSameAs(memberServiceB);
}
}
μ»΄ν¬λνΈ μ€μΊμ ν΅ν΄ μ€νλ§ μ»¨ν μ΄λμ λ±λ‘ λ κ°μ²΄λ€μ λͺ¨λ κ°μ μ°Έμ‘°κ°μ κ°μ§κ³ μμμ νμΈνμλ€.
μ€νλ§ μ»¨ν μ΄λλ μ΄μ²λΌ κ°μ²΄λ€μ μ±κΈν€μΌλ‘ μ μ§νλλ‘ λμμ£ΌκΈ° λλ¬Έμ μ±κΈν€ 컨ν μ΄λλΌκ³ λ νλ©°, μ±κΈν€μΌλ‘ κ°μ²΄λ₯Ό μμ±νκ³ κ΄λ¦¬νλ κΈ°λ₯μ μ±κΈν€ λ μ§μ€νΈλ¦¬λΌκ³ νλ€.
λΆλ‘: Configuration κ°μ²΄μ CGLIB Proxy
1νΈμμ μ€νλ§ μ»¨ν μ΄λμ κ°μ²΄λ₯Ό λ±λ‘νλ λ°©λ²μΌλ‘ μ»΄ν¬λνΈ μ€μΊμ ν΅ν λ°©λ² μΈμλ μλ μ€μ μ ν΅ν λ±λ‘μ΄ μμμ νμ΅νμλ€. κ·Έλ μμ±ν μλ λ±λ‘ μ€μ Config νμΌμ μμ€λ λ€μκ³Ό κ°λ€.
@Configuration
public class MemberConfig {
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
}
ν΄λΉ μμ€λ₯Ό μ μ΄ν΄λ³΄λ©΄ μ΄μν μ μ΄ νλ μλ€. λ°λ‘ memberRepository κ°μ²΄λ₯Ό λλ² νΈμΆνμ¬ λ°ννλ κ² μ²λΌ 보μΈλ€. MemberService κ°μ²΄ λ΄μ μλ MemberRepository μ λν μμ‘΄μ±μ μ£Όμ νκΈ° μν΄ λ€μκ³Ό κ°μ μν©μ΄ λλλ°, μ€νλ§μμ μ€μ λ‘ MemberRepository κ°μ²΄λ₯Ό λλ² μμ±νλμ§ μ§μ νμΈν΄λ³΄μ.
μ°μ Config κ°μ²΄λ₯Ό λ€μκ³Ό κ°μ΄ μμ νλ€.
@Configuration
public class MemberConfig {
@Bean
public MemberRepository memberRepository() {
System.out.println("MemberConfig.memberRepository Generated");
return new MemoryMemberRepository();
}
@Bean
public MemberService memberService() {
System.out.println("MemberConfig.memberService Generated");
return new MemberServiceImpl(memberRepository());
}
}
μ§μν κ²μ΄ λ§λ€λ©΄ MemberConfig κ°μ²΄ μμ±μμ μΆλ ₯λλ λ¬Έμμ΄μ΄ MemberRepositoryμ μμ‘΄μ±μ μ£Όμ νλ©΄μ νλ², κ·Έλ¦¬κ³ MemberServiceμ μμ‘΄μ±μ μ£Όμ νλ©΄μ λ€μ νλ² μ΄ λ λ² μΆλ ₯λμ΄μΌ ν κ²μ΄λ€.
λ€μκ³Ό κ°μ΄ κ°λ¨ν μ€νλ§ μ»¨ν μ΄λ νΈμΆ ν μ€νΈλ₯Ό μμ±νκ³ μ€νν΄λ³΄μ.
@Test
void configurationTest() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MemberConfig.class);
ac.close();
}
ν μ€νΈλ₯Ό μ€νν΄λ³΄λ©΄ μμκ³Όλ λ€λ₯΄κ² memberRepository μμ±μ νλ²λ§ νΈμΆλκ³ μ±κΈν€μ΄ μ μ§λκ³ μμμ μ μ μλ€. κ·Έλ λ€λ©΄ μ΄λ»κ² μ€νλ§ μ»¨ν μ΄λλ λλ² νΈμΆμ μλνλ μ½λμμ νλ²λ§ κ°μ²΄ μμ±μ νμ¬μ μ±κΈν€μ μ μ§ν μ μμμκΉ?
μμ ν μ€νΈ μ½λλ₯Ό λ€μκ³Ό κ°μ΄ μ‘°κΈ μμ νμ¬ Config κ°μ²΄μ ν΄λμ€λ₯Ό νλ² νμΈν΄λ³΄μ.
@Test
void configurationTest() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MemberConfig.class);
MemberConfig memberConfig = ac.getBean(MemberConfig.class);
//MemberConfig κ°μ²΄λ₯Ό νμΈν΄λ³΄μ.
System.out.println("memberConfig = " + memberConfig);
ac.close();
}
λΆλͺ κΈ°λν κ°μ²΄μ ν΄λμ€λ MemberConfig μΈλ° EnHancerBySpringCGLIB κ° λΆμ΄μλ νΉμ΄ν ννμ κ°μ²΄κ° μΆλ ₯λκ³ μλ€.
μ΄λ λ°μ΄νΈμ½λλ₯Ό μ‘°μνλ μ€νλ§ λΌμ΄λΈλ¬λ¦¬μ νλλ‘μ¨ μΌμ’ μ νλ‘μ κ°μ²΄μ΄λ€.
νλ‘μ κ°μ²΄λ ν΄λΉ κ°μ²΄λ₯Ό μ§μ μ μΌλ‘ μ¬μ©νμ§ μκ³ μμμ ν΅ν λ체 κ°μ²΄μ΄λ€. μ΄λ₯Ό μ΄μ©νλ λμμΈ ν¨ν΄μ νλ‘μ ν¨ν΄μ΄λΌ νλ©°, νμ΄λ²λ€μ΄νΈ, Mockito, μ€νλ§ λ± μ¬λ¬ νλ μμν¬μμλ μ¬μ©λκ³ μλ κ²½μ°λ₯Ό μ½κ² μ°Ύμλ³Ό μ μλ€.
νλ‘μ ν¨ν΄ λ° CGLIBμ κ΄ν΄μλ μΆν μμΈνκ² νμ΅νμ¬ μ 리ν μμ μ΄λ€.
νκ°μ§ μ¬λ―Έμλ μ μ, MemberConfig κ°μ²΄μ private μμ±μλ₯Ό μ μνλ©΄ νλ‘μ κ°μ²΄λ₯Ό μμ±νμ§ λͺ»νμ¬ μμΈκ° λ°μνλ€.
λ³Έλ‘ μΌλ‘ λμμ€μλ©΄ MemberConfig λ₯Ό μμλ°μ νλ‘μκ°μ²΄κ° κ°μ κ°μ²΄κ° λλ² μμ±λλμ§λ₯Ό νμΈνμ¬ μ€νλ§ μ»¨ν μ΄λμ λ±λ‘ν΄μ£Όλ μν μ νλ κ²μ΄λ€. μ΄λ₯Ό ν΅ν΄ μλ λ±λ‘ λ°©μ λν μ±κΈν€μ μ μ§ν μ μλ€.
4. μ±κΈν€ μ£Όμμ¬ν
μ±κΈν€ ν¨ν΄(νΉμ μ±κΈν€ 컨ν μ΄λ)μ νλμ κ°μ²΄λ₯Ό μ¬λ¬ ν΄λΌμ΄μΈνΈκ° 곡μ νκΈ° λλ¬Έμ νμ λμμ± λ¬Έμ λ₯Ό μ£Όμν΄μΌ νλ€. μ΄λ₯Ό ν΅ν΄ μ£Όμν΄μΌ ν μ¬νμ λͺκ°μ§ μ 리ν΄λ³΄λ©΄
1. νΉμ ν΄λΌμ΄μΈνΈμ μμ‘΄μ μ΄λ©΄ μλλ€.
2. νΉμ ν΄λΌμ΄μΈνΈκ° νλμ κ°μ λ³κ²½ν μ μμΌλ©΄ μλλ€.
3. νλ λμ μ§μλ³μ, νλΌλ―Έν°, ThreadLocal λ±μ μ¬μ©νλ€.
μ±κΈν€ κ°μ²΄μ νλμμ κ°μ μ§μ λ€λ£¨μ΄ μκΈΈμ μλ λ¬Έμ λ₯Ό μ΄ν΄λ³΄κΈ° μν΄ κ°λ¨ν μμλ₯Ό μμ±ν΄λ³΄μ.
μ΄λ©μΌμ 보λ΄λ μλΉμ€ λ‘μ§μμ, ν΄λΌμ΄μΈνΈκ° μμ²νλ μ΄λ©μΌ μ£Όμλ₯Ό 곡μ νλμμ μ²λ¦¬νκ³ μλ€κ³ κ°μ νλ€.
public class EmailSenderService {
// Stateful Field
private String userEmail;
public void send(String userEmail) {
System.out.println("[EMAIL SEND LOGIC] " + userEmail);
// Stateful Logic
this.userEmail = userEmail;
}
public String getResult() {
return "[EMAIL SEND LOG] " + this.userEmail;
}
}
ν΄λΉ λ©μΌ λ°μ‘ μλΉμ€μ μ΄λ ν μμΌλ‘ λ¬Έμ κ° λ°μνλμ§ ν μ€νΈ νλ μ½λλ μμ±ν΄λ³΄μ.
class EmailSenderServiceTest {
@Test
void statefulTest() {
AnnotationConfigApplicationContext ac
= new AnnotationConfigApplicationContext(EmailSenderConfig.class);
// λλͺ
μ ν΄λΌμ΄μΈνΈκ° κ°μ νΈμΆνλ μν©μ΄λΌκ³ κ°μ νλ€
EmailSenderService serviceA = ac.getBean(EmailSenderService.class);
EmailSenderService serviceB = ac.getBean(EmailSenderService.class);
String emailA = "addrA@email.com";
String emailB = "addrB@email.co.kr";
// μλ‘ λ€λ₯Έ μ΄λ©μΌμ λμμΌλ‘ λ‘μ§μ μννλ€.
serviceA.send(emailA);
serviceB.send(emailB);
String resultA = serviceA.getResult();
String resultB = serviceB.getResult();
System.out.println("resultA = " + resultA);
assertThat(resultA).isNotEqualTo(emailA);
ac.close();
}
//μ€νλ§ μ»¨ν
μ΄λλ₯Ό μν Config κ°μ²΄
static class EmailSenderConfig {
@Bean
public EmailSenderService emailSenderService() {
return new EmailSenderService();
}
}
}
μ΄λ©μΌμ λ°μ‘ ν κ²°κ³ΌA κ° μμ μ κ²°κ³Όκ°μ΄ μλ λ€λ₯Έ ν΄λΌμ΄μΈνΈμ κ²°κ³Όλ₯Ό μΆλ ₯νκ³ μλ κ²μ νμΈν μ μλ€.
μν μ μ§λ‘ μΈν λ¬Έμ λ μ€μ μμ© μ½λμμλ μΆμ λ μ΄λ €μΈ λΏλλ¬ μ΄μμ λ§λν μμ€μ κ°μ Έμ¬ μ μμΌλ―λ‘ μ λλ‘ Stateful ν μ€κ³λ μ§μν΄μΌ νλ€.
5. λ§μΉλ©°
μ€νλ§ μ»¨ν μ΄λκ° μ±κΈν€ λ°©μμΌλ‘ μλνκ³ μλ€λ μ¬μ€μ μ½κ² μ ν μ μμ§λ§ μ μ±κΈν€ λ°©μμ μ¬μ©ν΄μΌ νλμ§, κ·Έλ¦¬κ³ μ±κΈν€ λ°©μμ μ£Όμμ μλ μ΄λ ν κ²λ€μ΄ μλμ§λ₯Ό νμΈν΄λ³΄μλ€. 무μνλ‘ μ€κ³νλ κ²μ λΉλ¨ λμμ± μ΄μλ‘ μΈν μ₯μ λ₯Ό λ°©μ§νλλ°μλ λμμ΄ λμ§λ§, μνμ νμ₯μλ λ§μ λμμ μ€λ€. λ°λΌμ κ°λ°μ μκ°μ νΈμλ₯Ό μν΄ μν μ μ§λ₯Ό λ¨λ°νλ μ½λ μμ±μ μμΌλ‘λ μ΅λν μμ€νλλ‘ νμ.
ν΄λΉ μ리μ¦λ μΈνλ°μ κΉμν κ°μ¬λ [λ§ν¬]'μ€νλ§ ν΅μ¬μ리 - κΈ°λ³ΈνΈ' κ°μλ₯Ό λ°νμΌλ‘
λ΄μ©κ³Ό μμ€λ₯Ό μ¬κ΅¬μ±νμ¬ μμ±νμμ΅λλ€.
'λ ΈνΈ > Spring' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
TransactionRequiredException ν΄κ²° - μ΄κΈ°ν μμ μ Transaction νλ (0) | 2024.01.29 |
---|---|
μ€νλ§ κΈ°λ³Έ λ³΅μ΅ 3 - λΉ μ€μ½νμ μλͺ μ£ΌκΈ° (0) | 2024.01.29 |
μ€νλ§ μ»¨ν μ΄λλ₯Ό νμ©ν μ λ΅ν¨ν΄ (0) | 2024.01.23 |
μ€νλ§ κΈ°λ³Έ λ³΅μ΅ 1 - DIP, OCP μμΉκ³Ό μ€νλ§ μ»¨ν μ΄λ (0) | 2022.04.28 |