์ฌ๋ฌ๊ฐ์ DB ์์ ์ ๋์์ ์ํํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ํฉ์ณ์ ๋ณด๋ด์ค์ผ ํ ๋
์ฌ๋ฌ๊ฐ์ DB ์์ ์ ๋์์ ์ํํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ํฉ์ณ์ ๋ณด๋ด์ค์ผ ํ ๋ ๊ด๋ จ
์๋ฒ ๊ธ์์ ๋ค๋ฃจ๊ณ ์ ํ๋ ์ ์
- Spring ์์ API ์ฒ๋ฆฌ ์
- Asynchronous ํ Hibernate DB ์์ ์ ํ๊ณ
- ๋ชจ๋ ๊ฒฐ๊ณผ๋ฌผ์ wait ํด์ ํจ๊ป ์ฒ๋ฆฌํ ๋ค ๋ด๋ ค์ค์ผ ํ๋ ์ํฉ
3๊ฐ์ง ์์๋ฅผ ๋ค ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ๋ํ ๊ณ ๋ฏผ์ ๋๋ค.
๋ฉ์ธํ์ด์ง์ ๋ณด์ฌ์ฃผ๋ ์ฌ๋ฌ๊ฐ์ง ์์ ฏ์ ํ๋ฒ์ ๋ด๋ ค์ค์ผ ํ๋ ์ํฉ์ด์๋๋ฐ, ์์ ฏ 1๊ฐ๋ง๋ค ๋ณต์กํ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ ค DB ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ผ ํ๋ ์ํฉ์ด๋ผ ์์ฐจ์ ์ผ๋ก ์ํํ๋ฉด 7์ด๊ฐ ๋๊ฒ ๊ฑธ๋ฆฌ๊ณ ์์์ต๋๋ค. ๊ทธ๋๋ ์ด ์์ ๋ค์ ๋์์ ๋ชจ๋ ์์ ฏ์ ๋ถ๋ฌ์จ ๋ค, ํ๋ฒ์ ๋ด๋ ค ์ฃผ๋ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌํ๊ณ ์ ์ 3๊ฐ์ง ์์๊ฐ ํ์ํด์ก์ต๋๋ค.
์ฒซ๋ฒ์งธ ์๋, CompletableFuture
Note
concurrency ๋ future ์ ๋ํด ์ต์ํ์ง ์์ผ์๋ค๋ฉด ์๋ ๊ธ๋ฑ์ ์ฝ์ด๋ณด์๋ฉด ์ข์ต๋๋ค.
ํ์ฌ์์ ๊ฐ๋ฐ์ค์ธ ์์ค์ฝ๋ ์ค์๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ก์ง์ ์ํด CompletableFuture
๋ฅผ ์ฌ์ฉํ๊ณ ์๋ ๋ถ๋ถ๋ค์ด ์์ต๋๋ค. Mybatis
์ธ์
์ ์ด์ฉํ๋ ๊ฒฝ์ฐ, ๋ณ ๋ฌธ์ ์์ด ํด๋น ์์
์ CompletableFuture.runAsync()
๋ฅผ ์ด์ฉํด์ ์ฌ๋ฌ task ๋ฅผ ๋๋ฆฐ ๋ค, CompletableFuture.allOf(futureTaskArray).join()
์ ์ด์ฉํด ๋์์ ์ํํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ค๋ ธ๋ค๊ฐ ๋ฐ์ ์ฌ ์ ์์์ต๋๋ค.
๋์ผํ ๋ฐฉ๋ฒ์ ์ด๋ฒ ์์
์ ์ฌ์ฉํด ๋ณด์์ง๋ง, ๊ธฐ์กด์ ํตํฉ๊ฒ์์ด Mybatis
session ์ ์ด์ฉํ ๊ฒ๊ณผ ๋ฌ๋ฆฌ์์ ฏ๋ถ๋ถ์ Hibernate
๋ฅผ ์ฌ์ฉํ๊ณ ์์๊ณ , Hibernate
๋ ๊ธฐ๋ณธ์ ์ผ๋ก Synchronized-transactionalsession ์ด ํ์ํ๊ธฐ์ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค
Caused by: org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:134)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
at com.healing.beauty.dao.BaseDao.getCurSession(BaseDao.java:44)
์ ํฌ hibernate ๋ ๋ฏธ๋ฆฌ ๋ง๋ค์ด ๋ BaseDao ๋ผ๋ ๋ถ๋ชจ Dao ํด๋์ค์์ session ์ ์ก์๋ค ์ฌ์ฉํ๊ฒ ๋์ด์๋๋ฐ, ํด๋น ์์
์ SpringSessionContext
์์๋ ์๋์ ๊ฐ์ด synchorize ํ ํธ๋์ญ์
์ ๊ฐ์ง๊ณ ์๋์ง๋ฅผ ์ฒดํฌํ๊ฒ ๋ฉ๋๋ค.
if (TransactionSynchronizationManager.isSynchronizationActive()) {
Session session = this.sessionFactory.openSession();
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.MANUAL);
}
SessionHolder sessionHolder = new SessionHolder(session);
TransactionSynchronizationManager.registerSynchronization(
new SpringSessionSynchronization(sessionHolder, this.sessionFactory, true)
);
TransactionSynchronizationManager.bindResource(this.sessionFactory, sessionHolder);
sessionHolder.setSynchronizedWithTransaction(true);
return session;
}
else {
throw new HibernateException("Could not obtain transaction-synchronized Session for current thread");
}
๊ฒฐ๊ตญ completableFuture ๋ฅผ ์ธ ๊ฒฝ์ฐ, ์ด ๋ถ๋ถ์ ํต๊ณผํ์ง ๋ชปํ์ต๋๋ค.
2. @Async
์ด๋
ธํ
์ด์
์ฌ์ฉ
Note
Async ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ ์ ์ ์กฐ๊ฑด์ ์์ธํ ์ค๋ช ๋ค์ด ๋งํฌ์ ์์ผ๋ ์ฐธ๊ณ ๋ฐ๋๋๋ค
@Async
๋ ๊ฐ๋จํ ๋งํ์๋ฉด, proxy ๋ฅผ ์ฌ์ฉํ์ฌ ๋น๋๊ธฐ์ฒ๋ฆฌ๋ฅผ ์ํํ๋ ๋ฐฉ์์ด๊ธฐ ๋๋ฌธ์, ๋ฌด์กฐ๊ฑด ๊ฐ์ ์๋น์ค๋ด ํธ์ถ์ด ์๋ ๋ค๋ฅธ ์๋น์ค ํน์ ์ปจํธ๋กค๋ฌ์์ ์๋น์ค ํธ์ถ๋ฑ์ผ๋ก ํ๋ก์๋ฅผ ๊ฑฐ์น๊ฒ ํด์ผ ํฉ๋๋ค. ๊ทธ๋์ ๊ธฐ์กด์ WidgetService
๋ด์์ setting ํ๋ ๋ถ๋ถ์ ๋ผ์ด๋ด์ WidgetAsyncService
๋ฅผ ์ถ๊ฐํด์ ๋ฉ์๋๋ฅผ ๋ง๋ค์ด์ฃผ๊ณ ์์ ์ด๋
ธํ
์ด์
์ ๋ถ์์ต๋๋ค.
@Async
public Future<Void> setDataV3(Widget widget, WidgetFilter filter) {
logger.info("========== async STARTTTT ========== " + widget.getTitle());
/***
์ด์ฉ๊ตฌ ์ ์ฉ๊ตฌ ์์ ฏ์ ๊ฐ์ง๊ณ ํ์
๋ณ๋ก Dao ๋ฑ๊ณผ ์ฐ๊ณํด์ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ๊ธฐ๋ฅ
***/
logger.info("========== async ENDDDDDDDDDD ========== " + widget.getTitle());
return new AsyncResult<>(null);
}
๊ทธ๋ฆฌ๊ณ ์ํํ๋๋ฐ ๊ฒฐ๊ณผ๊ฐ ๋๊ฐ.... ๋ ๋๊ธฐํ๋ ํธ๋์ญ์ ์ด ์๋ต๋๋ค.
๊ทธ๋๋ ์๊น์ ๋ฌ๋ฆฌ ์ด๋ฒ์ spring annotation ์ ์ด์ฉํ task executor ๋ฅผ ์ฌ์ฉํ๊ณ ์๊ธฐ์, ์ฌ๊ธฐ์ Transaction Propagation ์ ์ ์ฉํด๋ณด์์ต๋๋ค. (์ฐธ๊ณ ๋ก compleatable future ๋ฅผ ์ฌ์ฉํ ๋๋ propagation ์ ์ ์ฉํด๋ ๋ณํ๊ฐ ์์๋ค.)
3. @Async
+ @Transactional(Propagation.NOT_SUPPORTED)
๊ธฐ๋ณธ์ ์ผ๋ก ํด๋น ํ๋ก์ ํธ์ ๋ชจ๋ @Service
์๋ @Transactional
์ด ๊ฑธ๋ ค์์ต๋๋ค. ๊ทธ๋ ๊ธฐ์ ๋ชจ๋ ์๋น์ค๋ค์ด ํธ๋์ญ์
๋ด์์ ์ํ๋๊ณ ์๋ค๊ณ ์๊ฐํ๋ฉด ๋ฉ๋๋ค. ๊ทธ๋ฐ๋ฐ @async
๋ ๊ธฐ๋ณธ์ ์ผ๋ก ThreadLocal
์์ ๋ฒ์ด๋ ์ํ๋๋ฉด์ Synchronize ํ ํธ๋์ญ์
์ ๊ฐ์ง์ง ๋ชปํ๊ฒ ๋๊ณ ๊ทธ๋ฌ๋ฉด์ hibernate ์ํ์ด ๋์ง ์์์ต๋๋ค.
๊ทธ๋์ Propagation ๋ฃฐ์ ๋ฐ๊ฟ์ฃผ๊ธฐ๋ก ํ์ต๋๋ค.
์ด ์ผ์ด์ค์์๋ Propagation.NEVER
๋ ํธ๋์ญ์
์ ์ ๋ฌํ์ง ์๊ธฐ์ ์์ ๊ฐ์ ์ค๋ฅ๊ฐ ๋ ๊ฒ์ด๋ฏ๋ก, NOT_SUPPORTED
๋ REQUIRED_NEW
๊ฐ ํ์ํ๋ฐ, ์ด์ฐจํผ ์กฐํ๋ง ํ๋ ์์
์ด๊ธฐ์ ๋ถ๋ชจ-์์ ํธ๋์ญ์
์๊ด์๊ธฐ์ NOT_SUPPORTED
๋ก ํ๊ณ , readOnly
๋ true ๋ก ์ฃผ์์ต๋๋ค.
// ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๋ ๋ถ๋ถ WidgetAsyncService ๋ด. ional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
@Async
public Future<Void> setDataV3(Widget widget, WidgetFilter filter) {
logger.info("========== async STARTTTT ========== " + widget.getTitle());
/***
์ด์ฉ๊ตฌ ์ ์ฉ๊ตฌ ์์ ฏ์ ๊ฐ์ง๊ณ ํ์
๋ณ๋ก Dao ๋ฑ๊ณผ ์ฐ๊ณํด์ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ๊ธฐ๋ฅ
***/
logger.info("========== async ENDDDDDDDDDD ========== " + widget.getTitle());
return new AsyncResult<>(null);
}
์ด๋ฒ์ ์ฑ๊ณตํ ์ฝ๋์ด๊ธฐ ๋๋ฌธ์ ๋น๋๊ธฐ๋ฅผ ๋ถ๋ฅด๋ ๋๊ธฐ๋ถ๋ถ ์๋น์ค๊น์ง๋ ์ถ๊ฐ!
๋น๋๊ธฐ ๋์ ํ์ธ
๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ์ ๋๋ก ๋์๊ฐ๋์ง ๋ณด๊ธฐ ์ํด์ ๊ผญ ๋ก๊น ์ด ํ์ํฉ๋๋ค. ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ถ๋ถ์ด ์ ๋๋ก ๋๊ธฐ ํ๋ก์ธ์ค์ ๋ณ๊ฐ๋ก ์ํ ๋๋์ง ๋ณด๊ธฐ ์ํด ๋น๋๊ธฐ ๋ฉ์๋ ์ํ ์ /ํ, ๋น๋๊ธฐ ๋ฉ์๋ ์ ์์๊ณผ ๋์ ๋ก๊ทธ๋ฅผ ์ถ๊ฐํ์ต๋๋ค
@Transactional
public PagingResult<Widget> getWidgetListByV3(WidgetFilter filter) {
// dao ์ ์ ์ํด ๋จผ์ ์์ ฏ ๋ฆฌ์คํธ ๋ถ๋ฌ์ค๊ธฐ
PagingResult<Widget> result = getDao().getListBy(filter);
// ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ๊ฒฐ๊ณผ๋ฅผ ๋ด์ array
List<Future> futures = new ArrayList<>();
logger.info(" ============== 111");
// ์์ ฏ๋ง๋ค ํ์ํ ๋ฐ์ดํฐ ์ธํ
for (Widget widget : result.getData()) {
logger.info("========== method start ========== " + widget.getTitle());
futures.add(widgetAsyncService.setDataV3(widget, filter));
logger.info("========== method end ========== " + widget.getTitle());
}
logger.info(" ============== 222");
// ์๋ฃ๋๊ธฐ
for (Future future : futures) {
try {
// AsyncResult ์ ๊ฐ์ Future object ๋ค์ get ์ ์ด์ฉํด ๊ฒฐ๊ณผ ๊ฐ์ ๋ฐ์๋๊น์ง wait ํ ์ ์๋ค.
// ์ด ์์ ์์ void ํํ์ ๋ฆฌํด๊ฐ ์๋ ๋น๋๊ธฐ ๋ฉ์๋์์ง๋ง, ๊ฒฐ๊ณผ ๊ฐ์ด ํ์ํ ๊ฒฝ์ฐ ๋น๋๊ธฐ๋ฉ์๋์ return ์์ AsyncResult ์ ๋ฃ์ด์ฃผ๊ณ
// ์๋ Future.get() ์ ์ด์ฉํด ํด๋น ๊ฐ์ ๋ฐ์๋ด์ ์ฒ๋ฆฌํ ์ ์๋ค.
future.get();
} catch(ExecutionException e) {
e.printStackTrace();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
logger.info(" ============== 333 ");
// ์์ ฏ ํํฐ๋ง ๋ฐ ์ ๋ ฌ
List<Widget> finalList = sortAndFilterWidget(result.getData());
result.setData(finalList);
logger.info(" ============== 444 ");
return result;
}
๋ง์ฝ, ๋น๋๊ธฐ๋ก ์ํํ๋ ๋ถ๋ถ์ด ๊ฒฐ๊ณผ๊ฐ๋ ํ์์๋ ๊ฒฝ์ฐ๋ผ๋ฉด (count
๋ฅผ ์ฌ๋ฆฌ๊ฑฐ๋, push
๋ฅผ ๋ณด๋ด๊ฑฐ๋ ํ๋ api response ์ ํ์ํ ๋ถ๋ถ์ด ์๋๋ผ๋ฉด) ์์ ์ฒ๋ผ get ์ ํด์ ๊ตณ์ด ๊ฒฐ๊ณผ๊ฐ์ ๊ธฐ๋ค๋ฆด ํ์ ์์ด ๋ฐ๋ก return ํด๋ฒ๋ฆฌ๋ฉด ๋ฉ๋๋ค.
์ ์๋ฌดํผ ๊ทธ๋์ ์์ฒ๋ผ ์ํํ๋ฉด ์์๋๋ ๊ฒฐ๊ณผ๋ ์๋์ฒ๋ผ ๋ก๊ทธ๋ก ๋์ต๋๋ค.
//๋ก๊ทธ ๋ด์ฉ์ด ๋ง์ ์ถ์ฝํ์ต๋๋ค.
2019-02-03 02:20:25.234 INFO --- [-nio-8080-exec-5] c.example.service.WidgetService [160] : ============== 111
2019-02-03 02:20:25.242 INFO --- [-nio-8080-exec-5] c.example.service.WidgetService [164] : ========== method start ========== ์์ ฏ ํ์ดํ A
2019-02-03 02:20:25.244 INFO --- [executor-task-19] c.example.service.WidgetAsyncService [ 80] : ========== async STARTTTT ========== ์์ ฏ ํ์ดํA
2019-02-03 02:20:25.244 INFO --- [-nio-8080-exec-5] c.example.service.WidgetService [166] : ========== method end ========== ์์ ฏ ํ์ดํ A
2019-02-03 02:20:25.249 INFO --- [-nio-8080-exec-5] c.example.service.WidgetService [164] : ========== method start ========== ์์ ฏ ํ์ดํ B
2019-02-03 02:20:25.250 INFO --- [-nio-8080-exec-5] c.example.service.WidgetService [166] : ========== method end ========== ์์ ฏ ํ์ดํ B
2019-02-03 02:20:25.251 INFO --- [-nio-8080-exec-5] c.example.service.WidgetService [164] : ========== method start ========== ์์ ฏ ํ์ดํ C
2019-02-03 02:20:25.251 INFO --- [-nio-8080-exec-5] c.example.service.WidgetService [166] : ========== method end ========== ์์ ฏ ํ์ดํ C
2019-02-03 02:20:25.252 INFO --- [-nio-8080-exec-5] c.example.service.WidgetService [170] : ============== 222
2019-02-03 02:20:25.252 INFO --- [executor-task-19] c.example.service.WidgetAsyncService [ 80] : ========== async ENDDDDDDDDDD ========== ์์ ฏ ํ์ดํ A
2019-02-03 02:20:25.255 INFO --- [ executor-task-7] c.example.service.WidgetAsyncService [ 80] : ========== async STARTTTT ========== ์์ ฏ ํ์ดํ C
2019-02-03 02:20:25.255 INFO --- [ executor-task-1] c.example.service.WidgetAsyncService [ 80] : ========== async STARTTTT ========== ์์ ฏ ํ์ดํ B
2019-02-03 02:20:26.552 INFO --- [ executor-task-1] c.example.service.WidgetAsyncService [293] : ========== async ENDDDDDDDDDD ========== ์์ ฏ ํ์ดํ B
2019-02-03 02:20:26.674 INFO --- [ executor-task-7] c.example.service.WidgetAsyncService [293] : ========== async ENDDDDDDDDDD ========== ์์ ฏ ํ์ดํ C
2019-02-03 02:20:26.674 INFO --- [-nio-8080-exec-5] c.example.service.WidgetService [182] : ============== 333
2019-02-03 02:20:26.675 INFO --- [-nio-8080-exec-5] c.example.service.WidgetService [187] : ============== 444
111 ๋ถ๋ถ์ด ์์ฐจ์ ์ผ๋ก ์ํ๋๊ณ , ๋น๋๊ธฐ ๋ฉ์๋๊ฐ ์์ ฏ ์ซ์๋งํผ ์คํ๋๋๋ฐ, ๋ฉ์๋ ์ ํ์ ๋ก๊ทธ๋ค์ด ๋น๋๊ธฐ์ด๊ธฐ์ ๋ฐ๋ก ์์๋๋ก ์ญ ์คํ ๋๋ฉด์ ๋ฃจํ๊ฐ ๋๋๋ฉด ๋ฐ๋ก 222๊ฐ ์ฐํ๋๋ค. ๊ทธ์ ๋์์ ์์ ฏ ๋ด์ฉ์ ๋ถ๋ฌ์ ์ธํ ํ๋ ๋ฉ์๋๋ค์ด ๋์ ๋ค๋ฐ์ ์ผ๋ก Async ์์ ์ ์์ํ๊ณ ๊ฐ๋ณ์ ์ผ๋ก ๋๋๊ฒ ๋ฉ๋๋ค.
์ฆ 222๋ฅผ ์ฐ๋ ๋ถ๋ถ๊ณผ ๊ฐ ์์ ฏ๋ด์ฉ์ ๋น๋๊ธฐ๋ก ๋ถ๋ฌ์ค๋ ๋ถ๋ถ์ ์๋ก ๋ณ๋ ฌํ๊ฒ ์งํ๋ฉ๋๋ค.
๋จ, 333 ์ด์ ์ ๋ชจ๋ future ์์ ์ get ํด์ ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์ 333์ ๋ชจ๋ async method end ์ดํ์ ์ฐํ๊ฒ ๋ ๊ฒ์ด๊ณ , ๊ทธ ํ ์ ๋ ฌ ์์ ๋ฑ ๋ค์ 444 ๊ฐ ์ฐํ ๋ค ๋ชจ๋ ์์ ์ด ๋๋๊ณ ๊ฒฐ๊ณผ๋ฌผ์ ๋ด๋ ค์ฃผ๊ฒ ๋ฉ๋๋ค.
์ ๋ก๊ทธ๋ฅผ ๋ณด๋ฉด [executor-task-19]
์ฒ๋ผ Async ์์ ์ฌ์ฉํ๋ ๋น๋๊ธฐ ํ์คํฌ์ ๋ฒํธ๋ฅผ ๋ณผ ์ ์์ต๋๋ค. Async ์ฌ์ฉ ์ด์ ์ Config ์ธํ
์ผ๋ก ๋์ ์ฌ์ฉํ ์ ์๋ executor ์ ์ซ์ ๋ผ๋๊ฐ, ํด๋น ์์
์ด hibernate ์ DB ์์
์ด๊ธฐ์ connection pool ๊ฐ์ ๋ฑ์ ๋๋ฌด ์๊ฒ ์ธํ
ํด ๋์๋ค๋ฉด ๋์์ ํ ์ ์๋ ์์
์ด ํ๊ณ๊ฐ ์์ด์ ๋ก๊ทธ๊ฐ ์์๊ณผ ๋ค๋ฅด๊ฒ ์ฐํ ์ ๋ ์์ต๋๋ค. (์ค์ ๋ก ์ฒซ๋ฒ์งธ ์๋ํ์ ๋ ์ปค๋ฅ์
ํ ๊ฐ์๊ฐ 5๊ฐ ์๋๋ฐ ์กฐํ๋ ์์ ฏ์ด 10๊ฐ์์ด์ 5๊ฐ ์์
๋ง ๋์์ ์ํ ๋์์ต๋๋ค.)
๋ง๋ฌด๋ฆฌ
์ ๋ฐฉ๋ฒ์ด ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ด๋ผ๊ธฐ๋ณด๋จ, ์ ์ ํ๊ฒ ์ฑ๊ณตํ ์ ์ผํ ๋ฐฉ๋ฒ์ด๋ผ์ ์ ๋ฆฌํด ๋ณด์์ต๋๋ค. ์ข๋ Future
ํด๋์ค์ Transaction session ์ ๋ํ ๊น์ด๊ฐ ์๋ค๋ฉด ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก๋ ์ฌ์ฉ์ด ๊ฐ๋ฅํ ์ง ๋ชจ๋ฅด๊ฒ ์ต๋๋ค.
์ด์จ๋ , ๋์๋ค๋ฐ์ ์ผ๋ก DB ์ ๋ถ์ด์ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๊ณ , ํด๋น ๊ฒฐ๊ณผ๋ฅผ ๋ชจ์์ ๋ค์ api response ๋ก ๋ด๋ ค์ค ํ์๊ฐ ์๋ค๋ฉด ์ด์ ๋น์ทํ ๋ฐฉ์์ผ๋ก ํด๊ฒฐํ ์ ์์ต๋๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก ์ด์ ์ 7์ด 8์ด ์ ๋ ๊ฑธ๋ฆฌ๋ ์์ ์ 1์ด ๋ง์ ํด๊ฒฐ๋์์ต๋๋ค. ๊ฑฐ๊ธฐ์ ์ถ๊ฐ์ ์ผ๋ก ์บ์ฑ๊น์ง ๊ฑธ์ด์ 5๋ถ์ 1๋ฒ๋ง 1์ด์ ๋ ๊ฑธ๋ฆฌ๊ณ ๋๋จธ์ง ์์ฒญ๋ค์ 200ms ์ ๋๋ง์ ์ฒ๋ฆฌ๋๊ฒ ๋ณ๊ฒฝํ์์ต๋๋ค. ๋!