Skip to content

seoyoung-min/OpenMind

 
 

Repository files navigation

코드잇 프론트엔드 부트캠프 1기 기초 프로젝트

오픈마인드

질문과 답변을 통해 마음을 열고 대화 나누는 소통 플랫폼 서비스입니다.


🗓 개발 기간

2023.11.03 ~ 2023.11.17


👨‍👩‍👧‍👧 팀원소개

민서영 송규경 안지수 안혜정
👉@seoyoung-min     👉@Ssong-Q            👉@An-jisu          👉@hyejungan    

🌐 Demo

오픈마인드


🖥️ 프론트엔드 개발 환경


⚒️ 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 사용을 권장합니다

About

익명 Q&A 공간, 오픈마인드

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 98.9%
  • Other 1.1%