직전 글에서 다짐의 DgText를 사례로 이야기를 풀어갔었는데, 이번에는 잠깐 쉬어가는 느낌으로 Text 컴포넌트를 실제로 어떻게 구현해서 쓰고 있는지 공유해보려고 한다.
<DgText fontType={'subTitle_17_bold'} blueColor style={css`align-text:center;`}>
다짐 Text 컴포넌트
</DgText>
먼저 Text 컴포넌트의 default props를 설정해주었다. 다짐에서는 휴대폰 설정에 따른 폰트 사이즈 변경을 허용하지 않기 때문에, 'allowFontScaling' prop을 false로 설정해주어야 한다. 그리고 추가적으로 ios에서 한글 자동 개행을 위해'lineBreakStrategyIOS' prop을 'hangul-word'로 설정해주기로 했다. lineBreakStrategyIOS은 ios에서 텍스트 개행 방법을 설정하는 prop인데, RN 0.71버전부터 'hangul-word'가 추가되어 사용이 가능해졌다. (자세한 내용이 궁금하다면)
처음에는 DgText 구현체를 Class Component로 작성한 뒤 props에 넣어 사용했었는데, react native navigation 내부에서 사용되는 Text Component에는 적용되지 않아서 폰트 사이즈가 변경되는 이슈가 있었다. 이런 이유로 글로벌하게 프로젝트에서 사용되는 모든 Text에 적용할 수 있도록 default props로 설정하게 되었다. 여담으로 Class Component로 작성해야했던 이유는 AnimatedDgText 컴포넌트가 필요했기 때문이다. (Animated.createAnimatedComponent는 Functional Component를 지원하지 않는다.)
// dg-text-default-props.ts
import {Text} from 'react-native';
interface TextWithDefaultProps extends Text {
defaultProps?: {
allowFontScaling?: boolean;
lineBreakStrategyIOS?: 'hangul-word';
};
}
(Text as unknown as TextWithDefaultProps).defaultProps!.allowFontScaling =
false;
(Text as unknown as TextWithDefaultProps).defaultProps!.lineBreakStrategyIOS =
'hangul-word';
//app.tsx
import 'dg/text/default/props/path/dg-text-default-props.ts'
default props를 설정하는 방법은 파일 내부에서 위와같이 코드를 작성한 뒤 app.tsx에서 import 해주기만 하면 된다.
다짐의 디자인 시스템에는 사용되는 모든 텍스트의 typography가 정리되어 있다. 구현할 DgText는 fontType props로 텍스트를 구분할 예정이므로, FontType의 타입을 먼저 정의해주기로 했다. 귀찮다고 타입 정의를 건너뛰었다가는 자동완성을 기대하기 어려우므로 생산성을 위해서도 꼭 타입 정의를 습관화하는게 좋다.
export type FontType =
| 'title_20_bold'
| 'button1_15_semibold'
| 'body1_15_regular' | ...
type FontStyle = {
fontFamily: string;
fontSize: string;
lineHeight: string;
letterSpacing: string;
};
이번에는 FontType에 따라 적용되어야 하는 스타일을 정리해줄 것이다. 이때 값을 꺼내 쓰기 쉽도록 object 형태로 작성해주겠다.
const fontStyles: Record<FontType, FontStyleTypes> = {
title_20_bold: {
fontFamily: 'Pretendard-Bold',
fontSize: '20px',
lineHeight: '32px',
letterSpacing: '-0.2px',
},
button1_15_semibold: {
fontFamily: 'Pretendard-SemiBold',
fontSize: '15px',
lineHeight: '25px',
letterSpacing: '-0.2px',
},
body1_15_regular: {
fontFamily: 'Pretendard-Regular',
fontSize: '15px',
lineHeight: '25px',
letterSpacing: '-0.2px',
},
...
};
다짐은 emotion을 사용하고 있으므로 emotion으로 DgText를 정의하면 다음과 같다.
export type DgTextProps = {
fontType?: FontType;
};
const DEFAULT_FONT_TYPE = 'body1_15_regular';
const DgText = styled.Text<DgTextProps>`
font-family: ${({fontType = DEFAULT_FONT_TYPE}) =>
fontStyles[fontType].fontFamily};
font-size: ${({fontType = DEFAULT_FONT_TYPE}) =>
fontStyles[fontType].fontSize};
line-height: ${({fontType = DEFAULT_FONT_TYPE}) =>
fontStyles[fontType].lineHeight};
letter-spacing: ${({fontType = DEFAULT_FONT_TYPE}) =>
fontStyles[fontType].letterSpacing};
`;
다짐에서 자주 사용되는 color는 theme에서 import하지 않아도 간단하게 props로 표현할 수 있도록 미리 설정해두었다. 위에서 정의한 DgText에 color props까지 확장한 최종 DgText의 모습이다.
export type DgTextProps = {
fontType?: FontType;
secondaryColor?: boolean;
whiteColor?: boolean;
blueColor?: boolean;
redColor?: boolean;
placeholderColor?: boolean;
};
const DEFAULT_FONT_TYPE = 'body1_15_regular';
const DgText = styled.Text<DgTextProps>`
...
color: ${({
theme,
secondaryColor,
whiteColor,
blueColor,
redColor,
placeholderColor,
}) =>
secondaryColor
? theme.colors.textSecondary
: whiteColor
? 'white'
: blueColor
? theme.colors.dagymBlue
: redColor
? theme.colors.dangerRed
: placeholderColor
? theme.colors.textPlaceholder
: theme.colors.textDefault};
`;
export default DgText;
코드를 보면 알겠지만 color를 줄때 단일 prop으로 value를 달리해서 구분하는게 아니라 각 색상마다 개별 prop을 부여해줬다. 이는 조금이라도 직관적으로 사용하고 싶다는 욕심에서 비롯된 결과물이다.
<DgText color={'blue'}>다짐</DgText>
<DgText color={'placeholder'}>다짐</DgText>
<DgText blueColor>다짐</DgText>
<DgText placeholderColor>다짐</DgText>
나만 그런지는 모르겠지만 뭔가 아래처럼 사용하는게 조금 더 쿨하고 직관적으로 보였다.(맞다. 그냥 취향이다.)
다만 이 방법도 단점이 있는데, preset color들 간의 스위칭이 필요한 경우 꽤나 가독성이 안좋아지기도 한다.
const diff = standardValue - currentValue
const isStay = diff === 0;
const isUp = diff < 0
<DgText color={isStay ? 'default' : isUp ? 'red' : 'blue'}>그래프</DgText>
<DgText blueColor={!isStay && !isUp} redColor={!isStay && isUp}>그래프</DgText>
하지만 개인적으로는 만족하면서 사용하고 있는 편이다.😀
마지막으로 간혹 쓰이는 AnimatedDgText도 export 해주면 끝이다.
export const AnimatedDgText = Animated.createAnimatedComponent(DgText);