글을 적을때마다 느끼는 건데 항상 첫 마디를 어떻게 시작할까가 제일 어려운 것 같아요😂
저번 포스팅에는 회사 프로젝트에 디자인시스템을 적용한 경험을 이야기해보았는데요, 이번에는 디자인 시스템과 연결되어, 특히 '리액트' 개발자라면 꼭 알고 있어야 할 '컴포넌트화' 또는 '모듈화' 개념에 대해서 알아보겠습니다.
체리 프로젝트에서 주로 '버튼' 이란 것은 페이지 하단에 위치하거나 모달창 하단에 위치해서 어떤 동작을 마치거나 넘어갈 때 쓰이는데요!
테두리가 있는 화이트 컬러 배경의 이전 버튼, 그레이 컬러 배경의 비활성화 버튼, 체리블루 컬러의 활성화 버튼 3가지로 나누어 지는데요, 아래 사진을 보면 어떤 식으로 쓰이는 지 알 수 있습니다.
버튼들의 공통점은 height와 border radius 정도가 똑같고, 안에 들어가는 텍스트나 너비, 색깔 등은 다릅니다.
물론 스타일링을 할 때 Styled Component를 써도 되고, View나 GlobalStyled 등을 통해서 페이지마다 써도 되지만,
Button.js 라는 하나의 js 파일로 만들어 두면 언제든 import 해서 쓸 수 있지 않을까?
라는 생각을 해볼수 있습니다.
그리고 리액트는 이런 컴포넌트화 방식을 자연스럽게 적용할 수 있도록 만들어진 프레임워크라고 생각합니다! 혹시 컴포넌트화에 대해 아직 잘 모르시겠다고 하면 '리액트 컴포넌트'와 더불어 'props' 라는 개념을 알아보시면 좋을 것 같습니다:)
그럼 어떤 식으로 Button.js 파일을 만드는지, 어떻게 적용하는 지 알아보겠습니다.
const Button = ({
width,
height,
color,
textColor,
title,
borderWidth,
borderColor,
borderRadius,
onPress,
disabled,
}) => {
return (
)
}
export default Button;
기본 셋팅은 위와 같습니다. 저는 리액트의 함수형 프로그래밍 방식을 사용했고, 클래스형 프로그래밍 방식과는 조금 다릅니다! 이 부분도 궁금하시면 리액트 함수형 프로그래밍 / 리액트 클래스형 프로그래밍 키워드로 검색해보시길 바랍니다.
({}) 안에 수많은 변수들은 리액트의 props 기술인데 prop으로 받아 온 값들을 적용 할 수 있습니다. 그러면 return 문 안을 어떻게 구성해야 할까요?
일단 버튼이니 react-native에서 제공하는 TouchableOpacity를 사용해보겠습니다.
const Button = ({
width, // 버튼 너비
height, // 버튼 높이
color, // 버튼 색상
textColor, // 텍스트 색상
title, // 텍스트 내용
borderWidth, // 외곽선 두께
borderColor, // 외곽선 컬러
borderRadius, // 외곽선 둥글기
onPress, // 버튼 눌렀을 때 실행 될 콜백함수
disabled, // 버튼을 비활성화 하는 조건
}) => {
return (
<TouchableOpaicty
style ={{
width: width || '100%',
height: height || 45,
backgroundColor: color ? color : 'white',
borderWidth: borderWidth || 0,
borderColor: borderColor ? borderColor : color,
borderRadius: borderRadius || 12,
justifyContent: 'center',
alignItems: 'center',
}}
disabled={disabled ? disabled : false}
onPress={onPress}
>
<GlobalStyled.Noto4 color={textColor || 'white'}>
{title}
</GlobalStyled.Noto4>
</TouchableOpacity>
)
}
export default Button;
width || '100%' 가 뭐지? 싶은 분들이 있을 것 같습니다.
자바스크립트의 논리연산자(||, &&) / 단축평가의 개념을 아시나요?
||의 경우, 둘 중 하나만 true 이면 true로 평가 되므로, 왼쪽 피연산자가 true이면 바로 왼쪽 값을 반환합니다.
"apple" || "banana" >>> "apple"
false || true >>> true
false || "banana" >>> "banana"
즉, 패턴을 정리해보면 다음과 같습니다.
값 || true >>> 값
값 || false >>> 값
true || 값 >>> true
false || 값 >>> 값
값A || 값B >>> 값A
false && "banana" >>> false
"apple" && false >>> false
"apple" && true >>> true
즉, 패턴을 정리해보면 다음과 같습니다.
false && 값 >>> false
true && 값 >>> 값
값 && false >>> false
값 && true >>> true
값A && 값B >>> 값B
그렇다면, width || '100%' 은 무엇일까요?
prop으로
<Button width={50} />
이런 값이 온다고 하면, Button.js 내에서
50 || '100%'
단축평가를 할 것입니다.
50이라는 true 값이 들어 왔으니 width 는 50이 됩니다.
width라는 prop을 안주었다면, false || '100%'로 평가되어, 그냥 너비가 '100%'인 버튼을 반환하겠지요?
그렇다면 아래와 같은 버튼을 생성해봅시다.
import Button from '../../components/Button'; // 예시입니다.
const ButtonTestPage = () => {
const onPress = () => {
// 버튼을 누르면 실행되는 코드
}
return (
<Button
title="완료"
color={theme.SubBlue}
onPress={onPress}
/>
)
}
export default ButtonTestPage;
이런식으로 간단하게 만들 수 있습니다!
응용해서, 두개가 나란히 이어져 있는 버튼과 활성화/비활성화 상태는 어떻게 구분할까요?
<GlobalStyled.ViewRow
style={{
justifyContent: 'space-between',
height: 'auto',
width: wp(100) - 48,
}}
>
<Button
width={(wp(100) - 48 - 15) / 2}
color={theme.White}
borderWidth={1}
borderColor={theme.LightGray2}
title={'이전'}
textColor={theme.DarkGray1}
onPress={() => {
navigation.goBack();
}}
/>
<Button
width={(wp(100) - 48 - 15) / 2}
color={활성화조건 ? theme.SubBlue : theme.LightGray2}
title={'다음'}
disabled={!(활성화조건)}
onPress={onPress}
/>
</GlobalStyled.ViewRow>
width를 저렇게 준 이유는 양옆 margin 24*2dp, 가운데 15dp를 빼주고 2로 나누면 버튼 하나의 크기가 나오기 때문입니다😀
간단하게 버튼으로 예시를 들어봤는데, 컴포넌트화 참 쉽죠? prop으로 숫자나 문자열 같은 값 뿐 아니라 함수도 보낼 수 있으니 앞으로 코드를 짤 때 컴포넌트화를 적극적으로 활용하시길 바래요✨