반응형

Google Cloud 사이트 접속

https://console.cloud.google.com/

 

1번 클릭하여 프로젝트 선택

 

 

2번 IAM 및 관리자 클릭

 

 

3번 서비스 계정 클릭4번 서비스 계정 클릭

 

 

4번 서비스 계정 클릭

 

 

5번 키 클릭6번 키 추가 / 새 키 만들기 클릭

 

 

6번 키 추가 / 새 키 만들기 클릭

 

 

7번과 같이 키 생성 되고 ‘service-account.json’ 파일이 다운로드 됨.

주의사항 : 해당 파일은 다시 다운로드가 불가능하다.

반응형
블로그 이미지

SKY STORY

,
반응형

두가지 모두 비동기 프로그래밍을 위해 사용되는 기술로, 멀티 스레딩 및 동시성 처리를 할때 사용된다.

두 기술에 대한 비교는 다음과 같다.

 

  1. 비동기 작업 처리:
    • 두 기술 모두 비동기적으로 작업을 수행할 수 있다. 이를 통해 메인 스레드가 블로킹되지 않으며, 사용자 인터페이스가 멈추지 않게 할 수 있다.
  2. 멀티 스레딩 지원:
    • GCD는 작업을 비동기적으로 다양한 큐에서 실행하고, Swift Concurrency는 태스크를 사용하여 비동기 코드를 스케줄링한다.
    • 둘 다 멀티코어 CPU의 성능을 활용해 병렬 처리가 가능하다.
  3. 스레드 관리 자동화:
    • GCD는 스레드 풀을 관리하여 시스템 리소스를 효율적으로 사용한다.
    • Swift Concurrency도 내부적으로 Task Scheduler를 사용하여 스레드 관리와 태스크 스케줄링을 자동으로 최적화한다.
  4. QoS (Quality of Service):
    • 두 기술 모두 작업의 우선 순위를 지정할 수 있습니다.
      • GCD에서는 DispatchQoS를 사용해 작업 우선 순위를 설정한다.
      • Swift Concurrency에서는 Task의 우선 순위를 조정할 수 있다 (Task(priority: .high) 등).
  5. 캔슬 가능한 작업:
    • GCD에서 작업을 수동으로 취소할 수 있는 것은 아니지만, Swift Concurrency에서는 Task를 취소할 수 있다. 다만, GCD에서도 DispatchWorkItem을 사용하여 작업을 취소할 수 있는 형태로 구현할 수 있다.
    • Swift Concurrency에서는 Task.checkCancellation() 등을 사용해 비동기 태스크 내에서 취소 상태를 확인할 수 있다.

 

요약

공통점 GCD (Grand Central Dispatch) Swift Concurrency (Async/Await)
비동기 작업 처리 DispatchQueue.async {} async/await, Task
멀티 스레딩 지원 다양한 DispatchQueue Task 스케줄러
스레드 관리 자동화 자동으로 스레드 풀 관리 Task 스케줄러로 자동 최적화
우선 순위 제어 DispatchQoS Task(priority:)
작업 취소 지원 DispatchWorkItem (부분적으로 지원) Task.cancel(), Task.checkCancellation()

 

특징 GCD (Grand Central Dispatch) Swift Concurrency (Async/Await)
개념 스레드 풀을 사용해 작업을 비동기적으로 처리하기 위한 API. Swift 5.5에서 도입된 구조화된 비동기 프로그래밍 모델.
도입 시기 iOS 4 iOS 15, macOS 12
사용 방식 큐를 사용해 작업을 비동기적으로 추가하고, 콜백을 사용해 결과 처리. async/await 키워드를 사용해 코드 흐름을 동기적으로 작성.
코드 가독성 콜백 지옥 (Callback Hell) 발생 가능 코드 흐름이 동기 코드와 유사하여 가독성이 높음
성능 최적화 스레드 풀 관리, 우선순위 조정 등 수동으로 최적화 가능 시스템이 자동으로 최적화 (더 나은 스케줄링)
에러 처리 콜백 내에서 수동으로 에러 처리 do-catch 블록을 통한 구조화된 에러 처리
캔슬레이션 지원 수동으로 작업 취소 관리 Task 객체를 통한 내장된 캔슬레이션 지원
메모리 관리 클로저 캡처에 주의해야 하며, weak self를 사용해 메모리 누수 방지 async/await는 메모리 안전성을 자동으로 보장
주요 사용 사례 레거시 코드, iOS 14 이하 호환성 최신 iOS 프로젝트, 비동기 코드 리팩토링

 

GCD (Grand Central Dispatch) 샘플 코드

import UIKit

func fetchDataWithGCD() {
    let url = URL(string: "https://www.apple.com")!
    
    // 네트워크 요청을 백그라운드에서 수행하고, 메인 스레드에서 UI 업데이트를 수행
    DispatchQueue.global(qos: .userInitiated).async {
        // 클로저 내부에서 self를 캡처하기 때문에 메모리 누수 방지를 위해 [weak self]를 사용해야 한다.
        guard let data = try? Data(contentsOf: url) else { return }
        DispatchQueue.main.async {
            print("Data fetched: \(data)")
        }
    }
}

fetchDataWithGCD()

 

Swift Concurrency (Async/Await) 샘플 코드

import Foundation

func fetchData() async {
    let url = URL(string: "https://www.apple.com")!
    
    do {
        // async/await를 사용해 네트워크 요청을 수행하고 결과를 처리 (비동기 방식)
        let (data, _) = try await URLSession.shared.data(from: url)
        print("Data fetched: \(data)")
    } catch {
        print("Error fetching data: \(error)")
    }
}

// Task를 생성하여 비동기 함수 호출
Task {
    await fetchData()
}

 

iOS 15 이상을 타겟으로 하는 프로젝트에서는 Swift Concurrency (async/await)를 사용하는 것이 코드 가독성, 자동 메모리 관리, 에러 처리 등에서 GCD에 비해 이점이 있다.

 

반응형
블로그 이미지

SKY STORY

,
반응형

GitHub에서 포크한 저장소를 삭제하는 방법은 다음과 같다:

  1. GitHub에 로그인: GitHub 계정으로 로그인한다.
  2. 저장소 페이지로 이동: 삭제하려는 포크된 저장소 페이지로 이동한다.
  3. Settings (설정) 메뉴로 이동:
    • 저장소의 상단 메뉴에서 Settings 탭을 클릭한다.
  4. 저장소 삭제 옵션으로 이동:
    • Settings 페이지의 하단으로 스크롤하여 Danger Zone 섹션에서 Delete this repository 버튼을 클릭한다.
  5. 확인 및 삭제:
    • 나타나는 팝업 창에 저장소 이름을 정확히 입력한 후, 삭제 확인을 위해 다시 한 번 I understand the consequences, delete this repository 버튼을 클릭한다.

이 과정을 완료하면 포크한 저장소가 삭제된다. 

만약 포크한 저장소가 너무 많아서 일일이 삭제하기 어려운 경우, GitHub CLI를 사용하면 쉽게 일괄 삭제할 수 있다.

  1. GitHub CLI 설치: GitHub CLI가 설치되어 있지 않다면 GitHubCLI설치가이드에 따라 설치한다.
  2. GitHub 로그인:명령어를 입력하고, 지시에 따라 GitHub 계정으로 로그인한다.
    gh auth login
  3. 포크된 저장소 리스트 확인: 모든 포크된 저장소를 확인하려면 다음 명령어를 사용한다.
    gh repo list YOUR_USERNAME --fork --json name
  4. 포크된 저장소 일괄 삭제: 특정 조건을 만족하는 포크만 삭제하려면, 스크립트를 이용해 필터링하여 삭제할 수 있다. 아래는 모든 포크된 저장소를 삭제하는 예제이다.  주의: 이 스크립트는 본인 계정의 모든 포크된 저장소를 반복적으로 삭제한다.
    gh repo list YOUR_USERNAME --fork --json name -q '.[].name' | while read repo; do gh repo delete YOUR_USERNAME/$repo --confirm done

 

