Luvit ํ๋ฌ ์คํฐ๋
๐๏ธ์ต์ข ํ๋ก์ ํธ: Moving
Svelte์ ๊ธฐ๋ณธ ๋ฌธ๋ฒ๋ถํฐ, ๋ผ์ฐํ , API ํต์ , ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฐ๋๊น์ง, ๊ทธ๋์ ํ์ตํ ๊ฒ๋ค์ ์ข ํฉํด์ TMDB API๋ฅผ ํ์ฉํ OTT ์ฌ์ดํธ Moving ํด๋ก ํ๋ก์ ํธ๋ฅผ ์งํํ๋ค. ๋ฐ์ดํฐ ํ๋ฆ๊ณผ ์ปดํฌ๋ํธ ์ํคํ ์ฒ๋ฅผ ๊ณ ๋ฏผํ๋ฉฐ ์ค์ ์๋น์ค์ ์ ์ฌํ ๊ตฌ์กฐ๋ฅผ ๊ตฌํํด๋ณผ ์ ์๋ ํ๋ก์ ํธ ๊ฒฝํ์ด์๋ค.
ํ๋ก์ ํธ ๊ฐ์
Moving ํ๋ก์ ํธ๋ ๋ฉ์ธ ํ์ด์ง์ ์๋ ์ฌ๋ผ์ด๋ ๋ฐฐ๋, ํ์ฌ ์์์/์ธ๊ธฐ ์์์ ๋ฑ ์นดํ ๊ณ ๋ฆฌ๋ณ ์ํ ๋ชฉ๋ก, ๊ทธ๋ฆฌ๊ณ ๊ฐ ์ํ์ ์์ธ ํ์ด์ง๋ก ๊ตฌ์ฑ๋ SPA์ด๋ค. ํ๋ก์ ํธ ๊ตฌํ์ ์ํด svelte-routing์ผ๋ก ํ์ด์ง ๊ตฌ์กฐ๋ฅผ ์ค๊ณํ๊ณ , swiper.js๋ก ๋์ ์ธ UI๋ฅผ ๊ตฌํํ๋ค. ๋ Svelte์ {#await} ๋ธ๋ก๊ณผ ์ปค์คํ ์คํ ์ด๋ฅผ ์ฌ์ฉํด TMDB API ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฐ๋ํด ์ฌ์ฉํ๋ค.
ํต์ฌ ๊ธฐ๋ฅ/ํ์ต ๋ด์ฉ
1. ์ปจํ ์ด๋/ํ๋ ์ ํ ์ด์ ๋ ํจํด์ ์ ์ฉํ ์ปดํฌ๋ํธ ์ค๊ณ
์ด๋ฒ ํ๋ก์ ํธ์์๋ ์ฝ๋ ๊ด์ฌ์ฌ ๋ถ๋ฆฌ๋ฅผ ์ํด ์ปจํ ์ด๋(Container)์ ํ๋ ์ ํ ์ด์ ๋(Presentational) ํจํด์ ๋์ ํ๋ค.
- ์ปจํ ์ด๋ ์ปดํฌ๋ํธ(/containers): API ํธ์ถ, ์ํ ๊ด๋ฆฌ ๋ฑ ๋ฐ์ดํฐ์ ๋ก์ง์ ์ ๋ดํ๋ค. {#await} ๋ธ๋ก์ ์ฌ์ฉํด ๋น๋๊ธฐ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์จ ํ, ํ๋ ์ ํ ์ด์ ๋ ์ปดํฌ๋ํธ๋ก ์ ๋ฌํ๋ ์ญํ ์ ํ๋ค.
- ํ๋ ์ ํ ์ด์ ๋ ์ปดํฌ๋ํธ(/components): ์ปจํ ์ด๋๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ๋ฐ์ ์์ํ๊ฒ UI๋ฅผ ๊ทธ๋ฆฌ๋ ์ญํ ์๋ง ์ง์คํ๋ ์ปดํฌ๋ํธ. ๋ก๋ฉ ์คํผ๋, ์๋ฌ ํ์ด์ง, ์ํ ๋ชฉ๋ก ๋ฑ์ด ํฌํจ๋๋ค.
components ํด๋๋ 3๊ฐ์ง ํ์ ํด๋๋ก ๋ ์ธ๋ถํํ์๋ค.
- common: ๋ชจ๋ ํ์ด์ง์์ ๊ณตํต์ ์ผ๋ก ์ฌ์ฉํ ์ปดํฌ๋ํธ ๋ชจ์
- main: ๋ฉ์ธํ์ด์ง์๋ง ํ์ํ๋ ์ปดํฌ๋ํธ ๋ชจ์
- sub: ์๋ธ ํ์ด์ง์์ ํ์ํ๋ ์ปดํฌ๋ํธ ๋ชจ์
์ด๋ ๊ฒ ๋ฐ์ดํฐ ๋ก์ง๊ณผ UI ๋ ๋๋ง ๋ก์ง์ ๋ช ํํ๊ฒ ๊ตฌ๋ถํ์ฌ ์ปดํฌ๋ํธ ๊ตฌ์กฐ๋ฅผ ๊ตฌ์ฑํ์๋ค.
<!-- /src/containers/MainListContainer.svelte (์ปจํ
์ด๋) -->
<script>
import { mainPromise } from '../libs/store'; // Promise๋ฅผ ๋ด์ ์คํ ์ด
import MainList from '../components/main/MainList.svelte'; // UI ์ปดํฌ๋ํธ
import MainLoading from '../components/common/MainLoading.svelte';
import Error from '../components/common/Error.svelte';
</script>
{#await $mainPromise}
<MainLoading />
{:then res}
<!-- ๋ก์ง ์ฒ๋ฆฌ ํ UI ์ปดํฌ๋ํธ๋ก ๋ฐ์ดํฐ ์ ๋ฌ -->
<MainList movies={res.data.results} />
{:catch error}
<Error {error} />
{/await}
2. svelte-routing์ ๋ฆฌ์ฉํ ๋ค์ค ํ์ด์ง ๊ตฌ์ฑ
๋ฉ์ธ ํ์ด์ง์ ๊ฐ ์ํ์ ์์ธ ํ์ด์ง ๊ฐ ์ด๋์ ๊ตฌํํ๊ธฐ ์ํด svelte-routing์ ์ฌ์ฉํ๋ค. App.svelte์์ ์ ์ฒด์ ์ธ ๋ผ์ฐํ ๊ตฌ์กฐ๋ฅผ ์ ์ํ๊ณ , ๋์ ํ๋ผ๋ฏธํฐ(:id)๋ฅผ ํ์ฉํ์ฌ ๊ฐ๊ธฐ ๋ค๋ฅธ ์ํ ์์ธ ํ์ด์ง๋ฅผ ๋ ๋๋งํ๋๋ก ๊ตฌํํ๋ค.
<!-- /src/App.svelte -->
<script>
import { Router, Route } from "svelte-routing";
import Header from './components/common/Header.svelte';
import MainPage from './pages/MainPage.svelte';
import NowPage from './pages/NowPage.svelte';
import NowSubPage from './pages/NowSubPage.svelte';
// ...
</script>
<Header />
<main>
<Router {url}>
<Route path="/" component={MainPage} />
<Route path="/now" component={NowPage} />
<!-- ๋์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํ ์์ธ ํ์ด์ง ๋ผ์ฐํธ -->
<Route path="/now/:id" component={NowSubPage} />
<!-- ... -->
</Router>
</main>
<!-- ... -->
3. swiper.js๋ก ๊ตฌํํ ์ฌ๋ผ์ด๋ UI
๋ฉ์ธ ํ์ด์ง์ ์๋จ ๋ฐฐ๋์ ์นดํ ๊ณ ๋ฆฌ๋ณ ์ํ ๋ชฉ๋ก์ swiper.js ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐ๋ํ์ฌ ๋์ ์ธ ์บ๋ฌ์ ํํ๋ก ๊ตฌํํ๋ค. Svelte์ฉ Swiper ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋, ๋ค์ดํฐ๋ธ Svelte ์ปดํฌ๋ํธ๋ฅผ ๋ค๋ฃจ๋ฏ props๋ก ์ต์ ์ ์ ๋ฌํ๋ ๊ฒ๋ง์ผ๋ก ์๋ ์ฌ์, ๋ค๋น๊ฒ์ด์ , ํ์ด์ง๋ค์ด์ ๋ฑ ๋ณต์กํ ๊ธฐ๋ฅ์ ์ฝ๊ฒ ์ ์ฉํ ์ ์์๋ค.
<!-- /src/components/main/MainList.svelte -->
<script>
import { Swiper, SwiperSlide } from 'swiper/svelte';
import { Navigation, Pagination, Autoplay } from 'swiper';
import 'swiper/css';
// ...
</script>
<Swiper
modules={[Navigation, Pagination, Autoplay]}
navigation
pagination={{ clickable: true }}
autoplay={{ delay: 2500, disableOnInteraction: false }}
loop={true}
>
{#each movies as movie}
<SwiperSlide>
<!-- ์ฌ๋ผ์ด๋ ์์ดํ
-->
</SwiperSlide>
{/each}
</Swiper>

4. ์คํ ์ด์ {#await} ๋ธ๋ก์ ํ์ฉํ API ๋ฐ์ดํฐ ๊ด๋ฆฌ
store.js ํ์ผ ๋ด์ axios๋ฅผ ์ฌ์ฉํ์ฌ TMDB API๋ฅผ ํธ์ถํ๊ณ , ๊ทธ ๊ฒฐ๊ณผ์ธ Promise ์์ฒด๋ฅผ writable ์คํ ์ด์ ๋ด์ ๋ฐํํ๋ ์ปค์คํ ์คํ ์ด ํจ์๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๋ค.
// /src/libs/store.js
import { writable } from "svelte/store";
import axios from 'axios';
const setPromise = (url) => {
let promise;
const { subscribe } = writable(promise);
const getPromise = async () => {
// ... API ํธ์ถ ์ค์ ...
try {
const res = await axios.request(options);
return res;
} catch(error) {
throw new Error(error);
}
};
promise = getPromise();
return { subscribe };
}
export const mainPromise = setPromise('now_playing');
// ...
์ปจํ ์ด๋ ์ปดํฌ๋ํธ์์๋ ์ด ์คํ ์ด๋ฅผ ๊ตฌ๋ ($mainPromise)ํ์ฌ {#await} ๋ธ๋ก์ ๋๊ฒจ์ฃผ๊ธฐ๋ง ํ๋ฉด, ๋ก๋ฉ ์ค, ์ฑ๊ณต, ์คํจ ๊ฐ ์ํ์ ๋ง๋ UI๋ฅผ ์ ์ธ์ ์ผ๋ก ๋ ๋๋งํ ์ ์๋ค. ์ด ํจํด์ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ์ script ์์ญ์์ ๋น๋๊ธฐ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ์ฝ๋๊ฐ ์ฌ๋ผ์ ธ ๊น๋ํ๊ณ ์ง๊ด์ ์ธ ๊ตฌ์กฐ๋ฅผ ์ ์งํ ์ ์์๋ค.



๋๋์
Moving ํ๋ก์ ํธ๋ ์ง๊ธ๊น์ง ํ์ตํ Svelte ๊ธฐ๋ฅ๋ค์ ํ๋์ ํ๋ก์ ํธ๋ก ํตํฉํด๋ณด๋ ์ต์ข ๊ณผ์ ์๋ค. ์ปดํฌ๋ํธ ์ํคํ ์ณ ์ค๊ณ๋ถํฐ ๋ผ์ฐํ ์ ํตํ ํ์ด์ง ํ๋ฆ ๊ตฌ์ฑ, ์คํ ์ด๋ฅผ ํ์ฉํ ๋ฐ์ดํฐ ์ํ ๊ด๋ฆฌ, ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋์ ์ ํตํ UI ํ์ฅ๊น์ง Svelte ๊ฐ๋ฐ์ ์ ๋ฐ์ ์ธ ๊ณผ์ ์ ์ง์ ๊ฒฝํํ ์ ์์๋ค. ์ด๋ฅผ ํตํด Svelte๊ฐ ๊ฐ์ง ๊ฐ๊ฒฐํจ๊ณผ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๋ค์ด ์ค์ ํ๋ก์ ํธ์์ ์ด๋ป๊ฒ ํจ๊ณผ์ ์ผ๋ก ์ฐ์ผ ์ ์๋์ง ์ฒด๊ฐํ ์ ์์๊ณ , ์ ๋ฐ์ ์ธ ๊ฐ๋ฐ ์๋ จ๋ ์ญ์ ํ ์ธต ๋์ผ ์ ์๋ ๊ฒฝํ์ด์๋ค.
๐ฅณ์๋ก์ด ์น ๊ฐ๋ฐ์ ์์ ์ค๋ฒจํธ Svelte ์๋ ํ๊ธฐ
ํ ๋ฌ๊ฐ์ Svelte ์คํฐ๋๊ฐ ๋๋ฌ๋ค. ์ค๋ฒจํธ๋ฅผ ์ฒ์ ์ ํ๋ ์
์ฅ์์ ๋งค์ฐ ๋ง์กฑ์ค๋ฌ์ด ํ์ต ๊ฒฝํ์ด์๋ค.
๐งฉ๊ตฌ์กฐ์ ์ธ ํ์ต ๋ฐฉ์
์ฑํฐ๋ณ๋ก ํ์ต ๋ด์ฉ์ ๋๋์๋ณผ ์ ์๋ ๋์๋ณด๊ธฐ์ ์ชฝ์ง ์ํ ์ฝ๋๊ฐ ์์ด, ์ฑํฐ์ ํต์ฌ ๊ฐ๋ ์ ์ ๋ฆฌํ๊ณ ๋ณต์ตํ๋๋ฐ ๋ง์ ๋์์ด ๋์๋ค. ๋ด์ฉ ์ญ์ ์ฝ๋ ์์ ๋ฅผ ์ค์ฌ์ผ๋ก ๊ตฌ์ฑ๋์ด ์์ด, ๋ฐ๋ผ์ ํ์ดํํด๋ณด๋ฉด์ ๊ฐ๋ ์ ์ฒดํํ ์ ์๋ ๊ตฌ์ฑ์ด์๋ค.
๐จ์ค์ต ์์ฃผ์ ๊ตฌ์ฑ
์ด ์ธ๊ฐ์ง ํ๋ก์ ํธ๊ฐ ํฌํจ๋์ด ์๋ค:
- My Bucket List
- Best Tour
- Moving
์ด ํ๋ก์ ํธ๋ค์ ํตํด ์คํ ์ด ์ํ ๊ด๋ฆฌ, ๋ผ์ฐํ ๊ตฌ์ฑ, ์ธ๋ถ API ์ฐ๋, UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ์ฉ ๋ฑ ๋ค์ํ ๊ธฐ๋ฅ๋ค์ ์ง์ ๊ตฌํํด๋ณผ ์ ์์๋ค. Moving ํ๋ก์ ํธ์ ๊ฒฝ์ฐ, ์ํ ์ฝ๋๋ฅผ ๋์ด ์ค์ ๊ฐ๋ฐ์ ๊ฐ๊น์ด ๊ฒฝํ์ ํ ์ ์์๋ ์ ์ด ํนํ ์ข์๋ค. ๋๋ถ์ ์ค์ ์๋น์ค์์ ์ค๋ฒจํธ๋ฅผ ์ด๋ป๊ฒ ํ์ฉํ ์ ์์์ง ๊ฐ์ ์ก์ ์ ์์๋ค.
๐๋ฒ์ ๊ณ ๋ฏผ x
์ค์ต์ sveltekit ์์ด ์งํ๋์๋ค. ํ์ง๋ง ์ฑ ๋ถ๋ก์์ sveltekit ๊ด๋ จ ๋ด์ฉ๋ ๊ฐ๋จํ ๋ค๋ค์ฃผ๊ธฐ ๋๋ฌธ์ ์ต์ ๋ฒ์ ์ ์ค๋ฒจํธ๋ฅผ ์ฌ์ฉํ ๋๋ ํฌ๊ฒ ๋ฌด๋ฆฌ๊ฐ ์์ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค. ๋ฒ์ ์ฐจ์ด๋ก ์ธํ ๋ฌธ์ ๋ ์์๊ณ , ์ค์ต ์ฝ๋๋ฅผ ๋ฐ๋ก ์ ์ฉํด๋ ์ ์๋ํ๊ธฐ ๋๋ฌธ์ ๋ฒ์ ์ ๋ํ ๊ฑฑ์ ์์ด ํ์ตํ ์ ์๋ ์ฑ ์ด๋ค. 25๋ ์ถ๊ฐ๋ ์ต์ ๋์์ฌ์, ์ง๊ธ ์์ ์์ Svelte๋ฅผ ํ์ตํ๋ ค๋ ์ฌ๋์๊ฒ ์ ํฉํ๋ค.
์ฑ ์ผ๋ก svelte๋ฅผ ํ์ตํ๊ณ ์ ํ๋ค๋ฉด Luvit...๊ฐ์ถ๐ฏ
๐ฉ๋ถ๋ด์ค๋ฝ์ง ์์ ๋ถ๋
๋๋ 1-2๋ถ, 3๋ถ, 4-5๋ถ, 6-7๋ถ ์ด๋ ๊ฒ 4๋ถํ ํ์ฌ ํ์ต์ ์งํํ๋ค.
์ผ์ฃผ์ผ์ ํ๋ฃจ์ ๋ ํฌ์ํ๋๋ฐ ์ฌ์ ๋กญ๊ฒ ์๋
ํ ์ ์์๋ค. ์ฌ์ด ์ค๋ช
๊ณผ ๋ถ๋ด์ค๋ฝ์ง ์์ ๋ถ๋์ผ๋ก, ์ฝ๊ณ ๋น ๋ฅด๊ฒ ์ค๋ฒจํธ๋ฅผ ์ตํ ์ ์๋ ๊ตฌ์ฑ์ด์๋ค.
ํ ๋ฌ๋์ ๊ฒฝํํ ์ค๋ฒจํธ๋ ์ ๋ง ๊ฐ๋ณ๊ณ ์ง๊ด์ ์ธ ํ๋ ์์ํฌ์๋ค. ์ด ์ฑ
์ ๊ทธ๋ฐ Svelte์ ์ฒ ํ๊ณผ ๊ธฐ๋ฅ์ ๋น ๋ฅด๊ฒ ์ตํ๊ณ ์ง์ ์ฌ์ฉํด๋ณผ ์ ์๋ ํ๋ฅญํ ๊ฐ์ด๋์๋ค. ์งง์ ๊ธฐ๊ฐ ๋์ ํ๋ ์์ํฌ์ ์ ๋ฐ์ ์ธ ๊ตฌ์กฐ์ ์ค์ ์ฌ์ฉ๋ฒ๊น์ง ์ตํ ์ ์์๋ ์์ฐฌ ๊ฒฝํ์ด์๊ณ , Svelte๋ฅผ ๋น ๋ฅด๊ฒ ๋ฐฐ์ฐ๊ณ ์ถ์ ์ฌ๋์ด๋ผ๋ฉด ์ด ์ฑ
์ ์ ๊ทน ์ถ์ฒํ๊ณ ์ถ๋ค!
