์๋ ํ์ธ์ ์ ์ธ์ ๋๋ค :)
๊ฐ์ธ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉฐ ๊ธฐ์กด์ ๊ณต๋ถํ๋ ๊ธฐ์ ๋ค์ ํ๋์ฉ ์ ์ฉํด๋ณด๊ณ ์ถ๊ฐ๋ก ๊ณต๋ถํ๊ณ ์๋๋ฐ์!
์ด๋ฒ ๊ฒ์๊ธ์์๋ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉฐ ๊ฒช์ ๋ฌธ์ ๋ฅผ ํ๋ ๊ณต์ ํด๋ณด๋ ค๊ณ ํฉ๋๋ค.
์์ํ๊ธฐ ์ , ํด๋น ๋ฌธ์ ๋ ํด๋จผ์๋ฌ์ ์ํด ๋ฐ์ํ ๋ฌธ์ ์๊ธฐ ๋๋ฌธ์ ํด๊ฒฐ ๊ณผ์ ์์ ์ด๋ค ๋๋จํ ๊ธฐ์ ์ ๊ณต๋ถํ๊ณ ์ ์ฉํ ๊ฒฝํ์ ๊ณต์ ํ๊ธฐ ์ํ ๊ฒ์๊ธ์ด ์๋๋๋ค. ํ์ง๋ง, ์ค์๋ก ์๋ชป ์ ์ ์ฝ๋ ํ์ค์ด ์ผ๋ง๋ ํฐ ๊ฒฐ๊ณผ๋ฅผ ๋ถ๋ฌ์ผ์ผํฌ ์ ์๋์ง๋ฅผ ๊นจ๋ฌ์๋ ๊ฒฝํ์ด์๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ๊ณต์ ํ๊ณ ๋ ํด๊ฒฐ ๊ณผ์ ์์ ์ด๋ป๊ฒ ๋ฌธ์ ๋ถ๋ถ์ ์ฐพ์๋๋์ง ๋๋ฒ๊น ๋ฐฉ๋ฒ์ ๊ณต์ ํด๋ณด๋ ค๊ณ ํฉ๋๋ค!
์ฝ๋ ํ ์ค ์๋ชป์ ์๋ค๊ฐ DB๋ฅผ ๋ ๋ ค๋ฒ๋ ธ๋ค..!
๊ต์ฅํ ์๊ทน์ ์ธ ํ์ดํ.. ํ์ง๋ง ์คํ์ ๋๋ค๐ฅ
์ฌ๊ฑด์ ๋ฐ๋จ์ ํด๋ฆฐ ์ํคํ ์ฒ ๊ธฐ๋ฐ์ ํ๋ก์ ํธ์์ ๊ฐ ๋ ์ด์ด์ ์ญํ ์ ๋ช ํํ ํ๊ณ ๊ฐ์ฒด์ ์ฑ ์์ ๋ถ๋ฆฌํ์ฌ ์ฅ๊ธฐ์ ์ธ ๊ด์ ์์ ์ฝ๋ ์ ์ง๋ณด์์ฑ์ ๋์ด๊ธฐ ์ํด ์์ํ ๋คํธ์ํน ์ฝ๋ ์ ์ฒด ๋ฆฌํฉํ ๋ง์ด์์ต๋๋ค.
๊ธฐ์กด ๋คํธ์ํน ์ฝ๋ ์ ์ฒด๋ฅผ ๋ฆฌํฉํ ๋งํ๋ ๋๋ฆ ๋๊ท๋ชจ ๊ณต์ฌ์๊ธฐ ๋๋ฌธ์ ์ฝ๋ ์์ ์ด ์๊ธฐ๋ ํ์ผ์ด ๋ง๊ณ , ๋น์ทํ ๊ตฌ์กฐ์ ๋ฉ์๋๋ ๋ง์์ด์.
๊ทธ๋์ ์ฝ๋ ํ ์ค์ ์๋ชป ์ ๊ฒ ๋ฉ๋๋ค.. ๋ฌธ์ ๊ฐ ๋ ์ฝ๋์ ํด๊ฒฐ ํ์ ์ฝ๋๋ฅผ ํจ๊ป ๋ณด์์ ธ..
๋ฌธ์ ์ ์ฝ๋
// UserRepositoryImpl
func fetchUserData(userId: String) -> AnyPublisher<UserEntity, NetworkError> {
service.fetch(key: dbKey, path: userId)
.mapError { _ in NetworkError.serverError}
.flatMap { value in
if let value {
return Just(value)
.tryMap { try JSONSerialization.data(withJSONObject: $0) }
.decode(type: UserDTO.self, decoder: JSONDecoder())
.map { $0.toEntity() }
.mapError { _ in NetworkError.serverError }
.eraseToAnyPublisher()
} else {
return Empty().eraseToAnyPublisher()
}
}
.eraseToAnyPublisher()
}
์์ ํ ์ฝ๋
// UserRepositoryImpl
func fetchUserData(userId: String) -> AnyPublisher<UserEntity, NetworkError> {
service.fetch(key: dbKey, path: userId)
.mapError { _ in NetworkError.serverError}
.flatMap { value in
if let value {
return Just(value)
.tryMap { try JSONSerialization.data(withJSONObject: $0) }
.decode(type: UserDTO.self, decoder: JSONDecoder())
.map { $0.toEntity() }
.mapError { _ in NetworkError.serverError }
.eraseToAnyPublisher()
} else {
return Fail(error: NetworkError.invalidURL).eraseToAnyPublisher()
}
}
.eraseToAnyPublisher()
}
์์ ์ ํ ์ฝ๋์ ์ฐจ์ด๋ ์ฌ์ค ํ์ค ๋ฐ์ ๋์ง ์๋๋ฐ์,
์ฝ๋๋ฅผ ๊ฐ๋จํ๊ฒ ์ค๋ช ํด๋ณด์๋ฉด, ์ค์ ๋คํธ์ํน์ ์ํํ๋ ๊ฐ์ฒด์ธ service์ fetch ๋ฉ์๋๋ฅผ ์ด์ฉํด DB์ ์ ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ์ฝ๋์ ๋๋ค. fetch์ successํ๊ฒ ๋๋ฉด ๋ฐ์์ค๋ ๋ฐ์ดํฐ ํ์ ์ DTO?(์ต์ ๋) ํ์ ์ด์ง๋ง Entity๋ก ๋งคํ์ ํ๊ธฐ ์ํด์ if let์ ํตํด์ ์ต์ ๋ ๋ฐ์ธ๋ฉ์ ํ๊ฒ ๋ฉ๋๋ค. ์ด ๋ ๊ฐ์ด ์๋ค๋ฉด Entity๋ก ๋งคํํด Publish ํ๊ณ , ๊ฐ์ด nil์ด๋ผ๋ฉด fetch error์ ๊ฐ์ด ๊ฐ์ฃผํ์ฌ Fail์ ๋ฐฉ์ถํด์ค๋๋ค.
์๋์ ์๋๋ ์์ ๊ฐ์์ผ๋, ์์ ์ ์ฝ๋๋ฅผ ๋ณด๋ฉด if let else ๊ตฌ๋ฌธ์์ Fail์ด ์๋ Empty Publisher๋ฅผ ๋ฐฉ์ถํด์ฃผ๊ณ ์์ฃ .. ์์.. ์ค์๋ฅผ ํด๋ฒ๋ ธ์ต๋๋ค..
๋ฌธ์ ์ํฉ
์ฝ๋ ํ์ค์ ์ค์๊ฐ ๋ถ๋ฌ์จ ๋ฌธ์ ๋ ํ์ ํํด ๊ธฐ๋ฅ ํ ์คํธ ๊ณผ์ ์์ ๋ฐ์ํ์ต๋๋ค.
์์ ์ฝ๋ ์คํ ๊ฒฐ๊ณผ์ ์ํฅ์ ๋ฐ๊ฒ ๋๋ ์ฝ๋๋ฅผ ๋จผ์ ๋ณด์ฌ๋๋ฆฌ๊ฒ ์ต๋๋ค.
final class DeleteAccountUseCaseImpl: DeleteAccountUseCase {
private let userRepository: UserRepository
init(userRepository: UserRepository) {
self.userRepository = userRepository
}
func execute() -> AnyPublisher<Void, NetworkError> {
userRepository.deleteUserData(userId: UserDefaults.userId)
.mapError{ _ in
return .serverError
}
.eraseToAnyPublisher()
}
}
์ ์ ๊ฐ ๊ณ์ ์ ์ญ์ ํ๊ฒ ๋ ๊ฒฝ์ฐ, DB์ ์ ์ ๋ฐ์ดํฐ ๋ํ ์์ ํ๊ฒ ์ญ์ ํด์ฃผ๊ณ ์์ต๋๋ค.
์ด ๋, ์ญ์ ํ ์ ๋ณด์ ๊ฒฝ๋ก๋ https://luckyvicky์ด์ฉ๊ตฌ์ ์ฉ๊ตฌ/Users/์ ์ ์์ด๋ ์ ํํ์ ๋๋ค.
์ ์ ๊ฐ ํํดํ๊ธฐ ๋ฒํผ์ ๋๋ฅด๋ฉด deleteAccount ๋ก์ง์ ์คํํ๊ฒ ๋๊ณ , UserDefaults์ ์ ์ฅ๋์ด์๋ ์ ์ ์์ด๋ ๊ฐ์ ์ฐพ์์ ํด๋น ๊ฒฝ๋ก ํ์์ ์๋ ์ ์ ์ ๋ณด๋ฅผ ์ญ์ ํ๋ ๋ฐฉ์์ผ๋ก DB์ ์ ๋ณด๊ฐ ์ญ์ ๋ฉ๋๋ค.
๊ทธ๋ฐ๋ฐ, UserDefaults์ ๊ฐ์ ์์ fetchData์ ๊ฒฐ๊ณผ ๊ฐ์ ๋ฐ๋ผ ๋ณ๊ฒฝ๋ฉ๋๋ค.
extension LoginViewModel {
private func login(_ result: Result<ASAuthorization, Error>) {
...
useCase.executeSignIn(authorization, nonce: nonce)
.sink { [weak self] completion in
if case .failure(_) = completion {
self?.state.hasErrorOccurred = true
}
self?.state.isLoading = false
} receiveValue: { [weak self] user in
UserDefaults.userId = user.id
UserDefaults.usedCount = user.usedCount
UserDefaults.isFirstLaunch = false
self?.coordinator.present(sheet: .selectCharacter)
}.store(in: &cancellables)
...
}
}
์์ ์ฝ๋๋ ๋ก๊ทธ์ธ ๋ก์ง์ ์ผ๋ถ์ ๋๋ค. executeSignIn์ ํธ์ถํ๋ฉด authentication ์ํ์ ๋ฐ๋ผ ๋ก๊ทธ์ธ ๋๋ ํ์๊ฐ์ ์ ์ํค๊ณ ,
์์์ ๋ดค๋ fetchUserData ๋ฉ์๋๋ฅผ ํตํด ์ด๊ธฐ ์ ์ ์ ๋ณด๋ฅผ UserDefaults์ ์ ์ฅํฉ๋๋ค. (๋ก๊ทธ์ธ ์ฑ๊ณต ์)
์ฆ, ๋ก๊ทธ์ธ ๋ก์ง์ fetchUserData ๋ฉ์๋ ํธ์ถ์ด ์ฌ์ฉ๋๋ ๊ฒ์ด์ฃ .
์์ ์ฝ๋์์ receiveValue ๊ตฌ๋ฌธ์์๋ง UserDefaults์ ๊ฐ์ ์ธํ ํด์ฃผ๊ณ ์์ต๋๋ค.
์ด๋ ๋ก๊ทธ์ธ์ ์ฑ๊ณต ํ ๊ฒฝ์ฐ๋ง ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ์ดํ ๋ก์ง์ ์คํํ๋ค๋ ์๋ฏธ์ ๋๋ค.
๋ก๊ทธ์ธ ๊ณผ์ (authentication ์ฒดํฌ - ์ ํ์์ ๊ถํ ์์ - idToken๋ฐ๊ธ - ํ์๊ฐ์ /๋ก๊ทธ์ธ - DB๋ฐ์ดํฐ fetch) ์ค ํ๋๋ผ๋ ์คํจํ ๊ฒฝ์ฐ, ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๋์ , ์ ์ ๊ฐ ๋ก๊ทธ์ธ์ ์ฌ์๋ ํ ์ ์๋๋ก ์๋ด๋ฅผ ํด์ผ๊ฒ ์ฃ .
// UserRepositoryImpl
func fetchUserData(userId: String) -> AnyPublisher<UserEntity, NetworkError> {
service.fetch(key: dbKey, path: userId)
.mapError { _ in NetworkError.serverError}
.flatMap { value in
if let value {
return Just(value)
.tryMap { try JSONSerialization.data(withJSONObject: $0) }
.decode(type: UserDTO.self, decoder: JSONDecoder())
.map { $0.toEntity() }
.mapError { _ in NetworkError.serverError }
.eraseToAnyPublisher()
} else {
return Empty().eraseToAnyPublisher()
}
}
.eraseToAnyPublisher()
}
์์์ ์ค์ํ ์ฝ๋๋ฅผ ๋ค์ ๊ฐ์ ธ์๋ณผ๊ฒ์.
์ ๋ ๋ก๊ทธ์ธ ๊ณผ์ ์ค ํ๋์ธ fetchUserData์ ์คํจํ ๊ฒฝ์ฐ Error ๋ฅผ ๋ฐฉ์ถํ๋ ๊ฒ์ด ์๋ Empty๋ผ๋ ๋น ๊ฐ์ ๋ฐฉ์ถํด ์ฑ๊ณต ์ผ์ด์ค๋ก ๋๊ฒจ๋ฒ๋ฆฐ ๊ฒ์ ๋๋ค.
์ด๋ ๊ฒ ๋๋ฉด, UserDefaults์ UserId ๊ฐ์ด "" ์ด๋ฐ์์ผ๋ก ๋น ๊ฐ์ด ์ ์ฅ๋๊ณ , ์ด ํ ๊ณ์ ์ญ์ ๋ฅผ ํ๊ฒ ๋๋ฉด https://luckyvicky์ด์ฉ๊ตฌ์ ์ฉ๊ตฌ/Users/"" ์ด๋ ๊ฒ ์ ์ ์์ด๋ ๊ฐ์ด ๋ค์ด๊ฐ์ผ ํ ์๋ฆฌ์ ๋น ๊ฐ์ด ๋ค์ด๊ฐ๊ฒ ๋์ด Users ํ์์ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ์ญ์ ํด์ค! ํ๊ณ ์์ฒญํ๋ ํฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ฒ ๋๋ ๊ฒ์ด์ฃ ..
๋๋ฒ๊น ๋ฐฉ๋ฒ
์ผ๋จ ๋๋ฒ๊น ์ดํ ์ฐพ์ ๋ฌธ์ ์์ธ๋ถํฐ ์ค๋ช ๋๋ ธ๋๋ฐ์, ๊ทธ๋ฌ๋ฉด ์ด ๋ถ๋ถ์ด ๋ฌธ์ ๋ผ๋ ๊ฒ์ ์ด๋ป๊ฒ ์ฐพ์๋?
๋ฐ๋ก๋ฐ๋ก lldb๋ฅผ ์ด์ฉํ ๋นํ์๋ ๋๋ฒ๊น .. ์ด๋ผ๊ณ ํ ์ ์๊ฒ ์ต๋๋ค.
๋จผ์ , ์ด๋ ๊ฒ ์์ ์คํ ์์๋๋ก Breakpoint๋ฅผ ์ฐ์ด์ค๋๋ค.
๊ทธ๋ฌ๋ฉด, ์ด๋ฐ ์์ผ๋ก ์ค์ ๋ก ์คํ๋๋ ์์๋๋ก breakpoint์ ๊ฑธ๋ฆฌ๊ฒ ๋ฉ๋๋ค.
๋ง์ฝ, ์ฐ์ด๋์ breakpoint์ ๋ชจ๋ ๊ฑธ๋ฆฌ์ง ์๊ณ ์ข ๋ฃ๋๋ฉด, ์ค๊ฐ์ ํธ์ถ์ด ๋๊ธด ๊ฒ์ด๋, ๋๊ธด ์ง์ ์ ์ฐพ์์ ์ฝ๋๋ฅผ ํ์ธํด๋ณด๋ฉด ๋ฉ๋๋ค.
์ค๊ฐ์ค๊ฐ ์ค์ ๋ก ์ด๋ค ๋ฐ์ดํฐ๊ฐ ๋ด๊ธฐ๋? ํ์ธํด๋ณด๊ณ ์ถ๋ค๋ฉด lldb์ po ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ด๋ ๊ฒ breakpoint๊ฐ ๊ฑธ๋ฆฌ๋ ๋ผ์ธ์์ path์ ๋ด๊ธด ๋ฐ์ดํฐ๋ฅผ ํ์ธํ๊ธฐ ์ํด po path๋ฅผ ์ ๋ ฅํ๊ฒ ๋๋ฉด ๊ฐ์ด ๋๋ฒ๊น ์ฐฝ์ ๋์ค๊ฒ ๋ฉ๋๋ค.
์ ์ ๊ฒฝ์ฐ, ์ด ๊ณผ์ ์์ path์ ๊ฒฐ๊ณผ๊ฐ Users/ ๋ก ๋์ค๊ฒ ๋์ด์ ์์ ๊ฐ์ด ์๋์ค๋ค? UserId ๊ฐ์ด UserDefaults์ ์ ์ฅ๋๋ ๊ณผ์ ์ ๋๋ฒ๊น ํด๋ด์ผ์ง! ์ ํ๋ฆ์ผ๋ก ๋๋ฒ๊น ์ ํด์ ๋ฌธ์ ์์ธ์ ์ฐพ์ ์ ์์์ต๋๋ค.
DB ๋ณต๊ตฌ ๋ฐฉ๋ฒ
๊ทธ๋ ๋ค๋ฉด.. DB ๋ณต๊ตฌ๋ ์ด๋ป๊ฒ ํ์ จ์ด์??
์ ๋ํ ๋๋ต์.. ์๋์ผ๋ก ํ๊ธด ํ์ต๋๋ค.. ใ ใ
ํ์ง๋ง..! Json ํ์์ผ๋ก ๋น ๋ฅด๊ฒ ์์ฑํด์ DB์ ๋ฐ์ํด์ฃผ๊ธด ํ์ต๋๋ค ..
์ ๋ Firebase์ Realtime Database๋ฅผ ์ฌ์ฉํ๊ณ ์๋๋ฐ์(๋ฐ์ดํฐ๊ฐ id๊ฐ, ์ ์ ๋ณ ํ๋ฃจ ์ฌ์ฉํ์๋ก ๊ฐ๋จํ๊ธฐ ๋๋ฌธ์),
์์ ๋ก๊ทธ์ธ๋ FirebaseAuth๋ฅผ ์ด์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์ ๊ฐ์ฅ ์ค์ํ ์ ๋ณด์ธ UserId๊ฐ์ ์ฐพ์์ ๋ณต๊ตฌ ํ ์ ์์์ต๋๋ค.
Firebase์ Authentication ํญ์ผ๋ก ๊ฐ๋ฉด ์ด๋ ๊ฒ ๊ฐ์ ํ ์ ์ ์ ์ฌ์ฉ์ UID๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
์ด๋ฅผ ์ด์ฉํด์ DB๋ฅผ ๋ณต๊ตฌํ์ต๋๋ค. ์ ์ ์๋น์ค์ ๊ฒฝ์ฐ ํ๋ฃจ๊ฐ ์ง๋๋ฉด ์ด์ฐจํผ ์ด๊ธฐํ๋๋ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ์์๊ธฐ ๋๋ฌธ์ ํฐ ๋ฌธ์ ์์ด ๋ณต๊ตฌํ ์ ์๊ธด ํ์ต๋๋ค.. ํ์ง๋ง.. ์ค์ํ ๋ฐ์ดํฐ์๋ค๋ฉด.. ๋งค์ฐ ์์ฐ- ํ๋ค์.
๋๋์
๋จผ์ , ํ ์ค ์๋ชป ์ด ์ฝ๋๊ฐ ์ด๋ ๊ฒ ํฐ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค๋ ๊ฒ์ ๋ผ์ ๋ฆฌ๊ฒ ๋๊ผ์ต๋๋ค..
๋ ์ค์ํ ๊ฒ์ debug๋ชจ๋, release๋ชจ๋๋ฅผ ๋ถ๋ฆฌํด์ ์ค์ db๋ ํ ์คํธ ํ๊ฒฝ์์ ๋ ๋ฆฝ๋ ์ ์๋๋ก ํ๋ ๊ฒ์ด ํ์ํ๊ฒ ๊ตฌ๋๋ฅผ ๋๊ผ์ต๋๋ค.
(+ ํน์๋ ๋ชจ๋ฅผ ์ํฉ์ DB ๋ฐฑ์ ๋ ์ฃผ๊ธฐ์ ์ผ๋ก ํด์ผ๊ฒ ์ต๋๋ค. )
์ค๋๋ ๊ธด ๊ธ ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค๐
ํธ๋ฌ๋ธ์ํ ์ ์ฃผ์ธ๊ณต ๋ญํค๋นํค ์๋น์ค๋ ๋ค์ด๋ฐ์์ฃผ์ธ์~ใ