반응형
Swift UI - 4. 로그인 UI 화면 구현
이전에 구현한 회원가입으로 회원이 등록되었다(DRF로 회원가입 API를 구현) 이제 등록된 회원으로 로그인을 진행하고 키체인을 이용해서 Access Token 및 Refresh Token을 저장하여 앱 사용 중 로그인 상태를 유지할 수 있도록 해보자.
//
// LoginView.swift
// mbtiPlayground
//
// Created by 박종후 on 2022/03/16.
//
import SwiftUI
struct LoginView: View {
@State private var email: String = ""
@State private var password: String = ""
@State private var loginStatus: Bool = false // TODO env 변수로 선언 후 토큰 계속 확인
@State private var userAccessToken: String = ""
var body: some View {
NavigationView{
if self.loginStatus != false {
ProfileDetail(userEmail: self.email, userAccessToken: self.userAccessToken)
} else {
VStack{
Text("Sign In")
.font(.title)
.fontWeight(.medium)
.padding()
.foregroundColor(.black)
HStack{
Image(systemName: "person.fill")
.resizable()
.scaledToFit()
.frame(width: 30, height: 30)
.padding(.bottom)
TextField("닉네임을 입력하세요.", text: $email)
.frame(width: 300, height: 10)
.padding()
.background(Color(.systemGray6))
.cornerRadius(5.0)
.padding(.bottom, 20)
}
HStack{
Image(systemName: "lock")
.resizable()
.scaledToFit()
.frame(width: 30, height: 30)
.padding(.bottom)
SecureField("비밀번호를 입력하세요", text: $password)
.frame(width: 300, height: 10)
.padding()
.background(Color(.systemGray6))
.cornerRadius(5.0)
.padding(.bottom, 20)
}
HStack{
Button(action: {
print(self.email + self.password)
let rft = readItemKeyChain(userId: self.email)
if rft != nil {
UserDefaults.standard.set(rft, forKey: self.email)
}else{
sendPostRequest("<http://localhost:8000/auth/login>", parameters: ["username": self.email, "password": self.password]){
responseObject, error in guard let _ = responseObject, error == nil else {
print(error ?? "Unknown error")
return
}
self.loginStatus = true
if let rftToken = responseObject{
let rft = rftToken["refresh"] as? String
self.userAccessToken = rftToken["access"] as? String ?? ""
setItemKeyChain(userId: self.email, rft: rft!)
UserDefaults.standard.set(rft, forKey: self.email)
}
}
}
}){
Text("로그인")
.frame(width: 80, height: 10)
.font(.headline)
.foregroundColor(.white)
.padding()
.background(Color(.systemBlue))
.cornerRadius(10)
}
.padding()
NavigationLink(destination: SignUpView()){
Text("회원가입")
.frame(width: 80, height: 10)
.font(.headline)
.foregroundColor(.white)
.padding()
.background(Color(.systemBlue))
.cornerRadius(10)
}
}
}
.padding(.all, 30)
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
}
}
}
}
struct LoginView_Previews: PreviewProvider {
static var previews: some View {
LoginView()
}
}
우선 21번 째 줄에서 유저의 로그인 상태에 따라 프로필 상태(로그인 후 유저의 정보를 보여줄 페이지)를 보여줄지 아니면 로그인 화면을 보여줄지에 대해 조건문을 넣었다.
UI에서는 닉네임과 비밀번호를 입력하여 로그인하는 화면을 구현하였다. (로그인 시 이메일 또는 닉네임을 사용하도록 구현할 수 있는데, 현재 DRF에서는 닉네임만 사용하여 로그인하도록 설정한 상태)
여기서 로그인 성공 후 토큰을 발급받는 과정에서 리프레시 토큰을 키체인에 저장하고 나중에 유저가 다시 접속했을 때에도 다시 로그인 하지 않도록 구현하였다.
setItemKeyChain(userId: self.email, rft: rft!)
UserDefaults.standard.set(rft, forKey: self.email)
setItemKeyChain 함수는 로그인한 유저의 리프레시 토큰 정보를 저장하기 위해 작성하였다.
func setItemKeyChain(userId: String, rft: String){
let previousQuery: [CFString: Any] = [kSecClass: kSecClassIdentity, kSecAttrAccount: userId]
let updateQuery: [CFString: Any] = [kSecValueData: rft]
let status = SecItemUpdate(previousQuery as CFDictionary, updateQuery as CFDictionary)
if status == errSecSuccess {
print("Update!")
} else {
print("Update fail")
}
}
추가적으로 유저의 데이터를 저장하기 위해 UserDefaults 도 사용하였다. UserDefaults 는 Redis 사용과 유사하다
위 두 코드를 통해 사용자가 로그인 후 발급받은 액세스 토큰 및 리프레시 토큰을 사용자의 기기에 저장하여 로그아웃 없이 계속 사용할 수 있다.
반응형
'IOS(Swift)' 카테고리의 다른 글
[IOS/Swift] MBTI 카드 선택 게임 UI 구현 (0) | 2022.05.22 |
---|---|
[IOS/Swift] 콘텐츠 검색 앱 구현 - 3. 회원가입 UI 구현 (0) | 2022.04.04 |
[IOS/Swift] 콘텐츠 검색 앱 구현 - 2. 채팅방 UI 구현 (0) | 2022.03.05 |
[IOS/Swift] 콘텐츠 검색 앱 구현 - 1. 콘텐츠 UI 구현 (0) | 2022.03.05 |