Skip to content

Releases: LESANF/react-native-template-lesa

v0.0.12

Choose a tag to compare

@LESANF LESANF released this 28 Sep 08:59
1d4812e

Warning

Still a PoC. 0.0.x promises no API or structural stability.

A small release: one developer-experience fix carried as a patch, and dependency housekeeping.

The Element Inspector no longer throws away your screen

Toggling Touchables in the in-app Element Inspector bumped the root view's key and remounted the entire app, so you landed back on the first screen with navigation and component state gone. That code has been in React Native since 2016. The template now carries a pnpm patch on react-native@0.86.3 that makes the PressabilityDebug flag subscribable and drops the remount; the dotted boxes and pressable Text subscribe to it instead. Nothing attaches outside __DEV__, so release builds are untouched.

We sent the same change upstream as facebook/react-native#58705. Even when it lands it will be 0.88+ with no backport, so the patch stays until the template moves there. patches/README.md has the dated entry and the exact removal condition.

Expo patch versions

expo, expo-router, expo-constants, expo-build-properties and @expo/metro-runtime moved to the versions expo-doctor expects. 21/21.

Already generated a project?

MIGRATION.md has a v0.0.10 / v0.0.11 row: npx expo install --fix for the versions, and the patch copied straight from this tag if you want it.


Warning

여전히 PoC 입니다. 0.0.x 는 API·구조 안정성을 약속하지 않습니다.

작은 릴리즈입니다. 패치로 나가는 개발 경험 수정 하나와 의존성 정리.

Element Inspector 가 화면을 날리지 않습니다

인앱 Element Inspector 의 Touchables 를 누르면 루트 뷰의 key 가 올라가 앱 전체가 리마운트됐습니다. 내비게이션과 컴포넌트 상태가 사라지고 첫 화면으로 돌아갔죠. 2016년부터 React Native 에 있던 코드입니다. 템플릿은 이제 react-native@0.86.3 에 pnpm 패치를 얹습니다 — PressabilityDebug 플래그를 구독 가능하게 바꾸고 리마운트를 없앴습니다. 점선 박스와 onPress 있는 Text 가 대신 구독합니다. __DEV__ 밖에는 아무것도 붙지 않아 릴리즈 빌드는 그대로입니다.

같은 변경을 facebook/react-native#58705 로 업스트림에 올렸습니다. 들어가도 0.88+ 이고 백포트는 없으니 그 버전으로 갈 때까지 패치가 남습니다. patches/README.md 에 날짜별 항목과 지우는 조건이 있습니다.

Expo 패치 버전

expo·expo-router·expo-constants·expo-build-properties·@expo/metro-runtime 을 expo-doctor 가 기대하는 버전으로 올렸습니다. 21/21.

이미 프로젝트를 만들었다면

MIGRATION.md 에 v0.0.10 · v0.0.11 행이 있습니다. 버전은 npx expo install --fix, 패치는 원하면 이 태그에서 파일을 그대로 받습니다.

v0.0.11

Choose a tag to compare

@LESANF LESANF released this 22 Sep 06:41
c82bb61

Fixed

  • semantic.css 에 @variant light 블록을 되살렸다. 0.0.10 은 light 값을 @theme static 에만 두어
    Uniwind 가 부팅 때 Theme light is missing variable --color-… 를 냈다. 의미 토큰은 등록·light·dark
    세 블록에 같이 넣는다 — use-colors.test 가 셋의 집합이 같은지 본다

v0.0.10

Choose a tag to compare

@LESANF LESANF released this 22 Sep 05:36
b12c304

Added

  • UnderlineText·StrikethroughText — 선을 뷰로 그린다. textDecorationLine 은 두께·색·위치를 못 잡는다
  • useDismissKeyboardOnBackground(루트 레이아웃에서 호출) — iOS 가 백그라운드 전환 때 포커스를 남겨 복귀
    후 키보드가 안 뜨는 문제. useDebouncedValue
  • components/icons/icon.tsx — 모든 아이콘의 바탕. onPress 를 주면 Pressable 로 감싼다(hitSlop 16).
    탭 아이콘이 이 위로 옮겨 갔다
  • lib/dayjs(로케일을 DEFAULT_LANGUAGE 로 한 번 건다) · utils/format(로케일 중립 표기만) · dayjs 의존성
  • constants/motion — 시간·곡선·스프링 공통 상수
  • ESLint: 상위 폴더 상대경로(../) import 를 warn 으로 막고 @/ alias 로 통일
  • docs/ui.md "UI 원칙" — Figma 인스턴스 분포 검증, 높이는 감싸는 뷰가, 구조 우선, 행간 안전값

