아이콘과 커스텀 폰트 — 앱 디자인의 기본 요소
아이콘과 폰트는 앱의 첫인상을 결정합니다. 이미지 아이콘 대신 벡터 아이콘을, 시스템 폰트 대신 커스텀 폰트를 사용하면 훨씬 세련된 앱을 만들 수 있습니다.
벡터 아이콘 — react-native-vector-icons
설치
# Expo
npx expo install @expo/vector-icons
# CLI
npm install react-native-vector-icons
npm install -D @types/react-native-vector-icons
cd ios && pod install
기본 사용법
// Expo에서는 @expo/vector-icons 사용
import { Ionicons, MaterialIcons, FontAwesome } from '@expo/vector-icons';
// CLI에서는 react-native-vector-icons 사용
// import Ionicons from 'react-native-vector-icons/Ionicons';
function IconExample() {
return (
<View style={styles.row}>
<Ionicons name="home" size={24} color="#007AFF" />
<MaterialIcons name="search" size={24} color="#333" />
<FontAwesome name="heart" size={24} color="red" />
<Ionicons name="settings-outline" size={24} color="#666" />
</View>
);
}
아이콘 세트 종류
| 세트 | 특징 | 아이콘 수 |
|---|---|---|
| Ionicons | iOS/Android 스타일 통일 | 1300+ |
| MaterialIcons | Google Material Design | 2000+ |
| FontAwesome | 웹에서 가장 유명 | 1600+ |
| Feather | 깔끔한 라인 아이콘 | 280+ |
| MaterialCommunityIcons | Material 확장판 | 6000+ |
아이콘 버튼 만들기
import { Pressable, StyleSheet } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
function IconButton({
name,
size = 24,
color = '#007AFF',
onPress,
}: {
name: keyof typeof Ionicons.glyphMap;
size?: number;
color?: string;
onPress: () => void;
}) {
return (
<Pressable
onPress={onPress}
hitSlop={12}
style={({ pressed }) => [
styles.iconButton,
pressed && styles.pressed,
]}
>
<Ionicons name={name} size={size} color={color} />
</Pressable>
);
}
const styles = StyleSheet.create({
iconButton: {
padding: 8,
borderRadius: 20,
},
pressed: {
backgroundColor: 'rgba(0, 0, 0, 0.05)',
},
});
탭 네비게이터에서 아이콘 사용
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Ionicons } from '@expo/vector-icons';
const Tab = createBottomTabNavigator();
function TabNavigator() {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
// 포커스 상태에 따라 아이콘 변경
const iconName = {
Home: focused ? 'home' : 'home-outline',
Search: focused ? 'search' : 'search-outline',
Profile: focused ? 'person' : 'person-outline',
}[route.name] as keyof typeof Ionicons.glyphMap;
return <Ionicons name={iconName} size={size} color={color} />;
},
tabBarActiveTintColor: '#007AFF',
tabBarInactiveTintColor: '#999',
})}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Search" component={SearchScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
);
}
커스텀 폰트
Expo에서 폰트 로드
import { useFonts } from 'expo-font';
import * as SplashScreen from 'expo-splash-screen';
import { useEffect } from 'react';
// 앱 시작 시 스플래시 유지
SplashScreen.preventAutoHideAsync();
export default function App() {
const [fontsLoaded] = useFonts({
'Pretendard-Regular': require('./assets/fonts/Pretendard-Regular.otf'),
'Pretendard-Bold': require('./assets/fonts/Pretendard-Bold.otf'),
'Pretendard-SemiBold': require('./assets/fonts/Pretendard-SemiBold.otf'),
});
useEffect(() => {
if (fontsLoaded) {
// 폰트 로드 완료 후 스플래시 숨기기
SplashScreen.hideAsync();
}
}, [fontsLoaded]);
if (!fontsLoaded) {
return null;
}
return <MainApp />;
}
CLI에서 폰트 로드
assets/fonts/
├── Pretendard-Regular.otf
├── Pretendard-Bold.otf
└── Pretendard-SemiBold.otf
// react-native.config.js
module.exports = {
project: {
ios: {},
android: {},
},
assets: ['./assets/fonts/'],
};
# 폰트 링크
npx react-native-asset
폰트 사용
import { Text, StyleSheet } from 'react-native';
function Typography() {
return (
<>
<Text style={styles.heading}>제목 텍스트</Text>
<Text style={styles.body}>본문 텍스트입니다.</Text>
<Text style={styles.caption}>캡션 텍스트</Text>
</>
);
}
const styles = StyleSheet.create({
heading: {
fontFamily: 'Pretendard-Bold',
fontSize: 24,
color: '#1a1a1a',
},
body: {
fontFamily: 'Pretendard-Regular',
fontSize: 16,
lineHeight: 24,
color: '#333',
},
caption: {
fontFamily: 'Pretendard-Regular',
fontSize: 12,
color: '#999',
},
});
타이포그래피 시스템 만들기
// theme/typography.ts
import { TextStyle } from 'react-native';
export const typography: Record<string, TextStyle> = {
h1: {
fontFamily: 'Pretendard-Bold',
fontSize: 28,
lineHeight: 36,
},
h2: {
fontFamily: 'Pretendard-Bold',
fontSize: 24,
lineHeight: 32,
},
h3: {
fontFamily: 'Pretendard-SemiBold',
fontSize: 20,
lineHeight: 28,
},
body1: {
fontFamily: 'Pretendard-Regular',
fontSize: 16,
lineHeight: 24,
},
body2: {
fontFamily: 'Pretendard-Regular',
fontSize: 14,
lineHeight: 20,
},
caption: {
fontFamily: 'Pretendard-Regular',
fontSize: 12,
lineHeight: 16,
},
};
// 사용
function Card() {
return (
<View>
<Text style={[typography.h3, { color: '#1a1a1a' }]}>카드 제목</Text>
<Text style={[typography.body2, { color: '#666' }]}>카드 설명</Text>
</View>
);
}
Google Fonts (Expo)
# Expo Google Fonts 패키지 설치
npx expo install @expo-google-fonts/noto-sans-kr expo-font
import {
useFonts,
NotoSansKR_400Regular,
NotoSansKR_700Bold,
} from '@expo-google-fonts/noto-sans-kr';
export default function App() {
const [fontsLoaded] = useFonts({
NotoSansKR_400Regular,
NotoSansKR_700Bold,
});
if (!fontsLoaded) return null;
return (
<Text style={{ fontFamily: 'NotoSansKR_400Regular' }}>
한글 폰트 적용
</Text>
);
}
SVG 아이콘 사용
# SVG 지원 패키지
npm install react-native-svg
# Expo: npx expo install react-native-svg
# SVG 파일을 컴포넌트로 변환
npm install react-native-svg-transformer
// 커스텀 SVG 아이콘 컴포넌트
import Svg, { Path } from 'react-native-svg';
function CustomIcon({
size = 24,
color = '#000',
}: {
size?: number;
color?: string;
}) {
return (
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
<Path
d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z"
fill={color}
/>
</Svg>
);
}
플랫폼별 폰트 주의사항
import { Platform, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
text: {
// iOS와 Android에서 같은 폰트를 다른 이름으로 참조할 수 있음
fontFamily: Platform.select({
ios: 'Pretendard-Regular',
android: 'Pretendard-Regular', // 보통 같지만 확인 필요
}),
// Android에서 fontWeight와 fontFamily 충돌 주의
// fontWeight를 함께 쓰면 Android에서 무시될 수 있음
// 대신 각 웨이트별 폰트 파일을 별도로 지정
},
});
Android에서
fontFamily와fontWeight를 함께 사용하면 의도한 대로 동작하지 않는 경우가 있습니다. 각 웨이트(Regular, Bold, SemiBold)별로 별도의 폰트 파일을 등록하고,fontFamily로 직접 지정하는 것이 안전합니다.
정리
- 벡터 아이콘은 이미지보다 가볍고 크기 조절이 자유 롭습니다
- Expo에서는
@expo/vector-icons, CLI에서는react-native-vector-icons를 사용하세요 - 커스텀 폰트는 앱 시작 시 로딩 완료 후 화면을 보여줘야 합니다
- 타이포그래피 시스템을 만들어두면 앱 전체의 텍스트 스타일을 일관되게 유지할 수 있습니다
- Android에서는 fontWeight와 fontFamily 충돌에 주의하세요
댓글 로딩 중...