JSX와 Core Components — View, Text, Image 제대로 쓰기
React Native의 Core Components는 플랫폼별 네이티브 뷰로 매핑되는 기본 빌딩 블록입니다.
웹 개발에서 HTML 태그를 사용하듯, React Native에서는 Core Components를 사용합니다. 하지만 단순히 이름만 바뀐 게 아니라 동작 방식과 제약 조건 이 다릅니다.
View — 모든 레이아웃의 기본
View는 웹의 <div>에 해당하는 컨테이너 컴포넌트입니다. 모든 레이아웃의 시작점이죠.
import { View, StyleSheet } from 'react-native';
// View는 기본적으로 flexDirection: 'column'
function CardLayout() {
return (
<View style={styles.card}>
<View style={styles.header}>
{/* 헤더 영역 */}
</View>
<View style={styles.body}>
{/* 본문 영역 */}
</View>
</View>
);
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 12,
padding: 16,
// 웹과 달리 기본이 column 방향
// flexDirection: 'column'이 기본값
},
header: {
borderBottomWidth: 1,
borderBottomColor: '#eee',
paddingBottom: 12,
},
body: {
paddingTop: 12,
},
});
웹 div와의 주요 차이점
- 기본
flexDirection이 column (웹은 row) display: 'flex'가 기본값 (웹처럼block이 아님)- 직접 텍스트를 넣을 수 없음 — 반드시
<Text>로 감싸야 함 overflow: 'hidden'이 아닌overflow: 'visible'이 기본값 (Android)
면접에서 "React Native의 View와 웹의 div 차이점"을 물으면, flexDirection 기본값 차이를 꼭 언급하세요. 이걸 모르면 레이아웃이 의도와 다르게 나옵니다.
Text — 텍스트 표시의 유일한 방법
React Native에서 텍스트를 표시하려면 반드시 <Text> 컴포넌트를 사용해야 합니다.
import { Text, StyleSheet } from 'react-native';
function TextExample() {
return (
<>
{/* 기본 텍스트 */}
<Text style={styles.title}>제목입니다</Text>
{/* 중첩 텍스트 — 인라인 스타일링 */}
<Text style={styles.body}>
이것은 <Text style={styles.bold}>굵은 텍스트</Text>와
<Text style={styles.highlight}> 강조 텍스트</Text>를 포함합니다.
</Text>
{/* 줄 수 제한 */}
<Text numberOfLines={2} ellipsizeMode="tail">
매우 긴 텍스트가 있을 때 2줄로 제한하고
나머지는 말줄임표로 표시할 수 있습니다.
이 부분은 잘려서 보이지 않습니다.
</Text>
{/* 선택 가능한 텍스트 */}
<Text selectable>이 텍스트는 길게 눌러서 복사할 수 있습니다.</Text>
</>
);
}
const styles = StyleSheet.create({
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#1a1a1a',
},
body: {
fontSize: 16,
lineHeight: 24,
color: '#333',
},
bold: {
fontWeight: 'bold',
},
highlight: {
color: '#007AFF',
backgroundColor: '#E5F0FF',
},
});
Text의 스타일 상속
웹과 다른 중요한 특성입니다.
// 웹에서는 부모의 font-size, color 등이 자식에게 상속됨
// React Native에서는 Text 컴포넌트 안에서만 상속됨
function StyleInheritance() {
return (
// View의 스타일은 Text에 상속되지 않음!
<View style={{ fontSize: 20 }}>
<Text>이 텍스트는 fontSize 20이 적용되지 않습니다</Text>
</View>
);
}
function CorrectInheritance() {
return (
// Text 안의 Text는 스타일이 상속됨
<Text style={{ fontSize: 20, color: 'blue' }}>
부모 텍스트
<Text style={{ fontWeight: 'bold' }}>
{/* fontSize 20, color blue가 상속됨 */}
자식 텍스트도 파란색이고 20px
</Text>
</Text>
);
}
Image — 이미지 표시
import { Image, StyleSheet } from 'react-native';
function ImageExample() {
return (
<>
{/* 로컬 이미지 — require 사용 */}
<Image
source={require('./assets/logo.png')}
style={styles.logo}
/>
{/* 네트워크 이미지 — uri와 크기 필수 */}
<Image
source={{ uri: 'https://example.com/photo.jpg' }}
style={styles.networkImage}
resizeMode="cover"
/>
{/* Base64 이미지 */}
<Image
source={{ uri: 'data:image/png;base64,...' }}
style={styles.logo}
/>
</>
);
}
const styles = StyleSheet.create({
logo: {
width: 100,
height: 100,
},
networkImage: {
width: 300,
height: 200,
borderRadius: 8,
},
});
로컬 vs 네트워크 이미지 차이
| 항목 | 로컬 이미지 | 네트워크 이미지 |
|---|---|---|
| 소스 | require('./path') | { uri: 'https://...' } |
| 크기 지정 | 자동 감지 가능 | 반드시 width/height 지정 |
| 로딩 | 즉시 | 비동기 |
| 캐싱 | 번들에 포함 | 별도 캐싱 필요 |
네트워크 이미지에 width와 height를 지정하지 않으면 아무것도 보이지 않습니다. 이 부분에서 많이 헷갈렸습니다.
resizeMode 옵션
// 이미지를 어떻게 맞출지 결정
<Image
source={{ uri: imageUrl }}
style={{ width: 200, height: 200 }}
resizeMode="cover" // 비율 유지, 영역 꽉 채움 (잘릴 수 있음)
// resizeMode="contain" // 비율 유지, 영역 안에 들어감 (여백 가능)
// resizeMode="stretch" // 비율 무시, 영역에 맞춤
// resizeMode="center" // 원본 크기로 중앙 배치
/>
ImageBackground — 배경 이미지
import { ImageBackground, Text, StyleSheet } from 'react-native';
function HeroBanner() {
return (
<ImageBackground
source={{ uri: 'https://example.com/banner.jpg' }}
style={styles.banner}
resizeMode="cover"
>
{/* 이미지 위에 컨텐츠 배치 */}
<Text style={styles.bannerText}>환영합니다!</Text>
</ImageBackground>
);
}
const styles = StyleSheet.create({
banner: {
width: '100%',
height: 200,
justifyContent: 'center',
alignItems: 'center',
},
bannerText: {
color: 'white',
fontSize: 28,
fontWeight: 'bold',
},
});
JSX 주의사항
텍스트는 반드시 Text로 감싸기
// 잘못된 예 — 크래시 발생
function Wrong() {
return (
<View>
안녕하세요 {/* 에러! View 안에 직접 텍스트 불가 */}
</View>
);
}
// 올바른 예
function Correct() {
return (
<View>
<Text>안녕하세요</Text>
</View>
);
}
조건부 렌더링 주의
function ConditionalRender({ count }: { count: number }) {
return (
<View>
{/* 위험: count가 0이면 "0"이 텍스트로 렌더링되어 크래시 */}
{count && <Text>알림 {count}개</Text>}
{/* 안전: Boolean으로 명시적 변환 */}
{count > 0 && <Text>알림 {count}개</Text>}
{/* 또는 삼항 연산자 사용 */}
{count > 0 ? <Text>알림 {count}개</Text> : null}
</View>
);
}
웹 React에서는
0이 렌더링되어도 문제없지만, React Native에서는 View 안에 숫자가 직접 렌더링되면 크래시가 납니다. 이 차이를 알아두면 디버깅 시간을 크게 줄일 수 있습니다.
정리
View는 기본 Flexbox 컨테이너이며, flexDirection 기본값이 column 입니다Text는 텍스트를 표시하는 유일한 방법이고, 스타일 상속은 Text 내부에서만 동작합니다Image의 네트워크 이미지는 ** 반드시 크기를 지정 **해야 합니다- JSX에서
0,''같은 falsy 값이 View 안에 렌더링되지 않도록 조건부 렌더링에 주의하세요
댓글 로딩 중...