Changed

  • Input 구조 — 높이·테두리·배경이 입력에서 감싸는 뷰로 옮겨 갔다(className 대상이 바뀐다).
    입력은 text-size-md 만, 행간 없음. hint·left·right 추가, editable 대신 disabled. 모양은 그대로

  • Button 동작 — textClassName, 라벨 한 줄, ghost·link 는 로딩 중 라벨 유지, 로띠 점 색을
    variant 별 토큰으로. size 와 모양은 그대로

  • 색 토큰을 원시색 층으로 분리 — colors.css 는 @theme static 원시색만, semantic.css 가 의미 토큰을
    등록하고 light 값을 주며 dark 는 값만 바꾼다. 새 앱은 원시색만 바꾼다. 탭 라벨 색이
    --color-tab-active·--color-tab-inactive 토큰이 됐다(값은 그대로)

  • JS 색은 useColors() (lib/theme/use-colors.ts) — 토큰 전부를 구독 하나로 읽고 테마를 따라간다.
    constants/tab-bar.ts 는 지웠다. ESLint 가 src 의 hex 리터럴, use-colors.ts 밖의 useCSSVariable,
    getCSSVariable 을 error 로 막는다(docs/colors-in-js.md)

v0.0.9

Choose a tag to compare

@LESANF LESANF released this 17 Sep 04:09
9aea1f4

Warning

Still a PoC. 0.0.x promises no API or structural stability.

Two things change what you get, and a round of housekeeping.

Rich push images on iOS

iOS hands an image push to a Notification Service Extension, not to the app. Without one, a background or killed app shows the text and drops the image. The template now ships that extension: targets/notification-service/ is the reference app's, copied as-is, and @bacons/apple-targets turns it into an Xcode target at prebuild. It reads fcm_options.image — the same key the foreground path already used.

It follows the template's existing rule: the folder's presence turns it on, and only while push itself is on. Delete the folder and the target is gone; push off means no target regardless. The bundle is <bundleId>.ImageNotification, and a device build needs a provisioning profile for that bundle.

@bacons/apple-targets@4.0.6 crashes on any non-clean prebuild once the target exists — the loop that removes the old build configuration list clears the very property it is iterating. patches/ carries the fix. Upstream has the identical change open as a PR with no review since August, and 5.0.0 has the same code, so this is a patch we expect to keep and re-target on upgrades. patches/README.md states the exact condition for dropping it.

One trap found on the first device test: iOS attachments accept JPEG, PNG and GIF only. placehold.co/600x400 returns SVG unless you add .png, and the notification then arrives without its image and without any error you would see. Both the extension and the foreground path fail the same way.

pnpm: a floor, not a pin

packageManager: pnpm@11.10.0 was doing the opposite of what a template wants. The pnpm binary is usually the corepack shim, and corepack honors that field — so a machine with pnpm 12 installed globally ran 11.10.0 the moment it entered the project, and every generated app inherited the same downgrade. The pin is gone. engines.pnpm: ">=12" sets a floor that pnpm enforces locally, only-allow pnpm keeps blocking npm and yarn, and CI passes version: 12 to pnpm/action-setup, which had been reading the pin. pnpm 12 keeps 11's lockfile format, so nothing else moves.

Housekeeping

docs/handoff.md still described SDK 55 and pnpm 10 and pointed at placeholder paths; it is gone. docs/template-completion.md stays as the repo's own progress log but no longer ships to generated projects (CLI 0.0.9). patches/README.md now opens with a dated table of every patch added or removed — including the one-day expo patch from 0.0.4 — so the next person can see why each existed.

Already generated a project?

MIGRATION.md has rows for v0.0.7 and v0.0.8: the pnpm change is a two-line edit everyone should make; the extension is opt-in and the guide copies the files straight from this tag rather than asking you to retype them.


Warning

여전히 PoC 입니다. 0.0.x 는 API·구조 안정성을 약속하지 않습니다.

받는 쪽이 달라지는 변경 두 가지와 정리 한 번입니다.

iOS 리치 푸시 이미지

iOS 는 이미지가 든 푸시를 앱이 아니라 Notification Service Extension 에 넘깁니다. 이게 없으면 백그라운드·종료 상태에서 글만 뜨고 이미지는 빠집니다. 이번에 그 확장을 넣었습니다. targets/notification-service/ 는 참조 앱의 것을 그대로 복사했고, @bacons/apple-targets 가 prebuild 때 Xcode 타깃으로 만듭니다. 읽는 키는 fcm_options.image 로, 포그라운드 경로가 이미 쓰던 것과 같습니다.

