LSS 1 | _app.tsx / context / ThemeProvider

미연·2022년 10월 3일
0
// _app.tsx
import { ThemeProvider } from "styled-components"

<ThemeProvider theme={lightTheme}>
  <component />
</ThemeProvider>

가장 원초적인 _app.tsx를 뜯어 보면서 가장 의문이 들었던 ThemeProvider를 구글링해 보았다.

검색을 해보니, Styled-component에서 context API 기반으로 이루어진 전역적인 스타일 관리법인 듯했다.

ThemeProvider로 감싸진 자식 컴포넌트들은 ThemeProvider로 전달받은 themeprops로 전달받아서 사용이 가능하다고 한다.

여기서 context API 라는 것이 궁금해서 또 구글링을 해보았다.

context

context는 리액트 컴포넌트 간에 어떠한 값을 공유할 수 있게 해 주는 기능이다. 주로 context전역적(global)으로 필요한 값을 다룰 때 사용하는데, 꼭 전역적일 필요는 없다.

리액트 컴포넌트에서 props가 아닌 또 다른 방식(전역적(global))으로 컴포넌트 간에 값을 전달하는 방법

리액트에서는 일반적으로 컴포넌트에게 데이터를 전달해주어야 할 때 props를 통해 전달한다. 그런데 깊숙히 위치한 컴포넌트에 전달해야 할 경우에는, 여러 컴포넌트를 거쳐 연달아서 props를 설정해 주어야 하기 때문에 매우 불편하다.

컴포넌트를 한두 개 정도 거쳐서 props를 전달하는 거라면 괜찮지만, 여러 개 거쳐서 전달하게 된다면 너무 불편할 것이다.

  • props로 전달받은 값이 어디서 오는 건지 파악하려고 한다면?
    => 상위 컴포넌트로 타고 또 타고 거슬러 올라가야 됨
  • 특정 변수명을 다른 변수명으로 변경을 하고 싶어진다면?
    => 통일성을 맞추기 위해 여러 컴포넌트들을 수정해야 됨

이러한 문제들은 context를 사용하면 깔끔하게 해결할 수 있다.

contextcreateContext 라는 함수를 불러와서 만들 수 있다.

import { createContext } from 'react'

const MyContext = createContext()

context 객체 안에는 Provider라는 컴포넌트가 들어있다. 그리고 그 컴포넌트 간에 공유하고자 하는 값을 value라는 props로 설정하면 자식 컴포넌트들에서 해당 값에 바로 접근할 수 있다.

import { createContext } from 'react'

const MyContext = createContext()

function App() {
	return (
    	<MyContext.Provider value="Hello world">
        	<Component />
        </MyContext>
    )
}

이렇게 하면, 원하는 자식 컴포넌트에서 useContext 라는 Hook을 사용하여 context에 넣은 값에 바로 접근할 수 있다. 해당 Hook의 인자에는 createContext로 만든 MyContext를 넣는다.

import { createContext, useContext } from 'react'

function Msg() {
	const value = useContext(MyContext)
    
    return <div>{value}</div>
}

ThemeProvider

ThemeProvider 의 작동 방식은 Context API를 기반으로 이루어져 있다. ThemeProvider로 감싸진 자식 컴포넌트들은, ThemeProvider로 전달받은 Theme을 props로 전달받아서 사용이 가능하다.

import { ThemeProvider } from 'styled-components'
import Theme from '../theme.js'

const App = () => {
	return (
    	<ThemeProvider theme={ThemeProvider}>
        	<Navbar />
        	<Search />
      	</ThemeProvider>
    )
}

ThemeProvider 하위에 있는 자식 컴포넌트들은 전달받은 theme의 값들을 받아서 사용하고 있다. 전달할 theme은 내가 지정한 theme의 값들을 전달해 주면 된다.

이때 theme.js 파일은 전역적으로 스타일을 관리하는 스타일 관련 코드이며, 하위 컴포넌트에게 전달할 style 요소들을 지정해주는 작업을 거쳐야 한다.

// theme.js
const fontSizes = {
  small: calcRem(14),
  base: calcRem(16),
  lg: calcRem(18),
  xl: calcRem(20),
  xxl: calcRem(22),
  xxxl: calcRem(24),
  titleSize: calcRem(50),
};

const paddings = {
  small: calcRem(8),
  base: calcRem(10),
  lg: calcRem(12),
  xl: calcRem(14),
  xxl: calcRem(16),
  xxxl: calcRem(18),
};


const NavBar = styled.div`
	padding: ${({theme}) => theme.paddings.xl};
	font-size: ${({theme}) => theme.fontSizes.base};
`

_app.tsx

export default function MyApp({ Component, pageProps }) {
	return <Component {...pageProps} />
}
  • _app.tsx에서 렌더링 하는 값은 모든 페이지에 영향을 준다.
  • 최초로 실행되는 것은 _app.tsx이다.
  • _app.tsx은 클라이언트에서 띄우길 바라는 전체 컴포넌트 -> 공통 레이아웃임으로 최초 실행되며, 내부에 컴포넌트들을 실행한다.
  • 내부에 컴포넌트가 있다면 전부 실행하고 html의 body로 구성한다.
  • Component, pageProps를 props로 받는다.
    • Component : 요청한 페이지. get / 요청을 보냈다면, Component에는 /pages/index.js 파일이 props로 내려오게 된다.
    • pageProps는 페이지 getInitialProps를 통해 내려받은 props들을 말한다.
  • 페이지를 업데이트 하기 전에 원하는 방식으로 페이지를 업데이트 하는 통로
  • _app.tsx에서 console.log 실행시 client, server 둘 다 콘솔 찍힌다. (localhost:3000 웹과 터미널에서 둘 다 콘솔이 보인다)

다중 레이아웃

다중 레이아웃은 getLayout이라는 기능을 추가해주기만 하면 된다.

import type { ReactElement, ReactNode } from 'react';
import type { NextPage } from 'next'
import type { AppProps } from 'next/app'

// 각 페이지별 적용된 레이아웃 입력 및 반환 타입 지정
/* NextPageWithLayout으로 page의 타입(ReactElement)을 지정하면,
getLayout 속성 함수를 사용할 수 있게 된다.
(? 부분은 사용해도 되고, 안 해도 되고) */
type NextPageWithLayout = NextPage & {
	getLayout?: (page: ReactElement) => ReactNode;
}

// 각 페이지를 컴포넌트 단위로 받을 커스텀 타입
/* 기존 AppProps 타입에 layout을 추가한 것*/
type AppPropsWithLayout = AppProps & {
	Component: NextPageWithLayout
}

export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
    // 페이지 단위에서 정의한 레이아웃이 있다면, 해당 레이아웃을 적용한다.
	const getLayout = Component.getLayout ?? ((page) => page)
    
    return getLayout(<Component {...pageProps} />)

}
profile
FE Developer