[Tistory] 01. OTOK 프로젝트 작업일지

원글 페이지 : 바로가기

반 년 정도 틈틈이 Swift 문법과 iOS 프로그래밍에 대해 공부했다. 슬슬 프로젝트를 한 번 시작해 볼까 해서 시작했던 “나쁜 일기장” 앱은 일단 보류가 되었다. 이유가 몇 가지 있는데 이 글의 주제와 어긋나니, 여기까지만 쓰겠다. 이번 토이 프로젝트는 지난 프로젝트에서 아쉬웠던 점을 보완하여 새로 진행해 보려 한다. 0. 강박처럼 따라다니는 “기본”에 대한 집착으로 RxSwift나 Alamofire 같은 기술들은 전혀 쓰지 않고 아주 레거시하게 만들었다. -> 이번에는 이것저것 원티드 공고글에 써져 있는 여러 기술들을 써보려 한다. 1. 디렉토리를 어떻게 정리해야 할지 몰라 프로젝트 내내 디렉토리가 변경되었다. -> 좀 더 디깅해보자. 이건 뭐 많이 실수해 봐야 될 것 같다. 아니면 어디 소속이 있어서 컨벤션 따라가거나 2. storyboard 기반 ui를 작성했다. -> 이게 잘못된 건 아니지만 그렇다고 잘된 것도 아닌 것 같다. SwiftUI도 써보고 싶지만 당장 할 게 너무 많기 때문에 우선은 UIKit 코드 기반으로 작성해 보려 한다. 나중에 리팩토링하자. 3. 이해하지 못하고 쓴 로직이 종종 있다. -> 이게 가장 큰 문제였다. 이번엔 이해가 안 가면 그 부분을 작성해두겠다. 4. 메모를 안 했다. -> 무슨 자신감으로 그랬을까? — 아무튼 이번 프로젝트의 가장 큰 목표는 “출시”가 아니고 “이해”와 “여러 기술들”이다. 어제 하루 종일 기획서를 만들었다. 내용은… 궁금하면 연락 주세요. 데옵&&인프라: 깃허브 액션, AWS, RDS 예정 디비: MySQL -> 지금 알았는데 내 노트북에는 디비버가 없고 워크벤치가 있다. 귀찮으니 MySQL로 간다. 백엔드: Node.js (express) -> TS 쓸까 하다가 그냥 JS로 후딱 쳐내고 iOS에 중점을 두려고 한다. 이 일지에는 앱 이야기만 쓸 거다. 또한 해결법은 링크를 첨부하거나, 간단히만 설명하고 넘기려 한다. — 일단 무지성으로 프로젝트를 생성했다. 타겟 지정: 처음에 뭐했지…? 아! 타겟 지정! 미니멈 타겟은 13으로 했다. 불만? 왜냐면 최신 RxSwift의 최소 호환 버전이 12인데 뭔가 찝찝해서 13으로 했다. 13이면 2019년 출시인데 이 정도면 되잖아요. 아 그리고 폰에서만 서비스하도록 했고 무.적.권 정방향으로만 쓰도록했다. 눕히지마라. storyboard 제거: 진짜 이거만 지우면 바로 터져버린다. https://storing.tistory.com/43 [Swift / Xcode] 프로젝트 Storyboard 삭제 세팅 – Code Base UI 프로젝트 진행 간 Storyboard를 제거하고 Code로만 UI를 구성해보게 되었는데,이를 위해 필요한 세팅을 기록해두고자 합니다. 1. Main Storyboard 삭제프로젝트에서 Storyboard를 제거하는 것이 목표이기 storing.tistory.com 디렉토리 구조 세팅: 이 부분에서 고민이 좀 되었다. 이번 프로젝트는 MVVM 패턴을 써보려 하기 때문에, 일단은 이 형태로 가져가기로 했다. 며칠 전 뵈었던 에이전시 대표님께서 애플은 개발자도 사용자로 보기 때문에 Swift 언어 자체가 디렉토리를 크게 신경 쓰지 않아도 되도록 설계되어 있다고 하셨다. 일단 이렇게 가져가고 혹여 바뀐다면 다시 쓰겠다. pod init: 지난 프로젝트는 완전 레거시였기 때문에 SPM, pod 자체를 안 썼지만 이번에는 pod를 써보려 한다. 우선 RxSwift 받고 워크스페이스 열어서 빌드해 보니 바로 에러가 터지는 게 아닌가? 이때부터 짜증이 좀 났다. rm -rf ~/Library/Developer/Xcode/DerivedData 안되면 일단 DerivedData부터 지워본다. <- 이거 국룰 아님? 근데 안되더라... Xcode에서 앱을 빌드할 때 파일 쓰기 권한 문제로 발생한 거란다. Build Settings/ENABLE_USER_SCRIPT_SANDBOXING을 No로 변경해준다. <- 이거 뭐냐면 스크립트 실행 시 사용자 권한을 제한하지 않겠다는 거다. https://stackoverflow.com/questions/76590131/error-while-build-ios-app-in-xcode-sandbox-rsync-samba-13105-deny1-file-w error while build iOS app in Xcode : Sandbox: rsync.samba (13105) deny(1) file-write-create, Flutter failed to write to a file while building iOS app on Xcode I got these 2 errors, I tried to build the iOS on visual studio code and I got the same errors. the operating system macOS 14.0 beta. processor M1 Pro Could downgra... stackoverflow.com Keychain: 내가 만드려는 앱은 반.드.시 무.적.권 로그인을 해야 사용할 수 있다. 그럼 로그인 유지를 해야 할 텐데 RN의 경우 스토리지에 리프레시 토큰을 암호화해서 넣어두는 방식으로 했었다. 근데 여기선 뭐 어떻게 해야 하는 건지 찾아보니 Keychain 또는 UserDefaults를 사용하라고 나오더라. CoreData도 나올 줄 알았는데 안 나오더라 Keychain: 민감한 데이터를 안전하게 저장할 수 있는 Apple의 암호화된 저장소, Keychain을 사용하면 앱을 껐다 켜도 데이터가 안전하게 남아 있다. UserDefaults: 설정 값을 저장하는 데 적합하지만, 민감한 정보를 저장하기에는 보안성이 낮다. 따라서, 민감한 데이터를 UserDefaults에 직접 저장하는 것은 피하는 것을 권장한다. 그러나, 암호화 라이브러리를 사용하여 데이터를 암호화한 후 UserDefaults에 저장할 수 있다. 아니 그럼 CoreData에도 암호화해서 넣으면 되는 거 아님? ㅇㅇ 맞대 나는 Keychain으로 간다. TODO: 이 코드 온전히 이해할 것 // // KeychainService.swift // iOS // // Created by 전율 on 7/14/24. // import Foundation import Security class KeychainService { static func save(key: String, data: Data) -> Bool {