다른 방법으로 GitHub 개발자 설정에서 토큰을 발급 받은 후 아래와 같이 python 코드를 사용하여 삭제도 가능하다.

import requests
import time

# GitHub 개인 액세스 토큰과 사용자 이름
TOKEN = 'github_pat_11ABHO ... Q5L5R6ly7vhP'
USERNAME = 'YOUR USERNAME'

# GitHub API URL 설정
API_URL = f'https://api.github.com/users/{USERNAME}/repos'

# 요청 헤더 설정
headers = {
    'Authorization': f'token {TOKEN}'
}

# 저장소 목록 가져오기
response = requests.get(API_URL, headers=headers)

# 응답 상태 코드 확인
if response.status_code != 200:
    print(f"API 요청 실패: {response.status_code}, {response.text}")
else:
    repos = response.json()

    # 포크된 저장소만 필터링하여 삭제
    for repo in repos:
        if isinstance(repo, dict) and repo.get('fork') and repo.get('owner', {}).get('login') == USERNAME:
            repo_name = repo['name']
            delete_url = f'https://api.github.com/repos/{USERNAME}/{repo_name}'
            delete_response = requests.delete(delete_url, headers=headers)

            if delete_response.status_code == 204:
                print(f'{repo_name} 삭제 완료')
            else:
                print(f'{repo_name} 삭제 실패: {delete_response.status_code}, {delete_response.text}')
            
    print("모든 포크된 저장소 삭제 작업이 완료되었습니다.")

위 코드 실행시 약 25개정도 삭제될때마다 API 요청이 거절된다. 이유는 GitHub API는 짧은 시간에 많은 요청이 들어오면 요청을 제한하거나 차단한다. 요청시 일정 시간 딜레이를 주거나 여러번 실행하여 해결할 수 있다.

 

 

반응형
블로그 이미지

SKY STORY

,
반응형

.gitignore에 my_ignore_folder/ 폴더를 추가했음에도 Git이 계속 해당 폴더의 변경사항을 추적하는 이유는, 이미 Git이 이전에 해당 폴더를 트래킹하고 있기 때문이다. .gitignore 파일을 설정한 후에는 Git에 해당 폴더를 무시하도록 아래와 같이 추가 조치를 취해보자.

  1. 추적된 my_ignore_folder/ 폴더의 캐시 삭제
    .gitignore에 추가된 폴더가 이미 Git에서 추적 중인 상태라면 캐시를 삭제해야 한다. 
     
    git rm -r --cached my_ignore_folder/
  2. 변경 사항 커밋
    캐시에서 삭제된 내용을 커밋하여 기록에 반영한다.
     
    git commit -m "Remove my_ignore_folder folder from tracking"
  3. 푸시
    원격 저장소에 반영하려면 다음과 같이 푸시한다.
  4. git push

이 작업을 마친 후, my_ignore_folder/ 폴더는 .gitignore 설정에 따라 무시되어, 이후 변경 사항이 발생해도 Git이 추적하지 않게 된다.

