✅ 불필요한 PROPS 복사 및 연산
🌈 결론
// 변경 전
function component({ value }) {
const [copyValue] = useState(무거운_연산(value));
return <div>{copyValue}</div>;
}
// 변경 후
function component({ value }) {
const [copyValue] = useMemo(() => 무거운_연산(value), [value]);
return <div>{copyValue}</div>;
}
✍️ 내용
props
로 전달 받은 값을useState
에서 셋팅하는 것이 아닌, 바로 사용하는 것이 좋음-
아래와 같이
props
로 전달 받은 값을 가지고 무거운 연산을 진행하면, 렌더링할 때마다 해당 컴포넌트가 호출되어서 연산을 지속적으로 하기 때문에 비효율적임 → 그래서 애초에props
로 전달하기 전에 이미 무거운 연산을 한 결과 값을props
로 전달을 해야 함, 아니면useMemo
를 사용function CopyProps({ value }) { const copyValue = 값_비싸고_무거운_연산(value); const [copyValue] = useMemo(() => 무거운_연산(value), [value]); return <div>{copyValue}</div>; }
⭐️ 요약
불필요한 연산을 줄이는 방법
Props
바로 사용하기(useState
담기 X, 무거운 연산의props
로 사용 X)- 연산된 값을
Props
로 넘기기 useMemo
로 연산 최적화하기
✅ Curly Braces
🌈 결론
- 중괄호(Curly Braces) 사용법
<Image alt="image" src="image.jpg" style={{ width: 100 }} className="clean-div" />
✍️ 내용
-
Curly Brace 사용 O
- 값이 계산되는 경우(논리적인 숫자, Boolean, 객체, 배열, 함수 표현식)
- 객체를 넣어야 하는 경우
-
Curly Brace 사용 X
- 문자열일 경우
<Image alt={'image'} src={'image.jpg'} style={{ width: 100 }} className="clean-div" />
⭐️ 요약
- String일 경우 Curly Brace 사용하지 않기
✅ Props 축약하기
🌈 결론
// 변경 전
function component(props) {
<HeaderComponent hasPadding={props.hasPadding}>
<ChildComponent isDarkMode={props.isDarkMode} isLogin={props.isLogin} />
</HeaderComponent>;
}
// 변경 후
function component({ hasPadding, ...props }) {
<HeaderComponent hasPadding>
<ChildComponent {...props} />
</HeaderComponent>;
}
✍️ 내용
ShortHand Props는언제 사용할까?
- 토글링 값을 Props로 전달 할 때
function component({ hasPadding, ...props }) {
<HeaderComponent hasPadding>
<ChildComponent {...props} />
</HeaderComponent>
⭐️ 요약
- ShortHand Props로 Props를 축약할 수 있다.
✅ Single Quotes vs Double Quotes
🌈 결론
// ✅
<a href="https://www.naver.com">Naver</a>
// ❌
<input class='ccc' type="button" value='Clean' />
// ❌
<Clean style={{ backgroundPosition: "left" }} />
✍️ 내용
- 팀에서 일반적인 규칙 ⇒ 일관성을 지키기 위함
-
HTML과 JS 환경에서 사용하는 부분 구분
- HTML은 Double Quotes 주로 사용(HTML Attribute를 위한 값)
- JS은 Single Quotes 주로 사용(객체의 값) cf) JSX는 Single Quotes
- 결론적으로 규칙을 정하고 그 맥락을 파악하고 공유하자 ⇒ Lint, 포맷팅 도구(Prettier)에 위임하자
⭐️ 요약
- HTML, JS를 구분해서 Single Quotes와 Double Quotes를 결정하자
- 규칙은 팀끼리 정해서 자동 포맷팅 시키자
✅ Props 네이밍
🌈 결론
// ❌
<ChildComponent
class="mt-0"
Clean="code"
clean_code="react"
otherComponent={OtherComponent}
isShow={true}
/>
// ✅
<ChildComponent
className="mt-0"
clean="code"
cleanCode="react"
OtherComponent={OtherComponent}
isShow
/>
✍️ 내용
- React Component는 파스칼로 한다.
⭐️ 요약
- class는
className
으로 사용하기 camel case
사용하기- 무조건 true라면
isShow={true}
가 아닌,isShow
로 축약하기 - 컴포넌트라면 대문자로 시작하기
✅ 인라인 스타일 주의 하기
🌈 결론
// ❌
function InlineStyle(): Element {
return <button style="background-color: 'red'; font-size: '14px';">Clean Code</button>;
}
// ✅
function InlineStyle(): Element {
const myStyle = { backgroundColor: 'red', fontSize: '14px' };
return <button style={myStyle}>Clean Code</button>;
}
✍️ 내용
- JS로 HTML을 표현하는 문법이 바로 JSX임
-
고정된 스타일 객체 값이라면, 컴포넌트 외부로 빼는 것이 좋음(매번 랜더링 될 때마다 계속 평가되기 때문)
const myStyle = { backgroundColor: 'red', fontSize: '14px' }; function InlineStyle(): Element { return <button style={myStyle}>Clean Code</button>; }
⭐️ 요약
- JSX에서 인라인 스타일을 쓰려면 중괄호 안에
camelCase key
를 가진 객체를 넣어야 한다.
✅ CSS IN JS 인라인 스타일 지양하기
🌈 결론
// ❌
function InlineStyle(): Element {
return (
<button css={
css`
background-color: white;
border: 1px solid #eee;
border-radius: 0.5rem;
padding: 1rem;
`}
}>
Clean Code
</button>
);
}
// ✅
function InlineStyle(): Element {
return (
<button css={cardCss.self}>Clean Code</button>
);
}
✍️ 내용
- css 백틱으로 진행했을 때에는 VSCode 자동완성과 DX 측면에서 좋지 않기 때문에 JS 스타일을 주는 것이 좋음
-
아래와 같이 스타일을 외부 뺐을 때의 장점
- 외부로 분리했기 때문에 스타일이 렌더링 될 때마다 직렬화 되지 않는다. → 한번만 된다.
- 동적인 스타일을 실수로 건드는 확률이 적어진다.
- 스타일 관련 코드를 분리해서 로직에 집중하고 JSX를 볼 때 조금 더 간결하게 볼 수 있다.
// 장점
// - 타입 안정성
// - 자동 완성으로 생산성 DX 향상
// - export 할 경우, 외부 컴포넌트에서 사용 가능
const cardCSS = {
self: css({
backgroundColor: 'white',
border: '1px solid #eee',
borderRadius: '0.5rem',
padding: '1rem',
}),
title: css({
fontSize: '1.25rem',
}),
};
// CSS IN JS 인라인 스타일 지양하기
// - 성능에 민감함
export function Card({ title, children }) {
return (
<div css={cardCss.self}>
<h5 css={cardCss.title}>{title}</h5>
{children}
</div>
);
}
⭐️ 요약
-
CSS in JS 인라인 스타일을 지양해야 하는 이유
- 성능 저하를 일으킴
- 휴먼 에러가 발생할 수 있음
- export 할 수 없음
✅ 객체 Props 지양하기
✍️ 내용
- React는 JS로 개발되어 있음
// 객체 Props 지양하기
// - 변하지 않는 값일 경우 컴포넌트 외부로 드러내기
// - 필요한 값만 객체를 분해해서 Props로 내려 준다.
// - 정말 값 비싼 연산, 너무 잦은 연산이 있을 경우 useMemo() 활용하여 계산된 값을 메모이제이션 한다.
// - Props 값을 나누어서 다른 컴포넌트에 Props를 전달한다.
function SomeComponent() {
return <ChildComponent propObj={{ hello: 'world' }} propArr={['hello', 'hello']} />;
}
// ✅ 방법 1
function SomeComponent() {
const [propArr, setPropArr] = useState(['hello', 'hello']);
return <ChildComponent hello1="world" hello2={propArr.at(0)} />;
}
// ✅ 방법 2
function SomeComponent({ heavyState }) {
const [propArr, setPropArr] = useState(['hello', 'hello']);
const computedState = useMemo(
() => ({
heavyState: heavyState,
}),
[heavyState],
);
return <ChildComponent hello1="world" hello2={propArr.at(0)} computedState={computedState} />;
}
// React의 Dependency 배열에 해당 Object.is 함수가 내장되어있음
// 아래 결과 false
Object.is(
{ hello: 'world' }, // 초기 렌더링
{ hello: 'world' }, // 두번째 렌더링
);
// 아래 결과 false
Object.is(
['hello'], // 초기 렌더링
['hello'], // 두번째 렌더링
);
✅ HTML Attribute 주의하기
🌈 결론
// ❌
function MyButton({ children, type }) {
return <button type={type}>{children}</button>;
}
// ✅
function MyButton({ children, ...rest }) {
return <button {...rest}>{children}</button>;
}
✍️ 내용
-
HTML 기본 속성 주의하기
- HTML와 JSX에서 사용하는 예약어 주의
- HTML 표준어 찾아서 주의(내가 만든 Component의 Props와 겹치지는지 확인)
function HTMLDefaultAttribute() { const MyButton = ({ children, ...rest }) => <button {...rest}>{children}</button>; return ( <> <MyButton className="mt-0" type="submit"> Clean Code </MyButton> <MyButton type="number" maxLength="99"> Clean Code </MyButton> </> ); }
⭐️ 요약
- HTML, JS에서 정의한 예약어와 커스텀 컴포넌트 Props가 혼용되지 않도록 주의
✅ Spread 연산자 쓸 때 주의할 점
🌈 결론
// ❌
const ParentComponent = (props) => {
return <childOrHOCComponent {...props} />;
};
// ✅
const ParentComponent = (props) => {
const { 관련_없는_props, 관련_있는_props, ...나머지_props } = props;
return <childOrHOCComponent 관련_있는_props={관련_있는_props} {...나머지_props} />;
};
✍️ 내용
Spread 연산으로 내려오는 Props 주의할 점
- 코드를 예측하기 어렵다.
⭐️ 요약
- props에서 spread 연산자가 쓰이면 관련 있는 props, 없는 props, 나머지 props로 나눠보자
✅ 많은 Props 분리하기
🌈 결론
// ❌
<JoinForm
user={user}
auth={auth}
location={location}
favorite={favorite}
handleSubmit={handleSubmit}
handleReset={handleReset}
handleCancel={handleCancel}
/>
// ✅
<JoinForm
handleSubmit={handleSubmit}
handleReset={handleReset}
handleCancel={handleCancel}
>
<CheckBoxForm formData={user} />
<CheckBoxForm formData={auth} />
<RadioButtonForm formData={location} />
<SectionForm formData={favorite} />
</JoinForm>
✍️ 내용
너무 많은 Props를 넘기는 경우
- 결과보다는 일단 실행 → 분리의 대상?
- TanStack Query, Form Library, 상태 관리자, Context API, Composition
리팩토링 과정
- One Depth 분리를 한다.
- 확장성을 위한 분리를 위해 도메인 로직을 다른 곳으로 모아넣는다.
- 꼭 라이브러리를 먼저 도입하는게 아니라, 먼저 분리 후 생각한다.
⭐️ 요약
- props가 많다면 컴포넌트를 분리해보자.
✅ 단순하게 Props 내리기
🌈 결론
// ❌
const UserInfo = ({ user }) => {
return (
<div>
<img src={user.avatarImgUrl} />
<h3>{user.userName}</h3>
<h4>{user.email}</h4>
</div>
);
};
// ✅
const UserInfo = ({ avatarImgUrl, userName, email }) => {
return (
<div>
<img src={avatarImgUrl} />
<h3>{userName}</h3>
<h4>{email}</h4>
</div>
);
};
✍️ 내용
객체보다는 단순한 Props
- 불필요한 렌더링 방지
⭐️ 요약
- props에 객체 전체를 내리지 말고 꼭 필요한 값만 내리자