Ver3.0 ๐ฑ NFC ํ๊ทธ ์ฝ๊ธฐ/์ฐ๊ธฐ ์์ฉ ์์ค์ฝ๋ ์๋ฒฝ ๊ฐ์ด๋ - ์ค์ ์์ ๋ก ๋ฐฐ์ฐ๋ ๋ชจ๋ฐ์ผ NFC ํ๋ก๊ทธ๋๋ฐ

๐ฑ NFC ํ๊ทธ ์ฝ๊ธฐ/์ฐ๊ธฐ ์์ฉ ์์ค์ฝ๋ ์๋ฒฝ ๊ฐ์ด๋ - ์ค์ ์์ ๋ก ๋ฐฐ์ฐ๋ ๋ชจ๋ฐ์ผ NFC ํ๋ก๊ทธ๋๋ฐ
์๋๋ก์ด๋ NFC ๊ธฐ์ ์ ํ์ฉํ ์ค๋ฌด ๊ฐ๋ฐ ๋ ธํ์ฐ ๋๊ณต๊ฐ ๐
์์ฆ ์ค๋งํธํฐ์ผ๋ก ๊ตํต์นด๋ ์ฐ๊ณ , ๊ฒฐ์ ํ๊ณ , ์ถ์
๋ฌธ ์ด๊ณ ํ๋ ๊ฑฐ ๋ค๋ค ์์ฐ์ค๋ฝ๊ฒ ์ฐ๊ณ ์์ฃ ?ใ
ใ
ใ
์ด๊ฒ ๋ค NFC ๊ธฐ์ ๋๋ถ์ธ๋ฐ์! ๐ณ
๊ทผ๋ฐ ๋ง์ ์ด๊ฑธ ์ง์ ๊ฐ๋ฐํ๋ ค๊ณ ํ๋ฉด "์ด? ์๊ฐ๋ณด๋ค ๋ณต์กํ๋ฐ?" ํ๋ ๋ถ๋ค์ด ๋ง๋๋ผ๊ณ ์. ํนํ NFC ํ๊ทธ ์ฝ๊ณ ์ฐ๋ ๊ธฐ๋ฅ ๊ตฌํํ ๋ ๋ง์ด์ฃ .
์ค๋์ ์ค์ ๋ก ๋์ํ๋ NFC ํ๊ทธ ์ฝ๊ธฐ/์ฐ๊ธฐ ์์ฉ ์์ค์ฝ๋๋ฅผ ์์ ํํค์ณ๋ณผ ๊ฑฐ์์! ์ด๋ก ๋ง ์ฃผ๊ตฌ์ฅ์ฐฝ ๋์ด๋๋ ๊ฒ ์๋๋ผ, ์ง์ง ๋ณต๋ถํด์ ์ธ ์ ์๋ ์ค์ ์ฝ๋ ์์ฃผ๋ก ์ค๋ช
๋๋ฆด๊ฒ์ ๐
๋ณธ๊ฒฉ์ ์ผ๋ก ์ฝ๋ ๋ค์ด๊ฐ๊ธฐ ์ ์ NFC๊ฐ ๋ญ์ง๋ถํฐ ๊ฐ๋จํ๊ฒ ์ ๋ฆฌํ๊ณ ๊ฐ๊ฒ์!ใ
ใ
NFC(Near Field Communication)๋ ๋ง ๊ทธ๋๋ก '๊ทผ๊ฑฐ๋ฆฌ ๋ฌด์ ํต์ ' ๊ธฐ์ ์ด์์. ๋ณดํต 10cm ์ด๋ด์ ์งง์ ๊ฑฐ๋ฆฌ์์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๋ฐฉ์์ธ๋ฐ, ๋ธ๋ฃจํฌ์ค๋ WiFi๋ณด๋ค ํจ์ฌ ๊ฐ๊น์ด ๊ฑฐ๋ฆฌ์์๋ง ์๋ํ์ฃ .
์ด๊ฒ ์คํ๋ ค ์ฅ์ ์ด์์! ๋ณด์์ด ์ข๊ณ , ๋น ๋ฅด๊ณ , ์ ๋ ฅ ์๋ชจ๋ ์ ๊ฑฐ๋ ์ ๐ช
NFC๋ ํฌ๊ฒ ์ธ ๊ฐ์ง ๋ชจ๋๋ก ๋์ํด์:
๐ Reader/Writer ๋ชจ๋
์ค๋งํธํฐ์ด NFC ํ๊ทธ๋ฅผ ์ฝ๊ฑฐ๋ ์ฐ๋ ๋ชจ๋์์. ์ฐ๋ฆฌ๊ฐ ์ค๋ ๋ค๋ฃฐ ํต์ฌ ๊ธฐ๋ฅ!
๐ค P2P ๋ชจ๋
๋ ๊ฐ์ NFC ๊ธฐ๊ธฐ๋ผ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๋ชจ๋. Android Beam ๊ฐ์ ๊ธฐ๋ฅ์ด ์ฌ๊ธฐ ํด๋น๋ผ์.
๐ณ Card Emulation ๋ชจ๋
์ค๋งํธํฐ์ด ์ ์ฉ์นด๋๋ ๊ตํต์นด๋์ฒ๋ผ ๋์ํ๋ ๋ชจ๋. ์ผ์ฑํ์ด, ๊ตฌ๊ธํ์ด๊ฐ ์ด ๋ฐฉ์์ด์ฃ !
์ฐ๋ฆฌ๊ฐ ์ง์คํ ๊ฑด Reader/Writer ๋ชจ๋์์. ์ด๊ฒ ๊ฐ์ฅ ๊ธฐ๋ณธ์ด๋ฉด์๋ ํ์ฉ๋๊ฐ ๋๊ฑฐ๋ ์ ๐จ
์, ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ๊ฐ๋ฐ ๋ค์ด๊ฐ๋ณผ๊น์? ๋จผ์ ํ์ํ ๊ฒ๋ค๋ถํฐ ์ฒดํฌํด๋ด
์๋ค!
ํ์ ์ค๋น๋ฌผ:
โ
Android Studio (์ต์ ๋ฒ์ ๊ถ์ฅ)
โ
NFC ๊ธฐ๋ฅ์ด ์๋ ์๋๋ก์ด๋ ํฐ (ํ
์คํธ์ฉ)
โ
NFC ํ๊ทธ ๋ช ๊ฐ (๋ค์ด์๋ ์จ๋ผ์ธ์์ ์ ๋ ดํ๊ฒ ๊ตฌ์
๊ฐ๋ฅใ
ใ
)
โ
์ต์ SDK ๋ฒ์ : API 19 (Android 4.4) ์ด์
์์ฆ ๋์ค๋ ์ค๋งํธํฐ์ ๊ฑฐ์ ๋ค NFC ์ง์ํ๋๊น ํฌ๊ฒ ๊ฑฑ์ ์ ํด๋ ๋ผ์!
๐ก ๊ฟํ! NFC ํ๊ทธ๋ NTAG213, NTAG215, NTAG216 ๊ฐ์ ๋ชจ๋ธ์ด ๊ฐ์ฅ ๋ฒ์ฉ์ ์ด์์. ์ฉ๋์ 144๋ฐ์ดํธ~888๋ฐ์ดํธ ์ ๋์ธ๋ฐ, ๊ฐ๋จํ ํ ์คํธ๋ URL ์ ์ฅํ๊ธฐ์ ์ถฉ๋ถํด์. ๊ฐ๊ฒฉ๋ ๊ฐ๋น 500์~1000์ ์ ๋๋ก ์ ๋ ดํ๊ณ ์ ๐
๊ฐ์ฅ ๋จผ์ ํด์ผ ํ ๊ฑด ๋งค๋ํ์คํธ ํ์ผ์ NFC ๊ถํ๊ณผ ๊ธฐ๋ฅ์ ์ ์ธํ๋ ๊ฑฐ์์. ์ด๊ฑฐ ์ ํ๋ฉด ์๋ฌด๊ฒ๋ ์ ๋ผ์ใ ใ ใ
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.nfcapp">
<!-- NFC ๊ถํ ์ ์ธ -->
<uses-permission android:name="android.permission.NFC" />
<!-- NFC ๊ธฐ๋ฅ ํ์ ์ฌ๋ถ ์ค์ -->
<uses-feature
android:name="android.hardware.nfc"
android:required="true" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- NFC ํ๊ทธ ๊ฐ์ง ์ธํ
ํธ ํํฐ -->
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
์ฌ๊ธฐ์ ์ค์ํ ํฌ์ธํธ ๋ช ๊ฐ์ง ์ง๊ณ ๋์ด๊ฐ๊ฒ์!
launchMode="singleTop" ์ด ๋ถ๋ถ ์ง์ง ์ค์ํด์! ์ด๊ฑฐ ์ ํ๋ฉด NFC ํ๊ทธ ์ฝ์ ๋๋ง๋ค ์กํฐ๋นํฐ๊ฐ ์๋ก ์์ฑ๋ผ์ ๋๋ฆฌ ๋๊ฑฐ๋ ์ใ
ใ
ใ
์ธํ
ํธ ํํฐ๋ ๋ ๊ฐ์ง๋ฅผ ๋ฑ๋กํ์ด์:
- NDEF_DISCOVERED: NDEF ํฌ๋งท์ผ๋ก ์ ์ฅ๋ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ๋
- TAG_DISCOVERED: ํฌ๋งท๋์ง ์์ ํ๊ทธ๋ ๋ค๋ฅธ ํ์์ ํ๊ทธ๋ฅผ ์ฝ์ ๋
์ด๋ ๊ฒ ํ๋ฉด ๊ฑฐ์ ๋ชจ๋ NFC ํ๊ทธ๋ฅผ ๊ฐ์งํ ์ ์์ด์ ๐
UI๋ ์ฌํํ๊ฒ ๊ฐ๋ณผ๊ฒ์. ์ค์ ๋ก๋ ๋ ์์๊ฒ ๊พธ๋ฏธ๊ฒ ์ง๋ง, ๊ธฐ๋ฅ ์ดํด์ ์ง์คํ๊ธฐ ์ํด ํ์ ์์๋ง ๋ฃ์์ด์!
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center_horizontal">
<TextView
android:id="@+id/tvStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="NFC ํ๊ทธ๋ฅผ ๊ฐ๊น์ด ๋์ฃผ์ธ์"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginTop="32dp"
android:textColor="#558b2f" />
<TextView
android:id="@+id/tvTagInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ํ๊ทธ ์ ๋ณด๊ฐ ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค"
android:textSize="14sp"
android:layout_marginTop="24dp"
android:padding="16dp"
android:background="#f5f5f5"
android:minHeight="120dp" />
<EditText
android:id="@+id/etWriteData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="ํ๊ทธ์ ์ธ ๋ด์ฉ์ ์
๋ ฅํ์ธ์"
android:layout_marginTop="24dp"
android:padding="12dp"
android:background="@android:drawable/edit_text" />
<Button
android:id="@+id/btnWrite"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="NFC ํ๊ทธ์ ์ฐ๊ธฐ"
android:layout_marginTop="16dp"
android:backgroundTint="#66bb6a"
android:textColor="#ffffff" />
<Button
android:id="@+id/btnClear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ํ๋ฉด ์ง์ฐ๊ธฐ"
android:layout_marginTop="8dp"
android:backgroundTint="#bdbdbd"
android:textColor="#ffffff" />
</LinearLayout>
๊ฐ๋จํ์ฃ ? ์ํ ํ์์ฉ ํ
์คํธ๋ทฐ, ํ๊ทธ ์ ๋ณด ํ์์ฉ ํ
์คํธ๋ทฐ, ๋ฐ์ดํฐ ์
๋ ฅ์ฉ ์๋ํธํ
์คํธ, ๊ทธ๋ฆฌ๊ณ ์ฐ๊ธฐ/์ง์ฐ๊ธฐ ๋ฒํผ๋ง ์์ผ๋ฉด ๋ผ์ โจ
์, ์ด์ ์ง์ง ํต์ฌ์ธ ๋ฉ์ธ ์กํฐ๋นํฐ ์ฝ๋๋ฅผ ๋ณผ ์ฐจ๋ก์์! ๋จผ์ NFC ํ๊ทธ ์ฝ๊ธฐ ๊ธฐ๋ฅ๋ถํฐ ๊ตฌํํด๋ณผ๊ฒ์.
์ฝ๋๊ฐ ์ข ๊ธธ์ง๋ง ํ๋ํ๋ ์ค๋ช
ํ ํ
๋๊น ์ฒ์ฒํ ๋ฐ๋ผ์ค์ธ์ ๐
package com.example.nfcapp;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.nfc.tech.NdefFormatable;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.io.IOException;
import java.nio.charset.Charset;
public class MainActivity extends AppCompatActivity {
private NfcAdapter nfcAdapter;
private PendingIntent pendingIntent;
private IntentFilter[] intentFiltersArray;
private String[][] techListsArray;
private TextView tvStatus;
private TextView tvTagInfo;
private EditText etWriteData;
private Button btnWrite;
private Button btnClear;
private boolean writeMode = false;
private String dataToWrite = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// UI ์์ ์ด๊ธฐํ
initViews();
// NFC ์ด๋ํฐ ์ด๊ธฐํ
initNFC();
// ๋ฒํผ ๋ฆฌ์ค๋ ์ค์
setupListeners();
// ์ธํ
ํธ๋ก ์ ๋ฌ๋ NFC ํ๊ทธ ์ฒ๋ฆฌ
handleIntent(getIntent());
}
private void initViews() {
tvStatus = findViewById(R.id.tvStatus);
tvTagInfo = findViewById(R.id.tvTagInfo);
etWriteData = findViewById(R.id.etWriteData);
btnWrite = findViewById(R.id.btnWrite);
btnClear = findViewById(R.id.btnClear);
}
private void initNFC() {
// NFC ์ด๋ํฐ ๊ฐ์ ธ์ค๊ธฐ
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) {
// ๊ธฐ๊ธฐ๊ฐ NFC๋ฅผ ์ง์ํ์ง ์์
tvStatus.setText("์ด ๊ธฐ๊ธฐ๋ NFC๋ฅผ ์ง์ํ์ง ์์ต๋๋ค ๐ข");
btnWrite.setEnabled(false);
return;
}
if (!nfcAdapter.isEnabled()) {
// NFC๊ฐ ๋นํ์ฑํ๋์ด ์์
tvStatus.setText("NFC๊ฐ ๊บผ์ ธ์์ด์! ์ค์ ์์ ์ผ์ฃผ์ธ์ ๐ง");
}
// PendingIntent ์์ฑ
Intent intent = new Intent(this, getClass())
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_MUTABLE);
// IntentFilter ์ค์
IntentFilter ndefFilter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
ndefFilter.addDataType("text/plain");
} catch (IntentFilter.MalformedMimeTypeException e) {
throw new RuntimeException("MIME ํ์
์ค์ ์คํจ", e);
}
intentFiltersArray = new IntentFilter[] { ndefFilter };
// Tech List ์ค์
techListsArray = new String[][] {
new String[] { Ndef.class.getName() },
new String[] { NdefFormatable.class.getName() }
};
}
private void setupListeners() {
btnWrite.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String data = etWriteData.getText().toString().trim();
if (data.isEmpty()) {
Toast.makeText(MainActivity.this,
"์ธ ๋ด์ฉ์ ์
๋ ฅํด์ฃผ์ธ์!", Toast.LENGTH_SHORT).show();
return;
}
dataToWrite = data;
writeMode = true;
tvStatus.setText("์ฐ๊ธฐ ๋ชจ๋ ํ์ฑํ! ํ๊ทธ๋ฅผ ๊ฐ๊น์ด ๋์ฃผ์ธ์ โ๏ธ");
Toast.makeText(MainActivity.this,
"์ด์ NFC ํ๊ทธ๋ฅผ ๊ฐ๊น์ด ๋์ฃผ์ธ์", Toast.LENGTH_LONG).show();
}
});
btnClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tvTagInfo.setText("ํ๊ทธ ์ ๋ณด๊ฐ ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค");
etWriteData.setText("");
writeMode = false;
tvStatus.setText("NFC ํ๊ทธ๋ฅผ ๊ฐ๊น์ด ๋์ฃผ์ธ์");
}
});
}
@Override
protected void onResume() {
super.onResume();
if (nfcAdapter != null) {
// Foreground Dispatch ํ์ฑํ
nfcAdapter.enableForegroundDispatch(
this, pendingIntent, intentFiltersArray, techListsArray);
}
}
@Override
protected void onPause() {
super.onPause();
if (nfcAdapter != null) {
// Foreground Dispatch ๋นํ์ฑํ
nfcAdapter.disableForegroundDispatch(this);
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
String action = intent.getAction();
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action) ||
NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (writeMode) {
// ์ฐ๊ธฐ ๋ชจ๋์ผ ๋
writeToTag(tag, dataToWrite);
} else {
// ์ฝ๊ธฐ ๋ชจ๋์ผ ๋
readFromTag(intent, tag);
}
}
}
private void readFromTag(Intent intent, Tag tag) {
// NDEF ๋ฉ์์ง ์ฝ๊ธฐ
Parcelable[] rawMessages =
intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
StringBuilder tagInfo = new StringBuilder();
tagInfo.append("๐ฑ ํ๊ทธ ์ ๋ณด\n\n");
// ํ๊ทธ ID ํ์
byte[] id = tag.getId();
tagInfo.append("๐ ํ๊ทธ ID: ").append(bytesToHex(id)).append("\n\n");
// ์ง์ ๊ธฐ์ ํ์
String[] techList = tag.getTechList();
tagInfo.append("๐ง ์ง์ ๊ธฐ์ :\n");
for (String tech : techList) {
String techName = tech.substring(tech.lastIndexOf(".") + 1);
tagInfo.append(" โข ").append(techName).append("\n");
}
tagInfo.append("\n");
// NDEF ๋ฉ์์ง ํ์ฑ
if (rawMessages != null && rawMessages.length > 0) {
NdefMessage[] messages = new NdefMessage[rawMessages.length];
for (int i = 0; i < rawMessages.length; i++) {
messages[i] = (NdefMessage) rawMessages[i];
}
tagInfo.append("๐ ์ ์ฅ๋ ๋ฐ์ดํฐ:\n\n");
for (NdefMessage message : messages) {
NdefRecord[] records = message.getRecords();
for (NdefRecord record : records) {
String payload = parseNdefRecord(record);
if (payload != null && !payload.isEmpty()) {
tagInfo.append(payload).append("\n");
}
}
}
} else {
tagInfo.append("๐ญ ์ ์ฅ๋ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค\n");
}
tvTagInfo.setText(tagInfo.toString());
tvStatus.setText("ํ๊ทธ ์ฝ๊ธฐ ์๋ฃ! โ
");
Toast.makeText(this, "NFC ํ๊ทธ๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ์ฝ์์ด์!",
Toast.LENGTH_SHORT).show();
}
private String parseNdefRecord(NdefRecord record) {
short tnf = record.getTnf();
byte[] payload = record.getPayload();
if (tnf == NdefRecord.TNF_WELL_KNOWN) {
byte[] type = record.getType();
if (java.util.Arrays.equals(type, NdefRecord.RTD_TEXT)) {
// ํ
์คํธ ๋ ์ฝ๋ ํ์ฑ
try {
// ์ฒซ ๋ฐ์ดํธ๋ ์ธ์ด ์ฝ๋ ๊ธธ์ด
int languageCodeLength = payload[0] & 0x3F;
// ์ค์ ํ
์คํธ ์ถ์ถ
String text = new String(payload,
languageCodeLength + 1,
payload.length - languageCodeLength - 1,
Charset.forName("UTF-8"));
return text;
} catch (Exception e) {
return "ํ
์คํธ ํ์ฑ ์คํจ";
}
} else if (java.util.Arrays.equals(type, NdefRecord.RTD_URI)) {
// URI ๋ ์ฝ๋ ํ์ฑ
return parseUriRecord(payload);
}
}
return "์ ์ ์๋ ๋ฐ์ดํฐ ํ์";
}
private String parseUriRecord(byte[] payload) {
// URI ์๋ณ์ ํ๋ฆฌํฝ์ค
String[] uriPrefixes = {
"", "http://www.", "https://www.", "http://", "https://",
"tel:", "mailto:", "ftp://anonymous:anonymous@", "ftp://ftp.",
"ftps://", "sftp://", "smb://", "nfs://", "ftp://", "dav://",
"news:", "telnet://", "imap:", "rtsp://", "urn:", "pop:",
"sip:", "sips:", "tftp:", "btspp://", "btl2cap://", "btgoep://",
"tcpobex://", "irdaobex://", "file://", "urn:epc:id:",
"urn:epc:tag:", "urn:epc:pat:", "urn:epc:raw:", "urn:epc:",
"urn:nfc:"
};
byte prefixIndex = payload[0];
String prefix = "";
if (prefixIndex >= 0 && prefixIndex < uriPrefixes.length) {
prefix = uriPrefixes[prefixIndex];
}
String uri = new String(payload, 1, payload.length - 1,
Charset.forName("UTF-8"));
return prefix + uri;
}
private String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X", b));
}
return sb.toString();
}
}
์... ์ฝ๋๊ฐ ์ข ๊ธธ์ฃ ?ใ
ใ
ใ
๊ทผ๋ฐ ํ๋ํ๋ ๋ฏ์ด๋ณด๋ฉด ๋ณ๊ฑฐ ์์ด์!
ํต์ฌ ํฌ์ธํธ๋ง ์ ๋ฆฌํด๋ณผ๊ฒ์:
1๏ธโฃ Foreground Dispatch System
onResume()์์ enableForegroundDispatch()๋ฅผ ํธ์ถํด์ ์ฑ์ด ํฌ๊ทธ๋ผ์ด๋์ ์์ ๋ NFC ํ๊ทธ๋ฅผ ์ฐ์ ์ ์ผ๋ก ์ฒ๋ฆฌํ๋๋ก ํด์. ์ด๊ฑฐ ์ ํ๋ฉด ๋ค๋ฅธ ์ฑ์ด ํ๊ทธ๋ฅผ ๊ฐ๋ก์ฑ ์ ์์ด์!
2๏ธโฃ onNewIntent() ์ฒ๋ฆฌ
launchMode๊ฐ singleTop์ด๋ผ์ ์๋ก์ด NFC ์ธํ
ํธ๊ฐ ๋ค์ด์ค๋ฉด onNewIntent()๊ฐ ํธ์ถ๋ผ์. ์ฌ๊ธฐ์ ํ๊ทธ๋ฅผ ์ฒ๋ฆฌํ๋ ๊ฑฐ์ฃ .
3๏ธโฃ NDEF ๋ฉ์์ง ํ์ฑ
NFC ํ๊ทธ์ ์ ์ฅ๋ ๋ฐ์ดํฐ๋ NDEF(NFC Data Exchange Format) ํ์์ด์์. ์ด๊ฑธ ํ์ฑํด์ ์ค์ ํ
์คํธ๋ URI๋ฅผ ์ถ์ถํ๋ ๊ฑฐ์์.
4๏ธโฃ ํ๊ทธ ์ ๋ณด ํ์
ํ๊ทธ ID, ์ง์ ๊ธฐ์ , ์ ์ฅ๋ ๋ฐ์ดํฐ ๋ฑ์ ๊น๋ํ๊ฒ ์ ๋ฆฌํด์ ๋ณด์ฌ์ค์ ๐
์ด์ ํ๊ทธ์ ๋ฐ์ดํฐ๋ฅผ ์ฐ๋ ๊ธฐ๋ฅ์ ์ถ๊ฐํด๋ณผ๊ฒ์! ์ MainActivity ํด๋์ค์ ๋ค์ ๋ฉ์๋๋ฅผ ์ถ๊ฐํ๋ฉด ๋ผ์.
private void writeToTag(Tag tag, String data) {
try {
// NDEF ๋ ์ฝ๋ ์์ฑ
NdefRecord record = createTextRecord(data);
NdefMessage message = new NdefMessage(new NdefRecord[] { record });
// Ndef ๊ธฐ์ ๊ฐ์ ธ์ค๊ธฐ
Ndef ndef = Ndef.get(tag);
if (ndef != null) {
// ์ด๋ฏธ ํฌ๋งท๋ ํ๊ทธ
ndef.connect();
// ์ฐ๊ธฐ ๊ฐ๋ฅ ์ฌ๋ถ ํ์ธ
if (!ndef.isWritable()) {
tvStatus.setText("์ด ํ๊ทธ๋ ์ฝ๊ธฐ ์ ์ฉ์ด์์ ๐");
Toast.makeText(this, "์ฐ๊ธฐ ๋ถ๊ฐ๋ฅํ ํ๊ทธ์
๋๋ค",
Toast.LENGTH_SHORT).show();
ndef.close();
return;
}
// ์ฉ๋ ํ์ธ
int size = message.toByteArray().length;
int maxSize = ndef.getMaxSize();
if (size > maxSize) {
tvStatus.setText("๋ฐ์ดํฐ๊ฐ ๋๋ฌด ์ปค์! ๐
");
Toast.makeText(this,
String.format("์ต๋ %d๋ฐ์ดํธ๊น์ง ๊ฐ๋ฅํด์", maxSize),
Toast.LENGTH_SHORT).show();
ndef.close();
return;
}
// ๋ฐ์ดํฐ ์ฐ๊ธฐ
ndef.writeNdefMessage(message);
ndef.close();
tvStatus.setText("์ฐ๊ธฐ ์๋ฃ! โ
");
tvTagInfo.setText("โ๏ธ ์ด ๋ด์ฉ:\n\n" + data);
Toast.makeText(this, "NFC ํ๊ทธ์ ์ฑ๊ณต์ ์ผ๋ก ์ผ์ด์!",
Toast.LENGTH_SHORT).show();
writeMode = false;
etWriteData.setText("");
} else {
// ํฌ๋งท๋์ง ์์ ํ๊ทธ
NdefFormatable format = NdefFormatable.get(tag);
if (format != null) {
try {
format.connect();
format.format(message);
format.close();
tvStatus.setText("ํฌ๋งท ํ ์ฐ๊ธฐ ์๋ฃ! โ
");
tvTagInfo.setText("โ๏ธ ์ด ๋ด์ฉ:\n\n" + data);
Toast.makeText(this,
"ํ๊ทธ๋ฅผ ํฌ๋งทํ๊ณ ๋ฐ์ดํฐ๋ฅผ ์ผ์ด์!",
Toast.LENGTH_SHORT).show();
writeMode = false;
etWriteData.setText("");
} catch (IOException e) {
tvStatus.setText("ํฌ๋งท ์คํจ ๐ข");
Toast.makeText(this, "ํ๊ทธ ํฌ๋งท์ ์คํจํ์ด์",
Toast.LENGTH_SHORT).show();
}
} else {
tvStatus.setText("์ง์ํ์ง ์๋ ํ๊ทธ์์ โ");
Toast.makeText(this, "์ด ํ๊ทธ๋ ์ธ ์ ์์ด์",
Toast.LENGTH_SHORT).show();
}
}
} catch (Exception e) {
tvStatus.setText("์ฐ๊ธฐ ์คํจ ๐ข");
Toast.makeText(this, "์ค๋ฅ: " + e.getMessage(),
Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
private NdefRecord createTextRecord(String text) {
// ์ธ์ด ์ฝ๋
String lang = "en";
byte[] langBytes = lang.getBytes(Charset.forName("US-ASCII"));
// ํ
์คํธ ๋ฐ์ดํธ
byte[] textBytes = text.getBytes(Charset.forName("UTF-8"));
// ํ์ด๋ก๋ ์์ฑ
int langLength = langBytes.length;
int textLength = textBytes.length;
byte[] payload = new byte[1 + langLength + textLength];
// ์ฒซ ๋ฐ์ดํธ: ์ธ์ด ์ฝ๋ ๊ธธ์ด
payload[0] = (byte) langLength;
// ์ธ์ด ์ฝ๋ ๋ณต์ฌ
System.arraycopy(langBytes, 0, payload, 1, langLength);
// ํ
์คํธ ๋ณต์ฌ
System.arraycopy(textBytes, 0, payload, 1 + langLength, textLength);
// NDEF ๋ ์ฝ๋ ์์ฑ
return new NdefRecord(
NdefRecord.TNF_WELL_KNOWN,
NdefRecord.RTD_TEXT,
new byte[0],
payload
);
}
// URI ๋ ์ฝ๋ ์์ฑ ๋ฉ์๋ (์ถ๊ฐ ๊ธฐ๋ฅ)
private NdefRecord createUriRecord(String uri) {
byte[] uriBytes = uri.getBytes(Charset.forName("UTF-8"));
byte[] payload = new byte[uriBytes.length + 1];
// ํ๋ฆฌํฝ์ค ์์
payload[0] = 0x00;
System.arraycopy(uriBytes, 0, payload, 1, uriBytes.length);
return new NdefRecord(
NdefRecord.TNF_WELL_KNOWN,
NdefRecord.RTD_URI,
new byte[0],
payload
);
}
์ฐ๊ธฐ ๊ธฐ๋ฅ์ ํต์ฌ ๋ก์ง์ ์ค๋ช
ํด๋ณผ๊ฒ์!
1. NDEF ๋ ์ฝ๋ ์์ฑ
createTextRecord() ๋ฉ์๋๋ก ํ
์คํธ๋ฅผ NDEF ํ์์ผ๋ก ๋ณํํด์. ์ธ์ด ์ฝ๋์ ์ค์ ํ
์คํธ๋ฅผ ์กฐํฉํด์ ํ์ด๋ก๋๋ฅผ ๋ง๋๋ ๊ฑฐ์ฃ .
2. ํ๊ทธ ์ํ ํ์ธ
ํ๊ทธ๊ฐ ์ด๋ฏธ ํฌ๋งท๋์ด ์๋์ง, ์ฐ๊ธฐ ๊ฐ๋ฅํ์ง, ์ฉ๋์ ์ถฉ๋ถํ์ง ์ฒดํฌํด์. ์ด๊ฑฐ ์ ํ๋ฉด ์๋ฌ ๋๊ธฐ ์ฌ์์ใ
ใ
3. ๋ฐ์ดํฐ ์ฐ๊ธฐ
ndef.writeNdefMessage()๋ก ์ค์ ๋ฐ์ดํฐ๋ฅผ ํ๊ทธ์ ์จ์. ํฌ๋งท๋์ง ์์ ํ๊ทธ๋ format.format()์ผ๋ก ํฌ๋งทํ๋ฉด์ ๋์์ ๋ฐ์ดํฐ๋ฅผ ์ธ ์ ์์ด์!
4. ์๋ฌ ์ฒ๋ฆฌ
๊ฐ ๋จ๊ณ๋ง๋ค ์ ์ ํ ์๋ฌ ๋ฉ์์ง๋ฅผ ํ์ํด์ ์ฌ์ฉ์๊ฐ ๋ญ๊ฐ ๋ฌธ์ ์ธ์ง ์ ์ ์๊ฒ ํด์ค์ ๐ฌ
NFC ์ฑ ๊ฐ๋ฐํ ๋ ๋ณด์๋ ์ ๊ฒฝ ์จ์ผ ํด์! ํนํ ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ๋ค๋ฃฌ๋ค๋ฉด ๋๋์ฑ์ด์ ๐
๋ช ๊ฐ์ง ์ค์ํ ๋ณด์ ํ์ ์๋ ค๋๋ฆด๊ฒ์:
1. ๋ฐ์ดํฐ ์ํธํ
์ค์ํ ์ ๋ณด๋ ๋ฐ๋์ ์ํธํํด์ ์ ์ฅํ์ธ์! AES ๊ฐ์ ๋์นญํค ์ํธํ๋ฅผ ์ฌ์ฉํ๋ฉด ์ข์์.
2. ํ๊ทธ ์ธ์ฆ
์ ํ ํ๊ทธ์ธ์ง ํ์ธํ๋ ๋ก์ง์ ์ถ๊ฐํ์ธ์. ํ๊ทธ ID๋ ํน์ ์๋ช
์ ๊ฒ์ฆํ๋ ๋ฐฉ์์ด ์์ด์.
3. ์
๋ ฅ ๊ฒ์ฆ
์ฌ์ฉ์๊ฐ ์
๋ ฅํ ๋ฐ์ดํฐ๋ฅผ ํ๊ทธ์ ์ฐ๊ธฐ ์ ์ ๋ฐ๋์ ๊ฒ์ฆํ์ธ์. SQL ์ธ์ ์
์ด๋ XSS ๊ฐ์ ๊ณต๊ฒฉ์ ๋ง์ ์ ์์ด์.
4. ๊ถํ ์ต์ํ
ํ์ํ ๊ถํ๋ง ์์ฒญํ์ธ์. NFC ๊ถํ ์ธ์ ๋ถํ์ํ ๊ถํ์ ๋นผ๋ ๊ฒ ์ข์์.
5. HTTPS ํต์
NFC ํ๊ทธ์์ ์ฝ์ URL๋ก ํต์ ํ ๋๋ ๋ฐ๋์ HTTPS๋ฅผ ์ฌ์ฉํ์ธ์!
๊ฐ๋จํ ์ํธํ ์์ ๋ ๋ณด์ฌ๋๋ฆด๊ฒ์:
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import android.util.Base64;
public class NFCEncryption {
private static final String ALGORITHM = "AES";
private static final String KEY = "MySecretKey12345"; // ์ค์ ๋ก๋ ์์ ํ๊ฒ ๊ด๋ฆฌ!
// ๋ฐ์ดํฐ ์ํธํ
public static String encrypt(String data) {
try {
SecretKeySpec key = new SecretKeySpec(KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(data.getBytes());
return Base64.encodeToString(encryptedBytes, Base64.DEFAULT);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// ๋ฐ์ดํฐ ๋ณตํธํ
public static String decrypt(String encryptedData) {
try {
SecretKeySpec key = new SecretKeySpec(KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decodedBytes = Base64.decode(encryptedData, Base64.DEFAULT);
byte[] decryptedBytes = cipher.doFinal(decodedBytes);
return new String(decryptedBytes);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
์ด๋ ๊ฒ ์ํธํํด์ ํ๊ทธ์ ์ ์ฅํ๋ฉด ํจ์ฌ ์์ ํ๊ฒ ์ฃ ? ๐
์ด์ ์ค์ ๋ก ์ด๋ป๊ฒ ํ์ฉํ ์ ์๋์ง ๋ช ๊ฐ์ง ์์ ๋ฅผ ๋ณด์ฌ๋๋ฆด๊ฒ์!
NFC ๊ธฐ์ ์ ์ ๋ง ๋ค์ํ ๋ถ์ผ์์ ์ธ ์ ์๊ฑฐ๋ ์ โจ
NFC ํ๊ทธ์ ์ฐ๋ฝ์ฒ ์ ๋ณด๋ฅผ ์ ์ฅํด์ ์ค๋งํธ ๋ช ํจ์ ๋ง๋ค ์ ์์ด์!
private NdefRecord createVCardRecord(String name, String phone, String email) {
String vCard = "BEGIN:VCARD\n" +
"VERSION:3.0\n" +
"FN:" + name + "\n" +
"TEL:" + phone + "\n" +
"EMAIL:" + email + "\n" +
"END:VCARD";
byte[] vCardBytes = vCard.getBytes(Charset.forName("UTF-8"));
return new NdefRecord(
NdefRecord.TNF_MIME_MEDIA,
"text/x-vcard".getBytes(),
new byte[0],
vCardBytes
);
}
// ์ฌ์ฉ ์์
NdefRecord vCardRecord = createVCardRecord(
"ํ๊ธธ๋",
"010-1234-5678",
"hong@example.com"
);
NdefMessage message = new NdefMessage(new NdefRecord[] { vCardRecord });
// ํ๊ทธ์ ์ฐ๊ธฐ...
NFC ํ๊ทธ๋ฅผ ์ง์ ๊ณณ๊ณณ์ ๋ถ์ฌ์ ์ค๋งํธ ํ ๊ธฐ๊ธฐ๋ฅผ ์ ์ดํ ์ ์์ด์!
private void handleSmartHomeTag(String command) {
switch (command) {
case "LIGHT_ON":
// ์กฐ๋ช
์ผ๊ธฐ
controlLight(true);
Toast.makeText(this, "์กฐ๋ช
์ ์ผฐ์ด์! ๐ก", Toast.LENGTH_SHORT).show();
break;
case "LIGHT_OFF":
// ์กฐ๋ช
๋๊ธฐ
controlLight(false);
Toast.makeText(this, "์กฐ๋ช
์ ๊ป์ด์! ๐", Toast.LENGTH_SHORT).show();
break;
case "AC_ON":
// ์์ด์ปจ ์ผ๊ธฐ
controlAirConditioner(true);
Toast.makeText(this, "์์ด์ปจ์ ์ผฐ์ด์! โ๏ธ", Toast.LENGTH_SHORT).show();
break;
case "GOODNIGHT":
// ์ทจ์นจ ๋ชจ๋
controlLight(false);
controlAirConditioner(true);
setAlarm();
Toast.makeText(this, "๊ตฟ๋์ ๋ชจ๋ ํ์ฑํ! ๐ด", Toast.LENGTH_SHORT).show();
break;
}
}
private void controlLight(boolean on) {
// ์ค์ ๋ก๋ ์ค๋งํธ ํ API ํธ์ถ
// ์: SmartHomeAPI.setLight(on);
}
private void controlAirConditioner(boolean on) {
// ์์ด์ปจ ์ ์ด ๋ก์ง
}
ํ์ฌ๋ ํ๊ต ์ถ์ ์ฆ์ผ๋ก๋ ํ์ฉํ ์ ์์ด์!
private void handleAccessControl(Tag tag) {
String tagId = bytesToHex(tag.getId());
// ์๋ฒ์ ์ธ์ฆ ์์ฒญ
checkAccessPermission(tagId, new AccessCallback() {
@Override
public void onSuccess(UserInfo userInfo) {
// ์ถ์
ํ๊ฐ
tvStatus.setText("ํ์ํฉ๋๋ค, " + userInfo.getName() + "๋! ๐");
openDoor();
logAccess(userInfo, true);
}
@Override
public void onFailure(String reason) {
// ์ถ์
๊ฑฐ๋ถ
tvStatus.setText("์ถ์
์ด ๊ฑฐ๋ถ๋์์ต๋๋ค โ");
Toast.makeText(MainActivity.this,
"์ฌ์ : " + reason, Toast.LENGTH_LONG).show();
logAccess(null, false);
}
});
}
private void checkAccessPermission(String tagId, AccessCallback callback) {
// ์ค์ ๋ก๋ ์๋ฒ API ํธ์ถ
// ์์ ์ฝ๋
new Thread(() -> {
try {
// API ํธ์ถ ๋ก์ง
// Response response = api.checkAccess(tagId);
// ์์ ์์
UserInfo user = new UserInfo("ํ๊ธธ๋", "๊ฐ๋ฐํ");
runOnUiThread(() -> callback.onSuccess(user));
} catch (Exception e) {
runOnUiThread(() -> callback.onFailure(e.getMessage()));
}
}).start();
}
interface AccessCallback {
void onSuccess(UserInfo userInfo);
void onFailure(String reason);
}
class UserInfo {
private String name;
private String department;
public UserInfo(String name, String department) {
this.name = name;
this.department = department;
}
public String getName() { return name; }
public String getDepartment() { return department; }
}
์ด๋ฐ ์์ผ๋ก ๋ค์ํ๊ฒ ์์ฉํ ์ ์์ด์! ์์ด๋์ด๋ง ์๋ค๋ฉด ๋ฌด๊ถ๋ฌด์งํ์ฃ ๐
NFC ๊ฐ๋ฐํ๋ค ๋ณด๋ฉด ์์ฃผ ๋ง๋๋ ์๋ฌ๋ค์ด ์์ด์. ๋ฏธ๋ฆฌ ์์๋๋ฉด ์ฝ์ง ์ ํด๋ ๋์ฃ ใ ใ ใ
โ ์๋ฌ 1: "NFC adapter is null"
์์ธ: ๊ธฐ๊ธฐ๊ฐ NFC๋ฅผ ์ง์ํ์ง ์๊ฑฐ๋ ์๋ฎฌ๋ ์ดํฐ์์ ์คํ ์ค
ํด๊ฒฐ: ์ค์ NFC ์ง์ ๊ธฐ๊ธฐ์์ ํ
์คํธํ๊ณ , null ์ฒดํฌ ๋ก์ง ์ถ๊ฐ
โ ์๋ฌ 2: "Tag was lost"
์์ธ: ํ๊ทธ๋ฅผ ๋๋ฌด ๋นจ๋ฆฌ ๋ผ๊ฑฐ๋ ํต์ ์ค ์ฐ๊ฒฐ์ด ๋๊น
ํด๊ฒฐ: ํ๊ทธ๋ฅผ 3์ด ์ ๋ ๊ฐ๊น์ด ๋๊ณ ์๋๋ก ์๋ด ๋ฉ์์ง ์ถ๊ฐ
โ ์๋ฌ 3: "Tag is not NDEF formatted"
์์ธ: ํ๊ทธ๊ฐ NDEF ํฌ๋งท์ด ์๋
ํด๊ฒฐ: NdefFormatable์ผ๋ก ํฌ๋งท ํ ์ฐ๊ธฐ (์ ์ฝ๋ ์ฐธ๊ณ )
โ ์๋ฌ 4: "IOException: Transceive failed"
์์ธ: ํ๊ทธ์์ ํต์ ์คํจ
ํด๊ฒฐ: ์ฌ์๋ ๋ก์ง ์ถ๊ฐ, ํ๊ทธ ํ์ง ํ์ธ
โ ์๋ฌ 5: "SecurityException: Need NFC permission"
์์ธ: ๋งค๋ํ์คํธ์ ๊ถํ ์ ์ธ ๋๋ฝ
ํด๊ฒฐ: AndroidManifest.xml์ NFC ๊ถํ ์ถ๊ฐ
์๋ฌ ์ฒ๋ฆฌ ๊ฐํ ๋ฒ์ ์ฝ๋๋ ๋ณด์ฌ๋๋ฆด๊ฒ์:
private void writeToTagWithRetry(Tag tag, String data, int retryCount) {
int attempts = 0;
boolean success = false;
while (attempts < retryCount && !success) {
try {
attempts++;
NdefRecord record = createTextRecord(data);
NdefMessage message = new NdefMessage(new NdefRecord[] { record });
Ndef ndef = Ndef.get(tag);
if (ndef != null) {
ndef.connect();
if (!ndef.isWritable()) {
throw new IOException("Tag is read-only");
}
ndef.writeNdefMessage(message);
ndef.close();
success = true;
tvStatus.setText("์ฐ๊ธฐ ์๋ฃ! โ
");
Toast.makeText(this, "์ฑ๊ณต!", Toast.LENGTH_SHORT).show();
} else {
throw new IOException("Tag is not NDEF formatted");
}
} catch (IOException e) {
if (attempts >= retryCount) {
tvStatus.setText("์ฐ๊ธฐ ์คํจ ๐ข");
Toast.makeText(this,
String.format("์คํจ (%d๋ฒ ์๋): %s", attempts, e.getMessage()),
Toast.LENGTH_LONG).show();
} else {
// ์ฌ์๋
try {
Thread.sleep(500); // 0.5์ด ๋๊ธฐ
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
}
}
์ด๋ ๊ฒ ์ฌ์๋ ๋ก์ง์ ์ถ๊ฐํ๋ฉด ํจ์ฌ ์์ ์ ์ผ๋ก ๋์ํด์! ๐ช
NFC ์ฑ์ ๋ ๋น ๋ฅด๊ณ ํจ์จ์ ์ผ๋ก ๋ง๋๋ ๋ฐฉ๋ฒ๋ค์ด์์!
1๏ธโฃ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋ ์ฌ์ฉ
NFC ์ฝ๊ธฐ/์ฐ๊ธฐ ์์
์ ๋ฉ์ธ ์ค๋ ๋๋ฅผ ๋ธ๋กํนํ ์ ์์ด์. AsyncTask๋ Coroutine์ ์ฌ์ฉํ์ธ์!
2๏ธโฃ ์บ์ฑ ํ์ฉ
์์ฃผ ์ฝ๋ ํ๊ทธ ์ ๋ณด๋ ์บ์ฑํด์ ์ฌ์ฌ์ฉํ์ธ์. SharedPreferences๋ Room DB๋ฅผ ํ์ฉํ๋ฉด ์ข์์.
3๏ธโฃ ๋ฐฐํฐ๋ฆฌ ์ต์ ํ
Foreground Dispatch๋ onPause()์์ ๋ฐ๋์ ๋นํ์ฑํํ์ธ์. ์ ๊ทธ๋ฌ๋ฉด ๋ฐฐํฐ๋ฆฌ๊ฐ ๋นจ๋ฆฌ ๋ณ์์!
4๏ธโฃ ๋ฐ์ดํฐ ์์ถ
ํฐ ๋ฐ์ดํฐ๋ ์์ถํด์ ์ ์ฅํ๋ฉด ํ๊ทธ ์ฉ๋์ ์ ์ฝํ ์ ์์ด์.
5๏ธโฃ ์๋ฌ ๋ก๊น
Firebase Crashlytics ๊ฐ์ ๋๊ตฌ๋ก ์๋ฌ๋ฅผ ์ถ์ ํ๋ฉด ๋๋ฒ๊น
์ด ํจ์ฌ ์ฌ์์ ธ์!
AsyncTask ์์ ๋ ๋ณด์ฌ๋๋ฆด๊ฒ์:
private class WriteNFCTask extends AsyncTask<Void, Void, Boolean> {
private Tag tag;
private String data;
private String errorMessage;
public WriteNFCTask(Tag tag, String data) {
this.tag = tag;
this.data = data;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
// UI ์
๋ฐ์ดํธ (๋ก๋ฉ ํ์)
tvStatus.setText("์ฐ๋ ์ค... โณ");
btnWrite.setEnabled(false);
}
@Override
protected Boolean doInBackground(Void... voids) {
try {
NdefRecord record = createTextRecord(data);
NdefMessage message = new NdefMessage(new NdefRecord[] { record });
Ndef ndef = Ndef.get(tag);
if (ndef != null) {
ndef.connect();
ndef.writeNdefMessage(message);
ndef.close();
return true;
}
errorMessage = "NDEF not supported";
return false;
} catch (Exception e) {
errorMessage = e.getMessage();
return false;
}
}
@Override
protected void onPostExecute(Boolean success) {
super.onPostExecute(success);
btnWrite.setEnabled(true);
if (success) {
tvStatus.setText("์ฐ๊ธฐ ์๋ฃ! โ
");
Toast.makeText(MainActivity.this,
"์ฑ๊ณต์ ์ผ๋ก ์ผ์ด์!", Toast.LENGTH_SHORT).show();
} else {
tvStatus.setText("์ฐ๊ธฐ ์คํจ ๐ข");
Toast.makeText(MainActivity.this,
"์ค๋ฅ: " + errorMessage, Toast.LENGTH_SHORT).show();
}
}
}
// ์ฌ์ฉ
new WriteNFCTask(tag, dataToWrite).execute();
์์ฆ์ Kotlin Coroutine์ ๋ ๋ง์ด ์ฐ๊ธด ํ๋๋ฐ, AsyncTask๋ ์ฌ์ ํ ์ ์ฉํด์!
๊ธฐ๋ฅ๋ ์ค์ํ์ง๋ง ์ฌ์ฉ์ ๊ฒฝํ๋ ์์ฒญ ์ค์ํ์ฃ ! ๋ช ๊ฐ์ง UI/UX ํ์ ๋๋ฆด๊ฒ์ ๐
๐ก ์ฌ์ฉ์ ์นํ์ ์ธ UI ๋ง๋ค๊ธฐ
1. ์๊ฐ์ ํผ๋๋ฐฑ
ํ๊ทธ๋ฅผ ๊ฐ์งํ๋ฉด ์ง๋์ด๋ ์๋ฆฌ๋ก ์๋ ค์ฃผ์ธ์. ์ฌ์ฉ์๊ฐ "์ด? ๋๋?" ํ๋ ๋ถ์๊ฐ์ ์์จ ์ ์์ด์!
2. ๋ช
ํํ ์๋ด
"ํ๊ทธ๋ฅผ ํฐ ๋ท๋ฉด ์ค์์ 3์ด๊ฐ ๋์ฃผ์ธ์" ๊ฐ์ ๊ตฌ์ฒด์ ์ธ ์๋ด๋ฅผ ์ ๊ณตํ์ธ์.
3. ์ ๋๋ฉ์ด์
ํจ๊ณผ
NFC ๊ฐ์ง ์ค์ผ ๋ ๋ฌผ๊ฒฐ ์ ๋๋ฉ์ด์
๊ฐ์ ๊ฑธ ๋ณด์ฌ์ฃผ๋ฉด ๋ ์ง๊ด์ ์ด์์!
4. ์๋ฌ ๋ฉ์์ง ๊ฐ์
"์ค๋ฅ ๋ฐ์"๋ณด๋ค๋ "ํ๊ทธ๋ฅผ ์กฐ๊ธ ๋ ๊ฐ๊น์ด ๋์ฃผ์ธ์" ๊ฐ์ ์น์ ํ ๋ฉ์์ง๊ฐ ์ข์์.
5. ํ์คํ ๋ฆฌ ๊ธฐ๋ฅ
์ฝ์๋ ํ๊ทธ ๋ชฉ๋ก์ ๋ณด์ฌ์ฃผ๋ฉด ์ฌ์ฉ์๊ฐ ํธ๋ฆฌํ๊ฒ ๊ด๋ฆฌํ ์ ์์ด์!
์ง๋ ํผ๋๋ฐฑ ์ถ๊ฐํ๋ ์ฝ๋์์:
import android.os.Vibrator;
import android.content.Context;
private void provideHapticFeedback() {
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
if (vibrator != null && vibrator.hasVibrator()) {
// ์งง์ ์ง๋ (100ms)
vibrator.vibrate(100);
}
}
// NFC ํ๊ทธ ๊ฐ์ง ์ ํธ์ถ
private void handleIntent(Intent intent) {
String action = intent.getAction();
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action) ||
NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
// ์ง๋ ํผ๋๋ฐฑ
provideHapticFeedback();
// ์๋ฆฌ ํผ๋๋ฐฑ (์ ํ์ฌํญ)
playBeepSound();
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
// ... ๋๋จธ์ง ์ฒ๋ฆฌ
}
}
private void playBeepSound() {
// ToneGenerator๋ก ๋นํ์ ์ฌ์
ToneGenerator toneGen = new ToneGenerator(AudioManager.STREAM_MUSIC, 100);
toneGen.startTone(ToneGenerator.TONE_PROP_BEEP, 150);
}
์ฌ๋ฌ๋ถ์ด ์ด ๊ธ์ ๋ณด๊ณ NFC ๊ฐ๋ฐ ์ค๋ ฅ์ ์์๋ค๋ฉด, ์ฌ๋ฅ๋ท์์ ๋ค๋ฅธ ์ฌ๋๋ค๊ณผ ์ฌ๋ฅ์ ๊ณต์ ํด๋ณด๋ ๊ฑด ์ด๋จ๊น์?
์ฌ๋ฅ๋ท(https://www.jaenung.net)์ ๋ค์ํ IT ๊ฐ๋ฐ ์ฌ๋ฅ์ ๊ฑฐ๋ํ ์ ์๋ ํ๋ซํผ์ด์์! ๐ผ
์๋ฅผ ๋ค์ด:
โ
"NFC ์ฑ ๊ฐ๋ฐ ์ปจ์คํ
" ์ฌ๋ฅ ๋ฑ๋ก
โ
"์ค๋งํธ ๋ช
ํจ ์ฑ ์ ์" ์๋น์ค ์ ๊ณต
โ
"์ถ์
๊ด๋ฆฌ ์์คํ
๊ตฌ์ถ" ํ๋ก์ ํธ ์์ฃผ
โ
"NFC ํ๋ก๊ทธ๋๋ฐ ๊ณผ์ธ" ๊ฐ์
์ด๋ฐ ์์ผ๋ก ์ฌ๋ฌ๋ถ์ ์ ๋ฌธ์ฑ์ ํ์ฉํ ์ ์์ด์! ์ค์ ๋ก NFC ๊ธฐ์ ์ ์์๊ฐ ๊ฝค ์๋ ๋ถ์ผ๊ฑฐ๋ ์ ๐
๋ํ ์ฌ๋ฅ๋ท์ ์ง์์ธ์ ์ฒ ๊ฐ์ ๊ณณ์์ ์ฌ๋ฌ๋ถ์ ๋
ธํ์ฐ๋ฅผ ๊ณต์ ํ๋ฉด์ ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค๊ณผ ์ํตํ ์๋ ์๊ณ ์!
๋ ๊น์ด ๊ณต๋ถํ๊ณ ์ถ์ ๋ถ๋ค์ ์ํ ๋ฆฌ์์ค๋ค์ด์์!
๐ ๊ณต์ ๋ฌธ์
โข Android NFC ๊ฐ์ด๋: developer.android.com/guide/topics/connectivity/nfc
โข NFC Forum ์คํ: nfc-forum.org/our-work/specifications-and-application-documents
๐ ์จ๋ผ์ธ ๊ฐ์
โข Udemy: "Android NFC Development"
โข Coursera: "Mobile Application Development"
๐ป ์คํ์์ค ํ๋ก์ ํธ
โข GitHub์์ "android nfc" ๊ฒ์ํ๋ฉด ๋ค์ํ ์์ ํ๋ก์ ํธ๋ฅผ ์ฐพ์ ์ ์์ด์!
โข NFC Tools ์ฑ ์์ค์ฝ๋ ๋ถ์๋ ๋์์ด ๋ง์ด ๋ผ์.
๐ ์ปค๋ฎค๋ํฐ
โข Stack Overflow์ [android-nfc] ํ๊ทธ
โข Reddit์ r/androiddev
โข ํ๊ตญ ์๋๋ก์ด๋ ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ
์... ์ฌ๊ธฐ๊น์ง ์ ๋ง ๊ธด ์ฌ์ ์ด์๋ค์!ใ
ใ
ใ
๊ณ ์ ๋ง์ผ์
จ์ด์ ๐
์ค๋ ๋ค๋ฃฌ ๋ด์ฉ์ ์ ๋ฆฌํด๋ณผ๊ฒ์:
โ
NFC ๊ธฐ๋ณธ ๊ฐ๋
๊ณผ ๋์ ์๋ฆฌ
โ
์๋๋ก์ด๋ NFC ๊ฐ๋ฐ ํ๊ฒฝ ์ธํ
โ
NFC ํ๊ทธ ์ฝ๊ธฐ ๊ธฐ๋ฅ ๊ตฌํ
โ
NFC ํ๊ทธ ์ฐ๊ธฐ ๊ธฐ๋ฅ ๊ตฌํ
โ
๋ณด์ ๊ณ ๋ ค์ฌํญ๊ณผ ์ํธํ
โ
์ค์ ํ์ฉ ์์ ๋ค
โ
์๋ฌ ์ฒ๋ฆฌ์ ์ฑ๋ฅ ์ต์ ํ
โ
UI/UX ๊ฐ์ ๋ฐฉ๋ฒ
์ด ์ ๋๋ฉด ๊ธฐ๋ณธ์ ์ธ NFC ์ฑ์ ์ถฉ๋ถํ ๋ง๋ค ์ ์์ ๊ฑฐ์์! ๐ช
๋ฌผ๋ก ์ฒ์์ ์ด๋ ค์ธ ์ ์์ด์. ์ ๋ ์ฒ์ NFC ๊ฐ๋ฐํ ๋ ํ๊ทธ๊ฐ ์ ์ฝํ์ ๋ช ์๊ฐ ๋์ ์ฝ์งํ๋ ๊ธฐ์ต์ด ๋๋ค์ใ
ใ
ใ
ํ์ง๋ง ํฌ๊ธฐํ์ง ๋ง๊ณ ๊ณ์ ์๋ํด๋ณด์ธ์! ์ค์ ๊ธฐ๊ธฐ๋ก ํ
์คํธํ๋ฉด์ ํ๋ํ๋ ์ตํ๊ฐ๋ค ๋ณด๋ฉด ์ด๋์ ์ ๋ฌธ๊ฐ๊ฐ ๋์ด ์์ ๊ฑฐ์์ ๐
ํต์ฌ์ ์ค์ต์
๋๋ค! ์ด๋ก ๋ง ๋ณด์ง ๋ง๊ณ ์ง์ ์ฝ๋๋ฅผ ์์ฑํ๊ณ , ํ๊ทธ๋ฅผ ์ฝ๊ณ ์ฐ๋ฉด์ ๊ฒฝํ์ ์์ผ์ธ์.
๊ทธ๋ฆฌ๊ณ ๋งํ๋ ๋ถ๋ถ์ด ์์ผ๋ฉด ์ฃผ์ ํ์ง ๋ง๊ณ ์ปค๋ฎค๋ํฐ์ ์ง๋ฌธํ์ธ์. ๊ฐ๋ฐ์๋ค์ ์๋ก ๋๋ ๋ฌธํ๊ฐ ์์ผ๋๊น์! ๐ค
์ฌ๋ฌ๋ถ์ NFC ์ฑ ๊ฐ๋ฐ ์ฌ์ ์ ์ด ๊ธ์ด ๋์์ด ๋์์ผ๋ฉด ์ข๊ฒ ์ด์. ๋ฉ์ง ์ฑ ๋ง๋ค์ด์ ์ฌ๋ฅ๋ท์๋ ๊ณต์ ํด์ฃผ์๊ณ ์!
๊ทธ๋ผ ์ค๋๋ ์ฆ๊ฑฐ์ด ์ฝ๋ฉ ๋์ธ์! ํ์ดํ
! ๐โจ
๐ฏ Happy NFC Coding! ๐ฏ
์ง๋ฌธ์ด๋ ํผ๋๋ฐฑ์ ์ธ์ ๋ ํ์์ด์์! ๐
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.