만약 git rm -r --cached my_ignore_folder/ 명령을 실행했음에도 불구하고 my_ignore_folder/ 폴더가 여전히 Git에서 추적되고 있다면, 다음 사항들을 점검하고 추가 조치를 취해보자.

  1. .gitignore 파일이 제대로 설정되었는지 확인
    • .gitignore 파일에서 my_ignore_folder/ 경로가 올바르게 작성되었는지 다시 한번 확인한다.
    • .gitignore 파일이 Git의 루트 디렉토리에 위치해 있는지 확인한다. 프로젝트의 루트 디렉토리가 아니라면 무시되지 않을 수 있다.
  2. .gitignore 파일에 추가적인 경로 지정
    • .gitignore에 my_ignore_folder/ 외에 my_ignore_folder/*과 같은 패턴을 추가해 보세요. 간혹 Git이 하위 파일을 인식하는 경우가 있기 때문에, 아래와 같이 설정할 수도 있습니다:
       
      my_ignore_folder/
      my_ignore_folder/*
  3. git status로 확인
    • 터미널에서 git status 명령을 실행하여 my_ignore_folder/가 추적되고 있는지 확인해보자.
  4. 폴더 강제 삭제 후 커밋
    • 위 방법이 모두 실패한 경우, 강제로 my_ignore_folder/ 폴더를 무시하도록 처리한다.
      git rm -rf --cached my_ignore_folder
      git commit -m "Force ignore my_ignore_folder folder"
      git push
    • 이 명령어는 캐시에서 삭제를 강제하여 .gitignore 파일이 무시되도록 한다.
  5. 로컬 및 원격에 남아 있는지 확인
    • 최종적으로 SourceTree나 다른 Git 클라이언트를 사용하여 로컬과 원격 브랜치 모두에 my_ignore_folder/폴더가 여전히 남아 있는지 확인한다.

 

반응형
블로그 이미지

SKY STORY

,
반응형

아래 오류 메시지는 git push 과정중 네트워크나 git 서버 쪽에서 발생한 500 오류로 인해 연결이 끊어진 상황을 나타낸다. 이 문제는 주로 네트워크 상태나 원격 서버의 일시적인 오류로 인해 발생할 수 있지만, 몇 가지 설정을 조정해보면 해결할 수 있는 경우도 있다.

git --no-optional-locks -c color.branch=false -c color.diff=false -c color.status=false -c diff.mnemonicprefix=false -c core.quotepath=false -c credential.helper=sourcetree push -v --tags origin refs/heads/main:refs/heads/main 
Pushing to https://bitbucket.org/netcanis_workspace/gptapi.git
POST git-receive-pack (chunked)
error: RPC failed; HTTP 500 curl 22 The requested URL returned error: 500
send-pack: unexpected disconnect while reading sideband packet
fatal: the remote end hung up unexpectedly
Everything up-to-date
Completed with errors, see above

 

1. 캐시된 자격 증명 삭제 및 다시 로그인

git credential-osxkeychain erase

 

2. 푸시 버퍼 크기 증가

아래 설정은 500MB로 늘려주는 것으로, 서버와의 데이터 전송 오류를 해결할 수 있다. (대부분 이 문제이다.)

git config --global http.postBuffer 524288000

 

3. Bitbucket 서버 상태 확인

유지보수 작업등으로 인해 요류가 발생할 수 있다. (서버 상태를 확인해 본다.)

 

4. HTTP/2 사용 비활성화

일부 환경에서는 HTTP/2 프로토콜이 문제를 일으킬 수 있다. 아래 명령어를 통해 HTTP/2를 비활성화해 보자.

git config --global http.version HTTP/1.1

 

5. 다른 네트워크 사용

네트워크 연결 문제로 인해 푸시가 차단될 수 있다. 다른 네트워크(예: 핫스팟)로 전환 후 다시 푸시를 시도해 보자.

 

 

반응형

'개발 > Note' 카테고리의 다른 글

GitHub에서 포크한 저장소를 삭제  (0) 2024.11.06
특정 폴더 git 추적 삭제  (0) 2024.10.30
git 마지막 commit 취소, 메시지 수정  (0) 2024.10.16
BLE, Beacon, iBeacon  (0) 2024.01.11
BLE Advertising Payload format 샘플 분석  (1) 2024.01.11
블로그 이미지

SKY STORY

,
반응형
 

알림 메시지, 데이터 메시지 차이점을 알아보자.


구분 알림 메시지 (Notification Message) 데이터 메시지 (Data Message)
목적 시스템이 자동으로 알림 표시 앱이 직접 알림을 처리해야 함
앱 상태 백그라운드/완전 종료에서도 알림 표시 가능 포그라운드 상태에서만 앱이 직접 처리 가능
구성 제목, 본문, 이미지 등 알림 내용 포함 데이터 키-값 쌍으로 구성
사용 예 간단한 알림 전송에 유용 앱에서 특정 작업 수행에 유용
배너 알림 표시 자동으로 베너 알림 표시 앱이 포그라운드일 때 코드로 구현 가능
예시 코드 { "notification": { "title": "알림 제목", "body": "알림 내용" } } { "data": { "key1": "value1", "key2": "value2" } }

 

요약 

알림 메시지 : 시스템이 자동으로 베너 형태의 알림을 관리해주며, 앱의 상태와 무관하게 표시된다.

데이터 메시지 : 앱이 실행 중일 때만 앱 내 코드로 처리할 수 있다. 베너 알림을 표시하려면 직접 구현해야 한다.

 

반응형
블로그 이미지

SKY STORY

,
반응형
 

<application>  <activity> 태그의 속성과 하위 요소, 적용 가능 API, 필수여부 등을 정리해 보았다.

 

<application> 태그 속성

앱의 전체적인 설정을 제어하며, 여러 속성  하위 요소를 통해 앱의 동작, 권한, 리소스 등을 정의할  있다.

속성 적용 가능 API 버전
(Android 버전)
필수 여부 설명
android:icon API 1 (Android 1.0) 필수 앱의 기본 아이콘을 설정합니다.
android:label API 1 (Android 1.0) 선택 앱의 기본 이름(라벨)을 설정합니다.
android:theme API 1 (Android 1.0) 선택 애플리케이션의 기본 테마를 설정합니다.
android:permission API 1 (Android 1.0) 선택 앱에서 선언된 모든 컴포넌트에 적용되는 권한을 정의합니다.
android:debuggable API 1 (Android 1.0) 선택 디버깅 모드를 활성화할지 여부를 지정합니다.
android:enabled API 1 (Android 1.0) 선택 애플리케이션이 활성화 상태인지 여부를 지정합니다.
android:hasCode API 1 (Android 1.0) 선택 애플리케이션이 자체적으로 컴파일된 코드(DEX 파일)를 포함하는지 여부를 지정합니다.
android:persistent API 1 (Android 1.0) 선택 시스템 메모리에서 앱이 항상 실행 중인지 여부를 정의합니다.
android:taskAffinity API 1 (Android 1.0) 선택 애플리케이션의 태스크 친화도를 정의합니다.
android:process API 1 (Android 1.0) 선택 앱이 실행될 프로세스를 지정합니다.
android:allowTaskReparenting API 1 (Android 1.0) 선택 태스크 변경 시 액티비티가 재배치될 수 있는지 여부를 지정합니다.
android:killAfterRestore API 1 (Android 1.0) 선택 백업 후 애플리케이션을 종료할지 여부를 설정합니다.
android:restoreAnyVersion API 1 (Android 1.0) 선택 앱이 다른 버전의 백업 데이터를 복원할 수 있는지 여부를 설정합니다.
android:resizeableActivity API 1 (Android 1.0) 선택 애플리케이션의 액티비티가 크기 조정 가능한지 여부를 지정합니다.
android:usesCleartextTraffic API 1 (Android 1.0) 선택 앱에서 암호화되지 않은 트래픽을 허용할지 여부를 정의합니다.
android:installLocation API 8 (Android 2.2) 선택 앱이 설치될 위치를 지정합니다. (auto, internalOnly, preferExternal)
android:backupAgent API 8 (Android 2.2) 선택 백업 및 복원을 담당하는 백업 에이전트를 정의합니다.
android:allowBackup API 8 (Android 2.2) 선택 앱 데이터의 백업을 허용할지 여부를 정의합니다.
android:hardwareAccelerated API 11 (Android 3.0) 선택 애플리케이션에서 하드웨어 가속을 사용할지 여부를 설정합니다.
android:largeHeap API 11 (Android 3.0) 선택 애플리케이션이 더 큰 힙 메모리를 사용할 수 있는지 여부를 지정합니다.
android:vmSafeMode API 11 (Android 3.0) 선택 앱이 VM에서 안전 모드로 실행될지 여부를 지정합니다.
android:isGame API 19 (Android 4.4) 선택 앱이 게임인지 여부를 정의합니다.
android:banner API 20 (Android 4.4W) 선택 Android TV에서 사용할 배너 이미지를 지정합니다.
android:restrictedAccountType API 22 (Android 5.1) 선택 제한된 계정의 유형을 설정합니다.
android:fullBackupContent API 23 (Android 6.0) 선택 백업 내용과 제외 항목을 정의하는 XML 파일을 지정합니다.
android:roundIcon API 25 (Android 7.1) 선택 원형 아이콘을 지정합니다.
android:category API 26 (Android 8.0) 선택 애플리케이션의 카테고리를 정의합니다. (game, productivity, social 등)
android:appComponentFactory API 28 (Android 9.0) 선택 애플리케이션의 컴포넌트 생성을 처리하는 클래스를 지정합니다.
android:profileable API 29 (Android 10.0) 선택 애플리케이션이 성능 프로파일링이 가능한지 여부를 정의합니다.
android:dataExtractionRules API 31 (Android 12.0) 선택 앱의 데이터 추출 규칙을 정의하는 XML 파일 경로를 지정합니다.

<application> 태그 하위 요소

하위요소 적용 가능 API 버전
(Android 버전)
필수 여부 설명
<activity> API 1 (Android 1.0) 필수 앱의 액티비티를 선언합니다.
<service> API 1 (Android 1.0) 선택 백그라운드 작업을 수행하는 서비스 컴포넌트를 선언합니다.
<receiver> API 1 (Android 1.0) 선택 브로드캐스트 수신기를 선언합니다.
<provider> API 1 (Android 1.0) 선택 앱에서 데이터를 공유할 수 있는 콘텐츠 제공자를 선언합니다.
<uses-library> API 1 (Android 1.0) 선택 앱에서 사용할 추가 라이브러리를 선언합니다.
<meta-data> API 1 (Android 1.0) 선택 추가적인 메타데이터 정보를 설정합니다.
<activity-alias> API 1 (Android 1.0) 선택 기존 액티비티의 별칭을 정의하여 다른 이름으로 사용할 수 있도록 합니다.
<permission> API 1 (Android 1.0) 선택 앱에서 정의하는 권한을 선언합니다.
<permission-group> API 1 (Android 1.0) 선택 권한을 그룹화하여 선언합니다.
<permission-tree> API 1 (Android 1.0) 선택 권한의 계층 구조를 선언합니다.
<uses-permission> API 1 (Android 1.0) 선택 앱이 필요로 하는 권한을 정의합니다. 특정 기능을 사용하려면 권한 요청이 필요할 때 추가합니다.
<profileable> API 29 (Android 10.0) 선택 앱의 성능을 프로파일링할 수 있는지 여부를 정의합니다.

 


 

<activity> 태그 속성

액티비티의 동작과 속성을 정의한다. 앱의 개별 화면을 구성하며, 앱의 주요 동작 방식을 정의할 있다.

속성 적용 가능 API 버전
(Android 버전)
필수 여부 설명
android:name API 1 (Android 1.0) 필수 액티비티의 클래스 이름을 정의합니다.
android:label API 1 (Android 1.0) 선택 액티비티의 이름을 정의합니다.
android:theme API 1 (Android 1.0) 선택 액티비티에서 사용할 테마를 설정합니다.
android:icon API 1 (Android 1.0) 선택 액티비티에 사용할 아이콘을 설정합니다.
android:permission API 1 (Android 1.0) 선택 액티비티에 접근하기 위해 필요한 권한을 설정합니다.
android:configChanges API 1 (Android 1.0) 선택 액티비티가 처리할 구성 변경 사항을 설정합니다.
android:launchMode API 1 (Android 1.0) 선택 액티비티의 실행 모드를 지정합니다.
android:taskAffinity API 1 (Android 1.0) 선택 태스크의 친화도를 지정합니다.
android:windowSoftInputMode API 3 (Android 1.5) 선택 소프트 입력 모드의 동작을 설정합니다. (adjustResize, adjustPan 등)
android:hardwareAccelerated API 11 (Android 3.0) 선택 하드웨어 가속을 사용할지 여부를 설정합니다.
android:clearTaskOnLaunch API 1 (Android 1.0) 선택 태스크가 시작될 때 다른 액티비티를 모두 종료할지 여부를 설정합니다.
android:alwaysRetainTaskState API 1 (Android 1.0) 선택 태스크의 상태를 항상 유지할지 여부를 설정합니다.
android:autoRemoveFromRecents API 21 (Android 5.0) 선택 액티비티가 종료되면 최근 작업 목록에서 자동으로 제거될지 여부를 설정합니다.
android:allowEmbedded API 30 (Android 11.0) 선택 액티비티가 다른 액티비티 내에 임베드될 수 있는지 여부를 설정합니다.
android:exported API 31 (Android 12.0) 필수 외부에서 액티비티를 호출할 수 있는지 여부를 정의합니다.

<activity> 태그 하위 요소

하위요소 적용 가능 API 버전
(Android 버전)
필수 여부 설명
<intent-filter> API 1 (Android 1.0) 선택 액티비티가 처리할 수 있는 인텐트를 정의합니다.
<meta-data> API 1 (Android 1.0) 선택 액티비티에 대한 추가 메타데이터를 제공하여 앱 실행 시 필요한 정보를 설정할 수 있습니다.
<layout> API 1 (Android 1.0) 선택 액티비티가 사용할 레이아웃을 정의합니다. 보통 XML 파일로 참조됩니다.

 

<intent-filter> 태그 하위 요소

하위요소 적용 가능 API 버전
(Android 버전)
필수 여부 설명
<action> API 1 (Android 1.0) 필수 인텐트 필터에서 처리할 수 있는 인텐트 액션을 정의합니다. 예: android.intent.action.MAIN
<category> API 1 (Android 1.0) 선택 인텐트 필터에서 처리할 수 있는 인텐트 카테고리를 정의합니다. 예: android.intent.category.LAUNCHER
<data> API 1 (Android 1.0) 선택 인텐트 필터가 처리할 수 있는 데이터(URI, MIME 타입 등)를 정의합니다.

 

 

반응형
블로그 이미지

SKY STORY

,
반응형

Toast가 여러 줄로 출력되지 않는 경우는 디바이스의 화면 크기텍스트의 길이 때문이다. 그래서 여러 줄을 출력하기위해 TextView를 사용하는 MultiLine Toast를 만들어 보았다.

 

1. res/layout/multiline_toast.xml 파일 생성:

<!-- res/layout/multiline_toast.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@android:color/black"
    android:padding="10dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/multiline_toast_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/white"
        android:textSize="16sp"/>
</LinearLayout>

 

2. showMultiLineToast 함수 작성:

private fun showMultiLineToast(message: String) {
    // 1. LayoutInflater를 사용해 multiline_toast 레이아웃을 불러옴
    val inflater = layoutInflater
    val layout = inflater.inflate(R.layout.multiline_toast, findViewById(R.id.multiline_toast_text))

    // 2. multiline_toast 레이아웃의 TextView에 메시지를 설정
    val textView: TextView = layout.findViewById(R.id.multiline_toast_text)
    textView.text = message

    // 3. Toast에 multi-line layout을 설정
    with(Toast(applicationContext)) {
        duration = Toast.LENGTH_LONG
        view = layout
        show()
    }
}

 

3. 사용 예시:

val message = """
    BLE 스캔 성공:
    deviceName: ${result.deviceName}
    RSSI: ${result.rssi}
    data: ${result.data}
""".trimIndent()

showMultiLineToast(message)  // Multi-line Toast로 출력

 

 

반응형
블로그 이미지

SKY STORY

,

NFC 권한설정

개발/Android 2024. 10. 21. 11:50
반응형

NFC 태그 사용을 위한 권한설정에 대해 알아보겠다.

NFC 태그가 앱에 의해 감지될 때 android.nfc.action.TAG_DISCOVERED는 NFC 태그가 감지될 때 인텐트를 전달하는 데 필요하다. 이를 위해 AndroidManifest.xml에 설정을 추가해야 한다. 이 설정은 TAG_DISCOVERED, TECH_DISCOVERED, NDEF_DISCOVERED와 같은 액션을 통해 NFC 태그 감지 시 앱으로 인텐트를 전달하는 역할을 한다.

 

권한 설명

  1. android.nfc.action.NDEF_DISCOVERED:
    NDEF 메시지를 포함한 NFC 태그가 감지되었을 때 작동하는 액션이다. 주로 특정 애플리케이션 데이터 또는 MIME 타입이 있는 태그에서 사용된다.
  2. android.nfc.action.TAG_DISCOVERED:
    NFC 태그가 감지되었을 때 작동하는 기본 액션으로, NDEF 메시지가 포함되지 않은 모든 NFC 태그를 처리할 수 있다.
  3. android.nfc.action.TECH_DISCOVERED:
    태그가 특정 기술(예: NfcA, MifareUltralight 등)을 지원할 때 작동한다. 이를 통해 특정 기술 스택을 가진 태그를 처리할 수 있다. 예를 들어 nfc_tech_filter.xml에서 정의된 기술과 일치하는 태그가 감지되었을 때 인텐트를 처리한다.

추가 설명

  • MIME 타입은 NDEF 태그의 데이터를 처리할 때 필수입니다. 예를 들어, "text/plain"은 특정 애플리케이션 데이터 타입을 지정한 것입니다.
  • NDEF_DISCOVERED는 NDEF 메시지가 포함된 태그만 처리할 수 있으므로, 태그의 내용이 특정 유형(MIME 타입)일 때만 작동하게끔 설정됩니다.
  • TECH_DISCOVERED는 기술 스택을 기반으로 태그를 처리하며, nfc_tech_filter.xml에서 정의한 기술과 일치하는 태그가 감지되면 작동합니다.

AndroidManifest.xml 에 추가된 설정 예 :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mwkg.testwebview">


    <!-- NFC 권한 설정 -->
    <uses-permission android:name="android.permission.NFC"/>

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.TestWebView">

        <!-- FCM 푸시 알림 서비스 등록 -->
        <service
            android:name=".MyFirebaseMessagingService"
            android:exported="true"
            android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>

        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.TestWebView">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <!-- NFC NDEF 태그 인텐트 필터 -->
            <intent-filter>
                <!-- NDEF 메시지를 포함한 NFC 태그가 감지될 때 앱으로 인텐트를 전달 -->
                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />

                <!-- NDEF 메시지에서 처리할 MIME 유형 지정 (예: 특정 애플리케이션 또는 텍스트 MIME 유형) -->
                <data android:mimeType="text/plain" />  <!-- ex: "text/plain" 또는 "application/vnd.*" -->
            </intent-filter>

            <!-- NFC 태그 인텐트 필터 -->
            <intent-filter>
                <!-- 모든 NFC 태그가 감지될 때 앱으로 인텐트를 전달 (NDEF 메시지가 없을 때도 작동) -->
                <action android:name="android.nfc.action.TAG_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>

            <!-- NFC 기술 인텐트 필터 -->
            <intent-filter>
                <!-- NFC 태그에 기술 스택이 감지되면 앱으로 인텐트를 전달 (특정 기술 유형을 지원하는 태그를 처리) -->
                <action android:name="android.nfc.action.TECH_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>

            <!-- NFC 기술 필터 설정 (nfc_tech_filter.xml 파일을 참조하여 특정 기술을 처리) -->
            <meta-data
                android:name="android.nfc.action.TECH_DISCOVERED"
                android:resource="@xml/nfc_tech_filter" />
        </activity>
    </application>

</manifest>

 

nfc_tech_filter.xml 사용 예 :

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <!-- Ndef 태그 (NFC Data Exchange Format) -->
    <tech-list>
        <tech>android.nfc.tech.Ndef</tech>
    </tech-list>

    <!-- NdefFormatable 태그 (NFC 태그를 NDEF 형식으로 포맷할 수 있는 경우) -->
    <tech-list>
        <tech>android.nfc.tech.NdefFormatable</tech>
    </tech-list>

    <!-- NfcA 태그 (ISO 14443-3A) -->
    <tech-list>
        <tech>android.nfc.tech.NfcA</tech>
    </tech-list>

    <!-- NfcB 태그 (ISO 14443-3B) -->
    <tech-list>
        <tech>android.nfc.tech.NfcB</tech>
    </tech-list>

    <!-- NfcF 태그 (Felica, JIS 6319-4) -->
    <tech-list>
        <tech>android.nfc.tech.NfcF</tech>
    </tech-list>

    <!-- NfcV 태그 (ISO 15693) -->
    <tech-list>
        <tech>android.nfc.tech.NfcV</tech>
    </tech-list>

    <!-- IsoDep 태그 (ISO 14443-4) -->
    <tech-list>
        <tech>android.nfc.tech.IsoDep</tech>
    </tech-list>

    <!-- MifareClassic 태그 -->
    <tech-list>
        <tech>android.nfc.tech.MifareClassic</tech>
    </tech-list>

    <!-- MifareUltralight 태그 -->
    <tech-list>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>



    <!-- 기술 조합이 필요한 경우 -->
    <!-- NfcA와 Ndef, MifareUltralight를 동시에 지원하는 태그 -->
    <tech-list>
        <tech>android.nfc.tech.NfcA</tech>
        <tech>android.nfc.tech.Ndef</tech>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
</resources>

 

 

반응형
블로그 이미지

SKY STORY

,
반응형

NdefFormatable은 NFC 태그를 NDEF 형식으로 포맷할 수 있을 때 사용하는 인터페이스이다. 만약 NFC 태그가 NDEF 형식으로 포맷되지 않은 경우, NDEF 포맷팅 작업을 수행할 수 있다. 이 기능은 주로 NFC 태그가 기본적으로 NDEF 메시지를 지원하지 않는 경우에 사용된다. NDEF 포맷팅은 태그에 데이터를 기록할 수 있는 형태로 변환하는 과정을 말한다.

NdefFormatable 사용 시점:

  • NFC 태그가 아직 NDEF 포맷으로 되어 있지 않은 경우 NdefFormatable을 사용하여 태그를 포맷하고 NDEF 메시지를 기록할 수 있다.
  • 한 번만 포맷할 수 있는 태그, 비어 있는 상태에서만 포맷할 수 있는 태그에 유용하다.

일반적으로 NFC 태그를 처음 사용하거나 비어 있는 상태에서 NDEF 메시지를 기록하려는 경우 NdefFormatable은 포맷할 수 있는 태그에 대해 connect(), format()  writeNdefMessage()와 같은 작업을 제공한다.

다음은 NdefFormatable을 사용하여 NFC 태그를 NDEF 형식으로 포맷하고 데이터를 기록하는 예이다.

fun formatTagWithNdefMessage(tag: Tag, ndefMessage: NdefMessage): Boolean {
    val ndefFormatable = NdefFormatable.get(tag)

    if (ndefFormatable != null) {
        try {
            // NDEF 포맷팅을 위해 태그 연결
            ndefFormatable.connect()

            // NDEF 메시지로 태그 포맷 및 데이터 기록
            ndefFormatable.format(ndefMessage)

            Log.d("NFC", "태그를 NDEF 형식으로 포맷하고 메시지를 기록했습니다.")
            return true
        } catch (e: IOException) {
            Log.e("NFC", "태그 포맷팅 실패: ${e.message}")
        } finally {
            try {
                ndefFormatable.close()
            } catch (e: IOException) {
                Log.e("NFC", "태그 닫기 실패: ${e.message}")
            }
        }
    } else {
        Log.e("NFC", "이 태그는 NDEF 포맷이 지원되지 않습니다.")
    }

    return false
}
 

 

주요 메서드 설명:

  • NdefFormatable.get(tag): Tag 객체에서 NdefFormatable 인스턴스를 얻는다. NDEF 포맷 미지원 시 null을 반환 한다.
  • connect(): NFC 태그와 연결을 시도한다.
  • format(NdefMessage): 태그를 NDEF 형식으로 포맷하고, 제공된 NDEF 메시지를 기록한다.
  • close(): 태그와의 연결 종료한다.

주의사항:

  • NDEF 형식으로 포맷하면 원래 상태로 되돌릴 수 없다.
  • 일부 태그는 포맷 후에도 더 이상 기록할 수 없는 경우가 있다. (읽기 전용 태그로 변환).
  • 모든 태그가 NDEF 포맷을 지원하지 않는다.

 

반응형

'개발 > Android' 카테고리의 다른 글

멀티라인 Toast 메시지 출력  (0) 2024.10.24
NFC 권한설정  (1) 2024.10.21
NFC Tag 상세정보 출력  (2) 2024.10.18
안드로이드 각 버전별 추가된 주요 기능  (0) 2024.10.15
JIT, AOT 컴파일 비교  (1) 2024.10.15
블로그 이미지

SKY STORY

,
반응형

NFC Tag에서 얻을 수 있는 정보를 json 문자열 포맷으로 반환하는 함수를 만들어보았다.

테그 정보 :

  1. UID (고유 ID): tag.id에서 추출한 고유 식별자.
  2. Tech List: 태그가 지원하는 모든 기술 목록 (tag.techList).
  3. NfcA (ISO 14443-3A): ATQA, SAK, 최대 전송 길이, 타임아웃 정보.
  4. IsoDep (ISO 14443-4): 역사적 바이트(historicalBytes), 고층 응답(hiLayerResponse), 최대 전송 길이, 타임아웃 정보.
  5. NfcB (ISO 14443-3B): 응용 데이터, 프로토콜 정보, 최대 전송 길이.
  6. NfcF (Felica): 제조사 정보, 시스템 코드, 최대 전송 길이, 타임아웃.
  7. NfcV (ISO 15693): 응답 플래그, DSFID, 최대 전송 길이.
  8. MifareClassic: 타입, 크기, 섹터 블록 .
  9. MifareUltralight: 타입 정보 (Ultralight, Ultralight C).

 

구현 함수는 다음과 같다.

fun readTagDetailsAsJson(tag: Tag): String {
    val tagInfo = mutableMapOf<String, Any>()

    // UID 정보 (태그 고유 ID)
    tagInfo["uid"] = tag.id.joinToString("") { String.format("%02X", it) }

    // 기술 스택 나열 (태그가 지원하는 모든 기술들)
    tagInfo["techList"] = tag.techList.joinToString()

    // NfcA (ISO 14443-3A) 정보
    val nfcA = NfcA.get(tag)
    if (nfcA != null) {
        tagInfo["NfcA"] = mapOf(
            // ATQA: Answer to Request Type A. 태그가 리더기에 응답할 때 사용하는 정보
            "atqa" to nfcA.atqa.joinToString("") { String.format("%02X", it) },
            // SAK: Select Acknowledge. 태그가 리더기에 제공하는 지원 기능 정보
            "sak" to String.format("%02X", nfcA.sak),
            // 한 번에 보낼 수 있는 최대 전송 길이 (바이트 단위)
            "maxTransceiveLength" to nfcA.maxTransceiveLength,
            // 통신 타임아웃 시간 (밀리초 단위)
            "timeout" to nfcA.timeout
        )
    }

    // IsoDep (ISO 14443-4) 정보
    val isoDep = IsoDep.get(tag)
    if (isoDep != null) {
        tagInfo["IsoDep"] = mapOf(
            // Historical Bytes: 태그가 초기 통신 설정 시 리더기에 제공하는 추가 정보
            "historicalBytes" to (isoDep.historicalBytes?.joinToString("") { String.format("%02X", it) } ?: "N/A"),
            // Higher Layer Response: 고급 프로토콜에 대한 응답 정보
            "hiLayerResponse" to (isoDep.hiLayerResponse?.joinToString("") { String.format("%02X", it) } ?: "N/A"),
            // 한 번에 전송할 수 있는 최대 데이터 길이 (바이트 단위)
            "maxTransceiveLength" to (isoDep.maxTransceiveLength.toString() ?: "N/A"),
            // 통신 타임아웃 시간 (밀리초 단위)
            "timeout" to isoDep.timeout
        )
    }

    // NfcB (ISO 14443-3B) 정보
    val nfcB = NfcB.get(tag)
    if (nfcB != null) {
        tagInfo["NfcB"] = mapOf(
            // Application Data: 태그의 애플리케이션과 관련된 데이터
            "applicationData" to (nfcB.applicationData?.joinToString("") { String.format("%02X", it) } ?: "N/A"),
            // Protocol Info: 태그가 제공하는 프로토콜 정보
            "protocolInfo" to (nfcB.protocolInfo?.joinToString("") { String.format("%02X", it) } ?: "N/A")
        )
    }

    // NfcF (Felica, JIS 6319-4) 정보
    val nfcF = NfcF.get(tag)
    if (nfcF != null) {
        tagInfo["NfcF"] = mapOf(
            // 제조사 코드
            "manufacturer" to (nfcF.manufacturer?.joinToString("") { String.format("%02X", it) } ?: "N/A"),
            // 시스템 코드
            "systemCode" to (nfcF.systemCode?.joinToString("") { String.format("%02X", it) } ?: "N/A"),
            // 한 번에 전송할 수 있는 최대 데이터 길이 (바이트 단위)
            "maxTransceiveLength" to nfcF.maxTransceiveLength.toString(),
            // 통신 타임아웃 시간 (밀리초 단위)
            "timeout" to nfcF.timeout.toString()
        )
    }

    // NfcV (ISO 15693) 정보
    val nfcV = NfcV.get(tag)
    if (nfcV != null) {
        tagInfo["NfcV"] = mapOf(
            // 응답 플래그
            "responseFlags" to String.format("%02X", nfcV.responseFlags),
            // DSFID (Data Storage Format Identifier)
            "dsfId" to String.format("%02X", nfcV.dsfId),
            // 한 번에 전송할 수 있는 최대 데이터 길이 (바이트 단위)
            "maxTransceiveLength" to nfcV.maxTransceiveLength
        )
    }

    // NfcBarcode (Type V or JIS 6319-4) 정보 - maxTransceiveLength 지원 없음
    val nfcBarcode = NfcBarcode.get(tag)
    if (nfcBarcode != null) {
        tagInfo["NfcBarcode"] = mapOf(
            // 바코드 타입 정보 (Type V or JIS 6319-4)
            "type" to nfcBarcode.type
        )
    }

    // MifareClassic 정보
    val mifareClassic = MifareClassic.get(tag)
    if (mifareClassic != null) {
        tagInfo["MifareClassic"] = mapOf(
            // MifareClassic 타입 (Classic, Plus, Pro)
            "type" to when (mifareClassic.type) {
                MifareClassic.TYPE_CLASSIC -> "Classic"
                MifareClassic.TYPE_PLUS -> "Plus"
                MifareClassic.TYPE_PRO -> "Pro"
                else -> "Unknown"
            },
            // MifareClassic 메모리 크기
            "size" to mifareClassic.size,
            // 섹터 개수
            "sectorCount" to mifareClassic.sectorCount,
            // 블록 개수
            "blockCount" to mifareClassic.blockCount
        )
    }

    // MifareUltralight 정보
    val mifareUltralight = MifareUltralight.get(tag)
    if (mifareUltralight != null) {
        tagInfo["MifareUltralight"] = mapOf(
            // MifareUltralight 타입 (Ultralight, Ultralight C)
            "type" to when (mifareUltralight.type) {
                MifareUltralight.TYPE_ULTRALIGHT -> "Ultralight"
                MifareUltralight.TYPE_ULTRALIGHT_C -> "Ultralight C"
                else -> "Unknown"
            }
        )
    }

    // JSON 형태로 변환하여 반환
    return JSONObject(tagInfo as Map<*, *>).toString()
}

 

결과값 로그 출력 값은 아래와 같다.

로그 출력 값 예 :
{
  "uid": "641B27A2B67881",  // 시리얼 번호
  "techList": "android.nfc.tech.IsoDep, android.nfc.tech.NfcA", // 태그에서 지원하는 NFC 기술 목록
  "NfcA": {		// NfcA (ISO/IEC 14443-3A) 기술에 대한 정보
    "atqa": "4800",		// Answer to Request, Type A (ATQA) 값
    "sak": "20",		// Select Acknowledge (SAK) 값
    "maxTransceiveLength": 253,	// NFC 통신에서 한 번에 전송할 수 있는 최대 데이터 길이(바이트)
    "timeout": 618		// 통신 타임아웃 시간(밀리초)
  },
  "IsoDep": {	// IsoDep (ISO/IEC 14443-4) 기술에 대한 정보
    "historicalBytes": "",	// NFC 태그가 최초로 통신을 시작할 때 제공하는 부가적인 정보를 포함
    "hiLayerResponse": "N\/A",	// 태그가 고급 프로토콜을 지원하는 경우 제공
    "maxTransceiveLength": "65279",// IsoDep에서 한 번에 전송할 수 있는 최대 데이터 길이
    "timeout": 618		// 통신 타임아웃 시간(밀리초)
  }
}

 

 

반응형

'개발 > Android' 카테고리의 다른 글

NFC 권한설정  (1) 2024.10.21
NdefFormatable 태그  (0) 2024.10.21
안드로이드 각 버전별 추가된 주요 기능  (0) 2024.10.15
JIT, AOT 컴파일 비교  (1) 2024.10.15
FCM 푸시설정  (0) 2024.10.14
블로그 이미지

SKY STORY

,
반응형

마지막 커밋을 취소하고 변경 사항 유지

git reset --soft HEAD~1

 

마지막 커밋을 취소하고 변경 사항을 스테이징 취소

git reset --mixed HEAD~1

 

커밋과 함께 변경 사항도 모두 취소하고 되돌리기

git reset --hard HEAD~1

 

특정 커밋을 취소하려면

git revert <커밋 해시>

Ex) git revert d6f7e92b1c8f9fcd0e5e0b8ad72f2d7ecb839eba

 

마지막 커밋 메시지 수정

git commit --amend

 

반응형
블로그 이미지

SKY STORY

,
반응형

Android 5.0 (API 21 - Lollipop)

  1. Material Design 도입: 새로운 디자인 언어로, 더 직관적이고 일관된 UI 제공.
    • Elevation: 뷰에 그림자와 깊이를 추가하여 더 입체적인 UI 구현.
    • Ripple Effect: 버튼 등에서 터치 반응 효과 추가.
  2. RecyclerView: ListView보다 효율적인 데이터 표시를 위한 컴포넌트.
    • 더 나은 성능과 애니메이션 지원.
    • ViewHolder 패턴을 강제하여 성능 최적화.
  3. JobScheduler API: 작업을 백그라운드에서 실행하는 API로 배터리 효율성을 높임.
    • 네트워크 상태, 충전 중 여부 등을 기반으로 작업 실행 제어 가능.
  4. **ART(Android Runtime)**로 전환: JIT(Just-In-Time) 컴파일 대신 AOT(Ahead-Of-Time) 컴파일을 도입하여 앱 성능 향상.
  5. Notifications: 알림의 스타일을 개선하고, 사용자가 바로 상호작용할 수 있는 Heads-up 알림 추가.

Android 5.1 (API 22 - Lollipop)

  1. HD Voice: 고화질 음성 통화 지원.
  2. Dual-SIM Support: 두 개의 SIM 카드를 사용하는 기능 지원.
  3. Device Protection: 기기가 초기화되었을 때도 Google 계정을 통해 잠금을 유지할 수 있는 기능.
  4. Wi-Fi Calling: Wi-Fi를 이용해 통화할 수 있는 기능 도입.

Android 6.0 (API 23 - Marshmallow)

  1. App Permissions: 런타임 권한 시스템 도입, 사용자가 앱 실행 중 개별적으로 권한을 승인할 수 있음.
  2. Doze Mode: 배터리 절약을 위해 기기가 오랫동안 사용되지 않으면 백그라운드 활동을 제한.
  3. Fingerprint API: 지문 인식 기능을 이용해 앱에서 인증할 수 있는 기능 제공.
  4. App Links: HTTP 링크를 앱과 직접 연결하여 앱 내에서 URL을 처리 가능.
  5. Adoptable Storage: SD 카드나 USB 저장소를 내부 저장소로 포맷하여 앱 데이터를 저장할 수 있도록 지원.

Android 7.0 (API 24 - Nougat)

  1. Multi-Window Support: 화면을 분할하여 두 개의 앱을 동시에 실행할 수 있는 기능 도입.
    • Split-Screen(화면 분할) 및 Picture-in-Picture(PiP) 모드 지원.
  2. Direct Reply Notifications: 알림에서 바로 메시지나 답장을 할 수 있는 기능 제공.
  3. Doze on the Go: 기기가 이동 중일 때도 배터리 절약 기능을 활성화.
  4. Vulkan API: 고성능 3D 그래픽 렌더링을 위한 API 도입.
  5. Java 8 Support: 새로운 언어 기능을 포함한 Java 8의 일부 기능 지원.

Android 7.1 (API 25 - Nougat)

  1. App Shortcuts: 홈 화면에서 앱 아이콘을 길게 눌러 자주 사용하는 기능으로 바로 이동할 수 있는 기능.
  2. Image Keyboard: 키보드를 통해 직접 이미지를 전송할 수 있는 기능 도입.
  3. Circular App Icons: 아이콘을 원형으로 표시할 수 있는 기능 추가.

Android 8.0 (API 26 - Oreo)

  1. Adaptive Icons: 아이콘의 모양이 런처에 따라 동적으로 변할 수 있는 기능. 여러 모양(정사각형, 원형, 물방울 등)을 지원.
    • 아이콘을 전경(foreground)과 배경(background)으로 분리하여 다이내믹한 효과를 줄 수 있음.
  2. Notification Channels: 알림을 카테고리별로 그룹화하고, 사용자에게 더 세밀한 알림 제어권 제공.
    • 사용자가 각 채널별로 알림의 중요도, 사운드 등을 설정할 수 있음.
  3. Picture-in-Picture (PiP): 사용자가 비디오를 작은 창으로 띄워 다른 앱을 사용할 수 있는 기능.
  4. Background Execution Limits: 앱의 백그라운드 활동에 제한을 두어 배터리 절약.
  5. Autofill Framework: 사용자 정보를 자동으로 입력할 수 있는 기능.

Android 8.1 (API 27 - Oreo)

  1. Neural Networks API: 머신 러닝을 위한 하드웨어 가속 API 제공.
  2. Autofill Enhancements: 자동 완성 기능이 향상되어 더 많은 앱에서 지원 가능.
  3. Shared Memory API: 프로세스 간 공유 메모리를 안전하게 사용할 수 있는 기능.

Android 9 (API 28 - Pie)

  1. Adaptive Battery: 앱의 사용 패턴을 학습하여 자주 사용하는 앱에 우선순위를 부여해 배터리 효율을 향상.
  2. App Actions: 사용자의 행동을 예측하고 적절한 기능을 앱에서 바로 실행할 수 있는 기능 제공.
  3. Slices API: 앱의 주요 기능을 검색 결과나 Google Assistant에서 사용할 수 있게 함.
  4. Gesture Navigation: 홈 버튼 대신 제스처를 통해 내비게이션하는 새로운 방식 도입.
  5. Background App Privacy Restrictions: 백그라운드 앱의 카메라, 마이크, 센서 접근을 제한하여 보안 강화.

Android 10 (API 29 - Q)

  1. Dark Theme: 시스템 전반에서 다크 모드 제공.
  2. Gesture Navigation: 더 직관적이고 개선된 제스처 내비게이션 도입.
  3. Privacy Enhancements: 위치 권한을 앱이 포그라운드일 때만 사용하도록 설정 가능.
  4. Scoped Storage: 각 앱이 자신의 영역에만 파일을 저장할 수 있도록 제한.
  5. Foldable Device Support: 폴더블 기기를 지원하기 위한 새로운 화면 전환 및 상태 관리 API.

Android 11 (API 30)

  1. Bubbles API: 메시지 앱에서 대화형 버블을 통해 멀티태스킹 가능.
  2. One-time Permissions: 위치, 카메라, 마이크에 대한 1회성 권한 부여 가능.
  3. Scoped Storage 개선: 앱의 저장소 접근 방식을 개선하여 데이터 보안 강화.
  4. Conversations in Notifications: 대화형 알림이 더 직관적이고 우선적으로 처리되도록 개선.

Android 12 (API 31)

  1. Material You: 사용자 맞춤형 색상 테마를 설정할 수 있는 기능으로 UI 개인화 가능.
  2. Privacy Dashboard: 사용자가 앱이 접근한 위치, 카메라, 마이크 정보에 대해 더 명확하게 볼 수 있는 대시보드 제공.
  3. Approximate Location: 앱이 대략적인 위치 정보만 요청할 수 있도록 지원.
  4. Mic/Camera Indicators: 상태 표시줄에 마이크와 카메라 사용 여부를 표시하는 기능 추가.
  5. Haptic Feedback Enhancements: 진동 패턴을 더 세밀하게 제어할 수 있는 기능 제공.

Android 12L (API 32)

  1. Large Screen Enhancements: 태블릿 및 폴더블 기기와 같은 큰 화면을 위한 UI 개선.
  2. Taskbar: 대형 화면에서 더 쉬운 멀티태스킹을 위한 태스크바 추가.

Android 13 (API 33)

  1. Per-App Language Preferences: 앱마다 다른 언어 설정이 가능.
  2. Photo Picker: 특정 사진만 앱에 제공할 수 있도록 새로운 사진 선택기 도입.
  3. Notification Permission: 앱에서 알림을 보내기 위해 사용자의 동의를 받아야 함.
  4. Bluetooth LE Audio: 저전력 오디오 전송을 위한 Bluetooth LE Audio 지원.

Android 14 (API 34)

  1. Predictive Back Gesture: 뒤로 가기 제스처를 미리 볼 수 있는 기능 도입.
  2. Regional Preferences: 사용자 지역에 따라 온도 단위, 시작 요일 등을 커스터마이징할 수 있음.
  3. App Cloning: 같은 앱을 여러 계정으로 실행할 수 있는 기능 지원.
  4. Credential Manager API: 패스워드 및 패스키 기반 인증을 통합 관리할 수 있는 새로운 API.
반응형

'개발 > Android' 카테고리의 다른 글

NdefFormatable 태그  (0) 2024.10.21
NFC Tag 상세정보 출력  (2) 2024.10.18
JIT, AOT 컴파일 비교  (1) 2024.10.15
FCM 푸시설정  (0) 2024.10.14
디버그 서명 인증서와 릴리즈 서명 인증서 차이  (0) 2024.10.14
블로그 이미지

SKY STORY

,
반응형

JIT(Just-In-Time) 컴파일과 AOT(Ahead-Of-Time) 컴파일의 차이점을 표로 정리하면 다음과 같다.

항목JIT(Just-In-Time) 컴파일AOT(Ahead-Of-Time) 컴파일
컴파일 시점 프로그램 실행 중 (런타임) 프로그램 실행 전에 (빌드 또는 배포 시)
초기 실행 속도 초기 실행 시간이 느림 (실시간 컴파일) 초기 실행 시간이 빠름 (미리 컴파일된 상태)
최적화 방식 동적 최적화 (실행 중 분석 후 최적화 가능) 정적 최적화 (컴파일 시 최적화, 실행 중 추가 최적화 불가)
메모리 사용 필요할 때만 컴파일하여 상대적으로 메모리 사용이 적음 모든 코드를 미리 컴파일하므로 더 많은 메모리 사용 가능
코드 분석 실행 중 코드 패턴을 분석해 최적화 가능 실행 중 코드 패턴 분석 불가
배포 파일 크기 컴파일러와 일부 런타임 데이터를 포함하여 다소 큼 컴파일된 바이너리만 포함하여 비교적 작음
플랫폼 의존성 플랫폼에 따라 다르게 컴파일할 수 있어 유연함 플랫폼에 맞게 미리 컴파일되므로 다소 제한적일 수 있음
사용 예시 Java (JVM), .NET, Android iOS (Swift), 일부 C++ 컴파일러, Angular (Ivy)
장점 런타임 최적화로 반복 실행 시 성능 향상 가능 빠른 실행 시간, 미리 컴파일된 바이너리로 보안성이 높음
단점 초기 로딩이 느릴 수 있으며, 컴파일로 인한 런타임 오버헤드 발생 가능 런타임 최적화 불가, 모든 코드를 미리 컴파일하므로 비효율 가능

장단점 요약

  • JIT: 동적 최적화를 통해 실행 중 성능을 개선할 수 있지만, 초기 실행 시간이 느릴 수 있다.
  • AOT: 빠른 시작 시간이 장점이지만, JIT처럼 동적인 최적화를 할 수 없고, 모든 코드를 미리 컴파일하므로 메모리 사용량이 많아질 수 있다.

사용 사례

  • JIT: 일반적으로 자바나 .NET 같은 플랫폼에서 많이 사용되며, 코드가 여러 번 실행되고 최적화가 중요한 경우 유리하다.
  • AOT: iOS의 Swift와 같은 환경에서는 AOT를 많이 사용하며, 프로그램의 빠른 시작 시간이 중요한 경우 적합하다.
반응형
블로그 이미지

SKY STORY

,

FCM 푸시설정

개발/Android 2024. 10. 14. 15:21
반응형

다음은 FCM 푸시 설정관련 내용을 알아보겠다.

Firebase 앱등록은 이미 완료됬다고 가정하겠다.

 

만약 아래 그림과 같이 'Product Flavors' 설정을 하지 않았다면 1번 위치에

설정을 하였다면 2번과 같이 'google-services.json'파일 복사한다.

 

프로젝트 수준 build.gradle :

 

모듈 수준 build.gradle :

 

AndroidManifest.xml에 알림 권한 추가:

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

 

권한 요청 코드 (MainActivity 등에서):

안드로이드 13(API 33) 이상에서는 알림 권한이 필요하다. 

import android.Manifest
import android.content.pm.PackageManager
import androidx.core.content.ContextCompat
import androidx.core.app.ActivityCompat
import android.os.Build

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 푸시 알림 권한 요청
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            if (ContextCompat.checkSelfPermission(
                    this,
                    Manifest.permission.POST_NOTIFICATIONS
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                // 알림 권한 요청
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(Manifest.permission.POST_NOTIFICATIONS),
                    1000
                )
            }
        }

        // 그 외 FCM 설정
    }
}

 

FCM 토큰 생성 및 토큰 로그 확인

Firebase에서 푸시 메시지를 보내기 위해서는 FCM 토큰이 필요하다. 이 토큰은 앱 설치 시 자동으로 생성되며, 이를 통해 Firebase는 해당 기기에 푸시 메시지를 전송할 수 있다.

FCM 서비스 초기화 (FirebaseMessagingService 상속):

아래 코드와 같이 FirebaseMessagingService를 상속받아 FCM 토큰을 얻을 수 있다.

import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.FirebaseMessaging
import com.google.firebase.messaging.RemoteMessage
import android.util.Log

class MyFirebaseMessagingService : FirebaseMessagingService() {
	
    override fun onNewToken(token: String) {
        super.onNewToken(token)
        Log.d("FCM", "FCM 토큰 생성: $token")
        // 서버로 토큰 전송
    }

    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        super.onMessageReceived(remoteMessage)
        Log.d("FCM", "푸시 메시지 수신: ${remoteMessage.notification?.body}")
        // 알림을 처리하거나 사용자에게 알림을 표시
    }
}

 

Firebase 토큰을 가져오는 코드 (선택적, 로그 확인용):

이미 생성된 토큰을 얻는다.

FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
    if (task.isSuccessful) {
        val token = task.result
        Log.d("FCM", "FCM 토큰: $token")
    } else {
        Log.w("FCM", "FCM 토큰 생성 실패", task.exception)
    }
}

 

푸시 수신 처리

푸시 알림이 수신되면, onMessageReceived 메서드에서 이를 처리한다. FCM 서버에서 전송된 메시지의 내용을 바탕으로 푸시 알림을 처리하고, 알림을 기기에 표시할 수 있다.

알림 표시 예제 (푸시 수신 시):

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat

override fun onMessageReceived(remoteMessage: RemoteMessage) {
    super.onMessageReceived(remoteMessage)

    // 메시지의 제목과 본문을 가져옴
    val title = remoteMessage.notification?.title ?: "푸시 알림"
    val message = remoteMessage.notification?.body ?: "새로운 메시지가 있습니다."

    // Android 8.0 이상에서는 알림 채널이 필요
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channelId = "default_channel"
        val channelName = "기본 알림 채널"
        val importance = NotificationManager.IMPORTANCE_DEFAULT
        val notificationChannel = NotificationChannel(channelId, channelName, importance)
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(notificationChannel)
    }

    // 알림 생성
    val notificationBuilder = NotificationCompat.Builder(this, "default_channel")
        .setSmallIcon(android.R.drawable.ic_dialog_info)  // 알림 아이콘 설정
        .setContentTitle(title)
        .setContentText(message)
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)

    // 알림 표시
    with(NotificationManagerCompat.from(this)) {
        notify(0, notificationBuilder.build())
    }
}

 

 

반응형
블로그 이미지

SKY STORY

,