템플릿의 기존 규칙을 따릅니다 — 폴더가 있으면 켜지고, 푸시가 켜진 상태에서만입니다. 폴더를 지우면 타깃도 사라지고, 푸시가 꺼져 있으면 폴더가 있어도 붙지 않습니다. 번들은 <bundleId>.ImageNotification 이고 실기 빌드에는 그 번들의 프로비저닝 프로필이 따로 필요합니다.

@bacons/apple-targets@4.0.6 은 타깃이 이미 있는 상태의 prebuild(--no-clean)에서 죽습니다 — 기존 build configuration list 를 지우는 루프가 자기가 돌고 있는 프로퍼티를 지워버립니다. patches/ 가 고칩니다. 업스트림에 같은 수정이 8월부터 리뷰 없이 열려 있고 5.0.0 도 같은 코드라, 들어올 것으로 가정하지 않고 버전을 올릴 때마다 재타깃하는 패치로 관리합니다. 지우는 조건은 patches/README.md 에 있습니다.

첫 실기 테스트에서 밟은 함정 하나: iOS 첨부는 JPEG·PNG·GIF 만 받습니다. placehold.co/600x400 은 .png 를 붙이지 않으면 SVG 를 주고, 알림은 이미지 없이 아무 에러도 없이 도착합니다. 확장과 포그라운드 경로가 같은 방식으로 실패합니다.

pnpm — 핀이 아니라 하한

packageManager: pnpm@11.10.0 은 템플릿이 원하는 것과 반대로 움직였습니다. pnpm 바이너리는 대개 corepack 심이고 corepack 은 이 필드를 따릅니다. 전역에 pnpm 12 를 깔아둔 머신이 프로젝트에 들어오는 순간 11.10.0 으로 돌았고, 생성된 앱도 전부 같은 다운그레이드를 물려받았습니다. 핀을 뺐습니다. engines.pnpm: ">=12" 가 하한이고(로컬에서 불일치면 pnpm 이 실패합니다), only-allow pnpm 이 npm·yarn 을 막고, 핀을 읽고 있던 CI 의 pnpm/action-setup 에는 version: 12 를 줍니다. pnpm 12 는 11 의 lockfile 형식을 그대로 쓰니 그 외에는 움직이는 게 없습니다.

정리

docs/handoff.md 는 여전히 SDK 55·pnpm 10 을 이야기하고 자리표시 경로를 가리키고 있어 지웠습니다. docs/template-completion.md 는 레포의 진행 기록으로 남지만 생성 프로젝트에는 더 이상 가지 않습니다(CLI 0.0.9). patches/README.md 는 넣고 뺀 패치 전부의 날짜별 표로 시작합니다 — 0.0.4 때 하루 살았던 expo 패치까지 — 다음 사람이 왜 있었는지 볼 수 있게.

이미 프로젝트를 만들었다면

MIGRATION.md 에 v0.0.7·v0.0.8 행이 있습니다. pnpm 변경은 두 줄 편집이라 모두에게 권하고, 확장은 선택이며 가이드는 손으로 옮겨 적지 않고 이 태그에서 파일을 그대로 받게 되어 있습니다.

v0.0.8

Choose a tag to compare

@LESANF LESANF released this 16 Sep 13:15
e555319

Warning

Still a PoC. 0.0.x promises no API or structural stability.

No behaviour change. Two things that kept going wrong now have something to stop them.

pnpm release:check

A release has to move four things together — the template tag, the CLI's TEMPLATE_REF, the CLI tag, and npm. Doing it by hand missed one almost every time across 0.0.1 → 0.0.7, and a miss is silent: npx create-lesa-app hands out an older template with no error and no warning.

  ✓ 템플릿 package.json  0.0.8
  ✓ CLI TEMPLATE_REF     v0.0.8
  ✗ npm latest           0.0.5

  ✗ npm latest 가 0.0.5 인데 CLI 는 0.0.8 다 — `npm publish` 가 남았다

Prints what is left and exits 1. Reads the registry directly, since npm view's cache hid a publish that had already landed.

MIGRATION.md

create-lesa-app is a one-time scaffold, not a dependency — releasing the template does nothing for a project already generated from it. Until now there was no answer to "I generated last week, how do I get the iOS 27 fix".

generated from what it takes
v0.0.6 one dependency bump — its push path fails prebuild
v0.0.4 / v0.0.5 drop the hand-written UIScene patch and plugin, then bump and add the flag
v0.0.3 and earlier no Xcode 27 support at all — builds, never launches

