Moong

손쉽게 테마 바꾸기 - Sass, React 본문

Sass

손쉽게 테마 바꾸기 - Sass, React

방울토망토 2023. 4. 1. 15:46

이번엔 ReactSass를 이용하여 다크모드 / 라이트 모드 테마를 적용하는 법에 대해 이야기 해보겠습니다 🌕

 

테마를 설정하는 데는 크게 1. theme object 사용하기 / 2. 함수 사용하기 두 가지 방법이 있습니다.

우선 두 가지 방법을 설명하는 데에 앞서,

테마를 적용하는 원리에 대해 알아보겠습니다!

 

Scss에 테마 정보 알리기 - body 태그에 class를 넣었다 뺐다 하기

scss 파일에서 현재 테마가 어떤 것인지 알기 위해서class로 그 정보를 알려주어야 합니다.

따라서 상단에 존재하는 body 태그의 class에 테마 정보를 넣는 것이죠!

지금 현재 어떤 LIGHT 또는 DARK 테마인지에 대한 정보는 저는 기본 테마를 dark 모드로 할 예정이라

body 태그에 LIGHT class가 있는지, 없는지로 나타낼 수 있습니다.

기본 테마를 light 모드로 하실 분들은 body 태그에 DARK class가 있는지, 없는 지로 감지하시면 됩니다.

// dark mode - LIGHT class 존재 X (default)
<body>...</body>

// light mode - LIGHT class 존재
<body class="LIGHT">...</body>

 

테마 정보 저장 - localStorage

이전에 설정했던 테마가 계속 적용되면 더 좋겠죠!

테마에 대한 정보를 굳이 DB로 관리하지 않아도 된다면,

이전에 설정한 테마 정보를 불러오기 위하여 localStorage를 사용할 수 있습니다.

const [theme, setTheme] = useState('DARK'); // theme 정보

