게으른개발너D

Framework Overview 3 - Styles and Custom App 본문

개발/NextJS

Framework Overview 3 - Styles and Custom App

lazyhysong 2023. 11. 23. 19:29

1. CSS Modules

next.js 앱에 CSS를 추가하는 방법은 아주 많다.

한가지 방법은 이전 게시글에서 사용해봤던 방법이다. 태그에 직접적으로 style property를 넣는것.

 

이것 말고도 modules라고 불리는 걸 사용해보자.

NavBar.js와 같은 위치에 NavBar.modules.css라는 파일을 만들자.

/* NavBar.modules.css */

.nav {
  display: flex;
  justify-content: space-between;
  background-color: tomato;
}

 

NavBar.js에 해당 모듈 파일을 자바스크립트 오브젝트로서 import한다.

// NavBar.js

import styles from './NavBar.modules.css';

 

그리고나서 우리가 할 일은 NavBar.modules.css에서 정의한 클래스 이름을 NavBar.js에 추가하는 것이다.

 

예상하기로는 <nav className="nav">라고 해야할 것 같지만 아래와같이 작성해야한다.

<nav className={styles.nav} >

해당 패턴을 css module이라고 하는데 우리에게 평범한 css를 사용할 수 있도록 한다.

그리고 우리가 클래스 이름을 적을 때 해당 클래스명을 문자로 적지 않고 JS 오브젝트에서의 프로퍼티 형식으로 적는다.

이러한 접근방식의 장점은 개발자 모드를 켜서 해당 부분의 코드를 보면 알 수 있다.

 

우리가 적용한 클래스명이 나오지 않는다. 이렇게 되면 그 어떤 충돌도 만들지 않게 된다.

다른 컴포넌트에서도 nav라는 클래스명을 사용할 수 있다는 뜻이다. 클래스명을 중복 고민 없이 얼마든지 재사용할 수 있다.

 

또한 해당 모듈 내에 다른 클래스명이 존재한다면, 유저가 브라우저에서 봤을 때 모두 이상한 무작위 이름으로 작성된다.

 

이 css module을 이용해서 이전 게시글에서 다뤘던 navbar의 글자색을 변경하는 걸 구현해보자.

/* NavBar.modules.css */

.active {
  color: tomato;
}

 

그리고 NavBar.js에 아래와같이 적용시켜줄 수 있다.

// NavBar.js

import Link from 'next/link';
import { useRouter } from 'next/router';
import styles from './NavBar.modules.css';

function NavBar() {
  const router = useRouter();
  return <nav>
    <Link href="/">
      <a className={router.pathname === '/' ? styles.active : ''}>Home</a>
    </Link>
    <Link href="/about">
      <a className={router.pathname === '/about' ? styles.active : ''}>About</a>
    </Link>
  </nav>
}

export default NavBar;

 

링크가 active 되면 tomato 색이 적용된다.

 

 

만약에 Link에 추가하고 싶은 또다른 클래스 이름이 있다면 어떻게 해야할까?

예를들어 link라는 이름의 표준 클래스이름을 갖는다고 하자.

/* NavBar.modules.css */

.link {
  text-decoration: none;
}

.active {
  color: tomato;
}

 

어떻게 두 클래스 이름을 동일한 엘리먼트에 적용할 수 있는지 알아보자.

active는 조건부로 들어가고, link는 상시 들어가야한다.

 

방법은 특정 문자열을 만들어서 삽입해야한다.

// NavBar.js

import Link from 'next/link';
import { useRouter } from 'next/router';
import styles from './NavBar.modules.css';

function NavBar() {
  const router = useRouter();
  return <nav>
    <Link href="/">
      <a className={`${styles.link} ${router.pathname === '/' ? styles.active : ''}`}>
        Home
      </a>
    </Link>
    <Link href="/about">
      <a className={`${styles.link} ${router.pathname === '/about' ? styles.active : ''}`}>
        About
      </a>
    </Link>
  </nav>
}

export default NavBar;

 

