코딩하는라민

[React+TS] Styled-components 전역 theme 적용하기 본문

개발 공부/React

[React+TS] Styled-components 전역 theme 적용하기

코딩하는라민 2024. 4. 24. 10:32
728x90
반응형

[React+TS] Styled-components theme 적용하기

 

 

이전 프로젝트들은 GlobalStyle 에 `:root` 를 이용해 테마 색상을 정의해서 사용했다. styled-components 에도 이러한 기능이 내장되어 있어 사용해보고자 한다.

 

styled-components 는 `ThemeProvide` 라는 컴포넌트를 제공한다. 이 컴포넌트는 theme 라는 props 에 theme 정보들을 넘겨받아 하위 컴포넌트들에 그 정보들을 사용할 수 있게 해준다.

ThemeProvide 이 `Context API` 기반으로 만들어졌기 때문에 가능하다. 즉, Context API 를 통해 여러 하위 컴포넌트들이 Theme 정보에 접근할 수 있도록 해주는 원리이다.

 

Theme 정보 생성하기

styled.d.ts 에 테마에 대한 타입 선언

src/styles 디렉토리에 styled.d.ts 파일을 생성해준다.

  • declare module 'styled-components' : 모듈을 확장하여 타입스크립트가 사용자가 정의한 속성을 추가할 수 있도록 해준다.
  • interface DefaultTheme : DefaultTheme 인터페이스는 styled-component 라이브러리에서 제공된다. DefaultTheme 인터페이스를 확장하여 사용자 정의 속성을 추가할 수 있다. 
import 'styled-components'

declare module 'styled-components' {
  export interface DefaultTheme {
    color: {
      main: string
      sub: string
      white: string
    }
  }
}

 

theme.ts 에 사용할 테마 정보 정의

다음으로 src/styles 디렉토리에 Theme.ts 파일을 생성한다.

  • styled-components 에서 제공하는 DefaultTheme 을 가져와 우리가 정의해줄 theme 의 타입으로 지정해준다.
  • DefaultTheme 에는 우리가 정의해준 사용자 정의 속성 color 의 실제 값들을 입력해준다.
  • colors 의 각 키 값들은 string 타입으로 `문자열` 형태로 적어준다.
import { DefaultTheme } from 'styled-components'

export const theme: DefaultTheme = {
  color: {
    main: '#3c5afe',
    sub: '#ff6e40',
    white: '#ffffff'
  }
}

 

`<ThemeProvider>` 적용하기

  • Theme 적용할 컴포넌트를 styled-components 에서 제공하는 `ThemeProvider` 컴포넌트로 감싸준다.
  • ThemeProvider 의 props 로 아까 정의해준 Theme 를 가져와서 전달해준다.
import { ThemeProvider } from 'styled-components'
import { theme } from '@/styles/theme'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <ThemeProvider theme={theme}>
    <GlobalStyle />
    <RouterProvider router={router} />
  </ThemeProvider>
)

 

이제 하위 컴포넌트들에도 props 로 theme 가 적용된다.

 

Theme 사용하기

스타일드 컴포넌트 내부에서 사용하기

스타일드 컴포넌트 안에서 `props.theme` 의 형태로 우리가 사용자 정의 속성으로 추가해준 객체들을 꺼내 사용할 수 있다.

const Container = styled.div`
  background: ${props => props.theme.color.main};
  color: ${props => props.theme.color.white};
`

 

구조분해할당으로 더 간단하게 표현하면 아래와 같다.

const Container = styled.div`
  background: ${({ theme }) => theme.color.main};
  color: ${({ theme }) => theme.color.white};
`

 

스타일드 컴포넌트 외부에서 사용하기

스타일드 컴포넌트가 아닌 외부에서 theme 를 사용하려고 하면 props.theme 가 가져와지지 않는다.

이 theme 는 스타일드 컴포넌트에서만 상속되는 속성이기 때문이다.

따라서 외부에서 theme 를 가져오기 위해서는 `useTheme` 훅이나 `withTheme` HOC 를 사용해야한다.

 

useTheme Hook 사용하기
import { useTheme } from 'styled-components'

const Header = () => {
  const theme = useTheme()
  console.log(theme.color.white) // ✅ '#ffffff'
  
  return (
    ...
  )
}

 

구조분해할당으로 더 간단하게 표현하면 아래와 같다.

import { useTheme } from 'styled-components'

const Header = () => {
  const { color } = useTheme()
  console.log(color.white) // ✅ '#ffffff'
  
  return (
    ...
  )
}

 

withTheme HOC 사용하기
import { withTheme } from 'styled-components';

const Header = ({ theme }) => (
  console.log(theme.color.white) // '#ffffff'
  return (
    ...
  )
);

// withTheme HOC를 통해 테마를 전달
const AppWithTheme = withTheme(Header);

 

Theme Props 를 함수에 전달하여 Theme Function 으로 사용하기

  • Theme 를 정의해준 뒤,
  • invertTheme 에 Theme 의 색상을 반전하여 정의해준다.
  • invertTheme 를 theme 대신 ThemeProvider 컴포넌트에 전달해주면 하위 컴포넌트에서 메인 색상과 반전된 색상을 가져와 사용할 수도 있다.
// Define our `fg` and `bg` on the theme
const theme = {
  fg: "#BF4F74",
  bg: "white"
};

// This theme swaps `fg` and `bg`
const invertTheme = ({ fg, bg }) => ({
  fg: bg,
  bg: fg
});

render(
  <ThemeProvider theme={theme}>
    <div>
      <Button>Default Theme</Button>

      <ThemeProvider theme={invertTheme}>
        <Button>Inverted Theme</Button>
      </ThemeProvider>
    </div>
  </ThemeProvider>
);

 

styled.d.ts 의 복잡해진 타입 간단하게 정의하기

theme 에 정의한 속성들이 많아지면 타입을 일일이 지정해주기 번거로워진다.

  • theme.ts 파일로 가서 우리가 정의해준 color 에 `typeof` 연산자를 통해 객체 타입으로 변환해준다.
  • 이때 color 속성은 바깥으로 따로 꺼내어 정의해준다.
import { DefaultTheme } from 'styled-components'

const color = {
  main: '#3c5afe',
  sub: '#ff6e40',
  white: '#ffffff'
}

export type ColorTypes = typeof color

export const theme: DefaultTheme = {
  color
}

 

`typeof` 연산자는 피연산자의 평가 전 자료형을 나타내는 문자열을 반환한다.

즉, `typeof` 를 통해 우리가 직접 값으로 정의한 color 객체의 타입을 자동으로 가져올 수 있는 것이다.

 

  • 이제 이렇게 추출한 타입을 styled.d.ts 로 가져와서 color 에 타입을 지정해주면 된다.
import 'styled-components'
import { ColorTypes } from '@/styles/theme'

declare module 'styled-components' {
  export interface DefaultTheme {
    color: ColorTypes
  }
}


참고

728x90
반응형