질문과 답변을 통해 마음을 열고 대화 나누는 소통 플랫폼 서비스입니다.
2023.11.03 ~ 2023.11.17
민서영 | 송규경 | 안지수 | 안혜정 |
---|---|---|---|
👉@seoyoung-min | 👉@Ssong-Q | 👉@An-jisu | 👉@hyejungan |
⚒️ Tech stack
🤲 협업툴
-
협업 규칙, git-flow전략, commit & pr & merge 규칙, 명명 컨벤션, 폴더 구조 등 공유
-
- 브랜치 전략 : Main(배포용) 브랜치> Dev(Development; 개발) 브랜치> feature/(기능) 브랜치 : 각 기능브랜치에서 push. develop branch로 Squash and Merge. origin에서 pull --rebase를 통해 직선적인 git graph를 고려함.
-
🖼️ API, 디자인 코드잇 제공
@코드잇 👉 본 페이지를 통해 제공하는 모든 자료는 저작권법에 의해 보호받는 ㈜코드잇의 자산이며, 무단 사용 및 도용, 복제 및 배포를 금합니다.
🫵 각 스택 결정이유
- vscode
- VSCode는 Windows, macOS 등 다양한 운영 체제에서 모두 사용 가능하고, 다양한 테마, 확장 기능, 사용자 정의 설정 등을 통해 기능을 확장할 수 있으며 기존 팀원이 모두 사용하고 친숙한 IDE를 선택했습니다.
- css-in-js
- 스타일 관련 스택으로 Css-in-Js로 결정했습니다. 각 요소의 클래스명을 신경쓰지 않고, 동적으로 속성을 넣어 스타일 변화를 줄 수 있기 때문입니다. 그 중에서도 팀원들이 가장 익숙한 방식의 Styled-Component를 선택했습니다.
- netlify
- GitHub 등과의 연동을 통해 손쉽게 배포 파이프라인을 구축하고, CI/CD (Continuous Integration/Continuous Deployment)를 쉽게 설정할 수 있어 선택했습니다.
public
- index.html
src
- assets
- icons
- components
- common
- containers
- App.js
- api
- pages
- hooks
- utils
- styles
- GlobalStyle
🔎 미리 보기
- 중복되지 않은 닉네임을 입력하면 질문 받을 계정을 생성할 수 있습니다. 중복된 닉네임 사용시 경고창이 뜹니다.
- 게스트로 입장하여 질문을 작성할 수 있습니다.
- 질문 목록 페이지 개별피드에서 질문을 확인, 생성할 수 있습니다.
- 계정으로 입장하면 다른 유저가 남긴 질문에 대해 답변을 작성, 수정, 거절 할 수 있습니다.
- 👍 좋아요 👎 싫어요 를 표현할 수 있습니다.
- header의 image를 클릭하여 계정을 관리할 수 있습니다.
- 메인페이지의 토글아이콘을 클릭하여 크리스마스테마로 변경할 수 있습니다.
- 닉네임을 정규식을 통해 띄어쓰기 등을 사용하지 못하게 한정시키고, 첫 렌더링 시 등록된 닉네임과 비교해 중복 등록을 막았습니다.
let regex = /^[a-zA-Z0-9가-힣]*$/; if (!regex.test(name)) ,,, ,,, if (allList[0].includes(name)) { setIsError(true); setErrorMessage('이미 존재하는 닉네임 입니다. 다시 입력해주세요.')
- 닉네임 등록 후 localStorage에 저장하게 하고, 이 후 로그인 시 해당 localStorage에 닉네임이 없는 경우 로그인을 막았습니다.
if (getLocalStorage(name)) { const storedId = getLocalStorage(name); const { id: userId } = await getSubjects(storedId); navigate(`/post/${userId}/answer`);
- 질문대상이 되는 사용자들의 데이터에서 닉네임과 랜덤 사진으로 카드섹션을 만들고, 기본 8개로 limit을 설정하여 페이지네이션을 통해 데이터를 더 불러오도록 했습니다.
이때, 페이지를 나눠주는 페이지 번호 양 끝단에 화살표를 두어 이동하게 했고, 클릭된 번호의 주변번호만 보이게 하기 위해 새로운 배열을 만들어서 나타나게 했습니다.
let arrLen = width > 767 ? 7 : 5; let pageArr = createPageArray(TOTAL_PAGE, pageNum, arrLen); ```js let pageArray = []; //1. arrLen>=TotalPage if (arrLen >= total) { for (let i = 1; i <= total; i++) { pageArray.push(i); } return pageArray; } //2. arrLen<TotalPage let SIDE = Math.floor(arrLen / 2); let firstNum = +pageNum - SIDE; let lastNum = +pageNum + SIDE; //배열의 시작 숫자 조정하기 if (firstNum < 1) firstNum = 1; if (lastNum > total) firstNum = total - (arrLen - 1); //배열만들기 for (let i = firstNum; i <= firstNum + (arrLen - 1); i++) { pageArray.push(i); } return pageArray;
- uselocation으로 pathname의 id값을 가져와 페이지 번호의 이동, 뒤로가기 시 페이지에 맞는 offset적용되어 리렌더링되도록 했습니다.
<Route path="list"> <Route path=":page/:sorted" element={<QuestionListPage />} /> </Route> ,,, const location = useLocation(); const pageNum = location.pathname.split('/')[2]; const [num, setNum] = useState(+pageNum); ,,, useEffect(() => { onClick(limit * (Number(pageNum) - 1)); //setOffset변화를 줌 setNum(+pageNum); }, [location]);
- intersectionObserver를 이용해 infinit scroll(무한스크롤)을 구현했고, 브라우저 엔진이 useRef 요소의 가시성 변화를 감지하여 렌더링 하도록 했습니다.
next값이 없을때, hasNaxt를 이용해 DOM 요소가 보이지 않도록 했습니다.
const [hasNext,setHasNext] = useState(true); ,,, const observer = new IntersectionObserver(observeCallback, { threshold: 0.4, }); ,,, useEffect(() => { observer.observe(target.current); }, [location, offset]); ,,, {hasNext && <Styled.ObserveTargetBox ref={target} />}
- 질문 작성 후 모달창이 닫혔을때, 바로 폼에 적용되어 보여질 수 있도록 했습니다.
const formData = JSON.stringify({ content: `${value}` }); const response = await postSubjectsQuestion(subjectId, formData); if (questionData.data.length) { setQuestionData((prevData) => { const { data: prevArray } = prevData; return { data: [response, ...prevArray] };
최신 chrome Browser 사용을 권장합니다