44. 로그인
로그인은 다음과 같은 시퀀스를 가집니다.
- 회원은 이메일, 비밀번호를 입력 후 ‘Login’을 누릅니다.
- my-auth 앱은 이메일과 비밀번호로 구글 로그인을 합니다.
- 구글 로그인을 성공하면 user객체를 받아 받은 user객체의 user.id로 my-auth 웹앱에 등록된 계정 정보를 가져옵니다.
- 이때 my-auth의 계정 검색은 구글에서 받은 user객체의 uid로 my-auth 계정을 찾는 것입니다. 구글 user객체의 user.id는 my-auth 웹앱 계정의 uids 배열 요소 중 하나입니다.
- my-auth 계정 정보를 받아 로그인 처리를 합니다.
- 로그인을 하면 app-bar의 우측에 로그아웃 아이콘이 나타납니다.
- 로그인 상태에서 브라우저를 떠나도 다시 사이트에 접속하면 자동 로그인이 됩니다.
UI
1. 개발을 위한 몇가지 질문
ChatGPT에게 물었습니다.
1. firestore 컬렉션의 배열 필드의 값중 하나가 일치하는 문서 가져오는 방법은?
질문의 의도는 구글 로그인을 성공하면 user객체를 받아 받은 user객체의 user.id로 my-auth 웹앱에 등록된 계정 정보를 가져와야 합니다. 로그인 후 구글로부터 받은 user 객체의 uid로 my-auth 웹앱에 등록된 계정 정보를 가져와야 합니다. 이것을 구현하기 위한 질문입니다.
ChatGPT
Firestore에서 컬렉션의 배열 필드 내의 특정 값을 일치시키는 문서를 가져오려면 array-contains 쿼리를 사용할 수 있습니다. 이 쿼리를 통해 배열 필드에 특정 값이 포함된 문서를 쉽게 찾을 수 있습니다.
예제 코드
다음은 Firestore에서 uids라는 배열 필드에 특정 값이 포함된 문서를 가져오는 방법을 보여주는 예제입니다:
Firestore 설정
먼저 Firebase를 설정합니다:
// firebase.js
import firebase from 'firebase/app';
import 'firebase/firestore';
const firebaseConfig = {
apiKey: 'your-api-key',
authDomain: 'your-auth-domain',
projectId: 'your-project-id',
storageBucket: 'your-storage-bucket',
messagingSenderId: 'your-messaging-sender-id',
appId: 'your-app-id'
};
firebase.initializeApp(firebaseConfig);
const db = firebase.firestore();
export { db };
Firestore 쿼리
uids 배열 필드에 특정 값이 포함된 문서를 가져오는 쿼리 예제:
// fetchData.js
import { db } from './firebase';
async function fetchDocumentsWithUid(uid) {
try {
const querySnapshot = await db.collection('users').where('uids', 'array-contains', uid).get();
const documents = querySnapshot.docs.map(doc => doc.data());
console.log('Matching documents:', documents);
return documents;
} catch (error) {
console.error('Error fetching documents:', error);
}
}
// Example usage
fetchDocumentsWithUid('specific-uid');
설명
1. Firebase 초기화:
firebase.js 파일에서 Firebase 앱을 초기화하고 Firestore 인스턴스를 설정합니다.
2. Firestore 쿼리:
- fetchDocumentsWithUid 함수에서 Firestore 컬렉션 users를 쿼리합니다.
- where 메서드의 array-contains 연산자를 사용하여 uids 배열 필드에 특정 uid 값을 포함하는 문서를 필터링합니다.
- 쿼리 결과를 querySnapshot으로 받아와서, 각 문서의 데이터를 documents 배열로 매핑합니다.
3. 함수 호출:
- fetchDocumentsWithUid 함수를 호출하면서 찾고자 하는 uid 값을 전달합니다.
이 예제는 Firestore에서 배열 필드 내의 특정 값을 일치시키는 문서를 가져오는 기본적인 방법을 보여줍니다. 이를 통해 특정 조건을 만족하는 문서를 쉽게 검색할 수 있습니다.
2. firestore 컬렉션의 where 사용법?
Firestore Collection에서 where 조건문을 이용하여 데이터를 가져옵니다. where 조건문이 궁금합니다.
ChatGPT
Firestore의 where 메서드는 특정 필드에 대해 조건을 걸어 문서를 필터링하는 데 사용됩니다. where 메서드는 여러 조건을 지원하며, 각 조건은 필드 이름, 연산자, 값의 세 가지 인수를 필요로 합니다. 다음은 where 메서드를 사용하는 방법과 예제들을 소개합니다.
기본 사용법
db.collection('collectionName').where('fieldName', 'operator', 'value').get();
연산자
- ==: 값이 일치하는 문서
- !=: 값이 일치하지 않는 문서
- >: 값이 보다 큰 문서
- <: 값이 보다 작은 문서
- >=: 값이 크거나 같은 문서
- <=: 값이 작거나 같은 문서
- array-contains: 배열 필드에 특정 값을 포함하는 문서
- array-contains-any: 배열 필드에 특정 값 중 하나라도 포함하는 문서
- in: 특정 값 중 하나를 가지는 문서
- not-in: 특정 값 중 하나를 가지지 않는 문서
예제
1. 특정 필드가 특정 값과 일치하는 문서 조회
const querySnapshot = await db.collection('users').where('age', '==', 25).get();
querySnapshot.forEach(doc => {
console.log(doc.id, ' => ', doc.data());
});
2. 특정 필드가 특정 값보다 큰 문서 조회
const querySnapshot = await db.collection('users').where('age', '>', 25).get();
querySnapshot.forEach(doc => {
console.log(doc.id, ' => ', doc.data());
});
3. 배열 필드에 특정 값을 포함하는 문서 조회
const querySnapshot = await db.collection('users').where('uids', 'array-contains', 'specific-uid').get();
querySnapshot.forEach(doc => {
console.log(doc.id, ' => ', doc.data());
});
4. 배열 필드에 특정 값 중 하나라도 포함하는 문서 조회
const querySnapshot = await db.collection('users').where('uids', 'array-contains-any', ['uid1', 'uid2']).get();
querySnapshot.forEach(doc => {
console.log(doc.id, ' => ', doc.data());
});
5. 특정 필드가 여러 값 중 하나를 가지는 문서 조회
const querySnapshot = await db.collection('users').where('status', 'in', ['active', 'pending']).get();
querySnapshot.forEach(doc => {
console.log(doc.id, ' => ', doc.data());
});
전체 예제 코드
다음은 users 컬렉션의 uids 배열 필드에 특정 값이 포함된 문서를 조회하는 예제입니다.
firebase.js
// firebase.js
import firebase from 'firebase/app';
import 'firebase/firestore';
const firebaseConfig = {
apiKey: 'your-api-key',
authDomain: 'your-auth-domain',
projectId: 'your-project-id',
storageBucket: 'your-storage-bucket',
messagingSenderId: 'your-messaging-sender-id',
appId: 'your-app-id'
};
firebase.initializeApp(firebaseConfig);
const db = firebase.firestore();
export { db };
fetchData.js
// fetchData.js
import { db } from './firebase';
async function fetchDocumentsWithUid(uid) {
try {
const querySnapshot = await db.collection('users').where('uids', 'array-contains', uid).get();
if (querySnapshot.empty) {
console.log('No matching documents.');
return [];
}
const documents = querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
console.log('Matching documents:', documents);
return documents;
} catch (error) {
console.error('Error fetching documents:', error);
}
}
// Example usage
fetchDocumentsWithUid('specific-uid');
데이터 삽입 예제
데이터를 Firestore에 삽입하여 쿼리 결과를 확인할 수 있도록 합니다.
addUser.js
// addUser.js
import { db } from './firebase';
async function addUser() {
try {
await db.collection('users').add({
uids: ['specific-uid', 'another-uid'],
name: 'Test User',
email: 'test@example.com'
});
console.log('User added');
} catch (error) {
console.error('Error adding user:', error);
}
}
// Example usage
addUser();
이 예제들을 통해 Firestore에서 where 메서드를 사용하는 방법을 이해하고, 다양한 조건을 기반으로 문서를 필터링할 수 있습니다.
2. ChatGPT가 시키는 대로 했습니다.
async function fetchDocumentsWithUid(uid)을 async fetchUserWithUid({ dispatch }, {uid})로 변경하여 저장후 실행했습니다.
로그인이 되었습니다.
3. Source Code
src/store/index.js
// src/store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import { db, auth, googleProvider } from '@/firebase';
import router from '@/router'; // Vue Router import
Vue.use(Vuex);
export default new Vuex.Store({
state: {
user: null,
users: [],
isLoading: false,
error: null
},
mutations: {
setUser(state, user) {
state.user = user;
},
setUsers(state, users) {
state.users = users;
state.isLoading = false;
},
setError(state, error) {
state.error = error;
state.isLoading = false;
},
setLoading(state, isLoading) {
state.isLoading = isLoading;
}
},
actions: {
async fetchUsers({ commit }) {
commit('setLoading', true);
try {
const usersSnapshot = await db.collection('users').get();
const users = usersSnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
//const user1 = users[0];
//console.log(user1.user.name);
//console.log(user1.user.email);
commit('setUsers', users);
} catch (error) {
commit('setError', error.message);
}
},
async addUser({ dispatch }, user) {
try {
await db.collection('users').add(user);
dispatch('fetchUsers');
} catch (error) {
console.error('Error adding user:', error);
}
},
async deleteUser({ dispatch }, userId) {
try {
await db.collection('users').doc(userId).delete();
dispatch('fetchUsers');
} catch (error) {
console.error('Error deleting user:', error);
}
},
async updateUser({ dispatch }, { userId, user }) {
try {
await db.collection('users').doc(userId).update(user);
dispatch('fetchUsers');
} catch (error) {
console.error('Error updating user:', error);
}
},
async login({ commit, dispatch }, { email, password }) {
try {
const { user } = await auth.signInWithEmailAndPassword(email, password);
//console.log(email, password);
//console.log(user.uid);
// 웹앱의 계정 정보를 가져와 로그인 설정을 한다.
dispatch('fetchUserWithUid', {uid: user.uid});
} catch (error) {
commit('setError', error.message);
}
},
async fetchUserWithUid({ commit }, {uid}) {
try {
const querySnapshot = await db.collection('users').where('uids', 'array-contains', uid).get();
const user = querySnapshot.docs.map(doc => doc.data());
console.log('Matching user:', user[0]);
// 로그인 설정을 한다.
commit('setUser', user[0]);
router.push("/"); // home으로
//return user;
} catch (error) {
console.error('Error fetching documents:', error);
}
},
// async login({ commit, getters }, { email, password }) {
// try {
// const { user } = await auth.signInWithEmailAndPassword(email, password);
// try {
// const myUser = getters.getUserByUid(user.uid);
// console.log(myUser);
// if (!myUser) {
// commit('setUser', myUser);
// console.log(myUser);
// router.push("/"); // home으로
// } else {
// console.log('User already exists');
// }
// } catch (error) {
// console.error('Error adding user:', error);
// }
// } catch (error) {
// commit('setError', error.message);
// }
// },
async register({ commit, dispatch }, { email, password, name }) {
try {
const { user } = await auth.createUserWithEmailAndPassword(email, password, name);
// 웹앱에 계정을 만든다.
const newUser = {
email: email,
name: name,
uids: [user.uid]
};
// newUser 객체가 추가됨
// dispatch('addUser', { user: newUser });
// newUser 계정 정보가 추가됨
dispatch('addUser', newUser);
// 로그인 설정
commit('setUser', newUser);
router.push("/"); // home으로
} catch (error) {
console.log(error);
commit('setError', error.message);
}
},
async googleLogin({ commit, getters }) {
try {
const { user } = await auth.signInWithPopup(googleProvider);
try {
const myUser = getters.getUserByUid(user.uid);
if (!myUser) {
commit('setUser', myUser);
console.log(myUser);
router.push("/"); // home으로
} else {
console.log('User already exists');
}
} catch (error) {
console.error('Error adding user:', error);
}
} catch (error) {
commit('setError', error.message);
}
},
async logout({ commit }) {
await auth.signOut();
commit('setUser', null);
},
setUser({ commit }, user) {
commit('setUser', user);
}
},
getters: {
user: state => state.user,
users: state => state.users,
isLoading: state => state.isLoading,
error: state => state.error,
getUserByUid: (state) => (uid) => {
return state.users.find(user => user.uids && user.uids.includes(uid));
}
}
});
src/views/Login.vue
<!-- src/views/Login.vue -->
<template>
<v-container>
<v-row>
<v-col cols="12" class="text-center my-5">
<h3>로그인</h3>
</v-col>
</v-row>
<v-row>
<v-col class="text-center" cols="10" offset="1" sm="8" offset-sm="2">
<v-form @submit.prevent="loginUser">
<v-text-field v-model="email" label="이메일" type="email" required></v-text-field>
<v-text-field v-model="password" label="비밀번호" type="password" required></v-text-field>
<v-btn type="submit" color="primary">Login</v-btn>
<v-alert v-if="error" type="error" dismissible>{{ error }}</v-alert>
</v-form>
</v-col>
</v-row>
<v-row>
<v-col class="text-center" cols="10" offset="1" sm="8" offset-sm="2">
<v-btn color="red" @click="googleLogin" dark>
<v-icon left>mdi-google</v-icon>
Sign in with Google
</v-btn>
</v-col>
</v-row>
</v-container>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
export default {
name: 'Login',
data() {
return {
email: '',
password: ''
};
},
computed: {
...mapGetters(['error'])
},
methods: {
...mapActions(['login']),
...mapActions(['googleLogin']),
async loginUser() {
await this.login({ email: this.email, password: this.password });
}
}
};
</script>
'PWA' 카테고리의 다른 글
사용자 인증 - Google 계정 연결 (0) | 2024.08.07 |
---|---|
사용자 인증 - 자동 로그인 (0) | 2024.08.06 |
사용자 인증 - 계정 만들기 (0) | 2024.08.03 |
사용자 인증 - 프로젝트 (0) | 2024.08.03 |
사용자 인증 - UI : Vuetify Component (0) | 2024.08.01 |