Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
Tags
- defaultContentConfiguration
- RxCocoa
- .filled
- cellForRowAt
- SWIFT
- 라이징캠프
- RequestInterceptor
- distinctUntilChanged
- swift6
- interceptor
- Moya
- iOS교육
- flatmap
- ReactiveX
- UICollectionViewListCell
- Xcode
- uibutton.configuration
- shareextension
- 컴공선배
- IOS
- Swift5
- alamofire
- observe(on:)
- QoS
- UIListContentConfiguration
- customButton
- ContentMode
- UIKit
- 개발블로그
- RxSwift
Archives
- Today
- Total
RB의 iOS 개발 이야기
Share Extension - Keychain Sharing 문제를 해결하다! 본문
현재 진행중인 프로젝트 BESTWISH에서 Share Extension 기능을 사용하여,
외부 플랫폼의 상품 정보를 베스트위시 앱에 저장하는 기능을 구현했습니다.
기능을 구현하면서 겪었던 Keychain Sharing 관련 내용을 공유하고자 포스팅을 작성하게 됐습니다.
1. 문제 상황
기능 요구사항은 아래와 같습니다.
- 유저가 사파리나 다른 쇼핑 앱에서 상품 링크를 공유하면
- Share Extension이 실행되어 상품 정보를 추출하고
- 이 데이터를 Supabase 서버에 저장해 Product 테이블에 상품이 추가되어야 하는 상황
문제는 여기서 발생..!
메인 앱에서는 로그인된 사용자 정보(Session)를 Keychain에 저장해두었는데,
Share Extension에서 Supabase에 저장하려면 같은 Session 정보가 필요했습니다.
하지만 Extension에서 Session을 불러오지 못해 인증 에러가 발생했고, 상품이 저장되지 않는 상황이었습니다.
2. 원인 분석
문제를 파고들어 보니 iOS에서 Extension과 메인 앱(BestWish)은 독립된 모듈이라는 점이 핵심 원인이었습니다.
- 메인 앱(BestWish)에서 로그인 시 Keychain에 토큰을 저장하고 있었지만
- 별도의 Target인 Share Extension은 Keychain을 기본적으로 공유하지 않음
- 결국 Session 정보를 Extension이 읽지 못했고, Supabase 인증에 실패하고 있던 것
결국 근본 원인은 Keychain Sharing 설정 누락이었습니다.
3. 해결 과정
1. Keychain Sharing 활성화
Xcode에서
- TARGETS → Signing & Capabilities → Keychain Sharing 추가
- 메인 앱(BestWish)과 Extension(ShareExtension) 둘 다 같은 Keychain Group Name으로 설정
2. Info.plist & Config.xcconfig 정리
- Info.plist와 Config.xcconfig 파일까지 작성할 필요는 없지만(공유되어도 문제되지 않는 내용)
- 기존에 작성되어 있던 APIKey, SupabaseUrl과 같이 동일한 Bundle 코드 작성을 위해
- Info.plist에 Sharing Key 항목 추가
- 환경별로 공유 설정이 꼬이지 않도록, Config.xcconfig에 SHARING_KEY를 관리해서 빌드 설정에 반영
3. KeyChainManagerImpl 클래스 코드 수정
- 토큰 저장/읽기/삭제 기능이 구현되어 있는 KeyChainManagerImpl 클래스에서
- 기존에는 kSecAttrAccessGroup을 안 썼다면 이제는 공유 Sharing Key로 접근하도록 수정.
4. Bundle 기반으로 접근
- 다중 Target이기 때문에 하드코딩 대신 Bundle → Info.plist → Sharing Key로 접근하도록 구성해 관리성을 높임.
extension Bundle {
var sharing: String {
guard let value = infoDictionary?["SHARING_KEY"] as? String else {
fatalError("❌ Info.plist에 SHARING_KEY가 없습니다.")
}
return value
}
}
struct Token {
let service: TokenType
let value: String?
let account: String = "current_user"
let sharing: String = Bundle.main.sharing
var data: Data? {
if let value {
return value.data(using: .utf8)
}
return nil
}
init(service: TokenType, value: String? = nil) {
self.service = service
self.value = value
}
}
/// 토큰 저장
private func save(token: Token) async {
let baseQuery: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: token.service.type,
kSecAttrAccount: token.account,
kSecAttrAccessGroup: token.sharing
]
SecItemDelete(baseQuery as CFDictionary)
guard let data = token.data else { return }
var addQuery = baseQuery
addQuery[kSecValueData] = data
SecItemAdd(addQuery as CFDictionary, nil)
let eData = String(data: data, encoding: .utf8) ?? ""
}
4. 적용 팁
- Keychain Sharing은 필수 조건
- Extension ↔︎ 메인 앱 간 Session/Token 공유라면 무조건 활성화 상태여야 함
- Sharing Key를 Info.plist에 관리하면 환경별로 팀원이 헷갈리지 않고 유지보수가 편리
5. 마무리 & 배운 점
이번 트러블슈팅으로 배운 점
- Extension은 별도 앱처럼 동작 → 인증/데이터 공유는 반드시 Keychain Sharing으로 관리 필요
- Keychain을 통한 Session 공유 과정으로 iOS 환경의 보안성을 다시 한번 느낄 수 있었음
- Share Extension이 별도의 Target으로 동작한다는 내용이 미숙한 상태에서 개발을 진행하다보니 미처 예상하지 못했던 트러블 슈팅이 발생했음
- 앞으로 별도의 Target 간의 공유가 필요한 내용을 개발할 때에는 이번 경험으로 빠른 대처와 개발을 진행할 수 있을 것 같음
읽어주셔서 감사합니다!
'iOS > Xcode' 카테고리의 다른 글
Xcode에서 메모리 누수를 확인하는 방법(?)! (0) | 2024.01.31 |
---|---|
Xcode의 영역들과 Interface Builder에 대해서 알아보자. (0) | 2023.08.27 |