- URL: https://dont-buyma-cat.web.app/
- 계정
ID
: lovecat@lovecat.comPassWord
: lovecat
🐱 사지마켓은 전국의 지자체 유기동물 보호센터와 연계하여 운영되는 유기묘 무료 분양 플랫폼입니다.
👥 우리는 턱없이 낮은 유기동물 입양률을 해결하기 위해 유기동물 보호소의 접근성을 높이는데 집중합니다.
💬 채팅 기능을 통해 보호센터와 간편하게 문의가 가능합니다.
😻 입양된 냥이들의 귀여운 일상 모습을 자랑하는 SNS 기능도 있답니다!
- 4명의 Front-end 개발자로 구성
역할 | 이름 | Github | 포지션 |
---|---|---|---|
팀장 | 최수빈 | subincdev | 코드리뷰에 미친 자 · 팀 내 기록 문서화 |
팀원 | 김시아 | Sia kim | 성능최적화에 목숨건 자 · 기술문제 해결사 |
팀원 | 조윤희 | unidagit | 못난UX 혐오자 · 디자인 및 피그마 제작 |
팀원 | 허지현 | Jihyun Heo | 버전관리에 인생을 바친 자 · 기획 및 컨벤션 수립 |
- FrontEnd : React, React-router, Hooks, Styled-components
- BackEnd : 제공된 API 사용
- GitHub Wiki : 컨벤션 및 프로젝트 회의록 기록
- GitHub Project : 프로젝트 진행사항 칸반보드 관리
- Notion : 프로젝트 일정관리 및 문서공유
- Discord : 실시간 채팅 및 온라인 미팅
0.splash | 1.회원가입 |
---|---|
2.로그인 | 3.홈화면 |
---|---|
4.계정 검색 | 5.채팅페이지 |
---|---|
6.마이 프로필 | 6-1.마이 프로필 수정 |
---|---|
7.입양등록 | 8.게시글 등록 |
---|---|
9.팔로우, 팔로워 | 10.유저 프로필 |
---|---|
11.게시글 댓글 | 12.입양공고 삭제 |
---|---|
모달 컴포넌트
- 독립적인 모달 컴포넌트를 구현하기 위해 createPortal을 사용하여 부모 컴포넌트의 자식 요소가 아닌,다른 노드의 자식으로 렌더링을 시키도록 분리
<!-- index.html -->
<body>
<div id="root"></div>
<div id="modal"></div>
<!--이 요소의 자식 요소로 모달 컴포넌트를 넣어줍니다-->
</body>
//modal.jsx
ReactDOM.createPortal(모달컴포넌트 , document.getElementById('modal’));
-
모달의 열고 닫힘을 setState함수를 직접적으로 넘겨주지 않고 handleOpenModal, handleCloseModal로 명확하게 분리시켜준 후, 버튼을 클릭시 모달리 열리도록, 모달 컴포넌트 내부에서는 모달을 닫을 수 있도록 handler함수로 넘겨줌
-
setState함수로 직접 넘겨주면, 자식 요소가 부모 요소의 state를 변경가능하기 때문에, 리액트가 지향하는 단방향 데이터 흐름에 위배되기 때문에 handler함수로 넘겨주는 것이 좋음
-
모달마다 다른 내용으로 이루어져있기 때문에, 모달 내용을 구성하는 컴포넌트들을 페이지별로 만들어줌
-
ModalBtn으로 감싼 요소가 모달의 내용을 구성할 수 있도록 children을 통해 모달에 모달내용 컴포넌트를 넘겨줌
여러개의 이미지 미리보기
-
<input type="file">
로 지정하고 useRef 훅을 사용하여 input에 못생긴 버튼을 이쁜 이미지 버튼으로 바꿈 -
삼항연산자를 사용하여 imageUrl.length를 확인하고 참과 거짓을 판별함. imageUrl배열의 index가 3개를 넘으면 alert 창을 띄우고 state에 추가하지 않음. imageUrl배열의 index가 3개 이하일때는 <S.ImgUploadBtn>에서 ref로 input DOM에 접근하여 onChange 이벤트위임을 발생시킴
<S.ImgUploadBtn
onClick={() =>
imageUrl.length >= 3
? alert('이미지는 3개까지만 업로드할 수 있습니다.')
: Upload_Input.current.click()
}
/>
-
최대 3개의 이미지파일만 올릴 수 있는 설정을 마쳤다면 미리보기 구현을 위해 특별한 작업을 해야 함. 보안상의 이유로 자동으로 상대경로를 블러 처리를 해버리기 때문에 Web API 중 FileReader라는 객체를 사용하여 미리보기를 구현 함. image를 base64이미지들(문자열배열)로 바꾼 후, setState 상태에 담음
-
&& 연산자 조건부 렌더링을 사용하여 base64이미지들(문자열배열)을 map으로 돌려 이미지 태그를 여러개 만들어주면 여러개 이미지가 미리보기로 띄워짐
<S.PostFormContainer>
{imageUrl &&
imageUrl.map((image, i) => (
<S.PreviewImage key={i} src={image} alt="이미지 미리보기" />
))}
사용자 프로필 페이지
useEffect(() => {
if (userToken) {
const getProfileData = async () => {
await axios({
method: 'get',
url: `https://mandarin.api.weniv.co.kr/profile/${
params.id === accountName ? accountName : params.id
}`,
headers: {
Authorization: `Bearer ${userToken}`,
'Content-type': 'application/json',
},
}).then((response) => {
setUserProfileData(response.data.profile);
});
};
-
parmas(현재 접속중인 프로필 url의 사용자 아이디)와 accountName(현재 로그인한 사용자)이 같으면 내 프로필 정보를 가져옴. 그렇지 않으면 현재 접속중인 URL의 사용자 아이디에서 그 사용자의 프로필 데이터를 가져옴
-
로컬스토리지에 저장된 accountName과, 현재 받아오고있는 사용자데이터의 accountname을 비교하여 isAuthorized상태를 설정함. isAuthorized상태에 따라 표시되는 프로필 화면이 다름. (내프로필 / 다른 사용자의 프로필)
-
로컬스토리지는 사용자가 언제든 바꿀 수 있기 때문에 보안상 좋지 않아보여서😂 추후 isAuthorized 상태 설정하는 방식을 변경할 예정
const getPostData = async () => {
...
};
getProfileData();
getPostData();
}
}, [params]);
- 다른 사용자의 프로필 페이지를 보다가 하단 네비바에서 ‘프로필’ 버튼을 클릭하면 내 프로필이 보이지 않고 이전의 프로필이 그대로 보이는 이슈가 있었음. 사용자 데이터를 불러오는 함수가 ‘userToken이 바뀔 때마다’ 실행되었기 때문. 따라서 의존성배열을 params(=현재 접속중인 url의 사용자 아이디)로 바꾸었습니다. url이 바뀔때마다 프로필 데이터를 가져오도록하여 문제를 해결함
어떤 문제가 있었나?
- 프로젝트... 그냥 코딩만 하면 되는 줄 알았는데 아니였음. 각자 코딩해서 PR날린 코드는 작업파일 겹침, 각자 다른 프리티어 설정 및 VS Code 편집기 설정 등의 이유로 충돌이 일상이었음
- 무작정 코딩을 시작하기 전에 어느정도 체계적인 컨벤션을 수립하는 것에 대한 필요성을 강하게 느낌
어떻게 해결했나?
-
eslintrc.js파일 추가하고 프로젝트 성격에 맞게 커스텀. 안정적인 코드를 위해 var키워드 사용시 오류로 출력, != 와 == 대신 !== 와 === 사용, 사용하지 않는 변수사용이나 훅 임포트시 경고는 하되 오류는 출력되지 않도록 설정
-
prettierrc 파일 추가하여 서로다른 프리티어 설정때문에 의미없는 커밋이 추가되거나 파일마다 코드 스타일이 바뀌지 않도록 함
-
깃플로우를 베이스로 하여 프로젝트 규모에 맞게 브랜치 3개(feature, develop, main)를 이용하는 전략으로 수정. 추후에 배포를 위한 release브랜치 추가
-
깃 커밋 템플릿을 global로 지정하여 커밋메세지를 정해진 규칙에 맞게 간편하게 작성할 수 있도록 하여 History 열람시 가독성을 높힘
-
깃헙에서의 PR템플릿, 이슈템플릿 등 적용하여 간편하게 형식에 맞는 글을 작성할 수 있도록 하고 가독성을 높힘. 특히 PR템플릿에
어떤 부분에 리뷰어가 집중하면 좋을까요?
추가하여 그부분을 중점으로 코드리뷰가 이루어질 수 있도록 함
어떤 문제가 있었나?
- 작업하던 레포지토리 내에 그대로 firebase를 세팅하고 배포를 마친 후에, 작업용 레포지토리와 배포용 레포지토리를 분리하기로 결정함.
- 배포할때 생긴 파일들이 있기 전으로, 즉 배포전으로 revert하고자 함.
- 하지만 우리가 돌아가려는 배포 commit 이후에 병합 commit들이 포함되어있어서 충돌이 일어남.
어떻게 해결했나?
-
CLI가 익숙치 않아 그래픽을 사용해서 revert하기로 함 -> Git Graph사용
-
돌아가려는 커밋 이후에 배포 관련 파일을 수정한 커밋들이 존재하는데, 아예 그 파일들이 존재하지 않는 상태로 revert를 하려니 충돌이 일어난 것 -> 파일 삭제하고 commit후 revert 진행
-
작업파일과 배포파일이 뒤섞여 commit 되어있는 상태여서 일어난 문제 -> 배포용 레포지토리와 작업용 레포지토리 분리의 중요성을 깨달음
기술블로그 운영으로 트러블슈팅 및 기술구현시 난제(삽질) 기록
- 여러개 이미지 화면에 미리보기 구현
- 실시간으로 유효성 검사를 해주는 input 컴포넌트 구현기
- 이젠, Nav바 만들 때 NavLink를 쓰자
- 0이 서버로 보내지지 않았던 엄청난 이유..!
-
상태관리 리팩토링
- 현재 로컬스토리지로 값을 저장해 옮겨다니는 유저 정보를 여러 상태관리 방법을 비교해보고 도입할 예정
- useContext, Redux tookit, Recoil
- 최적화작업
- useMemo 사용하여 불필요한 재렌더링 방지
-
스타일컴포넌트 리팩토링
- 중복되는 스타일 극한의 컴포넌트화
-
추가 기능구현
- 지현: 오픈 API이용한 소셜로그인 (카카오톡, 페이스북, 구글)
- 수빈: 입양 등록 수정 · 모달 컴포넌트 코드최적화 및 선택
- 시아: 컴포넌트 최적화 및 비동기 로직 분리
- 윤희: 게시물이미지 업로드 삭제기능 구현
- 기능분리
- 함수 하나에 한개의 기능만 있도록
- Custom Hooks, Reducer 사용한 axios함수 분리
- 사용자경험 개선
- 이미지 슬라이드 개선
- 네비게이션 바 클릭시 미세한 움직임 해결
- CI/CD
- github action을 통한 자동 배포화