🔗 배포 URL: https://beyonddevelops.github.io/AnimalTalk/
- email: [email protected]
- pw: 123123!@#
- 🏫 멋쟁이사자처럼 프론트엔드스쿨 3기 팀 프로젝트로 진행한 애니멀톡입니다.
- 🐱 애니멀톡 서비스는 참여하고 싶은 반려동물 모임(산책, 미용 등)을 등록하여 소통할 수 있는 SNS입니다.
- ✉️ 모임을 등록하지 않아도 일상을 공유하며 즐거운 SNS 활동을 할 수 있습니다.
- 📝 글과 사진을 함께 게시물로 작성하여 자신의 일상을 공유할 수 있습니다. 다른 사용자를 팔로우하면 유저가 올린 게시물을 홈 피드에서 소식을 확인할 수도 있습니다.
- 💕 피드를 구경하다가 마음에 드는 게시물을 발견했다면 좋아요를 누를 수 있고 댓글을 남기거나 공유할 수도 있습니다.
이지형 | 이재영 | 정수현 |
---|---|---|
- React와 TailwindCSS의 결합을 통해 마크업과 디자인 구현을 통합적으로 관리하고자 합니다.
- React Hook에 대한 동작 원리와 구현 과정을 이해하며 프로젝트를 진행합니다.
- 라우팅과 예외처리를 위해 React-Router-Dom (v6.4 이상) 및 React Query를 사용하며 프로젝트를 진행합니다.
- 상태관리를 위해 Redux-ToolKit을 사용합니다.
- 사용자의 웹 접근성을 고려하여 시맨틱하게 개발합니다.
- 컴포넌트 분리를 통한 재사용성 향상을 지향합니다.
- 적극적인 코드리뷰를 통해 서로의 정보를 공유하고, 직접 코드를 설명하는 시간을 가져봅니다.
- "참여하고 싶은 프로젝트", "자랑하고 싶은 프로젝트" 만들기를 팀 문화로 지정하였습니다.
- 참여하고 싶은 프로젝트 만들기
- 자랑하고 싶은 프로젝트 만들기
1.1 서로의 다름을 인정하기 서로의 감정과 생각을 솔직하게 말하자! => 매주 금요일 마감회의 때 감정회고 시간을 가져보기 => 자유로운 의견 제시 불편한 점이 있으면 “나는 이러저러한 불편함이 있었어요~”라고 말하기 => 금요일 저녁 10시에 맥주 회고 가지기
1.2 언제나 마음 편하게 페어프로그래밍 요청하기
1.3 주중 오후 4시에 코드 리뷰하기
1.4 자기 시간을 확보하도록 하기 => 주중 오후 8시 이후, 주말에는 최대한 자기 시간 확보하기
1.5 불참을 하게 되는 경우에는 하루 전에 말해주기
2.1 타인에게 설명할 수 있을 정도로 하나의 기능이라도 구현하려고 노력하기
2.2 내 실력을 온전히 보여줄 수 있는 수준의 코드 작성하기 => 배우면서 적용하되, 따라하지 말자!
- Front : React, TailwindCSS, React-router-dom, Axios, React Query, React Form Hook, Redux-ToolKit
- Back : 제공된 API 사용
- 버전 관리 및 이슈 : 🔗GitHub, 🔗GitHub Issues, 🔗GitHub Project
- 서비스 배포 환경 : 🔗 GitHub
URL : 🔗 https://beyonddevelops.github.io/AnimalTalk/
- public/assets/ : 이미지, svg 등
- src/api/ : axios API 관리
- src/components/ : 단일 페이지의 하위 요소로 들어가는 컴포넌트
- src/context/ : 사용자 정보 등 상태관리
- src/hooks/ : 컴포넌트에서 사용되는 훅 관리
- src/pages/ : 화면에 렌더링 되는 컴포넌트
- src/shared/ : 페이지에서 공통적으로 쓰이는 컴포넌트
- src/store/ : 상태관리용 컴포넌트
- src/types/ : 타입 관리를 위한 컴포넌트
- src/utils/ : 뷰와 무관한 함수 모듈
AnimalTal듈
│
├─public
│ └─assets
│ ├─img
│ └─index.html
│
└─src
├─api
│ ├─Club/
│ ├─Comment/
│ ├─Feed/
│ ├─Image/
│ ├─Like/
│ ├─Profile/
│ ├─Search/
│ ├─User/
│ ├─Valid/
│ └─axios.ts
│
│
├─components
│ ├─ChatDummyData
│ ├─ChatModule
│ ├─CheckAuth
│ ├─ErrorFeed
│ ├─HeaderParts
│ ├─Loading
│ ├─ModalModule
│ ├─NoComment
│ ├─NoFeed
│ ├─PostTypeSelectBar
│ ├─SimpleClub
│ └─Textarea
│
├─context
│ └─AuthContext.jsx
│
├─hooks
│ └─useFeeds.jsx
│
├─pages
│ ├─ChatList
│ ├─ChatRoom
│ ├─ClubUpload
│ ├─EditProfile
│ ├─Follows
│ ├─Home
│ ├─LoginEmail
│ ├─LoginHome
│ ├─NotFound
│ ├─PostDetail
│ ├─PostUpload
│ ├─SignUp
│ ├─SignupProfile
│ ├─SplashScreen
│ ├─UserFeed
│ └─UserSearch
│
├─shared
│ ├─Footer
│ ├─Header
│ ├─Post
│ ├─Profile
│ ├─SimpleUserList
│ └─UserClub
│
├─types
│ └─post.ts
│
├─utils
│ ├─currentUnitComma.ts
│ └─date.ts
│
├─Root.tsx
├─Router.tsx
├─index.css
└─index.tsx
- 라우팅, 로그인 API, 헤더 부분
- 🔗github/IntHyun
- 회원가입 기능 및 페이지, 회원정보 수정 기능 및 페이지, 모달 컴포넌트, 채팅 목록 페이지 및 해당 페이지 구현을 위한 하위 컴포넌트 등
- 리팩토링 담당
- 🔗github/July249
- 홈, 검색 페이지, 팔로우 페이지, 사용자 피드 페이지 및 해당 페이지 구현을 위한 하위 컴포넌트 등
- 리팩토링 담당
- 🔗github/GreattitJY
- 게시글 페이지, 모임 페이지, 채팅 페이지, 푸터, 유저 프로필, 댓글 및 좋아요 기능 및 해당 페이지 및 기능 구현을 위한 하위 컴포넌트 등
- 전체 개발 기간 : 2022.12.09 ~ 현재
- 버전 v1.1.0 : 2023.04.08 ~ 현재
- 버전 v1.0.1 : 2023.01.07 ~ 2023.04.07
- 버전 v1.0.0 : 2022.12.09 ~ 2023.01.06
-
🔗GitHub Projects를 사용하여 진행도와 상황을 꾸준히 공유하였습니다.
-
Pull Request를 보낼 때 이슈번호와 연동하여 issue 관리를 진행, 스크린샷을 통해 코드리뷰의 효율성을 높였습니다.
- 22.12.09 ~ 22.12.13 : 기본 환경 설정 및 공통 스타일 작업 (레이아웃, 헤더, 푸터 등)
- 22.12.14 ~ 22.12.20 : 기본적인 페이지, 컴포넌트 스타일 및 디자인 UI 기능 구현
- 22.12.21 ~ 23.01.03 : 핵심 기능 구현(게시글, 팔로우, 좋아요, 댓글, 프로필 수정 등)
- 23.01.04 ~ 23.04.07 : useFeeds, useLengthCheck 커스텀 훅을 통한 로직 재사용 관리, Header 컴포넌트 통합, 레이아웃 반응형 수정, UserSearch 성능 개선(debounce)
- 23.04.08 ~ :
주간회의를 진행하여 작업 방향이나 코드 고민에 대해 나누었고 GitHub Wiki를 사용하여 기록하였습니다.
-
- 사용할 기술 스택에 대한 논의
- 물리적 거리의 한계를 극복하고자 Discord에서 주로 회의 진행
- GitHub Orgarization 생성
- GitHub Wiki, Projects, Discussions를 적극적으로 활용하여 회의록 및 프로젝트 진행사항 확인
- GitHub Flow 방식 차용하여 진행
-
- 조 이름, 조장 정하기, GitHub Origanization 생성 및 등록
-
- 커밋 컨벤션 지정
- 코드 컨벤션 지정
- GitHub flow 방식에 따라 Pull Request 연습하기
-
- 이미지 assets 폴더 public vs. src 논의 => public 지정
- TailwindCSS 공통 스타일, 초기 설정 지정
-
- Commit, PR, Issue 템플릿 적용
- PR 단위 의논
- Merge 할 때 규칙
- Approve 이후 셀프Merge 진행
- 다음 기능 구현을 위한 빠른 Merge 요구시 즉각적인 셀프 머지 진행
- Merge 전에는 항상 코멘트 남기기
- Branch 명 정하기 => (커밋 컨벤션)/issue_(이슈번호) ex. feat/issue_120
- 코드리뷰 시간은 주중 오후 4시
-
- VSCode prettier 설정 동기화를 통해 esLint 미적용
-
- API 명세 및 피그마 디자인을 기준으로 폴더구조 설정
- 라우터 설정
- 서버 데이터 생성 논의 (썬더 클라이언트 이용)
- 디자인 작업 종료 시점 논의
- 기능 구현 업무 분담 => 페이지 단위로 진행
- 로그인 기능 구현 페어프로그래밍
-
- 페이지 업무 분담 및 추가 업무 분담
-
- 작업한 페이지 확인
- 버그 및 미완성 기능 확인
- 추가 작업 사항 논의
-
- 교육기간 내 프로젝트에 대한 서로의 회고
- 서로에 대한 추천서 써주기
- 교육 이후 프로젝트 진행 방향 및 방식 논의
🔗splash | 🔗로그인 | 🔗회원가입 |
---|---|---|
![]() |
![]() |
![]() |
🔗홈 | 🔗검색 | 🔗채팅 |
---|---|---|
![]() |
![]() |
![]() |
🔗작성 | 🔗삭제 |
---|---|
![]() |
![]() |
🔗수정 | 🔗상세 |
---|---|
![]() |
![]() |
🔗내 프로필 | 🔗프로필 수정 | 🔗팔로우 |
---|---|---|
![]() |
![]() |
![]() |
🔗팔로워 리스트 | 🔗팔로잉 리스트 | 🔗로그아웃 |
---|---|---|
![]() |
![]() |
![]() |
🔗모임 만들기 | 🔗모임 수정하기 | 🔗모임 삭제하기 |
---|---|---|
![]() |
![]() |
![]() |
🔗좋아요 | 🔗댓글 작성 및 삭제 |
---|---|
![]() |
![]() |
- 프로필 수정 시 계정 이름을 변경하면 useContext로 내려받던 유저 정보가 업데이트 되지 않아 렌더링 오류가 발생
-
해결방법 : 근본적인 해결 방법은 쿼리를 사용해서 서버 상태를 감시하는 것이지만 useState를 사용해서 클라이언트 쪽에서 한 번 더 감시해서 해결했습니다.
-
해결 코드
const { accountname } = useContext(UserContext); const [newAccountname, setNewAccountname] = useState(accountname); const token = localStorage.getItem("token"); useEffect(() => { const token = localStorage.getItem("token"); const getUserInfo = async () => { try { const res = await axios.get("/user/myinfo", { headers: { Authorization: `Bearer ${token}`, }, }); setNewAccountname(res.data.user.accountname); } catch (err) { console.log(err); } }; getUserInfo(); }, []);
-
트러블 코드
const CheckAuth = () => { const token = localStorage.getItem("token"); const [userInfo, setUserInfo] = useState(null); useEffect(() => { if (!userInfo) { const getUserInfo = async () => { try { const res = await api.get("/user/myinfo", { headers: { Authorization: `Bearer ${token}`, }, }); setUserInfo({ ...res.data.user }); } catch (err) { console.log(err); } }; getUserInfo(); } }, [token, userInfo]); return ( <UserContext.Provider value={{ ...userInfo }}> {token ? <Outlet /> : <Navigate to="/login" replace="true" />} </UserContext.Provider> ); };
-
무한 스크롤 기능을 구현하기 위해 Intersection Observer API를 이용하며 겪은 API 데이터 요청 이슈
- 기능 구현 1: Observe Target 요소가 viewport 안에 intersect 되었을 때 추가 데이터를 얻기 위해 API 요청이 진행되도록 Home 페이지에서 기능 구현하였음
const getFollowersFeeds = async () => { try { const res = await axios.get(`/post/feed?limit=10&skip=${state.postNum}`, { headers: { Authorization: `Bearer ${token}`, }, }); setPosts((prev) => [...prev, ...res.data.posts]); setState((prev) => ({ postNum: prev.postNum + 10, moreFeed: posts.length % 10 === 0 })); } catch (err) { console.log(err); } }; const observerTarget = useRef(null); useInterset(observerTarget, state.postNum, state.moreFeed, getFollowersFeeds);
- 기능 구현 2: Home, UserFeed, Followers 페이지에서 무한스크롤을 반복적으로 사용하게 되므로 커스텀 훅으로 관리해야할 필요성을 느낌
import { useEffect } from "react"; export default function useInterset(targetRef, stateCount, stateMore, requestAPIFunc) { useEffect(() => { if (!targetRef.current || !stateMore) return; const observerCallback = (entries, observer) => { if (entries[0].isIntersecting) { requestAPIFunc(); } }; const observer = new IntersectionObserver(observerCallback); observer.observe(targetRef.current); return () => observer.disconnect(); }, [stateCount, stateMore]); }
-
이슈1: Home 페이지 내에 팔로잉하는 사용자가 없는 경우 Observer Target이 노출됨에 따른 무한 API 데이터 요청 이슈가 있음을 확인함
-
이슈1 해결방법: 받아온 게시글이 있을 때에만 Target 요소가 노출될 수 있도록 조건부 렌더링을 적용하여 해결
{ posts.length > 0 && <div ref={observerTarget}></div>; }
-
이슈2: Home 페이지 최초 렌더링 시 API 요청이 비동기이기 때문에 발생하는 게시글 배열의 크기가 0인 이슈로 인해 API 요청 함수 자체가 동작하지 않음
-
이슈2 해결방법: useEffect를 이용하여 최초 렌더링 시 API 요청 함수가 무조건 1회 동작하도록 코드를 추가하여 해결
useEffect(() => { if (posts.length === 0) { getFollowersFeeds(); } }, []);
-
이슈3: useIntersect 커스텀 훅을 이용하여 UserFeed 페이지 구현시 무한 스크롤 기능을 구현하였으나, 마지막 게시글에 대해 이슈1번과 동일한 문제가 발생하였음
-
이슈3 해결방법: 프로필 수정시 계정 ID를 수정하기 위해 재영님이 적용한 isUpload 상태가 Target 요소가 나타났을 때 API 요청을 지속하는 문제를 해결하는 핵심 기능이 되었음. 해결이 진행된 이유를 명확하게 파악해야하는 이슈가 아직 남아있음
const getPageProfile = async () => { try { const res = await api.get(`/profile/${pageAccount}`, { headers: { Authorization: `Bearer ${token}`, }, }); setPageProfile(res.data.profile); } catch (err) { console.log(err); } }; //.. 중략 const observerTarget = useRef(null); useIntersect(observerTarget, state.postNum, state.moreFeed, getUserFeeds); useEffect(() => { if (!isUpload) return; getPageProfile(); getUserClub(); getUserFeeds(); setIsUpload(false); }, [pageAccount, isUpload, postDataArray, pageProfile]);
-
이미지를 받아올 때 아래의 코드와 같이 api주소가 중복되는 버그가 발생하여 x-box가 렌더링 되는 버그 발생
<img src="https://user-images.githubusercontent.com/68059880/210592195-18303b33-9e99-405b-82d7-c44bb8f52b4a.png" />
이유 : 기존에는 아래와 같이 url을 삽입하여 경로를 설정해주었는데 서버에서 img경로를 받아올때 url 주소가
파일이름
혹은서버주소+파일이름
처럼 일관되지 않게 들어오는 문제 발생<img src=`https://api.mandarin.weniv.co.kr/${img}` />
해결방안
<img src={img.includes("https") ? img : `https://api.mandarin.weniv.co.kr/${img}`} />
includes
를 통해https
가 있는지 검사하고https
가 있다면img
를 바로 넣어주고 없다면 주소를 넣어img
를 넣어주어 x-box가 렌더링이 되는 문제를 해결<img onError={(e) => { e.target.src = profileSmallImg; }>
추가적으로 이미지를 제대로 받아오지 못했을 때 x-box가 뜨는 버그를 수정해주기 위해 위와 같이 onError 이벤트를 활용하여 애니멀 톡의 기본 여자이미지로 변경되도록 리팩토링
-
프로젝트 목표 중 하나인 웹 접근성을 고려하여 시맨틱한 코드를 작성하였으나 아직 부족한 부분이 있습니다. 추후 접근성과 성능 개선이 필요합니다.
-
권장사항 :
- HTTPS 사용하지 않음 (폰트 사용 시 안전하지 않은 URL)
- 이미지가 낮은 해상도로 제공
-
해결 방법 : import url 누락된 https를 입력 및 이미지 파일 svg로 변경
- 중복된 api 요청 관리하기
- 각 컴포넌트에서 선언된 api 요청들을 하나의 파일에서 관리하는 게 필요합니다.
-
머지를 진행하기 전에 팀원들에게 리뷰를 받아 코드를 수정하는 과정을 거친 뒤 머지를 진행
-
바로 머지를 진행하여야 하는 경우 팀원들에게 알린 뒤 바로 머지를 진행
-
머지 한 후에 해당 브랜치 삭제 후 issue close
- Prettier : tap 간격 통일, 홑 따옴표 사용, tap Width 통일
- props로 전달되는 함수 앞에는 on 키워드를 붙여 전달
- 함수명 이름의 시작은 handle로 통일
- 변수표기는 Camel 표기법 사용
키워드 사용 시점 feat 새로운 기능 추가 fix 버그 수정 docs 문서 수정 style 세미콜론 누락 등 기능 수정이 없는 경우 design 사용자 UI 디자인 변경 (CSS 등) test 테스트 코드, 리팩토링 테스트 코드 추가 refactor 코드 리팩토링 build 빌드 파일 수정 setting 패키지 매니저 수정, 개발 환경 설정 등 rename 파일 혹은 폴더명을 수정만 한 경우 remove 파일을 삭제한 경우 - 브랜치 생성전 issue를 생성하여 어떤 것에 대한 작업을 진행할 것인지 미리 작성
- 브랜치 생성에 있어 네이밍을 단순화 및 획일화 하여 추적이 가능하다는 점에서 (커밋컨벤션 키워드)/issue_(이슈번호)로 브랜치 생성