๐ก์ด ํฌ์คํ ์ Spring Bean์ ๋ฌด์ํ(stateless)์ ๋ํด ์ ๋ฆฌํ ํฌ์คํ ์ ๋๋ค.
Spring Bean stateless์ ๋ํด ํ์ตํ GitHub Repository์ ๋๋ค.
GitHub - kiekk/test-spring-bean-stateless
Contribute to kiekk/test-spring-bean-stateless development by creating an account on GitHub.
github.com
Spring Bean์ ๊ดํด ์ฐพ์๋ณด๋ฉด ๋ฌด์ํ(stateless)๋ก ์ค๊ณํด์ผ ํ๋ค๊ณ ํฉ๋๋ค. ๋ฌด์ํ๋ ๋ง ๊ทธ๋๋ก ์ํ๊ฐ ์๋ ๊ฒ์ ๋ปํ๋ฉฐ Bean์ด ํน์ ์ํ๋ฅผ ๊ฐ์ง๊ณ ์์ง ์๋ ๊ฒฝ์ฐ๋ฅผ ๋งํฉ๋๋ค.
ํ์ง๋ง ์ด ๋ถ๋ถ์ ์กฐ๊ธ ๋ ์ค๋ช ํด ๋ณด๋ฉด Bean์ด๋ผ์ ๋ฌด์ํ(statelss)์ฌ์ผ ํ๋ค๋ ๊ฒ์ ์๋๊ณ , Spring์์ Bean์ ๊ธฐ๋ณธ์ ์ผ๋ก singleton ์ค์ฝํ๋ก ์์ฑ๋๊ธฐ ๋๋ฌธ์ IoC ์ปจํ ์ด๋์ ๋จ ํ ๊ฐ์ ๊ฐ์ฒด๋ง ์กด์ฌํ๊ฒ ๋ฉ๋๋ค.
๊ทธ๋์ Spring์์ Bean์ด singleton์ด๊ธฐ ๋๋ฌธ์ ๋ฌด์ํ์ฌ์ผ ํ๋ค๊ณ ํ๋ ๊ฒ์ด๋ฉฐ, ์ ๋ฆฌํด ๋ณด๋ฉด singleton ํจํด์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์๋ ํด๋น ํด๋์ค๋ฅผ ํญ์ ๋ฌด์ํ(statelss)๋ก ์ค๊ณํด์ผ ํ๋ค๋ ๊ฒ์ ๋๋ค.
singleton
singleton ํจํด์ ๊ฐ์ฒด์ ์ธ์คํด์ค๋ฅผ ํ๋๋ง ์์ฑํ๊ณ ์ด๋ฅผ ๊ณต์ ํ์ฌ ์ฌ์ฉํฉ๋๋ค. Spring์ ๊ฒฝ์ฐ IoC ์ปจํ ์ด๋์ ์ํด Bean๋ค์ด ๊ด๋ฆฌ๋ฉ๋๋ค.
์ฌ์ฉ์๋ค์ด Bean์ ์ฌ์ฉํ๋ ค๊ณ ์์ฒญํ ๊ฒฝ์ฐ IoC ์ปจํ ์ด๋ ์๋ Bean์ ์ฌ์ฉ์๋ค์ด ์๋ก ๊ณต์ ํ์ฌ ์ฌ์ฉํ๋๋ฐ, ์ด ๋ถ๋ถ์์ Bean์ ํน์ ๊ฐ์ ์ ์ฅํ ์ ์๋ค๊ณ ๊ฐ์ ํ๋ค๋ฉด, ์ฌ์ฉ์๊ฐ Bean์ ์ฌ์ฉํ ๋๋ง๋ค ๊ฐ์ด ๋ฐ๋๊ฒ ๋ ๊ฒ์ ๋๋ค.
๋ฐ๋ก ์ด ๋ถ๋ถ์ '๋์์ฑ' ์ด์๋ผ๊ณ ํ๋๋ฐ, ๊ณต์ ๋ ์์์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์ ๋ฐ๋์ ๋ค๋ฐ๋ผ ์ค๋ ๋ฌธ์ ์ ๋๋ค.
// StatefulService Bean ๋ฑ๋ก ์ Singleton scope ๋ก ๋ฑ๋กํ๊ธฐ ๋๋ฌธ์
// UserA, UserB๊ฐ ๊ฐ๊ฐ StatefulService ์ ์ ๊ทผํ ๋๋ง๋ค ๊ฐ์ด ๋ณ๊ฒฝ๋์ด
// ๋์์ฑ ์ด์๊ฐ ๋ฐ์
@Test
void statefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(StatefulService.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
//ThreadA: A์ฌ์ฉ์ 10000์ ์ฃผ๋ฌธ
statefulService1.order("userA", 10000);
//ThreadB: B์ฌ์ฉ์ 20000์ ์ฃผ๋ฌธ
statefulService2.order("userB", 20000);
//ThreadA: ์ฌ์ฉ์A ์ฃผ๋ฌธ ๊ธ์ก ์กฐํ
int priceOfUserA = statefulService1.getPrice();
System.out.println("priceOfUserA = " + priceOfUserA);
//ThreadA: ์ฌ์ฉ์B ์ฃผ๋ฌธ ๊ธ์ก ์กฐํ
int priceOfUserB = statefulService1.getPrice();
System.out.println("priceOfUserB = " + priceOfUserB);
// UserA์ statefulService1, UserB์ statefulService2 ๋น๊ต
System.out.println("statefulService1 : " + statefulService1);
System.out.println("statefulService2 : " + statefulService2);
System.out.println("statefulService1 == statefulService2 : " + (statefulService1 == statefulService2));
Assertions.assertThat(statefulService1.getPrice()).isEqualTo(10000); // ์๋ฌ๋ฐ์
}
static class StatefulService {
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;
}
}
์ํ๋ฅผ ๊ฐ์ง๊ณ ์๋ StatefulService ๊ฐ์ฒด๋ฅผ ์์ฑํ์ต๋๋ค. ๋ด๋ถ์ price๋ผ๋ ํ๋๋ฅผ ํตํด ์ํ๋ฅผ ๊ฐ์ง๊ฒ ๋์๋๋ฐ, statefulServiceSingleton() ๋ฉ์๋์์ ํ์ธํ ์ ์๋ฏ์ด ์ฌ์ฉ์ A์ ์ฌ์ฉ์ B๊ฐ statefulService๋ฅผ ์ฌ์ฉํ์ฌ ์ํ(price)๋ฅผ ์ ์ฅํฉ๋๋ค.
์์ ํ ์คํธ๋ก ๋ณด๋ฉด ์ฌ์ฉ์B๊ฐ ๋ง์ง๋ง์ statefulService๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์ต์ข ์ ์ผ๋ก๋ ์ฌ์ฉ์ B์ ์ํ(price)๊ฐ ์ ์ฅ๋์ด ์์ ๊ฒ์ ๋๋ค.
๋ฌธ์ ๋ ์ฌ์ฉ์A๊ฐ ๋ค์ ์ํ(price)๋ฅผ ์กฐํํ์ ๋ ์์ ์ด ์ ์ฅํ๋ ์ํ๊ฐ ์๋ ์ฌ์ฉ์ B์ ์ํ๊ฐ ๋ฐํ๋๋ค๋ ๊ฒ์ ๋๋ค.
์ด์ฒ๋ผ ์ํ๊ฐ ์๋ ๊ฐ์ฒด๋ฅผ ๊ณต์ ํ์ฌ ์ฌ์ฉํ๊ฒ ๋ ๊ฒฝ์ฐ ํน์ ์ฌ์ฉ์์ ์ํ๋ฅผ ๋ณด์ฅํ ์ ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ง์ฝ ์ด ์ํ๊ฐ ๋ฏผ๊ฐํ ๊ฐ์ธ ์ ๋ณด๋ผ๋ฉด ์ด๋จ๊น์? ๋ณด์์๋ ๊ต์ฅํ ์ทจ์ฝํ๋ค๊ณ ํ ์ ์์ต๋๋ค.
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ ๋ฐฉ๋ฒ์ผ๋ก๋ ๋จผ์ scope๋ฅผ ๋ณ๊ฒฝํ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
Scope ๋ณ๊ฒฝ - prototype
singleton์ ๊ฒฝ์ฐ IoC ์ปจํ ์ด๋๊ฐ ์ธ์คํด์ค๋ฅผ ๊ด๋ฆฌํ๋ค๊ณ ํ๋๋ฐ, prototype์ ๊ฒฝ์ฐ ์ฌ์ฉ์๊ฐ Bean์ ์์ฒญํ ๋๋ง๋ค ์๋ก์ด ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ์์ฑํ์ฌ ๋ฐํํ๊ฒ ๋๋ฉฐ, ๋ฐํํ ํ์๋ IoC ์ปจํ ์ด๋๊ฐ ๋ ์ด์ ๊ด๋ฆฌํ์ง ์์ต๋๋ค.
๋ฐ๋ผ์ ์ฌ์ฉ์๋ง๋ค ์๋ก ๋ค๋ฅธ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๊ฒ ๋จ์ผ๋ก์จ ์ํ๋ฅผ ๊ฐ์ง๋๋ผ๋ ์์ ๊ฐ์ด '๋์์ฑ' ์ด์๋ ๋ฐ์ํ์ง ์์ต๋๋ค.
// scope๋ฅผ prototype์ผ๋ก ์ค์ ํ๋ฉด
// UserA์ statefulService, UserB์ statefulService๊ฐ ์๋ก ๋ค๋ฅธ ๊ฐ์ฒด๊ฐ ํ ๋น์ด ๋์ด
// singleton์์์ ๋์์ฑ ์ด์๋ ๋ฐ์ํ์ง ์๋๋ค.
// ํ์ง๋ง ์ด๋ฐ ๋ฐฉ์์ Spring์ด ์งํฅํ๋ ๊ฒ๊ณผ ์ ๋ฐ๋๋ก ๊ฐ๋ ๊ฑฐ๋ผ๊ณ ์๊ฐํด์ ์ณ์ง ์์ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ์๊ฐํ๋ค.
@Test
void statefulServicePrototype() {
ApplicationContext ac = new AnnotationConfigApplicationContext(StatefulServiceV2.class);
StatefulServiceV2 statefulService1 = ac.getBean(StatefulServiceV2.class);
StatefulServiceV2 statefulService2 = ac.getBean(StatefulServiceV2.class);
//ThreadA: A์ฌ์ฉ์ 10000์ ์ฃผ๋ฌธ
statefulService1.order("userA", 10000);
//ThreadB: B์ฌ์ฉ์ 20000์ ์ฃผ๋ฌธ
statefulService2.order("userB", 20000);
//ThreadA: ์ฌ์ฉ์A ์ฃผ๋ฌธ ๊ธ์ก ์กฐํ
int priceOfUserA = statefulService1.getPrice();
System.out.println("priceOfUserA = " + priceOfUserA);
//ThreadA: ์ฌ์ฉ์A ์ฃผ๋ฌธ ๊ธ์ก ์กฐํ
int priceOfUserB = statefulService2.getPrice();
System.out.println("priceOfUserB = " + priceOfUserB);
// UserA์ statefulService1, UserB์ statefulService2 ๋น๊ต
System.out.println("statefulService1 : " + statefulService1);
System.out.println("statefulService2 : " + statefulService2);
System.out.println("statefulService1 == statefulService2 : " + (statefulService1 == statefulService2));
Assertions.assertThat(statefulService1.getPrice()).isEqualTo(10000);
Assertions.assertThat(statefulService2.getPrice()).isEqualTo(20000);
Assertions.assertThat(statefulService1.getPrice()).isNotEqualTo(statefulService2.getPrice());
}
@Scope("prototype")
static class StatefulServiceV2 {
private int price; // ์ํ๋ฅผ ๊ฐ์ง๊ณ ์๋ ํ๋
public void order(String name, int price) {
System.out.println("name = " + name + " price = " + price);
this.price = price;
// ์ด์ Bean ์ ์์ฒญํ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ธฐ ๋๋ฌธ์
// ๋ ์ด์ ๋์์ฑ ์ด์๋ ๋ฐ์ํ์ง ์๋๋ค.
// ํ์ง๋ง ์ฌ์ฉ์๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑํด์ฃผ๊ธฐ ๋๋ฌธ์ ๋ถํ์ํ ๊ฐ์ฒด๋ค์ด ๋๋ฌด ๋ง์ด ์์ฑ๋๋ค.
}
public int getPrice() {
return price;
}
}
ํ์ง๋ง ์ด ๋ฐฉ๋ฒ์ด best๋ผ๊ณ ํ ์๋ ์์ ๊ฒ์ ๋๋ค. ์ด์ ๋ ์ฌ์ฉ์์ Bean ์์ฒญ ๋ง๋ค ์๋ก์ด ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ค๋ ์ ์ธ๋ฐ, ์ฌ์ฉ์์ ์๊ฐ 100๋ง ๋ช , 1000๋ง ๋ช ์ด๋ผ๊ณ ๊ทน๋จ์ ์ผ๋ก ๊ฐ์ ์ ํด๋ณด๋ฉด ๊ทธ๋งํผ์ Bean์ด ์์ฑ๋๊ธฐ ๋๋ฌธ์ ๋ถํ์ํ ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋๋ฌด ๋ง์์ ธ ์ฑ๋ฅ์ด ์ ํ๋๊ฑฐ๋ ์ฌํ ๋๋ ์๋ฒ๊ฐ ๋ค์ด์ด ๋ ์๋ ์์ต๋๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ด ๋ฐฉ๋ฒ์ ์ด๋ ๊ฒ๋ ํด๊ฒฐํ ์๊ฐ ์๊ตฌ๋. ์ ๋๋ก๋ง ์๊ณ ๋์ด๊ฐ์๋ฉด ๋ ๊ฒ ๊ฐ์ต๋๋ค.
๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ Spring์ ๊ถ์ฅ ๋ฐฉ๋ฒ์ด์ ์ด ํฌ์คํ ์ ์ ๋ชฉ์ธ Bean์ ๋ฌด์ํ(stateless)๋ก ์ค๊ณํ๋ ๊ฒ์ ๋๋ค.
๋ฌด์ํ๋ก ์ค๊ณํ ๊ฒฝ์ฐ ์ํ๋ฅผ ๊ฐ์ง๋ price๋ฅผ ์ ๊ฑฐํ๊ฒ ๋๋ ๊ฒ์ ๋๋ค.
์ํ๋ฅผ ์ ์ฅํ๋ ๊ฒ์ด ์๋ ์ํ๋ฅผ ๋ฐํํจ์ผ๋ก์จ Bean์ ์ฌ์ฉํ๋ ์ฌ์ฉ์ ์ธก์์ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ฒ ํ๋ ๊ฒ์ ๋๋ค.
// ์ ๋ฌธ์ ๋ฅผ ๋ชจ๋ ์ถฉ์กฑํ๋ ค๋ฉด Bean ์ด ๋ ์ด์ ์ํ๋ฅผ ๊ฐ์ง์ง ์๋๋ก ํ๋ฉด ๋๋ค.
// ์ํ๋ฅผ ๊ฐ์ง์ง ์๋๋ก ํ์ฌ Spring ์ด ์งํฅํ๋ singleton ๋ฐฉ์๋ ํด๊ฒฐํ๊ณ ๋์์ฑ ์ด์๋ ํด๊ฒฐํ ์ ์๋ค.
@Test
void statelessService() {
ApplicationContext ac = new AnnotationConfigApplicationContext(StatelessService.class);
StatelessService statelessService1 = ac.getBean(StatelessService.class);
StatelessService statelessService2 = ac.getBean(StatelessService.class);
//ThreadA: A์ฌ์ฉ์ 10000์ ์ฃผ๋ฌธ
int priceOfUserA = statelessService1.order("userA", 10000);
//ThreadB: B์ฌ์ฉ์ 20000์ ์ฃผ๋ฌธ
int priceOfUserB = statelessService2.order("userB", 20000);
System.out.println("priceOfUserA = " + priceOfUserA);
System.out.println("priceOfUserB = " + priceOfUserB);
// UserA์ statelessService1, UserB์ statelessService2 ๋น๊ต
System.out.println("statelessService1 : " + statelessService1);
System.out.println("statelessService2 : " + statelessService2);
System.out.println("statelessService1 == statelessService2 : " + (statelessService1 == statelessService2));
Assertions.assertThat(priceOfUserA).isEqualTo(10000);
Assertions.assertThat(priceOfUserB).isEqualTo(20000);
}
static class StatelessService {
public int order(String name, int price) {
System.out.println("name = " + name + " price = " + price);
return price;
}
}
์ด ์ฝ๋์ ๋ค๋ฅธ ์ ์ StatelessService์์ price๊ฐ ์ฌ๋ผ์ก๋ค๋ ์ ์ ๋๋ค. ์ฆ, ๋ ์ด์ ์ด ๊ฐ์ฒด๋ ์ํ๋ฅผ ์ ์ฅํ๊ณ ์์ง ์์ต๋๋ค.
๋์ ์ ๋ฌ ๋ฐ์ price๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉ์์๊ฒ ๋ฐํํจ์ผ๋ก์จ ์ด์ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ์ฃผ์ฒด๊ฐ Bean์ด ์๋ Bean์ ์ฌ์ฉํ๋ ์ฌ์ฉ์ ์ชฝ์ผ๋ก ๋๊ฒ๋ ํ์ฌ ์์ 2๊ฐ์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ต๋๋ค.
์ด์ ๊ฐ์ ์ด์ ๋ก ํญ์ Bean์ ๋ฌด์ํ(stateless)๋ก ์ค๊ณํ๋ ๊ฒ์ด ์ข์ผ๋ฉฐ, Bean์ ์ฌ์ฉํ๋ ์ชฝ์์ ์ํ๋ฅผ ๊ด๋ฆฌํ ์ ์๋๋ก ์ค๊ณํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
'Spring > Spring Framework' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
@Controller์์๋ ResponseEntity๋ฅผ ์ฌ์ฉํ๋ฉด Rest๊ฐ ๋๋ค?? (0) | 2023.03.11 |
---|---|
window HTTPie ์ค์น (0) | 2023.01.31 |
๋๊ธ