{"id":18562674,"url":"https://github.com/hjpark625/new-todos","last_synced_at":"2026-04-11T07:01:54.378Z","repository":{"id":153534134,"uuid":"535571291","full_name":"hjpark625/new-todos","owner":"hjpark625","description":"firebase와 개인개발 서버를 활용한 TODO프로젝트","archived":false,"fork":false,"pushed_at":"2023-05-24T08:44:21.000Z","size":3427,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"firebase","last_synced_at":"2025-08-26T03:45:14.438Z","etag":null,"topics":["firebase","firebase-realtime-database","firestore-database","react","redux","redux-saga","typescript"],"latest_commit_sha":null,"homepage":"https://hjpark625.github.io/new-todos/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hjpark625.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-09-12T08:17:25.000Z","updated_at":"2023-04-21T00:14:04.000Z","dependencies_parsed_at":"2023-06-09T07:00:10.325Z","dependency_job_id":null,"html_url":"https://github.com/hjpark625/new-todos","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/hjpark625/new-todos","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hjpark625%2Fnew-todos","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hjpark625%2Fnew-todos/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hjpark625%2Fnew-todos/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hjpark625%2Fnew-todos/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hjpark625","download_url":"https://codeload.github.com/hjpark625/new-todos/tar.gz/refs/heads/firebase","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hjpark625%2Fnew-todos/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31671630,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-10T17:19:37.612Z","status":"online","status_checked_at":"2026-04-11T02:00:05.776Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["firebase","firebase-realtime-database","firestore-database","react","redux","redux-saga","typescript"],"created_at":"2024-11-06T22:10:31.376Z","updated_at":"2026-04-11T07:01:54.340Z","avatar_url":"https://github.com/hjpark625.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Todos APP with Firebase\n\n## 💡 프로젝트 정보\n\n1. 프로젝트 명 : Todos App with Firebase\n2. 프로젝트 기간 : 2022. 9. 12. ~ 2022. 9. 19.\n3. 프로젝트 참가 인원: 단독프로젝트\n\n\u003cbr /\u003e\n\n---\n\n\u003cbr/\u003e\n\n## 🌈 실행 방법\n\n    $ git clone https://github.com/hjpark625/new-todos.git\n    $ cd new-todos\n    $ yarn install\n    $ yarn start\n\n\u003cbr /\u003e\n\n---\n\n## ⭐ 배포 링크\n\n\u003cbr/\u003e\n\n\u003e https://hjpark625.github.io/new-todos/\n\n\u003cbr/\u003e\n\n---\n\n## 🔐 배포 테스트 계정\n\n\u003cbr /\u003e\n\n\u003e id(email): tester@naver.com  \n\u003e password: 1q2w3e4r!\n\n\u003cbr/\u003e\n\n---\n\n## 📚 기술스택\n\n\u003cbr /\u003e\n\n![react](https://img.shields.io/badge/react-18.2.0-61DAFB?logo=react)\n![typescript](https://img.shields.io/badge/typescript-4.8.3-3178C6?logo=typescript)\n![styledComponents](https://img.shields.io/badge/styled--components-5.3.5-DB7093?logo=styledcomponents)\n![react-router-dom](https://img.shields.io/badge/react--router--dom-6.3.0-blue?logo=react-router)  \n![firebase](https://img.shields.io/badge/firebase-9.9.4-E1C537?logo=firebase)\n![redux](https://img.shields.io/badge/redux-4.2.0-pink?logo=redux)\n![react-redux](https://img.shields.io/badge/react--redux-8.0.2-pink?logo=redux)\n\n- **선정 이유**\n\n  - _TypeScript_\n    - 정적 타입 지원하므로 컴파일 단계에서 오류를 사전에 포착할 수 있으며 이를 통해 미리 디버깅이 가능함\n  - _Styled-Components_\n    - CSS-in-JS는 짧은 길이의 유니크한 클래스를 자동적으로 생성하기에 코드 경량화에 효과적\n    - 컴포넌트 기반 개발 방법에 적합하고 가장 많이 사용되는 CSS-in-JS 라이브러리\n    - JS 상수와 함수를 쉽게 공유하여 props를 활용한 조건부 렌더링에 용이\n    - 컴포넌트화하여 재활용 가능\n  - _Firebase_\n    - 백엔드의 부재로 인한 Todo리스트를 저장할 데이터 구축에 활용\n    - email, password로그인 API를 지원\n    - DB를 자체적으로 또한 자유롭게 구축할 수 있어 개인 프로젝트에 활용하기에 용이\n  - _React-Router-Dom_\n    - React의 SPA(Single Page Application)특성상 하나의 페이지(HTML)에서 모든 렌더링이 이루어짐\n    - React의 이러한 강점을 활용하기 위해 페이지의 로딩없이 페이지에 필요한 컴포넌트를 렌더링 하기 위해 사용\n  - _Redux_ / _React-Redux_\n    - 전역 상태 관리하는데 활용\n    - 전역 상태 관리에 있어 props drilling을 피할 수 있음과 동시에 성능 최적화에 용이\n\n\u003cbr /\u003e\n\n---\n\n## 📁 폴더 구조\n\n    root\n    |-- tsconfig.json\n    |-- README.md\n    |-- package.json\n    |-- yarn.lock\n    |-- .gitignore\n    |-- public\n    |   |-- index.html\n    |-- src\n        |-- compoenents\n           |-- auth\n               |-- styles\n           |-- Todos\n           |-- types\n        |-- pages\n           |-- Main\n           |-- Todo\n        |-- modules\n        |-- styles\n        |-- Router.tsx\n        |-- index.tsx\n        |-- react-app-env.d.ts\n        |-- firebase.ts\n\n---\n\n## 📝 구현 기능\n\n### Firebase 회원가입 및 로그인 기능 구현 및 DB구축\n\n- 회원가입\n  - 회원가입은 `firebase`에서 `createUserWithEmailAndPassword`메소드를 제공해준다.\n  - 위의 메소드를 활용해서 `firebase.ts`에 `register`함수를 제작해주었다.\n    ```ts\n    export const register = (email: string, password: string) =\u003e {\n      return createUserWithEmailAndPassword(auth, email, password);\n    };\n    ```\n- 로그인\n  - 로그인은 `firebase`에서 `signInWithEmailAndPassword`메소드를 제공해준다.\n  - 위의 메소드를 활용해서 마찬가지로 `firebase.ts`에 `login`함수를 제작했다.\n    ```ts\n    export const login = (email: string, password: string) =\u003e {\n      return signInWithEmailAndPassword(auth, email, password);\n    };\n    ```\n- 회원가입 및 로그인 할 때 `firebase`에서 성공했을 때의 응답을 `Realtime Database`에 `set`메소드를 활용해 유저데이터의 `email`과 `uid`를 저장했으며 추후 Todos리스트에 로그인 했을 때 각 고유별로 Todo를 갖게 하기 위해 `uid`를 활용하여 로그인 기능을 같이 구현하였다.\n- DB는 `Realtime Database`를 활용하여 구축을 했다.\n\n  - `firebase.ts`에서 `getDatabase`를 활용해서 db라는 이름의 변수를 만들어 주었고 그 db를 활용해서 CRUD에 활용하였다.\n  - db를 활용하기 위해서는 `ref`라는 메소드를 같이 활용해서 데이터가 입력되고 요청되어야 할 db의 url을 지정해줘야 한다.\n\n    ```ts\n    /* AuthForm.tsx - handleSubmit */\n    if (type === 'register') {\n    await register(userInfo.register.email, userInfo.register.password)\n      .then(res =\u003e {\n        set(ref(db, `users/${res.user.uid}`), { // set메소드에 ref를 달아준다.(데이터 베이스를 찾아가게 하기 위한 목적)\n          id: res.user.uid,\n          email: res.user.email,\n        });\n        alert('회원가입 성공!');\n        navigate('/todo');\n      })\n      /* ... */\n    ```\n\n### TodoList 가져오기\n\n- `get`메소드를 활용해 데이터베이스에서 해당 유저 `uid`에 맞는 Todo 데이터를 가져온다\n\n  ```ts\n  const token = localStorage.getItem('uid'); // 로그인 시 유저의 uid를 로컬스토리지에 저장한 후 활용하는 상태\n\n  useEffect(() =\u003e {\n    const todoRef = ref(db, `/todos/${token}`);\n    onValue(todoRef, res =\u003e {\n      setTodos(res.val());\n    });\n  }, []);\n  ```\n\n- 로그인 후 Todo앱으로 `navigate`될 때 최초 TodoList를 한 번 만 받아와서 렌더링을 하기 때문에 `useEffect`를 활용하였다.\n\n- TodoList 추가하기\n\n  - input창에 todo를 적은 후 해당 input의 텍스트를 데이터 베이스로 `submit`한다.\n\n    ```ts\n    const onSubmit = async (e: React.FormEvent\u003cHTMLFormElement\u003e) =\u003e {\n      try {\n        const postDB = ref(db, `/todos/${token}/${todoId}`);\n        e.preventDefault();\n        setTodoId(prev =\u003e prev + 1);\n        await set(postDB, {\n          id: todoId,\n          text: todoValue,\n          isCompleted: false,\n        });\n      } catch (err) {\n        alert('작성에 실패하였습니다.');\n        console.error(err);\n      } finally {\n        dispatch(changeInput(''));\n      }\n    };\n    ```\n\n    - submit할 때도 마찬가지로 `set`메소드와 `ref`메소드를 활용해 데이터베이스에 해당 유저 `uid`주소의 데이터베이스에 `POST`를 한다.\n\n### TodoList 수정하기\n\n- 수정하기는 `update`메소드를 활용해서 Todo의 수정과 완료/미완료 처리를 구현하였다.\n\n  ```ts\n  // Todo를 수정하는 함수\n  const editSubmit = async (e: React.FormEvent\u003cHTMLFormElement\u003e) =\u003e {\n    e.preventDefault();\n    const editRef = ref(db, `todos/${token}/${id}`);\n    await update(editRef, {\n      text: editTodo,\n    }).catch(err =\u003e {\n      alert('수정에 실패하였습니다.');\n      console.error(err);\n    });\n    setIsEdit(false);\n  };\n\n  // Todo의 완료/미완료를 수정하는 함수\n  const getDoneTodo = async () =\u003e {\n    const doneRef = ref(db, `todos/${token}/${id}`);\n    setIsDone(!isDone);\n    await update(doneRef, {\n      isCompleted: isDone,\n    }).catch(err =\u003e {\n      alert('오류가 발생했습니다.');\n      console.error(err);\n    });\n  };\n  ```\n\n- 부가적으로 수정할 때 수정아이콘을 클릭하면 수정하는 input으로 focus되도록 같이 구현하였다.\n  ```ts\n  const editRef = useRef\u003cHTMLInputElement | null\u003e(null); // ref를 수정버튼 클릭시 input창에 props로 전달\n  useLayoutEffect(() =\u003e {\n    // useLayoutEffect를 활용해 해당 input으로 focus되도록 처리\n    editRef.current !== null \u0026\u0026 editRef.current.focus();\n  });\n  ```\n\n### TodoList 삭제하기\n\n- 삭제는 매우 간단하게 `remove`메소드를 활용해서 삭제버튼 클릭 시 해당 id를 가진 Todo데이터를 삭제하는 것으로 구현했다.\n  ```ts\n  const deleteTodo = async () =\u003e {\n    const deleteRef = ref(db, `todos/${token}/${id}`);\n    remove(deleteRef);\n  };\n  ```\n\n### Realtime DB에서 FireStore DB로 마이그레이션(23. 2. 11(토) 완료))\n\n- FireStore는 쿼리 중심의 데이터베이스로 개인적으로 Realtime보다 FireStore 데이터베이스가 인덱싱부터 데이터 관리 측면에 있어 더 효율적이라고 판단되어 마이그레이션을 진행했다.\n  - 메서드 들은 Realtime과 큰 차이는 없으며 중점적으로 변경된 사항은 realtime은 ref라는 메서드를 활용해서 데이터 베이스에 접근했지만 firestore는 컬렉션을 기준으로 접근을 한다는 차이가 있다.\n  - 초기 데이터를 todos데이터를 불러오는데 있어 사용한 메서드는 query와 onSnapshot이라는 메서드로 get을 사용하던 realtime과의 차이가 있다.\n  - 데이터의 추가에 있어서는 realtime은 set, firestore는 addDoc 혹은 setDoc을 사용\n  - 삭제는 realtime에서는 remove, firestore는 deleteDoc\n  - 수정은 realtime에선 update, firestore는 updateDoc으로 활용된다.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhjpark625%2Fnew-todos","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhjpark625%2Fnew-todos","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhjpark625%2Fnew-todos/lists"}