Capacitor 커뮤니티 contacts 대안이 필요하다면, 가장 완벽한 드롭인 교체 방식은 @capgo/capacitor-contacts입니다. 이 플러그인은 @capacitor-community/contacts 기능(연락처 읽기, 생성, 삭제 및 선택)을 모두 지원하며, ID 변경 없이 연락처를 업데이트하는 기능, 페이지네이션, 그룹, 계정, 네이티브 연락처 화면, 그리고 2027년 Google Play 연락처 정책에 중요한 Android 권한 없는 연락처 선택 기능까지 추가되었습니다. 이 가이드는 두 플러그인을 비교하고 모든 호출 방식을 매핑하여 오후 한나절 만에 마이그레이션을 마칠 수 있도록 도와줍니다.
왜 대안을 찾아야 할까요?
@capacitor-community/contacts 는 지속적으로 관리되고 있으며 Capacitor 8 버전이 출시되었습니다. 단순한 읽기 및 생성 흐름에는 좋은 선택입니다. 하지만 팀이 성장함에 따라 보통 다음과 같은 이유로 한계를 느끼게 됩니다:
- 업데이트 메서드 부재. 연락처를 생성하고 삭제할 수는 있지만, 수정할 수는 없습니다. 삭제 후 다시 생성하면 연락처 ID가 바뀌어 기존에 저장된 참조가 깨지고, 플러그인이 읽지 못하는 데이터가 손실될 수 있습니다.
- 페이지네이션 미지원.
getContacts는 한 번의 호출로 전체 주소록을 가져옵니다. 연락처와 사진이 5,000개 있는 기기에서는 속도가 느리고 메모리 사용량이 매우 높습니다. - Android에서 선택하려면 권한이 필요합니다. Android는
pickContact피커를 열기 전에 연락처 권한 그룹을 요청한 뒤 주소록에서 연락처를 읽어옵니다. Google Play 연락처 권한 정책에 따라Android 17을 타겟팅하는 앱은 핵심 기능을 위해서만READ_CONTACTS권한을 유지할 수 있습니다. - 그룹, 계정 또는 네이티브 화면은 지원하지 않습니다. 그룹을 나열하거나, 새 연락처가 저장될 계정을 선택하거나, 시스템 “새 연락처” 양식을 열 수 없습니다.
기능 비교
| 기능 | @capacitor-community/contacts 8.0 | @capgo/capacitor-contacts 8.1 |
|---|---|---|
| 연락처 하나 또는 전체 읽기 | getContact, getContacts |
getContactById, getContacts |
| 필드 선택 | projection 객체 |
fields 배열 |
| 페이지네이션 | 아니요 | limit, offset |
| 연락처 개수 세기 | 아니요 | countContacts |
| 생성 | createContact |
createContact |
| 기존 항목 업데이트 | 아니요 | updateContactById |
| 삭제 | deleteContact |
deleteContactById |
| 연락처 하나 선택 | pickContact |
pickContact |
| 여러 개 선택 | 아니요 | pickContacts({ multiple: true }) |
| 권한 없이 전화번호/이메일/주소 하나 선택 | 아니요 | pickContacts({ property }) |
| 그룹 | 아니요 | getGroups, getGroupById, createGroup, deleteGroupById |
| 계정 | 아니요 | getAccounts |
| 네이티브 생성 / 조회 / 편집 화면 | 아니요 | displayCreateContact, displayContactById, displayUpdateContactById |
| 읽기 및 쓰기 권한 상태 분리 | 아니요 (단일 별칭) | readContacts, writeContacts |
| iOS 제한적 접근 상태 | limited |
limited |
| 설정 열기 | 아니요 | openSettings |
설치
bun add @capgo/capacitor-contacts
bunx cap sync
이 플러그인은 Android manifest에 권한을 자동으로 추가하지 않습니다. 사용하는 권한만 직접 추가하세요:
<!-- Needed for getContacts, getContactById, countContacts, groups, accounts -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<!-- Needed for createContact, updateContactById, deleteContactById, groups writes -->
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
iOS의 경우, 다음을 추가하세요 NSContactsUsageDescription 대상 Info.plist 주소록을 읽거나 쓰는 경우에만 필요합니다. 연락처 선택(Picking) 시에는 필요하지 않습니다.
단계별 마이그레이션 가이드
권한
// Before
import { Contacts } from '@capacitor-community/contacts';
const perm = await Contacts.requestPermissions();
if (perm.contacts !== 'granted') return;
// After
import { CapacitorContacts } from '@capgo/capacitor-contacts';
const status = await CapacitorContacts.requestPermissions({ permissions: ['readContacts'] });
if (status.readContacts !== 'granted' && status.readContacts !== 'limited') {
await CapacitorContacts.openSettings();
return;
}
iOS 18 이상에서는 사용자가 연락처의 일부에만 접근 권한을 부여할 수 있습니다. 플러그인은 이를 다음과 같이 보고합니다: limited. 이를 당연하게 여기고 공유된 연락처만 기대하세요.
모든 연락처 읽기
// Before: whole address book, projection object
const { contacts } = await Contacts.getContacts({
projection: { name: true, phones: true, emails: true },
});
const first = contacts[0];
console.log(first.name?.display, first.phones?.[0]?.number);
// After: fields array, paginated
const pageSize = 200;
let offset = 0;
for (;;) {
const { contacts } = await CapacitorContacts.getContacts({
fields: ['displayName', 'givenName', 'familyName', 'phoneNumbers', 'emailAddresses'],
limit: pageSize,
offset,
});
render(contacts);
if (contacts.length < pageSize) break;
offset += pageSize;
}
declare function render(list: unknown[]): void;
사용 countContacts() 진행 표시줄을 보여주고 싶다면 먼저 실행하세요.
연락처 하나 읽기
// Before
const { contact } = await Contacts.getContact({
contactId: id,
projection: { name: true, phones: true },
});
// After
const { contact } = await CapacitorContacts.getContactById({
id,
fields: ['displayName', 'phoneNumbers'],
});
if (!contact) console.log('Not found');
연락처 생성
// Before
const { contactId } = await Contacts.createContact({
contact: {
name: { given: 'Ada', family: 'Lovelace' },
phones: [{ type: PhoneType.Mobile, number: '+44 20 7946 0000' }],
emails: [{ type: EmailType.Work, address: 'ada@example.com' }],
},
});
// After
const { id } = await CapacitorContacts.createContact({
contact: {
givenName: 'Ada',
familyName: 'Lovelace',
phoneNumbers: [{ type: 'MOBILE', value: '+44 20 7946 0000' }],
emailAddresses: [{ type: 'WORK', value: 'ada@example.com' }],
},
});
displayName Capgo 플러그인에서는 읽기 전용입니다. 쓰기 givenName 및 familyName, 하면 디바이스가 표시 이름을 포맷합니다.
연락처 업데이트 (신규)
await CapacitorContacts.updateContactById({
id,
contact: {
givenName: 'Ada',
familyName: 'King',
jobTitle: 'Analyst',
phoneNumbers: [{ type: 'MOBILE', value: '+44 20 7946 0000' }],
},
});
ID는 그대로 유지되므로 저장된 참조가 여전히 유효합니다. 실수로 데이터를 지우지 않도록 먼저 연락처를 읽은 뒤 유지할 필드들을 다시 보내세요.
사용자가 직접 편집하게 하려면 시스템 에디터를 여세요:
await CapacitorContacts.displayUpdateContactById({ id });
연락처 삭제
// Before
await Contacts.deleteContact({ contactId: id });
// After
await CapacitorContacts.deleteContactById({ id });
연락처 선택
// Before: requests the contacts permission on Android first
const { contact } = await Contacts.pickContact({
projection: { name: true, phones: true },
});
// After: one phone number, no permission on Android or iOS
import { ContactProperty } from '@capgo/capacitor-contacts';
const { contacts } = await CapacitorContacts.pickContacts({
property: ContactProperty.PhoneNumber,
});
const phone = contacts[0]?.phoneNumbers?.[0]?.value;
피커에서 전체 연락처 레코드가 필요한 경우 다음을 호출하세요 pickContact() 없이 property. Android에서는 다음 권한이 부여된 경우에만 READ_CONTACTS 전체 연락처를 반환합니다.
필드 매핑
| Community 페이로드 | Capgo Contact |
|---|---|
contactId |
id |
name.display |
displayName (읽기 전용) |
name.given / middle / family |
givenName / middleName / familyName |
name.prefix / suffix |
namePrefix / nameSuffix |
organization.company |
organizationName |
organization.jobTitle |
jobTitle |
birthday { day, month, year } |
birthday { day, month, year } |
note |
note |
phones[].number |
phoneNumbers[].value |
emails[].address |
emailAddresses[].value |
urls[] (문자열) |
urlAddresses[].value |
postalAddresses[].region / postcode |
postalAddresses[].state / postalCode |
image.base64String |
photo (base64 문자열) |
값의 타입도 변경됩니다. Capgo 플러그인은 다음과 같이 대문자 문자열을 사용합니다: 'MOBILE', 'HOME', 'WORK' 전화번호 및 이메일의 경우.
한 번에 하나의 화면씩 마이그레이션하세요
두 플러그인은 서로 다른 네이티브 이름으로 등록되어 있어 (Contacts 및 CapacitorContacts), 호출부를 옮기는 동안 함께 설치할 수 있습니다. 간단한 어댑터를 사용하면 앱의 나머지 부분은 변경할 필요가 없습니다:
import { CapacitorContacts } from '@capgo/capacitor-contacts';
export interface AppContact {
id: string;
name: string;
phones: string[];
}
export async function listContacts(limit = 200, offset = 0): Promise<AppContact[]> {
const { contacts } = await CapacitorContacts.getContacts({
fields: ['displayName', 'phoneNumbers'],
limit,
offset,
});
return contacts.map((c) => ({
id: c.id ?? '',
name: c.displayName ?? '',
phones: (c.phoneNumbers ?? []).map((p) => p.value),
}));
}
마지막 호출부까지 모두 옮겼다면, 커뮤니티 플러그인을 제거하고 bunx cap sync를 실행한 뒤, 병합된 Android manifest를 확인하여 필요한 권한만 남아 있는지 확인하세요.
문제 해결
getContacts iOS에서는 빈 리스트를 반환합니다. 사용자가 제한된 액세스 권한을 부여하여 연락처를 공유하지 않았거나, 액세스를 거부했습니다. readContacts 를 확인하고 openSettings().
Android의 특정 계정에서 생성된 연락처가 표시되지 않는 문제. 다음 항목을 전달하세요: account ( getAccounts())를
사진 때문에 목록 로딩이 느려집니다. 요청하지 마세요 photo 목록 뷰에서는. 다음을 사용해 로드하세요 getContactById 사용자가 연락처를 열 때.
Play Console 경고 READ_CONTACTS. 초대 및 공유 흐름을 pickContacts({ property }) 로 옮기고, 다른 곳에서 필요하지 않다면 권한을 제거하세요.
배포하기
플러그인 교체는 네이티브 변경이므로 스토어 빌드가 필요합니다. Capgo 빌드 클라우드에서 iOS 및 Android 빌드를 생성할 수 있습니다. 그 이후 연락처 화면 수정은 JavaScript이므로 함께 배포할 수 있습니다 Capgo Live Update 사용하기. 전체 API은 다음에 있습니다 contacts 플러그인 문서.