Component Driven Development (CDD) : 부품 단위로 UI 컴포넌트를 만들어 나가는 개발.
재사용할 수 있는 컴포넌트를 개발 가능하며, 컴포넌트 단위로 만들어 페이지를 조립하는 개발 방식인 상향식 개발에 가까움.
CSS Preprocessor (CSS 전처리기) : CSS 가 구조적으로 작성될 수 있게 도움을 주는 도구. 프로그래밍 개념(변수, 함수, 상속 등) 활용
SASS (Syntactically Awesome Style Sheets) : CSS 를 확장해 주는 스크립팅 언어. 변수 및 여러곳에서 변수 재사용 등의 기능을 가짐. SCSS 코드를 읽어서 전처리한 후 컴파일해서 전역 CSS 번들 파일을 만들어주는 전처리기(Preprocessor)의 역할을 함.
그러나 CSS 구조화를 해결해주는 것의 장점보다 컴파일된 CSS의 용량이 어마어마하게 커지는 단점이 발견됨.
BEM, OOCSS, SMACSS 같은 CSS 방법론의 대두
CSS-in-JS의 등장 - Styled-Component
Styled-Component는 기능적(Functional) 혹은 상태를 가진 컴포넌트들로부터 UI를 완전히 분리해 사용할 수 있는 아주 단순한 패턴을 제공함.
CSS in JS 라이브러리를 사용하면 CSS도 쉽게 Javascript 안에 넣어줄 수 있으므로, HTML + JS + CSS까지 묶어서 하나의 JS파일 안에서 컴포넌트 단위로 개발할 수 있게 됩니다. 이런 CSS in JS 라이브러리 중에서 현재 가장 인기 있는 라이브러리가 바로 Styled Components 입니다.
# with npm
$ npm install --save styled-components
# with yarn
$ yarn add styled-components
{
"resolutions": {
"styled-components": "^5"
}
}
import styled from "styled-components"
Styled Components는 ES6의 Templete Literals 문법, 즉 따옴표가 아닌 백틱(`)을 사용함
컴포넌트를 선언한 후 styled.태그종류
를 할당하고, 백틱 안에 기존에 CSS를 작성하던 문법과 똑같이 스타일 속성을 작성해주면 됩니다. 이렇게 만든 컴포넌트를 React 컴포넌트를 사용하듯 리턴문 안에 작성해주면 스타일이 적용된 컴포넌트가 렌더되는 것을 확인할 수 있습니다.
https://codesandbox.io/s/confident-driscoll-w6i4t6?file=/src/App.js
이미 만들어진 컴포넌트를 재활용해서 새로운 컴포넌트를 만들 수도 있습니다. 컴포넌트를 선언하고 styled()
에 재활용할 컴포넌트를 전달해준 다음, 추가하고 싶은 스타일 속성을 작성해주면 됩니다.
이미 만들어진 컴포넌트를 재활용해서 새로운 컴포넌트를 만들 수도 있습니다. 컴포넌트를 선언하고 styled()
에 재활용할 컴포넌트를 전달해준 다음, 추가하고 싶은 스타일 속성을 작성해주면 됩니다.
https://codesandbox.io/s/proud-platform-kt7obx
Styled Component로 만든 컴포넌트도 React 컴포넌트처럼 props를 내려줄 수 있습니다. 내려준 props 값에 따라서 컴포넌트를 렌더링하는 것도 가능합니다.
Styled Components는 템플릿 리터럴 문법( ${ }
)을 사용하여 JavaScript 코드를 사용할 수 있습니다. props를 받아오려면 props를 인자로 받는 함수를 만들어 사용하면 됩니다.
위 코드의 경우는 삼항연산자를 활용해 <Button>
컴포넌트에 skyblue
라는 props가 있는지 확인하고, 있으면 배경색으로 skyblue
를, 없을 경우 white
를 지정해주는 코드입니다. 이 코드에 따라 렌더링된 <Button>
컴포넌트는 아래 그림과 같을 것입니다.
Button1 의 경우는 skyblue
라는 props가 있어 배경색이 skyblue
로 지정됐고, Button2의 경우는 props가 아예 없어 배경색이 white
로 지정된 것을 확인할 수 있습니다.
3번과 비슷하게, props의 값을 통째로 활용해서 컴포넌트 렌더링에 활용할 수 있습니다.
똑같이 삼항연산자를 사용하고 있지만, 이번에는 props.color
가 없다면 white
를, props.color
가 있다면 props.color
의 값을 그대로 가져와서 스타일 속성 값으로 리턴해주고 있는 것을 볼 수 있습니다. 그 결과 color
라는 이름으로 받은 props의 값으로 배경색이 지정된 것을 확인할 수 있습니다.
꼭 삼항연산자만 사용해야하는 것은 아닙니다. JavaScript 코드라면 무엇이든 사용할 수 있으므로 원하는 값을 사용할 수 있도록 함수 코드를 만들어서 사용하면 됩니다. 따라서 위 예시와 같은 코드도 활용할 수 있을 것입니다. props도 원하는 만큼 받아서 사용할 수 있으니 다양하게 활용해보세요.
https://codesandbox.io/s/exciting-kapitsa-sghyky?file=/src/App.js
스타일을 컴포넌트로 만들 수 있다는 것은 좋지만, 전역에 스타일을 설정하고 싶을 땐 어떻게하면 좋을까요? Styled Components는 이런 경우를 대비한 컴포넌트도 준비해놓았습니다.
우선 전역 스타일을 설정하기 위해 Styled Components에서 createGlobalStyle
함수를 불러옵니다.
import { createGlobalStyle } from "styled-components";
그 다음 이 함수를 사용해 CSS 파일에서 작성하듯 설정해주고 싶은 스타일을 작성합니다.
const GlobalStyle = createGlobalStyle`
button {
padding : 5px;
margin : 2px;
border-radius : 5px;
}
`
이렇게 만들어진 <GlobalStyle>
컴포넌트를 최상위 컴포넌트에서 사용해주면 전역에 <GlobalStyle>
컴포넌트의 스타일이 적용됩니다.
function App() {
return (
<>
<GlobalStyle />
<Button>전역 스타일 적용하기</Button>
</>);
}
잘 살펴보면 3. Props 활용하기의 예제에서 이미 위 코드와 같은 전역 스타일이 설정되어 있음을 알 수 있습니다. 코드를 보고 <GlobalStyle>
이 어떻게 적용되어 있는지 살펴보세요.
hover 속성 적용하기
const RoundBtn = styled.button`
background: ${(props) => props.color || "white"};
border-radius: 35px;
// hover 를 넣을 스타일 컴포넌트 안에 &hover 를 넣고 그 안에 호버시에 변경될 CSS 내용을 적어주면 됨.
&:hover {
background: purple;
color: white;
}
`;
Storybook은 UI 개발 즉, Component Driven Development를 하기 위한 도구입니다. 각각의 컴포넌트들을 따로 볼 수 있게 구성해 주어 한 번에 하나의 컴포넌트에서 작업할 수 있습니다. 복잡한 개발 스택을 시작하거나, 특정 데이터를 데이터베이스로 강제 이동하거나, 애플리케이션을 탐색할 필요 없이 전체 UI를 한눈에 보고 개발할 수 있습니다.
Storybook은 재사용성을 확대하기 위해 컴포넌트를 문서화하고, 자동으로 컴포넌트를 시각화하여 시뮬레이션할 수 있는 다양한 테스트 상태를 확인할 수 있습니다. 이를 통해 버그를 사전에 방지할 수 있도록 도와줍니다. 또한 테스트 및 개발 속도를 향상시키는 장점이 있으며, 애플리케이션 또한 의존성을 걱정하지 않고 빌드할 수 있습니다.
Storybook은 기본적으로 독립적인 개발 환경에서 실행됩니다. 개발자는 애플리케이션의 다양한 상황에 구애받지 않고 UI 컴포넌트를 집중적으로 개발할 수 있습니다.
아래 예시처럼 회사의 내부 개발자들을 위해 문서화(documentation)를 하여 회사의 UI 라이브러리로써 사용하거나, 외부 공개용 디자인 시스템(Design System)을 개발하기 위한 기본 플랫폼으로 사용할 수 있습니다.
https://github.com/storybookjs/storybook
지금까지 React를 공부하면서 익히셨던 내용을 돌이켜보면, React만 가지고 거의 대부분의 프론트엔드 요구사항을 구현할 수 있었습니다. DOM 지식이 필요 없다고 생각하실지도 모르겠습니다. 하지만 React로 모든 개발 요구 사항을 충족할 수는 없습니다. 아래와 같이 DOM 엘리먼트의 주소값을 활용해야 하는 경우 특히 그렇습니다.
React는 이런 예외적인 상황에서 useRef
로 DOM 노드, 엘리먼트, 그리고 React 컴포넌트 주소값을 참조할 수 있습니다. 아래 예시 코드처럼 작성하시면 주소값을 활용할 수 있습니다.
const 주소값을_담는_그릇 = useRef(참조자료형)
// 이제 주소값을_담는_그릇 변수에 어떤 주소값이든 담을 수 있습니다.
return (
<div>
<input ref={주소값을_담는_그릇} type="text" />
{/* React에서 사용 가능한 ref라는 속성에 주소값을_담는_그릇을 값으로 할당하면*/}
{/* 주소값을_담는_그릇 변수에는 input DOM 엘리먼트의 주소가 담깁니다. */}
{/* 향후 다른 컴포넌트에서 input DOM 엘리먼트를 활용할 수 있습니다. */}
</div>);
이 주소값은 컴포넌트가 re-render 되더라도 바뀌지 않습니다. 이 특성을 활용하여 아래의 제한된 상황에서 useRef
를 활용할 수 있습니다.
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>);
}
제시된 상황 제외한 대부분의 경우 기본 React 문법을 벗어나 useRef
를 남용하는 것은 부적절하고, React의 특징이자 장점인 선언형 프로그래밍 원칙과 배치되기 때문에, 조심해서 사용해야 합니다.