💻오늘 배운 내용
React 프로젝트를 GitHub Page로 배포하기
리액트를 쓸 때 CRA(Create-React-App)을 설치해서 사용 하는데 이렇게 하면 깃허브 페이지가 호스팅 되지 않는다.
아마도 페이지 주소를 열면 Readme를 만나는 당혹스러움을 경험했을것..
github pages로 web을 deploy했을 때 가장 기본으로 나오는 메인 페이지는 자동으로 repository의 가장 상단에 있는 index.html을 기준으로 하는데
CRA에서 index.html은 public 폴더 안에 있기 때문 >>
그래서 React 프로젝트를 GitHub Pages로 배포하기 위해서는 몇가지 작업이 필요하다.
구글에 방법이 여러가지 있었지만 난 간편한게 가장 좋기 때문에..
비교적 제일 덜 복잡하고 빨리 할 수 있는 방법을 알아왔다.
4단계로 완료 할 수 있다.
1. 프로젝트에 gh-pages 패키지를 설치. 터미널에 입력하면 된다.
npm install gh-pages --save-dev
2. 설치가 완료 후 프로젝트에 있는 package.json 파일을 열어 중괄호 안에 "homepage" 주소를 추가
// 형식은 http://{사용자 이름}.github.io/{저장소 이름}
"homepage": "http://HyunseoY.github.io/Todo_list"
3. script 부분에 predeploy, deploy 를 추가
"scripts": {
//...
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
}
4. 터미널창을 열어 npm run deploy를 실행
npm run deploy
그럼 GitHub Repository > Settings > 좌측 사이드의 Pages > Branch를 gh-page로 설정하고 save하면
주소를 만들어준다.
시간이 쪼금 걸린다. 기껏 주소 생겨서 가봤더니 Readme가 나와도 일단은 실망하지 말자. 한 5분만 더 기다려주자..
❓발생한 이슈/고민
드디어 두렵고 두려워 미루고 미뤘던 component 분리를 시도해보았다.
컴포넌트 분리를 하면서 코드도 깔끔 & 줄일 수 있는 부분은 줄여 보면서 가독성을 높이려는 시도를 해보았다.
💡해결과정
컴포넌트는 총 5개로 분리했다.
return (
<div className="layout">
<Header />
<Form todos={todos} setTodos={setTodos} />
<List todos={todos} setTodos={setTodos} />
</div>
);
App 컴포넌트 내부를 3개의 컴포넌트로 나누고
List 컴포넌트 안에서 다시 자식 컴포넌트로 TodoCard로 분리했다.
가장 부모컴포넌트인 App에 todos의 state가 선언되어 있기 때문에
const [todos, setTodos] = useState([]);
투두를 작성하고 렌더링 하는 부분인 Form과 List 컴포넌트에는 todos state는 {todos}로, state를 조절하는 함수인 setTodos는 {setTodos}로 각각 props를 할당해 주었다.
Form 컴포넌트에서는 생성될 todo의 초기값을 설정해 주었는데,
const INIT_VALUE = { title: '', content: '', id: 1, isDone: false };
title, contents, id, isDone 키를 가진 객체를 초기값으로 두었다.
대신 이전에 state에 지정해뒀던 값은 삭제했다.
const [todos, setTodos] = useState([
{
id: 1,
title: '리액트 공부하기',
contents: '리액트 기초를 공부해 봅시다',
contents: '리액트 기초를 공부해봅시다.',
isDone: false,
},
{
id: 2,
title: '리액트 공부하기',
contents: '리액트 기초를 공부했습니다.',
isDone: true,
},
]); // 삭제함
List 컴포넌트에서는 삭제버튼과 업데이트버튼, 그리고 투두 입력시 생성될 jsx가 작성되어 있었는데,
<h2>🔥Working</h2>
{todos.map((item) => {
if (!item.isDone) {
return (
<div key={item.id}>
<h3>{item.title}</h3>
<p>{item.contents}</p>
<button onClick={() => todoDeleteClickBtn(item.id)}>
삭제하기
</button>
<button onClick={() => todoUpdateClickBtn(item.id)}>
완료
</button>
</div>
);
} else {
return null;
}
})}
투두가 작성되는 부분이 너무 길고 보기 불편하여 return 되는 부분을 따로 TodoCard로 분리해주었다.
{todos
.filter((item) => !item.isDone) // 아직 완료되지 않은 항목 필터링
.map((item) => (
<TodoCard
item={item}
key={item.id}
todoDeleteClickBtn={todoDeleteClickBtn}
todoUpdateClickBtn={todoUpdateClickBtn}
/>
))}
또한 if문 대신 filter 메서드를 사용하여 더욱 가독성을 높였다.
TodoCard 컴포넌트는 List 컴포넌트의 return부분을 가져왔더니
<button onClick={() => todoUpdateClickBtn(item.id)}>
완료
</button>
같은 자리에 완료와 취소가 다르게 들어가야 하는데 구분이 되지 않는 문제가 발생했다.
{item.isDone ? '취소' : '완료'}
그래서 삼항연산자를 이용해 isDone 값이 true라면 '취소' false라면 '완료'를 반환 할 수 있게 설정했다.
마지막으로 코드를 좀 더 간결하게 줄이고 가독성을 높이는 방법을 고민하다가
Form 컴포넌트의 인풋 입력창 관련 코드를
const [title, setTitle] = useState('');
const setTitleHandler = (event) => {
setTitle(event.target.value);
};
const [contents, setContents] = useState('');
const setContentsHandler = (event) => {
setContents(event.target.value);
};
위 코드에서
const [todo, setTodo] = useState(INIT_VALUE);
const onChangeHandler = (event) => {
const { name, value } = event.target;
setTodo({ ...todo, [name]: value });
};
아래처럼 줄였다.
title과 contents 따로 state를 설정하지말고 todo라고 하나의 state를 둔 뒤, onChangeHandler에서 event.target을
구조분해할당하여 name과 value값을 받는다.
제목
<input
className="submit-input"
name="title"
value={todo.title}
onChange={onChangeHandler}
/>
내용
<input
className="submit-input"
name="content"
value={todo.content}
onChange={onChangeHandler}
/>
그래서 input 태그에서 제목의 name은 "title"로 내용의 name은 "content"로 두개의 인풋의 value를 구분했다.
const todoAddClickBtn = (e) => {
e.preventDefault();
if (todo.title === '' || todo.content === '') return;
setTodos([...todos, { ...todo, id: todoID }]);
setTodo(INIT_VALUE);
};
초기값도 설정해뒀으니 todoAddClickBtn에 선언해 두었던 newTodo 객체도 필요 없어서 삭제해서 줄어들었다.
const todoAddClickBtn = (e) => {
e.preventDefault();
if (title === '' || contents === '') return;
const newTodos = {
id: number,
title,
contents,
};
console.log(number);
setTodos([...todos, newTodos]);
setNumber((prev) => prev + 1);
setTitle('');
setContents('');
};
또한 setTitle(''), setContents('')도 state를 삭제해주었기 때문에 setTodo(INIT_VALUE); 한 줄로 줄어들었다.
setNumber((prev) => prev + 1) 부분 또한
const [number, setNumber] = useState(3);
state로 설정해뒀던 부분을 아예 삼항연산자로 조건을 설정하여
const todoID = todos.length > 0 ? todos[todos.length - 1].id + 1 : 1;
todos.length가 0보다 크면 todos의 마지막 요소의 id 값에 +1을 해주고 아니면 1을 반환한다.
setTodos([...todos, { ...todo, id: todoID }]);
setTodos도 수정해줘야함.
🧐궁금점과 부족한 내용
항상 다른 사람들 코드 치는걸 보면서 저사람들은 어떻게 저렇게 속도가 빠르지 의문이었다.
나만 거북이얌...독수리 쪼쪼야...
타수가 빠르다고 전부는 아니니.. 조원분이 유용한 단축키들을 몇가지 알려줘서 메모해뒀다.
<유용한 단축키들>
ctrl+enter =다음줄 이동
alt 누르며 방향키 =원하는 방향으로 이동
alt+shist+방향키 =원하는 방향으로 복사
shift+delete =한줄 전체 삭제
ctrl+delete =단어 앞까지 삭제
ctrl+d =하나선택
ctrl+d (반복) =같은 단어 계속 찾아감
ctrl+alt+방향키 =여러줄 동시에 작성
alt+원하는 줄 클릭 =선택한 줄 동시에 작성
📋레퍼런스
'내일배움캠프 > Today I Learned' 카테고리의 다른 글
[TIL 2023.06.19] React Styled components / useEffect / useRef (0) | 2023.06.20 |
---|---|
[TIL 2023.06.16] 후발대 과제 / React counter app 만들기 (0) | 2023.06.19 |
[TIL 2023.06.14] React로 Todo list 만들어 보기 (0) | 2023.06.15 |
[TIL 2023.06.13] React 불변성/Rendering/counter 프로그램 만들어 보기 (1) | 2023.06.14 |
[TIL 2023.06.12] React Component / JSX / Props / State (1) | 2023.06.12 |