1. 기능구현 - 추천사 컴포넌트 firebase 소셜로그인, crud
1.1 배포 버젼의 주소를 firebase에 인증 제공업체 도메인 추가하기 - 해결
로컬에선 깃허브 소셜 로그인이 잘 동작하다가 임시 배포 버젼에서 로그인을 시도할 경우 배포된 내 앱의 주소가 firebase에 인증 제공 업체의 도메인으로 추가되어 있지 않아 이를 추가해주어야만 로그인 관련 작성한 코드를 실행시킬 수 있다.
파이어베이스 콘솔 → 내 앱 → 인증 → Settings → 승인된 도메인 (이 곳에 내 앱의 주소를 추가해주면 된다.)
1.2 새로고침 시 로그인 유지하기 - 임시 해결
참고자료: https://prohannah.tistory.com/139
- (해결)현재 로그인 여부의 state 값의 변경되는 경우의 로직 보완 필요
- 현재 코드로는 로그인 실패의 경우에도 로그인 상태로 바뀔 것으로 예상됨.
- (해결)새로고침 시 로그인 유지 화면 미반영
- 새로고침 시 리덕스가 초기화되어 파이어베이스에선 로그인된 상태를 유지하나 화면으로는 로그인이 되지 않은 초기의 상태로 돌아가 보이는 것으로 예상됨.
리덕스는 새로고침되면 초기화되므로 만약 로그인 후에 페이지를 새로고침한다면 최초에 로그인 시 리덕스에 저장된 로그인 정보는 초기화되므로 파이어베이스상으로는 로그인 되어 있을 지라도 리덕스는 초기화되어 유저의 정보가 없다.
이를 해결하기위해서 인증 상태 지속성 옵션을 변경해보았으나 의도대로 동작하지 않았다. 구글링 해보니 세션스토리지에 로그인되었음을 저장하는 방법도 있었다.
하지만 리덕스나 세션 스토리지에 저장된 로그인 정보는 로그인 당시에 저장된 정보이고, 현재 파이어베이스 상에서 로그인 상태인지 아닌지 확인이 어려운데, 우선 내 계정으로 로그인했을 땐 브라우저를 새로고침하더라도 파이어베이스 콘솔을 보니 로그인이 유지되고 있는것으로 보인다.
정정: 파이어베이스 콘솔의 Authentication → Users 탭에서 로그인한 사람은 확인이 되나 로그아웃 했을때 Users 탭에서 사라지지는 않는걸로 보인다. 회원 탈퇴 기능을 만들어서 실행시켜야 사라지는 걸까?
// 현재 파일: client/src/components/testimonials/Auth.js
// * 로그인 상태 state
const [isLoggedin, setIsLoggedIn] = useState(false);
// * 로그인한 유저의 정보 state
// * 로그인버튼
const handleGithubSignIn = async () => {
await dispatch(githubSignInInitiate());
return setIsLoggedIn(true);
};
파이어베이스 상으로는 로그인이 유지되고 있지만 보여지는 브라우저 화면에서는 리덕스가 초기화되어서 화면 상에선 로그인이 풀린 것으로 보여지는 것 같다.
현재 내 코드는 단순히 로그인 여부의 state는 로그인 버튼 클릭 시 리덕스의 action으로 정의한 로그인 기능의 미들웨어 함수를 실행시키고 결과를 받지는 않고 단순히 결과가 나온 후 로그인 상태를 true로만 변경시키고 있다.
그러니 새로고침하면 리덕스도 초기화되고 state도 당연히 초기화되니 화면상으로는 로그인이 풀려 보일 것이고 아마 로그인이 실패하더라도 로그인 상태로 state가 바뀔 것으로 예상한다.
임시 해결: 로컬 스토리지 사용
파이어베이스에서 제공하는 indexedDB를 사용하면 로컬 스토리지나 세션 스토리지보다 다양한 기능을 제공하기 때문에 사용하는게 좋겠지만 아직 내용이 어려워서 우선 로컬스토리지로 새로고침시에도 로그인 상태가 유지되도록 했다.
// client/src/components/testimonials/Auth.js
import React, { useState, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import styled from 'styled-components';
import { AiFillGithub } from 'react-icons/ai';
import { githubSignInInitiate } from '../../redux/actions';
import Logout from './routes/Logout';
// import { getAuth, onAuthStateChanged } from 'firebase/auth'; // 추가
import { authService } from '../../firebase';
function Auth() {
const dispatch = useDispatch();
// * 로그인 상태 state
const [isLoggedin, setIsLoggedIn] = useState(false);
useEffect(() => {
authService.onAuthStateChanged(user => {
console.log(user);
if (user) {
const loginStatus = 'true';
localStorage.setItem('loginStatus', loginStatus);
setIsLoggedIn(true);
} else {
const loginStatus = 'false';
localStorage.setItem('loginStatus', loginStatus);
setIsLoggedIn(false);
}
});
}, []);
// * 로그인버튼
const handleGithubSignIn = async () => {
dispatch(githubSignInInitiate());
console.log(isLoggedin); // 불필요한 코드인데 린트 에러때문에 임시로 넣음
};
return (
<AuthWrapper>
{localStorage.getItem('loginStatus') === 'true' ? (
<>
<div>
{localStorage.getItem('displayName')}
<div>
<img src={localStorage.getItem('photoURL')} alt="profile" />
</div>
</div>
<Logout setIsLoggedIn={setIsLoggedIn} />
</>
) : (
<SignUpArea>
<LoginButton name="github">
<ButtonTextWrapper>
<AiFillGithub />
<span onClick={handleGithubSignIn}>깃허브 로그인</span>
</ButtonTextWrapper>
</LoginButton>
</SignUpArea>
)}
</AuthWrapper>
);
}
export default Auth;
- isLoggedIn 변수를 사용하지 않고 있다고 eslint 경고가 나오고 있어 배포가 되지 않아 임시로 콘솔로그로 찍어서 사용했다. 의존성 배열에 넣으니 기능이 의도대로 동작하지 않았음.
- useEffect 훅 내부의 파이어베이스 onAuthChanged 메서드 콜백함수에서 실행되는 콘솔로그가 최초 렌더링시에도 일단 2번 찍히고 로그인/로그아웃 시에도 2번씩 찍힌다. 어디서 잡아야 될 지 공부가 좀 필요한데 아직은 잘 모르겠다.
// client/src/redux/actions.js
// * 로그아웃 버튼 클릭시 실행
export const logoutInitiate = () => {
return async function (dispatch) {
dispatch(logoutStart);
const auth = getAuth();
await signOut(auth)
.then(res => {
// Sign-out successful.
const loginStatus = false;
localStorage.setItem('loginStatus', loginStatus);
return dispatch(logoutSuccess());
})
.catch(error => {
// An error happened.
return dispatch(logoutFail(error.message));
});
};
};
// * 깃허브 로그인 버튼 클릭시 실행
export const githubSignInInitiate = () => {
return async function (dispatch) {
dispatch(githubSignInStart);
const auth = getAuth();
await setPersistence(auth, browserLocalPersistence)
.then(() => {
return signInWithPopup(authService, provider);
})
.then(({ user }) => {
if (user) {
// console.log(user);
// 값 들어온다. 콘솔에서 확인할 것. 여기서 받은 사용자의 데이터를 리덕스로 상태관리하자.
// 변수로 뽑아서 값을 저장한 다음에 db로 보내야 하나?
const displayName = user.displayName;
const photoURL = user.photoURL;
const loginStatus = true;
localStorage.setItem('displayName', displayName);
localStorage.setItem('photoURL', photoURL);
localStorage.setItem('loginStatus', loginStatus);
return dispatch(githubSignInSuccess(user));
}
})
.catch(error => dispatch(githubSignInFail(error.message)));
};
};
- 로컬스토리지에 유저의 이름과 깃허브 프로필 사진 image path를 저장해서 로그인 성공 시 화면에 보여줄 값에서 사용하게 했다.
- Auth.js를 지금처럼 컴포넌트로 사용하는게 맞는지 확신이 안 선다.
// client/src/components/testimonials/routes/Logout.js
import React from 'react';
import styled from 'styled-components';
import { useDispatch } from 'react-redux';
import { logoutInitiate } from '../../../redux/actions';
function Logout({ setIsLoggedIn }) {
// const { currentUser } = useSelector(state => state.user);
const dispatch = useDispatch();
const handleAuth = async () => {
await dispatch(logoutInitiate());
return setIsLoggedIn(false);
};
return <LogoutButton onClick={handleAuth}>Logout</LogoutButton>;
}
export default Logout;
const LogoutButton = styled.div`
cursor: pointer;
`;
- 여기서 state를 변경시키고 있는게 현재 Auth.js의 useEffect 훅 안에서 실행하는 콜백함수의 콘솔로그실행이 2번 들어가고 있는걸까?
- 처음부터 계획적으로 작업을 한게 아니라 뒤죽박죽으로 하다 보니 라우터를 사용하다가 임시로 빼고 로그아웃 버튼을 라우팅에서 제외하고 해서 전체적으로 파일구조가 엉망이다. 수정이 필요하다.
1.3 로그인한 유저의 정보를 crud 시 사용하기(개선 필요)
지금 코드에선 firebase의 메서드인 onAuthStateChanged를 사용하고 있지 않는데, 이를 실행시켜 로그인시 로그인한 회원의 정보를 내 앱에서 받을 수 있다고 한다. 이부분이 있어야 로그인한 유저의 이름이나 프로필 사진 등의 정보를 내 앱에서 활용할 수 있는 부분인지 더 공부해보아야겠다. 여기에서 crud를 위해 axios로 http 요청이 필요한지도 확인해보아야 한다.
→ onAuthStateChanged 사용, 유저 정보 로컬스토리지로 저장하여 해결.
→ 유저 정보 받아오는걸 더 파이어베이스에서 하란 방식으로 개선해볼 수 있겠는데.. 아직은 잘 모르겠다.
추후 개선시 참고:
Firebase에서 사용자 관리하기 | Firebase 문서
1.4 CRUD에 Firebase의 데이터베이스 사용하기 - Cloud Firestore(작업중)
- 아직 생소한 디스트럭쳐링과 스프레드 문법. 더 공부해야한다.
// 예시 1. event 안에 있는 target으로부터 value를 받는다.
const onChange = (event) => {
const {
target: { value },
} = event;
};
// 예시 2. state 변경함수 안에서 콜백함수를 사용해서 이전 state 값에 새 값 덮어씌우기
const [message, setMessage] = useState({
username: '',
relation: '',
bodyText: '',
});
const onSubmit = e => {
e.preventDefault();
return console.log(message);
};
const onChange = e => {
const { name, value } = e.target;
setMessage(prev => ({
...prev,
[name]: value,
}));
};
- 오늘 만든 추천사 추가 인풋 폼
const MessageForm = () => {
const [message, setMessage] = useState({
username: '',
relation: '',
bodyText: '',
});
const onSubmit = e => {
e.preventDefault();
return console.log(message);
};
const onChange = e => {
const { name, value } = e.target;
setMessage(prev => ({
...prev,
[name]: value,
}));
};
return (
<FormContainer onSubmit={onSubmit}>
<div>
<span>추천사 추가하기</span>
</div>
<div>
<input
onChange={onChange}
type="text"
name="username"
placeholder="성함을 입력해주세요."
/>
</div>
<div>
<input
onChange={onChange}
type="text"
name="relation"
placeholder="관계를 입력해주세요."
/>
</div>
<div>
<input
onChange={onChange}
type="textarea"
name="bodyText"
placeholder="남겨주신 한글자 한글자가 큰 힘이 됩니다. 감사합니다!!"
/>
<div>
<input type="submit" value="추천사 추가하기" />
</div>
</div>
</FormContainer>
);
};
2. 남은 할 일
- 로컬 스토리지 - 테마 변경시 로컬스토리지로 값 저장해두어 새로고침 시에도 유지하기
- 조건부 렌더링 - 모바일과 데스크톱 환경에 따라 다른 홈 화면 렌더링하기
- Framer-motion - 스크롤 시 컴포넌트에 애니메이션 추가
- 코드 성능 개선 - 함수 중복실행되는 것들 확인하고 줄이기, 컴포넌트 성능 최적화
- CRUD - Firebase Cloud FireStore 연동
- CRUD - 게시글 수정, 삭제는 작성되어 화면에 반영된 개별 게시글에서 호버 시 수정, 삭제 아이콘이 나오고 클릭시 동작하도록 하기 → 본인이 작성한 글만 수정/삭제가 가능하도록 하려면 로그인시 받은 정보로 비교해서 필터링. uid로 비교해야 할까?
- nodemailer 붙이기 → 익스프레스 서버 따로 짜야 하는지, 파이어베이스로 가능한지 확인.
'개인프로젝트 > 개인 포트폴리오 웹앱' 카테고리의 다른 글
포트폴리오 앱 성능최적화 작업기 1편 (0) | 2022.10.26 |
---|---|
리액트 포트폴리오 사이트 제작기 5편 (0) | 2022.08.19 |
리액트 포트폴리오 사이트 제작기 3편 (0) | 2022.08.08 |
리액트 포트폴리오 사이트 제작기 2편 (0) | 2022.08.02 |
리액트 포트폴리오 사이트 제작기 1편 (0) | 2022.07.24 |