본문 바로가기

카테고리 없음

[React] React Native와 React의 코드 공유 전략

반응형

React와 React Native의 등장으로 웹과 모바일 앱 개발에서 JavaScript를 사용한 크로스 플랫폼 개발이 가능해졌습니다. 이 두 기술은 같은 철학과 비슷한 API를 공유하고 있어, 많은 부분에서 코드를 재사용할 수 있습니다. 이 블로그에서는 React와 React Native 사이의 효과적인 코드 공유 전략에 대해 알아보겠습니다.

1. 프로젝트 구조 설정

효과적인 코드 공유를 위해서는 적절한 프로젝트 구조가 필요합니다. 다음과 같은 구조를 고려해 볼 수 있습니다:

my-project/
├── src/
│   ├── common/
│   │   ├── components/
│   │   ├── hooks/
│   │   └── utils/
│   ├── web/
│   │   ├── components/
│   │   └── pages/
│   └── native/
│       ├── components/
│       └── screens/
├── package.json
└── README.md

이 구조에서 common 디렉터리는 공유 코드를 포함하고, webnative 디렉토리는 각각 플랫폼 특정 코드를 포함합니다.

2. 비즈니스 로직 공유하기

비즈니스 로직은 대부분 플랫폼에 독립적이므로 쉽게 공유할 수 있습니다.

예를 들어, 사용자 인증 로직을 살펴보겠습니다:

// src/common/utils/auth.js

export const validateEmail = (email) => {
  const re = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
  return re.test(String(email).toLowerCase());
};

export const authenticateUser = async (email, password) => {
  if (!validateEmail(email)) {
    throw new Error('Invalid email format');
  }

  // 여기에 실제 인증 로직 구현 (예: API 호출)
  const response = await fetch('https://api.example.com/login', {
    method: 'POST',
    body: JSON.stringify({ email, password }),
    headers: { 'Content-Type': 'application/json' },
  });

  if (!response.ok) {
    throw new Error('Authentication failed');
  }

  return response.json();
};

이 코드는 웹과 모바일 앱 모두에서 그대로 사용할 수 있습니다.

3. UI 컴포넌트 추상화

UI 컴포넌트는 플랫폼별로 다르게 구현해야 하지만, 인터페이스를 통일하여 사용 방법을 동일하게 만들 수 있습니다.

예를 들어, 버튼 컴포넌트를 살펴보겠습니다:

// src/common/components/Button.js

import React from 'react';

const Button = ({ onPress, title }) => {
  if (Platform.OS === 'web') {
    return <WebButton onClick={onPress}>{title}</WebButton>;
  } else {
    return <NativeButton onPress={onPress} title={title} />;
  }
};

export default Button;

// src/web/components/WebButton.js
import React from 'react';

const WebButton = ({ onClick, children }) => (
  <button onClick={onClick}>{children}</button>
);

export default WebButton;

// src/native/components/NativeButton.js
import React from 'react';
import { Button } from 'react-native';

const NativeButton = ({ onPress, title }) => (
  <Button onPress={onPress} title={title} />
);

export default NativeButton;

이렇게 하면 공통 인터페이스를 통해 플랫폼별 구현을 추상화할 수 있습니다.

4. 스타일 공유하기

스타일을 공유하기 위해 플랫폼 독립적인 스타일 객체를 만들 수 있습니다:

// src/common/styles/common.js

export const colors = {
  primary: '#007bff',
  secondary: '#6c757d',
  success: '#28a745',
  danger: '#dc3545',
};

export const fontSizes = {
  small: 12,
  medium: 16,
  large: 20,
};

// 웹용 스타일
export const webStyles = {
  button: {
    backgroundColor: colors.primary,
    color: 'white',
    padding: '10px 20px',
    fontSize: `${fontSizes.medium}px`,
    border: 'none',
    borderRadius: '4px',
  },
};

// React Native용 스타일
export const nativeStyles = {
  button: {
    backgroundColor: colors.primary,
    color: 'white',
    padding: 10,
    fontSize: fontSizes.medium,
    borderRadius: 4,
  },
};

이렇게 정의한 스타일은 각 플랫폼의 컴포넌트에서 쉽게 사용할 수 있습니다.

5. 환경 설정 공유하기