It ends with a verification list that says to prebuild with firebase/ populated. That is the check whose absence let v0.0.6 ship with a broken push path.

release-check.mjs is pruned from generated apps (create-lesa-app 0.0.8) — it inspects the template/CLI/npm chain, which means nothing inside an app.


Warning

여전히 PoC 입니다. 0.0.x 는 API·구조 안정성을 약속하지 않습니다.

동작 변경은 없습니다. 계속 어긋나던 두 가지에 장치를 붙였습니다.

pnpm release:check

릴리즈는 네 군데를 같이 올려야 합니다 — 템플릿 태그 · CLI TEMPLATE_REF · CLI 태그 · npm. 손으로 하니 0.0.1~0.0.7 에서 거의 매번 하나씩 빠졌고, 빠지면 조용합니다: npx create-lesa-app 이 옛 템플릿을 에러도 경고도 없이 줍니다.

  ✓ 템플릿 package.json  0.0.8
  ✓ CLI TEMPLATE_REF     v0.0.8
  ✗ npm latest           0.0.5

  ✗ npm latest 가 0.0.5 인데 CLI 는 0.0.8 다 — `npm publish` 가 남았다

무엇이 남았는지 출력하고 exit 1 합니다. 레지스트리를 직접 조회합니다 — npm view 캐시가 이미 올라간 발행을 숨긴 적이 있습니다.

MIGRATION.md

create-lesa-app 은 의존성이 아니라 한 번 쓰는 스캐폴드입니다. 템플릿을 릴리즈해도 이미 만든 프로젝트는 따라오지 않습니다. 지금까지 "지난주에 만들었는데 iOS 27 수정을 어떻게 받나" 에 답이 없었습니다.

만든 버전 해야 할 일
v0.0.6 의존성 한 줄 — 푸시 경로가 prebuild 에서 실패한다
v0.0.4 · v0.0.5 손수 만든 UIScene 패치·플러그인을 걷고, 올린 뒤 flag 추가
v0.0.3 이하 Xcode 27 대응 자체가 없음 — 빌드는 되고 실행이 안 됨

마지막에 firebase/ 를 채운 상태로 prebuild 하라는 줄이 있습니다. v0.0.6 이 푸시 경로가 깨진 채 나간 이유가 그 검사를 안 한 것입니다.

release-check.mjs 는 생성된 앱에서 제외됩니다(create-lesa-app 0.0.8) — 템플릿·CLI·npm 체인을 보는 것이라 앱 안에서는 의미가 없습니다.

전체 변경 내역

CHANGELOG.md ·
v0.0.7...v0.0.8

v0.0.7

Choose a tag to compare

@LESANF LESANF released this 16 Sep 09:26
28ad92d

Warning

Still a PoC. 0.0.x promises no API or structural stability.

Turning push on made prebuild fail in v0.0.6. If you are on that version and use push, take this one.

What broke

enableSceneSupport in expo-build-properties@57.0.19 refuses to run once another config plugin has modified AppDelegate — and turning push on does exactly that, since RNFB inserts FirebaseApp.configure().

Error: [ios.appDelegate]: `ios.enableSceneSupport` requires the standard
  Expo SDK 57 Swift AppDelegate.

Expo's announcement asks for 57.0.20, and #50210 is that bug. The floor is now ~57.0.20.

Why v0.0.6 shipped with it

Its verification ran with push off, which is the template default — no files in firebase/. Every check passed while the push path was broken, so the first app to drop in Firebase config would have found it instead.

Verified with push on

Dummy Firebase config in firebase/ to flip pushEnabled:

57.0.19 57.0.20
prebuild fails ✓
AppDelegate — FirebaseApp.configure() and ExpoReactNativeFactoryProvider
Info.plist — EXExpoAppSceneDelegate
xcodebuild — BUILD SUCCEEDED, 0 errors, Firebase pods compiled

Warning

여전히 PoC 입니다. 0.0.x 는 API·구조 안정성을 약속하지 않습니다.

v0.0.6 에서 푸시를 켜면 prebuild 가 실패했습니다. 그 버전을 쓰고 푸시를 쓰면 이걸로 올리세요.

무엇이 깨졌나

expo-build-properties@57.0.19 의 enableSceneSupport 는 AppDelegate 를 고치는 다른 플러그인이 있으면 실행을 거부합니다 — 푸시를 켜면 RNFB 가 FirebaseApp.configure() 를 넣으므로 정확히 그 경우입니다.

Error: [ios.appDelegate]: `ios.enableSceneSupport` requires the standard
  Expo SDK 57 Swift AppDelegate.

