Component Driven Development란 재사용할 수 있는 UI컴포넌트를 미리 디자인하고 개발하여 레고처럼 조립해 나갈 수 있는 부품단위로 UI컴포넌트를 만들어나가는 개발방식이다.(컴포넌트기반 Bottom-up 방식 개발)
storybook은 UI개발, Component Driven Development를 하기위한 도구이다.
각각의 컴포넌트들을 따로 볼 수 있게 구성해주어 한번에 하나의 컴포넌트에서 작업할 수 있다.
위의 사진에서 왼쪽의 디렉토리의 각js파일들에 하나의 UI컴포넌트를 작성하고, storybook을 실행시키면 오른쪽과 같이 하나의 컴포넌트만을 보여주며, 컴포넌트의 코드를 수정하면 바로 변경된 컴포넌트의 모습을 확인할 수 있다.
UI컴포넌트의 재사용성을 확대하기 위해 컴포넌트를 문서화하고, 자동으로 시각화하여 즉시 결과를 확인할 수 있다.
storybook공식페이지
프로젝트의 규모나 복잡도가 점점 커짐에 따라 CSS를 작성하는 일관된 기준이 없는 것은 개발자들의 큰 고민거리이다.
이러한 문제점을 해결하기 위해 CSS가 구조적으로 작성될 수 있게 도와주는 CSS전처리기라는 개념이 등장했다.
여태껏 내가 배웠던 웹페이지를 만드는 방식은 HTML,CSS,JS파일을 각자 작성하는 방법과 react를 이용해 JS,CSS파일을 작성하는 방법 두가지였다.
항상 스타일을 정의하기위해 CSS파일을 따로 작성해야했다.
웹페이지가 점점 커지고 복잡해지면서 정의해야할 스타일의 수가 많아지고, 선택자를 과다하게 사용하면서 코드가 지저분해지고 가독성이 매우 떨어질 것이다.
어떤 스타일을 수정하기 위해서는 기존 CSS파일에서 수정해야할 스타일을 정의한 코드를 찾아야 할 것이고, 그 후에는 수정된 스타일이 다른 곳에 적용되 예상치 못한 변경이 있는지 찾아야한다.(이는 유지보수 측면에서 굉장히 불리하다.)
이를 해결하기 위해 등장한 것이 CSS전처리기이다.
CSS전처리기는 CSS가 구조적으로 작성될 수 있게 도움을 주는 도구이다.
CSS문서를 작성하면서 요구되는 됭장히 많은 반복적인 작업, 번거로운 작업 같은 문제점들을 해결하기 위해 프로그래밍 개념(변수,함수,상속등)을 활용하여 해결해준다.
다만 CSS전처리기 자체만으로는 웹서버가 인지하지 못하기 때문에 CSS전처리기에 맞는 컴파일러를 사용하여 컴파일을 해야하고 컴파일을 하게되면 기존에 사용했던 실제 CSS파일로 변환된다.
CSS전처리기를 통해 CSS파일을 잘 구조화할 수 있게 되었고, 거대한 CSS파일을 몇개의 작은 파일로 분리할 수 있는 방법을 찾게 되었다.
대표적인 CSS전처리기 Sass는 Syntatically Awesome Style Sheets의 약자로 CSS를 확장해주는 스크립팅 언어이다.
CSS를 만들어주는 스크립팅 언어로 특정속성의 값을 변수로 선언해 필요한 곳에 사용할 수 있고, 함수나 상속의 개념도 사용하여 작성한다.
CSS전처리기는 CSS의 구조화를 해결해주는 장점이 분명했지만, 전처리기내에서 어떤 작업이 이루어지는지 명확하게 알지 못했고, 컴파일된 CSS차일의 용량이 엄청크다는 단점도 분명 존재했다.
이러한 CSS전처리기의 단점을 보완하기 위해 BEM,OOCSS,SMACSS같은 CSS방법론이 등장하게 되었다.
각각의 차이점은 있었지만 3가지 방법론 모두 다음과 같은 공통의 지향점이 있다.
CSS방법론중 BEM은 Block,Element,Modifier로 구분하여 클래스명을 작성하는 방법이며 각각의 요소들은 __와 --로 구분한다.
ex) .header__navigation--navi-text
BEM방식은 클래스의 이름을 보고 해당 스타일이 적용될 블록 또는 요소를 명확하게 알 수 있고 클래스명이 되기 깨문에 HTML에서도 더 일관된 코딩구조를 만들어준다.
하지만 클래스명이 지나치게 길어지고 재사용하려고 할때마다 UI컴포넌트를 명시적으로 확장해야하는 단점이 있다.
(재사용하는 UI컴포넌트가 다른 블럭에 있다면 클래스명은 block부분이 바뀌게 되므로 완전히 다른 클래스명을 갖게된다. => 동일한 스타일을 다시 정의해야된다.)
애플리케이션으로 개발방향이 진화화면서 컴포넌트 단위의 개발은 캡슐화의 중요성을 불러왔다.
CSS는 탄생한 이후로 단 한번도 컴포넌트 기반의 방식을 위해 만들어진 적이 없었지만 결국 CSS도 컴포넌트영역으로 불러들이기 위해 CSS-in-JS가 탄생하게 된다.
CSS-in-JS는 단어 그대로 JS파일에서 스타일을 정의하는 CSS를 작성하는 방식을 말한다.
대표적인 CSS-in-JS라이브러리 Styled Component는 React의 컴포넌트 기반 개발환경에서 스타일링을 위한 CSS의 성능향상을 위해 탄생하였다.
Styled Component를 이용하면 기존 CSS문법을 활용하여 스타일 속성이 추가된 React컴포넌트를 생성할 수 있다.
// React프로젝트의 App.js
import styled from 'styled-components';
const Button=styled.button`
margin:5px;
font-size:20px;
color:black;
background-color:grey;
border: 1px solid black;
border-radius:5px;
`;
const FixedButton=styled(Button)`
font-weight:bold;
font-family:cursive;
background-color:palegreen;
`;
const CustomButton=styled(Button)`
color:${(props)=>props.color};
background-color:${(props)=>props.background||"gold"};
`;
const Container=styled.div`
text-align:center;
`;
function App() {
return (
<Container>
<div><Button>Styled-Button</Button> </div>
<div><FixedButton>Fixed-Button</FixedButton></div>
<div><CustomButton color='seashell' background='salmon'>Custom-Button1</CustomButton></div>
<div><CustomButton color='seashell'>Custom-Button2</CustomButton></div>
</Container>
);
}
export default App;
Styled Component는 React에 내장되지 않은 외부 라이브러리이므로 npm을 이용해 설치해줘야한다.
$ npm install styled-components
이렇게 정의된 컴포넌트는 리액트의 컴포넌트처럼 사용할 수 있다.const 컴포넌트이름 = styled.HTML태그`
CSS코드
` ;
const 컴포넌트이름=styled(기존컴포넌트)`
CSS코드
`;
위 코드의 컴포넌트들은 다음과 같이 렌더링된다.
스타일 속성을 지닌 컴포넌트를 정의할 때는 반드시 리액트 컴포넌트 밖에서 정의되어야 한다.
그렇지 않으면 리액트 컴포넌트가 리렌더링 될때마다 스타일 속성을 지닌 컴포넌트가 재정의되어, 렌더링 속도가 느려진다.
Styled Component의 특징
처음으로 배운 웹페이지를 만드는 방법은 HTML,CSS,JS파일을 각각 작성하는 방법이었다.
이벤트와 DOM API를 이용해 자바스크립트 코드를 이용해 이벤트에 반응하여 HTML문서의 내용을 조작할 수 있었다.
React를 처음 배웠을 때, state와 props를 배우기 전에 리액트를 이용해 만든 웹페이지를 interactive하게 만들기 위해 querySelector를 이용한 JS파일을 작성해 페이지가 렌더링되는(div#root가 있는)index.html에 적용시켰던 적이 있었다.
index.html은 텅비어있는 문서였지만, index.js가 실행되어 컴포넌트들이 렌더링된후에 작성한 JS파일이 적용될거라 생각했지만 아니었다.
(querySelector로 적어주었던 선택자들이 존재하지 않는다는 에러만 잔뜩봤던 기억이 있다.)
그후에 state와 props라는 개념과 addEventLisetener와 같은 메서드를 이용해서가 아닌 컴포넌트에 직접 onClick={callback}을 적어주는 방식으로 이벤트리스너를 적용시키는 개념을 배운후에는 querySelector와 같이 DOM 엘리먼트의 주소값을 직접 참조할 필요성을 딱히 느낀적이 없었다.
React의 useRef는 DOM엘리먼트의 주소값을 참조할 수 있게해주는 querySelector와 비슷하게 리액트 컴포넌트의 주소값을 참조할 수 있게 해준다.
//React프로젝트의 App.js
import {useRef} from 'react';
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
inputEl.current.value='';
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>initialize</button>
</>
);
}
function App() {
return (
<TextInputWithFocusButton></TextInputWithFocusButton>
);
}
export default App;
위의 코드는 리액트 공식문서의 useRef부분의 예제코드이다.
useRef는 current라는 프로퍼티를 갖는 객체를 반환한다.
inputEl이라는 변수에 담긴 객체를 ref={inputEl}과 같이 적어주면 inputEl의 current프로퍼티에 <input>
의 주솟값이 담기게된다.
따라서 onButtonClick함수의 inputEl.current로 <input>
에 접근할 수 있게된다.
참조된 useRef.current값은 컴포넌트가 리렌더링되더라도 변하지 않는다.
---추가
https://www.daleseo.com/react-hooks-use-ref/
useRef가 DOM엘리먼트에 접근하기 위해 필요한 것이라고 생각했는데 이분의 블로그를 보고 useRef의 더 중요한 포인트는 '컴포넌트가 리렌더링되더라도 current의 값이 변하지 않는다' 인 것 같다.
useRef는 리액트 컴포넌트의 주소값을 저장하기 위해 필요하다. => 참조된 컴포넌트의 주소값은 컴포넌트가 리렌더링되어도 변하지않는다.(X)
useRef는 current라는 프로퍼티가 있는 객체를 반환하는데 current에 저장된 값은 리렌더링되어도 변하지 않는다. => 이러한 특성을 이용하여 리액트 컴포넌트의 주소값을 저장할수 있다.(O)