์ฐ๋ฆฌ๋ ์ด๋ฏธ ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ SPA(๋จ์ผ ํ์ด์ง ์ ํ๋ฆฌ์ผ์ด์ ) ์ํคํ ์ฒ๊ฐ ๋๋ฆฌ ์ฑํ๋ ์ฃผ์ ์ด์ ์ค ํ๋๊ฐ ๋ฏธํ์ ์ธ ๊ณ ๋ ค์ฌํญ ๋๋ฌธ์ด๋ผ๋ ์ ์ ๊ฐ์กฐํด์์ต๋๋ค.
์ฐ๋ฆฌ์ ์ฑ Hypermedia Systems์์ ์ธ๊ธํ๋ฏ์ด, Web 1.0 ์คํ์ผ์ ์ฐ๋ฝ์ฒ ๊ด๋ฆฌ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค๋ฃฐ ๋, SPA ๋ฒ์ ๊ณผ ๊ธฐ๋ฅ์ ๋์ผํ๋๋ผ๋ ํด๋น ์ ํ๋ฆฌ์ผ์ด์ ์๋ ์ฌ๊ฐํ ๋ฏธํ์ ๋ฌธ์ ๊ฐ ์กด์ฌํฉ๋๋ค.
์ฌ์ฉ์ ๊ฒฝํ ๊ด์ ์์: ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ด์ง๋ฅผ ์ด๋ํ๊ฑฐ๋, ์ฐ๋ฝ์ฒ๋ฅผ ์์ฑ, ์ ๋ฐ์ดํธ ๋๋ ์ญ์ ํ ๋ ๋์ ๋๋ ์๋ก๊ณ ์นจ์ด ๋ฐ์ํฉ๋๋ค. ์ด๋ ๋ชจ๋ ์ฌ์ฉ์ ์ํธ์์ฉ(๋งํฌ ํด๋ฆญ ๋๋ ํผ ์ ์ถ)์ด ์ ์ฒด ํ์ด์ง ์๋ก๊ณ ์นจ์ ์๊ตฌํ๋ฉฐ, ๊ฐ ์์ ํ์๋ ์๋ก์ด HTML ๋ฌธ์๋ฅผ ์ฒ๋ฆฌํด์ผ ํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
โHypermedia Systems - Chapter 5
์ด๋ฌํ ํ์ด์ง ๊ฐ์ ๋์ ๋๋ โ์นด-์ฒ(ka-chunk)โ ํจ๊ณผ์ ์ข ์ข ๋ฐ์ํ๋ ๋ฏธ ์คํ์ผ๋ง๋ ์ฝํ ์ธ ์ ๊น๋นก์์ ์ค๋ซ๋์ ๋ฌธ์ ๋ก ์ง์ ๋์ด ์์ต๋๋ค. ํ๋ ๋ธ๋ผ์ฐ์ ๊ฐ ์ด ์ํฉ์ ๋ค์ ๊ฐ์ ํ์ง๋ง(๋ฌผ๋ก , ์์ฒญ์ด ์งํ ์ค์์ ๋ ๋ช ํํ๊ฒ ๋ง๋๋ ๋จ์ ๋ ์์ต๋๋ค), ํนํ ์ ์ ์๋ SPA์ ๋น๊ตํ์ ๋ ์ฌ์ ํ ๋ฌธ์ ๊ฐ ๋ฉ๋๋ค.
์ด์ฐฝ๊ธฐ ์น์์๋ ์ด๋ฌํ ๋ฌธ์ ๊ฐ ํฌ๊ฒ ๋ฌธ์ ๊ฐ ๋์ง ์์์ต๋๋ค. ๋น์ ์ฐ๋ฆฌ๋ ๋ธ๋ผ์ฐ์ ์ ๋๊ตฌ ๋ชจ์์์ ๊ณต๋ฃก ์ฃผ์๋ก ๋ณ์ด ๋ ์๋ค๋๋ ๊ฒ์ ๋ณด๊ฑฐ๋, ๋ถํ๋ ํ ์คํธ, ํ ์ด๋ธ ๊ธฐ๋ฐ ๋ ์ด์์, ์ถค์ถ๋ ์๊ธฐ ๋ฑ์ ๋ณด๋ฉด์ FTP ํด๋ผ์ด์ธํธ์ ๋น๊ตํ๊ณ ์์์ต๋๋ค.
๊ธฐ์ค์ ๋ฎ์๊ณ , ๊ทธ ์์ ์ ์ข์์ต๋๋ค.
ํ์ง๋ง ์น์ ์ด์ ์ด๋ฌํ ์ ์นํ ๊ฒ๋ค์ ๋ฒ์ด๋์ผ ํ๊ณ , ์ด์ ์ฐ๋ฆฌ๋ ์ฌ์ฉ์์๊ฒ ๋งค๋๋ฝ๊ณ ๋งค๋ ฅ์ ์ธ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ ๊ฒ์ผ๋ก ๊ธฐ๋๋ฉ๋๋ค. ์ด๋ ํ๋์ ๋ณด๊ธฐ ์ํ์์ ๋ค๋ฅธ ์ํ๋ก์ ๋ถ๋๋ฌ์ด ์ ํ์ ํฌํจํฉ๋๋ค.
๋ค์ ๋งํด, ๋ง์ ํ๋ค์ด SPA ์ ๊ทผ ๋ฐฉ์์ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉํ๋ ์ด์ ๋ โ์ ๋ฐฉ์โ์ด ๊ทธ๋ฅโฆ ํฌ๋ฐํด ๋ณด์ด๊ธฐ ๋๋ฌธ์ ๋๋ค.
์ด๊ธฐ ์น ์์ง๋์ด๋ค์ ์น ๊ฐ๋ฐ์๋ค์ด ์๋ก ๋ค๋ฅธ ๋ณด๊ธฐ ์ํ ๊ฐ์ ๋ถ๋๋ฌ์ด ์ ํ์ ์ ๊ณตํ๊ณ ์ถ์ดํ ๊ฒ์ด๋ผ๋ ์ ์ ๊นจ๋ฌ์๊ณ , ์ด๋ฅผ ์ํด ๋ค์ํ ๊ธฐ์ ์ ์ ๊ณตํ์ต๋๋ค. ๊ทธ์ค ์ฃผ์ ๊ธฐ์ ์ CSS ์ ํ์ผ๋ก, ์ํ์์ ๋ค๋ฅธ ์ํ๋ก ์ํ์ ์ ํ์ ์ง์ ํ ์ ์์ต๋๋ค.
์ํ๊น๊ฒ๋ HTML์์ CSS ์ ํ์ JavaScript๋ฅผ ์ฌ์ฉํด์ผ๋ง ๊ฐ๋ฅํ์ต๋๋ค. ์ฆ, ์ ํ์ ํธ๋ฆฌ๊ฑฐํ๋ ค๋ฉด ์์๋ฅผ ๋์ ์ผ๋ก ๋ณ๊ฒฝํด์ผ ํ๋ฉฐ, โ์์โ HTML๋ก๋ ์ด๋ฅผ ์ํํ ์ ์์ต๋๋ค. ์ค์ ๋ก ์ด๋ JavaScript๋ฅผ ์ฌ์ฉํ์ฌ SPA๋ฅผ ๊ตฌ์ถํ๋ ๊ฐ๋ฐ์๋ค๋ง์ด ์ด๋ฌํ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ ์ ์์์์ ์๋ฏธํ๋ฉฐ, ์ด๋ SPA์ ๋ฏธํ์ ์ฐ์์ฑ์ ๋์ฑ ๊ณต๊ณ ํ ํ์ต๋๋ค.
htmx๋ฅผ ํตํด, ์๋ง๋ ์์๊ฒ ์ง๋ง, CSS ์ ํ์ ์์ HTML์์ ์ฌ์ฉํ ์ ์๋๋ก ๋ง๋ค ์ ์์ผ๋ฉฐ, ์ด๋ ๋ค์ ๋ณต์กํ ๊ต์ฒด ๋ชจ๋ธ์ ํตํด ๊ตฌํ๋ฉ๋๋ค. ์ฌ๊ธฐ์ ์ฐ๋ฆฌ๋ ์ ์ฝํ ์ธ ์ ์ค๋๋ ์ฝํ ์ธ ์ ๋ชจ๋ ์๋ ์์๋ค์ ๊ฐ์ ธ์ โ์ ์ฐฉโ ์์ฑ์ ์ค์ ํฉ๋๋ค. ์ด ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ฉด ํ์ดํผ๋ฏธ๋์ด ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ๋ ์ ์ ์๋ SPA์ฒ๋ผ ๋งค๋๋ฝ๊ฒ ๋๊ปด์ง๊ฒ ํ ์ ์์ต๋๋ค.
ํ์ง๋ง ์๋ก์ด API๊ฐ ๋ฑ์ฅํ์ต๋๋ค: View Transition API.
View Transition API๋ CSS ์ ํ๋ณด๋ค ํจ์ฌ ๋ ์ผ์ฌ ์ฐฌ ๋ชฉํ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. ์ด API๋ ๋จ์ํ๋ฉด์๋ ์ง๊ด์ ์ธ API๋ฅผ ์ ๊ณตํ์ฌ, ์ ์ฒด DOM์ ํ๋์ ์ํ์์ ๋ค๋ฅธ ์ํ๋ก ์ ํํ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ฅผ ํตํด ์ผ๋ฐ ์ฌ์ฉ์๋ค๋ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋์ฑ์ด ์ด API๋ JavaScript๋ฟ๋ง ์๋๋ผ HTML์์๋ ์ฌ์ฉํ ์ ์์ด, Web 1.0 ์ ๊ทผ ๋ฐฉ์์ ์ฌ์ฉํด๋ ํจ์ฌ ๋ ๋ฉ์ง ์ฌ์ฉ์ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌ์ถํ ์ ์๊ฒ ๋ฉ๋๋ค.
์ด ๊ธฐ๋ฅ์ด ๊ฐ๋ฅํด์ง๋ฉด โHypermedia Systemsโ์์ ๋ค๋ฃจ๋ ์ฐ๋ฝ์ฒ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค์ ์ดํด๋ณด๋ ๊ฒ๋ ์ฌ๋ฏธ์์ ๊ฒ์ ๋๋ค!
ํ์ง๋ง ์ด ๊ธ์ ์ฐ๋ ์์ ์์, ์ด API๋ CSS ์ ํ์ฒ๋ผ JavaScript์์๋ง ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, Chrome 111+์์๋ง ๋ง ์ถ์๋์์ต๋๋ค.
JavaScript์์ ์ด API๋ ๋งค์ฐ ๊ฐ๋จํฉ๋๋ค:
// ์ด๊ฒ๋ง์ผ๋ก๋ ์ํ ๊ฐ์ ๋งค๋๋ฌ์ด ์ ํ์ด ๊ฐ๋ฅํฉ๋๋ค!
document.startViewTransition(() => updateTheDOMSomehow(data));
์ด์ , ์ด๊ฑด ๋ด๊ฐ ์ข์ํ๋ API์ ๋๋ค.
์ด ์ข๊ฒ๋, ์ด API๋ฅผ ์ผ๋ฐ htmx ๊ต์ฒด ๋ชจ๋ธ ์ฃผ์์ ์ฝ๊ฒ ๊ฐ์ ์ ์์ด์, HTML์์ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๊ธฐ ์ ์ htmx์์ View Transition์ ํ์ํ ์ ์์ต๋๋ค!
๊ทธ๋ฆฌ๊ณ htmx 1.9.0๋ถํฐ๋ hx-swap
์์ฑ์ transition:true
์์ฑ์ ์ถ๊ฐํ์ฌ ์ด API๋ฅผ ์คํํด๋ณผ ์ ์์ต๋๋ค.
์ด์ ์ด ์๋ก์ด ๋ฉ์ง ์ฅ๋๊ฐ์ htmx์ ํจ๊ป ์ฌ์ฉํ ๊ฐ๋จํ ์์ ๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
์ด ์์ ์๋ ๋ ๊ฐ์ง ๋จ๊ณ๊ฐ ํ์ํฉ๋๋ค:
๋จผ์ ํด์ผ ํ ์ผ์ ์ฐ๋ฆฌ๊ฐ ์ํ๋ View Transition ์ ๋๋ฉ์ด์ ์ ์ ์ํ๋ ๊ฒ์ ๋๋ค.
@keyframes
๋ฅผ ์ฌ์ฉํฉ๋๋ค.:view-transition-old()
๋ฐ :view-transition-new()
์์ฌ ์ ํ์๋ฅผ ์ฌ์ฉํ์ฌ slide-it
์ด๋ผ๋ ์ด๋ฆ์ View Transition์ ์ ์ํฉ๋๋ค..sample-transition
ํด๋์ค๋ฅผ ๋ฐฉ๊ธ ์ ์ํ slide-it
View Transition์ ์ฐ๊ฒฐํ์ฌ CSS ํด๋์ค ์ด๋ฆ์ ํตํด ์์์ ๋ฐ์ธ๋ฉํ ์ ์๋๋ก ํฉ๋๋ค.(View Transition API์ ๋ํ ์์ธํ ๋ด์ฉ์ ์ด๋ฅผ ๋ฌธ์ํํ Chrome Developer Page์์ ํ์ธํ ์ ์์ต๋๋ค.)
<style>
@keyframes fade-in {
from { opacity: 0; }
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes slide-from-right {
from { transform: translateX(90px); }
}
@keyframes slide-to-left {
to { transform: translateX(-90px); }
}
/* ์ด์ ๋ฐ ์๋ก์ด ์ฝํ
์ธ ์ ๋ํ ์ ๋๋ฉ์ด์
์ ์ */
::view-transition-old(slide-it) {
animation: 180ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(slide-it) {
animation: 420ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
/* ์ง์ ๋ CSS ํด๋์ค์ View Transition ์ฐ๊ฒฐ */
.sample-transition {
view-transition-name: slide-it;
}
</style>
์ด CSS๋ .sample-transition
ํด๋์ค๋ฅผ ๊ฐ์ง ์ฝํ
์ธ ๊ฐ ์ ๊ฑฐ๋ ๋ ํ์ด๋์์ํ๊ณ ์ผ์ชฝ์ผ๋ก ์ฌ๋ผ์ด๋ํ๋ฉฐ, ์ ์ฝํ
์ธ ๋ ํ์ด๋์ธํ๊ณ ์ค๋ฅธ์ชฝ์์ ์ฌ๋ผ์ด๋ํด ๋ค์ด์ค๋๋ก ์ค์ ํฉ๋๋ค.
CSS๋ฅผ ํตํด View Transition์ ์ ์ํ์ผ๋ฏ๋ก, ๋ค์์ผ๋ก ํ ์ผ์ htmx๊ฐ ๋ณ๊ฒฝํ ์ค์ ์์์ ์ด View Transition์ ์ฐ๊ฒฐํ๊ณ , htmx๊ฐ View Transition API๋ฅผ ํ์ฉํ๋๋ก ์ง์ ํ๋ ๊ฒ์ ๋๋ค.
<div class="sample-transition">
<h1>์ด๊ธฐ ์ฝํ
์ธ </h1>
<button hx-get="/new-content"
hx-swap="innerHTML transition:true"
hx-target="closest div">
Swap It!
</button>
</div>
์ฌ๊ธฐ์๋ ์ ์ฝํ
์ธ ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด GET
์์ฒญ์ ๋ณด๋ด๊ณ , ์๋ต์ ์ฌ์ฉํ์ฌ ๊ฐ์ฅ ๊ฐ๊น์ด div์ ๋ด๋ถ HTML์ ๊ต์ฒดํ๋ ๋ฒํผ์ด ์์ต๋๋ค.
ํด๋น div์๋ sample-transition
ํด๋์ค๊ฐ ์์ผ๋ฏ๋ก, ์์์ ์ ์ํ View Transition์ด ์ ์ฉ๋ฉ๋๋ค.
๋ง์ง๋ง์ผ๋ก, hx-swap
์์ฑ์ transition:true
์ต์
์ด ํฌํจ๋์ด ์์ผ๋ฉฐ, ์ด๋ htmx๊ฐ ์ ํ ์ ๋ด๋ถ View Transition JavaScript API๋ฅผ ์ฌ์ฉํ๋๋ก ์ง์ํฉ๋๋ค.
์ด์ ๋ชจ๋ ๊ฒ์ ์ฐ๊ฒฐํ์ผ๋ฏ๋ก, htmx์ ํจ๊ป View Transition API๋ฅผ ์ฌ์ฉํด ๋ณผ ์ค๋น๊ฐ ๋์์ต๋๋ค. ๋ค์์ Chrome 111+์์ ์๋ํ๋ ๋ฐ๋ชจ์ ๋๋ค(๋ค๋ฅธ ๋ธ๋ผ์ฐ์ ์์๋ ์๋ํ์ง๋ง ๋ฉ์ง ์ ๋๋ฉ์ด์ ์ ํ์๋์ง ์์ต๋๋ค):
Chrome 111+์์ ์ด ํ์ด์ง๋ฅผ ๋ณด๊ณ ์๋ค๊ณ ๊ฐ์ ํ๋ฉด, ์์ ์ฝํ ์ธ ๊ฐ ์ผ์ชฝ์ผ๋ก ๋ถ๋๋ฝ๊ฒ ์ฌ๋ผ์ด๋์์๋๊ณ ์ค๋ฅธ์ชฝ์์ ์ ์ฝํ ์ธ ๊ฐ ์ฌ๋ผ์ด๋์ธํ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค. ๋ฉ์ง๋ค์!
์ด์ , ๊ฝค ๋ฉ์ง์ง ์๋์? ๊ทธ๋ฆฌ๊ณ ์ด ๊ฐ๋ ์ ์ดํดํ๊ณ ๋๋ฉด ๊ทธ๋ ๊ฒ ๋ง์ ์์ ์ด ํ์ํ์ง ์์ต๋๋ค! ์ด ์๋ก์ด API๋ ๋ง์ ๊ฐ๋ฅ์ฑ์ ๋ณด์ฌ์ค๋๋ค.
View Transitions๋ ๋งค์ฐ ํฅ๋ฏธ๋ก์ด ์๋ก์ด ๊ธฐ์ ๋ก, ํ์ฌ ๋๋ฆฌ ์ฌ์ฉ๋๋ SPA ์ํคํ ์ฒ์ ํ์ดํผ๋ฏธ๋์ด ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ์ ๊ฒฉ์ฐจ๋ฅผ ํฌ๊ฒ ์ค์ผ ์ ์๋ค๊ณ ์๊ฐํฉ๋๋ค.
Web 1.0 ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณด๊ธฐ ํํ โ์นด-์ฒโ์ ์์ ๊ณ , SPA ์ ๊ทผ ๋ฐฉ์์ ๋ฏธํ์ ์ด์ ์ ์ค์์ผ๋ก์จ, ๋ค์ํ ์ํคํ ์ฒ์ ๊ด๋ จ๋ ์ค์ ๊ธฐ์ ์ ์ ์ถฉ์ ์ ๋ ์ง์คํ ์ ์๊ฒ ๋ ๊ฒ์ ๋๋ค.
View Transitions๊ฐ ์์ HTML์์ ์ฌ์ฉ ๊ฐ๋ฅํด์ง๋ ๊ฒ์ ๊ณ ๋ํ๊ณ ์์ง๋ง, ๊ทธ๋๊น์ง๋ htmx์์ ์ด๋ฅผ ์ค๋๋ถํฐ ์ฌ์ฉํด ๋ณผ ์ ์์ต๋๋ค!