Main-Project 회고 (Stat & Us)

강성일·2024년 1월 11일
0
post-thumbnail

⚔️ 1일차


Issue 처리


디자인 회의 & 라우팅

  • App.tsx 삭제 & 라우팅 적용

멘토링 피드백

  1. Redux -> 리액트 쿼리
  2. 스토리북 (공동 컴포넌트 제작 & UI 테스트) <- TS로 정의
  3. 함수 params & API & Props
    사진 -> 백 -> 바디에 값 URL로 담아서 -> 백은 프론트에게 확인 메세지만 ->
    백은 이미지를 AWS에 서버를 하나 파서 URL로 저장

앞으로 참고할 사이트 정리

https://reactrouter.com/en/main/start/tutorial

https://tailwindcss.com/docs/width

https://loading.io/

https://opengameart.org/

https://assetstore.unity.com/ko


⚔️ 2일차



피드 페이지 제작

멘토님의 피드백을 듣고 기존 전역 상태관리 툴을 변경하기로 했다.

// map 에서 카테고리 분류하는 단계

// 1. client 부분 구현 완료
// 2. 어 근데 각각 map 에 맞게 어떻게 뿌려줌? (백엔드에서 map 관련해서는 명세서 정의 안된 상태)
// 3. 요청 보낼 때, map/:statusCode 가 맞게 들어오도록 백엔드에게 오더


{
  statusCode: 0,
  statusName: '힘',
  categories: [
    {
      categoryCode: 0,
      categoryName: '웨이트'
    },
    {
      categoryCode: 1,
      categoryName: '등산'
    }
  ]
}

// 4. 그거에 맞게 라우팅 수정
// 5. mock 파일 만듬. 저건 statusCode: 0라 public에 0.json으로 만듬
// 6. 각각 0번부터 4번까지 5개 만듬
// 7. 각각 번호에 맞게 요청이 들어가도록 코드 수정
// 8. 상호작용 시, 화면으로 맞게 들어가는지 확인
  • 당시 생각했던 타임라인

후에 수행했던 작업

  1. 백드롭, 오버 추가
  2. 파람 추가
const { statusCode } = useParams();
  console.log(statusCode);
  1. path: /map/:statusCode 으로 라우팅 수정
  2. 요청까지 다 마치고 design에 아이콘 넣고 posts 빼면서 수정

⚔️ 3일차



mock 데이터로 테스트

import { useEffect, useState } from 'react';
import { useParams } from 'react-router';
import Backdrop from '../components/common/Backdrop';
import ModalFrame from '../components/common/ModalFrame';
import ServerList from '../components/map/ServerList';
import { icons } from '../utility/icon';
import axios from 'axios';

interface Category {
  categoryCode: number;
  categoryName: string;
}

interface JsonData {
  statusCode: number;
  statusName: string;
  categories: Category[];
}

