원티드 프리온보딩 프론트엔드 인턴십 선발 과제의 best practice를 제작한 프로젝트입니다.
성지현 | 신재일 | 윤혜영 | 조병현 | 채하은 | 홍성욱 |
---|---|---|---|---|---|
@jhsung23 | @JaeIl00 | @creamy-ocean | @ChoByungHyun | @chaehaeun | @ukssss |
초기 프로젝트 셋팅 | api 요청 및 응답 처리 | todo CRUD | 로그인 | 회원가입 | route 및 global layout |
배포 링크: https://pre-onboarding-12th-1-3.vercel.app
본 repository를 clone한 다음 프로젝트 폴더 경로에서 다음 명령어를 실행하면, 개발 환경에서의 React App을 실행하실 수 있습니다.
npm install
npm start
브라우저가 자동으로 실행되지 않는다면 http://localhost:3000
에 직접 접속해 주세요.
- 회원가입과 로그인 페이지에 이메일과 비밀번호의 유효성 검사기능을 구현해주세요
- 이메일 조건:
@
포함- 비밀번호 조건: 8자 이상
- 입력된 이메일과 비밀번호가 유효성 검사를 통과하지 못한다면 button에
disabled
속성을 부여해주세요
이름 | 의견 |
---|---|
성지현 | - 이메일: includes(’@’), 비밀번호: length ≥ 8 - 인풋 컴포넌트의 onChange 이벤트 핸들링+유효성 검사, submit 기능을 useAuthForm 커스텀 훅을 만들어 로직 분리 - useAuthForm이 리턴하는 isValidForm 상태를 버튼 컴포넌트의 disabled 속성으로 줌 |
신재일 | - 이메일 : includes(’@’) 사용 - 비밀번호: length >= 8 사용 - 버튼: debounce 사용해 유저가 입력 완료한 뒤 유효성 검사 및 disabled 변경 - 중복 로직 유틸 함수 생성, 같은 의미를 지닌 값들을 하나의 변수로 관리 |
윤혜영 | - 이메일 : includes(’@’) 사용 - 비밀번호: length >= 8 사용 - 버튼 : handleEmail, handlePassword 함수에서 email, password의 유효성을 state로 상시 체크하여 버튼 disabled 속성 변경 |
조병현 | - 이메일 : includes(’@’)사용 - 비밀번호 : length >= 8 사용 - 버튼 : email, password valid를 useState로 관리, 상시체크 후 버튼 비활성화 |
채하은 | 페이지 컴포넌트에 로직을 많이 두고싶지 않아 커스텀훅과 폼, 인풋 컴포넌트에 로직 몰빵. 구조가 복잡해져 가독성이 나빠진 것에 아쉬움이 남음 -이메일 : includes(’@’)사용 -비밀번호 : length >= 8 사용 -useDebounce 커스텀훅을 활용해 일정 시간 내에 추가 입력이 없을 경우에만 유효성 검사 |
홍성욱 | - 이메일 : includes(’@’) 사용 - 비밀번호 : length >= 8 사용 - 버튼 : useEffect 를 사용해서 email, password 의 값을 useState로 관리, 유효성 검사후 만족할 경우 버튼 활성화 |
- 공통 UI 컴포넌트 재사용
- 생산성과 유지보수성 향상하기 위함
- Input Value, Validation 상태 관리 커스텀훅 생성 및 재사용
- 중복되는 로그인과 회원가입 form이 불필요하게 나뉘는 것을 방지하기 위해 공통 UI컴포넌트 및 Input Value, Validation 상태 관리 커스텀훅 사용.
- debounce 사용하여 Validation 상태 업데이트
- 불필요한 Validation검사가 많아지는 것을 방지하기 위해 debounce 커스텀 훅 생성 및 재사용.
- 회원가입 페이지에서 버튼을 클릭 시 회원가입을 진행하고 회원가입이 정상적으로 완료되었을 시
/signin
경로로 이동해주세요- 로그인 페이지에서 버튼을 클릭 시, 로그인을 진행하고 로그인이 정상적으로 완료되었을 시
/todo
경로로 이동해주세요- 로그인 API는 로그인이 성공했을 시 Response Body에 JWT를 포함해서 응답합니다.
- 응답받은 JWT는 로컬 스토리지에 저장해주세요
이름 | 의견 |
---|---|
공통 의견 | - 로그인/회원가입 성공 시 useNavigate 사용하여 페이지 이동 - 로그인 성공 시 localStorage 에 토큰 저장 |
- 로그인/회원가입 성공 시
useNavigate
사용하여 페이지 이동 - 로그인 실패 시 alert 창 띄우기 (
window.alert(err.response.data.message)
)- 사용자에게 실시간 피드백을 제공하여 무엇이 잘못되었는지 알려주기 위해 사용
- 로그인 여부에 따른 리다이렉트 처리를 구현해주세요
- 로컬 스토리지에 토큰이 있는 상태로
/signin
또는/signup
페이지에 접속한다면/todo
경로로 리다이렉트 시켜주세요- 로컬 스토리지에 토큰이 없는 상태로
/todo
페이지에 접속한다면/signin
경로로 리다이렉트 시켜주세요
이름 | 의견 |
---|---|
성지현 | - /signin 및 /signup 주소에 접속했을 때 마운트되는 SignInPage 및 SignUpPage 컴포넌트에서 로컬스토리지 내 토큰 존재 여부를 검사함. - 토큰이 존재하면 SignInPage 및 SignUpPage는 를 리턴. - /todo 경로에 해당하는 TodoPage는 ProtectedRoute 컴포넌트로 감싸주어 토큰이 있으면 TodoPage로 이동하지 않고 SignInPage로 이동하도록 함. |
신재일 | - 각 Page Root Component에서 useEffect로 local storage 토큰 유무 확인하고 useNavigate사용하여 리다이렉트 동작 |
윤혜영 | - signin, signup, todo 파일에서 각각 useEffect로 토큰 확인 후 useNavigate로 리다이렉트 |
조병현 | - 라우터 파일에서 useParams 를 의존하는 useEffect로 token존재 여부확인, path에 맞게 useNavigate로 리다이렉트 |
채하은 | - Context API를 사용하여 인증 토큰을 관리하고, 각 페이지에서 useEffect를 활용하여 로그인 상태에 따라 리다이렉트를 구현. 과제에서는 구현하지 않았지만 protected route 기능 추가하고 싶음 |
홍성욱 | - AuthContext 를 사용하여 인증 토큰 관리, 각 페이지에서 Context 를 통해 token 값을 조회하여 유/무에 따른 리다이렉트 구현 (useNavigate) |
createBrowserRouter
로 라우터 작성ProtectedRoute
컴포넌트에 토큰 검사하는 로직을 담고 그에 따른 라우팅 처리, 즉 각 페이지의 useEffect 로직을 컴포넌트화 했다고 생각- token의 유/무만 검사하는 방식이 아닌 token이 유효한 지를 검사하는 로직을 추가 (401 status, get 방식 사용)
ProtectedRoute
컴포넌트에서 token 값을 가져와 이를 유효성 검사를 진행, 유효한 토큰일 경우 리다이렉트를 구현하기 위해 만든navigateBasedOnAuth
함수가 실행navigateBasedOnAuth
함수를 제작해 path 경로와 유효한 token 을 갖고 있는지를 나타내는 isAuthenticated 변수를 사용해 경우에 따라 리다이렉트 구현- 리다이렉트 과정에서 이전 페이지의 값이 출력되었다가 사라지는 현상이 발생하여
shouldRender
함수에서 경우에 따라 null 혹은 element 를 출력하도록 하여 이를 방지
/todo
경로에 접속하면 투두 리스트의 목록을 볼 수 있도록 해주세요- 목록에서는 TODO의 내용과 완료 여부가 표시되어야 합니다.
- TODO의 완료 여부는
<input type="checkbox" />
를 통해 표현해주세요
이름 | 의견 |
---|---|
성지현 | - /todo 경로에 접속했을 때, loader를 통해 투두 목록을 가져오는 요청 함수가 실행되도록 함. - useLoaderData로 loader가 가져온 데이터를 TodoList 컴포넌트에 내려주어 화면에 렌더링. |
신재일 | - Prop으로 전달받은 access-token이 있다면 Todo List를 서버에 요청 함 - 별도 컴포넌트로 분리한 Todo List Item을 map 메서드 활용하여 Todo List 렌더링 |
윤혜영 | - uesEffect 에서 getTodoList 함수를 이용해 API 데이터를 받아와 todos state로 관리 - todos state를 map으로 렌더링 |
조병현 | - tode 랜더링 시 useEffect 로 api 데이터 요청 후 요청값을 useState 배열로 리스트 관리 - 관리된 todo배열을 map() 을 통해 랜더 |
채하은 | - useEffect 로 페이지 마운트 시점에 서버에서 데이터를 받아와 todos 스테이트에 할당. 투두 배열을 map으로 렌더링 |
홍성욱 | - useEffect 를 사용하여 token이 존재할 경우 setState를 통해 todos state에 값을 할당, 이를 map으로 투두들을 하나씩 렌더링 |
- useEffect로 최상위 페이지(투두 페이지) 마운트 시점에 todo 데이터를 요청해 state로 관리
- 데이터를 렌더링하는 부분은 페이지 컴포넌트에서 따로 분리
- state를 투두 아이템 컴포넌트로 맵핑하여 렌더
- 리스트 페이지에 새로운 TODO를 입력할 수 있는 input과 추가 button을 만들어주세요
- 추가 button을 클릭하면 입력 input의 내용이 새로운 TODO로 추가되도록 해주세요
- TODO를 추가 한 뒤 새로고침을 해도 추가한 TODO가 목록에 보여야 합니다
이름 | 의견 |
---|---|
성지현 | - /todo 경로의 children으로 투두 추가 기능을 처리할 라우트 객체(path: /todo/create)를 추가 - 새로운 투두를 입력할 수 있는 인풋을 fetcher.Form으로 감싸고, submit 시 /todo/create에 해당하는 action 함수를 실행 - action 함수에서 투두 인풋의 밸류가 빈 문자열이라면 submit 처리를 중단(return null)하고, 빈 문자열이 아니라면 api 요청 - action이 실행되면 TodoPage의 loader가 실행되어 투두 목록 업데이트 |
신재일 | - 새롭게 추가된 Todo 내용은 useState로 관리되며 이를 서버로 전달하고 응답 성공 시 기존 Todo List에 응답 데이터 추가 |
윤혜영 | - AddTodo 컴포넌트에 onCreate 함수를 전달해 해당 함수를 통해 API 요청 - 새롭게 만들어진 todo를 todos state 마지막 요소로 업데이트 해줌 |
조병현 | - input 내용 useState 로 저장 후 버튼 클릭 시 submit이벤트로 api요청, 해당 내용을 todo배열에 업데이트 |
채하은 | - handleSubmit 함수로 post 요청. 화면 렌더링을 위해 setTodos로 투두 배열 업데이트. inputRef 로 투두 작성 완료시 인풋에 다시 포커스 가게함 |
홍성욱 | - post 를 사용하여 todo 추가 기능 구현, setTodos를 통해 값 업데이트, 업데이트가 되면 input 값을 비워 새로운 값을 입력받을 수 있도록 구현 |
- 추가 버튼 클릭 시 인풋의 value로 post 요청 전송
- 인풋이 빈 문자열인 경우 전송하지 않음
- 요청에 대한 성공 응답으로 받은 투두 데이터를 기존에 관리하던 state에 concatenating하여 렌더
- 요청 실패 시 alert 창 띄우기 (
window.alert(err.response.data.message)
)- 사용자에게 실시간 피드백을 제공하여 무엇이 잘못되었는지 알려주기 위해 사용
- TODO의 체크박스를 통해 완료 여부를 수정할 수 있도록 해주세요.
- 투두 리스트의 수정 기능을 구현해주세요
이름 | 의견 |
---|---|
성지현 | - /todo 경로의 children으로 투두 수정 기능을 처리할 라우트 객체(path: /todo/update)를 추가 - 체크박스 or 제출 버튼 클릭 시 /todo/update에 해당하는 action 함수 실행 - action 함수에서 투두 인풋의 밸류가 빈 문자열이라면 submit 처리를 중단(return null)하고, 빈 문자열이 아니라면 api 요청 - 체크박스는 optimistic update, 제출 버튼을 통한 업데이트는 loader 실행되어 업데이트 |
신재일 | - debounce 활용하여 유저가 동작하는 마지막 체크박스 값을 서버로 전달 - Todo 수정 서버 요청 성공 후 filter 메소드 활용하여 응답 값 id로 필터링하여 Todo List 기존 값과 교체 |
윤혜영 | - TodoCard에 onUpdate 함수를 전달해 체크박스 값이 변경되거나 수정 완료 버튼을 클릭하면 API 요청 - 수정 후 getTodoList 함수를 실행해 전체 todo 목록을 다시 불러옴 |
조병현 | - 체크박스 클릭 시 api 요청 후 반영 - 수정버튼 클릭 시 아이디와 내용을 인자로 전달하여 기존 내용 및 아이디를 불러오고 수정 후 제출하면 api 요청 후 반영 |
채하은 | - 체크박스 클릭 또는 확인 버튼 클릭시 onUpdate(handleUpdate) 함수 호출 - 수정하려는 투두의 id와 일치하는 투두를 todos 배열에서 찾고, 만약 수정하려는 내용이 변경되지 않았다면 함수 실행 종료 - updateTodo api 함수 호출해 데이터 업데이트. 그리고 setTodos로 투두 변경 내용 업데이트 |
홍성욱 | 체크박스 클릭시 useState를 통해 기존값과 반대값으로 변경, onUpdate 함수를 호출하여 id, todoText가 일치하는 투두의 체크박스 값을 변경 - update를 사용, id, todoText를 동일, 체크여부에 따른 값 변경 |
- 제출 버튼 클릭 또는 체크박스 클릭 시 PUT 요청 전송
- 체크박스 클릭을 통한 PUT 요청은 단시간 다중 요청을 방지하기 위하여 디바운스 처리
- 요청 성공 응답으로 받은 투두의의 id에 해당하는 데이터를 관리하고 있던 state에서 업데이트
- 요청 실패 시 alert 창 띄우기 (
window.alert(err.response.data.message)
)- 사용자에게 실시간 피드백을 제공하여 무엇이 잘못되었는지 알려주기 위해 사용
- TODO 우측에 수정버튼과 삭제 버튼을 만들어주세요
- 투두 리스트의 삭제 기능을 구현해주세요
- 투두 리스트의 TODO 우측의 삭제버튼을 누르면 해당 아이템이 삭제되도록 해주세요
이름 | 의견 |
---|---|
성지현 | - /todo 경로의 children으로 투두 삭제 기능을 처리할 라우트 객체(path: /todo/delete)를 추가 - 삭제 버튼 클릭 시 /todo/delete에 해당하는 action 함수 실행 (api 삭제 요청) - action이 실행되면 TodoPage의 loader 실행되어 업데이트 |
신재일 | - 삭제 서버 요청 성공 시 filter 메소드 활용하여 기존 Todo List에서 삭제된 Todo id 필터링하고 리스트 업데이트 |
윤혜영 | - TodoCard에 onDelete 함수를 전달해 삭제 버튼 클릭 시 API 요청 - 삭제 후 getTodoList 함수를 실행해 전체 todo 목록을 다시 불러옴 |
조병현 | - 삭제버튼 클릭 시 해당 todo 아이디값을 삭제 요청 후 filter 메소드를 통해 반영 |
채하은 | - 삭제 원하는 투두의 id 값을 가져와 데이터 통신으로 삭제하고 filter 배열 메서드를 사용해 setTodos에 목록 반영 |
홍성욱 | - 삭제버튼 클릭시 지정된 id값을 delete 방식으로 삭제후 filter 메소드를 사용해서 삭제된 값을 배열에서 제거 |
- 삭제 버튼 클릭 시 DELETE 요청 전송
- 요청 성공 시 삭제하려는 투두 아이디를 state에서 filter하여 상태 업데이트 및 렌더
- 요청 실패 시 실패 시 alert 창 띄우기 (
window.alert(err.response.data.message)
)- 사용자에게 실시간 피드백을 제공하여 무엇이 잘못되었는지 알려주기 위해 사용
root
└── src/
├── components/
│ ├── common/
│ └── domain/
├── hooks/
├── pages/
├── routes/
├── utils/
└── apis/
- [Feat] 새로운 기능을 추가
- [Fix] 버그 수정
- [Design] CSS 등 사용자 UI 디자인 변경
- [!BREAKING] CHANGE 커다란 API 변경의 경우
- [!HOTFIX] 급하게 치명적인 버그를 고쳐야 하는 경우
- [Style] 코드 포맷 변경, 세미 콜론 누락, 코드 수정이 없는 경우
- [Refactor] 프로덕션 코드 리팩토링
- [Comment] 필요한 주석 추가 및 변경
- [Docs] 문서 수정
- [Test] 테스트 코드, 리팩토링 테스트 코드 추가, Production Code(실제로 사용하는 코드) 변경 없음
- [Chore] 빌드 업무 수정, 패키지 매니저 수정, 패키지 관리자 구성 등 업데이트, Production Code 변경 없음
- [Rename] 파일 혹은 폴더명을 수정하거나 옮기는 작업만인 경우
- [Remove] 파일을 삭제하는 작업만 수행한 경우
- [Type] 타입 수정