팀프로젝트 4주차 - firebase + 구글 OAuth

zizi·2022년 12월 11일
0

팀프로젝트

목록 보기
4/7

202212

이번주는 firebase + 구글 OAuth 로그인 + 백엔드 연결에 대해서 공부를 했다.

첫번째 수업

첫번째 수업에서는 http, https, session, cookie, JWT 토큰, firebase + google Oauth 로그인 과정에 대해 함께 수업을 들었고 이론적인 부분에 대해 공부했다.

두번째 수업

두번째 수업에서는 미리 firebase로 구글 로그인+로그아웃까지 구현하고 본격적으로 백엔드에 토큰 전달하여 구현을 하기 위해 함께 진행했으나 백엔드 서버에 문제가 있어서 추후에 다시 맞춰보고 재진행하기로 하였다.
멘토님께 pull request를 진행했던 메인페이지 브랜치에 대해서 피드백을 받았다.
-섞여있는 함수 사용 방식들 -> 화살표 함수로 변경
-switch 문은 약간 옛 방식 느낌 -> if문 두개로 바꾸는 게 더 좋음
-리스트 내부에 snake_case로 섞여 있는 것 -> camelCase로 바꿔주기
-List 컴포넌트 props 전달 React.FC / interface 설정(공부하기)
-TypeScript pick 공부하기


결정된 점

  • package.json에 proxy 서버 등록 완료 => CORS 오류 해결
  • 저번주에는 멘붕을 겪었었지만 브랜치를 생성하여 깃 프로젝트 티켓 별로 진행중
    -[Git] Fatal: Not possible to fast-forward, aborting
    → 팀원분과 같은 브랜치에서 pull 하면 이 오류가 계속 나서 여러가지 방법을 써도 잘 안 되었는데 브랜치를 나눠서 하다보니 오류가 잘 안나게 됨

더 공부해야할 점

  • redux-toolkit(thunk) - thunk의 경우에 저장해야할게 달라지면 헷갈린다...
  • 로그인 과정 복습

느낀 점

firebase로 구글 로그인+로그아웃까지 구현하는 것은 한번 해본 적이 있어서 크게 어렵지 않았고, 블로그 글과 firebase 문서를 참고하여 진행하였다. -최신 버전 firebase로 진행함

https://velog.io/@couchcoding/Firebase%EB%A1%9C-Google-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-React-%ED%8C%8C%ED%8A%B8 - 참고 블로그

그렇지만 백엔드 전달하는 건 해본 적이 없어서 많이 헤맸다.
백엔드와 같이 진행하는 것도 처음인데 토큰 사용, rest API, 익숙치않은 타입스크립트와 리덕스 툴킷까지 같이 묶어서 진행하려니 어떻게 써야할 지 뭘 모르겠는지도 모르는 느낌이었고 아직 너무 부족한 게 많다는 걸 느꼈다.

머리로는 로그인 과정이 한 50% 이해가 갈랑 말랑 하지만 구현까지 하려니까 나는 그 과정이 잘 그려지지 않았는데 만약 혼자했으면 며칠도 부족했을 것 같다. 다행히 우리 팀에 뛰어난 프론트엔드 분이 계셔서 팀원 분의 가르침 아래에서 많이 이해하고 배울 수 있었다.


firebase + 구글 OAuth 로그인 + 백엔드에 토큰 전달

*로그인 과정
우리 팀은 백엔드에서 JWT 토큰을 쿠키로 저장하여 인증하는데 사용하기로 결정했다.

*프론트엔드에서 해야할 일

  • firebase를 통해 구글 로그인/로그아웃 구현
  • 최상단에서 로그인 상태를 가져와 JWT token을 받아와서 헤더에 넣어서 headers: { Authorization: JWT토큰 } 백엔드로 전달 (get)
  • 유저 정보를 컴포넌트 여기저기서 사용하기위해 리덕스에 넣기
  • 로그아웃 시 헤더에 Authorization: JWT토큰을 백엔드로 전달 (delete)

1. firebase 연결 + 구글 로그인/로그아웃

  • firebaseAuth.ts
import { initializeApp } from 'firebase/app'
import {
  getAuth,
  GoogleAuthProvider,
  signInWithPopup,
  signOut,
} from 'firebase/auth'

const firebaseConfig = {
  apiKey: process.env.REACT_APP_API_KEY,
  authDomain: process.env.REACT_APP_AUTH_DOMAIN,
  projectId: process.env.REACT_APP_PROJECT_ID,
  storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_MESSAGING_ID,
  appId: process.env.REACT_APP_APP_ID,
  measurementId: process.env.REACT_APP_MEASUREMENT_ID,
}

