Chapter1. Component Driven Development
1-1. CDD
1-2. CSS-in-JS
Chapter2. CDD 개발 도구
2-1. Styled Components
2-2. Storybook
Chapter3. useRef
이러한 종류의 고민을 해결하기 위해 등장한 개발 방법이 바로 Component Driven Development (CDD)임
레고처럼 조립해 나갈 수 있는, 부품 단위로 UI 컴포넌트를 만들어 나가는 개발방법론
퀴즈
CDD를 활용한 개발 방법은 컴포넌트 단위로 만들어 페이지를 조립하는 개발 방식인 상향식 개발에 가깝습니다.
인터넷이 만들어진 이후 기술의 발달과 함께 사용자들은 다양한 환경(디바이스)에서 인터넷을 사용하기 시작함
-> 이에 따라 개발자들의 CSS 작성 방식도 진화해옴
💡 CSS 작업을 효율적으로 하기 위해 구조화된 CSS의 필요성이 대두
css 전처리기(CSS Preprocessor)의 등장
CSS가 구조적으로 작성될 수 있게 도움을 주는 도구
SASS
CSS 전처리기 중에서 가장 유명한 SASS는 Syntactically Awesome Style Sheets의 약자로 CSS를 확장해 주는 스크립팅 언어입니다.
CSS 방법론의 대두
CSS 전처리기의 문제를 보완하기 위해 BEM
, OOCSS
, SMACSS
같은 CSS 방법론이 대두되었습니다. 각각의 장단점이 있으나 결국 세 방법론 모두 같은 지향점을 가지고 있음
방법론의 공통 지향점은 다음과 같음
BEM
Block, Element, Modifier로 구분하여 클래스명을 작성하는 방법
Block, Element, Modifier 각각은 —와 __로 구분
하지만 이러한 방법론들에서도 문제점이 발생하기 시작합니다. 클래스명 선택자가 장황해지고, 이런 긴 클래스명 때문에 마크업이 불필요하게 커지며, 재사용하려고 할 때마다 모든 UI 컴포넌트를 명시적으로 확장해야만 했음
💡 encapsulation : 객체의 속성과 행위를 하나로 묶고 실제 구현 내용 일부를 외부에 감추어 은닉하는 개념
CSS-in-JS의 등장 - Styled-Component
애플리케이션으로 개발 방향이 진화하면서 컴포넌트 단위의 개발은 캡슐화의 중요성을 불러왔고,
결국 CSS도 컴포넌트 영역으로 불러들이기 위해 CSS-in-JS가 탄생해서 이 문제를 정확하게 해결하게 됨
Styled-Component는 기능적(Functional) 혹은 상태를 가진 컴포넌트들로부터 UI를 완전히 분리해 사용할 수 있는 아주 단순한 패턴을 제공함
React로 Component Driven Development를 할 수 있는 대표적인 도구
CSS를 컴포넌트화시키는, React 환경에서 사용 가능한 라이브러리
# with npm
$ npm install --save styled-components
# with yarn
$ yarn add styled-components
Styled Components는 package.json에 다음 코드를 추가하도록 권장하고 잇음
아래의 코드를 추가하면 여러 버전의 Styled Components가 설치되어 발생하는 문제를 줄여줌
{
"resolutions": {
"styled-components": "^5"
}
}
그 다음 작업할 파일에 Styled Components를 불러와주면 사용 준비는 완료입니다!
import styled from "styled-components"
Styled Components는 ES6의 Templete Literals 문법을 사용합니다. 즉, 따옴표가 아닌 백틱을 사용한다.
이렇게 만든 컴포넌트를 React 컴포넌트를 사용하듯 리턴문 안에 작성해주면 스타일이 적용된 컴포넌트가 렌더된다.
import styled from "styled-components";
const PinkBtn = styled.button`
background-color:pink;
color:white;
font-size:60px;
`;
export default function App() {
return <PinkBtn>pinkpink</PinkBtn>
}
컴포넌트를 선언하고 styled() 에 재활용할 컴포넌트를 전달해준 다음, 추가하고 싶은 스타일 속성을 작성
import styled from "styled-components";
const BlueButton = styled.button`
background-color: blue;
color: white;
`;
//만들어진 컴포넌트를 재활용해 컴포넌트를 만들 수 있습니다.
const BigBlueButton = styled(BlueButton)`
padding: 10px;
margin-top: 10px;
`;
//재활용한 컴포넌트를 재활용할 수도 있습니다.
const BigRedButton = styled(BigBlueButton)`
background-color: red;
`;
export default function App() {
return (
<>
<BlueButton>Blue Button</BlueButton>
<br />
<BigBlueButton>Big Blue Button</BigBlueButton>
<br />
<BigRedButton>Big Red Button</BigRedButton>
</>
);
}
Styled Component로 만든 컴포넌트도 React 컴포넌트처럼 props를 내려줄 수 있음
Styled Components는 템플릿 리터럴 문법( ${ } )을 사용하여 JavaScript 코드를 사용할 수 있습니다. props를 받아오려면 props를 인자로 받는 함수를 만들어 사용하면 됨
위 코드의 경우는 삼항연산자를 활용해 <Button
> 컴포넌트에 skyblue
라는 props가 있는지 확인하고, 있으면 배경색으로 skyblue
를, 없을 경우 white
를 지정해주는 코드
import styled from "styled-components";
//받아온 prop에 따라 조건부 렌더링이 가능합니다.
const Btn = styled.button`
background-color:${(props)=> (props.skyblue ? 'skyblue' : 'red')};
color: white;
`
export default function App() {
return (
<>
<Btn skyblue>나는 하늘색</Btn>
<Btn>나는 정열의 빨강</Btn>
</>
)
}
3번과 비슷하게, props의 값을 통째로 활용해서 컴포넌트 렌더링에 활용할 수 있음
똑같이 삼항연산자를 사용하고 있지만, 이번에는 props.color
가 없다면 white
를, props.color
가 있다면 props.color
의 값을 그대로 가져와서 스타일 속성 값으로 리턴해주고 있는 것을 볼 수 있습니다. 그 결과 color
라는 이름으로 받은 props의 값으로 배경색이 지정된 것을 확인할 수 있음
꼭 삼항연산자만 사용해야하는 것은 아닙니다. JavaScript 코드라면 무엇이든 사용할 수 있으므로 원하는 값을 사용할 수 있도록 함수 코드를 만들어서 사용하면 됨
import styled from "styled-components";
//받아온 prop 값을 그대로 이용해 렌더링할 수도 있습니다
const Button1 = styled.button`
background: ${(props) => (props.color ? props.color : "white")};
`;
//다음과 같은 형식으로도 활용할 수 있습니다.
const Button2 = styled.button`
background: ${(props) => props.color || "white"};
`;
export default function App() {
return (
<>
<Button1>Button1</Button1>
<Button1 color="orange">Button1</Button1>
<Button1 color="tomato">Button1</Button1>
<br />
<Button2>Button2</Button2>
<Button2 color="pink">Button2</Button2>
<Button2 color="turquoise">Button2</Button2>
</>
);
}
우선 전역 스타일을 설정하기 위해 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>
</>
);
}
예제
import styled from "styled-components";
import { createGlobalStyle } from "styled-components";
const GlobalStyle = createGlobalStyle`
button {
padding : 10px;
margin : 20px;
border-radius : 100%;
}
`;
//받아온 prop에 따라 조건부 렌더링이 가능합니다.
const Button1 = styled.button`
background: ${(props) => (props.skyblue ? "skyblue" : "white")};
`;
export default function App() {
return (
<>
<GlobalStyle />
<Button1>Button1</Button1>
<Button1 skyblue>Button1</Button1>
</>
);
}
전역 스타일링 적용전
적용후
컴포넌트들을 문서화하고 관리할 수 있는 UI 개발 도구
Component Driven Development 가 트렌드로 자리 잡게 되면서 이를 지원하는 도구 중 하나인 Component Explorer (컴포넌트 탐색기) 가 등장함. Component Explorer에는 많은 UI 개발 도구가 다양하게 있는데 그중 하나가 Storybook
임
Storybook은 UI 개발 즉, Component Driven Development를 하기 위한 도구입니다. - 각각의 컴포넌트들을 따로 볼 수 있게 구성해 주어 한 번에 하나의 컴포넌트에서 작업할 수 있음
- 전체 UI를 한눈에 보고 개발할 수 있습니다.
React 애플리케이션을 만들 때 DOM을 직접 조작하는 것은 지양해야한다고 배웠음. 하지만, 개발을 하다보면 DOM을 직접 건드려야하는 상황이 발생하기도 함. 이럴 때 사용할 수 있는 것이 바로 useRef
라는 Hook 함수임
useRef
의 필요성에대해 이해한다.useRef
가 필요한 상황들을 기억한다.useRef
의 사용법을 이해한다.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 onBtnclick = () => {
inputEl.current.focus();
}
return (
<>
<input ref={inputEl} type="text"></input>
<button onClick={onBtnClick}>Focus the input</button>
</>
)
}
제시된 상황 제외한 대부분의 경우 기본 React 문법을 벗어나 useRef 를 남용하는 것은 부적절하고, React의 특징이자 장점인 선언형 프로그래밍 원칙과 배치되기 때문에, 조심해서 사용해야 함
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") { //onKeyup이벤트의 key가 Enter라면
if (event.target === firstRef.current) {
console.log(event.target, firstRef.current);
console.log(firstRef); //
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>별 헤는 밤</label>
<input ref={firstRef} onKeyUp={handleInput} />
</div>
<div>
<label>메밀꽃 필 무렵</label>
<input ref={secondRef} onKeyUp={handleInput} />
</div>
<div>
<label>즐거운 편지</label>
<input ref={thirdRef} onKeyUp={handleInput} />
</div>
</div>
);
};
e.key
ㅂ ㅕ ㄹ Enter
event
onkeyup이벤트
event.target
이벤트가 발생한 인풋
firstRef.current
첫번째 input엘리먼트의 주소가 담긴 변수 firstRef의 current 속성
=== 첫번째 input엘리먼트
input 엘리먼트 주소가 담긴 객체 형태의 변수
import { useRef } from "react";
export default function App() {
const videoRef = useRef(null);
const playVideo = () => {
videoRef.current.play();
console.log(videoRef);
};
const pauseVideo = () => {
videoRef.current.pause();
};
return (
<div>
<div>
<button onClick={playVideo}>play</button>
<button onClick={pauseVideo}>pause</button>
</div>
<video ref={videoRef}>
<source
type="video/mp4"
src="https://player.vimeo.com/external/544643152.sd.mp4?s=7dbf132a4774254dde51f4f9baabbd92f6941282&profile_id=165"
/>
</video>
</div>
);
}
스토리북
useRef