2023. 1. 7. 01:53ใBackend/๐ฟ Spring
๋ชฉ์ฐจ
- ๋ฐฐ๊ฒฝ
- ๋ฌธ์
- API ๊ฐ์ ์๊ตฌ์ฌํญ
- ํ์ฌ API ์ฌํญ
- ๊ฐ์ ๋ API ์๊ตฌ์ฌํญ
- ์ฒซ ๋ฒ์งธ ์๋
- ๋ ๋ฒ์งธ ์๋
- ์ธ ๋ฒ์งธ ์๋
- Self-Invocation ํด๊ฒฐ ๋ฐฉ์ (Proxy ์์ฑ)
- 1) ApplicationContext DI
- 2) @Resource์ผ๋ก AutoWiring ๋ง๋ฌด๋ฆฌ
๋ฐฐ๊ฒฝ
- ํด๋ผ์ด์ธํธ์์ ํน์ ํค์๋๋ก ์กฐํํ๋ฉด, ํด๋น ํค์๋์ ํด๋นํ๋ ์ํ ๋ชฉ๋ก์ ๋ฐ์์จ๋ค.
- ์ผํ๋ชฐ API ์์ ์ํ์ ๋ณด๋ด์ฃผ๋ฉฐ, 12๊ฐ์ ์ํ์ ๋ํดํธ๋ก ๋ณด๋ด์ค๋ค.
- ์ผํ๋ชฐ API์์ ํด๋น ํค์๋์ ์ํ ์ฌ๊ณ ๊ฐ ๋ถ์กฑํ๋ฉด, 12๊ฐ ๋ฏธ๋ง์ผ๋ก ์๋ตํ ์ ์๋ค.
- ์ํ์ ๊ฐ์๊ฐ 9๊ฐ ๋ฏธ๋ง์ด๋ฉด, ์ฌ๊ณ ๋ณด์ถฉ notify ๋ฅผ ์ํด ์ฌ๋๋ด ๋ฉ์์ง๊ฐ ์ ์ก๋๋ค.
๋ฌธ์
์ํ ์ฌ๊ณ ๊ฐ ๋ถ์กฑํ ํค์๋๋ก api ์์ฒญ์ด ๊ณ์ ๋ค์ด์ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?
๋งค๋ฒ api ์์ฒญ๋ง๋ค ์ฌ๋๋ด์ด ์ ์ก๋๊ธฐ ๋๋ฌธ์ ๊ต์ฅํ ๊ท์ฐฎ์ ์ํฉ์ด ๋ฒ์ด์ง๋๋ค. ์๋์ฒ๋ผ์.
๋๋ฌธ์ ๋์ผํ ํค์๋๋ก ์์ฒญ์ด ๋ค์ด์ค๋๋ผ๋ ์ต์ด ํ ๋ฒ๋ง ์ฌ๋๋ด ๋ฉ์์ง๋ฅผ ์ ์กํ๊ณ , ๊ทธ ํ๋ถํฐ๋ ๋ฉ์์ง๊ฐ ์ ์ก๋์ง ์๋๋ก ์กฐ์น๊ฐ ํ์ํ์ต๋๋ค.
API ๊ฐ์ ์๊ตฌ์ฌํญ
- ํน์ ํค์๋์ ์ํ ๋ชฉ๋ก์ ๊ฐ์ ธ์จ๋ค.
- ์ํ ๋ชฉ๋ก์ ์์๋ ๋งค ์์ฒญ๋ง๋ค ๋๋ค์ผ๋ก ๋ฌ๋ผ์ง๋ค. (Shuffle ์ฒ๋ฆฌ)
- ์ํ ์ฌ๊ณ ๊ฐ 9๊ฐ ๋ฏธ๋ง์ผ๋ก ๋จ์ด์ง๋ฉด, ์ฌ๊ณ ๋ณด์ถฉ์ ์ํด ์ฌ๋๋ด ๋ฉ์์ง๋ฅผ ์ ์กํ๋ค.
- ์ฌ๋๋ด ๋ฉ์์ง๋ ๋์ผํ ํค์๋์ ๋ํด ์ต์ด 1ํ๋ง ์ ์ก๋์ผ ํ๋ค.
ํ์ฌ API ์ฌํญ
- ํน์ ํค์๋์ ์ํ ๋ชฉ๋ก์ ๊ฐ์ ธ์จ๋ค.
- ์ํ ๋ชฉ๋ก์ ์์๋ ๋งค ์์ฒญ๋ง๋ค ๋๋ค์ผ๋ก ๋ฌ๋ผ์ง๋ค. (Shuffle ์ฒ๋ฆฌ)
- ์ํ ์ฌ๊ณ ๊ฐ 9๊ฐ ๋ฏธ๋ง์ผ๋ก ๋จ์ด์ง๋ฉด, ์ฌ๊ณ ๋ณด์ถฉ์ ์ํด ์ฌ๋๋ด ๋ฉ์์ง๋ฅผ ์ ์กํ๋ค.
- ์ํ ์ฌ๊ณ ๊ฐ 9๊ฐ ๋ฏธ๋ง์ธ ํค์๋์ ๋งค ์์ฒญ๋ง๋ค ์ฌ๋๋ด ๋ฉ์์ง๊ฐ ์ ์ก๋๋ค. โฌ ๏ธ ๊ฐ์ ํ๊ณ ์ถ์ ์ฌํญ ๊ฐ์ ๋ API ์๊ตฌ์ฌํญ
- ์ฌ๋๋ด ๋ฉ์์ง๋, ๋์ผํ ํค์๋์ ๋ํด ์ต์ด 1ํ๋ง ์ ์ก๋ผ์ผ ํ๋ค.
์ฌ๋๋ด ๋ฉ์์ง๋ฅผ 1๋ฒ๋ง ์ ์กํ๊ธฐ ์ํด ์บ์๋ฅผ ์ ์ฉํ๊ธฐ๋ก ํ๊ณ , ๊ฒฝ๋ ์บ์์ธ Ehcache๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ํ์ต๋๋ค.
์ฒซ ์๋
์ํ์ฐฉ์ค ๊ณผ์ ์ ์ํด ์ฒ์ ์ง ์ฝ๋๋ถํฐ ๋ณด์ฌ๋๋ฆฌ๊ฒ ์ต๋๋ค.
("๋ญ์ผ, ํด๊ฒฐ์ฑ ์ด๋ ๋ณด์ฌ์ค์" ํ์๋ ๋ถ๋ค์ ์ธ๋ฒ์งธ ์๋ ๋ชฉ์ฐจ๋ก ๊ฐ์๋ฉด ๋ฉ๋๋ค ๋จ)
@Cacheable(cacheNames = "products", key = "#searchKeyword")
public List<ProductDto> getProductOfReview(String searchKeyword) {
List<ProductDto> result = getProduct(searchKeyword); // <- mall api ์์ ์ํ ์ ๋ณด ์กฐํ
if (result.size() < 9) {
sendSlackMessage(); // <- slackbot ์ ์ก ๋ฉ์๋
}
Collections.shuffle(result); // <- shuffle ์ฒ๋ฆฌ
return result;
}
// slackbot ์ ์ก ๋ฉ์๋
public void sendSlackMessage() {
try {
...
Slack.chatPostMessage(request);
...
} catch (SlackApiException | IOException exception) {
log.error("Slack Error" + exception.getMessage());
throw new BusinessException(SlackErrorCode.SLACK_ERROR);
}
}
์ํ์ ์กฐํํ๋ ๋ฉ์๋ ์์ฒด์ ์บ์๋ฅผ ๊ฑธ์์ต๋๋ค.
๋์ผํ searchKeyword์ ์บ์ ์ฒ๋ฆฌ๋ ์ ๋์ง๋ง 2๊ฐ์ง ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
1) ์ํ์ด shuffle ๋์ง ์์ -> ์ํ์ด ๋ฌด์์๋ก ๋จ์ด์ง์ง ์๊ณ , ๋์ผํ ์์๋ก ์กฐํ๋๋ค. (๊ธฐํ ์ ์ฑ ๊ณผ ๋ค๋ฆ)
2) ํ๋ฒ ์บ์ฑ๋๋ฉด, ์ํ์ด 9๊ฐ ๋ฏธ๋ง์ผ๋ก ๋จ์ด์ง๋๋ผ๋, ์ฌ๋ซ๋ด์ด ์ธ๋ฆฌ์ง ์๋๋ค. (์ฌ๊ณ ๊ฐ ๋ถ์กฑํด์ง๋๋ผ๋ ๋ ธํฐ ํ ์ ์์)
3) ์ฌ๋๋ด์ด ์ต์ด 1ํ์กฐ์ฐจ ์ ์ก๋์ง ์์ ์ ์๋ค.
์์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ ๋ฒ์งธ ์๋๋ก ๊ฐ๋๋ค.
๋๋ฒ์งธ ์๋
1. ์ฌ๋๋ด ์ ์ก ๋ฉ์๋ sendSlackMessage() ๋ง ์บ์์ฒ๋ฆฌ
- ์ฌ๋๋ด ์ ์ก ๋ฉ์๋๋ง ์บ์ฑ๋จ
- ์ต์ด ํ ๋ฒ๋ง ์ฌ๋๋ด์ด ๋์ํ๊ณ , ๊ทธ ์ดํ๋ก ๋ฉ์์ง๊ฐ ์ ์ก๋์ง ์๋๋ค.
์ด๋ ๊ฒ ๋๋ฉด ์ฒซ ๋ฒ์งธ ์๋์ ๋ฌธ์ ์ ์ ํด๊ฒฐํ ์ ์์ต๋๋ค.
1. ์ํ์ด ์ ์์ ์ผ๋ก shuffle ๋๋ค.
2. ์ํ์ด 9๊ฐ ๋ฏธ๋ง์ผ๋ก ๋จ์ด์ง๋ฉด, ์ฌ๋๋ด ์ ์ก ๋ฉ์๋๊ฐ ํธ์ถ๋๋ค.
public List<ProductDto> getProductOfReview(String searchKeyword) {
List<ProductDto> result = getProduct(searchKeyword);
if (result.size() < 9) {
log.info(">>> [Before] Caching method");
// slackbot ์ ์ก ๋ฉ์๋
sendSlackMessage(searchKeyword);
}
Collections.shuffle(result);
return result;
}
// slackbot ์ ์ก ๋ฉ์๋ ์บ์ ์ ์ฉ
@Cacheable(cacheNames = "products", key = "#searchKeyword")
public void sendSlackMessage(String searchKeyword) {
log.info(">>> [After] Slackbot message caching");
try {
...
Slack.chatPostMessage(request);
...
} catch (SlackApiException | IOException exception) {
log.error("Slack Error : {}", exception.getMessage());
throw new BusinessException(SlackErrorCode.SLACK_ERROR);
}
}
์ด์ ๋ฌธ์ ์ ์ ๋ชจ๋ ํด๊ฒฐํ์ผ๋ ์ฝ๋๋ฅผ ์คํํ์ฌ, ์บ์ฑ์ด ์ ๋๋์ง ํ์ธํด ๋ณด๊ฒ ์ต๋๋ค.
๋์ผํ ํค์๋๋ก API ์์ฒญ์ 2๋ฒ ๋ณด๋์ ๋ ๋ก๊ทธ ๊ฒฐ๊ณผ์ ๋๋ค.
์บ์๊ฐ ์ ๋๋ก ๋์ํ๋ค๋ฉด ์ต์ด ํ ๋ฒ๋ง >>> [After] Slackbot message caching ๋ก๊ทธ๊ฐ ์ถ๋ ฅ ๋ผ์ผ ํ์ง๋ง, ๋ ๋ฒ์งธ ํธ์ถ์๋ ๋ก๊ทธ๊ฐ ์ถ๋ ฅ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค. ์ ์บ์๊ฐ ๋์ํ์ง ์์๊น์?
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ Spring AOP์ self invocation ์ ๋ํด ์์์ผ ํฉ๋๋ค.
์ธ ๋ฒ์งธ ์๋
์ธ ๋ฒ์งธ ์๋ ์ ์ self invocation ์ด ๋ฌด์์ธ์ง ๊ฐ๋จํ ์์๋ณด๊ฒ ์ต๋๋ค.
Self invocation ์ด๋?
์ปจํธ๋กค๋ฌ ๋จ์์ proxyCall() ์ด๋ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค๊ณ ๊ฐ์ ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ ๋ด๋ถ์์ selfInvocation() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค. ์ฆ ๋์ผํ ๊ฐ์ฒด ๋ด์์ ์์ ์ธ์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ฒ์ self-invocation์ด๋ผ ๋ถ๋ฅด๋๋ฐ, Proxy์์๋ ์ด๋ฌํ self-invocation์์ ๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค.
Spring AOP
Spring AOP๋ @Cacheable ์ด ์ ์ฉ๋ ๋ฉ์๋๋ฅผ ์บ์ฑํ๊ธฐ ์ํด ์ธํฐ์ ํฐ๋ฅผ ํตํด ์ธ๋ถ์ ์์ฒญ์ ๊ฐ๋ก์ฑ๋๋ค. Proxy๋ฅผ ํตํด ๋ค์ด์ค๋ ๋ฉ์๋ ํธ์ถ๋ง ์ธํฐ์ ํธํ๊ธฐ ๋๋ฌธ์, ๋์ผ ํด๋์ค ๋ด๋ถ(this)์์ ์ ๊ทผ๋ ๊ฒฝ์ฐ๋ ์บ์๊ฐ ๋์ํ์ง ์์ต๋๋ค.
์ ๋ฆฌํ์๋ฉด Spring AOP๋@Cacheable์ ๋์์ ์ํด ์ธ๋ถ์ ์์ฒญ์ ์ธํฐ์ ํธํ์ฌ ์บ์ฑํ์ง๋ง, ๋ด๋ถ ํธ์ถ์ proxy ๊ฐ ์๋ ์ค์ ๊ฐ์ฒด๊ฐ ํธ์ถํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ Spring AOP ๊ฐ ์บ์ฑํ ์ ์์ต๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ฒซ ๋ฒ์งธ ์๋์์๋ ์บ์๊ฐ ์ ์์ ์ผ๋ก ๋์ํ์ฃ . getProductOfReview() ๋ฉ์๋๋ ์ปจํธ๋กค๋ฌ์์ (์ธ๋ถ) ํธ์ถํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
ํ์ง๋ง ๋ ๋ฒ์งธ ์๋์์ ์บ์๊ฐ ๋์ํ์ง ์์๋๋ฐ, sendSlackMessage() ๋ฉ์๋๊ฐ ์ธ๋ถ์์ ํธ์ถ๋์ง ์๊ณ getProductOfReview() ๋ฉ์๋ ๋ด๋ถ์์ ํธ์ถ๋๊ธฐ ๋๋ฌธ์ ๋๋ค. ์ฆ Self-invocation ๋์ต๋๋ค.
self-invocation ์์ ์ @Cacheable ์ด ๋์ํ์ง ์์๊น์? Spring AOP๋ ์บ์ฑ์ ์ํด proxy ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐ ํ ๋ฉ์๋๋ฅผ ํธ์ถํด์ผ ํ์ง๋ง, self-invocation ๋ฉ์๋๋ proxy ๊ฐ์ฒด๊ฐ ์๋ ์ค์ ๊ฐ์ฒด(this)๋ฅผ ํธ์ถํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
Self-Invocation ํด๊ฒฐ ๋ฐฉ์ (Proxy ์์ฑ)
๋ด๋ถ ํธ์ถ ์์๋ this ๊ฐ ์๋ proxy๋ฅผ ์ฐธ์กฐํ ์ ์๋ค๋ฉด, proxy์ ์บ์ฑ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์ฌ ์ ์์ต๋๋ค.
์ฆ @Cacheable sendSlackMessage() ๋ฉ์๋๋ฅผ self-invocation ์ด ์๋ proxy ๊ฐ์ฒด๋ก ํธ์ถํ๋ ๊ฒ์ ๋๋ค.
2๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก ์ ๊ทผํ ์ ์๋๋ฐ,
1) ApplicationContext์์ ๋์ผํ ํด๋์ค์ proxy bean์ ์์ฑํ์ฌ ์ฌ์ฉํ๊ฑฐ๋
2) @Resource ์ด๋ ธํ ์ด์ ์ผ๋ก self-autowiring ํ ์ ์์ต๋๋ค.
1) ApplicationContext DI
getSpringProxy() ๋ฉ์๋ ๋ด๋ถ์์ applicationContext.getBean(ReviewProductService.class)๋ฅผ ์ ์ธํฉ๋๋ค.
์ด๋ ๊ฒ ํ๋ฉด getBean()์ ์ธ์์ ๋์ผํ ํ์ ์ Bean ์ธ์คํด์ค๊ฐ ๋ฐํ๋ฉ๋๋ค. ์ฆ getBean() ์ ์ธ์๊ฐ ReviewProductService ํ์ ์ ํ๋ก์ ๊ฐ์ฒด๋ผ๋ฉด, ๋์ผํ๊ฒ ReviewProductService ํ์ ์ ํ๋ก์๊ฐ ๋ฆฌํด๋๋ ๊ฑฐ์ฃ .
ํ๋ก์ ๊ฐ์ฒด๊ฐ sendSlackMessage()๋ฅผ ํธ์ถํ๊ฒ ๋จ์ผ๋ก, @Cacheable ์ด ์ ์ฉ๋ sendSlackMessage()๋ ์ ์์ ์ผ๋ก ์บ์ ์ฒ๋ฆฌ ๋ฉ๋๋ค.
ํ๋ก์ ๊ฐ์ฒด๊ฐ sendSlackMessage() ๋ฅผ ํธ์ถํ๊ฒ ๋จ์ผ๋ก, @Cacheable ์ด ์ ์ฉ๋ sendSlackMessage() ๋ ์ ์์ ์ผ๋ก ์บ์ ์ฒ๋ฆฌ ๋ฉ๋๋ค.
2) @Resource์ผ๋ก AutoWiring
์คํ๋ง 4.3๋ถํด @Resource ์ด๋ ธํ ์ด์ ์ผ๋ก self autowiring์ ์ฌ์ฉํ ์ ์์ต๋๋ค. 1)์ ๋ฐฉ๋ฒ์ฒ๋ผ ApplicationContext๋ฅผ ํตํด Bean์ ๊ฐ์ ธ์ฌ ํ์์์ด ๊ฐ๋จํ ์์ ์ Bean ์ ์ฃผ์ ๋ฐ์ ์ ์์ต๋๋ค.
์ด๋ ๊ฒ ํ์ฌ API์ ์๊ตฌ์ฌํญ์ ๋ง์กฑํ๋ ๋์์ ์ฌ๋๋ด์ ํ ๋ฒ๋ง ์ ์ก๋๋๋ก API๋ฅผ ์์ ํ ์ ์์์ต๋๋ค.
๋ง๋ฌด๋ฆฌ
๋ฌด์ฌ์ฝ ์ฌ์ฉํ @Cacheable ์ด๋ ธํ ์ด์ ์๋ ์๋ง์ ์๋ฆฌ์ ์ด์ ๊ฐ ์์์ ๋ฐฐ์ ์ต๋๋ค. ๊ฐ๋จํ ์บ์ ์ ์ฉ์ด๋ผ ์๊ฐํ์ง๋ง, ๊ทธ ์์์ Self Invocation๊ณผ Spring AOP, Proxy๋ผ๋ ๋ค์ ๋ณต์กํ ๊ฐ๋ ์ ์ ์ ์์์ต๋๋ค.
'Backend > ๐ฟ Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Querydsl] Pagination ์ฑ๋ฅ ๊ฐ์ part1.PageableExecutionUtils (11) | 2023.02.21 |
---|---|
[SpringBoot] @NotNull, @NotEmpty, @NotBlank ์ฐจ์ด์ , ๊ทธ๋ง ํท๊ฐ๋ฆฌ๊ธฐ๋ก ํด์ (1) | 2023.01.28 |
[JPA] Can not issue data manipulation statements with executeQuery(). (0) | 2022.12.08 |
[IDE] Unable to resolve table ์๋ฌ ํด๊ฒฐ (0) | 2022.12.08 |
RDS - springboot ์ฐ๋ ์ ์๋ฌ ํธ๋ฌ๋ธ์ํ ๋ชจ์ (0) | 2022.09.11 |