환경 설정도 공유할 수 있습니다. 예를 들어, API 엔드포인트나 앱 설정을 공유 파일에 정의할 수 있습니다:

// src/common/config.js

export const API_BASE_URL = 'https://api.example.com';

export const APP_VERSION = '1.0.0';

export const MAX_ITEMS_PER_PAGE = 20;

이 설정 파일은 웹과 모바일 앱 모두에서 import 하여 사용할 수 있습니다.

6. 상태 관리 로직 공유하기

Redux나 MobX와 같은 상태 관리 라이브러리를 사용한다면, 상태 관리 로직의 대부분을 공유할 수 있습니다.

예를 들어, Redux 액션과 리듀서를 살펴보겠습니다:

// src/common/redux/userSlice.js

import { createSlice } from '@reduxjs/toolkit';

const userSlice = createSlice({
  name: 'user',
  initialState: {
    currentUser: null,
    isLoading: false,
    error: null,
  },
  reducers: {
    loginStart: (state) => {
      state.isLoading = true;
    },
    loginSuccess: (state, action) => {
      state.isLoading = false;
      state.currentUser = action.payload;
      state.error = null;
    },
    loginFailure: (state, action) => {
      state.isLoading = false;
      state.error = action.payload;
    },
    logout: (state) => {
      state.currentUser = null;
    },
  },
});

export const { loginStart, loginSuccess, loginFailure, logout } = userSlice.actions;

export default userSlice.reducer;

이 Redux 슬라이스는 웹과 모바일 앱 모두에서 그대로 사용할 수 있습니다.

7. 테스트 코드 공유하기

비즈니스 로직에 대한 테스트 코드도 공유할 수 있습니다. Jest를 사용한 테스트 예제를 살펴보겠습니다:

// src/common/utils/__tests__/auth.test.js

import { validateEmail, authenticateUser } from '../auth';

describe('Auth Utils', () => {
  test('validateEmail returns true for valid email', () => {
    expect(validateEmail('test@example.com')).toBe(true);
  });

  test('validateEmail returns false for invalid email', () => {
    expect(validateEmail('invalid-email')).toBe(false);
  });

  // authenticateUser에 대한 테스트는 모킹이 필요할 수 있습니다
});

이 테스트 코드는 웹과 모바일 앱 프로젝트 모두에서 실행할 수 있습니다.

8. 플랫폼별 차이 처리하기

때로는 플랫폼별로 다른 동작이 필요할 수 있습니다. 이런 경우 조건부 로직을 사용할 수 있습니다:

// src/common/utils/platform.js

import { Platform } from 'react-native';

export const isWeb = Platform.OS === 'web';
export const isIOS = Platform.OS === 'ios';
export const isAndroid = Platform.OS === 'android';

export const getPlatformSpecificValue = (webValue, mobileValue) => {
  return isWeb ? webValue : mobileValue;
};

이 유틸리티 함수들을 사용하여 플랫폼별 차이를 처리할 수 있습니다.


React와 React Native 사이의 코드 공유는 개발 효율성을 크게 높일 수 있는 전략입니다. 비즈니스 로직, UI 컴포넌트 인터페이스, 스타일, 설정, 상태 관리 로직, 그리고 테스트 코드 등 다양한 영역에서 코드를 공유할 수 있습니다.

 

효과적인 코드 공유를 위해서는 다음 사항들을 고려해야 합니다:

  1. 적절한 프로젝트 구조 설계
  2. 플랫폼 독립적인 로직과 플랫폼 종속적인 구현의 명확한 분리
  3. 공통 인터페이스를 통한 UI 컴포넌트 추상화
  4. 플랫폼별 차이를 처리하기 위한 유틸리티 함수 사용

이러한 전략을 적용하면 개발 시간을 단축하고, 코드의 일관성을 유지하며, 버그 발생 가능성을 줄일 수 있습니다. 또한, 새로운 기능을 추가하거나 버그를 수정할 때 한 곳에서 변경사항을 관리할 수 있어 유지보수성도 향상됩니다.

 

React와 React Native를 함께 사용하는 프로젝트에서는 이러한 코드 공유 전략을 적극적으로 고려해 보시기 바랍니다. 초기에는 약간의 추가 설정과 구조화 작업이 필요할 수 있지만, 장기적으로 볼 때 그 이상의 가치를 제공할 것입니다.

반응형