기존의 문제점
개선 이후
(해당 사진은 2주차 과제의 이슈이지만 원활한 설명을 위해 여기에 넣었다.)
기존의 문제점
(놀랍게도 커밋 하나만으로 issue하나를 끝냈다.)
(커밋메시지가 아예 없다.)
개선 이후
기존의 문제점
개선 이후
처음에 npm install husky --save-dev
를 통해 husky를 devDependencies에 설치한다.
이후 npx husky install
을 통해 .husky 폴더를 만들어 준다.
package.json에서 script를 설정해준다.
postinstall은 npm install후에 자동으로 husky install
이 될 수 있도록 하는 설정이다.
// package.json
{
"scripts": {
"postinstall": "husky install",
"format": "prettier --cache --write .",
"lint": "eslint --cache .",
},
}
add pre-commit, pre-push hook
npx husky add .husky/pre-commit "npm run format"
npx husky add .husky/pre-push "npm run lint"
이렇게 하면
(파일 내부에 명령어가 생긴다.)
이로써 git clone하고 npm install한 사람들은 자동으로 git hook설정이 끝나게되고 commit 혹은 push를 할때마다 모든 팀원들이 같은 설정으로 코드가 정리되게 된다.
// package.json
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"prettier --cache --write",
"eslint --cache --fix --max-warnings=0"
]
}
// .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
아키텍처란 구조화 된 옷장과 비슷한 겁니다. 처음 개발 할 때에는 규칙없이 그냥 코드를 만들다 보면 덩치가 커지는 순간 불편함이 생기고 정리가 안 되는 시점이 생깁니다. 그러니 처음부터 특정한 규칙을 만들어서 개발을 하는게 좋다는 것을 알게 되고 규칙을 하나씩 만들어가며 개발을 하다보면 이것이 반복이 되어 하나의 특정 패턴이 만들어집니다. 이러한 패턴들을 모두가 이해하고 따를 수 있도록 하는 구조를 아키텍쳐라고 부릅니다.
(출처: https://velog.io/@teo/프론트엔드에서-MV-아키텍쳐란-무엇인가요#1-아키텍쳐란-무엇일까요)
📦src
┣ 📂apis
┃ ┣ 📜authApi.js
┃ ┗ 📜todoApi.js
┣ 📂components
┃ ┗ 📂TodoList
┃ ┃ ┣ 📂TodoItem
┃ ┃ ┃ ┣ 📜index.jsx
┃ ┃ ┃ ┗ 📜useTodoItem.jsx
┃ ┃ ┗ 📜index.jsx
┣ 📂constants
┣ 📂hooks
┃ ┣ 📜useAuthForm.jsx
┃ ┣ 📜useInput.jsx
┃ ┗ 📜useMovePage.jsx
┣ 📂pages
┃ ┣ 📂Error
┃ ┣ 📂Root
┃ ┣ 📂SignIn
┃ ┣ 📂SignUp
┃ ┣ 📂Todo
┃ ┃ ┣ 📜index.jsx
┃ ┃ ┗ 📜useTodo.jsx
┃ ┗ 📜index.js
┣ 📂router
┃ ┣ 📂loaders
┃ ┃ ┣ 📜authLoader.js
┃ ┃ ┣ 📜index.js
┃ ┃ ┣ 📜rootLoader.js
┃ ┃ ┗ 📜todoLoader.js
┃ ┗ 📜index.js
┣ 📂utils
┃ ┣ 📜index.js
┃ ┣ 📜storage.js
┃ ┗ 📜validator.js
┣ 📜App.jsx
┗ 📜index.js
export const Todo = () => {
...
const [todos, setTodos] = useState(loadedTodoData);
const [isUpdated, setIsUpdated] = useState(false);
const refetchTodos = async () => {
const refetchedTodos = await getTodos();
setTodos(refetchedTodos);
setIsUpdated(false);
};
useEffect(() => {
refetchTodos();
}, [isUpdated]);
const handleCreateTodo = async (e) => {
...
await createTodo({ todo: todoValue });
setIsUpdated(true);
resetTodoInput();
};
refetchTodos
함수를 구현했다.isUpdated
를 플래그 상태 변수로 두고, 이것을 useEffect
의 dependency 배열에 넣었다.isUpdated
의 상태가 변경되면 refetchTodos 함수가 다시 호출되고, 새롭게 받아온 투두 데이터를 todos
상태 변수에 저장한다.// components/TodoItem/hook.jsx
const handleUpdateTodo = async () => {
await updateTodo(id, { todo: todoValue, isCompleted });
setIsUpdated(true);
setUpdatedTodoId(id);
};
useEffect(() => {
if (!isUpdated && updatedTodoId === id) {
setIsEdit(false);
setUpdatedTodoId(null);
}
}, [isUpdated]);
handleUpdateTodo
함수가 호출된다.만약 useEffect를 쓰지 않고 handleUpdateTodo 함수에서 바로 setIsEdit(false)
를 호출한다면, 아직 서버에서 데이터를 받아 렌더링을 하기도 전에 input이 숨겨진다.
즉 수정 전의 todo가 한 번 보인 후, 렌더링이 되는 것이다.
- 이것은 사용자에게 어색한 경험을 줄 수 있다.
(서버와의 비동기 통신이 이루어지기 전에 input이 숨겨지는 경우)
(useEffect를 이용해서 서버와의 비동기 통신이 이루어지기 나서 input이 숨겨지는 경우)
loader
와 redirect
를 활용하여 로그인 여부에 따른 리다이렉트를 처리한다.useLoaderData
훅을 통해 사용할 수 있다.loader
함수는 어떤 값을 반환해야 에러가 발생하지 않으므로 이 경우 null
을 반환해 준다.import { PATH_ROUTE } from 'constants';
import { SignIn, SignUp, Todo, Error, Root } from 'pages';
import { createBrowserRouter } from 'react-router-dom';
import { authLoader, rootLoader, todoLoader } from './loaders';
const routes = [
{
path: PATH_ROUTE.root,
element: <Root />,
errorElement: <Error />,
loader: rootLoader,
},
{
path: PATH_ROUTE.signIn,
element: <SignIn />,
errorElement: <Error />,
loader: authLoader,
},
{
path: PATH_ROUTE.signUp,
element: <SignUp />,
errorElement: <Error />,
loader: authLoader,
},
{
path: PATH_ROUTE.todo,
element: <Todo />,
errorElement: <Error />,
loader: todoLoader,
},
];
export const router = createBrowserRouter(routes, {
basename: '/pre-onboarding-9th-1-5',
})