기억의 실마리
2022. 11. 17. 00:04

# App.js

index.js를 제외하면 최상위 컴포넌트 파일이다.

App.js에서부터 속성들을 가져와야 한다.

필요한 속성은 userObj이다.

 

userObj는 파이어베이스에서 getFirestore.currentUser.[ 필요한키 ]

이렇게 가져올 수 있는 데이터이지만 userObj라는 변수를 만들어 속성에 넣고

여기저기서 전달받아 사용하는 이유는 소스를 통합하여 확장성 있게

사용하고 싶기때문이다. userObj 하나만 변경해도 통합되어 변경되기 때문

더 직관적으로 변경,저장하기 쉬워진다.

function App() {
  const [init, setInit] = useState(false);  //false = 홈화면대기중...
  const [userObj, setUserObj] = useState(null);  //유저데이터를 위한 스테이트
  useEffect(()=>{
    authService.onAuthStateChanged((user)=>{
    //firestore에서 auth 변화리스너, 고로 user = usedata이다.
    
      if (user) {
        setIsloggedIn(true);
        setUserObj({
          displayName: user.displayName,
          uid: user.uid,
          updateProfile: { displayName: user.displayName },
      /* user에는 아주 많은 유저데이터가 있는데 그 중 필요한 key만 가져와서
        값을 적용시켜주고 있다. 방대한 데이터는 re-render에 불리하기때문이다. */
          
        });
      } else {
        setIsloggedIn(false);  //flase = 로그인실패.
      }
      setInit(true); //true = 홈화면이 된다.
    });
      },[]);
  const refreshUser = () => {
    const user = authService.currentUser;
    //refreshUser에는 user가 존재하지 않아, 새로 선언해준다.
    
    setUserObj({
      displayName: user.displayName,
      uid: user.uid,
      updateProfile: { displayName: user.displayName },
    });
  }
  //refreshUser함수를 만든 이유는 프로필 업데이트 기능을 위해서다.
  
  return (
      <>
        { init ? (
            <AppRouter
                isLoggedIn={isLoggedIn}
                userObj={userObj}
                refreshUser={refreshUser}
            />
        ) : (
            "Initializing...."
        ) }
      </>
  );
   //AppRouter에 속성들을 전달시켜준다.
}

 

# Router.js

두번째 컴포넌트파일이며, Profile 컴포넌트로 속성들을 전달해준다.

import {
    BrowserRouter as Router, Route, Routes,
} from "react-router-dom";
import {Auth} from "routes/Auth";
import {Home} from "routes/Home";
import {Profile} from "routes/Profile";
import {Navigation} from "components/Navigation";

export const AppRouter = ( {refreshUser, isLoggedIn, userObj} )=> {
    return (
        <Router>
            {isLoggedIn && <Navigation userObj={userObj}/>}
            <Routes>
                {isLoggedIn ? (
                    <>
                        <Route path="/" element={<Home userObj={userObj}/>}/>
                        <Route path="/Profile"
                               element={ <Profile userObj={userObj}
                                refreshUser={refreshUser} /> }
                        />
                    </>
                ): (
                    <>
                        <Route path="/" element={<Auth/>}/>
                    </>
                )}
            </Routes>
        </Router>
    );
};

AppRouter 인자로 오브젝트 리터럴로 속성이름을 넣어서 전달받고

컴포넌트요소 안에도 동일명속성으로 중괄호 안도 넣어준다.

 

# Navigation.js

고정 되어있는 네비게이션 컴포넌트파일이다.

import React from "react";
import {Link} from "react-router-dom";

export const Navigation = ({ userObj })=> {
    return (
    <nav>
        <ul>
            <li>
                <Link to="/">Home</Link>
            </li>
            <li>
                <Link to="/profile">{userObj.displayName}의 Profile</Link>
            </li>
        </ul>
    </nav>
    );
};

Home 메뉴와 userObj.displayName + "의 Profile" 메뉴가 있다.

userObj.displayName는 현재 접속되어있는 유저데이터에서 이름을 가져온다.

 

# Profile.js

import React, {useEffect, useState} from "react";
import {authService, dbService} from "Fbase";
import { useNavigate } from "react-router-dom";
import {collection, getDocs, query, where} from "@firebase/firestore";
import {updateProfile} from "@firebase/auth";

export const Profile = ( { refreshUser, userObj } )=> {
         //상위 컴포넌트에서 가져온 속성들

    const navigate = useNavigate();
    //로그아웃 처리 후 홈화면으로 이동시킬때 사용
    
    const [newDisplayName, setNewDisplayName] = useState(userObj.displayName);
    //가져온 유저데이터에서 displayName
    
    const onLogOutClick = () => {
        authService.signOut();
        navigate("/",{replace:true});
    };
    
    // eslint-disable-next-line react-hooks/exhaustive-deps
    const getMyZweets = async ()=> {
        try {
            const q = query(
                collection(dbService,"zweets"),
                where("creatorId", "==", userObj.uid),
            );
            const querySnapshot = await getDocs(q);
            querySnapshot.forEach((doc)=>{
                console.log(doc.id,"=>",doc.data());
            });
        } catch (e) {
            console.log(e);
        }
    }
    useEffect(()=> {
        getMyZweets();
    },[getMyZweets])
    const onChange = ( {target: {value} }) => {
        setNewDisplayName(value);
    }
    const onSubmit = async (e)=> {
        e.preventDefault();
        if(userObj.displayName !== newDisplayName){
            await updateProfile(authService.currentUser,{displayName: newDisplayName});
             /*여기서 userObj가 아닌 authService.currentUser를 사용한 이유는
             파이어베이스 v9부터는 메소드의 영역이 다르기 때문에 위와같이 해결했다. */
        }
        refreshUser();
        //서브밋되면 리프레쉬유저함수를 실행시켜 프로필도 re-render되게 만든다.
    }
    return (
        <>
            <form onSubmit={onSubmit}>
                <input
                    onChange={onChange}
                    type="text"
                    placeholder="Display name"
                    value={newDisplayName}
                />
                <input type="submit" value="Update Profile"/>
            </form>
            <button onClick={onLogOutClick}>Log Out</button>
        </>
    )
};

프로필 화면에서 텍스트인풋내에 글을 쓴 후 Update Profile 버튼이나

엔터를 눌러 서브밋하면 refreshUser함수가 실행되어 네비게이션의 프로필이름이 바뀐다.

 

# refreshUser 함수 다시보기

  const refreshUser = () => {
    const user = authService.currentUser;
    setUserObj({
      displayName: user.displayName,
      uid: user.uid,
      updateProfile: { displayName: user.displayName },
    });
  }

setUserObj로 인하여 userObj가 변경되고

변화를 감지한 react가 re-render시킨 것이다.