Flutter로 작업할 때 일반적인 크로스 플랫폼 문제 해결
게시 됨: 2022-03-10나는 Flutter를 사용한 웹 개발과 관련하여 온라인에서 많은 혼란을 보았고, 종종 슬프게도 잘못된 이유 때문입니다.
특히 사람들은 기본적으로 래퍼 앱 내에서 실행되는 브라우저 내에서 실행되는 웹 페이지에 불과했던 이전 웹 기반 모바일(및 데스크톱) 크로스 플랫폼 프레임워크와 이를 혼동하기도 합니다.
웹에서 일반적으로 액세스할 수 있는 인터페이스에만 액세스할 수 있었기 때문에 인터페이스가 동일했다는 점에서 진정한 크로스 플랫폼이었습니다.
Flutter는 그렇지 않습니다. 각 플랫폼에서 기본적으로 실행되며 Android 및 iOS에서 Java/Kotlin 또는 Objective-C/Swift로 작성된 경우 실행되는 것처럼 각 앱이 거의 실행된다는 의미입니다. 이것은 매우 다양한 플랫폼 간의 많은 차이점을 처리해야 함을 의미하기 때문에 알아야 합니다.
이 기사에서는 이러한 차이점 중 일부와 이를 극복하는 방법을 살펴보겠습니다. 더 구체적으로 말하면, 개발자가 크로스 플랫폼을 원하는 Flutter 코드를 작성할 때 가장 자주 혼동을 일으키는 스토리지 및 UI 차이점에 대해 이야기할 것입니다.
예 1: 저장
나는 최근에 모바일 앱과 비교할 때 웹 앱에 JWT를 저장하는 다른 접근 방식의 필요성에 대해 블로그에 썼습니다.
이는 플랫폼의 스토리지 옵션의 특성이 다르고 각각의 고유한 개발 도구를 알아야 하기 때문입니다.
편물
웹 앱을 작성할 때 사용할 수 있는 스토리지 옵션은 다음과 같습니다.
- 사용자 상호 작용이 필요하므로 사용자가 읽거나 생성해야 하는 파일에만 적합한 디스크로/에서 파일을 다운로드/업로드합니다.
- JS에서 액세스할 수 있거나 액세스할 수 없는 쿠키 사용(
httpOnly
인지 여부에 따라 다름), 지정된 도메인에 대한 요청과 함께 자동으로 전송되고 응답의 일부로 올 때 저장됩니다. - JS
localStorage
및sessionStorage
사용, 웹사이트의 모든 JS에서 액세스할 수 있지만 해당 웹사이트 페이지의 일부인 JS에서만 액세스할 수 있습니다.
이동하는
모바일 앱의 경우 상황은 완전히 다릅니다. 저장 옵션은 다음과 같습니다.
- 해당 앱에서 액세스할 수 있는 로컬 앱 문서 또는 캐시 저장소
- 사용자 생성/판독 가능한 파일에 대한 기타 로컬 저장 경로
- 키-값 저장을 위해 iOS 및 Android에서 각각
NSUserDefaults
및SharedPreferences
; - iOS의
Keychain
및 Android의KeyStore
는 각각 모든 데이터 및 암호화 키의 안전한 저장을 위한 것입니다.
그것을 모른다면 실제로 사용하고 있는 스토리지 솔루션과 장점과 단점이 무엇인지 알아야 하기 때문에 구현을 엉망으로 만들 것입니다.
플랫폼 간 솔루션: 초기 접근 방식
Flutter shared_preferences
패키지를 사용하면 웹에서는 localStorage
, Android에서는 SharedPreferences
, iOS에서는 NSUserDefaults
를 사용합니다. 특히 세션 토큰과 같은 민감한 정보를 저장하는 경우 앱에 완전히 다른 의미가 있습니다. localStorage
는 클라이언트에서 읽을 수 있으므로 XSS에 취약한 경우 문제가 됩니다. 모바일 앱이 XSS에 실제로 취약하지는 않지만 SharedPreferences
및 NSUserDefaults
는 안전한 저장소가 아니며 암호화되지 않기 때문에 클라이언트 측에서 손상될 수 있으므로 안전한 저장소 방법이 아닙니다. 이는 iOS의 경우 여기에서 언급한 것처럼, 그리고 저장하기 전에 데이터를 암호화하기 위해 SharedPreferences
에 래퍼를 제공하도록 설계된 보안 라이브러리에 대해 이야기할 때 Android 문서에서 언급한 것처럼 사용자 환경 설정을 위한 것이기 때문입니다.
모바일의 보안 스토리지
모바일의 유일한 보안 저장소 솔루션은 iOS 및 Android의 Keychain
및 KeyStore
뿐인 반면 웹에는 보안 저장소가 없습니다 .
Keychain
과 KeyStore
는 본질적으로 매우 다릅니다. Keychain
은 일반 자격 증명 저장 솔루션인 반면 KeyStore
는 대칭 키 또는 공개/개인 키인 암호화 키 를 저장(및 생성할 수 있음)하는 데 사용됩니다.
즉, 예를 들어 세션 토큰을 저장해야 하는 경우 iOS에서는 OS가 암호화 부분을 관리하도록 하고 토큰을 Keychain
으로 보낼 수 있지만 Android에서는 다음이 필요하기 때문에 수동 경험이 조금 더 많습니다. 하드 코드가 아닌 키를 생성 하려면 이를 사용하여 토큰을 암호화하고 암호화된 토큰을 SharedPreferences
에 저장하고 키를 KeyStore
에 저장합니다.
보안 분야와 마찬가지로 이에 대한 다양한 접근 방식이 있지만 가장 간단한 방법은 대칭 암호화를 사용하는 것입니다. 앱에서 토큰을 암호화하고 해독하기 때문에 공개 키 암호화가 필요하지 않기 때문입니다.
예를 들어, 모든 작업을 수행하는 Flutter 플러그인이 있기 때문에 이 모든 작업을 수행하는 모바일 플랫폼별 코드를 작성할 필요가 없습니다.
웹상의 보안 스토리지 부족
사실 이 글을 쓰게 된 동기이기도 하다. 나는 그 패키지를 사용하여 모바일 앱에 JWT를 저장하는 것에 대해 썼고 사람들은 그 웹 버전 을 원했지만 내가 말했듯 이 웹에는 안전한 저장소가 없습니다 . 존재하지 않습니다.
그것은 당신의 JWT가 공개되어야 한다는 것을 의미합니까?
아니, 전혀. httpOnly
쿠키를 사용할 수 있습니까? JS에서 액세스할 수 없으며 서버로만 전송됩니다. 문제는 사용자 중 한 명이 다른 사람의 웹사이트에서 GET 요청 URL을 클릭하고 해당 GET 요청에 귀하 또는 귀하의 사용자가 원하지 않는 부작용이 있는 경우에도 항상 귀하의 서버로 전송된다는 것입니다. 이것은 실제로 다른 요청 유형에서도 작동하지만 더 복잡합니다. 이를 Cross-Site Request Forgery라고 하며 원하지 않습니다. Mozilla의 MDN 문서에 언급된 웹 보안 위협 중 하나입니다. 여기에서 더 완전한 설명을 찾을 수 있습니다.
예방 방법이 있습니다. 가장 일반적인 것은 실제로 두 개의 토큰을 갖는 것입니다. 하나는 httpOnly
쿠키로 클라이언트에 전달되고 다른 하나는 응답의 일부로 전달됩니다. 후자는 서버에 자동으로 전송되는 것을 원하지 않기 때문에 쿠키가 아닌 localStorage
에 저장해야 합니다.
둘 다 해결하기
모바일 앱과 웹 앱이 모두 있다면 어떻게 될까요?
다음 두 가지 방법 중 하나로 처리할 수 있습니다.
- 동일한 백엔드 엔드포인트를 사용하지만 쿠키 관련 HTTP 헤더를 사용하여 쿠키를 수동으로 가져오고 보냅니다.
- 웹 앱에서 사용하는 토큰 과 다른 토큰을 생성하는 별도의 비 웹 백엔드 끝점을 만든 다음 클라이언트가 모바일 전용 토큰을 제공할 수 있는 경우 일반 JWT 인증을 허용합니다.
다른 플랫폼에서 다른 코드 실행하기
이제 차이점을 보완할 수 있도록 다른 플랫폼에서 다른 코드를 실행하는 방법을 살펴보겠습니다.
Flutter 플러그인 만들기
특히 스토리지 문제를 해결하기 위해 플러그인 패키지를 사용하는 것이 한 가지 방법입니다. 플러그인은 공통 Dart 인터페이스를 제공하고 기본 플랫폼별 Kotlin/Java 또는 Swift/Objective-C 코드를 비롯한 다양한 플랫폼에서 다양한 코드를 실행할 수 있습니다. . 패키지와 플러그인을 개발하는 것은 다소 복잡하지만 공식 Flutter 문서를 포함하여 웹과 다른 곳(예: Flutter 책)의 여러 곳에 설명되어 있습니다.
예를 들어 모바일 플랫폼의 경우 이미 보안 저장소 플러그인이 있으며 그것은 여기에서 사용 예를 찾을 수 있는 flutter_secure_storage
입니다. 하지만 예를 들어 웹에서는 작동하지 않습니다.
반면에 웹에서도 작동하는 간단한 키-값 저장소의 경우, shared_preferences
SharedPreferences
크로스 플랫폼 Google 개발 자사 플러그인 패키지가 있습니다. 이 패키지에는 NSUserDefaults
, shared_preferences_web
또는 localStorage
플랫폼에 따라.
Flutter의 TargetPlatform
package:flutter/foundation.dart
를 가져온 후 Theme.of(context).platform
을 값과 비교할 수 있습니다.
-
TargetPlatform.android
-
TargetPlatform.iOS
-
TargetPlatform.linux
-
TargetPlatform.windows
-
TargetPlatform.macOS
-
TargetPlatform.fuchsia
지원하려는 각 플랫폼에 대해 적절한 작업을 수행하도록 기능을 작성하십시오. 이것은 플랫폼 차이의 다음 예에 특히 유용할 것이며, 이는 위젯이 다른 플랫폼에 표시되는 방식의 차이입니다.
특히 이 사용 사례의 경우 플랫폼 인식 위젯의 개발을 단순화하는 상당히 인기 있는 flutter_platform_widgets
플러그인도 있습니다.
예 2: 동일한 위젯이 표시되는 방식의 차이점
Android 및 iOS 앱이 WebView가 되고 데스크톱 앱이 Electron으로 구축되기를 원하지 않는 한 크로스 플랫폼 코드를 작성하고 브라우저, 전화, 컴퓨터 및 스마트워치가 같은 것으로 가장할 수 없습니다. . 그렇게 하지 않을 이유는 많습니다. 이 글의 요점은 Flutter와 같은 프레임워크를 사용하여 앱을 기본 상태로 유지하고 그에 따른 모든 성능 및 사용자 경험 이점을 제공하는 동시에 대부분의 경우 모든 플랫폼에서 동일할 코드를 작성하십시오.
하지만 이를 위해서는 주의와 주의가 필요하며 최소한 지원하려는 플랫폼, 실제 네이티브 API 및 그 모든 것에 대한 기본 지식이 필요합니다. React Native 사용자는 프레임워크가 내장 OS 위젯을 사용하기 때문에 더 많은 주의를 기울여야 합니다. 따라서 실제로 전환할 수 없는 두 플랫폼에서 광범위하게 테스트하여 앱이 어떻게 보이는지 더 주의를 기울여야 합니다. Flutter에서 가능한 것처럼 즉석에서 iOS 및 Material 위젯.
귀하의 요청 없이 변경되는 사항
플랫폼을 전환할 때 자동으로 변경되는 앱 UI의 일부 측면이 있습니다. 이 섹션에서는 이와 관련하여 Flutter와 React Native 간의 변경 사항도 언급합니다.
Android와 iOS 사이(Flutter)
Flutter는 iOS(및 Android의 Cupertino(iOS 유사) 위젯)에서 Material 위젯을 렌더링할 수 있지만, 하지 않는 것은 Android와 iOS에서 정확히 동일한 것을 표시하는 것입니다. Material 테마는 특히 각 플랫폼의 규칙에 맞게 조정됩니다. .
예를 들어 탐색 애니메이션과 전환, 기본 글꼴은 다르지만 앱에 큰 영향을 미치지는 않습니다.
미학이나 UX와 관련하여 일부 선택에 영향을 줄 수 있는 것은 일부 정적 요소도 변경된다는 사실입니다. 특히, 일부 아이콘은 두 플랫폼 간에 변경됩니다. 앱 바 제목은 iOS의 경우 가운데에 있고 Android의 경우 왼쪽에 있습니다(뒤로 버튼이나 서랍을 여는 버튼이 있는 경우 사용 가능한 공간 왼쪽에 있습니다(여기에 설명됨) 머티리얼 디자인 가이드라인 및 햄버거 메뉴라고도 함) Android에서 서랍이 있는 머티리얼 앱은 다음과 같습니다.
iOS에서 동일한 매우 간단한 Material 앱이 어떻게 생겼습니까?
모바일과 웹 사이 및 화면 노치 포함(Flutter)
웹에는 Flutter를 사용한 반응형 웹 개발에 대한 이 Smashing 기사에서도 언급했듯이 약간 다른 상황이 있습니다. — 이 기사의 주요 초점 — 때때로 위젯이 브라우저 창 외부에 배치된다는 사실에 대해 걱정해야 합니다. 또한 일부 휴대전화에는 화면 상단에 노치가 있거나 일종의 장애물로 인해 앱을 올바르게 보는 데 방해가 되는 기타 장애물이 있습니다.
이 두 가지 문제는 위젯을 SafeArea
위젯으로 래핑하여 피할 수 있습니다. 이 위젯은 위젯이 사용자의 기능을 방해하지 않고 실제로 표시될 수 있는 위치에 있도록 하는 특정 종류의 패딩 위젯입니다. 하드웨어 또는 소프트웨어 제약이 있습니다.
리액트 네이티브에서
React Native는 두 플랫폼 모두에서 앱을 테스트할 수 있으려면 최소한 iOS 시뮬레이터와 Android 에뮬레이터를 실행해야 하는 것 외에도 각 플랫폼에 대해 훨씬 더 많은 관심과 더 깊은 지식이 필요합니다. 동일하며 JavaScript UI 요소를 플랫폼별 위젯으로 변환합니다. 다시 말해서, React Native 앱은 항상 iOS처럼 보일 것입니다(때때로 Cupertino UI 요소라고도 함). 그리고 Android 앱은 플랫폼의 위젯을 사용하기 때문에 항상 일반 Material Design Android 앱처럼 보일 것입니다.
여기서 차이점은 Flutter가 자체 저수준 렌더링 엔진으로 위젯을 렌더링한다는 것입니다. 즉, 하나의 플랫폼에서 두 앱 버전을 모두 테스트할 수 있습니다.
문제 해결
매우 구체적인 것을 목표로 하지 않는 한 앱은 다른 플랫폼에서 다르게 보일 것입니다. 그렇지 않으면 일부 사용자가 불만을 가질 것입니다.
단순히 모바일 앱을 웹으로 보내면 안 되는 것처럼(앞서 언급한 Smashing 게시물에서 썼듯이) Cupertino 위젯으로 가득 찬 앱을 Android 사용자에게 보내서는 안 됩니다. 대부분. 반면에 다른 플랫폼을 위한 위젯이 있는 앱을 실제로 실행할 기회가 있으면 두 개의 장치를 반드시 사용할 필요 없이 앱을 테스트하고 두 버전의 사람들에게 표시할 수 있습니다.
다른 쪽: 올바른 이유로 잘못된 위젯 사용
그러나 이것은 또한 iOS 사용자의 경험을 희생하지 않고 Linux 또는 Windows 워크스테이션에서 Flutter 개발의 대부분을 수행할 수 있음을 의미하며, 다른 플랫폼용 앱을 빌드하기만 하면 철저하게 테스트하는 것에 대해 걱정할 필요가 없습니다.
다음 단계
크로스 플랫폼 프레임워크는 훌륭하지만 각 플랫폼이 작동하는 방식과 앱이 사용자에게 적합하고 쾌적하게 사용되도록 하는 방법을 이해하는 책임을 개발자에게 전가합니다. 고려해야 할 다른 작은 사항은 예를 들어 다른 플랫폼에 다른 규칙이 있는 경우 본질적으로 동일한 것일 수 있는 것에 대해 다른 설명을 사용하는 것일 수 있습니다.
서로 다른 언어를 사용하여 두 개(또는 그 이상) 앱을 별도로 빌드할 필요가 없다는 것은 좋은 일이지만, 본질적으로 하나 이상의 앱을 빌드하고 있으며 빌드 중인 각 앱에 대해 생각해야 한다는 점을 명심해야 합니다. .
추가 리소스
- Flutter Gallery 웹사이트 및 Android 앱, 다양한 플랫폼의 일반적인 Flutter 위젯 사용과 플랫폼 불가지론을 보여줍니다.
- TargetPlatform의 Flutter API 문서
- 패키지 및 플러그인 생성에 대한 Flutter 문서
- 플랫폼 적응에 대한 Flutter 문서
- 쿠키에 대한 MDN 문서