원문: https://blog.openreplay.com/exploring-sveltekit-in-2022-by-building-a-portfolio-website
몇 달 전 Svelte팀에서 SvelteKit을 소개했습니다. SvelteKit은 Sapper를 대체하는 새로운 프레임워크입니다. 멋진 개발 경험과 유연한 파일시스템 기반 라우팅으로 Svelte 앱을 제일 빠르게 구축할 수 있습니다.
이 글에서는 프레임워크에서 필요한 모든 업데이트를 포함한 샘플 포트폴리오 웹사이트를 구축하여 SvelteKit이 어떻게 작동하는지 살펴보고자 합니다.
SvelteKit을 사용하여 웹앱을 구축하는 데에 필요한 모든 기본 개념, 왜 Sapper에서 SvelteKit으로 전환하는지, SvelteKit에서 REST API를 사용하는 방법 등을 앱 내에 데모 블로그를 구축하면서 모두 배우실 수 있을겁니다.
더불어 SvelteKit 애플리케이션을 Netlify를 이용해 배포하는 방법까지 배우실 수 있습니다.
먼저 Svelte가 처음이신 분들을 위해, Svelte 웹사이트에서는 Svelte를 아래와 같이 안내하고 있습니다.
Svelte는 급진적이고 새로운 방법으로 사용자 인터페이스를 구축합니다.
리액트나 Vue와 같은 기존의 프레임워크는 대부분의 작업을 브라우저에서 수행합니다.
하지만 Svelte에서는 이 작업을 앱을 빌드할 때 컴파일 단계에서 수행합니다.
가상 DOM 비교와 같은 기술을 사용하는 대신 Svelte는 애플리케이션의 상태가 변경될 때 마치 수술하듯 DOM을 업데이트합니다.
Svelte는 컴포넌트 기반 자바스크립트 프레임워크로(리액트, Vue, angular와 같은), 웹 애플리케이션을 만드는 데 사용됩니다.
반대로 SvelteKit은 Svelte 앱을 구축하기 위한 프레임워크입니다. 복잡한 애플리케이션도 작은 설치 공간으로 구축 할 수 있게 해줍니다. 뛰어난 개발 경험, 유연한 파일시스템 기반 라우팅, 서버 사이드 렌더링(SSR), 클라이언트 사이드 하이드레이션 및 기타 고유한 기능들을 제공합니다.
Svelte를 만든 Richard Harris는 Sapper를 SvelteKit으로 바꾸게 된 몇 가지 이유들을 언급했습니다.
SvelteKit은 최신 웹 애플리케이션 구축의 모든 병목을 해소합니다. 여러분이 창의적인 부분에 집중하는 동안 귀찮은 일들은 SvelteKit이 처리합니다. 모던 웹을 위해 설계된 번개처럼 빠른 프론트엔드 빌드 도구인 Snowpack에 의해 구동됩니다. Vite와 Svelte 플러그인을 사용하여 HMR(Hot Module Replacement)을 통해 빠르고, 다양한 기능을 갖춘 개발 환경을 제공합니다. 코드의 변경은 즉시 브라우저에 반영됩니다.
SvelteKit으로 프로젝트를 설정하려면 NodeJS가 있어야 합니다. 아래 명령을 실행하여 SvelteKit 데모 프로젝트를 세팅한 후 폴더로 이동하여 필요한 모든 종속성을 설치하세요.
npm init svelte@next my_portfolio_website
cd my_portfolio_website
npm install
npm run dev
첫 번째 명령어는 'my_portfolio_website' 디렉토리에 새로운 프로젝트를 설치하며 타입스크립트와 같은 기본 툴도 설정하고 싶은지 여부를 묻습니다. 이 프로젝트에서는 타입스크립트를 사용하지 않기 떄문에 '아니오'를 선택하세요. 이어지는 명령어들은 종속성을 설치하고 localhost:3000에서 서버를 시작합니다.
아래는 우리가 만들고 있는 샘플 데모의 모습입니다.
이 글을 따라 같이 만들어보고 싶으시다면 깃헙 레포를 참고해도 좋습니다.
SvelteKit의 훌륭한 기능 중 하나는 파일 시스템 기반의 라우터입니다. 즉, 코드 베이스의 구조가 애플리케이션의 구조를 정의합니다. 루트에는 페이지와 엔드포인트의 2종류가 있습니다. 일반적으로 페이지는 사용자에게 표시하는 데 필요한 HTML, CSS 및 자바스크립트를 생성하는 반면, 엔드포인트는 서버(또는 사이트를 빌드할 때 사전 렌더링하는 경우)에서만 실행됩니다.
다른 컴포넌트 기반 프레임워크와 마찬가지로 페이지는 .svelte
파일(또는 config.extension에 나열된 확장자를 가진 파일)로 작성된 Svelte 컴포넌트입니다. 사용자가 처음 애플리케이션을 방문하면 기본적으로 서버에서 렌더링된 페이지가 제공됩니다. 여기서부터 다른 페이지로 이동하는 작업은 전적으로 클라이언트에 의해 처리됩니다.
파일명은 라우트를 결정합니다. 예를 들어 src/routes/index.svelte는 데모 사이트의 루트입니다.
원하는 코드 에디터로 폴더를 열고 src/routes/index.svelte로 이동하여 다음 코드를 삽입하세요.
<!-- src/routes/index.svelte -->
<svelte:head>
<title>David Adeneye</title>
</svelte:head>
<div class="container">
<div class="info">
<div class="animate-background">
<div class="container info-container">
<div class="info-text">
<h1>
<p class="animate-text-1">School-taught Computer Scientist,</p>
<p class="text-2">Self-taught Software Engineer</p>
</h1>
</div>
<h2 class="subtitle">
On a mission to help SMEs across the globe do businesses seamlessly
and digitally.
</h2>
<div
style="color:black"
class="scroll-button animate-scroll"
tabindex="0"
>
<a href="/about">Click Here</a>
<div class="arrow">→</div>
</div>
</div>
</div>
</div>
</div>
<div class="background-div"></div>
두 번쨰 줄의 <svelte:head>
태그 엘리먼트를 사용하면 문서의 <head>
안에 엘리먼트를 삽입할 수 있습니다. 나머지 코드는 index 페이지의 HTML 페이지 구조입니다. 다음 섹션에서는 SvelteKit의 스타일링에 대해 알아보겠습니다.
SvelteKit에서의 스타일링 방법은 일반 HTML&CSS 프로젝트와 거의 같은 프로세스로 이루어집니다. 외부 CSS 파일 또는 app.html의 head 태그에 있는 모든 페이지에 적용되는 루트 스타일을 참조하고 document 태그에 셀렉터를 붙여 스타일을 적용할 수 있습니다.
위의 index.svelte 파일에 스타일을 적용하기 위해서 마지막 코드 아래에 다음 스타일을 추가해보세요.
<style>
.info {
text-align: center;
position: relative;
}
.info-container {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
text-align: left;
align-items: start;
}
.container {
max-width: 1400px;
margin: 190px 0 0 2.5rem;
}
.info h1 {
font-size: 4rem;
margin: 0;
}
.info h1 p {
position: relative;
font: inherit;
margin: 0;
text-align: left;
line-height: 130%;
width: fit-content;
font-weight: 800;
letter-spacing: -0.03em;
}
.info h2 {
max-width: 65ch;
font-weight: 200;
margin-top: 0.5rem;
}
.background-div {
background-color: var(--background);
width: 100%;
height: 56vh;
margin: -155px auto;
}
.info .info-text {
position: relative;
}
.scroll-button {
display: flex;
border: none;
outline: none;
cursor: pointer;
}
.scroll-button .arrow {
margin-left: 0.5rem;
}
</style>
또, 글로벌 스타일을 다른 페이지에서도 적용하기 위해 app.html을 열고, 다음의 스타일을 html 클로징 태크 아래에 삽입해주세요.
<style>
body {
background-position: left top;
font-family: "Inter", sans-serif;
margin: 0;
background-repeat: no-repeat;
}
* {
box-sizing: border-box;
}
a {
text-decoration: none;
color: black;
}
:root {
--background: #ecebe8;
}
nav {
position: sticky;
top: 0;
left: 0;
right: 0;
margin: 0 80px 0 80px;
padding-top: 2rem;
padding-bottom: 2rem;
}
nav .nav-container {
display: flex;
justify-content: space-between;
align-items: center;
}
.nav-links {
display: flex;
justify-content: flex-end;
align-items: center;
margin: -1em 0;
flex: 1 0 auto;
}
.nav-links .link {
position: relative;
margin: 1em;
font-size: 0.85rem;
letter-spacing: 1px;
color: black;
}
.nav-logo {
position: relative;
line-height: 1;
font-size: 1.1rem;
color: black;
}
</style>
Svelte에서 컴포넌트를 import 할 때, 각 컴포넌트의 스타일의 범위는 컴포넌트 내부적으로만 적용된다는 것에 주목하세요. 즉, import된 컴포넌트에 적용되는 스타일은 컴포넌트 자체에만 영향을 미칩니다.
앞서 설명한 바와 같이, 페이지는 완전히 독립된 컴포넌트로 취급됩니다. 따라서 네비게이션과 동시에 기존 컴포넌트가 파괴되고 새로운 컴포넌트로 교체됩니다. 그러나 많은 애플리케이션에는 네비게이션 메뉴나 푸터와 같은 모든 페이지에 표시해야 하는 컴포넌트들이 있습니다. 따라서 모든 페이지에서 반복해서 사용하는 대신 레이아웃 컴포넌트를 사용할 수 있습니다.
웹사이트의 모든 페이지에 적용되는 레이아웃 컴포넌트를 작성하려면 src/routes/__layout.svelte 파일을 만들고 아래 코드를 안에 붙여넣으세요.
<!-- src/routes/__layout.svelte -->
<nav>
<div class="nav-container">
<a href="/" class="nav-logo" title="Back to Homepage">David Adeneye</a>
<div class="nav-links">
<a href="/about" class="link">About</a>
<a href="/blog" class="link">Blog</a>
<a href="/projects" class="link">Projects</a>
<a href="/contact" class="link">Contact</a>
</div>
</div>
</nav>
<div class="container">
<!-- 페이지는 아래에 삽입됩니다 -->
<slot></slot>
</div>
<style>
.container {
max-width: 1400px;
margin: 50px auto;
}
</style>
위의 코드는 포트폴리오 웹사이트의 모든 페이지에 적용되는 내비게이션 메뉴바입니다. SvelteKit에서 사용하는 기본 레이아웃 컴포넌트는(따로 추가하지 않는다면) 다음과 같습니다.
<slot></slot>
또한, 임의의 마크업, 스타일 및 동작도 사용할 수 있습니다. 위의 __layout.svelte 파일과 같이 컴포넌트에 페이지 컨텐트를 위한 <slot>
이 포함되어야 한다는 조건만 충족하면 됩니다.
다른 유명한 SSR 솔루션과 마찬가지로, SvelteKit은 뛰어난 파일 기반의 라우팅 시스템을 갖추고 있습니다. 애플리케이션의 페이지는 기본적으로 src/routes 폴더에 저장됩니다. 즉, 애플리케이션의 구조는 코드베이스의 구조, 특히 src/routes의 내용에 의해 정의되는 것입니다.
우리의 포트폴리오 웹사이트는 약 6개의 페이지로 구성됩니다.
index.svelte
(이미 위에서 생성했습니다.)about.svelte
(이번 섹션에서 생성합니다.)blog.svelte
(다음 섹션에서 생성합니다.)projects.svelte
(깃헙 레포의 코드를 복사하세요)contact.svelte
(깃헙 레포의 코드를 복사하세요)위 페이지들에서 사용할 네비게이션 헤더 구조를 src/routes/__layout.svelte에 만들었으므로 about.svelte를 열어 아래의 코드를 삽입하세요.
<!-- src/routes/about.svelte -->
<svelte:head>
<title>About</title>
</svelte:head>
<div class="main_container">
<section>
<div class="inner-container">
<h2 class="about-title">I'm David Adeneye👋</h2>
<div class="about-subtitle">
<p>
Software Engineer and a Technical Writer with a solid background in
Computer Science, I'm passionate about making the web accessible for
everyone. I design and develop visually compell ing applications with
user-friendly interaction that drive business g owth and improve user
experience. When I'm not solving problems, I love to create technical
content for engineers and tech startups across the globe.
</p>
<p>
Creating magic daily on the internet. You can send me an email at
<span class="info-text">adeneyeabiodun@gmail.com</span>
</p>
</div>
</div>
</section>
</div>
<style>
.main_container {
margin: 0 80px 0 65px;
}
.about-title {
font-size: 4.2rem;
font-weight: bold;
margin-top: 11rem;
}
.about-subtitle {
margin-top: -2rem;
}
.about-subtitle > p {
width: 60%;
font-weight: normal;
font-size: 1rem;
line-height: 2rem;
}
.info-text {
font-style: italic;
font-weight: bold;
}
</style>
네비게이션 컴포넌트는 항상 표시되며, 페이지를 클릭하면 항상 페이지 콘텐츠가 바뀝니다. 이제 포트폴리오 웹사이트가 작동하는 모습을 확인해보세요. about 페이지가 다음과 같이 표시되나요?
REST API에서 데이터를 가져오는 방법과 SvelteKit에서 로딩의 개념을 배우기 위해 웹사이트에 데모 블로그 포스트를 만들어봅시다. 무료 더미 REST API인 JSONPlaceholder를 사용할 예정입니다. 테스트 및 프로토타이핑을 위한 무료 온라인 REST API 입니다.
src/routes/blog.svelte를 열고 다음 코드를 붙여넣거나 함께 소스 코드를 읽어봅시다.
<!-- src/routes/blog.svelte -->
<svelte:head>
<title>Blog</title>
</svelte:head>
<script context="module">
export const load = async ({ fetch }) => {
const res = await fetch("https://jsonplaceholder.typicode.com/posts");
const blogposts = await res.json();
return {
props: {
blogposts,
}
}
}
</script>
<script>
export let blogposts;
</script>
<div class="container">
<h1>My Articles</h1>
<div class="blogposts">
{#each blogposts as post}
<div class="post">
<h2>{post.title.substring(0, 20)}</h2>
<p>{post.body.substring(0, 80)}</p>
<p class="readmore">
<a style=" color: rgb(10, 10, 139);" href={`/blog/${post.id}`}>
Read More
</a>
</p>
</div>
{/each}
</div>
</div>
위의 코드를 같이 분석해봅시다. 6번째 줄의 <srcipt context="module">
에 주목하세요. load
가 컴포넌트가 렌더링되기 전에 실행되어야 하므로 이 줄이 필요합니다. 컴포넌트 단위의 인스턴스 코드는 두 번째 <script>
태그에 있어야 합니다. 그 후 load 함수를 이용하여 jsonplaceholder fake api로 데모 블로그 포스트를 가져와 웹사이트에 표시합니다.
SvelteKit의 load
함수는 클라이언트와 서버 모두에서 실행된다는 것을 제외하고는 Next.js의 getStaticProps
혹은 getServerSideProps
와 비슷합니다.
또한, 이 튜토리얼과 함께 빌드하고 계시다면 페이지 스타일을 위해 코드 블록에 CSS 코드를 복사하는 것도 잊지 마세요.
blog.svelte 페이지는 아래와 같이 보여야 합니다.
이런 블로그를 구축하려면 동적으로 생성된 라우트가 필요합니다. SvelteKit은 동적 라우트를 통해 이를 쉽게 구현할 수 있도록 합니다.
이를 구현하려면 blog라는 새 폴더를 만들어야 합니다. SvelteKit의 동적 파라미터는 [bracket]
를 사용하여 인코딩됩니다. 예를 들어 [id].svelte라는 파일을 블로그 폴더 안에 만듭니다. 파일을 열고 다음 코드를 붙여넣어보세요.
<script context="module">
export const load = async ({ page, fetch }) => {
const id = page.params.id;
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
const post = await res.json();
return {
props: {
post,
},
};
};
</script>
코드를 보면, load 함수 안에서 파라미터에 접근하여 변수 id
에 저장하였습니다. 이 파라미터를 사용해서 해당 id의 블로그 포스트를 가져옵니다. 예를 들어 blog.svelte로 돌아간다고 생각해봅시다. 아래 33행에서 href
를 사용하여 동적으로 생성된 페이지를 어떻게 접근했는지 확인할 수 있습니댜.
<p class="readmore">
<a style="color: rgb(10, 10, 139);" href={`/blog/${post.id}`}>
Read More
</a>
</p>
post.id
로 어떻게 해당 블로그 포스트에 접근했는지 확인할 수 있습니다. 따라서 Read More를 클릭하면 블로그 전체를 읽을 수 있는 새로운 페이지가 로드됩니다.
SvelteKit에서는 앱을 배포하기 전에 개발 타겟에 맞게 조정해야합니다. 어댑터라고도 불리는 몇 가지 플러그인을 통해 배포 프로세스를 단순화해줍니다. 어댑터는 빌드된 애플리케이션을 인풋으로 받아 배포를 위한 아웃풋을 생성하는 작은 플러그인입니다.
SvelteKit은 공식적으로 지원되는 몇 가지 어댑터를 제공합니다.
Adapter Static: 깃헙 페이지 등과 같은 호스팅 서비스와 호환되는 정적 파일 모음으로 사이트를 미리 렌더링하는 SvelteKit 애플리케이션용 어댑터입니다.
Adapter Vercel: 동적 서버 렌더링 기능을 사용하여 Vercel 애플리케이션을 생성하는 Svelte 애플리케이션용 어댑터입니다. Vercel 호스팅 플랫폼에 앱을 배포하는 데 사용됩니다.
Adapter Netlify: 동적으로 페이지를 생성하기 위한 서비스 기능을 사용하여 Netlify 앱을 생성하는 Svelte 애플리케이션용 어댑터입니다. Netlify 플랫폼에 앱을 배포하는 데 사용됩니다.
데모 애플리케이션을 Netlify에 배포하는 방법을 함께 살펴보겠습니다.
SvelteKit을 Netlify에 배포하기 위해서는 netlify-sveltekit 어댑터를 사용해야 합니다.
아래 명령어를 사용하여 프로젝트에 어댑터를 설치해봅시다.
npm install -D @sveltejs/adapter-netlify@next
설치가 완료되면 프로젝트의 svelte.config.js 파일에 어댑터를 추가합니다. 아래의 코드를 파일에 삽입하세요.
import adapter from "@sveltejs/adapter-netlify";
export default {
kit: {
adapter: adapter(),
target: "#svelte",
},
};
그리고 프로젝트의 베이스 디렉토리에 netlify.toml 파일을 생성하고 빌드 명령 및 배포 디렉터리를 지정합니다. 아래의 코드를 파일에 삽입하세요.
[build]
command = "npm run build"
publish = "build"
이 모든 설정이 끝나면, Git 또는 Netlify CLI를 사용하여 Netlify에 프로젝트를 배포합니다.
jolly-franklin-11e50c.netlify.app: Netlify에 최종 배포된 사이트 링크입니다.
튜토리얼 즐겁게 진행하셨길 바랍니다. 샘플 포트폴리오 웹 사이트를 구축하며 SvelteKit의 기본 개념에 대해 알아보고, 무엇보다도 데모 블로그에서 REST API를 사용하는 방법, Netlify에서 SvelteKit 애플리케이션을 배포하는 방법을 알아봤습니다. 스스로 포트폴리오 웹사이트를 구축하려는 경우 다른 페이지를 구축하며 배운 내용을 적용해보거나, 이 데모를 취향에 맞게 업그레이드 할 수 있습니다.
이 글에 관련된 레포는 깃헙에서 확인하실 수 있습니다.
와! 좋은 내용 번역글 너무 너무 감사합니다! 잘 읽었습니다 :)