Background Sync - team-yaza/mozi-client GitHub Wiki
๊ฐ์
์ด ๊ธ์ MOZI์์ ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ๋ฅผ ๊ตฌํํ ๋์ ๊ณ ๋ฏผ์ด ๋ด๊ธด ๊ธ์ด๋ค.
๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ์ ๊ฐ๋
์ ์ฌ์ฉ์์ ์ธํฐ๋ท ์ฐ๊ฒฐ์ด ์์ ์ ์ด์ง ์์ ์๋๋ฆฌ์ค๋ฅผ ์ฒ๋ฆฌํ๋ ๊ฒ์ด๋ค.
๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ๋ ์ฌ์ฉ์๊ฐ ์์ ์ ์ธ ์ธํฐ๋ท ์ฐ๊ฒฐ ์ ๊น์ง ํ๋์ ๋ฏธ๋ฃจ๋ ์น API์ด๋ค.
์๋น์ค ์์ปค๋ฅผ ์ฌ์ฉํ๋ฉด ์คํ๋ผ์ธ ํ๊ฒฝ์ ์ํด asset์ ์บ์ํ ์ ์์ผ๋ฉฐ IndexedDB๋ฅผ ํตํด ๋ฐ์ดํฐ ์ ์ฅ์๋ก ์์
ํ ์ ์๋ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ค.
๋ฐฑ๊ทธ๋ผ์ด๋์์ ์์
์ ์ฒ๋ฆฌํ๋ฉด, ์ก๊ฒ ํ๋ฐ๋๋ ๊ฐ๋ณ ํ์ด์ง์ ํน์ฑ์์ ๋ฒ์ด๋ ์ ์๋ค. ์นํ์ด์ง๋ ์ด์ ๋ ์ง ๋ซํ ์ ์๊ณ , ์ฌ์ฉ์ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ ๋์ด์ง ์ ์์ผ๋ฉฐ ์๋ฒ๊ฐ ์ฃฝ์ ์ ์๋ค.
์ฌ์ฉ์ ๊ธฐ๊ธฐ์ ๋ธ๋ผ์ฐ์ ๊ฐ ์ค์น๋์ด์๋ ํ ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ ์์
์ ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋ ๋๊น์ง ์ฌ๋ผ์ง์ง ์๋๋ค.
๋ฐ๋ผ์ ํ์ด์ง๊ฐ ๋ซํ๋ ๊ณ์ ์งํ๋์ด์ผ ํ๋ ๋ชจ๋ ์์
์ ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ ์ฌ์ฉ์ ๊ณ ๋ คํด ๋ณผ ์ ์๋ค.
์ฌ์ฉ์๊ฐ ๋ฉ์์ง๋ฅผ ๋ณด๋ด๊ฑฐ๋ ํด์ผํ ์ผ ๋ชฉ๋ก์ค ํ๋๋ฅผ ์๋ฃ ํ์ํ๊ฑฐ๋ ์บ๋ฆฐ๋์ ์ด๋ฒคํธ๋ฅผ ์ถ๊ฐํ ๋ ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ๋ฅผ ์ฌ์ฉํ๋ฉด ์ด ์์
๋ค์ด ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋ ๊ฒ์ ๋ณด์ฅํ ์ ์๋ค.
ํ์ด์ง์์ Ajax ํธ์ถ๊ณผ ๊ฐ์ ์์ ์ ์ํํ๋ ๋์ ๋๊ธฐํ ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ๋ค.
navigator.serviceWorker.ready.then(function(registration) {
registration.sync.register('send-messages');
})
์ ์ฝ๋๋ ์นํ์ด์ง์์ ์คํ๋ ์ ์๋ค. ํ์ฑํ๋ ์๋น์ค ์์ปค์ ๋ฑ๋ก ๊ฐ์ฒด๋ฅผ ๋ฐ์์ 'send-messages'๋ผ๋ ๋๊ธฐํ ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ๋ค.
๊ทธ ํ ์๋น์ค ์์ปค์ ๋๊ธฐํ ์ด๋ฒคํธ๋ฅผ ์์ ํ sync ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํ๋ค. ์ด ์ด๋ฒคํธ๋ ํ์ด์ง๊ฐ ์๋ ์๋น์ค ์์ปค์์ ์์
์ ์ํํ ๋ ํ์ํ ๋ก์ง์ ํฌํจํ๋ค.
self.addEventListener("sync", function (event) {
if (event.tag === "send-messages") {
event.waitUntil(function () {
var sent = sendMessages()
if (sent) {
return Promise.resolve()
} else {
return Promise.reject()
}
})
}
})
sync ์ด๋ฒคํธ ๋ฆฌ์ค๋์์ waitUntil์ ์ฌ์ฉํด ์ด๋ฒคํธ ์ข
๋ฃ๋ฅผ ์์ฒญํ๊ธฐ ์ ๊น์ง ์ด๋ฒคํธ๊ฐ ์ ์ง๋ ์ ์๋๋ก ์ฒ๋ฆฌํ๋ค.
์ด๋ ๊ฒ ํจ์ผ๋ก์จ ํ์ํ ์์
์ ์๋ํ๊ณ , ์คํํ ์ ์๋ ์๊ฐ์ ๋ฒ ์ ์๊ณ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ ์ด๋ฒคํธ๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ๋ฆฌ์กธ๋ธํ๊ฑฐ๋ ๋ฆฌ์ ํ ์ ์๋ค.
sync ์ด๋ฒคํธ ๋ฆฌ์ค๋์์ ๋ฆฌ์ ๋ ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํํ๋ฉด ๋ธ๋ผ์ฐ์ ๋ ํด๋น ๋๊ธฐํ ์์ ์ ํ์ ์์ ๋ค์๋ฒ์ ๋ค์ ์๋๋๋๋ก ํ ๊ฒ์ด๋ค. ๋ค์ ๋งํด์ send-messages๋ผ๋ sync ์ด๋ฒคํธ๋ ์ฌ์ฉ์๊ฐ ์ฑ์ ์ข ๋ฃํ ํ์๋ ๋ค์ ์๋๋ ๊ฒ์ด๋ค.
์ฌ์ฉ์๋ ์ฑ์ ์ข
๋ฃํ๋๋ผ๋ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋ฉ์์ง๊ฐ ์ ์ก๋ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๋ค.
์คํ๋ผ์ธ ์์ ๋ณด๋ธ ๋ฉ์์ง๋ ๋ค๋ฅธ ๋ฉ์์ง๋ค ์ฒ๋ผ ์คํธ๋ฆผ์ ์์นํ๋ฉด์ ์๊ณ์์ด์ฝ์ด ํ์๋์ด ์ด ๋ฉ์์ง๊ฐ ์ ์ก๋ ๊ฒ์ด๋ผ๋ ๊ฒ์ ์๋ ค์ค๋ค. ๋ฉ์์ง๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์ ์ก๋๋ ์ฆ์ ์๊ณ ์์ด์ฝ์ ์ฒดํฌ ์์ด์ฝ์ผ๋ก ๋ฐ๋๋ค.
SyncManager
๋๊ธฐํ ์ด๋ฒคํธ์ ๊ด๋ จ๋ ๋ชจ๋ ์ํธ ์์ฉ์ SyncManager๋ฅผ ํตํด ์ด๋ฃจ์ด์ง๋ค.
SyncManager๋ ๋๊ธฐํ ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ๊ณ ํ์ฌ ๋ฑ๋ก๋ ๋๊ธฐํ ์์
์ ๊ฐ์ ธ์ค๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ์๋น์ค ์์ปค ์ธํฐํ์ด์ค์ด๋ค.
ํ์ฑํ๋ ์๋น์ค ์์ปค ๋ฑ๋ก ๊ฐ์ฒด๋ฅผ ํตํด SyncManager์ ์ ๊ทผํ ์ ์๋ค. ๋ฑ๋ก ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฐฉ๋ฒ์ ์๋น์ค ์์ปค์์ ๊ฐ์ ธ์ค๋์ง, ์๋๋ฉด ํ์ด์ง์์ ์ง์ ๊ฐ์ ธ์ค๋์ง์ ๋ฐ๋ผ ์กฐ๊ธ ๋ฌ๋ผ์ง๋ค.
์๋น์ค ์์ปค ๋ด์์๋ ๊ธ๋ก๋ฒ ๊ฐ์ฒด๋ฅผ ํตํด ์ฝ๊ฒ ์๋น์ค ์์ปค ๋ฑ๋ก ๊ฐ์ฒด์ ์ ๊ทผ ๊ฐ๋ฅํ๋ค.
self.registration
์๋น์ค ์์ปค๊ฐ ๊ด๋ฆฌํ๋ ํ์ด์ง์์๋ navigator.serviceWorker.ready๋ฅผ ํธ์ถํ์ฌ ํ์ฌ ํ์ฑํ๋ ์๋น์ค ์์ปค ๋ฑ๋ก ๊ฐ์ฒด์ ์ ๊ทผํ ์ ์๋ค. ํจ์๋ฅผ ํธ์ถํ๋ฉด ์ฑ๊ณต์ ์ผ๋ก ๋ฆฌ์กธ๋ธ ๋ ๋ ์๋น์ค ์์ปค ๋ฑ๋ก ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ ํ๋ก๋ฏธ์ค๊ฐ ๋ฐํ๋๋ค.
navigator.serviceWorker.ready.then(function(registration){});
const registration: ServiceWorkerRegistration = await navigator.serviceWorker.ready;
์ผ๋จ ์๋น์ค ์์ปค ๋ฑ๋ก ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์๋ค๋ฉด SyncManager๋ฅผ ํตํ ๋๋จธ์ง ์ํธ์์ฉ์ ์๋น์ค ์์ปค์์ ํ๋ ํ์ด์ง์์ ํ๋ ์๊ด์์ด ๋์ผํ๋ค.
์๋น์ค ์์ปค์์ 'send-messages' ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ๊ธฐ ์ํด์๋ ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ฅผ ์ ๋ ฅํ๋ค.
self.registration.sync.register('send-messages')
์๋น์ค ์์ปค๊ฐ ์ ์ดํ๋ ํ์ด์ง์ ๊ฐ์ ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ฅผ ์ฌ์ฉํ๋ค.
navigator.serviceWorker.ready.then(function(registration){
registration.sync.register('send-messages')
})
SyncManager๋ ๊ฐ๋จํ ๋๊ธฐํ ์ด๋ฒคํธ ํ๊ทธ ๋ชฉ๋ก์ ์ ์งํ๋ค.
์ด ๋ชฉ๋ก์๋ ๊ฐ๊ฐ์ ์ด๋ฒคํธ๊ฐ ์ด๋ค ์ด๋ฒคํธ์ธ์ง, ๋ฌด์์ ํ๋์ง์ ๋ํ ๋ก์ง์ ํฌํจ๋์ด ์์ง์๋ค. ๊ตฌํ์ ์ ์ ์ผ๋ก ์๋น์ค ์์ปค์ sync ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ฝ๋์ ๋ฌ๋ ค์๋ค.
SyncManager๊ฐ ์๊ณ ์๋ ๊ฒ์ ์ด๋ค ์ด๋ฒคํธ๊ฐ ๋ฑ๋ก๋์๋์ง, ์ธ์ ํธ์ถ๋์๋์ง, ์ด๋ป๊ฒ ๋๊ธฐํ ์ด๋ฒคํธ๋ฅผ ์ ๋ฌํ ์ง์ ๋ํ ๊ฒ์ด๋ค.
SyncManager๋ ๋ค์๊ณผ ๊ฐ์ ๊ฒฝ์ฐ์ sync์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํจ๋ค.
- ๋๊ธฐํ ์ด๋ฒคํธ ๋ฑ๋ก ์งํ
- ์ฌ์ฉ์ ์ํ๊ฐ ์คํ๋ผ์ธ์์ ์จ๋ผ์ธ์ผ๋ก ๋ณ๊ฒฝ๋ ๋
- ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋์ง ์์ ๋๊ธฐํ ์ด๋ฒคํธ๊ฐ ์์ ๊ฒฝ์ฐ, ๋งค ๋ถ๋ง๋ค
์๋น์ค ์์ปค๋ ๋ฐ์ก๋ ๋๊ธฐํ ์ด๋ฒคํธ๋ฅผ ํ๋ก๋ฏธ์ค ํ์์ผ๋ก ์์ ํ๊ณ ์ฒ๋ฆฌํ ์ ์๋ค. ํ๋ก๋ฏธ์ค๊ฐ ๋ฆฌ์กธ๋ธ ๋๋ฉด SyncManager์์ ํด๋น ๋๊ธฐํ ์ด๋ฒคํธ๊ฐ ์ญ์ ๋๋ค. ํ๋ก๋ฏธ์ค๊ฐ ๋ฆฌ์ ๋๋ฉด ๋ค์ ๋ฒ ๋๊ธฐํ ์์ ์ ๋ค์ ์๋๋ ์ ์๋๋ก SyncManager์ ๋จ๊ฒ ๋๋ค.
์ด๋ฒคํธ ํ๊ทธ๋ ์ ์ผํด์ผํ๋ค. SyncManager์ ์ด๋ฏธ ์กด์ฌํ๋ ํ๊ทธ๋ช ์ผ๋ก sync ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ๋ฉด SyncManager๋ ์ด๋ฅผ ๋ฌด์ํ๊ณ ์ค๋ณต์ผ๋ก ์ถ๊ฐํ์ง ์๋๋ค. ์ฒ์์๋ ์ด๊ฒ์ด ์ ์ฝ์ฒ๋ผ ๋๊ปด์ง ์ ์์ง๋ง ์ฌ์ค SyncManager์ ๊ฐ์ฅ ์ ์ฉํ ํน์ง ์ค ํ๋์ด๋ค. ์ด ํน์ง์ ๋ง์ ์์ ๋น์ทํ ์์ ์ ํ๋์ ์ด๋ฒคํธ๋ก ๊ทธ๋ฃนํํ์ฌ ์ฒ๋ฆฌํ ์ ์๋๋ก ํ๋ค.
SyncManager์ getTags()
๋ฉ์๋๋ฅผ ํ์ฉํ๋ฉด ์คํ ์์ ์ธ ์ ์ฒด ๋๊ธฐํ ์ด๋ฒคํธ ๋ชฉ๋ก์ ๋ฐ์์ฌ ์ ์๋ค.
ํ์ฌ ๋ฑ๋ก๋ ๋ชจ๋ ์ด๋ฒคํธ ๋ชฉ๋ก์ ์ถ๋ ฅํ๋ ค๋ฉด ์๋์ ๊ฐ์ดํ๋ค.
์๋น์ค ์์ปค ์ธํฐํ์ด์ค์ ๋ง์ฐฌ๊ฐ์ง๋ก getTags()
๋ ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํํ๋ค. ํ๋ก๋ฏธ์ค๊ฐ ๋ฆฌ์กธ๋ธ๋๋ฉด ๋๊ธฐํ ์ด๋ฒคํธ ํ๊ทธ ์ด๋ฆ์ด ์ฑ์์ง ๋ฐฐ์ด์ ๋ฐ์ ์ ์๋ค.
์๋ ์ฝ๋๋ ์ด๋ฒคํธ๊ฐ ๋ฑ๋ก๋๋ฉด ํ์ฌ ๋ฑ๋ก๋ ๋ชจ๋ ์ด๋ฒคํธ ๋ชฉ๋ก์ด ์ฝ์์ ์ถ๋ ฅ๋๋ค.
self.registration
.sync()
.register("hello-sync")
.then(function () {
return self.registration.sync.getTags()
})
.then(function(tags) {console.log(tags)})
์๋น์ค ์์ปค๊ฐ ์ ์ดํ๋ ํ์ด์ง์์๋ ready๋ฅผ ์ฌ์ฉํด ๋ฑ๋ก ๊ฐ์ฒด๋ฅผ ๋จผ์ ๋ฐ์์ค๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํด ๋น์ทํ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์๋ค.
navigator.serviceWorker.ready.then((registration) => {
registration.sync
.register('hello-sync')
.then(() => {
return registration.sync.getTags();
})
.then((tags) => {
console.log(tags);
});
});
์๋น์ค ์์ปค๊ฐ ์ ์ดํ๋ ํ์ด์ง์์ ์ด ์ฝ๋๋ฅผ ์คํํ๋ฉด ["hello-sync"]๊ฐ ์ฝ์์ ์ถ๋ ฅ๋์ด์ผ ํ๋ค.
์๋ฅผ ๋ค์ด ์ด๋ฉ์ผ ์๋น์ค๋ฅผ ๊ตฌํํ๋ค๊ณ ๊ฐ์ ํด๋ณด์.
์ฌ์ฉ์๊ฐ ์ด๋ฉ์ผ์ ๋ณด๋ผ๋๋ง๋ค IndexdDB์ ๋ณด๋ธ ํธ์งํจ์ ์ด๋ฉ์ผ์ ์ ์ฅํ๊ณ , send-unsent-messages
๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ๋๋ก ๊ตฌํํ ์ ์๋ค.
์ด์ ๋์๋๋ ์๋น์ค์์ปค์ชฝ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ IndexedDB์ ๋ณด๋ธ ํธ์งํจ์ ๋ชจ๋ ์ด๋ฉ์ผ์ ์ํํ๋ฉฐ ์ด๋ฉ์ผ ์ ์ก์ ์๋ํ๊ณ , ์ฑ๊ณต์ ์ผ๋ก ๋ฐ์กํ์ง ๋ชปํ ์ด๋ฉ์ผ์ด ํ๋๋ผ๋ ์์ผ๋ฉด, ์ ์ฒด sync ์ด๋ฒคํธ๊ฐ ๋ฆฌ์ ๋ ๊ฒ์ด๋ค. ์ดํ SyncManager๋ ์ฌ์ฉ์์ ๋คํธ์ํฌ ํ๊ฒฝ์ด ๋ฐ๋๊ฑฐ๋ ์ผ์ ์๊ฐ์ด ์ง๋๊ฒฝ์ฐ, ๋ค์ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํค๊ณ , ๋ค์ ํ๋ฒ IndexedDB์ ๋ณด๋ธ ํธ์งํจ์ ๋๋ฉฐ, ์์ ์ ์ก๋์ง ์์๋ ์ด๋ฉ์ผ๊ณผ ๊ทธ ์ดํ์ ์๋ก ์์ฑํ ์ด๋ฉ์ผ์ ๋ค์ ๋ฐ์กํ๊ณ ๋ณด๋ธ ํธ์งํจ์ ๋น์ด๋ค.
์ด๋ ๊ฒ ๊ตฌ์ฑํ๋ฉด ๋ฉ์ผ์ด ๋ณด๋ธ ํธ์งํจ์ ์๋์ง ์๋์ง ์ฒดํฌํ ํ์๊ฐ ์๋ค. ๋ณด๋ธ ํธ์งํจ์ ์ ์ก๋์ง ์์ ์ด๋ฉ์ผ์ด ์๋ ํ, ๋๊ธฐํ ์ด๋ฒคํธ๋ ๋ฑ๋ก๋ ์ํ๋ฅผ ์ ์งํ๋ฉฐ, ์ฃผ๊ธฐ์ ์ผ๋ก ํด๋น ์ด๋ฉ์ผ์ ์ ์กํ๋ ค๊ณ ์๋ํ ๊ฒ์ด๋ค.
์ฌ์ฉ์๊ฐ ์ ๋ฉ์ผ์ ์์ฑํ ๊ฒฝ์ฐ์๋ ๊ฐ์ ํ๊ทธ๋ช
์ ๊ฐ๋ ๋๊ธฐํ ์ด๋ฒคํธ๋ ์ค๋ณตํด์ ๋ฑ๋ก๋์ง ์๊ธฐ ๋๋ฌธ์ send-unsent-messages
๊ฐ ์ด๋ฏธ ์๋์ง ์๋๋ฉด ์คํ์ค์ธ์ง ํ์ธํ ํ์๋ ์๋ค.
๊ฐ๋์ฉ SyncManager๊ฐ ํน์ sync ์ด๋ฒคํธ๊ฐ ๊ณ์ ์คํจํ๋ค๊ณ ํ๋จํ๊ณ ์์ ๋ญ๋น๋ฅผ ๋ง๊ธฐ ์ํด ์ด๋ฒคํธ๋ฅผ ์ ๊ฑฐํ๊ธฐ ์ ๋ง์ง๋ง์ผ๋ก ํ๋ฒ ๋ sync ์ด๋ฒคํธ๋ฅผ ๋ณด๋ด๊ธฐ๋ก ๊ฒฐ์ ํ ์๋ ์๋ค. ์ด๋ฐ ๊ฒฝ์ฐ ์ ๋ฌ๋ Sync ์ด๋ฒคํธ์ lastChance ์์ฑ์ ํ์ธํด ํด๋น ์ด๋ฒคํธ๊ฐ SyncManager๊ฐ ๋ง์ง๋ง์ผ๋ก ๋ณด๋ธ ์ด๋ฒคํธ๋ผ๋ ๊ฒ์ ๊ฐ์งํ ์ ์๊ณ ์ด์๋ฐ๋ผ ํ์ํ ์์ ์ ์ํํ ์ ์๋ค.
self.addEventListener("sync", event=> {
if (event.tag === "add-reservation") {
event.waitUntil(
addReservation()
.then(() => {
return Promise.resolve()
})
.catch((error) => {
if (event.lastChance) {
return removeReservation()
} else {
return Promise.reject()
}
})
)
}
})
์์ ์ ์ํํ๋ ์ฝ๋๋ฅผ ํ์ด์ง์์ ์๋น์ค์์ปค๋ก ์ฎ๊ธฐ๋ฉด, ๋ฌด์จ ์ผ์ด ์์ด๋ ์์ ์ด ์ํ๋๋๋ก ๋ง๋ค ์ ์๋ค. ํ์ง๋ง ์ด๋ก์ธํด ์๋ก์ด ๋ณต์ก์ฑ์ด ์๊ธฐ๊ฒ ๋๋ค.
๋๋ถ๋ถ์ ๊ฒฝ์ฐ ํ์ด์ง์์ ์ํ๋๋ ์์
์ ์๋ฃํ๊ธฐ ์ํด์๋ ๋ฐ์ดํฐ๊ฐ ํ์ํ๋ค. ๋ฉ์์ง๋ฅผ ์ ์กํ๋ ํจ์๋ฅผ ํธ์ถํ๋ ํ์ด์ง๋ ๋ฉ์์ง ํ
์คํธ๊ฐ ํ์ํ๋ค.
ํฌ์คํ
์ ์ข์์๋ฅผ ๋๋ฅด๋ ํจ์๋ ํฌ์คํ
์ ID๊ฐ ํ์ํ๋ค. ํ์ง๋ง ๋๊ธฐํ ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ ๋๋ ์ด๋ฒคํธ ์ด๋ฆ๋ง ์ ๋ฌํ ์ ์๋ค. ๋ค์๋งํด ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋ฉ์์ง๋ฅผ ์ ์กํ๋๋ก ์๋น์ค ์์ปค์ ์์ฒญํ ์๋ ์์ง๋ง ๋ฉ์์ง ํ
์คํธ๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ ํจ์์ ์ธ์๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ฒ๋ผ ๊ฐ๋จํ์ง ์๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ค์ํ ๋ฐฉ๋ฒ์ด ์กด์ฌํ๋ค.
IndexedDB์ ์ก์ ํ ๋ง๋ค๊ธฐ
๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ ์์ ์ด ์์๋๊ธฐ ์ ์ ์ฌ์ฉ์๊ฐ ์์ ํ๊ณ ์๋ ๋ด์ฉ์ IndexedDB์ ์ ์ฅํ๋ ๋ฐฉ๋ฒ์ด๋ค. ๊ทธ ํ ์๋น์ค ์์ปค์ ๋๊ธฐํ ์ด๋ฒคํธ ์ฝ๋๋ ๊ฐ์ฒด ์ ์ฅ์๋ฅผ ์ํํ๋ฉฐ ์ ์ฅ๋ ๋ด์ฉ์ ๊ธฐ๋ฐ์ผ๋ก ํ์ํ ์์ ์ ์ํํ๋ค.
๋ฉ์์ง ์ฑ์ผ๋ก ๋์๊ฐ ์ด ๋ฐฉ๋ฒ์ ์ ์ฉํด๋ณด๋ฉด, ๋ชจ๋ ์ ๊ท ๋ฉ์์ง๋ฅผ message-queue ๊ฐ์ฒด ์ ์ฅ์์ ์ถ๊ฐํํ ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํ send-messages ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ๋ค.
์ด ์ด๋ฒคํธ๋ message-queue์ ๋ชจ๋ ๋ฉ์์ง๋ฅผ ์ํํ์ฌ ๊ฐ ๋ฉ์์ง๋ฅผ ๋คํธ์ํฌ๋ก ์ ์กํ๊ณ message-queue์์ ์ญ์ ํ๋ค. ๋ชจ๋ ๋ฉ์์ง๊ฐ ์ ์ก๋๊ณ ๊ฐ์ฒด ์ ์ฅ์๊ฐ ๋น์์ง ํ์ sync ์ด๋ฒคํธ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ๋ฆฌ์กธ๋ธ๋๋ค. ๋ฉ์์ง๊ฐ ํ๋๋ผ๋ ์ ์ก์ ์คํจํ๋ฉด, ๋ฆฌ์ ๋ ํ๋ก๋ฏธ์ค๊ฐ ์ด๋ฒคํธ๋ก ๋ฐํ๋๊ณ SyncManager๋ ์ฐจํ์ ๋ค์ ๋๊ธฐํ ์ด๋ฒคํธ๋ฅผ ์์ํ๋ค.
ํ์ํ ํ๋ง๋ค(์: ๋ฐ์ ๋ฉ์์ง์ฉ ํ, ํฌ์คํ ์ข์์ ํ) ๋ณ๊ฐ์ ๊ฐ์ฒด ์ ์ฅ์๋ฅผ ์ ์งํ์ฌ, ๊ฐ๊ฐ์ ์ฒ๋ฆฌํ๋ ๋ณ๋์ ๋๊ธฐํ ์ด๋ฒคํธ๋ฅผ ๋ง๋ค ์ ์๋ค.
์ด ๋ฐฉ๋ฒ์ ์ฌ์ฉํด ์ฝ๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๊ต์ฒดํ ์ ์๋ค.
const sendMessage = function(subject, message) {
fetch('/new-message', {
method: 'post',
body: JSON.stringify({
subject,
message
})
})
}
์ฌ๊ธฐ์ ์ด๋ ๊ฒ ์ถ๊ฐํด๋ณด์.
const triggerMessageQueueUpdate = function() {
navigator.serviceWorker.ready.then(function(registration) {
registration.sync.register('message-queue-sync')
})
}
const sendMessage = function(subject, message) {
addToObjectStore("message-queue", {
subj: subject,
msg: message
})
}
๋ค์์ ์๋น์ค์์ปค์ ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ฅผ ์ถ๊ฐํ๋ค.
self.addEventListener("sync", (event) => {
if (event.tag === 'message-queue-sync') {
event.waitUntil(
() => {
return getAllMessages().then((messages) => {
return Promise.all(
messages.map((message) => {
return fetch("/new-message", {
method: 'post',
body: JSON.stringify({
subj:subject,
msg: message
})
}).then(() => {
return deleteMessageFromQueue(message)
})
})
)
})
}
)
}
})
getAllMessages()๋ฅผ ์ฌ์ฉํด IndexedDB์ ์์ฌ ์๋ ๋ชจ๋ ๋ฉ์์ง๋ฅผ ๊ฐ์ ธ์จ๋ค. ์ดํ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ๋ด๋ถ์์ ์ฌ์ฉํ๋ ๋ชจ๋ ํ๋ก๋ฏธ์ค๊ฐ ๋ฆฌ์กธ๋ธ๋ ๊ฒฝ์ฐ์๋ง ๋ฆฌ์กธ๋ธ๋๋ ํ๋ก๋ฏธ์ค๋ฅผ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ก ๋ฐํํ๋ค.
์ด ํ๋ก๋ฏธ์ค๋ Promise.all ํจ์์ ํ๋ก๋ฏธ์ค ๋ฐฐ์ด์ ๋๊ฒจ ํธ์ถํ๋ ๋ฐฉ์์ผ๋ก ๋ง๋ค์ด์ง๋ค. ํ๋ก๋ฏธ์ค ๋ฐฐ์ด์ IndexedDB์์ ๊ฐ์ ธ์จ ๋ฉ์์ง ๋ฐฐ์ด์ ๋ํด map()์ ์คํํด, ๊ฐ ๋ฉ์์ง์ ๋ํด ๊ฐ๊ฐ์ ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํํ๋ ๋ฐฉ๋ฒ์ผ๋ก ์์ฑ๋๋ค. ์ด๋ค ๊ฐ๊ฐ์ ํ๋ก๋ฏธ์ค๋ ๋ฉ์์ง๊ฐ ์ฑ๊ณต์ ์ผ๋ก ๋ฐ์ก๋์ด ํ์์ ์ญ์ ๋์์ ๋๋ง ๋ฆฌ์กธ๋ธ๋๋ค.
์ด ๋ฐฉ๋ฒ์ ์กฐ๊ธ ๋ค๋ฅด๊ฒ ์๋ํด ๋ณผ ์๋ ์๋ค. ๋๊ธฐํ ์์ ์ ํ์ํ ๊ฐ์ฒด์ ์ฑ๊ณต์ ์ผ๋ก ๋๊ธฐํ๊ฐ๋ ๊ฐ์ฒด๋ฅผ ํจ๊ป ๋์ผํ ๊ฐ์ฒด์ ์ฅ์์ ์ ์ฅํ๋ ๋ฐฉ๋ฒ์ด๋ค.
์ด ๊ฒฝ์ฐ์๋ ๊ฐ ๊ฐ์ฒด์ ์ํ๋ฅผ ์ ์ฅํด๋์๋ค๊ฐ, ๊ฐ์ฒด๊ฐ ์ฑ๊ณต์ ์ผ๋ก ๋๊ธฐํ๋๋ฉด ์ด๋ฅผ ์ ๋ฐ์ดํธ ํ ์ ์๋ค. ์๋ฅผ ๋ค์ด ์ฑ์์ ๋ฐ์ก๋ ๋ฉ์์ง์ ๋ฏธ๋ฐ์ก๋ ๋ฉ์์ง๋ฅผ ๊ฐ์ ์ ์ฅ์์ ์ ์ฅํด๋๋ค. ๋ฉ์์ง ๊ฐ์ฒด์๋ ๋ฉ์์ง ์ฝํ ์ธ ๋ฟ๋ง ์๋๋ผ sent์ pending ๊ฐ์ ํ์ฌ ์ํ๋ ํฌํจ๋๋ค. ๊ทธํ ๋๊ธฐํ ์์ ์ pending ์ํ์ ๋ชจ๋ ๋ฉ์์ง๋ฅผ ์ํํ๊ธฐ ์ํด ์ปค์๋ฅผ ์คํํ๊ณ , ์ ์กํ๊ณ , ์ ์ก ํ์ ์ํ๋ฅผ sent๋ก ๋ณ๊ฒฝํ๋ค.
IndexedDB์ ์์ฒญ ํ ๋ง๋ค๊ธฐ
์ด๋ฏธ ์์ฑ๋ ํ๋ก์ ํธ๋ฅผ ์์ ํด์ผํ๋ ๊ฒฝ์ฐ, ๊ฐ์ฒด๋ฅผ ๋ก์ปฌ์ ์ ์ฅํ๋๋ก ์ฑ์ ๊ตฌ์กฐ๋ฅผ ๋ฐ๊พธ๊ณ ๊ฐ์ฒด ์ํ๋ฅผ ์ถ์ ํ๊ธฐ ์ํ ๋ก์ง์ ๊ตฌํํ๋ ๊ฒ์ ๋๋ฌด ๊ณผํ ์ผ์ด ๋ ์ ์๋ค. ๊ธฐ์กด ํ๋ก์ ํธ์ ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ๋ฅผ ์ ์ฉํ ๋ ์ข๋ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ๊ธฐ์กด Ajax ํธ์ถ์ ์์ฒญ ํ๋ก ๋ฐ๊พธ๋ ๊ฒ์ด๋ค.
์ด ๋ฐฉ์์ ์ ์ฉํ๋ฉด ๊ฐ ๋คํธ์ํฌ ์์ฒญ์, IndexedDB์ ์ธ๋ถ ์์ฒญ์ฌํญ์ ์ ์ฅํ๋ ๋ฉ์๋๋ก ๊ต์ฒดํ๊ณ , ๋๊ธฐํ ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ๋ค. ๋ฑ๋ก๋ ๋๊ธฐํ ์ด๋ฒคํธ๋ ๊ฐ์ฒด ์ ์ฅ์์ ์ ์ฅ๋ ๋ชจ๋ ์์ฒญ์ ์ดํผ๊ณ ํ๋ฒ์ ํ๋์ฉ ๊ฐ ์์ฒญ์ ๋ณด๋ธ๋ค.
์ด์ ๋ฐฉ๋ฒ๊ณผ ๋ฌ๋ฆฌ, ๋๊ธฐํ ์ด๋ฒคํธ์์ ๊ฐ ๋คํธ์ํฌ ์์ฒญ์ ์ํํ๋ ํ์ํ ๋ชจ๋ ์ธ๋ถ์ฌํญ์ IndexedDB์ ์ ์ฅํ๋ค. ๋๊ธฐํ ์ฝ๋๋ ๊ฐ๊ฐ์ ์์ ์ด ์ฌ์ดํธ์์ ๋ฌด์จ ์๋ฏธ์ธ์ง ์ดํดํ ํ์๊ฐ ์๋ค. ๊ทธ์ ์์ฒญ ๋ชฉ๋ก์ ๋งน๋ชฉ์ ์ผ๋ก ํ์ํ๋ฉฐ, ํ๋์ฉ ์คํํ๊ธฐ๋ง ํ๋ฉด๋๋ค.
const sendMessage = function(subject, message) {
fetch('/new-message', {
method: 'POST',
body: JSON.stringify({
subject,
message
})
})
}
const likePost = function(postId) {
fetch('/like-post?id=' + postId)
}
์ด๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๋ฅผ ๋ฐ๊ฟ ์ ์๋ค.
const triggerRequestQueueSync = () => {
navigator.serviceWorker.ready.then((registration) => {
registration.sync.register('request-queue');
});
};
const sendMessage = (subject, message) => {
addToObjectStore("request-queue", {
url: "/new-message",
method: "POST",
body: JSON.stringify({
subject,
message
})
})
triggerRequestQueueSync();
}
const likePost = (postId) => {
addToObjectStore("request-queue", {
url: "/like-post?id=" + postId,
method: "GET"
})
triggerRequestQueueSync();
}
๋คํธ์ํฌ ์์ฒญ ์ฝ๋๋ฅผ request-queue๋ผ๋ ๊ฐ์ฒด ์ ์ฅ์์ ๊ฐ๋ณ ์์ฒญ์ ๋ํ๋ด๋ ๊ฐ์ฒด๋ฅผ ์ ์ฅํ๋ ์ฝ๋๋ก ๊ต์ฒดํ๋ค. ์ ์ฅ๋๋ ๊ฐ๊ฐ์ ๊ฐ์ฒด๋ ๋คํธ์ํฌ ์์ฒญ์ ํ์ํ ๋ชจ๋ ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ค. ๊ทธ ๋ค์ ์๋น์ค ์์ปค์ sync ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํ์ฌ request-queue ์์ ๋ชจ๋ ์์ฒญ์ ๊ฒํ ํ๊ณ , ๊ฐ๊ฐ์ ๋ํ ๋คํธ์ํฌ ์์ฒญ์ ๋ง๋ค๊ณ ์์ฒญ์ด ์ฑ๊ณตํ๋ฉด, ๊ฐ์ฒด ์ ์ฅ์์์ ํด๋น ์์ฒญ์ ์ญ์ ํ๋ค.
self.addEventListener('sync', (event) => {
if (event.tag === 'request-queue') {
event.waitUntil(
() => {
return getAllObjectsFrom("request-queue").then((requests) => {
return Promise.all(
requests.map((request) => {
return fetch(request.url, {
method: request.method,
body: request.body
}).then(() => {
return deleteRequestFromQueue(message) // returns a promise
})
})
)
})
}
)
}
})
์ฑ๊ณตํ ์์ฒญ์ deleteRequestFromQueue() ๋ฉ์๋ ํธ์ถ์ ํตํด IndexedDB ํ์์ ์ญ์ ๋๋ค. ์คํจํ ์์ฒญ์ ํ์ ๋จ๊ณ , ๋ฆฌ์ ๋ ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํํ๋ค. ๋คํธ์ํฌ ์์ฒญ ์ค ํ๋๋ผ๋ ๋ฆฌ์ ๋ ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํํ๋ค๋ฉด, ์ ์ํ sync ์ด๋ฒคํธ๊ฐ ๋ค์ ๋ฐ์ํ๋ค. ์์ ์ฑ๊ณต์ ์ผ๋ก ํธ์ถ๋ ํ์์ ์ญ์ ๋ ๋คํธ์ํฌ ์์ฒญ์ ์ ์ธํ ๋๋จธ์ง ์์ฒญ๋ค์ ๋ค์ ๊ฒํ ํ์ฌ ๋คํธ์ํฌ ์์ฒญ์ ์๋ํ๋ค.
๋๊ธฐํ ์ด๋ฒคํธ ํ๊ทธ๋ฅผ ํตํด ๋ฐ์ดํฐ ์ ๋ฌํ๊ธฐ
๋๊ธฐํ ํจ์์ ๊ฐ๋จํ ๊ฐ์ ์ ๋ฌํด์ผํ ๋, ๋ชจ๋ ์์ ์ ์ผ์ผ์ด ์ถ์ ํ๊ธฐ ์ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๊ตฌํํ๋ ๊ฒ์ ๋๋ฌด ๊ณผํ๊ฒ ๋๊ปด์ง ์ ์๋ค.
์ฌ์ฉ์๊ฐ ํ์ด์ง์ ํ์๋ ํน์ ํฌ์คํธ์ ์ข์์๋ฅผ ๋๋ฅผ ์ ์๋ค๊ณ ๊ฐ์ ํด๋ณด์. ์ด๋ ํฌ์คํ ์ ID๋ฅผ ํน์ URL๋ก ์ ๋ฌํ๋ ๊ฐ๋จํ ์์ ์ด๋ค. ๊ธฐ์กด ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
const likePost = function(postId) {
fetch("/like-post?id=" + postId)
}
์ด๋ฅผ ๋ฐ๊ฟ๋ณด์.
const likePost = function(postId) {
navigator.serviceWorker.ready.then((registration) => {
registration.sync.register('like-post?id=' + postId)
})
}
๊ทธ๋ฆฌ๊ณ ID๋ฅผ ์ถ์ถํ๋ ๋ฐฉ๋ฒ์ผ๋ก ๊ตฌํํ ์ ์๋ค.
self.addEventListener('sync', function(event) {
if (event.tag.startsWith("like-post-")) {
event.waitUntil(function() {
const postId = event.tag.slice(10);
return fetch("/like-post?id=" + postId)
})
}
})
react-query์์์ ๊ณ ๋ฏผ
react-query์๋ ๋์ค์ ์ฌ์ฉํ ์ ์๋๋ก queryClient ๋ฐ ํด๋น ์บ์์ ์ํ๋ฅผ ์ ์งํ๋ ๊ธฐ๋ฅ์ด์๋ค.
react-query์ background sync๊ธฐ๋ฅ์ ๊ณต์กด ์ํค๊ณ ์ถ์๋ค.
react-query๋ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ด ์๋ ๊ฒฝ์ฐ query
์ mutation
์ด ์ด๋ป๊ฒ ์๋ํด์ผ ํ๋์ง ๊ตฌ๋ถํ๊ธฐ ์ํด ์ธ๊ฐ์ง ๋ค๋ฅธ ๋คํธ์ํฌ ๋ชจ๋๋ฅผ ์ ๊ณตํ๋ค.
์ด ๋ชจ๋๋ค์ query
์ mutation
์ ๋ํด ๊ฐ๋ณ์ ์ผ๋ก ๋๋ ๊ธฐ๋ณธ๊ฐ์ ์ ์ญ์ ์ผ๋ก ์ค์ ํ ์ ์๋ค.
Since React Query is most often used for data fetching in combination with data fetching libraries, the default network mode is online.
Network Mode: online
์ด ๋ชจ๋์์๋ ์ฌ๋ฌ๋ถ์ด ์จ๋ผ์ธ ์ํ๊ฐ ์๋๋ผ๋ฉด query
์ mutation
์ด ์คํ๋์ง ์๋๋ค.
๋คํธ์ํฌ ์ฐ๊ฒฐ์ด ์์ด query
๋ฅผ ์ํํ ๊ฒฝ์ฐ ํญ์ ์ํ(loading
, error
, success
)๋ฅผ ์ ์งํ๋ค. ๊ทธ๋ฌ๋ fetchStatus
๊ฐ ์ถ๊ฐ๋ก ๋
ธ์ถ๋๋ค.
fetchStatus
๋ ์๋์ ๊ฐ๋ค.
fetching
: ThequeryFn
is really executing - a request is in-flight.paused
: The query is not executing - it ispaused
until you have connection againidle
: The query is not fetching and not paused
isFetching
๋ฐ isPaused
ํ๋๊ทธ๋ ์ด ์ํ์์ ํ์๋๋ฉฐ ํธ์๋ฅผ ์ํด ๋
ธ์ถ๋๋ค.
๋ง์ฝ query
๊ฐ ์คํ์ค์ offline ์ํ๊ฐ ๋๋ค๋ฉด react-query๋ retry mechanism์ ์ผ์ ์ค์งํ๋ค. ์ผ์ ์ค์ง๋ query
๋ ๋คํธ์ํฌ์ ๋ค์ ์ฐ๊ฒฐ๋๋ฉด ๊ณ์ ์คํ๋๋ค.
์ด๊ฒ์ refetchOnReconnect
์๋ ๋ฌด๊ดํ๋ค. ์คํ๋ ค ์ด๊ฒ์ refetch
๊ฐ ์๋๊ณ continue
์ ๊ฐ๊น๋ค.
๋ง์ฝ query
๊ฐ ์ทจ์๋๋ค๋ฉด continue
๋์ง๋ ์๋๋ค.
Network Mode: always
In this mode, React Query will always fetch and ignore the online / offline state. This is likely the mode you want to choose if you use React Query in an environment where you don't need an active network connection for your Queries to work - e.g. if you just read from AsyncStorage, or if you just want to return Promise.resolve(5) from your queryFn.
- Queries will never be
paused
because you have no network connection. - retries will also not pause - you Query will go to
error
state if it fails. refetchOnReconnect
defaults tofalse
in this mode, because reconnecting to the network is not a good indicator anymore that stale queries should be refetched. You can still turn it on if you want.
Network Mode: offlineFirst
This mode is the middle ground between the first two options, where react-query will run the queryFn
once, but then pause retries.
This is very handy if you have a serviceWorker
that intercepts a request for caching like in an offline-first PWA, or if you use HTTP caching via the Cache-Control header.
In those situations, the first fetch might succeed because it comes from an offline storage / cache. However, if there is a cache miss, the network request will go out and fail, in which case this mode behaves like an online
query - pausing retries.
Devtools
The react-query-devtools will show queries in a paused
state if they would be fetching, but there is no network connection.
There is also a toggle button to Mock offline behavior. Please note that this button will not actually mess with your network connection, but it will set the OnlineManager in an offline state.
Signature
networkMode
:online
,always
,offlineFirst
- optional
- defaults to
online