Expo 공지가 57.0.20 을 요구하고(공지), #50210 이 그 버그입니다. 하한을 ~57.0.20 으로 올렸습니다.

v0.0.6 이 이걸 안고 나간 이유

검증을 푸시 OFF 로만 돌렸습니다 — firebase/ 에 파일이 없는 템플릿 기본 상태입니다. 그래서 푸시 경로가 깨진 채로 모든 검사가 통과했고, Firebase 설정을 처음 넣는 앱이 대신 발견할 상황이었습니다.

푸시 ON 으로 검증

firebase/ 에 더미 설정을 넣어 pushEnabled 를 켠 상태:

57.0.19 57.0.20
prebuild 실패 ✓
AppDelegate — FirebaseApp.configure() 와 ExpoReactNativeFactoryProvider 공존
Info.plist — EXExpoAppSceneDelegate
xcodebuild — BUILD SUCCEEDED, error 0, Firebase pods 컴파일

전체 변경 내역

CHANGELOG.md ·
v0.0.6...v0.0.7

v0.0.6

Choose a tag to compare

@LESANF LESANF released this 16 Sep 08:43
fa2a8c6

Warning

Still a PoC. 0.0.x promises no API or structural stability.

Expo shipped the SDK 57 scene lifecycle one day after v0.0.4 patched it in by hand. The patch and the hand-written plugin are both gone.

The local workaround is replaced by one line

