Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

developer_kcm

JUSTCODE 2차 팀 프로젝트 본문

프로젝트

JUSTCODE 2차 팀 프로젝트

fullkeem 2022. 10. 21. 23:19

대장정의 두 번째 팀 프로젝트도 끝났다..

 

 

9/19 ~ 9/30까지 진행했던 JUSTCODE 2차 프로젝트 회고록을 작성해보려고 한다.

 

 

FLORIDA의 메인페이지

🎵프로젝트 소개

온라인 음악 서비스를 제공하는 FLO 클론 프로젝트

 

 

👀클론 사이트 선정 이유

팀원 모두가 1차 프로젝트와는 다른 성격을 띠는 사이트를 해보고 싶다는 의견이 많았다.

그래서 여러 분야의 사이트를 찾던 중 음악 서비스를 제공하는 사이트로 의견이 좁혔고 2주라는 기간 동안 가장 좋은 퀄리티로 만들 수 있을 것 같은 FLO 선정하게 되었다.

 

 

👨‍👩‍👧‍👦 팀 소개

프: 김진영, 김충만, 임지영, 최승철

백: 김교은, 이신희

 

🔗GitHub repo

프론트앤드

 

GitHub - wecode-bootcamp-korea/justcode-6-2nd-team6-front

Contribute to wecode-bootcamp-korea/justcode-6-2nd-team6-front development by creating an account on GitHub.

github.com

백앤드

 

GitHub - wecode-bootcamp-korea/justcode-6-2nd-team6-back

Contribute to wecode-bootcamp-korea/justcode-6-2nd-team6-back development by creating an account on GitHub.

github.com

 

 

⚙️ 적용 기술

  • Javascript
  • React
  • Styled-Components
  • RESTful API

👥 협업 도구

  • GitHub
  • Slack
  • Notion
  • Zep
  • Zoom

🧑‍💻 내가 맡은 역할

  • 메인 페이지
  • 상세 페이지(앨범, 가수, 플레이리스트)

 

1. 메인 페이지

메인 페이지 슬라이더

메인 페이지에서 첫 번째 section인 carousel의 UI는 React-slick 라이브러리를 사용하여 구현했다. 많은 캐러셀 라이브러리 중 가장 깔끔하고 커스텀하기 적절하기에 선택했다.

Next, Prev Arrow를 다른 아이콘으로 사용하고 싶어서 react-icons에서 적절한 아이콘을 찾은 후 svg로 변환하고 변환한 파일을 컴포넌트로 만들어서 사용했습니다.

각 슬라이드 속 UI는 fetch함수를 사용하여 백앤드 서버에 데이터를 받아 state에 저장 후 map 함수를 중첩 사용해 구현했습니다.

 

 

메인 페이지 필터링

최신 발매 음악 section은 백앤드의 신희님과 회의를 통해 fetch함수를 통해 받아오는 data의 scope의 value 값을 국내와 해외로 구분 짓고 프런트단에서 filter()를 사용해 각 버튼의 맞는 결괏값을 map함수를 사용해 구현했습니다.

 


 

2. 상세 페이지

가수 상세페이지

 

상세 페이지는 플레이리스트, 가수, 앨범, 곡으로 총 4개의 상세 페이지를 구현했다.

각 상세 페이지에 맞게 백앤드의 신희님 서버에서 데이터를 요청할 url을 useParams 함수를 이용하여 각 상세페이지별로 데이터를 받아와 state에 저장해 map 함수를 사용해 구현했습니다.

이번 프로젝트에서 아쉬운 부분 중 하나인데, 그 이유는 처음 프로젝트의 틀을 잡을 때 구체적인 상세 페이지의 구성을 잘 파악하지 못한 상태로 프로젝트를 시작했고 결과적으로 프로젝트 완성하는 시간적인 부분에서 부담이 많이 되었다.

이번 사태로 인해 프로젝트를 애자일 방법론의 scrum 방식의 큰 장점을 간접적(?)으로 느끼게 된 계기였다.

 


