728x90

들어가며,

Next.js로 먼저 기능을 개발했었는데, 프로젝트의 방향이 React.js로 변경되면서 마이그레이션을 위한 대안점을 찾아야 했습니다.

 

기존에 사용하던 것과 바뀐 것들을 정리하면 다음과 같습니다.

- Next.js -> React.js

- NextAuth.js -> Firebase/Auth

- Prizma ORM -> Firebase

- Postgres -> firebase/firestore

 

FireBase를 사용하게 되면서 대부분의 기능을 Firebase가 담당해주면서 비교적 빠른 속도로 개발할 수 있었습니다.

 

개발의 주안점

1. 리다이렉트 되어 돌아온 비회원
 로컬스토리지에 저장된 이전 페이지 값을 불러낸 뒤 변수에 저장.
로컬스토리지는 비우고 저장된 변수를 활용해 다시 돌려보냄

2. 로그인 정보 유지
로그인 정보를 유지하기 위해 AuthGuard 컴포넌트와 useUser 훅을 사용

미리보기

 

회원 가입과 로그인 기능의 파라미터 값은 최대한 기본적인 요소를 사용하되, 오류나 검증 로직을 작성해봤습니다.

 

회원 가입

function SignUpPage() {
  const navigate = useNavigate()

  const handleSubmit = async (formValues: FormValues) => {
    const { email, password, name } = formValues

    const { user } = await createUserWithEmailAndPassword(auth, email, password)
    await updateProfile(user, {
      displayName: name,
    })
    const newUser = {
      uid: user.uid,
      email: user.email,
      displayName: name,
    }
    await setDoc(doc(collection(store, 'USER'), user.uid), newUser)
    navigate('/')
  }
  return (
    <div>
      <Form onSubmit={handleSubmit} />
    </div>
  )
}

export default SignUpPage

 

Form 컴포넌트를 따로 두고 Page 컴포넌트에서는 firebase로 데이터를 보내는 역할에 집중하게 했습니다.

<코드 동작>
1.폼에서 받은 formValues를 이용하여 이메일, 비밀번호, 이름을 추출합니다.
2.createUserWithEmailAndPassword 함수를 사용하여 이메일과 비밀번호를 사용하여 새로운 사용자를 생성합니다.
3.updateProfile 함수를 사용하여 새로 생성된 사용자의 프로필을 업데이트합니다.
4.새로운 사용자의 정보를 newUser 객체에 저장합니다.
5.setDoc 함수를 사용하여 데이터베이스에 새로운 사용자의 정보를 저장합니다.
6. 페이지를 '/'로 이동합니다.

 

Axios 로 스프링 부트와 API통신

import Form from '@/components/signup/Form'
import { FormValues } from '@/models/signup'
import axios from 'axios'
import { useState } from 'react'

function SignUpPage() {
  const [error, setError] = useState('')

  const handleSubmit = async (formValues: FormValues) => {
    const { email, password, name } = formValues

    try {
      const memberId = '1'
      const newUser = { memberId, email, password, name }
      // 스프링 서버에 회원가입 정보를 전송
      await axios.post('http://localhost:8080/members/register', newUser)
      console.log('회원가입에 성공했습니다.')
    } catch (error) {
      setError('회원가입에 실패했습니다.')
      console.error(error)
    }
  }

  return (
    <div>
      {error && <p>{error}</p>}
      <Form onSubmit={handleSubmit} />
    </div>
  )
}

export default SignUpPage

 

주문로직에서 결제 API와 연동한 스프링부트 어플리케이션과의 통신을 연습해보기 위해 회원가입 기능에서 POST 방식의 API를 보내 DB에 데이터가 저장되는 것을 확인했습니다.

-> 연습용으로 사용한 스프링 부트 어플리케이션은 이전에 간단하게 회원가입/로그인 기능을 구현했던 것을 재활용했습니다.

 

로그인 기능

import { useAlertContext } from '@/contexts/AlertContext'
import { FirebaseError } from 'firebase/app'
import { signInWithEmailAndPassword } from 'firebase/auth'
import { useCallback } from 'react'
import { useNavigate } from 'react-router-dom'

function SignInPage() {
  const navigate = useNavigate()
  const { open } = useAlertContext()

  const handleSubmit = useCallback(
    async (formValues: FormValues) => {
      const { email, password } = formValues
      const storedUrl = localStorage.getItem('redirectUrl')
      try {
        await signInWithEmailAndPassword(auth, email, password)
        if (storedUrl !== null) {
          localStorage.removeItem('redirectUrl') // 사용된 URL은 삭제
          navigate(storedUrl)
        } else {
          navigate('/')
        }
      } catch (e) {
        if (e instanceof FirebaseError) {
          if (e.code === 'auth/invalid-credential') {
            open({
              title: '이메일 및 패스워드를 다시 확인해주세요',
              onButtonClick: () => {
                console.log('Error', e)
              },
            })
            return
          }
        }
        open({
          title: '잠시 후 다시 시도해주세요',
          onButtonClick: () => {
            console.log('Error', e)
          },
        })
      }
    },
    [open, navigate],
  )

  return (
    <div>
      <Form onSubmit={handleSubmit} />
    </div>
  )
}

export default SignInPage

 

로그인 기능 역시 구현은 회원가입과 크게 다르지 않았습니다. firebase에서 제공하는 함수를 통해 쉽게 로그인을 성공시킬 수 있었습니다.

 

FORM컴포넌트에서의 검증 로직 : Validate

function validate(formValues: FormValues) {
  let errors: Partial<FormValues> = {}

  if (validator.isEmail(formValues.email) === false) {
    errors.email = '이메일 형식을 확인해주세요'
  }

  if (formValues.password.length < 8) {
    errors.password = '비밀번호를 8글자 이상 입력해주세요'
  }

  return errors
}

 

 

그러나, 본격적으로 로그인에 성공하면 로그인 정보를 유지해야 한다는 것과 회원 전용 페이지에 접근을 시도하다 리다이렉트되어 온 비회원에 대한 처리를 구현하려다 보니 몇가지 로직이 추가되었습니다.

 

1. 리다이렉트 되어 돌아온 비회원
 로컬스토리지에 저장된 이전 페이지 값을 불러낸 뒤 변수에 저장.
로컬스토리지는 비우고 저장된 변수를 활용해 다시 돌려보냄

2. 로그인 정보 유지
로그인 정보를 유지하기 위해 AuthGuard 컴포넌트와 useUser 훅을 사용

 

로그인 정보 유지와 관련된 내용은 Recoil 글에서 다루겠습니다.

+ Recent posts