-
[Next.js] 3. Assets, Metadata, and CSS공부 2021. 10. 25. 17:56
Assets
Next.js에서 이미지와 같은 정적 자산은 public 디렉토리 아래에서 제공된다. public안에 있는 파일들은 pages와 비슷하게 어플리케이션의 root로부터 참조 된다.
public 디렉토리는 robots.txt, Google Site Verification과 다른 정적 자산에도 유용하다.
Download Your Profile Picture
프로파일 이미지를 가져오자.
- 프로파일 이지미를 jpg 파일 형식으로 다운로드 받는다.
- images 디렉토리는 public 디렉토리 안에 만든다.
- 이미지를 profile.jpg로 public/images안에 저장한다.
Unoptimized Image
일반 HTML에서는 다음과 같이 프로필 사진을 추가한다.
<img src="/images/profile.jpg" alt="Your Name" />
하지만, 이는 다음을 수동으로 처리해야 함을 의미한다.
- 이미지가 다양한 화면 크기에 반응하는지 확인한다.
- 타사 도구 또는 라이브러리로 이미지를 최적화 한다.
- 뷰포트에 들어왔을때에만 이미지를 로딩한다.
Next.js는 이러한 사항들을 즉시 처리할 수 있는 Image 컴포넌트를 제공한다.
Image Component and Image Optimization
next/image는 모던 웹을 위해 진화한 HTML의 <img>엘리먼트의 확장이다.
Next.js는 기본적으로 이미지 최적화를 지원하고 있다. 이는 리사이징, 최적화, WebP와 같은 모던 포맷의 이미지 제공을 가능하게 한다. 이렇게 하면 더 작은 뷰포트를 가진 기기에 큰 이미지를 전송하는 것을 방지할 수 있다.
자동 이미지 최적화는 이미지 소스가 무엇이든지 작동한다. 심지어 CMS와 같은 외부 데이터 소스에서 호스팅 되더라도 여전히 최적화 할 수 있다.
Using the Image Component
Next.js는 이미지를 빌드 할때 최적화 하는 것보다, 유저가 요청하는 대로 즉시 최적화 한다. 정적 사이트 생성기 및 정적 전용 솔루션과 달리 10개의 이미지를 전송하든 천만개의 이미지를 배송하든 빌드 시간이 증가하지 않는다.
이미지는 기본적으로 지연 로드된다. 이는 페이지 속도가 뷰포트 밖에 있는 이미지에 대해서 영향을 받지 않는다는 것을 뜻한다. 이미지는 뷰포트로 스크롤이 될 때에만 로드된다.
이미지는 Google이 검색 순위에 사용하는 핵심 Web Vital인 누적 레이아웃 이동을 방지하는 방식으로 항상 렌더링 된다.
Metadata
HTML태그인 <title>같은 metadata를 변경하고 싶을 때 어떡할까?
<title>은 HTML의 <head>의 한 부분이다. 그러니 Next.js 페이지에서 <head>태그를 어떻게 변경하는지 알아보자.
에디터에서 pages/index.js 파일을 열고 아래 코드를 찾는다.
<Head> <title>Create Next App</title> <link rel="icon" href="/favicon.ico" /> </Head>
소문자<head> 대신에 <Head>가 쓰였다. <Head>는 Next.js에 내장되어 있는 리액트 컴포넌트이다. 이는 페이지의 <head>를 수정할 수 있도록 한다.
Adding Head to first-post.js
/posts/first-post 루트에 <title>을 추가해보자. pages/posts/first-post.js 파일을 열고 <Head>를 import 한다.
import Head from 'next/head'
그리고 내보낸 FirstPost 컴포넌트에 Head컴포넌트를 포함시킨다. 지금은 title 태그만 추가한다.
export default function FirstPost() { return ( <> <Head> <title>First Post</title> </Head> <h1>First Post</h1> <h2> <Link href="/"> <a>Back to home</a> </Link> </h2> </> ) }
http://localhost:3000/posts/first-post에 접속해서 브라우저 탭이 First Post로 변경된 것을 확인한다. 개발자 도구를 통해 title 태그가 <head>안에 추가된 것을 확인할 수 있다.
CSS Styling
pages/index.js 파일을 살펴보면 아래와 같은 코드를 볼 수 있다.
<style jsx>{` … `}</style>
이는 styled-jsx라는 라이브러리를 사용하고 있기 때문이다. styled-jsx는 CSS-in-JS 라이브러리로, 리액트 컴포넌트 안에서 CSS를 작성할 수 있도록 하며 해당 CSS style은 범위가 지정된다. (다른 컴포넌트는 영향을 받지 않는다.)
Next.js는 내장된 styled-jsx를 제공하지만 styled-components나 emotion과 같은 다른 유명한 CSS-in-JS도 사용할 수 있다.
Tailwind CSS와 같은 인기 있는 라이브러리 사용도 지원된다.
이번 강의에선 Next.js에 CSS파일을 어떻게 작성하고 import하는지 배우고, Next.js의 CSS모듈과 Sass에 대한 내장 지원에 대해 알아본다.
Layout Component
먼저 모든 페이지에 공유할 Layout 컴포넌트를 만든다.
- components 라는 최상위 디렉토리를 만든다.
- 해당 디렉토리 안에 layout.js 라는 파일을 만들고 아래 코드를 작성한다.
export default function Layout({ children }) { return <div>{children}</div> }
pages/posts/first-post.js 파일에 Layout컴포넌트를 import하고 가장 바깥쪽 컴포넌트로 만든다.
import Head from 'next/head' import Link from 'next/link' import Layout from '../../components/layout' export default function FirstPost() { return ( <Layout> <Head> <title>First Post</title> </Head> <h1>First Post</h1> <h2> <Link href="/"> <a>Back to home</a> </Link> </h2> </Layout> ) }
Adding CSS
Layout 컴포넌트에 CSS 모듈을 사용해서 스타일 해보자. CSS 모듈은 리액트 컴포넌트에 있는 CSS 파일을 import할 수 있도록 한다.
components/layout.module.css 라는 파일을 만들고 아래 코드를 작성한다.
.container { max-width: 36rem; padding: 0 1rem; margin: 3rem auto 6rem; }
* 중요: CSS 모듈을 사용하기 위해서는 CSS파일 이름이 반드시 .modules.css 로 끝나야 한다.
components/layout.module.css 파일의 container 클래스를 사용하기 위해서
- CSS파일을 import하고 styles와 같은 이름을 할당한다.
- styles.container를 className으로 사용한다.
components/layout.js를 열고 아래의 코드로 대체한다.
import styles from './layout.module.css' export default function Layout({ children }) { return <div className={styles.container}>{children}</div> }
http://localhost:3000/posts/first-post에 접속하면 텍스트가 중앙에 있는 컨테이너 안에 있는 것을 볼 수 있다.
Automatically Generates Unique Class Names
개발자 도구를 통해 HTML을 보면, Layout 컴포넌트에 의해 렌더된 div가 아래와 같은 클래스 이름을 갖는걸 볼 수 있다.
CSS 모듈은 자동적으로 고유한 클래스 이름을 생성한다. 따라서 CSS 모듈을 사용하는 한 클래스 이름 충돌에 대해서 걱정할 필요가 없다.
또한 Next.js의 코드 분할 기능은 CSS 모듈에서도 작동한다. 각 페이지에 대해 최소한의 CSS로드를 보장하며 결과적으로 번들 크기가 더 작아진다.
CSS 모듈은 빌드 시 JavaScript 번들에서 추출되며, Next.js에 의해 자동으로 로드되는 .css파일을 생성한다.
Global Styles
CSS 모듈은 컨포넌트 레벨을 스타일 할 때 유용하다. 하지만 CSS를 모든 페이지에서 로드하고 싶다면 Next.js는 다음과 같은 방식을 제공한다.
global CSS 파일을 로드하기 위해서 pages/_app.js 파일을 만들고 아래의 코드를 작성한다.
export default function App({ Component, pageProps }) { return <Component {...pageProps} /> }
이 APP 컴포넌트는 모든 페이지에서 공통으로 들어갈 최상위 레벨 컴포넌트이다. 예를 들어 페이지간 이동을 할 때 APP 컴포넌트를 사용해서 상태를 유지할 수 있다.
Restart the Development Server
* 중요: pages/_app.js 파일을 추가했다면 반드시 개발 서버를 재실행 해야한다. Ctrl+c 로 멈췄다가 yarn dev 로 재실행 하자.
Adding Global CSS
Next.js에서는 pages/_app.js 파일을 import해서 global CSS파일을 추가할수 있다. 다른곳에서는 global CSS를 import 할 수 없다.
global CSS가 pages/_app.js 이외에서 import할 수 없는 이유는 global CSS가 페이지에 있는 모든 엘리먼트에 영향을 미치기 때문이다.
만약 홈페이지에서부터 posts/first-post 페이지로 이동하는 경우, 홈페이지의 전역 스타일은 의도치 않게 post/first-post에 영향을 미친다.
global CSS를 어디에나 배치하고 임의의 이름을 사용할 수 있다. 다음을 수행해보자.
- styles 라는 디렉토리를 최상위에 만들고 그 안에 global.css 파일을 만든다.
- 아래 코드를 styles/global.css에 작성한다.
html, body { padding: 0; margin: 0; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; line-height: 1.6; font-size: 18px; } * { box-sizing: border-box; } a { color: #0070f3; text-decoration: none; } a:hover { text-decoration: underline; } img { max-width: 100%; display: block; }
마지막으로 pages/_app.js를 열고 아래의 CSS코드를 작성한다.
import '../styles/global.css' export default function App({ Component, pageProps }) { return <Component {...pageProps} /> }
http://localhost:3000/posts/first-post에 접속하면 스타일이 적용된 것을 확인할 수 있다.
안된다면 pages/_app.js 파일을 주가하고 개발 서버를 재실행 했는지 확인한다.
Polishing Layout
지금까지 우리는 CSS 모듈의 개념을 설명하고자 최소한의 리액트와 CSS 코드를 작성했다. 데이터 가져오기에 대한 강의로 넘어가기 전에 페이지 스타일과 코드를 다듬어보자.
Update components/layout.module.css
첫번째로, components/layout.module.css 파일을 열고 아래의 코드로 대체한다.
.container { max-width: 36rem; padding: 0 1rem; margin: 3rem auto 6rem; } .header { display: flex; flex-direction: column; align-items: center; } .backToHome { margin: 3rem 0 0; }
Create styles/utils.module.css
두번째로, 여러 컴포넌트에서 유용할 타이포그래피 및 기타 유틸리티 CSS 클래스 세트를 만든다.
styles/utils.modules.css 라는 파일을 만들고 아래 코드를 작성한다.
.heading2Xl { font-size: 2.5rem; line-height: 1.2; font-weight: 800; letter-spacing: -0.05rem; margin: 1rem 0; } .headingXl { font-size: 2rem; line-height: 1.3; font-weight: 800; letter-spacing: -0.05rem; margin: 1rem 0; } .headingLg { font-size: 1.5rem; line-height: 1.4; margin: 1rem 0; } .headingMd { font-size: 1.2rem; line-height: 1.5; } .borderCircle { border-radius: 9999px; } .colorInherit { color: inherit; } .padding1px { padding-top: 1px; } .list { list-style: none; padding: 0; margin: 0; } .listItem { margin: 0 0 1.25rem; } .lightText { color: #666; }
Update components/layout.js
세번째로, components/layout.js 파일을 열고 아래 코드로 대체하고, Your Name을 내 이름으로 바꾼다.
import Head from 'next/head' import Image from 'next/image' import styles from './layout.module.css' import utilStyles from '../styles/utils.module.css' import Link from 'next/link' const name = 'Your Name' export const siteTitle = 'Next.js Sample Website' export default function Layout({ children, home }) { return ( <div className={styles.container}> <Head> <link rel="icon" href="/favicon.ico" /> <meta name="description" content="Learn how to build a personal website using Next.js" /> <meta property="og:image" content={`https://og-image.vercel.app/${encodeURI( siteTitle )}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.vercel.com%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`} /> <meta name="og:title" content={siteTitle} /> <meta name="twitter:card" content="summary_large_image" /> </Head> <header className={styles.header}> {home ? ( <> <Image priority src="/images/profile.jpg" className={utilStyles.borderCircle} height={144} width={144} alt={name} /> <h1 className={utilStyles.heading2Xl}>{name}</h1> </> ) : ( <> <Link href="/"> <a> <Image priority src="/images/profile.jpg" className={utilStyles.borderCircle} height={108} width={108} alt={name} /> </a> </Link> <h2 className={utilStyles.headingLg}> <Link href="/"> <a className={utilStyles.colorInherit}>{name}</a> </Link> </h2> </> )} </header> <main>{children}</main> {!home && ( <div className={styles.backToHome}> <Link href="/"> <a>← Back to home</a> </Link> </div> )} </div> ) }
여기서 새로운 사항들:
- og:image와 같은 meta태그들은 페이지의 컨텐트를 설명하기 위해 사용된다.
- home 프롭은 이미지와 타이틀의 사이즈를 조정할 것이다.
- Back to home 링크는 home이 false 일 때 하단에 위치한다.
- 우선수위 속성이 미리 로드된 next/image의 이미지들을 추가한다.
Update pages/index.js
마지막으로 홈페이지를 업데이트 한다.
pages/index.js를 열고 아래의 코드로 대체한다.
import Head from 'next/head' import Layout, { siteTitle } from '../components/layout' import utilStyles from '../styles/utils.module.css' export default function Home() { return ( <Layout home> <Head> <title>{siteTitle}</title> </Head> <section className={utilStyles.headingMd}> <p>[Your Self Introduction]</p> <p> (This is a sample website - you’ll be building a site like this on{' '} <a href="https://nextjs.org/learn">our Next.js tutorial</a>.) </p> </section> </Layout> ) }
그리고 Your Self Introduction을 내 소개로 변경한다.
Styling Tips
참고 https://nextjs.org/learn/basics/assets-metadata-css/assets
'공부' 카테고리의 다른 글
[Next.js] 5. Dynamic Routes (0) 2021.10.27 [Next.js] 4. Pre-rendering and Data Fetching (0) 2021.10.26 [Next.js] 2. Navigate Between Pages (0) 2021.10.22 [Next.js] 1. Create a Next.js App (0) 2021.10.21 [React] styled-components를 사용하는 이유 (0) 2021.10.20