🥲 아쉬운 코드

  const [loading, setLoading] = useState(false);
  const params = useParams();
  const [roleType, setRoleType] = useState([
    { id: 1, name: '전체', selected: true },
    { id: 2, name: '정규/싱글', selected: false },
    { id: 3, name: '참여', selected: false },
  ]);
  const [sortType, setSortType] = useState([
    { id: 1, name: '최신순', selected: true },
    { id: 2, name: '인기순', selected: false },
    { id: 3, name: '가나다순', selected: false },
  ]);
  const [songsData, setSongsData] = useState([]);
  
    const sortHandler = (e) => {
    const arr = sortType.map((data) => {
      return data.id === Number(e.target.type)
        ? { id: data.id, name: data.name, selected: true }
        : { id: data.id, name: data.name, selected: false };
    });
    setSortType(arr);

    const selectedName = arr.filter((result) => {
      return result.selected;
    })[0].name;
    const newArr = songsData.filter((data) => {
      return !data.includes('sortType=');
    });

    if (selectedName == '최신순') {
      setSongsData(() => [...newArr, 'sortType=RECENT']);
    } else if (selectedName == '인기순') {
      return setSongsData(() => [...newArr, 'sortType=POPULARITY']);
    } else if (selectedName == '가나다순') {
      return setSongsData(() => [...newArr, 'sortType=WORD']);
    }
  };

  const roleHandler = (e) => {
    const arr = roleType.map((data) => {
      return data.id === Number(e.target.type)
        ? { id: data.id, name: data.name, selected: true }
        : { id: data.id, name: data.name, selected: false };
    });
    setRoleType(arr);

    const selectedName = arr.filter((result) => {
      return result.selected;
    })[0].name;
    const newArr = songsData.filter((data) => {
      return !data.includes('roleType=');
    });

    if (selectedName == '전체') {
      setSongsData(() => [...newArr, ' roleType=ALL']);
    } else if (selectedName == '정규/싱글') {
      return setSongsData(() => [...newArr, 'roleType=RELEASE']);
    } else if (selectedName == '참여') {
      return setSongsData(() => [...newArr, 'roleType=JOIN']);
    }
  };

  useEffect(() => {
    const queryString =
      songsData.length === 0
        ? '?roleType=ALL&sortType=RECENT'
        : '?' + songsData.join('&');
    fetch(
      `http://3.34.53.252:8000/detail/artist/${params.artistId}/songs${queryString}`,
      {
        method: 'GET',
        headers: { 'content-type': 'application/json' },
      }
    )
      .then((res) => res.json())
      .then((data) => {
        setTrackData(data.artistSongs);
      });
  }, [songsData]);

가수 상세 페이지에 다중 필터를 구현하기 위해 로직을 짰지만 마감 기한을 앞두고 완성하지 못해 결국 다중 필터 기능을 빼고 제출하게 되었다.

queryString으로 요청 url을 만들어야 했다. queryString의 사용법을 잘 몰랐기에 다른 기술 블로그와 유튜브를 참고하고 다른 동기분의 도움을 받아 로직을 짰다.

먼저 필러팅 조건이 될 sortType과 roleType를 각각 state에 선언한 후, 각각의 필터 버튼을 클릭할 경우 담아질 값이 달라질 배열을 선언한 필터 핸들러 함수를 만듭니다.

배열에 필터링 함수를 사용해 queryString에 필요한 코드를 분별할 name을 저장하는 변수 selectName를 선언합니다.

그리고 selectName이 화면에 클릭되었을 때 버튼의 이름과 같을 경우 queryString에 적용할 코드를 미리 만들어 놓은 state에 저장합니다.

결과적으로 가수 상세페이지가 처음 렌더링 되었을 때 초기값을 지정해 놓지 않고 로직을 짰기 때문에 에러가 떴고,

기한 내에 결과물을 내야 했기에 결국 다중 필터링 기능은 빼고 제출하게 되었다.

그래서 꼭 다시 queyryString을 이용한 다중 필터 기능을 공부하고 수정할 계획이다.

 

