- Assignment 1 - Login / Signup
- 로그인 / 회원가입 페이지 UI 개발 - React Hook Form
- React-query를 이용한 API 연동
- Assignment 2 - Todo List
- Todo 페이지 UI 개발 - Mui, Modal
- API 정리 - Axios Instance, React-query Hook(CRUD)
- Refactoring 1
- 코드리뷰 반영 - React Hook Form, 로그인 후 액션
- Global Alert Message - Zustand
- Route - 메인 HomePage 구현
- Refactoring 2
- Todo content - 줄바꿈 허용을 위한 TextArea(+ react hook form) 적용
- PrivateRoute, Suspense를 통한 ErrorBoundary 설정
- 로그아웃 기능 추가
로그인에 성공 후, Todo 페이지가 바로 rendering 되지 않고 Error 페이지로 이동하고, Refresh 후 정상동작
비동기 처리 시(loading, 랜더링할 데이터가 도착하기 이전) 보여줄 UI가 없어서 ErrorElement 컴포넌트가 보이게 됨
React Router caught the following error during render Error:
A component suspended while responding to synchronous input.
This will cause the UI to be replaced with a loading indicator.
To fix, updates that suspend should be wrapped with startTransition.
- 비동기 상태 처리 관련 코드 - if (isGetTodoListLoading) return ; 제거
- Todo 컴포넌트를 사용하는 APP 단에서 로딩 상태 처리
- 언로드된 페이지에서 데이터를 처음 로드하는 경우
{
path: TODO_URL,
element: (
<Suspense fallback={<Loading />}>
<PrivateRoute>
<Todo />
</PrivateRoute>
</Suspense>
),
},
에러 상세 내용 - To fix, updates that suspend should be wrapped with startTransition.에서 startTransition에 대해 추가 정보
- 비동기적인 작업을 수행하면서 UI 렌더링을 중단하지 않도록 해줌
- 예시
- Fetch Data 라는 버튼을 눌러도 아래에 있는 기존 UI에 대한 반응성은 유지가 됨
- 이미 로드된 페이지에서 새 데이터를 로드하는 경우
import { startTransition, useState } from 'react';
function App() {
const [data, setData] = useState(null);
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
setData(result);
};
const handleButtonClick = () => {
startTransition(() => {
fetchData();
});
};
return (
<div>
<button onClick={handleButtonClick}>Fetch Data</button>
<div>
{data ? (
<ul>
{data.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
) : (
<p>No data yet.</p>
)}
</div>
</div>
);
}
export default App;
yarn install
yarn start- 제공해드리는 API Repository를 활용하여 가이드에 따라
Todo App을 작성, 본인의 github에Public으로 올려주세요. README.md를 꼭 작성해 주세요. 본인에 대한 소개나 프로젝트 소개 등 자유롭게 작성해주시면 됩니다.- 반드시 함수 컴포넌트로 개발해주세요. (React Hooks)
- /auth 경로에 로그인 / 회원가입 기능을 개발합니다
- 로그인, 회원가입을 별도의 경로로 분리해도 무방합니다
- 최소한 이메일, 비밀번호 input, 제출 button을 갖도록 구성해주세요
- 이메일과 비밀번호의 유효성을 확인합니다
- 이메일 조건 : 최소
@,.포함 - 비밀번호 조건 : 8자 이상 입력
- 이메일과 비밀번호가 모두 입력되어 있고, 조건을 만족해야 제출 버튼이 활성화 되도록 해주세요
- 이메일 조건 : 최소
- 로그인 API를 호출하고, 올바른 응답을 받았을 때 루트 경로로 이동시켜주세요
- 응답으로 받은 토큰은 로컬 스토리지에 저장해주세요
- 다음 번에 로그인 시 토큰이 존재한다면 루트 경로로 리다이렉트 시켜주세요
- 어떤 경우든 토큰이 유효하지 않다면 사용자에게 알리고 로그인 페이지로 리다이렉트 시켜주세요
-
Todo List API를 호출하여 Todo List CRUD 기능을 구현해주세요
- 목록 / 상세 영역으로 나누어 구현해주세요
- Todo 목록을 볼 수 있습니다.
- Todo 추가 버튼을 클릭하면 할 일이 추가 됩니다.
- Todo 수정 버튼을 클릭하면 수정 모드를 활성화하고, 수정 내용을 제출하거나 취소할 수 있습니다.
- Todo 삭제 버튼을 클릭하면 해당 Todo를 삭제할 수 있습니다.
-
한 화면 내에서 Todo List와 개별 Todo의 상세를 확인할 수 있도록 해주세요.
- 새로고침을 했을 때 현재 상태가 유지되어야 합니다.
- 개별 Todo를 조회 순서에 따라 페이지 뒤로가기를 통하여 조회할 수 있도록 해주세요.
-
한 페이지 내에서 새로고침 없이 데이터가 정합성을 갖추도록 구현해주세요
- 수정되는 Todo의 내용이 목록에서도 실시간으로 반영되어야 합니다
-
로컬 서버를 실행했을 때 생성되는
db/db.json이 DB 역할을 하게 됩니다. 해당 파일을 삭제하면 DB는 초기화 됩니다. -
로그인 / 회원 가입 기능은 유저를 DB에 추가하고 JWT 토큰을 응답으로 돌려줄 뿐, 실제 유저별로 Todo 목록을 관계 지어 관리하지는 않습니다. (모든 유저가 하나의 Todo를 가짐)
-
로그아웃은 클라이언트 단에서 localStorage에 저장된 token을 삭제하는 방식으로 간단히 구현해주세요.
- GET
/todos - Headers
- Authorization: login token
{
"data": [
{
"title": "hi",
"content": "hello",
"id": "z3FGrcRL55qDCFnP4KRtn",
"createdAt": "2022-07-24T14:15:55.537Z",
"updatedAt": "2022-07-24T14:15:55.537Z"
},
{
"title": "hi",
"content": "hello",
"id": "z3FGrcRL55qDCFnP4KRtn",
"createdAt": "2022-07-24T14:15:55.537Z",
"updatedAt": "2022-07-24T14:15:55.537Z"
}
]
}- GET
/todos/:id - Headers
- Authorization: login token
{
"data": {
"title": "hi",
"content": "hello",
"id": "z3FGrcRL55qDCFnP4KRtn",
"createdAt": "2022-07-24T14:15:55.537Z",
"updatedAt": "2022-07-24T14:15:55.537Z"
}
}- POST
/todos - Parameter
- title: string
- content: string
- Headers
- Authorization: login token
{
"data": {
"title": "hi",
"content": "hello",
"id": "z3FGrcRL55qDCFnP4KRtn",
"createdAt": "2022-07-24T14:15:55.537Z",
"updatedAt": "2022-07-24T14:15:55.537Z"
}
}- PUT
/todos/:id - Parameter
- title: string
- content: string
- Headers
- Authorization: login token
{
"data": {
"title": "제목 변경",
"content": "내용 변경",
"id": "RMfi3XyOKoI5zd0A_bsPL",
"createdAt": "2022-07-24T14:25:48.627Z",
"updatedAt": "2022-07-24T14:25:48.627Z"
}
}- DELETE
/todos/:id - Headers
- Authorization: login token
{
"data": null
}- POST
/users/login - Parameter
- email: string
- password: string
{
"message": "성공적으로 로그인 했습니다",
"token": "eyJhbGciOiJIUzI1NiJ9.YXNkZkBhc2RmYXNkZi5jb20.h-oLZnV0pCeNKa_AM3ilQzerD2Uj7bKUn1xDft5DzOk"
}- POST
/users/create - Parameter
- email: string
- password: string
{
"message": "계정이 성공적으로 생성되었습니다",
"token": "eyJhbGciOiJIUzI1NiJ9.YXNkZkBhc2RmYXNkZi5jb20.h-oLZnV0pCeNKa_AM3ilQzerD2Uj7bKUn1xDft5DzOk"
}