const app = initializeApp(firebaseConfig)
export const auth = getAuth()
const provider = new GoogleAuthProvider()

export const googleSignIn = async () => {
  const res = await signInWithPopup(auth, provider)
}

export const googleSignOut = async () => {
  const res = await signOut(auth)
}

2. 백엔드에 토큰 전달 시 필요한 axios 함수 작성

  • authService.ts
import axios from 'axios'

interface SignInParam {
  token: string
}

const signIn = async ({ token }: SignInParam) => {
  const res = await axios.get('/users/me', {
    headers: { Authorization: `${token}` },
  })

  return res
}

const signOut = async ({ token }: SignInParam) => {
  const res = await axios.delete('/users/me', {
    headers: { Authorization: `${token}` },
  })
  return res
}

// 이 함수를 불러와서 사용할 때는 authService.signIn(토큰)으로 사용 가능
export const authService = { signIn, signOut } 

3. 최상단에서 onAuthStateChanged로 로그인한 사용자를 가져오기 -> 사용자의 토큰을 가져와서 백엔드 전달(get) -> dispatch로 유저 정보 redux에 넣어주기

  • App.tsx
useEffect(() => {
	// 로그인한 사용자 감지
    const onAuthListener = onAuthStateChanged(auth, async (user) => {
      if (user) {
       	// 토큰 가져오기
        const token: string = await user.getIdToken()
        
		// 토큰 가져와서 백엔드로 전달(get) - 2번 참고
        if (token) {
        const res = await authService.signIn({ token })
        
        // 유저 정보 리덕스에 저장
        dispatch(setUser({ token, email: res.data }))
        }
			}
    })
    return onAuthListener
  }, [])

4. 로그인

  • LoginModal.tsx - 로그인 버튼 클릭시 구글 로그인 진행
import { googleSignIn } from '@services/firebaseAuth'
interface ModalProps {
  setModalOpen: React.Dispatch<React.SetStateAction<boolean>>
}

const LoginModal = ({ setModalOpen }: ModalProps) => {
  
  const logIn = async () => {
    await googleSignIn() // 구글 로그인 진행
    setModalOpen(false)
  }

  return (
    <LoginWrap>
      <LoginBox>
        <CloseBtn onClick={() => setModalOpen(false)}>X</CloseBtn>
        <Content>
          <div className="logo">Logo</div>
          <LoginBtn onClick={logIn}>Google 로그인</LoginBtn>
        </Content>
      </LoginBox>
    </LoginWrap>
  )
}

export default LoginModal

5. useSelector로 필요시 유저 정보 가져와서 사용(nav에 표기하기 위함)
로그아웃 버튼 클릭시 구글 로그아웃 진행 -> 백엔드로 토큰 전달(delete) -> 리덕스에 유저 정보(null) 업데이트

  • Navigation.tsx
const Navigation = () => {
  const navigate = useNavigate()
  const dispatch = useDispatch()
  const user = useSelector((state: RootState) => state.user.user) // 유저 정보 가져와서 사용
  const [modalOpen, setModalOpen] = useState<boolean>(false)

  const signOutHandler = async () => {
    googleSignOut() // 구글 로그아웃 
    
    if (user) {
      try {
      	// 백엔드로 토큰 전달
        await authService.signOut({ token: user.token }) 
        
        // 리덕스에 유저 정보 업데이트
        dispatch(setUser(null)) 
      } catch (e) {
        console.log(e)
      }
    }
  }
  return (
    <>
      <NavigationContainer>
       ...생략

        {user ? (
          <LogoutContainer>
            <Avatar></Avatar>
            <p>{user.email.split('@')[0]}</p>
            <FunButton onClick={signOutHandler}>Logout</FunButton>
          </LogoutContainer>
        ) : (
          <FunButton
            onClick={() => {
              setModalOpen(true)
            }
          >
            Login
          </FunButton>
        )}

        {modalOpen && <LoginModal setModalOpen={setModalOpen} />}
      </NavigationContainer>

      <main>
        <Outlet />
      </main>
    </>
  )
}

export default Navigation

현재는 App.tsx의 useEffect 내부에 있는 부분(토큰 가져와서 백엔드로 전달하고 유저 정보 리덕스에 저장하는 부분)도 thunk로 만들었는데 hook으로 아예 useEffect 부분을 뺄지 고민중이다.

0개의 댓글