// 키체인에 저장할 항목을 정의하는 쿼리
let query = [
kSecClass: kSecClassGenericPassword, // 저장할 데이터의 타입 (일반 비밀번호)
kSecAttrAccount: key, // key
kSecValueData: data // value
] as [String: Any]

SecItemDelete(query as CFDictionary) // key 값이 중복인지 확인 -> 중복일 경우 삭제

let status = SecItemAdd(query as CFDictionary, nil) // 키체인에 저장

return status == errSecSuccess // 저장이 성공하면 errSecSuccess를 반환, 실패하면 다른 상태 코드를 반환
}

// 키체인에서 데이터를 로드하는 함수
static func load(key: String) -> Data? {

// 키체인에서 데이터를 가져오기 위한 쿼리
let query = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key,
kSecReturnData: true, // 데이터를 반환하도록 설정
kSecMatchLimit: kSecMatchLimitOne // // 한 개의 항목만 반환하도록 설정
] as [String: Any]

var dataTypeRef: AnyObject? // 반환될 데이터를 저장할 함수
let status = SecItemCopyMatching(query as CFDictionary, &dataTypeRef) // 쿼리를 실행하여 키체인에서 데이터를 가져옴

// 성공 시 데이터, 실패 시 nil 반환
if status == errSecSuccess {
return dataTypeRef as? Data
} else {
return nil
}
}

// 키체인에서 데이터를 삭제하는 함수
static func delete(key: String) {
// 키체인에서 데이터를 삭제하기 위한 쿼리
let query = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key
] as [String: Any]

// 삭제
SecItemDelete(query as CFDictionary)
}
} SceneDelegate: 앱을 처음 켜면 뭐 해? 로그인을 하던지 || 이미 로그인되어 있다면 뭐든 하겠지. 그럼 상태에 따라 분기처리 해줘야겠지? 그걸 SceneDelegate에서 했다. 이거 뭐 iOS 13? 버전 위에서 생긴 개념인데 그 이하 버전에서는 앱 델리게이트에서 했다. 이건 연구 노트가 아니고 내 작업 일지니까 상세한 설명은 넘어간다. 궁금하면 내 깃에 practice_ios 부분 뒤져보면 나옴. 아무튼 키체인에서 리프레시 토큰을 가져와서 서버에 유효성 검사하고 반환값에 따라 루트를 바꿔주는 코드를 여기서 만들었다. class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)

checkToken { isValid in
DispatchQueue.main.async {
if isValid {
self.navigateToMainPage()
} else {
self.navigateToLoginPage()
self.showTokenExpiredAlert()
}
}
}

