프로젝트를 통한 배움 - (5)

응애 나 프론트애긔👶·2022년 12월 4일
0
post-thumbnail

인기 해시태그 리스트


메인 페이지 중 인기 해시태그들을 나열하는 Section이 있다.

기존의 mockData를 만들 때 이를 구현하기 위해 중복되는 해시태그들을 몇 가지 집어넣었다.

해시태그 배열들만 hashTagList라는 배열 안에 담아 둔 상태이다.

const arrangeHashtag = () => {
    const hashTagList = mockData.map(el => el.hash_tag);
    console.log(hashTagList);

육안으로 보았을 때 이 중복되는 것이 hash_tag 1/2/3, hash_tag B 그리고 HOT와 GOD가 있다.


해시태그 인기 순으로 나열해보자

// hashTag의 이름과 갯수를 저장할 객체
let hashTagObj = {};
    hashTagList.map(items => {
      items.map(el => {
        // hashTagObj 안에 해당 해시태그를 1씩 추가한다.
        hashTagObj[el] = (hashTagObj[el] || 0) + 1;
      });
    });

	// hashTagObj의 객체를 배열로 변경 후 value 값(개수)을 sort함수로 비교하여 인기 순으로 나열한다.
    const hashTagSortList = Object.entries(hashTagObj).sort((a, b) => {
      return b[1] - a[1];
    });


위와 같이 코드를 작성하여 해시태그들을 인기 순서대로 정리하였다.
이제 hashTagSortList 배열을 가지고 Section에 리스트를 만들어내면 된다.


// 전체코드
const PopularHashTag = () => {
  const mockData = useRecoilValue(getTest);
  const arrangeHashtag = () => {
    const hashTagList = mockData.map(el => el.hash_tag);
    
    let hashTagObj = {};
    hashTagList.map(items => {
      items.map(el => {
        hashTagObj[el] = (hashTagObj[el] || 0) + 1;
      });
    });

    const hashTagSortList = Object.entries(hashTagObj).sort((a, b) => {
      return b[1] - a[1];
    });
    return hashTagSortList;
  };

  return (
    <>
      <Container>
        <SectionTitle
          titleName={'인기 해시태그'}
          border={'none'}
          titleMargin={2.56}
        />
        <PopularHashTagList>
          {arrangeHashtag(mockData).map((el) => {
            if (index < 5) {
              return (
                <PopularHashTagItem key={el[0]}>
                  {index + 1}. {el[0]}
                </PopularHashTagItem>
              );
            }
          })}
        </PopularHashTagList>
      </Container>
    </>
  );
};

export default PopularHashTag;


다크모드 리팩토링


저번에 Toggle을 만들어 사용한 다크모드를 조금 더 좋은 방법으로 개선하기 위해 리팩토링을 했다.

토글의 boolean은 직관적이다.

저번에 느꼈던 점은 다크모드의 전역상태 관리 토글을 boolean의 형태가 아닌
'light'/'dark'라는 문자열이 더 직관적이라고 생각했다.

하지만 기존에 만들었던 boolean 형태의 토글 또한 직관적이고 효율적이라 생각했다.

그런 이유는 첫번째로 변수 이름은 darkMode라는 이름을 사용한다.

default 값으로 false를 주는데 이는 꽤나 직관적이다.

현재 다크모드를 안하고 있다는 뜻을 쉽게 알 수 있기 때문이다.
darkMode라는 변수 이름으로 false => 라이트모드 / true => 다크모드라는 것을 예측할 수 있다.

두번째는 쉬운 삼항식이다.

기존의 문자열을 사용하게 된다면

// Boolean 형태
  background-color: ${props =>
    props.darkMode
      ? 'black'
      : 'white'};
// String 형태
  background-color: ${props =>
    props.themeMode === 'dark'
      ? 'black'
      : 'white'};

각각 props로 전달 받아 삼항식을 사용 했을 때 두 형태 모두 개발자가 알아보기 쉬운 형태로 되어있다.
하지만 darkMode는 boolean 형태로 들어오기 때문에 === 'dark'와 같은 비교 연산을 넣을 필요가 없다.

그래서 토글의 형태는 Boolean으로 변함없이 작성하기로 했다.


색상들도 전역으로 관리한다.

색상들 또한 전역적으로 관리하기로 했다.

export const themeColor = atom({
  key: 'themeColor',
  default: {
    darkMain: '#0D1117',
    darkNavAndFooter: '#161B22',
    darkLine: '#8B949E',
    darkFont: '#E5E7EE',
    lightMain: '#F5F5F5',
    lightNavAndFooter: '#C6C2C2',
    lightLine: '#000000',
    lightFont: '#000000',
  },
});

테마컬러라는 이름의 데이터를 전역적으로 사용하기로 했다.
해당 컬러를 컴포넌트에 props로 전달받아 색상을 입히는 방식이다.

// 전체코드
import React from 'react';
import styled from 'styled-components';
import { useRecoilValue } from 'recoil';
import { themeColor, darkModeToggle } from '../atoms/atoms';

// import Components
import SectionContainer from '../components/Main/SectionContainer';
import NavBar from '../components/common/NavBar';
import Footer from '../components/common/Footer';
import Carousel from '../components/Carousel';

const MainContainer = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100vh;
// color와 background-color에 props로 사용
  color: ${props =>
    props.darkMode
      ? props.themeColorObject.darkFont
      : props.themeColorObject.lightFont};
  background-color: ${props =>
    props.darkMode
      ? props.themeColorObject.darkMain
      : props.themeColorObject.lightMain};
`;

const MainSectionContainer = styled.div`
  display: flex;
  height: 100%;
  flex-direction: column;
  justify-content: flex-start;
  align-items: center;
`;

const Main = () => {
  const darkMode = useRecoilValue(darkModeToggle);
  const themeColorObject = useRecoilValue(themeColor);

  return (
    // 컴포넌트에 props로 사용
    <MainContainer darkMode={darkMode} themeColorObject={themeColorObject}>
      <NavBar />
      <MainSectionContainer>
        <Carousel />
        <SectionContainer />
      </MainSectionContainer>
      <Footer />
    </MainContainer>
  );
};

export default Main;

🤨: 그냥 기존에 boolean만으로도 충분히 다크모드가 구현이 가능한데 왜 전역적으로 관리하는거지 ?


이유는 유지보수이다.

기존의 코드와 현재코드를 비교해보겠다.

// Boolean으로만 변경하는 형태
  background-color: ${props =>
    props.darkMode
      ? 'black'
      : 'white'};
// atom을 받아온 형태
  background-color: ${props =>
    props.darkMode
      ? props.themeColorObject.darkMain
      : props.themeColorObject.lightMain};

단순히 보기에는 크게 다른 점이 없다.

잠시만
옆자리에서 UI/UX 디자이너과 기획자분께서 내 쪽으로 걸어오신다...

🦸: 혹시 다크모드 색상을 변경 할 수 있을까요 ?


다시 위의 코드를 살펴보자.

현재 Boolean으로 변경한 형태에서 색상을 변경하려면 해당 부분을 찾아 모조리 바꿔야한다.
열심히 Ctrl + F를 눌러 찾고 Ctrl + D를 눌러 색상을 변경한다.

반면 전역적으로 관리한 경우
원하는 부분의 색상만 변경해주면 전체적인 색상이 변경되게 된다.

이러한 유지보수 차원에서 스타일 값들을 전역적으로 관리하는게 좋다고 판단되어 리팩토링을 진행했다.

느낀점


조금 아쉬움이 남는 작업들이였다.

두 작업들 모두 같은 아쉬움인데 바로 올바른 방법인지 아닌지 스스로가 판단하기 어렵다는 점이다.

첫번째로 Data들을 정렬하는 것은 프론트에서 하는 것인지 백에서 하는 것인지 파악하기 어렵다.
순수하게 백에서 기본적인 data들만 API로 보내주고 해당 data를 프론트에서 가공하는 것인지
아니면 백에서 다 가공된 data를 가지고 프론트는 화면에 나타내기만 하면 되는 것인지 조금 헷갈린다.

개인적으로 백에서 가공된 data를 주는게 맞다고 생각이 들긴한다.
이유는 프론트에서 복잡한 연산이 많을 수록 페이지 로딩과 렌더링에 시간 소요가 많이 될 거라 예상하기 때문이다.

반면 해당 해시태그 리스트 같은 경우는 프론트에서 충분히 정렬 가능한 부분인데 백엔드 개발자가
프론트 단에서 정렬하고 사용하라고 하면 마땅히 백에서 해주어야 한다는 근거가 조금 부족하게 느껴졌다.

물론 연산이 많아지면 그땐 이야기를 해야하겠지만...

두번째로 다크모드의 색상을 저렇게 전역적으로 관리하고 props로 넘겨주는 것이 맞는 것인지 자세히 모르겠다.

물론 필자는 저게 가장 효율적인 방법이라 생각이 들지만 이것을 피드백 해줄 개발자가 주변에 없기 때문에
옳바른 길을 가고 있더라도 스스로를 의심하게 만드는 것 같다.


0개의 댓글