2024.07.11 프로그래머스 - React 동적 UI 개발 2
React 기반 동적 UI 개발 - 2
레이아웃은 왜 필요할까
- 프로젝트의 기본적인 화면 구조를 잡기 위해
- 반복적으로 들어가야 하는 Header, Footer 를 매 화면마다 제공
- 상황과 필요에 따라 레이아웃이 변경될 수 있도록 대비 가능
스타일 적용하기
글로벌 스타일을 왜 필요할까
- 프로젝트에
일관된
스타일링을 적용 - “user agent stylesheet” 로 표시되는 브라우저의 기본 스타일이 차이를 만든다.
브라우저 간의 스타일 차이를 극복하기 위해 사용
- 글로벌 css
- 에릭마이어의 reset.css
- normalize.css
- sanitize.css
프로젝트에 sanitize.css 적용
npm 설치
1
npm install sanitize.css
index.tsx에 import
1
import "sanitize.css";
프로젝트에 styled Component 적용
- CSS-in-JS 라이브러리
- 필요한 이유
- 전역 충돌 방지
- css 의존성 관리의 어려움
- 불필요한 코드 오버라이딩
- 압축
- 상태 공유의 어려움 해결
- 순서와 명시
캡슐화
관심사의 분리
styled Component 사용
- VScode 에서는 ‘vscode-styled-component’ 확장 프로그램을 설치하면 라이브러리 코드 가독성이 좋아진다.
테마 제작
테마 사용의 이유
- UI, UX 일관성 유지
- 유지보수 용이
- 확장성 용이
- 재사용성 용이
- 사용자 정의 가능
context API 사용
상태 주입 기능이 있는 context API를 통해 Provider로 제공되는 value (context)를 하위(자식) 컴포넌트에서 공통으로 사용할 수 있게 정의한다.
theme.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
import { createContext, useEffect, useState } from "react"; import { getTheme, ThemeName } from "../style/theme"; import { ThemeProvider } from "styled-components"; import { GlobalStyle } from "../style/global"; const DEFAULT_THEME_NAME = "light"; const THEME_LOCALSTORAGE_KEY = "book_store_theme"; interface State { themeName: ThemeName; toggleTheme: () => void; } export const state: State = { themeName: "light", toggleTheme: () => {}, }; export const ThemeContext = createContext<State>(state); export const BookStoreThemeProvider = ({ children, }: { children: React.ReactNode; }) => { const [themeName, setThemeName] = useState<ThemeName>("light"); const toggleTheme = () => { setThemeName(themeName === "light" ? "dark" : "light"); localStorage.setItem( THEME_LOCALSTORAGE_KEY, themeName === "light" ? "dark" : "light" ); }; const ctx: State = { themeName, toggleTheme, }; useEffect(() => { const savedThemeName = localStorage.getItem(THEME_LOCALSTORAGE_KEY); setThemeName( savedThemeName ? (savedThemeName as ThemeName) : DEFAULT_THEME_NAME ); }, []); return ( <ThemeContext.Provider value={ctx}> <ThemeProvider theme={getTheme(ctx.themeName)}> <GlobalStyle themeName={ctx.themeName} /> {children} </ThemeProvider> </ThemeContext.Provider> ); };
localStorage 사용
localStorage는 sessionStorage와 비슷하지만, localStorage의 데이터는 만료되지 않고 sessionStorage의 데이터는 페이지 세션이 끝날 떄 사라진다.
프로젝트에서 theme
에 관한 정보 (’light’ or ‘dark’) 정보를 localStorage
에 저장하여
페이지를 refresh 했을 때도, 기존에 사용하던 theme을 그대로 사용할 수 있도록 구현하였다.
- context / theme.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
export const BookStoreThemeProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const [themeName, setThemeName] = useState<ThemeName>("light");
const toggleTheme = () => {
setThemeName(themeName === "light" ? "dark" : "light");
localStorage.setItem(
THEME_LOCALSTORAGE_KEY,
themeName === "light" ? "dark" : "light"
);
};
const ctx: State = {
themeName,
toggleTheme,
};
useEffect(() => {
const savedThemeName = localStorage.getItem(THEME_LOCALSTORAGE_KEY);
setThemeName(
savedThemeName ? (savedThemeName as ThemeName) : DEFAULT_THEME_NAME
);
}, []);
return (
<ThemeContext.Provider value={ctx}>
<ThemeProvider theme={getTheme(ctx.themeName)}>
<GlobalStyle themeName={ctx.themeName} />
{children}
</ThemeProvider>
</ThemeContext.Provider>
);
};
useEffect
훅을 사용하여, 의존성으로 는 빈 배열을 입력함으로써
컴포넌트의 첫 렌더링
시에만 해당 로직이 수행되도록 진행하였다.
- 논리
- localStorage에서 theme의 값으로 사용되기로 한 value를 들고있는 key에서 getItem으로 값을 추출
- setThemeName(state Setter) 로 1번에서 추출한 savedThemeName을 할당
소감평
context API를 생각보다 더 가볍게 쓸 수 있는 방법은 처음 알았다.
그리고 강의에서도 Redux나 zustand 같이 전역 상태 관리 라이브러리가 아닌
context API 를 사용하는 것이 조금 놀란 점이었다.
혹시나 해당 프로젝트 강의가 종료될 때, 그대로 context API를 사용한다면,
스스로 zustand 나 redux 둘 중 하나로 리팩토링 하는 방향도 생각해봐야겠다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.