폰트, 공용 레이아웃, 이미지 에셋, 공용 컴포넌트 제작하기
기초공사 항목)
- 폰트 세팅
Google Web Fonts 를 이용한 프로젝트에 사용되는 폰트 세팅
- 레이아웃 세팅
모든 페이지에 반영되는 레이아웃 세팅
- 이미지 에셋 세팅
감정 이미지들을 프로젝트에서 불러와 사용할 수 있는 환경 세팅
- 공통 컴포넌트 세팅
모든 페이지에 공통으로 사용되는 버튼, 헤더 컴포넌트 세팅
1. 기초공사 1 : Font
Nanum Pen Script (Sandoll), Yeon Sung (Woowahan brothers) - Google Fonts
Open Font License : SIL 개방형 글꼴 라이선스(OFL)는 글꼴 디자인 및 언어 소프트웨어 엔지니어링 경험을 바탕으로 글꼴 및 관련 소프트웨어용으로 특별히 설계된 무료 오픈 소스 라이선스입니다. OFL 은 협업 방식으로 글꼴 및 관련 소프트웨어의 세계적인 개발, 공유 및 개선을 위한 법적 프레임워크 및 인프라를 제공합니다. 이를 통해 글꼴 작성자는 사용, 번들링, 수정 및 재배포를 허용하는 공통 라이선스에 따라 작업을 릴리스 할 수 있습니다. 공유 가치를 장려하고 특정 컴퓨팅 플랫폼이나 환경에 국한되지 않으며 다른 조직이나 개인이 사용할수 있습니다.
구글 검색창에 'google web font' 검색하여 https://fonts.google.com/ 링크 접속한다. Google Fonts 에서 검색창에 'Nanum Pen Script' 검색하여 https://fonts.google.com/specimen/Nanum+Pen+Script?query=Nanum+Pen+Script 링크에 접속한다. Select Regular 400 을 클릭. Google Fonts 검색창에 'Yeon Sung' 을 검색하여 https://fonts.google.com/specimen/Yeon+Sung?query=Yeon+Sung 링크에서 Select Regular 400 을 클릭. 이후 오른쪽 사이드 바의 Use on the web 에서 @import 체크한 뒤, style 태그의 예시 구문을 복사.
- 구문
@import url('https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&family=Yeon+Sung&display=swap');
이후 App.css 에서 최상단에 구문을 붙여넣기 한다. 다시 Google Fonts 로 돌아와서 오른쪽 사이드 바의 CSS rules to specify families 의 예시 구문을 복사.
- 구문
font-family: 'Nanum Pen Script', cursive;
font-family: 'Yeon Sung', system-ui;
이후 App.css 에서 .App 의 중괄호 안에 붙여넣기 한다.
- App.css
@import url('https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&family=Yeon+Sung&display=swap');
.App {
padding: 20px;
font-family: 'Nanum Pen Script', cursive;
font-family: 'Yeon Sung', system-ui;
}
기존 구문을 그대로 붙여넣었다가 'Yeon Sung' 이 첫 번째로 오게하도록 바꾸면 'Nanum Pen Script' 폰트가 적용된 것을 확인할 수 있다. font-family 속성이 여러개라면, 가장 아래에 위치한 속성을 따른다. 같은 라인에서는 제일 처음인, 즉 제일 왼쪽에 있는 폰트가 적용된다. 다시 'Yeon Sung' 폰트를 사용하도록 가장 아래의 위치로 옮긴다.
2. 기초공사 2 : Layout
App.css 에서 작업한다.
React 의 모든 요소들은 'root' 라는 id 를 갖는 div 요소 안에 있다. 또 그 상위 태그로는 body 태그가 있다.
- App.css
@import url('https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&family=Yeon+Sung&display=swap');
body {
background-color: #f6f6f6;
display: flex;
justify-content: center; /* body 태그 기준으로 가운데(가로축 기준 가운데) */
align-items: center; /* display: flex 일 때 세로축 기준으로 가운데 */
font-family: 'Nanum Pen Script';
min-height: 100vh; /* 현재 웹 스크린의 실제 크기의 100% 를 최소 높이로 갖겠다. */
margin: 0px;
}
/* media 쿼리 - 반응형 웹을 작성하도록 도와주는 CSS 도구 */
@media(min-width: 650px) { /* 웹 브라우저 화면이 650px 이상일 때 규칙적용 */
.App {
width: 640px;
}
}
@media(max-width: 650px) { /* 웹 브라우저 화면이 650px 이하일 때 규칙적용 */
.App {
width: 90vw; /* 지금 화면에서 90% 차지하도록 하겠다. */
}
}
#root {
background-color: white;
box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px; /* rgba 의 a 는 투명도 조절 */
}
.App {
min-height: 100vh; /* 기본적으로 화면의 100% 를 차지하는 높이 갖겠다. */
padding-left: 20px;
padding-right: 20px;
}
3. 기초공사 3 : Image Assets
5개의 감정 이미지 파일을 public\assets 폴더를 생성하여 붙여넣는다. App 컴포넌트에서 이미지들을 불러와 사용한다.
- App.js
import './App.css';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Home from './pages/Home';
import New from './pages/New';
import Edit from './pages/Edit';
import Diary from './pages/Diary';
// import RouteTest from './components/RouteTest';
function App() {
// PUBLIC_URL 작동 안할 경우 대비
const env = process.env;
env.PUBLIC_URL = env.PUBLIC_URL || '';
return (
<BrowserRouter>
<div className="App">
<h2>App.js</h2>
<img src={process.env.PUBLIC_URL + `/assets/emotion1.png`} />
<img src={process.env.PUBLIC_URL + `/assets/emotion2.png`} />
<img src={process.env.PUBLIC_URL + `/assets/emotion3.png`} />
<img src={process.env.PUBLIC_URL + `/assets/emotion4.png`} />
<img src={process.env.PUBLIC_URL + `/assets/emotion5.png`} />
<Routes>
<Route path='/' element={<Home />} />
<Route path='/new' element={<New />} />
<Route path='/edit' element={<Edit />} />
<Route path='/diary/:id' element={<Diary />} />
</Routes>
{/* <RouteTest /> */}
</div>
</BrowserRouter>
);
}
export default App;
process.env.PUBLIC_URL 은 public 폴더를 가리킨다.
4. 기초공사 4 : 공통 컴포넌트 세팅
* UI 요소 패턴화 : 어떤 기준으로 얼마 만큼 변화하게 되는가 찾아 패턴화 하기.
- 작성완료 버튼
type : POSITIVE
text : "작성완료"
onClick : ?
- 수정하기 버튼
type : DEFAULT(or undefined)
text : "수정하기"
onClick : ?
- 삭제하기 버튼
type : NEGATIVE
text : "삭제하기"
onClick : ?
버튼 컴포넌트를 src\components 경로에 MyButton.js 파일을 만들어 구현한다. 이후 App 컴포넌트에서 MyButton 컴포넌트를 불러와 사용하도록 한다.
- MyButton.js
const MyButton = ({ text, type, onClick }) => {
return (
<button className={'MyButton'} onClick={onClick}>
{text}
</button>
);
};
export default MyButton;
- App.js
import './App.css';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Home from './pages/Home';
import New from './pages/New';
import Edit from './pages/Edit';
import Diary from './pages/Diary';
// COMPONENTS
import MyButton from './components/MyButton';
function App() {
return (
<BrowserRouter>
<div className="App">
<h2>App.js</h2>
<MyButton
text={'버튼'}
onClick={() => alert('버튼 클릭')}
type={'positive'}
/>
<Routes>
<Route path='/' element={<Home />} />
<Route path='/new' element={<New />} />
<Route path='/edit' element={<Edit />} />
<Route path='/diary/:id' element={<Diary />} />
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
무난한 버튼이 만들어졌고, 잘 동작한다.
버튼에 스타일을 적용한다.
- App.css
/* MyButton */
.MyButton {
cursor: pointer;
border: none;
border-radius: 5px;
padding-top: 10px;
padding-bottom: 10px;
padding-left: 20px;
padding-right: 20px;
font-size: 18px;
white-space: nowrap; /* 버튼 안에 글자가 잘려 2줄 되지 않게 하기 */
font-family: 'Nanum Pen Script';
}
버튼을 type props 에 따라서 3가지 스타일 가지도록 만든다.
- MyButton.js
const MyButton = ({ text, type, onClick }) => {
return (
<button className={['MyButton', `MyButton_${type}`].join(' ')} onClick={onClick}>
{text}
</button>
);
};
// props 기본값 설정
MyButton.defaultProps = {
type: 'default'
};
export default MyButton;
- App.js
import './App.css';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Home from './pages/Home';
import New from './pages/New';
import Edit from './pages/Edit';
import Diary from './pages/Diary';
// COMPONENTS
import MyButton from './components/MyButton';
function App() {
return (
<BrowserRouter>
<div className="App">
<h2>App.js</h2>
<MyButton
text={'버튼'}
onClick={() => alert('버튼 클릭')}
type={'positive'}
/>
<MyButton
text={'버튼'}
onClick={() => alert('버튼 클릭')}
type={'negative'}
/>
<MyButton
text={'버튼'}
onClick={() => alert('버튼 클릭')}
/>
<Routes>
<Route path='/' element={<Home />} />
<Route path='/new' element={<New />} />
<Route path='/edit' element={<Edit />} />
<Route path='/diary/:id' element={<Diary />} />
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
className 이 type 에 따라 'MyButton MyButton_타입명' 과 같이 설정된다. 3가지 타입에 따라 설정된 class 에 대해 스타일을 적용한다.
- App.css
...
.MyButton_default {
background-color: #ececec;
color: black;
}
.MyButton_positive {
background-color: #64c964;
color: white;
}
.MyButton_negative {
background-color: #fd565f;
color: white;
}
...
새로운 타입의 버튼이 생성되었을 때 'default' 타입으로 설정되도록 처리한다.
- MyButton.js
const MyButton = ({ text, type, onClick }) => {
const btnType = ['positive', 'negative'].includes(type) ? type : 'default';
return (
<button className={['MyButton', `MyButton_${btnType}`].join(' ')} onClick={onClick}>
{text}
</button>
);
};
// props 기본값 설정
MyButton.defaultProps = {
type: 'default'
};
export default MyButton;
헤더 컴포넌트를 생성하기 전 규칙에 따라 생성할 props 를 확인한다.
- 규칙
왼쪽 자식 / 헤드 텍스트 / 오른쪽 자식
leftChild / headText / rightChild
헤더 컴포넌트를 src\components 경로에 MyHeader.js 파일을 만들어 구현한다. 이후 App 컴포넌트에서 MyHeader 컴포넌트를 불러와 사용하도록 한다.
- MyHeader.js
const MyHeader = ({ leftChild, headText, rightChild }) => {
return (
<header>
<div className="head_btn_left">
{leftChild}
</div>
<div className="head_text">
{headText}
</div>
<div className="head_btn_right">
{rightChild}
</div>
</header>
)
}
export default MyHeader;
- App.js
import './App.css';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Home from './pages/Home';
import New from './pages/New';
import Edit from './pages/Edit';
import Diary from './pages/Diary';
// COMPONENTS
import MyButton from './components/MyButton';
import MyHeader from './components/MyHeader';
function App() {
return (
<BrowserRouter>
<div className="App">
<MyHeader headText={'App'}/>
<h2>App.js</h2>
<MyButton
text={'버튼'}
onClick={() => alert('버튼 클릭')}
type={'positive'}
/>
<MyButton
text={'버튼'}
onClick={() => alert('버튼 클릭')}
type={'negative'}
/>
<MyButton
text={'버튼'}
onClick={() => alert('버튼 클릭')}
/>
<Routes>
<Route path='/' element={<Home />} />
<Route path='/new' element={<New />} />
<Route path='/edit' element={<Edit />} />
<Route path='/diary/:id' element={<Diary />} />
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
헤더에 스타일을 적용한다.
- App.css
...
/* header */
header {
padding-top: 20px;
padding-bottom: 20px;
display: flex; /* header 태그 자식요소들이 가로로 정렬됨 */
align-items: center; /* 세로축 중앙에 요소 위치시킴 */
border-bottom: 1px solid #e2e2e2;
}
header > div {
display: flex;
}
header .head_text {
width: 50%;
font-size: 25px;
justify-content: center; /* 가로축 기준 중앙정렬 */
}
header .head_btn_left {
width: 25%;
justify-content: start; /* 가로축 기준 왼쪽 */
}
header .head_btn_right {
width: 25%;
justify-content: end; /* 가로축 기준 오른쪽 */
}
header button {
font-family: 'Nanum Pen Script';
}
...
App 컴포넌트에서 왼쪽 버튼과 오른쪽 버튼까지 적용해본다.
- App.js
import './App.css';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Home from './pages/Home';
import New from './pages/New';
import Edit from './pages/Edit';
import Diary from './pages/Diary';
// COMPONENTS
import MyButton from './components/MyButton';
import MyHeader from './components/MyHeader';
function App() {
return (
<BrowserRouter>
<div className="App">
<MyHeader
headText={'App'}
leftChild={
<MyButton text={'왼쪽 버튼'} onClick={() => alert('왼쪽 클릭')} />
}
rightChild={
<MyButton text={'오른쪽 버튼'} onClick={() => alert('오른쪽 클릭')} />
}
/>
<h2>App.js</h2>
<MyButton
text={'버튼'}
onClick={() => alert('버튼 클릭')}
type={'positive'}
/>
<MyButton
text={'버튼'}
onClick={() => alert('버튼 클릭')}
type={'negative'}
/>
<MyButton
text={'버튼'}
onClick={() => alert('버튼 클릭')}
/>
<Routes>
<Route path='/' element={<Home />} />
<Route path='/new' element={<New />} />
<Route path='/edit' element={<Edit />} />
<Route path='/diary/:id' element={<Diary />} />
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
MyButton 컴포넌트 자체를 props 로 전달했는데, 이렇게 컴포넌트 자체를 props 로 전달하면 text 나 onClick 같은 요소들을 따로따로 전달할 필요가 없기 때문에, 전달되는 props 의 개수를 줄일 수 있는 좋은 방법이다.
참고강의 : https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%EB%A6%AC%EC%95%A1%ED%8A%B8#
한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지 강의 - 인프런
개념부터 독특한 프로젝트까지 함께 다뤄보며 자바스크립트와 리액트를 이 강의로 한 번에 끝내요. 학습은 짧게, 응용은 길게 17시간 분량의 All-in-one 강의!, 리액트, 한 강의로 끝장낼 수 있어요.
www.inflearn.com
'강의 실습 > 한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지' 카테고리의 다른 글
페이지 구현 - 홈 (/) (4) | 2024.02.04 |
---|---|
프로젝트 기초 공사 2 (0) | 2024.02.03 |
페이지 라우팅 2 - React Router 응용 (0) | 2024.01.31 |
페이지 라우팅 1 - React Router 기본 (2) | 2024.01.30 |
페이지 라우팅 0 - React SPA & CSR (2) | 2024.01.29 |
댓글