끄적이는 개발노트

React Native - SVG 적용 본문

React Native

React Native - SVG 적용

크런키스틱 2025. 1. 25. 07:40
728x90

React Native에서 SVG를 사용하기 위해서는 react-native-svg 라이브러리가 필요하다.

이를 설치하면 svg를 태그를 통해 직접 디자인할 수가 있다.

npm install react-native-svg
#or
yarn add react-native-svg

 

https://www.npmjs.com/package/react-native-svg

 

react-native-svg

SVG library for react-native. Latest version: 15.11.1, last published: 15 days ago. Start using react-native-svg in your project by running `npm i react-native-svg`. There are 2074 other projects in the npm registry using react-native-svg.

www.npmjs.com

https://github.com/software-mansion/react-native-svg/blob/main/USAGE.md

 

react-native-svg/USAGE.md at main · software-mansion/react-native-svg

SVG library for React Native, React Native Web, and plain React web projects. - software-mansion/react-native-svg

github.com

 

다만, svg 파일을 따로 assets에 추가하고 태그를 바꿔서 사용하는 것이 아닌 import를 통해 바로 사용하기를 원할 경우, 간단한 설치와 설정만 해주면 가능하다.

 

1. 설치

react-native-svg 라이브러리 설치가 선행되어야 한다.

npm install --save-dev react-native-svg-transformer
#or
yarn add --dev react-native-svg-transformer

 

 

2. metro.config.js 설정

// metro.config.js

const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");

const defaultConfig = getDefaultConfig(__dirname);
const { assetExts, sourceExts } = defaultConfig.resolver;

/**
 * Metro configuration
 * https://reactnative.dev/docs/metro
 *
 * @type {import('metro-config').MetroConfig}
 */
const config = {
  transformer: {
    babelTransformerPath: require.resolve(
      "react-native-svg-transformer/react-native"
    )
  },
  resolver: {
    assetExts: assetExts.filter((ext) => ext !== "svg"),
    sourceExts: [...sourceExts, "svg"]
  }
};

module.exports = mergeConfig(defaultConfig, config);

 

기본 metro.config.js를 수정하는 방식이다. 하지만, nativewind와 같이 필요한 라이브러리를 설치하다보면 metro.config.js의 내용을 이미 변경하여 어떻게 적용해야할지 당황스러운 경우가 있다. 그럴 때는 포괄적으로 적용이 되야하는 설정을 마지막에 해주는 형태로 코드를 작성한다. 예를 들어, nativewind를 적용한 설정에 추가할 경우 아래 코드와 같이 nativewind를 최종적으로 감싸주는 형태로 코드를 작성하면 된다.

// metro.config.js

const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
const {withNativeWind} = require('nativewind/metro');

const defaultConfig = getDefaultConfig(__dirname);
const {assetExts, sourceExts} = defaultConfig.resolver;
/**
 * Metro configuration
 * https://reactnative.dev/docs/metro
 *
 * @type {import('metro-config').MetroConfig}
 */

const config = {
  transformer: {
    babelTransformerPath: require.resolve(
      'react-native-svg-transformer/react-native',
    ),
  },
  resolver: {
    assetExts: assetExts.filter(ext => ext !== 'svg'),
    sourceExts: [...sourceExts, 'svg'],
  },
};

const svgConfig = mergeConfig(defaultConfig, config);

module.exports = withNativeWind(svgConfig, {input: './global.css'});

 

 

3. Typescript 설정

Typescript 적용을 위해 declarations.d.ts 파일을 생성해준다.

// declarations.d.ts

declare module '*.svg' {
  import React from 'react';
  import {SvgProps} from 'react-native-svg';
  const content: React.FC<SvgProps>;
  export default content;
}

 

 

3. 코드

아래와 같이 사용하려는 svg 파일을 생성해주고 import해서 사용하면 된다.

 

예시를 위해 지난번 navigation 예시 코드에서 bottom tab icon과 backbutton icon을 추가해봤다.

// navigation.tsx

import DetailScreen from '@/screen/detail';
import HomeScreen from '@/screen/home';
import TestScreen from '@/screen/test';
import {StackParamList} from '@/types/stack';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import AngleLeft from '@assets/svg/angle-left.svg';
import Home from '@assets/svg/home.svg';
import HomeOutline from '@assets/svg/home-outline.svg';
import InfoCircle from '@assets/svg/info-circle.svg';
import InfoCircleOutline from '@assets/svg/info-circle-outline.svg';

const Stack = createNativeStackNavigator<StackParamList>();
const Tab = createBottomTabNavigator<StackParamList>();

const TabStack = () => {
  return (
    <Tab.Navigator
      screenOptions={{
        tabBarActiveTintColor: '#22bb33',
        tabBarInactiveTintColor: 'gray',
      }}>
      <Tab.Screen
        name="Home"
        component={HomeScreen}
        options={{
          headerShown: true,
          headerShadowVisible: false,
          headerTintColor: '#000000',
          headerTitleStyle: {
            fontFamily: 'GmarketSansMedium',
          },
          headerTitleAlign: 'center',
          headerTitle: '홈',
          tabBarIcon: ({focused, size, color}) => {
            const HomeIcon = focused ? (
              <Home fontSize={size} color={color} />
            ) : (
              <HomeOutline fontSize={size} color={color} />
            );
            return HomeIcon;
          },
        }}
      />
      <Tab.Screen
        name="Detail"
        component={DetailScreen}
        options={{
          headerShown: true,
          headerShadowVisible: false,
          headerTintColor: '#000000',
          headerTitleStyle: {
            fontFamily: 'GmarketSansMedium',
          },
          headerTitleAlign: 'center',
          headerTitle: '상세정보',
          tabBarIcon: ({focused, size, color}) => {
            const InfoIcon = focused ? (
              <InfoCircle fontSize={size} color={color} />
            ) : (
              <InfoCircleOutline fontSize={size} color={color} />
            );
            return InfoIcon;
          },
        }}
      />
    </Tab.Navigator>
  );
};

const Navigation = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator
        initialRouteName="Main"
        screenOptions={{headerShown: false, animation: 'flip'}}>
        <Stack.Screen name="Main" component={TabStack} />
        <Stack.Screen
          name="Test"
          component={TestScreen}
          options={({navigation}) => ({
            headerShown: true,
            headerBackVisible: false,
            headerLeft: () => <AngleLeft onPress={() => navigation.goBack()} />,
          })}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

export default Navigation;

 

 

4. 실행

실행하면 아래와 같이 잘 적용된 것을 확인할 수 있다.

728x90