또는 해당 클래스명들을 배열로 만든 후 join하면 된다.

    <Link href="/">
      <a 
        className={[
          styles.link, 
          router.pathname === '/' ? styles.active : '',
        ].join(' ')}
      >
        Home
      </a>
    </Link>
    <Link href="/about">
      <a 
        className={[
          styles.link, 
          router.pathname === '/about' ? styles.active : '',
        ].join(' ')}
      >
        About
      </a>
    </Link>

 

module 방법의 단점은 파일을 하나 더 만들어야한다는 점과 class명을 기억해야한다는 점이다.

그리고 module을 사용하는 부분에서 클래스이름 자체를 구현하는게 꽤 복잡하고 귀찮다.

 

 

 

2. Styles JSX

styles JSX는 어플리케이션에 styles를 추가하는 또다른 방식으로, Next.js 고유의 방법이다.

먼저 HTML style 태그를 연다.

style 태그 안에 jsx라는 prop을 넣어주고 태그 사이에 중괄호를 넣어준다.

중괄호 안에 백틱을 넣어주고 일반적인 css 코드를 넣어주면 된다.

 

// NavBar.js

function NavBar() {
  const router = useRouter();
  return <nav>
    <Link href="/">
      <a>Home</a>
    </Link>
    <Link href="/about">
      <a>About</a>
    </Link>
    <style jsx>{`
      nav {
        background-color: tomato;
      }
      a {
        text-decoration: none;
      }
    `}</style>
  </nav>
}

 

이제 개발자모드를 보면 위 css modules를 썼을때보다 클래스명이 더 기이해져 있음을 알 수 있다.

위 NavBar.js에서 a 태그의 스타일을 정의했는데, NavBar.js component를 사용하는 index.js에서 a 태그의 스타일을 정의하면 어떻게 될까?

 

// index.js

function Home() {
  return <div>
    <NavBar />
    <div>Hello</div>
    <style jsx>{`
      a {
        color: white;
      }
    `}</style>
  </div>
}

 

결과 화면을 보면 NavBar.js의 a태그에는 전혀 영향을 주지 못한다.

각각의 파일들에 적용시킨 style들이 독립적이라서 서로 영향을 주지 않으므로 클래스명에대해 신경을 쓸 필요가 없다. 그리고 파일을 따로 만들고 import도 해줄 필요가 없다.

 

이제 className을 적용시켜주자.

// NavBar.js

function NavBar() {
  const router = useRouter();
  return <nav>
    <Link href="/">
      <a className={router.pathname === '/' ? 'active' : ''}>Home</a>
    </Link>
    <Link href="/about">
      <a className={router.pathname === '/about' ? 'active' : ''}>About</a>
    </Link>
    <style jsx>{`
      nav {
        background-color: tomato;
      }
      a {
        text-decoration: none;
      }
      .active {
        color: yellow;
      }
    `}</style>
  </nav>
}

 

css module에서 했던 방식대로 하면 된다.

 

.active 같은 클래스명을 다른 컴포넌트에서 사용한다면? 해당 클래스명의 스코프는 오직 NavBar.js 내부 범위 안에서만 한정되므로 다른 컴포넌트에서는 스타일이 적용되지 않는다.

 

 

 

 

 

3. Custom App

어플리케이션에 global styles를 추가하는 방법을 다루자.

이를 위해서 Next.js의 아주 중요한 컨셉인 App Component와 App Page에 대해 알아야한다.

 

3.1 global

global style을 사용하기 위해선 style jsx에 global만 추가해 주면 된다.

// index.js

function Home() {
  return <div>
    <NavBar />
    <div>Hello</div>
    <style jsx global>{`
      a {
        color: white;
      }
    `}</style>
  </div>
}

 

화면을보면 index.js에서 style을 적용해주었음에도 NavBar.js에서 작성한 a 태그의 글자들이 흰색으로 변함을 알 수 있다.

하지만 about 페이지로 이동하면 글자가 변형되어있지 않다.

 

