프론트엔드 데브코스 5기 TIL 51 - 재사용 가능한 컴포넌트 연습(3)

김영현·2023년 12월 8일
0

TIL

목록 보기
60/129

Progress

로딩바, 진행바.


출처 : https://www.invisionapp.com/inside-design/progress-bar/
TED내용인데 프로그레스바가 사용자의 행복에 영향을 끼친다는 연구결과다. 정확성은 상관없다고 한다 ㅎㅎ

Slider컴포넌트와 거의 흡사해서 다 올리진 않겠다.
대신 신기한 스타일을 가져와봄

  background-size: 10px;
  background-image: linear-gradient(
    45deg,
    rgba(255, 255, 255, 0.15) 25%,
    transparent 25%,
    transparent 50%,
    rgba(255, 255, 255, 0.15) 50%,
    rgba(255, 255, 255, 0.15) 75%,
    transparent 75%,
    transparent 100%
  );
  animation: move 1000ms linear infinite;

  @keyframes move {
    from {
      background-position: 0 0;
    }
    to {
      background-position: 40px 0;
    }
  }

이렇게 하면 프로그레스바가 움직이는것처럼 보이게 된다.

움짤을 따와야하나..?


Divider


텍스트 사이에 선을 말하는 것!
내가 만들었던 CrossLine컴포넌트와 흡사하다!

const Divider = ({ type = "horizontal", size = 8, ...props }) => {
  const dividerStyle = {
    margin: type === "vertical" ? `0 ${size}px` : `${size}px 0`,
  };
  return (
    <Line
      {...props}
      className={type}
      style={{ dividerStyle, ...props.style }}
    />
  );
};

<hr>태그가 뭔지 몰랐는데, 수평 가로선을 의미하는 시멘틱 태그가 존재했다.
항상 잘 알고써야지...


Skeleton

상태가 업데이트되기 전에 보여주는 UI

출처:https://www.whitespectre.com/ideas/skeleton-screens-for-a-better-loading-experience-in-react/

이런걸 생각하면 된다. 핵심은 styledComponent로 잘게 쪼갠 뒤, 모양에 맞게 사용하는 것이다.

//Base
const Base = styled.div`
  display: inline-block;
  border-radius: 4px;
  background-image: linear-gradient(
    98deg,
    #dfe3e8 0px,
    #efefef 40px,
    #dfe3e8 80px
  );
  background-size: 200% 100%;
  background-position: 0 center;
`;

//Box
const Box = styled(Base)`
  width: ${({ width }) => (typeof width === "number" ? `${width}px` : width)};
  height: ${({ height }) =>
    typeof height === "number" ? `${height}px` : height};
`;

만약 원형 스켈레톤UI가 필요하다면, 너비-높이를 통일 후 border-radius만 50%주면 되겠지?


Input

아~주 자주 사용하는 Input컴포넌트를 재사용 가능하게 만들어보자. 보통 label로 많이 감싸서 사용한다.

이번에도 스타일은 제외!

    <Wrapper {...wrapperProps}>
      <Label>{label}</Label>
      <StyledInput
        invalid={invalid}
        required={required}
        readOnly={readOnly}
        disabled={disabled}
        {...props}
      />
    </Wrapper>

별거 없구만!
사소한 의문점은 하나있다. HTML속성들은 대부분 카멜케이스를 지키지 않고 소문자로 작성하는데 왜 readOnly만 카멜케이스인가?
=> 생각해보니 html이 아니라 jsx구나...바보네?


Select

select태그를 이용한 컴포넌트. 참고로 placeholderoptions중 하나를 hidden으로 만들어 사용한다.

const formattedData = data.map((item) =>
    typeof item === "string" ? { label: item, value: item } : item
  );
  const options = formattedData.map((item) => (
    <options key={item.value} value={item.value}>
      {item.label}
    </options>
  ));

  if (placeholder) {
    options.unshift(
      <options key="placeholder" value="" hidden>
        {placeholder}
      </options>
    );
  }
  return (
    <Wrapper block={block} {...wrapperProps}>
      <Label>{label}</Label>
      <StyledSelect
        invalid={invalid}
        required={required}
        disabled={disabled}
        {...props}
      >
        {options}
      </StyledSelect>
    </Wrapper>

Flux

Flux는 디자인패턴만 들어봤는데, 무슨 컴포넌트인고 하니 flex를 이용하여 가로-세로 레이아웃을 컴포넌트 단위로 추상화 해놓은거다. grid와 기능이 비슷해보이는데, 아마 지원되지 않는 브라우저를 위해서 사용하는걸까?

클래스를 이용하면 간단하지만, 컴포넌트로 만들면 좀 더 확장성이 좋은게 장점인듯하다.

생각보다 코드가 더 길어져서 최대한 함축해보겠음..

//Row.js
const AlignToCSSValue = {
  top: "flex-start",
  middle: "center",
  bottom: "flex-end",
};
const StyleRow = styled.div`
  display: flex;
  flex-wrap: wrap;
  box-sizing: border-box;

  justify-content: ${({ justify }) => justify};
  align-items: ${({ align }) => AlignToCSSValue[align]};
`;

const Row = ({ children, justify, align, gutter, ...props }) => {
  return (
    <StyleRow {...props} align={align} justify={justify}>
      {children}
    </StyleRow>
  );
};

//Col.js
const StyleCol = styled.div`
  max-width: 100%fit-content;
  box-sizing: border-box;

  width: ${({ span }) => span && `${(span / 12) * 100}%`};
  margin: ${({ offset }) => offset && `${(offset / 12) * 100}%`};
`;

const Col = ({ children, span, offset, ...props }) => {
  return (
    <StyleCol {...props} span={span} offset={offset}>
      {children}
    </StyleCol>
  );
};

Row내부에 gap을 주고싶으면 contextAPI를 이용하여 값을 공유해서 Row에게만 할당하여 사용하는게 편하다. Col에 하나씩 줄 필요가 없으니까.

다만 고민되는점은 Row컴포넌트 내부 너비를 초과해서 Col컴포넌트를 렌더링하면 줄이 바뀐다. 코드를 읽을때 하나의 Row에 20개의 Col이존재한다면 분명 20개의 열이 있을거라고 볼텐데...예측하지 못한 동작이 아닐까?물론 일부러 줄바꿈 되라고 flex-wrap을 사용하셨다.
다만 줄바꿈이 된다면 Row를 하나 더 사용해야되지 않을까...


나머지 2개는 잘 모르는부분이없어서 숙지만 해뒀다..!


느낀점

컴포넌트를 굉장히 작은 단위로 잘 쪼개시고 특히 합성 기법을 잘 사용하셔서 좋았다. 재사용 가능한 컴포넌트가 어떤것인지 맛봄! 특히 children프롭을 이용하여 다루는게 정말 신기했다.

다만 프롭스의 갯수가 좀 많은점등은 고민이다. Select컴포넌트의 options도 아예 합성컴포넌트로 쪼개서 하신 것들 처럼 Selec.option이렇게 사용해보면 좋을것 같기도?ㅎㅎ

profile
모르는 것을 모른다고 하기

0개의 댓글