1. Android 권한

Android에서는 주로 3가지의 보호 수준을 분류합니다

  • normal
  • dangerous
  • signature 각 권한은 필요한 권한 수준에 맞게 개발하는 과정에서 설정할 수 있습니다.

Normal

기본적으로 설치하는 과정에서 자동으로 부여되는 권한입니다. 사용자에게 확인을 받을 필요가 없다는 점이 특징이네요

  • 권한 예시
    • INTERENT
    • VIBRATE
    • ACCESS_NETWORK_STATE
    • ACCESS_WIFI_STATE
    • WAKE_LOCK

Dangerous

사전에 사용자 동의를 받는 권한 체계로 거부시 해당 기능 사용이 불가능 합니다. 따라서 민감한 정보 혹은 만감은 권한을 필요로 하는 권한의 경우 해당 분류 체계에 속합니다.

  • 권한 예시
    • CAMERA
    • LOCATION
    • READ_CONTACTS
    • READ_EXTERNAL_STORAGE
    • RECORD_AUDIO

Android 6.0 이후 핵심 변화

  • 설치 시 → 실행 중 요청으로 변경
  • 사용자 선택권 강화

Signature

같은 서명의 앱만 사용가능한 권한 분류로 OS 레벨 또는 기업용 앱에서 사용

  • 권한 예시
    • BIND_DEVICE_ADMIN
    • INSTALL_PACKAGES
    • MANAGE_USERS

1-2. Android 버전별 권한 변화 흐름

점차 시간이 지날수록 권한이 사용자 통제 중심으로 바뀌고 있습니다.

1-3.안드로이드 권한 처리 실제 구조

1. 권한 선언

<uses-permission android:name="android.permission.CAMERA"/>

2. 권한 확인

if (ContextCompat.checkSelfPermission(
        this,
        Manifest.permission.CAMERA
    ) == PackageManager.PERMISSION_GRANTED
) {
    // 이미 허용됨 → 바로 사용
}

3. 권한 요청

ActivityCompat.requestPermissions(
    this,
    arrayOf(Manifest.permission.CAMERA),
    100
)

4. 권한 결과 처리

override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<String>,
    grantResults: IntArray
) {
    if (requestCode == 100) {
        if (grantResults.isNotEmpty() &&
            grantResults[0] == PackageManager.PERMISSION_GRANTED
        ) {
            // 허용됨
        } else {
            // 거부됨
        }
    }
}

2. IOS 권한

  • IOS에서는 info.plist에 사용 목적 문자열을 반드시 명시해야 한다.
info.plist key설명

3. Android / iOS 권한 요청 흐름

3-1. Android

3-2. iOS

3-3. 권한 상태 값 - canAskAgain

Granted

  • 허용됨.
  • 기능 즉시 사용 가능

Denied

  • 거부됨.
  • canAskAgain로 분기필요

Undetermined

  • 아직 요청 전.
  • 요청 시 허용 가능 (iOS)

Restricted

  • 시스템/보호자 정책으로 차단.
  • 요청 불가

Limited

  • 일부 허용.
  • 선택된 사진만 접근

4. 실제 권한 요청 코드

카메라, 갤러리 권한 요청

Import * as ImagePicker from 'expo-image-picker';
 
const pickImage = async () => {
	const {status } = await
image Picker.requestMediaLibraryPermissionAsync();
 
	if (status !== 'granted') { Alert.alert('권한 필요'); return; }
	
	const result = await ImagePicker.launchImageLibraryAsync({
		mediaTypes: ImagePicker.MediaTypeOptions.Images, quality: 0.8 });
	
	if(!result.canceled) setImage(result.assets[0].uri);
}

권한 거부 시 설정 앱 유도

iOS Pre-permission Dialog 패턴

5. Expo Modules vs 순수 Native Module의 차이점

Expo Modules

  • Expo Go에서 바로 테스트 가능
  • expo-camera, expo-image-picker 등
  • JS API만으로 네이티브 가능
  • 네이티브 빌드 없이 빠른 프로토타이핑 가능
  • 커스텀 네이티브 코드 제한적

Native Module

  • Swift / Kotlin 직접 작성
  • React Native Bridge로 JS 연결
  • Expo Go 불가 네이티브 빌드 필요
  • 완전한 네이티브 API 접근 가능
  • 빌드 시간, 설정 복잡성 증가

6. 푸시 알림 아키텍처

FCM

  • firebase에서 사용하는 클라우드 메시징

APM

  • Apple Push Notification Service

7. Expo Push Token vs Native Token

Expo Push Token

  • getPermissionAsync()
  • getExpoPushTokenAsync()

Native FCM / APNs Token

8. 앱 상태별 알림 처리

상태에 따라 알림 수신 동작이 달라집니다.

Foreground

addNotificationReceivedListener
  • 앱이 화면에 보이는 상태
  • 시스템 알림 배너 미표시
  • 앱 내에서 직접 UI 처리 필요

setNotificationHandler

앱 루트에서 핸들러를 명시적으로 설정해야 합니다

Background

addNotificationResponseReceivedListener
  • 앱이 메모리에 있지만 비활성
  • 시스템 알림 배너 자동 표시
  • 탭 시 앱 복귀 + 이벤트 전달

Killed

addNotificationReceivedListener
  • 앱 프로세스 완전 종료
  • 시스템 알림 배너 자동 표시
  • 탭 시 앱 실행 + 초기 알림 확인

Listener Cleanup

로컬 알림 발송

await Notiications.scheduleNotificationAsync({
	content: {
		title: '게시물 알림',
		body:'someeone님이 게시물을 좋아합니다',
		data: {postId: 'abc123'}
	},
	trigger: null
})

Doze Mode

Android의 배터리 최적화와 두 가지 알림

  • 화면 OFF + 충전기 미연결 + 일정 시간 경과 Doze 진입
  • 네트워크 , wake Lock, Alarm Manager 제한
  • normal 우선순위 알림은 Doze 해제까지 지연
  • high 우선순위 메시지만 즉시 전달 가능

로컬 알림

  • 앱 내부에서 스케줄링
  • 서버 불필요, 오프라인 가능

원격 알림

  • 서버에서 FCM / APNs로 발송
  • 네트워크 필수, 실시간 전달

알림 페이로드, 채널

FCM 페이로드 구조

Notification 객체

  • 운영체제가 자동으로 알림 배너 표시
  • title, body, image 등 포함
  • Foreground에서 onMessage 핸들러
  • Backenground에서 자동 표시 후 탭 이벤트 전달

data 객체

  • 운영체제 알림 표시하지 않음
  • key-value 커스텀 데이터
  • 앱 코드에서 직접 처리 필요
  • 백그라운드 작업 트리거에 활용

TTL, 메시지 우선순위, 전송 실패

Notification Channel, Rich Notification

URGENT

소리 + 화면 팝업

HIGH

소리 알림

DEFAULT

소리 알림, 상태바 표시

LOW

소리 없음, 상태바만

MIN

소리, 진동 없음, Shade에만

import * as Notifications from 'expo-notifications';
 
Notifications.setNotificationChannelAsync(
	'social',
	{
		name: '소셜 알림',
		importance: Notifications.AndroidImportance.HIGH,
		sound: 'default,
		vibrationPattern: [0, 250, 250, 250],
	}
);

Firebase와 푸시 토큰 서버 관리

Deep Link 보안

  1. URL Scheme 화이트리스트 검증
  2. 인증 상태 확인
  3. 파라미터 타입 검증
  4. 민감한 화면 직접 이동 차단