create-react-app으로 작업할 때 React 어플리케이션에서는 생각하지 않아도 되는 사항인데, next.js에서는 페이지별로 생각해봐야하는 사항이다.

about 페이지로 가면 렌더링되는 페이지는 index와 다른 페이지이며, 컴포넌트 또한 다른 컴포넌트이다.

 

 

이를 해결하기 위한 방법으로 App Component를 쓸 수 있다.

 

3.2 App Component

App Component는 Next.js가 모든 페이지를 렌더링할 수 있게 하는 어떠한 컴포넌트의 청사진이다.

기본적으로 프레임워크에 포함되어있다.

 

App component를 커스터마이징하려면 _app.js라는 파일을 만들어야한다.

무조건 해당 이름이어야한다.

 

왜냐하면 next.js는 about이나 index가 랜더링되기 전에 먼저 _app.js을 먼저 확인할 것이고, 그 다음에 about, index.js를 렌더링할 것이기 때문이다.

 

프레임워크는 우리 코드를 불러온다.

따라서 next.js는 _app.js를 불러오고, _app.js는 그 안에 작성한 함수를 자동으로 불러올 것이다.

함수는 Component와 pageProps라는 prop 두 가지를 가진다.

// _app.js

export default function App({Component, pageProps}) {
  return <Component {...pageProps} />
}

 

함수명은 마음대로 정해도 되지만 prop은 프레임워크에서 정한대로 Component와 pageProps를 넣어야한다.

 

우리가 about.js를 부르길 원한다면 next.js는 about.js 파일로 간 후 about.js에 있는 About 컴포넌트를 가지고 온 후 Component props 자리에 넣어줄 것이다.

그러고 나서 해당 컴포넌트와 함께 구현한 어떠한 것들을 return해준다.

 

위 내용처럼 반환값을 적지말고 아래처럼 작성한다면?

// _app.js

export default function App({Component, pageProps}) {
  return <div>
    <Component {...pageProps} />
    <div>hello</div>
  </div>
}

 

 

index 페이지를 가든, about 페이지를 가든 항상 아래쪽에 hello라는 문자를 볼 수 있게 된다.

 

 

이제 index, about 페이지 모두에 있던 NavBar 컴포넌트도 추가해주자

// _app.js

import NavBar from "../components/NavBar";

export default function App({Component, pageProps}) {
  return (
    <>
      <NavBar />
      <Component {...pageProps} />
    </>
  );
}

 

이제 index와 about 페이지에선 NavBar 컴포넌트가 필요없어졌다.

 

위에서 정의했던 global style 또한 넣어줄 수 있다.

 

// _app.js

import NavBar from "../components/NavBar";

export default function App({Component, pageProps}) {
  return (
    <>
      <NavBar />
      <Component {...pageProps} />
      <style jsx global>{`
        a {
          color: white;
        }
      `}</style>
    </>
  );
}

 

 

 

next.js로 앱을 만들땐 globals.css 파일을 import 할 수 없다.

만약  해당 파일을 임포트한다면 '커스텀 <App> 이외의 파일들로부터는 임포트할 수 없습니다'라는 메세지와 함께 컴파일 에러를 보게 될 것이다.

따라서 페이지나 컴포넌트 내에 css를 임포트하고싶다면 반드시 module이어야 한다.

 

하지만 컴스텀 <App> 컴포넌트가 있는 곳이라면 모든 global styles를 임포트할 수 있다.

// _app.js

import NavBar from "../components/NavBar";
import "../styles/globals.css";

export default function App({Component, pageProps}) {
  return (
    <>
      <NavBar />
      <Component {...pageProps} />
      <style jsx global>{`
        a {
          color: white;
        }
      `}</style>
    </>
  );
}

 

 

 

 

 


출처

노마드코더

https://nomadcoders.co/nextjs-fundamentals/lobby?utm_source=free_course&utm_campaign=nextjs-fundamentals&utm_medium=site

 

NextJS 시작하기 – 노마드 코더 Nomad Coders

NextJS for Beginners

nomadcoders.co

 

Comments