React Native 0.77 - New Styling Features, Android’s 16KB page support, Swift Template

반응형

 

 

https://reactnative.dev/blog/2025/01/21/version-0.77

 

 

 

0.77로 업그레이드

기존 프로젝트의 React Native 버전 간 코드 변경 사항을 확인하려면 React Native Upgrade Helper를 사용하세요. 또한, 업그레이드 문서도 참고하시기 바랍니다.

새로운 프로젝트 생성하려면:
npx @react-native-community/cli@latest init MyProject --version latest

 

만약 Expo를 사용하고 있다면, React Native 0.77은 Expo SDK 52에서 지원됩니다 (Expo 프로젝트 내에서 React Native를 0.77.0으로 업데이트하는 방법에 대한 안내는 가까운 시일 내에 별도의 Expo 블로그 포스트에서 제공될 예정입니다).

 

 

 

 

이번 릴리스에서는 여러 가지 기능이 추가되었습니다.
새로운 스타일링 기능으로는 display: contents, boxSizing, mixBlendMode, 그리고 outline 관련 속성 지원이 포함되어 더욱 강력한 레이아웃 옵션을 제공합니다.
또한, 최신 Android 기기와의 호환성을 위해 Android 16KB 페이지 지원이 추가되었습니다.
커뮤니티 템플릿도 Swift로 마이그레이션하여 현대화하고 있으며, 여전히 Objective-C를 선호하는 개발자를 위해 기존의 호환성과 지원을 유지하고 있습니다.

 

 

더 나은 레이아웃, 크기 조정 및 블렌딩을 위한 새로운 CSS 기능

React Native 0.77은 React Native를 웹과 더욱 일치시키려는 목표를 한 단계 더 발전시킵니다.
이제 새로운 CSS 속성 지원이 추가되어, 앱의 레이아웃, 크기 조정, 블렌딩을 보다 세밀하게 제어할 수 있습니다.
이러한 변경 사항을 통해 복잡한 레이아웃을 단순화하고, 텍스처를 추가하며, 앱의 접근성을 향상시킬 수 있습니다.

 

 

display: contents로 더 간결한 레이아웃 만들기

display: contents 속성을 사용하면 요소가 레이아웃 구조에서는 사라지지만, 해당 자식 요소들은 여전히 부모 요소의 직속 자식인 것처럼 렌더링됩니다.

이 속성은 다음과 같은 상황에서 유용하게 사용할 수 있습니다.

  • 자식 요소에 스타일을 적용하면서도 전체 레이아웃에는 영향을 주지 않고 싶을 때
  • 이벤트 처리를 해야 하는 래퍼 컴포넌트를 만들 때
  • ShadowTree와 상호작용해야 할 때

기술적으로 보면?

display: contents는 해당 요소 자체의 레이아웃 박스는 생성하지 않지만, 자식 요소들의 레이아웃 박스는 그대로 유지됩니다.
즉, display: contents가 적용된 요소는 뷰 계층에서 평탄화(flattend out)되어 사라지지만, 자식 요소들은 기존 레이아웃을 유지합니다.

예제

예를 들어, 특정 위젯을 눌렀을 때 알림(alert)을 표시하고 싶다고 가정해 보겠습니다.
아래 코드에서는 컨테이너 뷰 안에 빨간색 위젯(Red Widget)이 포함되어 있습니다.

 

function Container() {
  return (
    <View style={styles.container}>
      <Widget />
    </View>
  );
}

 

 

 

이제 새로운 Alerting 래퍼 컴포넌트를 만들어 보겠습니다.
이 컴포넌트의 목표는 아래에 있는 요소가 눌렸을 때 사용자에게 알림을 표시하는 것입니다.
이를 위해 실험적인 포인터 이벤트(pointer events)를 사용합니다.

이해를 돕기 위해, 이 컴포넌트의 배경색을 파란색(blue)으로 설정했습니다.
아래와 같은 형태의 컴포넌트가 될 것입니다.

 

