1. 리액트 앱 설치
CRA
// book-store-c라는 이름의 프로젝트를 typescript 템플릿으로 생성
npx create-react-app book-store-c --template typescript
// 실행 명령어
npm run start
Vite
// book-store-v라는 이름의 프로젝트를 react-ts 템플릿으로 생성
npm create vite@latest book-store-v -- --template react-ts
// npm 설치
npm i
// 실행 명령어
npm run dev
2. 프로젝트 폴더 구조와 기본 설정
폴더 구조
- pages- 라우트에 대응하는 페이지 컴포넌트(컨테이너)
- components- 공통 컴포넌트, 각 페이지에서 사용되는 컴포넌트
- utils- 유틸리티
- hooks- 리액트 훅
- model- 모델(타입)
- api- api 연동을 위한 fetcher 등
기본 설정
1) typecheck 명령어 추가
package.json 파일의 debug 부분에 타입 오류를 판단하는 typecheck 명령어를 추가해줬다.
// tsc: 타입스크립트 컴파일 명령어
// noEmit: 자바스크립트 컴파일 후 출력하는 과정을 비활성화
// skipLibCheck: 외부 라이브러리 타입체크를 생략하겠다.
"typecheck": "tsc --noEmit --skipLibCheck"
2) pico.css 프레임워크 사용
index.html 파일의 title 태그 윗부분에 pico css 코드를 추가했다.
<link rel="stylesheet" href="css/pico.min.css" />
3. React Fragments 사용 이유
리액트에서는 컴포넌트가 하나의 element만을 return 할 수 있다. 따라서 여러 element를 반환하고자할 때 React Fragments를 사용한다. 다음과 같이 React.Fragement 태그로 감싸준다.
class Columns extends React.Component {
render() {
return (
<React.Fragment>
<td>Hello</td>
<td>World</td>
</React.Fragment>
);
}
}
Fragments를 선언할 때 단축 문법은 다음과 같이 빈 태그처럼 작성해주면 된다. (key가 없을 때)
class Columns extends React.Component {
render() {
return (
<>
<td>Hello</td>
<td>World</td>
</>
);
}
}
fragments를 사용하면 의미 없는 div 생성을 줄일 수 있다.
4. global style 적용
프로젝트에 일관된 스타일을 적용 시키기 위해 global style을 사용한다. global style은 "user agent stylesheet"로 표시되는 브라우저의 기본 스타일이 만드는 간섭을 해결하고, 브라우저 간의 스타일 차이를 극복하기 위해 사용한다.
대표적인 css 스타일
1. 에릭마이어의 reset.css
h1, h2, h3 간의 차이가 없다.
CSS Tools: Reset CSS
CSS Tools: Reset CSS The goal of a reset stylesheet is to reduce browser inconsistencies in things like default line heights, margins and font sizes of headings, and so on. The general reasoning behind this was discussed in a May 2007 post, if you're inter
meyerweb.com
2. normalize.css
h1, h2, h3가 다른 층위로 렌더링 된다.
GitHub - necolas/normalize.css: A modern alternative to CSS resets
A modern alternative to CSS resets. Contribute to necolas/normalize.css development by creating an account on GitHub.
github.com
3. sanitize.css
normalize.css의 발전된 버전이라고 생각하면 된다. 브라우저와 기기 간의 차이를 줄이는 데 집중한다. 브라우저의 디폴트 스타일을 살리는 방향으로 만들어졌다.
GitHub - csstools/sanitize.css: A best-practices CSS foundation
A best-practices CSS foundation. Contribute to csstools/sanitize.css development by creating an account on GitHub.
github.com
sanitize.css 설치
이 프로젝트에서는 sanitize.css를 사용할 것이다.
// 설치 명령어
npm install sanitize.css --save
sanitize.css를 설치하고, index.html에 기존에 예시로 설정해뒀던 pico.css를 제거했다. 그리고 index.tsx에 sanitize.css를 import 했다.
설치가 완료되었다면 개발자 도구에서 where라는 selector가 들어와 있음을 확인할 수 있다.
styled components
styled components는 React 애플리케이션에서 스타일을 작성하기 위한 CSS-in-JS 라이브러리이다. 기존 웹사이트를 개발할 때는 HTML, CSS, JS를 각각 별도의 파일을 두는 것을 관례로 하였지만, React, Vue, Angular 등의 프레임워크, 라이브러리들이 인기를 끌면서 웹 개발의 패러다임이 바뀌게 되었다. 이들은 웹 페이지를 여러 개의 컴포넌트로 분리하고, 각 컴포넌트에 HTML, CSS, JS를 종합하는 패턴으로 구성하는 컴포넌트 기반 개발 방법을 주류로 올렸다. 따라서 CSS도 CSS 파일을 외부로 분리하는 것이 아닌 JS 파일 안에 삽입하는 CSS-in-JS 방식이 트렌드가 되었다. 따라서 styled components를 사용하면 컴포넌트 기반으로 스타일을 정의하고 관리할 수 있으며, JS 또는 TS를 사용하여 스타일을 동적으로 생성할 수 있다.
+) CSS-in-JS는 왜 필요할까?
- 전역 충돌
- 의존성 관리 어려움
- 불필요한 코드, 오버라이딩
- 압축에 유리
- 상태 공유 어려움
- 순서와 명시도
- 캡슐화
쉽게 말해 css-in-js 라이브러리를 사용하면 CSS도 쉽게 js 안에 넣어줄 수 있다. HTML + JS + CSS까지 묶어서 하나의 JS 파일 안에서 컴포넌트 단위로 개발한다.
styled-components
CSS for the <Component> Age
styled-components.com
// 설치 명령어
npm install styled-components
Header.tsx와 global.ts 파일에 styled-components를 적용했다.
import { styled } from "styled-components";
function Header() {
return (
<HeaderStyle>
<h1>Book Store</h1>
</HeaderStyle>
);
}
const HeaderStyle = styled.header`
background-color: #333;
h1 {
color: white;
}
`;
export default Header;
-> Header.tsx
import { createGlobalStyle } from "styled-components";
import "sanitize.css";
export const GlobalStyle = createGlobalStyle`
body {
padding:0;
margin:0;
}
h1 {
margin:0;
}
`;
-> global.ts
그리고 index.tsx에 GlobalStyle을 적용했다.
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { GlobalStyle } from "./style/global";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<React.StrictMode>
<GlobalStyle />
<App />
</React.StrictMode>
);
-> index.tsx
5. 테마 만들기
테마를 만드는 이유?
- UI, UX의 일관성 유지
- 유지보수가 용이
- 확장성
- 재사용성
- 사용자 정의
type ThemeName = "light" | "dark";
type ColorKey = "primary" | "background" | "secondary" | "third";
interface Theme {
name: ThemeName;
color: Record<ColorKey, string>;
}
export const light: Theme = {
name: "light",
color: {
primary: "brown",
background: "white",
secondary: "blue",
third: "green",
},
};
export const dark: Theme = {
name: "dark",
color: {
primary: "coral",
background: "midnightblue",
secondary: "darkorange",
third: "darkgreen",
},
};
-> theme.ts
Line 6) Record 타입을 사용했다.
+) Record<Keys, Type>
이 유틸리티 타입은 key, value의 제네릭 타입을 값으로 갖는다. 타입의 프로퍼티를 다른 타입에 매핑시킬 수 있다.
interface PageInfo {
title: string;
}
type Page = "home" | "about" | "contact";
const nav: Record<Page, PageInfo> = {
about: { title: "about" },
contact: { title: "contact" },
home: { title: "home" },
};
6. Theme Switcher
- 사용자는 토글 UI를 통해 웹사이트의 색상 테마를 바꿀 수 있다.
- 색상 테마는 전역 상태로 존재한다.
- 사용자가 선택한 테마는 로컬스토리지에 저장한다.