일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 간단한 날씨 웹 만들기
- 개발공부
- JavaScript
- HTML
- leetcode
- [파이썬 실습] 중급 문제
- 자바스크립트 split()
- RN 프로젝트
- 코딩부트캠프
- 자바스크립트
- 엘리스
- 자바스크립트 reduce()
- [AI 5기] 연습 문제집
- 프로그래머스
- 삼항연산자
- 개발일기
- 자바스크립트 sort()
- 코드스테이츠
- 엘리스 AI 트랙 5기
- 자바스크립트 날씨
- [파이썬 실습] 심화 문제
- 프론트개발
- 리트코드
- 자바스크립트 날씨 웹 만들기
- 부트캠프
- 엘리스 ai 트랙
- [파이썬 실습] 기초 문제
- reactnativecli
- 프론트개발공부
- 날씨 웹 만들기
- Today
- Total
개발조각
[React-native CLI] RN스타일, styled-components로 화면 구현하기(Home화면 스타일) 본문
[React-native CLI] RN스타일, styled-components로 화면 구현하기(Home화면 스타일)
개발조각 2024. 3. 12. 22:08이번에는 메인스크린을 스타일링해보도록 하겠습니다.
이전 포스팅을 보셨으면 알겠지만 스타일을 styled-components를 사용해서 스타일링했습니다.
RN에서 styled-components 설치 방법에 대해 알고 싶다면 아래 링크를 클릭해 주세요!
https://development-piece.tistory.com/438
[React-native CLI] styled-components 설치 및 세팅
리액트 네이티브에서도 styled-components가 사용가능하더라고요. 이전에는 가능할 줄 몰라서 안 쓰기도 했고, 요즘 다시 전통적인 css가 좋아서 썼는데 리액트 네이티브에서 css를 사용해보니 css보다
development-piece.tistory.com
그동안 styled-components를 React에서만 사용해 봤지 React-native에서는 처음 사용해 봐서 세팅하는데 좀 오래 걸렸습니다.
styled-component가 React에서는 사용가능한 기능이 react-native에서는 사용불가능한 기능들도 많아서 실험하면서 하다 보니 힘드네요ㅠㅠ
이뿐만 아니라 react-native에서는 모든 속성과 값을 지원하지 않아서, 하나하나 다 파악하면서 스타일을 짜야되서 생각보다 배로 더 걸리더라고요...ㅠ
RN에서 스타일과 css의 차이점
이 포스팅에서는 styled-components를 사용했으며, 이에 대한 차이점에 대한 설명입니다.
(모든 차이점을 분석한 내용은 아니며, 사용해 보면서 느낀 차이점에 대해 작성해 보겠습니다.)
만약 styled-components를 사용 안 하고 RN스타일로만 스타일링을 하고 싶다면 아래 링크를 참고해 주세요.
https://wit.nts-corp.com/2020/03/23/6014
React Native UI 개발 시작하기 | WIT블로그
최근 React Native로 개발하는 프로젝트의 UI개발을 맡아 진행하였습니다. 컴포넌트나 스타일 등 웹에서 작업하던 것과 달라 초기에 많은 시행착오를 겪었는데요 저와 같이 처음 React Native를 경
wit.nts-corp.com
웹에서는 상위 태그에 css를 지정하면 하위 태그까지 반영이 되지만, RN은 그렇지 않다.
웹에서는 css를 작성할 시 선택자(#,.)에 따라 우선순위도 결정이 되고 상위 태그에 css를 지정하면 하위까지 반영이 되지만
RN은 이러한 css의 상속이 존재하지 않으며, 컴포넌트마다 각각 적용이 된다.
grid 못 사용한다.
다행히도 flex사용이 가능하지만 grid는 사용하지 못합니다.
웹에서 flex는 가로정렬이 기본값이지만 RN에서는 원하는 방향을 설정해 주어야 된다.
flex-direction: row;
position: absolute 할 때 relative를 사용 안 해도 되는 것 같다.
relative를 사용안해도 감싸져 있는 컴포넌트에 맞춰서 적용되는 것 같다.
%가 적용되지 않는다.
css에서 100%, 50%는 자주 사용이 되는데 이를 사용할 수 없다 다 px로 계산해서 적용시켜 줘야 된다.
50% 쓰면 계속 오류가 납니다.
// 다 못 사용한다.
transform: translate(-50%, -50%);
border-radius: 50%;
RN에서 styled-components 사용 시 차이점
Theme, Global Style 사용할 수 없다.
그래서 Theme는 다른 방식으로 적용해 보았습니다.
scss처럼 상위집합 같은 방식을 적용할 수 없다.
export const Greeting = styled.div`
position: absolute;
top: 25%;
left: 0;
max-width: 337px;
height: 264px;
h1 {
margin-bottom: 24px;
}
${({ theme }) => theme.device.mobile} {
width: 100%;
}
`;
그래도 RN이 React, Next, TS에서 styled-components를 사용하는 방법보다는 사용하기 쉬웠습니다.
오히려 제약사항이 많다 보니 안 쓰면 그만이라;;; 오히려 좋았습니다.
완성한 화면
폴더
styled-components는 제가 편하다 생각하는 방식으로 작업한 거라 일반적인 방식이 아닐 수 있습니다.
types/types.ts
export interface RecordType {
date: string;
card: number;
cash: number; // 현금
lpgInjectionVolume: number; // LPG 주입량
lpgUnitPrice: number; // LPG 단가
mileage: number; // 주행거리
businessDistance: number; // 영업거리
toll: number; // 통행료
operatingAmount: number; // 영업금액
lpgChargeAmount: number; // LPG 충전 금액
fuelEfficiency: number; // 연비
lpgUsage: number; // LPG 사용량
}
export interface RecordBoxType {
title: string;
state: number;
unit?: '원' | 'km' | 'km/L' | 'L';
option?: 'basics' | 'orange';
}
styles/Theme.ts
const colors = {
// main
main: '#FFA800',
mainDeep: '#FF7B00',
mainLight: '#FFEFD2',
// grey
grey: '#D9D9D9',
darkGrey: '#888888',
lightGrey: '#F5F5F7',
black: '#333',
red: '#F36E51',
blue: '#519CF3',
};
const fonts = {
// size
baseSize: '14px',
smallSize: '12px',
mediumSize: '18px',
largeSize: '20px',
// weight
redular: 'NotoSansKRRegular',
medium: 'NotoSansKRMedium',
bold: 'NotoSansKRBold',
// line-height 1.25
baseHight: '17.5px',
smallHeight: '15p',
mediumHeight: '22.5px',
largeHeight: '25px',
};
const fontCommon = {
base: `
font-size: ${fonts.baseSize};
line-height: ${fonts.baseHight};
`,
small: `
font-size: ${fonts.smallSize};
line-height: ${fonts.smallHeight};
`,
medium: `
font-size: ${fonts.mediumSize};
line-height: ${fonts.mediumHeight};
`,
large: `
font-size: ${fonts.largeSize};
line-height: ${fonts.largeHeight};
`,
};
const common = {
flexCenter: `
display: flex;
justify-content: center;
align-items: center;
`,
flexRowCenter: `
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
`,
};
const Theme = {
colors,
fonts,
fontCommon,
common,
};
export default Theme;
styles/home.styles.ts
// react, react-native
import {Text, TouchableOpacity, View} from 'react-native';
// library
import styled from 'styled-components';
// style
import Theme from './Theme';
// Home
export const Home = {
container: styled(View)`
flex: 1;
`,
bottomContainer: styled(View)`
padding: 0 16px;
margin-bottom: 96px;
`,
};
// YearBusinessAmount
export const YearBusinessAmount = {
container: styled(View)`
padding: 20px 0;
background: ${Theme.colors.mainLight};
${Theme.common.flexCenter}
`,
title: styled(Text)`
color: ${Theme.colors.mainDeep};
font-family: ${Theme.fonts.medium};
${Theme.fontCommon.base}
`,
valueText: styled(Text)`
color: ${Theme.colors.mainDeep};
font-family: ${Theme.fonts.bold};
${Theme.fontCommon.large}
margin-top: 4px;
`,
};
// MonthCalendar
interface PositionType {
position?: boolean;
}
export const MonthCalendar = {
container: styled(View)`
padding: 8px 16px;
`,
centerWrap: styled(View)`
${Theme.common.flexRowCenter}
gap: 8px;
`,
iconButton: styled(TouchableOpacity)<PositionType>`
${Theme.common.flexCenter}
width: 32px;
height: 32px;
/* props에 position이 있으면 position 주기 */
${props =>
props.position &&
`
position: absolute;
top: 8px;
right: 0;
`}
`,
text: styled(Text)`
font-family: ${Theme.fonts.medium};
${Theme.fontCommon.large}
color: ${Theme.colors.black};
`,
};
// MonthBusinessAmount
export const MonthBusinessAmount = {
container: styled(View)`
padding: 16px 0;
border-radius: 10px;
background: ${Theme.colors.lightGrey};
`,
topWrap: styled(View)`
${Theme.common.flexCenter}
`,
title: styled(Text)`
font-family: ${Theme.fonts.medium};
${Theme.fontCommon.base}
color: ${Theme.colors.mainDeep};
`,
moneyValueText: styled(Text)`
font-family: ${Theme.fonts.bold};
${Theme.fontCommon.large}
color: ${Theme.colors.mainDeep};
margin-top: 4px;
`,
bottomWrap: styled(View)`
${Theme.common.flexRowCenter}
margin-top: 16px;
`,
bottomBox: styled(View)`
${Theme.common.flexCenter}
flex: 1;
`,
subTitle: styled(Text)`
${Theme.fontCommon.base}
color: ${Theme.colors.black};
`,
subValueText: styled(Text)`
font-family: ${Theme.fonts.bold};
${Theme.fontCommon.medium}
color: ${Theme.colors.black};
margin-top: 4px;
`,
line: styled(View)`
width: 1px;
height: 100%;
background: ${Theme.colors.grey};
`,
};
// MonthRecordInfo
export const MonthRecordInfo = {
container: styled(View)`
margin-top: 8px;
`,
};
// CreateButton
export const CreateButton = {
button: styled(TouchableOpacity)`
position: absolute;
bottom: 20px;
right: 16px;
${Theme.common.flexCenter}
width: 56px;
height: 56px;
border-radius: 56px;
background: ${Theme.colors.mainLight};
`,
};
screens/Home.tsx
// react, react-native
import {ScrollView} from 'react-native';
// library
// assets, utils, realm
// component
import YearBusinessAmount from '../components/home/YearBusinessAmount.tsx';
import MonthCalendar from '../components/home/MonthCalendar.tsx';
import MonthBusinessAmount from '../components/home/MonthBusinessAmount.tsx';
import MonthRecordInfo from '../components/home/MonthRecordInfo.tsx';
import CreateButton from '../components/home/CreateButton.tsx';
// style
import {Home as Style} from '../styles/home.styles.ts';
const Home = () => {
return (
<Style.container>
<ScrollView>
{/* 연간 영업 금액 */}
<YearBusinessAmount />
{/* 월별 운행 기록 */}
<Style.bottomContainer>
{/* 월별 달력 */}
<MonthCalendar />
{/* 이번달 영업 금액 */}
<MonthBusinessAmount />
{/* 이번달 운행 정보 */}
<MonthRecordInfo />
</Style.bottomContainer>
</ScrollView>
{/* create 버튼 */}
<CreateButton />
</Style.container>
);
};
export default Home;
components/home/YearBusinessAmount.tsx
// react, react-native
// library
// assets, utils, realm
// component
// style
import {YearBusinessAmount as Style} from '../../styles/home.styles';
const MonthBusinessAmount = () => {
return (
<Style.container>
<Style.title>연간 영업 금액</Style.title>
<Style.valueText>3,000,000원</Style.valueText>
</Style.container>
);
};
export default MonthBusinessAmount;
components/home/MonthCalendar.tsx
// react, react-native
import {View} from 'react-native';
import {SvgXml} from 'react-native-svg';
// library
// assets, utils, realm
import {svg} from '../../assets/svg';
// component
// style
import {MonthCalendar as Style} from '../../styles/home.styles';
const MonthCalendar = () => {
return (
<Style.container>
<Style.centerWrap>
<Style.iconButton>
<View>
<SvgXml xml={svg.prev} />
</View>
</Style.iconButton>
<Style.text>2024년 3월</Style.text>
<Style.iconButton>
<View>
<SvgXml xml={svg.next} />
</View>
</Style.iconButton>
</Style.centerWrap>
<Style.iconButton position={true}>
<View>
<SvgXml xml={svg.turn} />
</View>
</Style.iconButton>
</Style.container>
);
};
export default MonthCalendar;
components/home/MonthBusinessAmount.tsx
// react, react-native
// library
// assets, utils, realm
// component
// style
import {MonthBusinessAmount as Style} from '../../styles/home.styles';
const MonthBusinessAmount = () => {
return (
<Style.container>
<Style.topWrap>
<Style.title>영업금액</Style.title>
<Style.moneyValueText>2,000,000원</Style.moneyValueText>
</Style.topWrap>
<Style.bottomWrap>
<Style.bottomBox>
<Style.subTitle>카드</Style.subTitle>
<Style.subValueText>2,000,000원</Style.subValueText>
</Style.bottomBox>
<Style.line></Style.line>
<Style.bottomBox>
<Style.subTitle>현금</Style.subTitle>
<Style.subValueText>2,000,000원</Style.subValueText>
</Style.bottomBox>
</Style.bottomWrap>
</Style.container>
);
};
export default MonthBusinessAmount;
components/home/MonthRecordInfo.tsx
// react, react-native
// library
// assets, utils, realm
// component
import RecordBox from '../common/RecordBox';
// style
import {MonthRecordInfo as Style} from '../../styles/home.styles';
import {RecordBox as RecordBoxStyle} from '../../styles/common.styles';
const MonthRecordInfo = () => {
return (
<Style.container>
<RecordBoxStyle.wrap>
<RecordBox title="LPG 주입량" state={10} unit="L" />
<RecordBox title="LPG 단가" state={10} />
<RecordBox title="LPG 충전 금액" state={10} option="orange" />
</RecordBoxStyle.wrap>
<RecordBoxStyle.wrap>
<RecordBox title="주행거리" state={10} unit="km" />
<RecordBox title="영업거리" state={10} unit="km" />
<RecordBox title="통행료" state={10} />
</RecordBoxStyle.wrap>
<RecordBoxStyle.wrap>
<RecordBox title="연비" state={10} unit="km/L" option="orange" />
<RecordBox title="LPG 사용량" state={10} unit="L" option="orange" />
</RecordBoxStyle.wrap>
</Style.container>
);
};
export default MonthRecordInfo;
styles/common.styles.ts
// react, react-native
import {Text, View} from 'react-native';
// library
import styled from 'styled-components';
// style
import Theme from './Theme';
import {RecordBoxType} from '../types/types';
type OptionType = Pick<RecordBoxType, 'option'>;
export const RecordBox = {
wrap: styled(View)`
${Theme.common.flexRowCenter}
gap: 8px;
margin-top: 8px;
`,
box: styled(View)<OptionType>`
flex: 1;
padding: 10px 6px;
border-radius: 10px;
background: ${props =>
props.option === 'orange'
? Theme.colors.mainLight
: Theme.colors.lightGrey};
`,
title: styled(Text)<OptionType>`
${Theme.fontCommon.base};
color: ${props =>
props.option === 'orange' ? Theme.colors.mainDeep : Theme.colors.black};
text-align: center;
`,
valueTextWrap: styled(View)`
margin-top: 6px;
padding: 4px 0;
width: 100%;
border-radius: 20px;
background: #fff;
`,
valueText: styled(Text)`
font-family: ${Theme.fonts.bold};
${Theme.fontCommon.base};
color: ${Theme.colors.black};
text-align: center;
`,
};
components/common/RecordBox.tsx
// react, react-native
// library
// assets, utils, realm, type
import {RecordBoxType} from '../../types/types';
// component
// style
import {RecordBox as Style} from '../../styles/common.styles';
const RecordBox = ({
title,
state,
unit = '원',
option = 'basics',
}: RecordBoxType) => {
return (
<Style.box option={option}>
<Style.title option={option}>{title}</Style.title>
<Style.valueTextWrap>
<Style.valueText>{`${state}${unit}`}</Style.valueText>
</Style.valueTextWrap>
</Style.box>
);
};
export default RecordBox;
components/home/CreateButton.tsx
// react, react-native
import {View} from 'react-native';
import {SvgXml} from 'react-native-svg';
// library
// assets, utils, realm
import {svg} from '../../assets/svg';
// component
// style
import {CreateButton as Style} from '../../styles/home.styles';
const CreateButton = () => {
return (
<Style.button>
<View>
<SvgXml xml={svg.plus} fill="#FFA800" />
</View>
</Style.button>
);
};
export default CreateButton;
'React-Native > [프로젝트] 택시 운행관리 기록장' 카테고리의 다른 글
[React-native CLI] RN 뒤로가기 (0) | 2024.03.13 |
---|---|
[React-native CLI] RN에서 react-naviation으로 다른 스크린으로 이동하는 방법 (0) | 2024.03.12 |
[React-native CLI] Realm 사용하기 및 사용방법 (0) | 2024.03.11 |
[React-native CLI] 폰트 적용하기, 글로벌 폰트 적용하기(글로벌 폰트는 애매함) (0) | 2024.03.07 |
[React-native CLI] styled-components 설치 및 세팅 (0) | 2024.03.06 |