📕 기억하고 싶은 코드

  const [currentTab, setCurrentTab] = useState(0);
  const params = useParams();

  const selectTabHandler = (index) => {
    setCurrentTab(index);
  };

  const countryArr = [
    { name: '종합', content: albumList.slice(0, 10) },
    {
      name: '국내',
      content: albumList.filter((country) => country.scope.includes('국내')),
    },
    {
      name: '해외',
      content: albumList.filter((country) => country.scope.includes('해외')),
    },
  ];
  
  .
  .
  .

  return (
    <StyledSection>
          {/*오늘 발매 음악 장르 */}
          <div className='second-section-genre'>
            <ul className='second-section-genre-title'>
              {countryArr.map((el, index) => {
                return (
                  <li
                    key={index}
                    className={currentTab === index ? 'on' : 'off'}
                    onClick={() => selectTabHandler(index)}
                  >
                    {el.name}
                  </li>
                );
              })}
            </ul>
          </div>
        </div>
        {/*최신 발매 음악 정보 끝 */}
        <div className='second-section-album-inner-box'>
          <div className='second-section-album-wrap'>
            {/*앨범리스트*/}
            {countryArr[currentTab].content.map((el) => {
              return (
                <div key={el.albumId} className='second-section-album-box'>
                  <div className='second-section-album-list'>
                    <Link
                      to={`/detail/album/${el.albumId}/details`}
                      className='second-section-album-link'
                    >
                      <div className='second-section-album-img-box'>
                        <img
                          alt='앨범 표지'
                          className='second-section-album-cover'
                          src={el.albumCover}
                        />
                      </div>
                    </Link>
                  </div>
                  <Link
                    to={`/detail/album/${el.albumId}/details`}
                    className='second-section-album-song-link'
                  >
                    <span className='second-section-song'>{el.albumTitle}</span>
                  </Link>
                  <Link
                    to={`/detail/artist/${el.artistId}/songs`}
                    className='second-section-album-singer-link'
                  >
                    <span className='second-section-album-singer'>
                      {el.artist}
                    </span>
                  </Link>
                </div>
              );
            })}

메인 페이지의 두 번째 section인 최산 발매 음악이다.

필터링 기능은 이번이 처음이라 어떤 식으로 코드를 구성해야 할까 고민을 많이 했다.

section에 들어갈 데이터들은 백앤드 서버에서 받아 오기 때문에 신희님과 처음 이 부분을 만들 때 많은 회의를 했다.

데이터를 받아오는 방식과 받아온 데이터에서 국내와 해외를 나눌 방법을 회의를 잘 진행한 덕분인지 큰 무리 없이 완성할 수 있었다.

필터링을 적용할 항목별로 배열에 담았다.

그리고 종합은 페이지 내에서 보여줘야 할 content가 10개만 필요했기 때문에 slice함수를 적용해 데이터의 10개만 뽑아왔고, 국내와 해외 content는 서버에서 보내주는 데이터의 scope value값이 각각 국내와 해외가 포함되어 있는 데이터 필터링해서 albumList 배열에 저장했다.

로직을 짜면서 javascript의 기본기가 탄탄해야 더 다양하고 효율적인 코드를 짤 수 있다는 걸 다시 한번 느꼈고, 처음 프로젝트의 과정을 구상할 때 백앤드와의 소통이 잘 이루어지고 계획을 잘 짜 놔야지 자잘한 오류에 시간 낭비하지 않고 프로젝트를 잘 진행해 나갈 수 있겠다는 생각을 많이 했다.

 

⌨️ 후기

이번 프로젝트를 통해 협업을 잘하는 개발의 중요성을 많이 느꼈습니다. 데일리 미팅을 통해 진행 상황을 공유해 서로 도울 수 있는 부분들을 적극적으로 피드백함으로써 작업의 속도를 높일 수 있었습니다. 또, 프런트 앤드와 백앤드 간의 언어의 이해가 부족한 부분도 많은 대화를 통해 무리 없이 잘 해결할 수 있었습니다. 그리고 프로젝트를 진행하면서 제가 개선해야 할 부분들을 찾은 매우 귀중한 시간이었습니다.