// 이전에 설정한 테마 정보 가져와서 저장하기
useEffect(() => {
	const _theme = localStorage.getItem('theme');
	setTheme(_theme === 'LIGHT' ? 'LIGHT' : 'DARK');
}

 

테마 바꾸기

핵심 로직은 다음과 같습니다

1. 이전에 설정했던 테마 정보를 가져오고 해당 정보를 state로 관리합니다

2. 테마 변경은 state를 변경하는 것으로 진행합니다

2. 테마 state가 바뀔 때마다 localStorage와 body의 class 정보를 갱신해줍니다

import { useState, useEffect } from 'react'; 

function App () {
  const [theme, setTheme] = useState('DARK'); // 현재 테마
  
  useEffect(()=>{
    // 테마가 바뀔 때마다 localStorage와 body class 정보 갱신
    changeThemeHandler();
  }, [theme])

  const changeThemeHandler = () => {
    // 테마가 바뀜에 따라 설정
    // 1) body class 설정
    let body = document.body;
    if (theme === 'LIGHT') {
      // light mode일 때 - 'LIGHT' class 추가
      body.classList.add('LIGHT');
    } else {
      // light mode일 때 - 'LIGHT' class 제거
      body.classList.remove('LIGHT');
    }
    
    // 2) localStorage 설정 - 테마 정보 기억하기 위해
    localStorage.setItem('theme', theme);
  }
  
  const changeTheme = () => {
    // 테마 바꾸는 함수
    setTheme(theme === 'LIGHT' ? 'DARK' : 'LIGHT');
  }
  
 useEffect(()=>{
   // 이전에 설정했던 테마 정보 가져오기
   let _theme = localStorage.getItem('theme');
   setTheme(_theme === 'LIGHT' ? 'LIGHT' : 'DARK');
 }, [])
  
  return (
    <div className="App">
      <button onClick={changeTheme}>Change Theme</button>
     </div>
   );
 }
 export default App;

 

 

자, 이제 테마를 바꾸기 위한 기초 세팅은 끝났습니다.

이제 테마에 따라 달라지는 색상, 배경 이미지 등등 세부 요소들을 설정하는 법에 대해 알려드리겠습니다!

 

 

테마 정보 설정하기 1. Object로 관리하기

테마 색상에 대한 정보를 object에 담아 관리하는 방식이 있습니다.

이 방식은 여러 테마가 존재할 때 범용성이 좋고, 색상이 일관적으로 바뀔 때 대응하기 좋습니다.

 

즉,

1. light / dark 모드일 때에 대한 색상 변경이 일관성이 있을 때

2. 또 다른 테마가 생길 가능성이 있을 때

권장드리는 방식입니다!

 

우선 theme.scss 파일에 테마에 대한 색상들을 정의해줍니다.

/* theme color 객체 정의 */
$colors:(
    dark: (
        primary-color : black,
        secondary-color : gray,
    ),
    light: (
        primary-color : white,
        secondary-color : beige,
    )
);

 

그러고 현재 테마 정보에 따라 색상을 설정해줄 수 있는 함수들을 만들어줍니다.

/* 테마 color object에서 테마 별 해당 색상을 가져오는 함수 */
@function get-color($key, $theme: 'dark') {
    @each $name, $color in map-get($colors, $theme) {
        @if($key == $name) {
            @return $color
        }
    }
}

/* 현재 테마에 따라 색상을 설정해주는 함수 */
@mixin get-color($property, $color-name) {
	// default -> dark 에서 색상 가져오기
    #{$property}: get-color($color-name);
    
    // body에 LIGHT class가 있으면 -> light 에서 색상 가져오기
    @at-root body.light & {
        #{$property}: get-color($color-name, light);
    }
}

 

 

사용 예시

이런 식으로 함수를 통해 색상을 지정해주시면 됩니다!

.background {
	@include get-color(background-color, primary-color);
    
    .title {
    	@include get-color(color, secondary-color);
    }
}

 

그럼 이런 식으로 렌더링 되겠죠!

/* light 모드일 때 */
.background {
	background-color: white;
}
.title {
    color: beige;
}

/* dark 모드일 때 */
.background {
	backround-color: black;
}
.title {
	background-color: gray;
}

 

하지만 이 방식은 테마 별로 바뀌어야 하는 색상이 많아질 수록 관리하기 어려워진다는 단점이 존재합니다.

이 단점을 보완해줄 수 있는 다른 방식에 대해 설명드리겠습니다!

 

 

테마 정보 설정하기 2. 함수로 관리하기

theme color object 없이, 바로 함수로 테마 색상을 설정할 수 있습니다.

 

즉,

1. 테마의 수가 적을 때

2. 테마별로 바뀌는 색상이 많고 불규칙적일 때

권장드리는 방식입니다!

 

해당 함수를 사용하여 light와 dark 모드일 때의 값을 넣어주면 바로 적용이 가능합니다.

@mixin theme($property, $light-value, $dark-value, $transition-duration: 0.3s) {
    transition : $transition-duration; // 부드러운 theme 전환을 위해
    #{$property}: $dark-value; // default -> dark color 지정
    
    // body에 LIGHT class가 있으면 -> light color 지정
    @at-root body.LIGHT & {
        #{$property}: $light-value;
    }
}

 

사용 예시

앞서 설명드린 예시 상황에서 2번째 방법을 통해 적용해 보았습니다.

.background {
	@include theme(background-color, white, black);
    
    .title {
    	@include theme(color, beige, gray);
    }
}

 

 

최종 예시

테마 정보 설정 2번 방법 - 함수로 관리하기 방식을 사용하여 간단한 light / dark 테마 바꾸는 예제를 만들어보았습니다.

theme 함수에 background-image property를 사용하여 light, dark 모드일 때의 value url을 각각 다르게 지정하면 테마 별로 다른 이미지를 보여주는 것도 쉽게 구현이 가능해요 :)

라이트 모드와 다크 모드일 때 해와 달이 동쪽에서 뜨고 지는 것을 애니메이션으로 표현해 보았어요! ☀️🌙

 

 

소스코드는 하단 codepen 예제를 통해 확인 가능합니다.

See the Pen Theme Changer - React by Moonchaeyeon (@moonchaeyeon) on CodePen.

 

저는 실제 서비스에 테마 바꾸는 것을 구현할 때

테마별로 일관성 있게 변화하는 색상들은 object로 관리하고, 불규칙하게 바뀌는 색상들은 2번의 함수를 사용하면서

테마 관리법 1번과 2번을 적절하게 섞어서 사용하고 있습니다! 😀

제 글이 도움이 되었길 바라며

이상 마치겠습니다 🍅

'Sass' 카테고리의 다른 글

완벽한 반응형 웹 만들기 - 특정 너비 이하부터 적용  (0) 2023.03.31
Comments