expo@57.0.23 the runtime classes (#50191)
expo-build-properties@57.0.19 the ios.enableSceneSupport switch (#50205)
ios: { useFrameworks: 'static', enableSceneSupport: true },

Deleted: patches/expo@57.0.22.patch and plugins/with-ios-scene.ts.

It is opt-in on purpose. Flipping the default mid-SDK would rewrite the AppDelegate of every existing SDK 57 app, so prebuild still emits the legacy lifecycle unless the flag is set — confirmed by pulling the flag and prebuilding. The first-party version is also cleaner: the generated project points at Expo's own EXExpoAppSceneDelegate instead of a SceneDelegate class written into each app.

Remove the line at SDK 58, where it becomes the default; prebuild warns if it is still set.

Verified

prebuild     Info.plist → EXExpoAppSceneDelegate · UIWindow(frame: absent
xcodebuild   ** BUILD SUCCEEDED **  0 errors
built app    Info.plist names EXExpoAppSceneDelegate
expo-doctor  18/18 · check-all 64 tests

Remaining custom plugins stay

Checked against the SDK 57 config reference: MainActivity configChanges/resizeableActivity, a separate Android display name, release signing and $RNFirebaseDisableSPM for push-off builds have no first-party equivalent.

Also clears two lint violations app.config.ts had been carrying — a dynamic process.env[key] Metro cannot inline, and a stale require('node:fs').


Warning

여전히 PoC 입니다. 0.0.x 는 API·구조 안정성을 약속하지 않습니다.

v0.0.4 가 손으로 패치해 넣은 iOS 27 UIScene 을 하루 뒤 Expo 가 공식으로 냈습니다. 패치와 자체 플러그인을 둘 다 지웠습니다.

임시 조치가 한 줄로 바뀌었습니다

expo@57.0.23 런타임 클래스 (#50191)
expo-build-properties@57.0.19 ios.enableSceneSupport 스위치 (#50205)
ios: { useFrameworks: 'static', enableSceneSupport: true },

삭제: patches/expo@57.0.22.patch · plugins/with-ios-scene.ts.

일부러 opt-in 입니다. SDK 중간에 기본값을 바꾸면 기존 SDK 57 앱의 AppDelegate 가 전부 다시 쓰이기 때문입니다 — 스위치를 빼고 prebuild 해서 확인했습니다. 공식 쪽이 더 낫습니다: 앱마다 SceneDelegate 클래스를 써넣지 않고 Expo 의 EXExpoAppSceneDelegate 를 직접 가리킵니다.

SDK 58 에서는 이 줄을 지웁니다 — 기본이 되고, 남겨두면 prebuild 가 경고합니다.

검증

prebuild     Info.plist → EXExpoAppSceneDelegate · UIWindow(frame: 없음
xcodebuild   ** BUILD SUCCEEDED **  error 0
빌드된 앱     Info.plist 가 EXExpoAppSceneDelegate 를 가리킴
expo-doctor  18/18 · check-all 64 테스트

남은 자체 플러그인은 유지합니다

SDK 57 config 레퍼런스와 대조했습니다 — MainActivity configChanges·resizeableActivity, Android 표시명 분리, release 서명, 푸시 off 빌드의 $RNFirebaseDisableSPM 은 공식 대체가 없습니다.

app.config.ts 가 들고 있던 lint 위반 2건도 정리했습니다 — Metro 가 인라인 못 하는 process.env[key] 동적 접근과 낡은 require('node:fs').

전체 변경 내역

CHANGELOG.md ·
v0.0.5...v0.0.6

v0.0.5

Choose a tag to compare

@LESANF LESANF released this 15 Sep 04:54
688ba9a

Warning

여전히 PoC 입니다.

v0.0.4 의 iOS 27 수정에 이어, Android 앱 아이콘이 런처 마스크에 잘리던 것을 고쳤습니다.

Android adaptive 아이콘

adaptive-icon.png 이 풀블리드 아이콘을 줄인 것이었습니다. Android 런처 마스크는 108dp 중
가운데 72dp 만 보장하므로 가장자리가 잘립니다. 아이콘 파이프라인이 그 용도의 여백 있는
foreground
를 이미 내고 있어서 그 파일을 그대로 가져왔습니다 — 다시 만들지 않았습니다.

icon.png 은 손대지 않았습니다. v0.0.4 의 1024 리사이즈 결과가 파이프라인 산출물
(AppIcon-1024.png)과 바이트 동일이라 iOS 는 풀블리드가 이미 맞았습니다.

두 파일은 역할이 다릅니다 — icon.png 은 풀블리드(iOS), adaptive-icon.png 은 여백 있는
foreground(Android). 풀블리드 하나를 두 곳에 넣으면 Android 가 잘립니다. docs/config.md 에
적어 뒀습니다.

검증

Android 를 prebuild 하고 생성된 ic_launcher_foreground 를 직접 렌더해서 확인했습니다.

배너 하단 전체 폭
배너 텍스트 위치 높이의 7076% — 마스크 안전 영역(16.783.3%) 안
피사체 안전 영역 중앙

app-icon-badge 는 adaptive 를 알고 따로 처리합니다(배너 높이 180px → 310px, 텍스트를
그 안에서 위로 정렬). 원본 크기만 1024 로 맞으면 됩니다.

시작하기

npx create-lesa-app my-app

전체 변경 내역

CHANGELOG.md ·
v0.0.4...v0.0.5

v0.0.4

Choose a tag to compare

@LESANF LESANF released this 15 Sep 04:26
6617bdc

Warning

여전히 PoC 입니다. 0.0.x 는 API·구조 안정성을 약속하지 않습니다.

iOS 27 SDK 에서 앱이 실행되지 않던 것을 고쳤습니다. Xcode 27 을 쓰는 사람에게는 이게
전부입니다 — 그 전 버전은 빌드는 되지만 실행이 안 됩니다.

iOS 27 SDK — UIScene 생명주기

Apple 이 UIScene 채택을 필수로 만들었습니다. SDK 57 템플릿은 AppDelegate 가 window 를
만들기 때문에 빌드는 성공하고 실행에서 죽습니다.

Application failed to launch: UIScene life cycle is required for apps built with this SDK.

빌드가 통과한다는 게 함정입니다. v0.0.3 에서 Xcode 27 을 확인했다고 적었는데, 그때 본 건
DeviceHub 대체와 컴파일뿐이었고 둘 다 이 문제를 건드리지 않습니다.

런타임 클래스는 upstream expo/expo#50026 에서
그대로 가져왔습니다. 그게 공식 백포트 PR 이고 sdk-57 을 타깃하며 아직 열려
있습니다.
커밋 008a21874e177800f983be16f5c14315e96f33fd 기준이고 손으로 고친 것은
없습니다.

patches/expo@57.0.22.patch ExpoAppSceneDelegate · ExpoReactNativeFactoryProvider · SceneEventForwarder
plugins/with-ios-scene.ts Info.plist scene manifest · AppDelegate 에서 window 생성 제거 · SceneDelegate 선언

플러그인은 앵커를 못 찾으면 throw 합니다. 조용히 빠지면 실행에서야 드러나고, 그게 가장
찾기 어려운 자리입니다.

SDK 54 에서는 네 패키지를 패치해야 했지만 57 은 하나로 끝납니다 — SceneGeometry 가
expo-modules-core 57 에 이미 있고, @expo/cli 57.0.24 에 DeviceHub fallback 이 들어
있고(#46757), Pod 배포타깃 상향도 Expo 가
prebuild 에서 직접 합니다.

patches/README.md
에 출처와 제거 조건을 적어 뒀습니다 — #50026 이 57.0.x 에 들어오면 패치와 플러그인을
같이 지웁니다.

앱 아이콘 배지 위치

app-icon-badge 는 1024×1024 고정 오버레이를 (0,0) 에 합성합니다. 아이콘이 1254px
이라 배너가 폭의 81.7% 만 덮고 하단이 아니라 y 67~82% 에 앉아 아이콘 본체를 가로질렀습니다.

전 후
배너 폭 81.7%, y 67~82% 전체 폭, 하단
리본 모서리가 일찍 잘림 우상단 모서리에 붙음

icon.png·adaptive-icon.png 를 1024×1024 로 맞췄습니다 — Apple 이 요구하는 크기이기도
합니다. sips 로 리사이즈했고 손으로 만지지 않았습니다.

검증

prebuild     Info.plist scene manifest · AppDelegate 수정 · SceneDelegate 생성 확인
pod install  28 pods
xcodebuild   ** BUILD SUCCEEDED **  error 0  (Swift 3개 컴파일 로그 확인)
expo-doctor  18/18
check-all    lint · tsc · 64 테스트

아직 검증되지 않은 것

런타임입니다. 그리고 이번에는 그게 더 중요합니다 — scene 생명주기에서는 링크가
AppDelegate 로 오지 않습니다.
SceneEventForwarder 가 넘기므로 콜드
Linking.getInitialURL() 보존이 첫 실행에서 확인할 항목입니다. 이 템플릿의 핵심이 딥링크
큐라 다른 앱보다 영향이 큽니다.

그 외: splash → tabs 이음새 · InsetView 탭 덜컹거림 · 푸시 3경로 · iOS NSE 미포함.

호환성

Expo SDK 57 (~57.0.22, expo 패치 적용)
React Native 0.86.3
Xcode 27 확인 (26.4+ 요구)
패키지 매니저 pnpm 11.10.0

시작하기

npx create-lesa-app my-app

전체 변경 내역

CHANGELOG.md ·
v0.0.3...v0.0.4

v0.0.3

Choose a tag to compare

@LESANF LESANF released this 15 Sep 02:54
e524685

Warning

여전히 PoC 입니다. 0.0.x 는 API·구조 안정성을 약속하지 않습니다.

0.0.2 가 "빌드는 된다" 를 확인한 릴리즈였다면, 0.0.3 은 조용히 틀리는 것들을 찾아낸
릴리즈입니다. 방법을 두 가지로 바꿨습니다 — 실패해도 아무 소리가 안 나는 경로를 찾아 찔러보고,
템플릿을 읽는 대신 생성한 프로젝트에서 실제로 기능을 붙여봤습니다.

배포된 앱까지 갔을 버그

크래시 리포터가 부팅을 죽일 수 있었습니다

runStage 가 catch 블록 안에서 onStageError 를 불렀습니다. 콜백이 throw 하면 catch 를
뚫고 나가 runPreloader 가 reject 되고, splash 가 끝나지 않아 앱이 아예 뜨지 않습니다.
이 콜백들은 이미 뭔가 잘못됐을 때 불리고, onStageError 에 붙일 건 뻔히 Sentry 입니다 —
리포터가 초기화 전이면 부팅이 통째로 죽습니다. 화면도 로그도 리포트도 없는 최악의 실패입니다.

실패 기록을 콜백보다 먼저 하도록 바꿨습니다. 전에는 콜백이 throw 하면 건네주던 기록까지
사라졌습니다.

인코딩된 딥링크 파라미터가 라우팅을 깨뜨렸습니다

파서가 경로를 나눠 세그먼트별로 디코딩한 뒤, 다시 이어 붙인 문자열을 재분할했습니다.

링크 전 후
menu-4/a%2Fb 3세그먼트 → 매칭 실패 2세그먼트 → 정상
menu-4/a%20b /(tabs)/menu-4/a b — 생 공백이 router.navigate 로 .../a%20b
한글 id 디코딩 후 재인코딩 없음 왕복 정상

증상은 "푸시를 눌러도 아무 일이 없다" 입니다. 백그라운드·앱 종료 상태라 콘솔도 못 봅니다.
세그먼트 배열이 원본이고 경로는 거기서 파생됩니다.

Android release 서명이 조용히 빠질 수 있었습니다

서명 주입은 app/build.gradle 을 AGP 템플릿에 대고 정규식으로 고칩니다. 앵커가 빗나가면
두 방향으로 깨지는데 한쪽은 그대로 출시됩니다 — buildTypes 치환이 실패하면 release
빌드가 debug 키를 그대로 씁니다. signingConfigs 와 buildTypes 사이에 주석 한 줄만
끼어도 첫 앵커가 깨지는 것을 실측했고, 그게 지금 회귀 테스트입니다. 두 앵커 모두 실패 시
고쳐야 할 정규식 이름과 함께 throw 합니다.

import 경계가 가장 중요한 곳에서 사문화돼 있었습니다

flat config 는 같은 rule 키를 쓰는 블록 중 마지막 것만 적용합니다. cross-feature 블록이
같은 no-restricted-imports 를 다시 써서 providers 규칙을 src/features 전체에서 덮었습니다 —
feature 가 providers 를 import 하는 게 바로 그 규칙이 막으려던 구조 파괴인데, 거기만 비어
있었습니다. 두 severity 가 rule 키를 하나씩 나눠 갖습니다.

실무가 바로 부딪히는 구멍

auth 게이트는 AUTH_LOGIN_PATH 로 push 하고, 재생을 위해 원래 목적지를 먼저 저장합니다.
그 라우트가 없었습니다. gates: ['auth'] 를 쓰는 첫 앱 — 실무에서 당연히 쓰는 것 —
이 +not-found 에 떨어지고, 보류 의도는 이미 저장된 뒤라 빠져나갈 길도 없습니다.

템플릿은 모든 탭에 자리표시 화면을 싣는데 자기 메커니즘이 요구하는 그 하나만 없었습니다.
같은 방식으로 채웠고 계약은 하나입니다 — 성공하면 signIn(tokens). 개발용 로그인
버튼은 __DEV__ 안이라 릴리즈 빌드에 나오지 않습니다.

생성한 프로젝트에 탭·API 도메인·상세 라우트·게이트 딥링크를 붙여서 찾았습니다.
그 외에는 템플릿을 고칠 일이 없었습니다.

테스트를 넣고, 다시 줄였습니다

8 스위트 64 케이스, 네이티브 모듈 목은 0개입니다. MMKV·RNFB·notifee·reanimated 를
목으로 세우자 목을 실제 API 와 맞추는 일이 본업이 됐습니다 — v4 는 delete 가 아니라
remove 인데 이름이 어긋난 목은 테스트가 생길 때까지 아무 소리도 안 냅니다. jest-setup.ts
를 지웠고, 남은 것은 목 없이 도는 순수 로직뿐입니다. 화면·스토어 테스트는 필요한 앱이 자기
방식대로 세웁니다.

CI 가 PR 마다 check-all 을 돌립니다 — 그동안 아무것도 돌지 않았습니다.

버전은 npm 이 아니라 Expo 가 정합니다

pnpm doctor 와 npx expo install --check 가 판정 기준이고 AGENTS.md ④ 에 박았습니다.
react-native 가 npm 최신 0.87.1 이 아니라 0.86.3 인 이유는 SDK 57 의
bundledNativeModules.json 이 그렇게 고정하기 때문입니다 — 손으로 올리면 코드젠·Podspec 이
expo 모듈들과 어긋납니다.

Xcode 27 은 Simulator.app 을 DeviceHub.app 으로 대체했습니다. fallback 이 이미
@expo/cli 에 있습니다(expo/expo#46757) —
devices:// 스킴 등록과 xcrun simctl 동작을 실측했으니 문서의 딥링크·푸시 검증 명령은
그대로 유효합니다. 템플릿이 할 일은 없습니다.

검증

expo install --check Dependencies are up to date
expo-doctor 18/18
check-all lint · tsc · 64 테스트

이 계보 앞에서 확인한 것: 이 템플릿으로 만든 프로젝트가 iOS·Android 양쪽에서 설치 가능한
바이너리까지 나오고, 식별자가 네이티브 프로젝트까지 그대로 갑니다.

아직 검증되지 않은 것

런타임입니다. 앱이 뜬 뒤를 아무도 보지 않았습니다.

  • 콜드 부팅 splash → tabs 이음새
  • InsetView 가 실제로 탭 전환 덜컹거림을 없앴는지
  • 딥링크 콜드/웜 진입 · 푸시 3경로(FCM/APNs 키 필요)
  • Xcode 27 에서 pnpm ios:development 가 DeviceHub 를 여는지
  • iOS NSE(리치 푸시 이미지)는 여전히 미포함

호환성

Expo SDK 57 (~57.0.22)
React Native 0.86.3
React 19.2.3
expo-router ~57.0.21
Xcode 26.4+ (27 확인)
패키지 매니저 pnpm 11.10.0

시작하기

npx create-lesa-app my-app

전체 변경 내역

CHANGELOG.md ·
v0.0.2...v0.0.3