const MapPage = () => {
  const [categoryList, setCategoryList] = useState<string[]>([]);
  const [iconNumbers, setIconNumbers] = useState<number[]>([]);
  const { statusCode } = useParams();

  useEffect(() => {
    const fetchCategoryData = async () => {
      try {
        const response = await axios.get<JsonData>(
          `../../public/categoryList/${statusCode}.json`,
        );
        const jsonData = response.data;

        const selectedCategoryCodes = jsonData.categories.map(
          (category) => category.categoryCode % icons.length,
        );

        const selectedCategoryNames = getSelectedCategoryNames(
          selectedCategoryCodes,
          jsonData.categories,
        );

        console.log(selectedCategoryCodes);
        console.log(selectedCategoryNames);

        setCategoryList(selectedCategoryNames);
        setIconNumbers(selectedCategoryCodes);
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };

    fetchCategoryData();
  }, [statusCode]);

  const getSelectedCategoryNames = (
    selectedCategoryCodes: number[],
    categories: Category[],
  ) => {
    return selectedCategoryCodes
      .filter((code) =>
        categories.some((category) => category.categoryCode === code),
      )
      .map((code) => {
        const selectedCategory = categories.find(
          (category) => category.categoryCode === code,
        );
        return selectedCategory ? selectedCategory.categoryName : '';
      });
  };

  return (
    <div className="flex justify-center items-center w-full h-full absolute bg-map bg-center bg-cover">
      <Backdrop>
        <ModalFrame width={1020} height={680}>
          <div className="w-[800px] h-[80px] flex justify-center items-center text-4xl mb-4">
            Servers
          </div>
          <div className="w-full h-px bg-gray-600"></div>
          <div className="w-[800px] h-[480px] flex flex-col justify-around items-center p-10 gap-4">
            {categoryList.map((categoryName, index) => (
              <ServerList
                key={index}
                title={categoryName}
                categoryCode={iconNumbers[index]}
              />
            ))}
          </div>
        </ModalFrame>
      </Backdrop>
    </div>
  );
};

export default MapPage;


// interface Category: 카테고리에 대한 정보를 담는 인터페이스입니다.
// interface JsonData: 서버로부터 받아온 JSON 데이터의 형식을 정의하는 인터페이스입니다.
// const MapPage: 맵 페이지 컴포넌트를 정의합니다.
// const [categoryList, setCategoryList]: 카테고리 이름 목록과 해당 이름들을 변경할 수 있는 함수를 저장하는 상태를 생성합니다.
// const [iconNumbers, setIconNumbers]: 카테고리 코드 목록과 해당 코드들을 변경할 수 있는 함수를 저장하는 상태를 생성합니다.
// const { statusCode } = useParams(): 현재 URL의 파라미터로부터 statusCode 값을 가져옵니다.
// useEffect: 컴포넌트가 렌더링된 이후에 실행되는 부분입니다.
// fetchCategoryData: 서버로부터 카테고리 데이터를 가져오는 함수입니다.
// const response = await axios.get<JsonData>(...): Axios를 사용하여 서버로부터 JSON 데이터를 가져옵니다.
// const selectedCategoryCodes = jsonData.categories.map(...): 카테고리 코드들을 배열로 만듭니다.
// const selectedCategoryNames = getSelectedCategoryNames(...): 카테고리 이름 목록을 가져옵니다.
// setCategoryList(selectedCategoryNames): 가져온 카테고리 이름 목록을 상태에 설정합니다.
// setIconNumbers(selectedCategoryCodes): 가져온 카테고리 코드 목록을 상태에 설정합니다.
// getSelectedCategoryNames: 선택된 카테고리 코드에 따른 이름 목록을 반환하는 함수입니다.
// return (...): 맵 페이지 컴포넌트의 UI를 정의합니다.
// {categoryList.map(...)}: 카테고리 목록을 순회하면서 ServerList 컴포넌트를 렌더링합니다.
// <ServerList ... />: 카테고리의 이름과 아이콘 코드를 ServerList 컴포넌트로 전달합니다.

1차 코드 리팩토링

import { useEffect, useState } from 'react';
import { useParams } from 'react-router';
import Backdrop from '../components/common/Backdrop';
import ModalFrame from '../components/common/ModalFrame';
import ServerList from '../components/map/ServerList';
import axios from 'axios';

interface Category {
  categoryCode: number;
  categoryName: string;
}

interface JsonData {
  statusCode: number;
  statusName: string;
  categories: Category[];
}

const MapPage = () => {
  const [categoryList, setCategoryList] = useState<string[]>([]);
  const [iconNumbers, setIconNumbers] = useState<number[]>([]);
  const { statusCode } = useParams();

  useEffect(() => {
    const fetchCategoryData = async () => {
      try {
        const response = await axios.get<JsonData>(
          `../../public/categoryList/${statusCode}.json`,
        );
        const jsonData = response.data;

        setCategoryList(
          jsonData.categories.map((category) => {
            const { categoryName } = category;

            return categoryName;
          }),
        );

        setIconNumbers(
          jsonData.categories.map((category) => {
            const { categoryCode } = category;

            return categoryCode;
          }),
        );
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };

    fetchCategoryData();
  }, [statusCode]);

  return (
    <div className="flex justify-center items-center w-full h-full absolute bg-map bg-center bg-cover">
      <Backdrop>
        <ModalFrame width={1020} height={680}>
          <div className="w-[800px] h-[80px] flex justify-center items-center text-4xl mb-4">
            Servers
          </div>
          <div className="w-full h-px bg-gray-600"></div>
          <div className="w-[800px] h-[480px] flex flex-col justify-around items-center p-10 gap-4">
            {categoryList.map((categoryName, index) => (
              <ServerList
                key={index}
                title={categoryName}
                categoryCode={iconNumbers[index]}
              />
            ))}
          </div>
        </ModalFrame>
      </Backdrop>
    </div>
  );
};

export default MapPage;

2차 코드 리팩토링

import { useEffect, useState } from 'react';
import { useParams } from 'react-router';
import Backdrop from '../components/common/Backdrop';
import ModalFrame from '../components/common/ModalFrame';
import ServerList from '../components/map/ServerList';
import axios from 'axios';

interface Category {
  categoryCode: number;
  categoryName: string;
}

interface JsonData {
  statusCode: number;
  statusName: string;
  categories: Category[];
}

const MapPage = () => {
  const [categoryList, setCategoryList] = useState<string[]>([]);
  const [iconNumbers, setIconNumbers] = useState<number[]>([]);
  const { statusCode } = useParams();

  useEffect(() => {
    const fetchCategoryData = async () => {
      try {
        const response = await axios.get<JsonData>(
          `../../public/categoryList/${statusCode}.json`,
        );
        const jsonData = response.data;

        const codes: number[] = [];
        const names: string[] = [];
        jsonData.categories.forEach((category) => {
          const { categoryCode, categoryName } = category;
          codes.push(categoryCode);
          names.push(categoryName);
        });
        setCategoryList(names);
        setIconNumbers(codes);
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };

    fetchCategoryData();
  }, [statusCode]);

  return (
    <div className="flex justify-center items-center w-full h-full absolute bg-map bg-center bg-cover">
      <Backdrop>
        <ModalFrame width={1020} height={680}>
          <div className="w-[800px] h-[80px] flex justify-center items-center text-4xl mb-4">
            Servers
          </div>
          <div className="w-full h-px bg-gray-600"></div>
          <div className="w-[800px] h-[480px] flex flex-col justify-around items-center p-10 gap-4">
            {categoryList.map((categoryName, index) => (
              <ServerList
                key={index}
                title={categoryName}
                categoryCode={iconNumbers[index]}
              />
            ))}
          </div>
        </ModalFrame>
      </Backdrop>
    </div>
  );
};

export default MapPage;
  • 메서드 forEach를 사용하여 categoryCode, categoryName을 한 번에 담아 처리했다.

  • index를 꼭 전달해야 하는 이유


⚔️ 4일차


수료 후
Next.js, 스토리 북 테스트 코드 등등 기존 프로젝트 확장


⚔️ 5일차



Params(Category)에 따른 페이지 리팩토링


Feed 페이지 구현

❗️ 위 제목을 누를 시, 해당 깃허브 코드 링크로 이동합니다.

팀원 한 분이 util 코드들을 둘로 쪼개로 map으로 서로 연결되게 처리했는데
이 부분에서 타입스크립트를 왜 쓰는지 알겠더라..

  1. FeedItem으로 props들을 다 넘겨주는 것이 아니라 feed 자체를 넘겨줘버리고
    안에서 구조 분해 할당으로 푸는 것 중요

  2. 이 feed는 Feed로 타입 지정해놨었으므로 맞춰서 타입 지정만 해주면 간편
    Main.tsx는 Feed들의 배열이고, FeedItem은 푼 상태로 전달받은 하나의 데이터이므로 그냥 Feed


⚔️ 6일차


검색 기능 추가


⚔️ 7일차


카테고리 별 페이지 이동 기능 추가


⚔️ 8일차


글 작성 기능 추가


⚔️ 9일차


무한 스크롤 기능 추가


⚔️ 10일차


유저 프로필 연결 & 클릭 시, 모달창 노출


좋아요 기능 추가


⚔️ 11일차 (내 생일 🎂)


피드 상세 페이지 추가


⚔️ 12일차


초보 용사 스타터 팩 !

  • 우리 서비스를 처음 접하는 유저들에게 driver.js 라이브러리를 이용해서
    화면에 팝업으로 사용 안내창을 띄워 설명하면 이해를 더욱 도울 수 있을 것 같았다.

⚔️ 13일차


벌써 마무리라니.. 🥹

이제 우리 서비스를 코드스테이츠 모든 팀원들 앞에서 발표하는 시간이 되었다.
유튜버 경험을 살려 우리 팀원들의 기술 발표 영상을 편집하여 인코딩했다.

매우 많은 시련이 있었다..

  • 이때도 매일 해당 글의 로그를 기록하고 있었는데, 이래저래 바빠 3개월이 지난 지금 글을 올린다.. 👻


✨ 기능 시연 영상


1. 로그인 & 회원가입 페이지2. 메인 페이지
ezgif com-video-to-gif (16)ezgif com-video-to-gif (12)
3. 스탯 & 맵 페이지4. 피드 페이지
ezgif com-video-to-gif (17)ezgif com-video-to-gif (13)
5. 좋아요 & 댓글6. 검색 기능
ezgif com-video-to-gif (14)ezgif com-video-to-gif (15)



🔎 이 글을 올리는 지금은 ?


허허.. 벌써 많은 시간이 흘렀다. 지금은 1월 11일이다.
그동안 자그마치 3개월이나 흘렀다.

해당 프로젝트 Stat & US은 8.28 ~ 9.19 동안 수행되었으니 말이다.
물론 그동안 놀았던 것은 절대 아니다.

나는 취업 준비에 바로 접어들었다.
취업 준비를 하면서 정말 시장이 매우 얼어있다는 것을 피부로 느꼈다.
매일 말로만 들었지 이 정도로 어려울 것이라곤 생각못했다.

몇 개월동안 준비하면서 코테를 준비해야 되나? 새로운 프로젝트를 해야되나?
정말 많은 생각들이 공존하면서도 이력서를 작성했고 면접은 계속 보러 다녔다.

덕분에 면접을 보면서 회사들이 실제로는 어떤 업무를 하고 있고
피드백을 받기도 하며, CS 등과 같은 지식이 정말 많이 늘었다.

계속 노마드코더를 보면서 Next.js & firebase를 공부했고, 현재도 하고 있다.
여전히 면접도 보러 다니고 있다. 모든 일은 역시 쉬운 것이 없다.

조금 여유가 남는대로 바로 이것저것 블로그에 작성하고 싶은 글이 많다.
그때까지 모두 화이팅이다. 넌 잘할 수 있고, 잘하고 있다 🔥

profile
아이디어가 넘치는 프론트엔드를 꿈꿉니다 🔥

0개의 댓글