์ง๋๋ฒ ํฌ์คํ ์ ์ดํ๋ก 3์ผ๊ฐ ํ ์คํธ ์ฝ๋์ ๊ดํ ๊ณต๋ถ๋ฅผ ํ๋ฉฐ ๋ฆฌํฉํฐ๋ง์ ์งํํ๋ค.
๋ฐ์ดํฐ ๋ถ์ฐ ํ๊ฒฝ์์์ ํธ๋์ญ์ ์ ๊ณ ๋ ค๋ ์ถฉ๋ถํ ์ค์ํ ์ค๊ณ ๊ฐ๋ค. ๊ทธ ๋ฆฌํฉํฐ๋ง ๊ณผ์ ์ ์ ๋ฆฌํด๋ณด๊ฒ ๋ค.
https://sweeeetgoguma.tistory.com/entry/%E3%80%8COutBox-Pattern%E3%80%8D-%ED%99%9C%EC%9A%A9
ใOutBox Patternใ ํ์ฉ
https://github.com/GroovyArea/MyChickenBreastShop/wiki/Version-1 GitHub - GroovyArea/MyChickenBreastShop: ChikenBreastShop API with Spring boot ChikenBreastShop API with Spring boot. Contribute to G..
sweeeetgoguma.tistory.com
> ์ง๋ ํฌ์คํ ์์ ์ ๋ฆฌํ๋ค์ํผ ๊ธฐ๋ฅ์ ์๋ฃ๊ฐ ๋์๋ค
๋ฌธ์ ์ ์ ์ ๋ฆฌํด๋ณด๊ฒ ๋ค.
1. ์์ ๋ฐ์ค ํจํด์ ์ด์ฉ ํ ํธ๋์ญ์ ์ฒ๋ฆฌ์ ๋ฌธ์
์ผํ ๋ณด๋ฉด ๋ฌธ์ ๊ฐ ์์ง๋ง ๋ฌธ์ ๊ฐ ์๋ค.
๋ฐ๋ก ์์๋ฐ์ค ๋ฐ์ดํฐ ๋ฆฌ์คํธ์ ์ฒ๋ฆฌ๊ฐ ํ๋์ ํธ๋์ญ์ ๋จ์๋ก ๋ฌถ์ฌ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๋ฆฌ์คํธ ์ค ํ๋๋ผ๋ ์๋ชป๋ ๋ฐ์ดํฐ๊ฐ ๋ค์ด์์ด ์์ธ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ ๋ชจ๋ ๋กค๋ฐฑ ํ ๋ค์ ๋ฌดํ ๋ฐ๋ณต์ ํ๊ฒ ๋ ๊ฒ์ด๋ค.
์ด๋ป๊ฒ ํด์ผ ํ ๊น?
@Scheduled(cron = "0/10 * * * * ?")
public void schedulingCheckStock() {
ObjectMapper objectMapper = new ObjectMapper();
log.info("์ฌ๊ณ ํ์ธ ์ค . . .");
List<OutBox> outBoxList = outBoxMapper.selectAllOrderOutBox();
if (!outBoxList.isEmpty()) {
List<Long> completedList = new LinkedList<>();
outBoxList.forEach(outBox -> {
outBoxStockCheck(objectMapper, completedList, outBox);
});
if (!completedList.isEmpty()) {
outBoxMapper.deleteAllById(completedList);
}
}
}
@Transactional
void outBoxStockCheck(ObjectMapper objectMapper, List<Long> completedList, OutBox outBox) {
String payload = outBox.getPayload();
try {
JsonNode jsonNode = objectMapper.readTree(payload);
String itemName = jsonNode.get("item_name").asText();
if (productMapper.selectStockOfProduct(itemName) < Integer.parseInt(jsonNode.get("quantity").asText())) {
kakaoPayService.changeStockFlag(false);
}
completedList.add(outBox.getId());
} catch (JsonProcessingException e) {
log.error(e.getMessage());
outBoxMapper.insertOrderOutBox(outBox);
}
}
@Scheduled(cron = "0/10 * * * * ?")
public void schedulingValidNumberEmail() {
ObjectMapper objectMapper = new ObjectMapper();
log.info("์ด๋ฉ์ผ ์ ์ก ์ค...");
List<OutBox> outBoxList = outBoxMapper.selectAllEmailOutBox();
if (!outBoxList.isEmpty()) {
List<Long> completedList = new LinkedList<>();
outBoxList.forEach(outBox -> {
validateEmailNumber(objectMapper, completedList, outBox);
});
if (!completedList.isEmpty()) {
outBoxMapper.deleteAllById(completedList);
}
}
}
@Transactional
void validateEmailNumber(ObjectMapper objectMapper, List<Long> completedList, OutBox outBox) {
String payload = outBox.getPayload();
try {
JsonNode jsonNode = objectMapper.readTree(payload);
String userEmail = jsonNode.get("email").asText();
String authKey = jsonNode.get("email_key").asText();
MailDTO content = mailContentService.createMailContent(payload);
sendMailService.sendEmail(content);
completedList.add(outBox.getId());
redisService.setDataExpire(userEmail, authKey, EXPIRE_DURATION);
} catch (MailException e) {
log.error("๋ฉ์ผ ๋ฐ์ก ์ค ์ค๋ฅ ๋ฐ์ . . .");
outBoxMapper.insertOutBox(outBox);
} catch (FailedPayloadConvertException | JsonProcessingException e) {
log.error(e.getMessage());
outBoxMapper.insertOutBox(outBox);
}
}
=> ํธ๋์ญ์ ๋จ์๋ฅผ ์ธ๋ถํ์์ผฐ๋ค. ์๋ฌ ๋ฐ์ ์ ๋ค์ ์์๋ฐ์ค ํ์ ๋ง์ง๋ง์ผ๋ก ๋ฌธ์ ๊ฐ ์๊ธด ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ๋ค. ๊ทธ๋ ๊ฒ ๋๋ฉด ๋ฌธ์ ์๋ ๋ ์ฝ๋ ์ดํ์ ๋ฐ์ดํฐ๊ฐ ์ ์์ ์ผ๋ก ํ์์ ๊บผ๋ด์ ธ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํด์ง๋ค.
2. SAGA ํจํด
SAGA ํจํด ๊ตฌํ ์ ๊ณ ๋ คํด์ผ ํ ์ฌํญ?
- ์ ์ฌํ๋ ์ผ์์ ์ธ ์ค๋ฅ๋ค์ ์ฒ๋ฆฌํ ์ ์์ด์ผ ํ๋ฉฐ, ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด ๋ฉฑ๋ฑ์ฑ ์ ๊ณต์ด ํ์
- Work Flow๋ฅผ ํญ์ ๋ชจ๋ํฐ๋งํ๊ณ ์ถ์ ํ๋ ๊ด์ฐฐ ๊ฐ๋ฅ์ฑ์ ๊ตฌํํ๋ ๊ฒ์ด ์ข๋ค.
3. OutBox ํจํด
๋ฐ์ ๊ฐ๋ฅํ ๋ฌธ์ ?
- ๋ฐ์ดํฐ์ ์ผ๊ด์ฑ ๋ณด์ฅ ๋ฌธ์
- ๋ฉ์ธ์ง์ ์ ๋ฌ ๋ณด์ฆ ์์ค์ ์ ๋ฐ์ ธ์ผ ํ๋ค.
4. ์คํ๋ง์์ ๋ํ์ ์ผ๋ก ์ฌ์ฉ๋๋ AOP
- @Transactional์ด ๋ํ์
- aop์ ์ถ๊ฐ์ ์ธ ๊ณต๋ถ๊ฐ ํ์ํ ๊ฒ ๊ฐ๋ค.
'๐ Spring Framework > Spring Project' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
ใ์ปจํธ๋กค๋ฌ ๋จ์ ํ ์คํธใ (0) | 2022.06.23 |
---|---|
ใํ ์คํธ ์ฝ๋ & Spring REST Docsใ (0) | 2022.06.20 |
ใOutBox Patternใ ํ์ฉ (0) | 2022.06.10 |
2022.06.07 ใํ๋ก์ ํธ ์ค๊ฐ ์ ๊ฒใ (0) | 2022.06.07 |
2022.06.02 ใDB ๋์์ฑ ๋ฌธ์ ใ (0) | 2022.06.02 |