두가지 모두 비동기 프로그래밍을 위해 사용되는 기술로,멀티 스레딩 및 동시성 처리를 할때 사용된다.
두 기술에 대한 비교는 다음과 같다.
비동기 작업 처리:
두 기술 모두비동기적으로 작업을 수행할 수 있다. 이를 통해 메인 스레드가 블로킹되지 않으며, 사용자 인터페이스가 멈추지 않게 할 수 있다.
멀티 스레딩 지원:
GCD는 작업을 비동기적으로다양한 큐에서 실행하고, Swift Concurrency는태스크를 사용하여 비동기 코드를 스케줄링한다.
둘 다 멀티코어 CPU의 성능을 활용해병렬 처리가 가능하다.
스레드 관리 자동화:
GCD는스레드 풀을 관리하여 시스템 리소스를 효율적으로 사용한다.
Swift Concurrency도 내부적으로Task Scheduler를 사용하여 스레드 관리와 태스크 스케줄링을 자동으로 최적화한다.
QoS (Quality of Service):
두 기술 모두작업의 우선 순위를 지정할 수 있습니다.
GCD에서는DispatchQoS를 사용해 작업 우선 순위를 설정한다.
Swift Concurrency에서는Task의 우선 순위를 조정할 수 있다 (Task(priority: .high)등).
캔슬 가능한 작업:
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에 비해 이점이 있다.
포크된 저장소 리스트 확인: 모든 포크된 저장소를 확인하려면 다음 명령어를 사용한다. gh repo list YOUR_USERNAME --fork --json name
포크된 저장소 일괄 삭제: 특정 조건을 만족하는 포크만 삭제하려면, 스크립트를 이용해 필터링하여 삭제할 수 있다. 아래는 모든 포크된 저장소를 삭제하는 예제이다. 주의: 이 스크립트는 본인 계정의 모든 포크된 저장소를 반복적으로 삭제한다. 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는 짧은 시간에 많은 요청이 들어오면 요청을 제한하거나 차단한다. 요청시 일정 시간 딜레이를 주거나 여러번 실행하여 해결할 수 있다.
.gitignore에 my_ignore_folder/폴더를 추가했음에도 Git이 계속 해당 폴더의 변경사항을 추적하는 이유는, 이미 Git이 이전에 해당 폴더를 트래킹하고 있기 때문이다..gitignore파일을 설정한 후에는 Git에 해당 폴더를 무시하도록 아래와 같이 추가 조치를 취해보자.
추적된 my_ignore_folder/폴더의 캐시 삭제 .gitignore에 추가된 폴더가 이미 Git에서 추적 중인 상태라면 캐시를 삭제해야 한다.
git rm -r --cached my_ignore_folder/
변경 사항 커밋 캐시에서 삭제된 내용을 커밋하여 기록에 반영한다.
git commit -m "Remove my_ignore_folder folder from tracking"
푸시 원격 저장소에 반영하려면 다음과 같이 푸시한다.
git push
이 작업을 마친 후, my_ignore_folder/폴더는.gitignore설정에 따라 무시되어, 이후 변경 사항이 발생해도 Git이 추적하지 않게 된다.
만약 git rm -r --cached my_ignore_folder/명령을 실행했음에도 불구하고 my_ignore_folder/폴더가 여전히 Git에서 추적되고 있다면, 다음 사항들을 점검하고 추가 조치를 취해보자.
.gitignore파일이 제대로 설정되었는지 확인
.gitignore파일에서 my_ignore_folder/경로가 올바르게 작성되었는지 다시 한번 확인한다.
.gitignore파일이 Git의 루트 디렉토리에 위치해 있는지 확인한다. 프로젝트의 루트 디렉토리가 아니라면 무시되지 않을 수 있다.
.gitignore파일에 추가적인 경로 지정
.gitignore에 my_ignore_folder/외에 my_ignore_folder/*과 같은 패턴을 추가해 보세요. 간혹 Git이 하위 파일을 인식하는 경우가 있기 때문에, 아래와 같이 설정할 수도 있습니다:
my_ignore_folder/
my_ignore_folder/*
git status로 확인
터미널에서git status명령을 실행하여 my_ignore_folder/가 추적되고 있는지 확인해보자.
폴더 강제 삭제 후 커밋
위 방법이 모두 실패한 경우, 강제로 my_ignore_folder/폴더를 무시하도록 처리한다.
아래 오류 메시지는 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.다른 네트워크 사용
네트워크 연결 문제로 인해 푸시가 차단될 수 있다. 다른 네트워크(예: 핫스팟)로 전환 후 다시 푸시를 시도해 보자.
NFC 태그가 앱에 의해 감지될 때android.nfc.action.TAG_DISCOVERED는 NFC 태그가 감지될 때 인텐트를 전달하는 데 필요하다. 이를 위해AndroidManifest.xml에 설정을 추가해야 한다. 이 설정은TAG_DISCOVERED,TECH_DISCOVERED,NDEF_DISCOVERED와 같은 액션을 통해 NFC 태그 감지 시 앱으로 인텐트를 전달하는 역할을 한다.
권한 설명
android.nfc.action.NDEF_DISCOVERED: NDEF 메시지를 포함한 NFC 태그가 감지되었을 때 작동하는 액션이다. 주로 특정 애플리케이션 데이터 또는 MIME 타입이 있는 태그에서 사용된다.
android.nfc.action.TAG_DISCOVERED: NFC 태그가 감지되었을 때 작동하는 기본 액션으로, NDEF 메시지가 포함되지 않은 모든 NFC 태그를 처리할 수 있다.
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>
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 형식으로 포맷하면 원래 상태로 되돌릴 수 없다.
일부 태그는 포맷 후에도 더 이상 기록할 수 없는 경우가 있다. (읽기 전용 태그로 변환).
NFC Tag에서 얻을 수 있는 정보를 json 문자열 포맷으로 반환하는 함수를 만들어보았다.
테그 정보 :
UID (고유 ID): tag.id에서추출한고유식별자.
Tech List: 태그가지원하는모든기술목록 (tag.techList).
NfcA (ISO 14443-3A): ATQA, SAK, 최대전송길이, 타임아웃정보.
IsoDep (ISO 14443-4): 역사적바이트(historicalBytes), 고층응답(hiLayerResponse), 최대전송길이, 타임아웃정보.
NfcB (ISO 14443-3B): 응용데이터, 프로토콜정보, 최대전송길이.
NfcF (Felica): 제조사정보, 시스템코드, 최대전송길이, 타임아웃.
NfcV (ISO 15693): 응답플래그, DSFID, 최대전송길이.
MifareClassic: 타입, 크기, 섹터및블록수.
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 // 통신 타임아웃 시간(밀리초)
}
}