Conversation
개요기기 ID 관리 시스템을 구현하여 Keychain 저장소에 기기 식별자를 생성하고 유지하며, FCM 토큰 업로드 시 기기 ID를 포함하도록 변경하고 의존성 주입을 통해 통합했습니다. 변경 사항
시퀀스 다이어그램sequenceDiagram
participant AppDelegate as 앱 시작
participant KeychainStore as KeychainDeviceIDStore
participant Keychain as Keychain 저장소
participant FCMUpsert as FCM 토큰 업로드
participant DeviceProvider as DeviceIDProvider
AppDelegate->>KeychainStore: getOrCreate()
KeychainStore->>Keychain: 기기 ID 조회
alt 기기 ID 존재
Keychain-->>KeychainStore: 기기 ID 반환
else 기기 ID 미존재
KeychainStore->>KeychainStore: UUID 생성
KeychainStore->>Keychain: UUID 저장
Keychain-->>KeychainStore: 저장 완료
end
KeychainStore-->>AppDelegate: 기기 ID 발행
AppDelegate->>FCMUpsert: 토큰 업로드 요청
FCMUpsert->>DeviceProvider: deviceID 조회
DeviceProvider->>Keychain: 캐시된 기기 ID 로드
Keychain-->>DeviceProvider: 기기 ID 반환
DeviceProvider-->>FCMUpsert: 기기 ID 반환
FCMUpsert->>FCMUpsert: fcmToken + deviceId로 요청 생성
FCMUpsert-->>AppDelegate: 업로드 완료
예상 코드 리뷰 노력🎯 3 (중간) | ⏱️ ~25분
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
src/Projects/Booket/Sources/AppDelegate.swift (1)
37-50: DI 패턴 일관성 고려
KeychainDeviceIDStore.shared를 직접 사용하고 있습니다. 프로젝트의 다른 부분에서는@Autowired를 통한 의존성 주입 패턴을 사용하고 있는데, 여기서만 싱글톤을 직접 참조하고 있어 일관성이 떨어집니다.
StorageAssembly에서DeviceIDStore가 이미 등록되어 있으므로, DI를 통해 주입받는 것을 고려해보세요.참고: Keychain 데이터는 앱 삭제 시 기본적으로 함께 삭제됩니다. 재설치 후에도 유지하려면
kSecAttrAccessible설정이 필요합니다.src/Projects/BKData/Sources/Repository/DefaultNotificationRepository.swift (1)
22-26: 로깅 방식 통일 권장Line 23에서
Log.error를 사용하고 있는데, 같은 파일 Line 32에서는debugError(logger: AppLogger.network)를 사용하고 있습니다. 프로젝트의 다른 부분(예: AppDelegate)에서도AppLogger를 사용하고 있으니, 일관성을 위해AppLogger.storage를 사용하는 것이 좋겠습니다.또한
DomainError.unknown은 너무 일반적인 에러입니다. 가능하다면 더 구체적인 에러 타입을 고려해보세요.다음과 같이 수정할 수 있습니다:
- guard let deviceID = deviceIDProvider.deviceID else { - Log.error("Device ID not available", logger: AppLogger.storage) - return Fail(error: DomainError.unknown) - .eraseToAnyPublisher() - } + guard let deviceID = deviceIDProvider.deviceID else { + AppLogger.error("Device ID not available", logger: AppLogger.storage) + return Fail(error: DomainError.unknown) + .eraseToAnyPublisher() + }src/Projects/BKStorage/Sources/DeviceStorage/KeychainDeviceIDStore.swift (1)
20-20: TokenError 사용이 의미적으로 부자연스러움Device ID 관련 작업에
TokenError를 사용하는 것은 의미적으로 맞지 않습니다. Device ID는 토큰과는 다른 개념입니다.가능하다면
DeviceIDError같은 별도의 에러 타입을 정의하거나, 더 일반적인StorageError를 사용하는 것을 고려해보세요. 다만 프로토콜 시그니처가 이미 정의되어 있다면, 향후 리팩토링 시 고려사항으로 남겨두시면 됩니다.Also applies to: 40-40
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
src/Projects/BKData/Sources/API/UserAPI.swift(2 hunks)src/Projects/BKData/Sources/DTO/Request/UpsertFCMTokenRequestDTO.swift(1 hunks)src/Projects/BKData/Sources/DataAssembly.swift(1 hunks)src/Projects/BKData/Sources/Interface/Storage/DeviceIDProvider.swift(1 hunks)src/Projects/BKData/Sources/Interface/Storage/DeviceIDStore.swift(1 hunks)src/Projects/BKData/Sources/Repository/DefaultNotificationRepository.swift(1 hunks)src/Projects/BKStorage/Sources/Constant/StorageKeys.swift(1 hunks)src/Projects/BKStorage/Sources/DeviceStorage/KeychainDeviceIDProvider.swift(1 hunks)src/Projects/BKStorage/Sources/DeviceStorage/KeychainDeviceIDStore.swift(1 hunks)src/Projects/BKStorage/Sources/StorageAssembly.swift(1 hunks)src/Projects/Booket/Sources/AppDelegate.swift(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: doyeonk429
Repo: YAPP-Github/Reed-iOS PR: 163
File: src/Projects/BKData/Sources/DTO/Request/UserBookRegisterRequestDTO.swift:7-9
Timestamp: 2025-08-08T16:13:31.712Z
Learning: Reed-iOS에서 서버 스펙 변경으로 DTO 필드명이 바뀔 때, doyeonk429는 코드 전반의 파라미터/시그니처 리네이밍까지 확장하지 않고, 실제 요청 직렬화 키가 맞는지(예: isbn13)만 보장하면 충분하다는 범위 최소화 방식을 선호한다. (PR: YAPP-Github/Reed-iOS#163, 파일: BKData/Sources/DTO/Request/UserBookRegisterRequestDTO.swift)
Learnt from: doyeonk429
Repo: YAPP-Github/Reed-iOS PR: 163
File: src/Projects/BKData/Sources/DTO/Response/UserBookResponseDTO.swift:0-0
Timestamp: 2025-08-08T16:14:35.659Z
Learning: Reed-iOS: 서버/DTO 응답 키는 isbn13로 정규화되었음(사용자 doyeonk429 확인). Domain은 현재 BookInfo가 isbn을 사용 중이라 혼재 상태이며, 향후 isbn13으로 통일 리팩터 필요 가능성이 있음.
📚 Learning: 2025-08-08T16:13:31.712Z
Learnt from: doyeonk429
Repo: YAPP-Github/Reed-iOS PR: 163
File: src/Projects/BKData/Sources/DTO/Request/UserBookRegisterRequestDTO.swift:7-9
Timestamp: 2025-08-08T16:13:31.712Z
Learning: Reed-iOS에서 서버 스펙 변경으로 DTO 필드명이 바뀔 때, doyeonk429는 코드 전반의 파라미터/시그니처 리네이밍까지 확장하지 않고, 실제 요청 직렬화 키가 맞는지(예: isbn13)만 보장하면 충분하다는 범위 최소화 방식을 선호한다. (PR: YAPP-Github/Reed-iOS#163, 파일: BKData/Sources/DTO/Request/UserBookRegisterRequestDTO.swift)
Applied to files:
src/Projects/BKData/Sources/API/UserAPI.swift
🧬 Code graph analysis (7)
src/Projects/Booket/Sources/AppDelegate.swift (2)
src/Projects/BKStorage/Sources/DeviceStorage/KeychainDeviceIDStore.swift (1)
getOrCreate(20-38)src/Projects/BKCore/Sources/Logger/AppLogger.swift (1)
error(44-53)
src/Projects/BKData/Sources/Repository/DefaultNotificationRepository.swift (1)
src/Projects/BKCore/Sources/Logger/AppLogger.swift (1)
error(44-53)
src/Projects/BKStorage/Sources/StorageAssembly.swift (1)
src/Projects/BKCore/Sources/DiContainer/DIContainer.swift (1)
register(25-46)
src/Projects/BKData/Sources/Interface/Storage/DeviceIDProvider.swift (1)
src/Projects/BKStorage/Sources/DeviceStorage/KeychainDeviceIDProvider.swift (1)
clearCache(29-31)
src/Projects/BKStorage/Sources/DeviceStorage/KeychainDeviceIDProvider.swift (1)
src/Projects/BKCore/Sources/Logger/AppLogger.swift (1)
error(44-53)
src/Projects/BKData/Sources/API/UserAPI.swift (1)
src/Projects/BKData/Sources/Repository/DefaultNotificationRepository.swift (1)
upsertFCMToken(19-36)
src/Projects/BKData/Sources/Interface/Storage/DeviceIDStore.swift (1)
src/Projects/BKStorage/Sources/DeviceStorage/KeychainDeviceIDStore.swift (2)
getOrCreate(20-38)clear(40-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (8)
src/Projects/BKStorage/Sources/Constant/StorageKeys.swift (1)
8-8: LGTM!기존 키 상수들과 일관된 네이밍 패턴을 따르고 있습니다.
src/Projects/BKData/Sources/DTO/Request/UpsertFCMTokenRequestDTO.swift (1)
5-8: LGTM!서버 스펙 변경사항을 올바르게 반영하고 있으며, DTO 구조가 적절합니다.
src/Projects/BKData/Sources/DataAssembly.swift (1)
133-142: LGTM!
DeviceIDProvider를 DI를 통해 주입하는 방식이 기존 패턴과 일관되게 잘 구현되어 있습니다.src/Projects/BKData/Sources/Interface/Storage/DeviceIDProvider.swift (1)
3-6: LGTM!프로토콜 설계가 명확하고, 기존 Provider 패턴과 일관성 있게 구현되어 있습니다.
src/Projects/BKData/Sources/Interface/Storage/DeviceIDStore.swift (1)
6-9: LGTM!기존
TokenStore패턴과 일관된 인터페이스 설계입니다. Combine을 활용한 비동기 처리 방식도 적절합니다.src/Projects/BKData/Sources/Repository/DefaultNotificationRepository.swift (1)
11-17: LGTM!
DeviceIDProvider의존성 주입이 올바르게 구현되어 있습니다.src/Projects/BKStorage/Sources/StorageAssembly.swift (1)
63-80: LGTM!
DeviceIDProvider와DeviceIDStore의 등록이 기존TokenProvider/TokenStore및PushTokenProvider/PushTokenStore패턴과 일관되게 구현되어 있습니다. Provider는 싱글톤으로, Store는 기본 스코프로 등록하는 방식이 적절합니다.src/Projects/BKData/Sources/API/UserAPI.swift (1)
8-8: LGTM! FCM 서버 스펙 변경사항이 올바르게 반영됨
upsertFCMTokenAPI에deviceId파라미터가 깔끔하게 추가되었고, DTO 생성 로직도 일관되게 업데이트되었습니다.DefaultNotificationRepository에서 deviceID가 없을 때 적절히 가드 처리하고 있어 안전합니다.Based on learnings: 서버 스펙 변경 시 최소 범위로 변경하는 팀의 선호도에 부합합니다.
Also applies to: 54-55
| public final class KeychainDeviceIDProvider: DeviceIDProvider { | ||
| private let storage: KeyValueStorage | ||
| private var cachedDeviceID: String? | ||
|
|
||
| public init(storage: KeyValueStorage) { | ||
| self.storage = storage | ||
| } | ||
|
|
||
| public var deviceID: String? { | ||
| if let cachedDeviceID { | ||
| return cachedDeviceID | ||
| } | ||
| do { | ||
| let id: String = try storage.load(for: StorageKeys.deviceIDKey) | ||
| self.cachedDeviceID = id | ||
| return id | ||
| } catch { | ||
| Log.error("Failed to load deviceID: \(error)", logger: AppLogger.storage) | ||
| return nil | ||
| } | ||
| } |
There was a problem hiding this comment.
스레드 안전성 문제: 캐시 접근이 동기화되지 않음
cachedDeviceID는 여러 스레드에서 동시에 접근될 수 있지만 동기화 메커니즘이 없습니다. 특히 deviceID 프로퍼티에서 캐시를 읽고(line 16-18), 저장소에서 로드한 후 캐시에 쓰는(line 21) 작업이 원자적이지 않아 레이스 컨디션이 발생할 수 있습니다.
다음 중 하나의 방법으로 해결하세요:
방법 1: DispatchQueue를 사용한 동기화
public final class KeychainDeviceIDProvider: DeviceIDProvider {
private let storage: KeyValueStorage
private var cachedDeviceID: String?
+ private let queue = DispatchQueue(label: "com.booket.deviceIDProvider")
public init(storage: KeyValueStorage) {
self.storage = storage
}
public var deviceID: String? {
- if let cachedDeviceID {
- return cachedDeviceID
- }
- do {
- let id: String = try storage.load(for: StorageKeys.deviceIDKey)
- self.cachedDeviceID = id
- return id
- } catch {
- Log.error("Failed to load deviceID: \(error)", logger: AppLogger.storage)
- return nil
+ return queue.sync {
+ if let cachedDeviceID {
+ return cachedDeviceID
+ }
+ do {
+ let id: String = try storage.load(for: StorageKeys.deviceIDKey)
+ self.cachedDeviceID = id
+ return id
+ } catch {
+ Log.error("Failed to load deviceID: \(error)", logger: AppLogger.storage)
+ return nil
+ }
}
}
public func clearCache() {
- cachedDeviceID = nil
+ queue.sync {
+ cachedDeviceID = nil
+ }
}방법 2: actor 사용 (iOS 13+ 지원 시)
actor를 사용하면 자동으로 스레드 안전성이 보장되지만, deviceID가 동기 프로퍼티에서 비동기로 변경되어야 합니다.
Committable suggestion skipped: line range outside the PR's diff.
| } | ||
|
|
||
| /// 디바이스 ID를 가져오거나, 없으면 새로 생성하여 저장 | ||
| /// 키체인에 저장되므로 앱 삭제 후 재설치해도 유지됨 |
There was a problem hiding this comment.
부정확한 주석: 키체인의 앱 재설치 시 동작
주석에서 "앱 삭제 후 재설치해도 유지됨"이라고 명시되어 있으나, 기본 키체인 항목은 앱 삭제 시 함께 삭제됩니다. 키체인 데이터를 유지하려면 별도의 접근 제어 설정(예: kSecAttrAccessGroup)과 앱 그룹 구성이 필요합니다.
주석을 수정하여 실제 동작을 정확히 반영하세요:
- /// 디바이스 ID를 가져오거나, 없으면 새로 생성하여 저장
- /// 키체인에 저장되므로 앱 삭제 후 재설치해도 유지됨
+ /// 디바이스 ID를 가져오거나, 없으면 새로 생성하여 저장
+ /// 키체인에 저장되므로 앱 업데이트나 재시작 시에도 유지됨 (앱 삭제 시에는 제거됨)Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/Projects/BKStorage/Sources/DeviceStorage/KeychainDeviceIDStore.swift
around line 19, the comment stating "앱 삭제 후 재설치해도 유지됨" is inaccurate; update the
comment to explain that by default keychain items may be removed when an app is
uninstalled and that retaining keychain data across uninstall/reinstall requires
explicit configuration (e.g., using kSecAttrAccessGroup and configuring an App
Group/Keychain Access Group or other secure backup strategies). Replace the
current statement with a concise, accurate note about default behavior and the
steps/requirements needed to preserve keychain entries across app
reinstallations.
There was a problem hiding this comment.
kSecAttrAccessGroup는 다른 이야기를 하고 있는거같은데. 이건 공유를 위해서 쓰는 키고.
실제로 키체인은 앱 재설치 이후에도 남아있는걸로 알고 있는데 ? 다시 확인해볼 필요가 있을 듯.
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
iOS keychain persistence after app uninstall reinstall
💡 Result:
Short answer: By default, Keychain items generally survive an app uninstall and are available after reinstall — because keychain storage is kept by the OS, separate from the app sandbox. They are only removed if the device/keychain is wiped or if the items are explicitly deleted (or the keychain is reset). [1][2][3][4]
Important caveats:
- The app must use the same keychain access group / code‑signing team/entitlements; otherwise the new install won’t be able to read the old items. [2][3]
- iCloud Keychain sync or backups can affect which items persist/are restored. [3]
- If you need “fresh” installs to start clean, detect first-launch (NSUserDefaults cleared on uninstall) and explicitly remove keychain items on first run, or revoke/remove server tokens before uninstall. [2][4]
Sources: Apple Keychain docs and community/engineering reports. [1][2][3][4]
@clxxrlove 말씀하신 것이 맞습니다. 죄송합니다. 제가 잘못된 정보를 드렸네요.
확인 결과, iOS 키체인 아이템은 기본적으로 앱 삭제 후 재설치해도 유지됩니다. 키체인 스토리지는 앱 샌드박스가 아닌 OS 레벨에서 별도로 관리되기 때문입니다.
말씀하신 대로 kSecAttrAccessGroup은 앱 간 키체인 공유를 위한 것이고, 재설치 후 지속성과는 별개의 개념입니다.
따라서 현재 코드의 주석 **"키체인에 저장되므로 앱 삭제 후 재설치해도 유지됨"**은 정확합니다. 제 리뷰 코멘트가 부정확했습니다.
참고사항:
- 재설치 후 동일한 코드 서명/엔타이틀먼트를 사용해야 기존 키체인 아이템에 접근 가능합니다
- 디바이스를 초기화하거나 명시적으로 삭제하지 않는 한 키체인 데이터는 유지됩니다
지적해주셔서 감사합니다! 🙏
🧠 Learnings used
Learnt from: doyeonk429
Repo: YAPP-Github/Reed-iOS PR: 68
File: src/Projects/BKDesign/PreviewApp/Sources/View/BKButtonTestViewController.swift:0-0
Timestamp: 2025-07-10T08:21:44.204Z
Learning: doyeonk429는 BKDesign PreviewApp의 테스트 파일에서 주석 처리된 코드를 의도적으로 남겨두어 쉽게 테스트 구성을 전환할 수 있도록 구현한다. 이는 개발 편의성을 위한 것으로, 테스트 파일에서는 일반적인 "주석 처리된 코드 제거" 규칙을 적용하지 않는다.
Learnt from: doyeonk429
Repo: YAPP-Github/Reed-iOS PR: 163
File: src/Projects/BKData/Sources/DTO/Request/UserBookRegisterRequestDTO.swift:7-9
Timestamp: 2025-08-08T16:13:31.712Z
Learning: Reed-iOS에서 서버 스펙 변경으로 DTO 필드명이 바뀔 때, doyeonk429는 코드 전반의 파라미터/시그니처 리네이밍까지 확장하지 않고, 실제 요청 직렬화 키가 맞는지(예: isbn13)만 보장하면 충분하다는 범위 최소화 방식을 선호한다. (PR: YAPP-Github/Reed-iOS#163, 파일: BKData/Sources/DTO/Request/UserBookRegisterRequestDTO.swift)
Learnt from: doyeonk429
Repo: YAPP-Github/Reed-iOS PR: 163
File: src/Projects/BKData/Sources/DTO/Response/UserBookResponseDTO.swift:0-0
Timestamp: 2025-08-08T16:14:35.659Z
Learning: Reed-iOS: 서버/DTO 응답 키는 isbn13로 정규화되었음(사용자 doyeonk429 확인). Domain은 현재 BookInfo가 isbn을 사용 중이라 혼재 상태이며, 향후 isbn13으로 통일 리팩터 필요 가능성이 있음.
| public func getOrCreate() -> AnyPublisher<String, TokenError> { | ||
| do { | ||
| if let existingID: String = try? storage.load(for: StorageKeys.deviceIDKey) { | ||
| return Just(existingID) | ||
| .setFailureType(to: TokenError.self) | ||
| .eraseToAnyPublisher() | ||
| } | ||
|
|
||
| let newDeviceID = UUID().uuidString | ||
| try storage.save(newDeviceID, for: StorageKeys.deviceIDKey) | ||
|
|
||
| return Just(newDeviceID) | ||
| .setFailureType(to: TokenError.self) | ||
| .eraseToAnyPublisher() | ||
| } catch { | ||
| return Fail(error: TokenError.saveFailed(underlying: error)) | ||
| .eraseToAnyPublisher() | ||
| } | ||
| } |
There was a problem hiding this comment.
레이스 컨디션: getOrCreate()의 check-then-act 패턴이 원자적이지 않음
getOrCreate() 메서드에서 저장소 확인(line 22)과 생성/저장(lines 28-29)이 별도의 작업으로 분리되어 있어, 여러 스레드나 여러 호출이 동시에 실행되면 중복된 UUID가 생성될 수 있습니다. 특히 앱 초기화 시 여러 컴포넌트에서 동시에 호출될 가능성이 있습니다.
저장소 레이어에 동기화 메커니즘을 추가하거나, 이 메서드 자체를 동기화 처리하세요:
+import Foundation
+
public struct KeychainDeviceIDStore: DeviceIDStore {
private let storage: KeyValueStorage
+ private static let lock = NSLock()
/// AppDelegate나 다른 곳에서 DI 없이 사용할 수 있도록 shared instance 제공
public static let shared = KeychainDeviceIDStore(storage: KeychainKeyValueStorage())
public init(storage: KeyValueStorage) {
self.storage = storage
}
/// 디바이스 ID를 가져오거나, 없으면 새로 생성하여 저장
/// 키체인에 저장되므로 앱 삭제 후 재설치해도 유지됨
public func getOrCreate() -> AnyPublisher<String, TokenError> {
+ Self.lock.lock()
+ defer { Self.lock.unlock() }
+
do {
if let existingID: String = try? storage.load(for: StorageKeys.deviceIDKey) {
return Just(existingID)
.setFailureType(to: TokenError.self)
.eraseToAnyPublisher()
}
let newDeviceID = UUID().uuidString
try storage.save(newDeviceID, for: StorageKeys.deviceIDKey)
return Just(newDeviceID)
.setFailureType(to: TokenError.self)
.eraseToAnyPublisher()
} catch {
return Fail(error: TokenError.saveFailed(underlying: error))
.eraseToAnyPublisher()
}
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/Projects/BKStorage/Sources/DeviceStorage/KeychainDeviceIDStore.swift
around lines 20 to 38, the getOrCreate() method uses a non-atomic check-then-act
sequence which can produce duplicate UUIDs under concurrent calls; make the
operation atomic by serializing access or using a concurrency primitive: either
protect the entire get-or-create sequence with a dedicated serial DispatchQueue
or lock (or convert to a Swift actor) so only one caller can check and create at
a time, or alternatively attempt to write the new UUID and on write-failure
re-load the stored value and return it; update error handling to reflect the
chosen approach and ensure the returned publisher always contains the single
canonical device ID.
| let newDeviceID = UUID().uuidString | ||
| try storage.save(newDeviceID, for: StorageKeys.deviceIDKey) | ||
|
|
There was a problem hiding this comment.
실제 디바이스 아이디 UIDevice.current.identifierForVendor!.uuidString 이런식으로 구할 수 있는걸로 알고 있긴 한데, 이거랑 UUID랑 크게 역할이 다르지 않을까요?
There was a problem hiding this comment.
애초에 FCM 토큰 외에 deviceId를 별도로 두는 이유는 FCM은 앱 재설치 등의 변수를 통해 변할 수 있기 떄문입니다. 이러면 deviceId를 딱히 쓰는 의미는 없다고 생각하구요. (생명 주기가 같으므로)
말씀해주신 IDFV는 실제 디바이스 아이디는 아니구요, Team ID 단위로 부여되는 Identifier인데, FCM이랑 마찬가지로 앱 재설치하는 경우 변합니다.
UUID가 이런걸 대체해주기보다는, 정확히는 키체인을 믿고 설계한겁니다. 키체인은 앱 재설치/기기 초기화 등 변수에서도 남아있을 수 있어요. 초기화의 경우에는 리스토어를 해줘야 돌아오긴 하지만요.
추가로, 실제 디바이스 ID에 해당하는 IMEI나 MAC 같은 정말 PK가 될 수 있는 값들은 정책상 접근 금지이므로 이런 꼼수를 통해 deviceId를 공유한거에요.
나름 조금 알아보고 했는데, 혹시라도 제 의견이 틀렸거나 다른 의견이 있으시면 말씀해주세요. 개선 방향이 있는지 더 알아볼게요.
There was a problem hiding this comment.
아 네 저도 일단 IDFV 생각하고 말씀드린거긴 한데, 이게 앱 재설치 했을 때 값이 변할 수 있다는 사실을 제가 몰랐었네요! 지금 방식도 충분한데, 왜 안쓰셨는지 궁금해서 여쭤봤습니다! 👍
🔗 관련 이슈
📘 작업 유형
📙 작업 내역
🧪 테스트 내역
💬 추가 설명 or 리뷰 포인트 (선택)
Summary by CodeRabbit
새로운 기능