function Alerting({children}) {
  return (
    <View
      style={{backgroundColor: 'blue'}}
      onPointerDown={() => alert('Hello World!')}>
      {children}
    </View>
}

function Container() {
  return (
    <View style={styles.container}>
      <Alerting>
        <Widget />
      </Alerting>
    </View>
  );
}

 

이 방식은 우리가 원하는 동작을 완전히 충족하지 않습니다.

Alerting 컴포넌트는 자체적인 경계를 가진 새로운 레이아웃 박스를 추가하게 되며, 이는 자식 요소인 Widget과는 별개입니다.
이 컴포넌트가 감싸는 요소의 스타일에 따라 레이아웃과 기능에 큰 변화가 생길 수도 있습니다.

이 예제에서는 파란색 배경 영역 전체가 탭 이벤트에 반응하여 알림(alert)을 표시하지만, 우리가 원하는 것은 오직 빨간색 "Hello World" 박스가 탭될 때만 알림이 뜨는 것입니다.

 

 

 

만약 Alerting의 View 래퍼에 display: contents를 설정한 상태로 다시 시도하면,
사용자가 Widget의 원래 경계 내에서 눌렀을 때만 알림이 표시됩니다.

이는 Alerting이 자체적인 박스를 추가하지 않기 때문이며,
여전히 Widget에서 버블링된 포인터 이벤트를 감지할 수 있기 때문입니다.

 

function Alerting({children}) {
  return (
    <View
      style={{display: 'contents'}}
      onPointerDown={() => alert('Hello World!')}>
      {children}
    </View>
  );
}

// ... function Container ...

 

 

 

 

박스 사이징 (Box Sizing)

boxSizing 속성은 요소의 다양한 크기 속성(width, height, minWidth, minHeight 등)이 어떻게 계산되는지를 정의합니다.

  • boxSizing: border-box인 경우, 크기 속성들은 요소의 테두리 박스(border box)에 적용됩니다.
  • boxSizing: content-box인 경우, 크기 속성들은 콘텐츠 박스(content box)에 적용됩니다.

기본값은 border-box이며, 웹의 기본값과 다릅니다.
이 속성에 대해 더 자세히 알고 싶다면, 웹 문서를 참고하는 것이 좋습니다.

border-box는 지금까지 기본값이었으며, 우리가 content-box를 추가하기 전까지 유일한 boxSizing 값이었습니다.

만약 기본값을 변경했다면, 여러 레이아웃이 갑자기 깨지는 문제가 발생하는 브레이킹 체인지(호환성을 깨뜨리는 변경)가 되었을 것입니다.

따라서 기존 코드와의 호환성을 유지하기 위해 border-box를 기본값으로 유지하기로 결정했습니다.

 

border-box와 content-box의 차이를 이해하려면 다음 예제를 살펴보세요.

두 개의 View가 있고, 둘 다 padding: 20 및 borderWidth: 10을 가지고 있습니다.

  • border-box를 사용할 경우, 요소의 크기를 계산할 때 border와 padding이 포함됩니다.
  • content-box를 사용할 경우, 오직 content(내용)만 크기 계산에 포함됩니다.

 

CSS mixBlendMode

mixBlendMode 속성을 사용하면 요소의 색상이 해당 스태킹 컨텍스트 내의 다른 요소들과 어떻게 혼합되는지를 제어할 수 있습니다.
각 혼합 방식에 대한 자세한 내용은 MDN 문서를 참고하세요.

isolation 속성 추가

더 세밀한 제어를 위해 isolation 속성도 추가되었습니다.
isolation: isolate을 View에 설정하면 독립적인 스태킹 컨텍스트가 형성됩니다.
즉, 특정 조상 View에 이 속성을 설정하면 그 하위 요소의 mixBlendMode가 해당 View를 벗어나지 않도록 제한할 수 있습니다.


mixBlendMode 값 설명

  • normal: 요소가 배경 위에 혼합 없이 그려짐.
  • multiply: 소스 색상과 대상 색상을 곱한 후 대상 색상을 대체.
  • screen: 보색을 곱한 후 다시 보색으로 변환하여 밝은 효과 생성.
  • overlay: 배경색 값에 따라 multiply 또는 screen을 적용.
  • darken: 배경색과 소스 색상 중 더 어두운 색상을 선택.
  • lighten: 배경색과 소스 색상 중 더 밝은 색상을 선택.
  • color-dodge: 배경색을 더 밝게 만들어 소스 색상을 반영(검정색은 변화 없음).
  • color-burn: 배경색을 더 어둡게 만들어 소스 색상을 반영(흰색은 변화 없음).
  • hard-light: 소스 색상에 따라 multiply 또는 screen 적용(강한 조명 효과).
  • soft-light: 소스 색상에 따라 어둡거나 밝게 조정(부드러운 조명 효과).
  • difference: 두 색상 중 더 어두운 색상을 밝은 색상에서 빼기.
  • exclusion: difference와 유사하지만 대비가 낮음.
  • hue: 소스 색상의 색조 + 배경색의 채도 및 명도 적용.
  • saturation: 소스 색상의 채도 + 배경색의 색조 및 명도 적용.
  • color: 소스 색상의 색조와 채도 + 배경색의 명도 적용(흑백 이미지 색칠할 때 유용).
  • luminosity: 소스 색상의 명도 + 배경색의 색조와 채도 적용(color의 반대 효과).

 

Outline 속성

새로 추가된 outlineWidth, outlineStyle, outlineSpread, outlineColor 속성은, 각각의 border 속성과 비슷하게 작동하지만, 패딩 박스가 아닌 경계 박스(border box) 주위에 렌더링됩니다. 이 속성들은 레이아웃에 영향을 주지 않으면서 요소의 외곽선을 그려 강조할 수 있게 해줍니다.

자세한 내용은 MDN 문서를 참고하세요.

 

 

Android 버전 15 지원 및 16KB 페이지 지원

Android 15에서의 강제 전체 화면 표시

우리는 이전 릴리즈에서 Android 15를 지원하기 위한 작업을 이미 진행한 바 있습니다. Android 15에서 눈에 띄는 변화 중 하나는 targetSdk 35로 앱을 빌드할 경우 강제 전체 화면 표시(edge-to-edge display)가 적용된다는 점입니다.

이 부분에 대해 아직 확인하지 않으셨다면, 이 기능을 처리하는 방법에 대해 이전에 권장했던 내용을 참고하세요. 이를 무시하면 앱의 UI가 깨질 수 있습니다.

앱에서 react-native-safe-area-context 라이브러리를 사용하고 있다면,
이 라이브러리가 이미 강제 전체 화면 표시(edge-to-edge)를 처리해주고 있습니다.

 

Android 15는 16KB 메모리 페이지 크기를 지원하여 앱 성능 향상 등 여러 이점을 제공하지만, 이전의 4KB 기반 앱은 향후 장치에서 호환되지 않을 수 있습니다. 현재는 선택적으로 일부 장치에서 테스트할 수 있는 기능으로, 향후 16KB 페이지 크기가 OS의 기본값이 될 준비가 되어 있습니다.

React Native 0.77 버전은 16KB 페이지 크기를 완전히 지원할 준비가 되어 있으며, 개발자는 이를 사용하여 16KB 장치에 대한 앱을 테스트하고 배포할 수 있습니다.

16KB 지원에 대한 자세한 정보는 공식 Android Developers 사이트를 참조하세요.

 

 

커뮤니티 CLI 및 템플릿 업데이트

커뮤니티 CLI: react-native init 명령어 폐지

이번 버전에서는 React Native 0.75에서 소개된 react-native init 명령어의 폐지가 완전히 이루어졌습니다.

다시 말하지만, 이제 react-native init 명령어를 사용할 수 없으며, 대신 다음 방법 중 하나를 사용해야 합니다:

  • 새로운 프로젝트를 생성하기 위해 Expo와 같은 프레임워크를 사용하고, Expo 전용 명령어인 npx create-expo-app을 사용하세요.
  • 커뮤니티 CLI를 직접 호출하여 npx @react-native-community/cli init을 사용하세요.

커뮤니티 CLI: Metro에서 "iOS/Android 실행" 키 핸들러 제거

이번 버전에서는 Metro에서 'a'와 'i' 키보드 단축키를 제거했습니다. 이 단축키는 각각 run-android와 run-ios 커뮤니티 CLI 명령어를 호출하는 데 사용되었습니다. 해당 키보드 단축키는 개발자 경험을 악화시키고 거의 사용되지 않았습니다. 또한 프레임워크가 터미널 출력을 조작하는 데 더 적합하다고 생각합니다. 이 변경 사항에 대해 자세히 읽으려면 전용 게시물을 참조하세요.

 

커뮤니티 템플릿: iOS 앱을 위한 프로그래밍 언어로 Swift 채택

이번 변경으로 우리는 커뮤니티 템플릿을 간소화했습니다. 기존에 있던 세 개의 파일(main.m, AppDelegate.h, AppDelegate.mm)을 하나의 새로운 파일(AppDelegate.swift)로 대체했습니다.

기술적으로 이 변경은 브레이킹 체인지(breaking change)입니다. 업그레이드 도우미에서 Objective-C에서 Swift로 변경된 것을 확인할 수 있습니다.

 

Swift로 마이그레이션할 필요는 없습니다. iOS 커뮤니티 템플릿의 Objective-C++ 변형은 여전히 지원됩니다(단, RCTAppDependencyProvider를 통합해야 합니다). 새로운 프로젝트는 iOS 앱 언어로 Swift를 사용하여 생성되지만, 필요하면 언제든지 Objective-C로 다시 마이그레이션할 수 있습니다.

 

제한 사항

앱에 C++로 작성된 로컬 모듈이 있다면, 이 가이드에서 설명한 대로 Swift에서 이를 등록할 수 없습니다.

만약 앱이 이 범주에 해당한다면, AppDelegate를 Swift로 마이그레이션하지 않고, Objective-C++를 계속 사용해야 합니다.

React Native의 핵심은 주로 iOS와 Android 및 다른 플랫폼 간의 코드 공유를 촉진하기 위해 C++로 개발되었습니다. Swift와 C++ 간의 상호 운용성은 아직 성숙하거나 안정적이지 않습니다. 우리는 이 간극을 메우고 Swift로의 마이그레이션을 지원할 방법을 검토하고 있습니다.

RCTAppDependencyProvider

React Native 0.77은 앱이 서드파티 의존성을 로드하는 방식을 약간 변경했습니다. 이는 커뮤니티 템플릿에 새로 추가된 부분으로, 이를 놓치면 런타임 문제를 일으킬 수 있습니다. 이 부분을 앱에 추가해야 합니다.

이에 해당하는 Objective-C 코드 라인은 다음과 같습니다:

#import "AppDelegate.h"

#import <React/RCTBundleURLProvider.h>
#import <ReactAppDependencyProvider/RCTAppDependencyProvider.h>


@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  self.moduleName = @"<Your app Name>";
  self.dependencyProvider = [RCTAppDependencyProvider new];
  // You can add your custom initial props in the dictionary below.
  // They will be passed down to the ViewController used by React Native.
  self.initialProps = @{};

  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

// remaining of the AppDelegate

 

 

Breaking Changes

Metro에서 console.log() 스트리밍 제거

우리는 React Native 디버깅의 모든 측면이 신뢰성 있게 동작하고, 최신 브라우저 도구의 기능을 일치시키기를 원합니다. 이 품질 기준을 충족시키기 위해, Metro를 통한 로그 전달은 0.76에서 이미 더 이상 사용되지 않으며, 0.77에서 완전히 제거되었습니다.

이 통합 방식은 장치에서 디버깅 대상을 커스터마이즈하여 통신하는 방식에 의존했었습니다. 이 변경으로 우리는 Chrome DevTools Protocol (CDP)만을 사용하도록 전환하고 있습니다.

JavaScript 로그를 보려면, React Native DevTools와 그 안의 완전한 기능을 갖춘 콘솔 패널을 사용하십시오. 이 패널은 로그 필터링, 풍부한 객체 검사, 실시간 표현식 등을 지원합니다. 또한, Expo Tools나 Radon IDE와 같은 서드파티 확장을 통해 VS Code를 CDP 디버거로 연결할 수도 있습니다.

이 통합은 React 팀에서 직접 지원하지 않지만, 2025년에는 VS Code에 대한 공식 지원을 제공할 예정입니다.

Expo는 Expo CLI에서 로그 스트리밍을 계속 제공합니다.

자세한 내용은 "Why are JavaScript logs leaving Metro?"를 참고하세요.

 

 

 

Other Breaking Changes

General

Animation:

  • 네이티브 루프 애니메이션은 루프가 끝날 때마다 React 상태 업데이트를 보내지 않습니다.

Layout:

  • ScrollView에서 스티키 헤더의 위치가 이제 고려됩니다.
  • 절대 위치 지정이 이제 더 일관되게 동작합니다.

JS Modules:

  • ReactFabricInternals 모듈 제거: 더 이상 접근할 수 없습니다.

Native Modules:

  • NativeModules 객체를 사용하여 이제 JS에서 터보 모듈을 로드할 수 있습니다.
  • 이는 네이티브 모듈과 터보 네이티브 모듈 간의 호환성을 향상시킵니다.

Packages:

  • dev-middleware: 프레임워크는 미들웨어 호스트에 대해 serverBaseUrl을 상대 경로로 지정해야 합니다.

API Changes:

  • AppRegistry에서 useConcurrentRoot의 타입을 제거했습니다. 이미 무시되고 있었습니다.
  • NativeMethods TypeScript 정의에서 refs 속성이 제거되었습니다.

UX Changes:

  • 개발 서버 키 명령에서 "run on iOS"와 "run on Android"가 제거되었습니다.

Android

Kotlin:

  • 이번 React Native 버전은 Kotlin 2.0.21을 기반으로 빌드된 첫 번째 버전입니다. Kotlin 2.0에 대한 변경 사항은 언어 릴리스 노트를 참조하세요.

API Changes:

  • Nullability:
    • ReadableArray의 비원시 getter가 이제 올바르게 선택적(optional) 타입으로 지정됩니다.
    • ReactHost.createSurface() 메서드를 널(null) 가능하지 않도록 변경했습니다.
  • Renamed:
    • DevSupportManagerBase.getCurrentContext()를 DevSupportManagerBase.getCurrentReactContext()로 이름을 변경했습니다.

추가적으로, 몇몇 API는 제거되거나 가시성이 제한되어 더 이상 접근할 수 없습니다. 이러한 API들은 내부용으로, React Native 개발자에게는 필요하지 않았습니다. 아래에서 전체 목록을 확인할 수 있습니다.

다음 패키지는 이제 내부 패키지로 변경되어 더 이상 접근할 수 없습니다:
com.facebook.react.views.progressbar
com.facebook.react.views.safeareaview
com.facebook.react.modules.accessibilityinfo
com.facebook.react.modules.appstate
com.facebook.react.modules.clipboard
com.facebook.react.modules.devmodule
com.facebook.react.modules.reactdevtoolssettings
com.facebook.react.views.unimplementedview

다음 클래스들은 이제 내부 클래스이거나 삭제되어 더 이상 접근할 수 없습니다:
BackHandler.removeEventListener
BaseViewManagerInterface
BindingImpl
CompositeReactPackage
DebugOverlayTags
Method create() from DefaultDevSupportManagerFactory
DevToolsReactPerfLogger
FabricComponents
ImageStoreManager
InteropModuleRegistry
NativeModulePerfLogger
NoopPrinter
NotThreadSafeViewHierarchyUpdateDebugListener
OkHttpCallUtil
PrinterHolder
Printer
ReactDebugOverlayTags
ReactNativeFlipper
ReactViewBackgroundManager
ReactViewGroup.getBackgroundColor()
ReactVirtualTextShadowNode
ReactVirtualTextViewManager
SimpleSettableFuture
SwipeRefreshLayoutManager
TaskCompletionSource
Parameter jsBundleLoader from DefaultReactHost.getDefaultReactHost()

 

iOS

API 변경 사항
삭제됨:

  • RCTConstants.RCTGetMemoryPressureUnloadLevel
  • partialBatchDidFlush
  • RCTRuntimeExecutor
  • UseNativeViewConfigsInBridgelessMode (적절한 기능 플래그로 대체됨)
  • UseTurboModuleInteropForAllTurboModules (Turbo Modules에서 Interop 계층이 항상 활성화됨)

변경됨:

  • CGColorRef 대신 UIColor 사용
  • RCTAppDelegate는 이제 RCTDependencyProvider를 사용하여 서드파티 종속성을 로드해야 함
  • CocoaPods는 모든 서드파티 종속성에 대해 C++ 버전을 설정하여 컴파일 문제를 방지함
React 19는 2024년 12월 6일에 출시되었습니다. 그 당시, 우리는 이미 React Native 0.77의 브랜치를 생성했고, React Native 0.77을 위한 세 개의 RC(Release Candidate)를 이미 출시했습니다. React Native 0.77의 출시 일정상 React 19를 이번 릴리스에 포함시키기에는 너무 늦었습니다.
React 19는 React Native 0.78에 포함되어 출시될 예정이며, 우리는 이미 이 버전의 브랜치를 생성했습니다. 새로운 앱을 생성하려면 다음 명령어를 사용하여 시도해볼 수 있습니다:
npx @react-native-community/cli init YourReact19App --version 0.78.0-rc.0

 

 

 

반응형

댓글

Designed by JB FACTORY