window?.makeKeyAndVisible()
}

func sceneDidDisconnect(_ scene: UIScene) {}

func sceneDidBecomeActive(_ scene: UIScene) {}

func sceneWillResignActive(_ scene: UIScene) {}

func sceneWillEnterForeground(_ scene: UIScene) {}

func sceneDidEnterBackground(_ scene: UIScene) {}

func checkToken(completion: @escaping (Bool) -> Void) {
if let loadedData = KeychainService.load(key: “refreshToken”), let token = String(data: loadedData, encoding: .utf8) {
validateToken(token) { isValid in
completion(isValid)
}
} else {
completion(false) // 토큰이 없는 경우
}
}

// TODO: 토큰 검증 로직, 네트워크로 빼기
func validateToken(_ token: String, completion: @escaping (Bool) -> Void) {
completion(false)
}

// 로그인 페이지
func navigateToLoginPage() {
let loginViewController = LoginViewController()
let navController = UINavigationController(rootViewController: loginViewController)
window?.rootViewController = navController
}

// 메인 페이지
func navigateToMainPage() {
let mainTabBarController = MainTabBarController()
window?.rootViewController = mainTabBarController
}

// 세션 만료 알림
func showTokenExpiredAlert() {
guard let window = window else { return }

let alert = UIAlertController(title: “세션 만료”, message: “세션이 만료되어 로그인 페이지로 이동합니다.”, preferredStyle: .alert)
let action = UIAlertAction(title: “확인”, style: .default) { [weak self] _ in
self?.navigateToLoginPage()
}
alert.addAction(action)
window.rootViewController?.present(alert, animated: true, completion: nil)
}
} 아, 만들었다 x 만드는 중이다. 회원가입 하면 검증 로직도 같이 짤 예정이다. 저기서 꼭 weak self 써야 하나? 어차피 레퍼런스 카운트 바로 내려가지 않나? 애매하잖아? 그럼 걍 안전빵으로 가셈ㅇㅇ kakao: 우선 나는 카카오의 여러 서비스 중 로그인만 쓸 것이기 때문에 pod ‘KakaoSDKUser’, pod ‘KakaoSDKAuth’만 썼다. 뭐 설정할 게 드럽게 많았다. RN 선녀였구나… 초기화, 스키마 등록 기타 등등 해줘야 한다. TODO: delegate들에 설정한 코드, URL Schemes에 대해 알아볼 것 https://velog.io/@mandos1995/iOS-%EC%B9%B4%EC%B9%B4%EC%98%A4-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0 iOS 카카오 로그인 구현하기 로그인하는 앱들중의 대부분은 소셜로그인 기능이 기본적으로 들어가고 있습니다.이번에 다룰 내용은 카카오톡 소셜 로그인 기능을 구현해보려 합니다.일단 카카오 로그인 기능은 Kakao Developers velog.io .xcconfig: 카카오 로그인을 구현해 보며, 아니 이렇게 키 값을 대놓고 올려도 되나? 의심이 들었다. RN에선 .env에 넣어서 관리했는데 여기선 뭐 어쩌라는 거지? 역시 방법은 다 있었다. https://velog.io/@cheshire0105/.xcconfig%EB%A1%9C-api-key%EB%A5%BC-%EC%88%A8%EA%B2%A8%EB%B3%B4%EC%9E%90 .xcconfig로 api key를 숨겨보자! 개요 프로젝트를 하던 중, 프로젝트를 GIT에 public으로 변경 하려고 한다. 그런데 한가지 걸리는 점이 있었다. 혼자 작업을 하는 프로젝트 였기 때문에 키를 공개 하고 비공개 하는 작업이 필요 없 velog.io 위 포스팅에서 살짝 다른 점이 있다면 나는 릴리즈용과 디버그용을 따로 만들어서 관리했고, Xcode 버전이 달라서인지 살짝 뭔가 달랐지만 딱 보면 눈치껏 할 수 있는 정도의 변화다. xcconfig는 깃 이그노어 ㄱ 왼쪽: 디렉토리, 오른쪽: 프로젝트/인포 왼쪽: 앱 델리게이트, 오른쪽: 피리스트 configurations 설정으로 모드에 따라 각 xcconfig 파일을 읽을 수 있고 xcconfig의 키 값을 plist에 명시해주고 불러다가 쓰는 그런 플로우 같다. 이건 뇌피셜이니까 다시 한 번 확인해보겠다. <- 이 글이 안 지워지면 이게 맞는 거임ㅇㅇ 오늘은 여기까지- 외에 백엔드 템플릿 만들고 디비랑 붙였고 테스트까지 했다. ---

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다