개발일지
Next.js로 블로그 만들기 - 다크모드 적용 본문
MDX 적용이 궁금하신 분은 이 포스트에 정리했습니다.
다른 서비스들 두고 왜..?
저는 티스토리 블로그를 사용하고 있었습니다. 블로그를 사용하면서, 제 마음에 드는 블로그 스킨이 없었고, 프론트엔드 개발자로 시작한 이상 제 블로그를 직접 만들어서 사용하는 것도 의미가 있겠다란 생각을 했습니다.
그리고 제가 사용하면서 제가 필요한 기능들은 개발하면서 추가해나가면서 블로그를 운영하면 실력도 늘고, 좋은 경험이 될 것이라 생각했습니다.
사용한 스택
사용한 스택은 다음과 같습니다. Typescript, Next.js, Styled-Components, MDX, Zustand 입니다.
구현한 (구현하지 못한) 기능들
제가 처음부터 있었으면 하는 기능들은 다음과 같습니다.
- 마크다운으로 블로그 포스트 작성
- 다크모드 구현
- 댓글 혹은 방명록 페이지
이 중 MDX를 활용하여 블로그 포스트 및 메인 화면을 만들 수 있었고, 다크모드도 벨로퍼트님의 게시글를 통해 구현할 수 있었습니다.
다크모드
다크모드를 구현할 때, 다양한 선택지가 있었습니다. 제가 사용하는 Styled-Components 의 ThemeProvider 와, 제가 즐겨 찾아보는 velog의 운영자이신 벨로퍼트님의 게시글을 보고 따라할 수 있었습니다. 개인적으로 구현하는 방식에 있어서는 ThemeProvider를 사용하는 것이 조금 더 편하겠지만, 프론트엔드 개발자들에겐 아이돌인 벨로퍼트님의 방식을 한 번 따라해 보고 싶었습니다. 거기다 velog에는 다크모드가 처음부터 있었던게 아닌, 업데이트된 기능이어서 운영중인 서비스에 현업자는 어떻게 새로운 기능을 추가했을까? 라는 궁금증이 생겨 벨로퍼트님의 방식을 따라하기로 했습니다.
GlobalStyles 설정
먼저 GlobalStyles파일에 설정할 것들이 있습니다. 바로 기본적으로 css에서 사용할 변수와 그 값을 설정하는 건데요.
body {
--text: #ffffff;
}
이런식으로 Styled-Components 의 GlobalStyles 에서 사용할 css의 변수명을 지정해 주는겁니다. 이렇게 지정을 할 때, themes.ts파일을 만들어 세부 설정을 해줍니다.
export type Colors = {
key: value
}
type Theme = 'light' | 'dark'
type ColorsKey = keyof Colors
const themeVariableSets: Record<Theme, Colors> = {
dark: {
text: '#d2d3d7',
element: '#292927',
...
},
light: {
text: '#4d5156',
element: '#ededeb',
...
},
}
const buildCssVariables = (variables: Colors) => {
const keys = Object.keys(variables) as (keyof Colors)[]
return keys.reduce((acc, key) => acc.concat(`--${key.replace(/_/g, '-')}: ${variables[key]};`, '\n'), '')
}
export const themes = {
light: buildCssVariables(themeVariableSets.light),
dark: buildCssVariables(themeVariableSets.dark),
}
위의 코드를 보시면, 다크모드에서의 값과 라이트모드에서의 값(색상)은 다르지만, 공통된 키값을 가지고 있습니다. 이것들을 themeVariableSets 에 지정하고, buildCssVariables 를 이용해서 "--키값: "#AAAAAA"이런식으로 하나하나 변경하여 최종적으로는
export const themes = {
light: {
`--text: '#d2d3d7';
--element: '#292927';`
},
dark: {
`--text: '#d2d3d7';
--element: '#292927';`
}
}
이런 값이 나오게 됩니다. 이것들을 css파일이나 GlobalStyles 와 같은 곳에 넣게 되면 여기에 쓰인 값들을 가져와서 사용할 수 있는 것이죠.
const GlobalStyles = createGlobalStyle`
...
body[data-theme='light'] {
${themes.light}
}
body[data-theme='dark'] {
${themes.dark}
}
...
`
이제는 이 지정한 값들을 각각의 컴포넌트에서 불러 올 수 있도록 만들어야 하는데요, 벨로퍼트님은 여기서 추가적인 과정을 거쳐 값을 사용할 수 있도록 만들었습니다.
...
const cssVar = (name: string) => `var(--${name.replace(/_/g, '-')})`
const variableKeys = Object.keys(themeVariableSets.light) as ColorsKey[]
export const themedPalette: Record<ColorsKey, string> = variableKeys.reduce((acc, current) => {
acc[current] = cssVar(current)
return acc
}, {} as ThemedPalette)
여기서 cssVar를 통해 themes의 키값들을 뽑아 낼 수 있게 만들고, themedPalette를 이용해서 이 키값들에 맞는 값을 가져올 수 있도록 만드셨습니다.
themedPalette 의 코드를 보면, reduce함수의 기본값을 {} as ThemedPalette 로 만들어 current값이 key가 되고, css에 설정한 변수들을 가져와 사용할 수 있도록 구현하셨습니다.
이 외에 사용자의 현재 환경(사용자가 사용중인 모드)을 찾고 그 값에 따라 화면을 보여주고, 다시 방문하는 사용자의 환경값을 기억하여 모드를 유지할 수 있도록 구현해야 합니다.
먼저, 두 가지 경우를 생각해야 하는데요. 처음은 사용자가 처음 이 블로그를 방문했을 경우와 두번째로 사용자가 재방문 했을 경우입니다.
첫 방문의 경우에는 사용자가 현재 사용하고 있는 테마와 같은 모드를 보여줘야 합니다. 이를 위해 prefers-color-scheme 을 사용합니다.
prefers-color-scheme CSS 미디어 특성은 사용자의 시스템이 라이트 테마나 다크 테마를 사용하는지 탐지하는 데에 사용됩니다. - 출처: MDN
이와 같은 기능을 활용해서, 사용자가 다크모드인 경우 사용할 themes를 설정해줍니다.
...
@media (prefers-color-scheme: dark) {
body {
${themes.dark}
}
}
이렇게 설정해놓으면 사용자의 테마에 따라 제가 설정한 테마의 색상으로 사용자에게 보여줄 수 있습니다.
_documets 파일 설정
Next.js를 사용하면서 사용자가 첫 화면에 진입했을 때 저장된 쿠키의 값을 읽고 어떤 테마를 보여주기 위해서는, _document 파일을 수정해야 합니다.
script 태그 내 dangerouslySetInnerHTML를 사용하여 쿠키의 값을 확인하고, 이 값을 활용해 어떤 테마를 보여줄지 정하는 코드를 입력해주면 됩니다.
여기서 추가적으로 dataset이라는 기능을 사용할 건데요. 이 기능은 HTML의 속성으로 테마를 식별하기 위해 사용됩니다. 쿠키에 저장된 값을 이용하여
css에서 어떤 테마를 이용해야 할지 알려줄 수 있습니다.
<Html lang="ko">
<script dangerouslySetInnerHTML={{
__html: `const cookie = document.cookie.split('=')[1];
document.body.dataset.theme = cookie;`,
}}/>
</Html>
이런식으로 저장된 쿠키를 확인하고, 사용자가 마지막에 어떤 테마를 사용했는지 알 수 있습니다.
마무리하면서..
아직 블로그에는 추가해야 할 기능과, 추가하고 싶은 기능이 남아있습니다. 또한 글에도 적지는 않았지만, MDX를 사용하는 것이라던가,
이미지를 어떻게 해야 사용자에게 이쁘게 보일지와 같은 내용은 길이 길어질 것 같아 따로 서술하진 않았습니다.
아무래도 다크모드 구현에 있어 벨로퍼트님의 코드를 보면서 많은 참고가 되었는데요. 왜 다른 사람의 코드를 보면서 학습하라고 하는지 알겠더라구요. 제가 생각하지 못한 방향으로 코드를 작성하셔서, 앞으로 코드를 작성하는데 있어 구조적으로, 단계적으로 차근차근 튼튼하게 코드를 작성해야겠다는 생각이 들었습니다.
또한, 블로그를 만들면서 많은 분들의 블로그와 지식을 참고하여 만들었습니다.
제 블로그 놀러와주세요!
새 블로그 : https://seobe-dev.vercel.app/
'개발일지' 카테고리의 다른 글
GitHub - 실수로 올리면 안될 것을 올렸을 때 (1) | 2024.10.16 |
---|---|
Next.js로 블로그 만들기 - MDX (0) | 2023.06.20 |
내가 만든 블로그에 SEO 적용하기 (2) | 2023.06.08 |
[Typescript + Redux-Toolkit] createAsyncThunk에서의 에러 핸들링 (0) | 2022.11.04 |
[Emotion + Typescript] styled방식을 이용하여 스타일링하기 (0) | 2022.10.28 |