JadeCode

[리뷰] Custom Component 본문

개발/웹

[리뷰] Custom Component

z-zero 2022. 6. 17. 14:00

CDD (Component Driven Development)

디자인과 개발 단계에서부터 재사용할 수 있는 UI컴포넌트를 미리 디자인하고 개발할 수 있다.

예를들어 버튼의 경우 여러 페이지에서 같은 디자인으로 필요하다고 할 때 버튼을 컴포넌트화하면 재사용 할 수 있다.

CDD개발방법을 사용하여 부붐 단위로 UI컴포넌트를 만들어가는 개발을 진행하는 방향(상향식)이 좋다.

 

CSS-in-JS

CSS 전처리기란 CSS가 구조적으로 작성될 수 있게 도움을 주는 도구이다.

가장 유명한 SASS는 Syntactically Awesome Style Sheets의 약자로 CSS를 확장해 주는 스크립팅 언어이다.

 

즉 CSS를 만들어주는 언어로, 자바스크립트처럼 특정 속성의 값을 변수로 선언하여 필요한 곳에 사용할 수 있게한다.

(ex: $mainColor:#020202)

하지만 SASS가 "CSS의 구조화"를 해결해주는 것의 장점보다 다른 문제들을 더 많이 만들어낸다. 결국 전처리기가 내부에서 어떤 작업을 하는지는 알지 못한 채, 스타일이 겹치는 문제를 해결하기 위해 단순히 계층 구조를 만들어 내는 것에 의지하게 되었고, 그 결과 컴파일된 CSS의 용량은 어마어마하게 커지게 되었다.

이러한 CSS 전처리기의 문제를 보완하기 위해 BEM, OOCSS, SMACSS같은 CSS방법론이 대두되었다.  이것들의 공통된 지향점은 코드의 재사용, 코드의 간결화(유지 보수 용이) , 코드의 확장성 , 코드의 예측성(클래스 명으로 의미 예측) 등이 있다.

이러한 방법들을 규칙으로 정해두는 것이 매우 중요한 작업이다.

하지만 이러한 방법들도 문제점이 발생한다. 클래스명이 장황해지고, 마크업이 불필요하게 커지며, 재사용하려고 할 때 마다 모든 UI컴포넌트를 명시적으로 확장해야만 했다. 또한 언어 로직 상 진정한 캡슐화의 개념이 없어, 클래스명에 의존할 수 밖에 없다.

애플리케이션으로 개발 방향이 진화하면서 컴포넌트 단위의 개발은 캡슐화의 중요성을 불러왔다. 결국 CSS도 컴포넌트 영역으로 불러들이기 위해 CSS-in-JS가 탄생하게 되었다. 대표적으로는 Styled-Component가 있다.

  특징 장점 단점
CSS 기본적인 스타일링 방법 - 일관된 패턴을 갖기 어려움, !import 남용
SASS(전처리기:Preprocessor) 프로그래밍 방법론을 도입하여, 컴파일된 CSS를 만들어내는 전처리기 변수/함수/상속 개념을 활용하여 재사용 가능, CSS의 구조화 전처리 과정이필요, 디버깅의 어려움이 있음, 컴파일한 CSS파일이 거대해짐
BEM CSS 클래스명 작성에 일관된 패턴을 강제하는 방법론 네이밍으로 문제 해결, 전처리 과정 불필요 선택자의 이름이 장황하고 클래스 목록이 너무 많아짐
CSS-in-JS 컴포넌트 기반으로 CSS를 작성할 수 있게 도와줌 CSS를 컴포넌트 안으로 캡슐화, 네이밍이나 최적화를 신경 쓸 필요가 없음 빠른 페이지 로드에 불리함

StyledComponent

//설치
npm install --save styled-components

//아래의 코드를 추가하면 여러 버전의 Styled Components가 설치되어 발생하는 문제를 줄여준다
{
  "resolutions": {
    "styled-components": "^5"
  }
}

//사용
import styled from "styled-components"
//사용
<Button color="red"/>
//정의
//props를 받아서 버튼의 배경 변경
const Button = styled.button`
	background-color: ${(props)=>props.color||"black"}
`

//확장
<BigButton color="red"/>
//정의(위에서 정의한 Button사용)
const BigButton = styled(Button)`
	font-size:2rem;
`

전역 스타일 설정

import { createGlobalStyle } from "styled-components";
const GlobalStyle = createGlobalStyle`
	button {
		padding : 5px;
        margin : 2px;
        border-radius : 5px;
	}
`

//최상위 컴포넌트 APP()
function App() {
	return (
		<>
			<GlobalStyle />
			<button>전역 스타일 적용하기</button>
		</>
	);
}

Storybook

Component Driven Development 가 트렌드로 자리 잡게 되면서 이를 지원하는 도구 중 하나인 Component Explorer (컴포넌트 탐색기) 가 등장했다. Component Explorer에는 많은 UI 개발 도구가 다양하게 있는데 그중 하나가 Storybook이다.

//앱 생성
npx create-react-app storybook-practice

//storybook설치
npx storybook init

//설치 완료 후 /.storybook폴더와 /src/stories폴더가 생성된 것을 확인 가능

//실행
npm run storybook//localhost:6006으로 접근하여 storybook 실행
//src 폴더안에 Title.js파일을 하나 만들고 간단한 React Component만들어 export

import React from "react";

// title은 h1 요소의 textContent, textColor은 글자색이 되는 props입니다.
const Title = ({title, textColor}) => (
<h1 style={{color: textColor}}>{title}</h1>
);

export default Title;

//같은 위치인 src폴더 안에 Title.stories.js 파일을 하나 만든다. /.storybook안에있는 Storybook설정 파일에 의해서 컴포넌트 파일과 똑같은 이름에 .stories를 붙여 파일을 만들면 알아서 스토리로 인식한다.
//Title.stories.js
// 앞에서 작성한 컴포넌트를 불러온다.
import Title from "./Title";

// title : 컴포넌트 이름으로, '/'를 넣어 카테고리화 할 수 있다.
//         이후 예시에서 조금 더 자세히 설명합니다.
// component : 어떤 컴포넌트를 가져와서 스토리로 만들 것인지 명시한다.
// argTypes : 컴포넌트에 필요한 전달인자의 종류와 타입을 정해준다.
//            지금은 title, textColor이라는 전달인자에 text 타입이 필요함을 의미한다.
export default {
    title: "Practice/Title", 
    component: Title,
    argTypes: {
        title: { control: "text" },
        textColor: {control: "text"}
    }
}

// 템플릿을 만들어줍니다. 이 템플릿에서는
// Title 컴포넌트가 args를 전달받아 props로 내려준다.
const Template = (args) => <Title {...args} />

// Storybook에서 확인하고 싶은 컴포넌트는 export const로 작성한다.
// 템플릿을 사용하여 Storybook에 넣어줄 스토리를 하나 만들어주었다.
// Template.bind({}); 는 정해진 문법이라고 생각하고 사용하시면 된다.
export const RedTitle = Template.bind({});

// 만들어준 스토리의 전달인자를 작성해줍니다.
RedTitle.args= {
    title: "Red Title",
    textColor: "red"
}

// 스토리를 하나 더 만든다.
export const BlueTitle = Template.bind({});

// 스토리의 전달인자를 작성해준다.
BlueTitle.args= {
    title: "Blue Title",
    textColor: "blue"
}

export const StorybookTitle = (args)=>{
	return <Title {...args} />
}//이 스토리는 템플릿을 활용하지 않고 바로 전달인자를 받고있다. 저장 후 다시 Storybook을 확인해보면, 6006포트에서 전달인자를 직접 작성해서 거기에 맞춰 모습이 변하는 것을 볼 수 있다.

//저장하고 다시 실행(npm run storybook)

전달인자를 직접 받으면서 Styled Component를 사용해 만든 컴포넌트를 스토리로 만들어보기

//Button.js
import React from "react";
import styled from "styled-components"

const StyledButton = styled.Button`
  background-color${(props)=>props.color||"white"};
  width:${(props)=>props.size === "big" ?"200px":"100px"};
  height:${(props)=>props.size==="big"?"80px":"40px"};
`

const Button = ({color,size,text})=>{
  return(
    <StyledButton color={color} size={size}>{text}</StyledButton>
  )
}
export default Button;

//Button.stories.js
import Button from "./Button";

export default{
	title:"Practice/Button",
    component:Button,
    argTypes:{
    	color:{control:"color"},
        size:{control:{type:"radio",options:["big","small"]}
        text:{control:"text"}
        
    }
}

export const StorybookButton = (args)=>{
	return(
    	<Button {...args}</Button>
    )
}

https://storybook.js.org/docs/react/get-started/introduction/

 

Introduction to Storybook

Storybook is a frontend workshop for building UI components and pages in isolation. Thousands of teams use it for UI development, testing, and documentation. It’s open source and free.

storybook.js.org

useRef

useRef를 사용하여 DOM엘리멘트 조작

focus, text selection, media playback, 애니메이션 적용, d3.js, greensock등 DOM기반 라이브러리를 활용할 때 팔요

// useRef예시, focus
import React, { useRef } from "react";

const Focus = () => {
  const firstRef = useRef(null);
  const secondRef = useRef(null);
  const thirdRef = useRef(null);

  const handleInput = (event) => {
    console.log(event.key, event);
    if (event.key === "Enter") {
      if (event.target === firstRef.current) {
        secondRef.current.focus();
        event.target.value = "";
      } else if (event.target === secondRef.current) {
        thirdRef.current.focus();
        event.target.value = "";
      } else if (event.target === thirdRef.current) {
        firstRef.current.focus();
        event.target.value = "";
      } else {
        return;
      }
    }
  };

  return (
    <div>
      <h1>타자연습</h1>
      <h3>각 단어를 바르게 입력하고 엔터를 누르세요.</h3>
      <div>
        <label>hello </label>
        <input ref={firstRef} onKeyUp={handleInput} />
      </div>
      <div>
        <label>world </label>
        <input ref={secondRef} onKeyUp={handleInput} />
      </div>
      <div>
        <label>cool </label>
        <input ref={thirdRef} onKeyUp={handleInput} />
      </div>
    </div>
  );
};

export default Focus;
//useRef 예시, media playback
import { useRef } from "react";

export default function App() {
  const videoRef = useRef(null);

  const playVideo = () => {
    videoRef.current.play();
    console.log(videoRef.current);
  };

  const pauseVideo = () => {
    videoRef.current.pause();
    videoRef.current.remove();
  };

  return (
    <div className="App">
      <div>
        <button onClick={playVideo}>Play</button>
        <button onClick={pauseVideo}>Pause</button>
      </div>
      <video ref={videoRef} width="320" height="240" controls>
        <source
          type="video/mp4"
          src="https://player.vimeo.com/external/544643152.sd.mp4?s=7dbf132a4774254dde51f4f9baabbd92f6941282&profile_id=165"
        />
      </video>
    </div>
  );
}

'개발 > ' 카테고리의 다른 글

[리뷰] 사용자 친화  (0) 2022.06.28
[리뷰] REDUX  (0) 2022.06.27
[리뷰] UI/ UX  (0) 2022.06.15
[리뷰] WEB Server(CORS,SOP)  (0) 2022.06.12
[리뷰] useEffect, 클라이언트 AJAX요청  (0) 2022.06.09
Comments