기록
  • Day 7 - 누이터 서브 기능 만들기
    2024년 09월 12일 16시 33분 15초에 업로드 된 글입니다.
    작성자: 삶은고구마
    • 내가 쓴 트윗만 보기
    • 프로필 페이지 기능 보완
    • 프로필 실시간 업데이트

     

    1.내가 쓴 트윗만 보기

    파이어스토어에서 로그인한 사람의 아이디와 트윗 작성자 아이디가 같은 경우에만 

    해당 트윗을 화면에 출력한다.

     

    1)파일 정리하기

    -routes 폴더의 EditProfile.js 삭제

    -Profile 컴포넌트에는 로그아웃 기능만 있는데 

    이 기능은 파이어베이스에 모든 연결을 그냥 끊어버리는 방식이었다. 

    그래서 Profile 컴포넌트에는 다른 데이터가 필요하지 않았었다.

    하지만 Profile 컴포넌트에 로그인한 사용자의 프로필을 보여주려면 사용자 정보가 필요하다.

    즉, userObj를 프롭스로 받아와야함. 라우터에서 프로필로 userObj 값을 넘겨주자

    그리고 Profile.js에서는 전달받은 인자를 userObj로 지정하면된다.

     

    Router.js

    <Route exact path="/profile" element={<Profile userObj={userObj}/>}></Route>

     

    Profile.js

    const Profile = (userObj) => {

     

     

    2)트윗 필터링 기능 구현하기

    로그인한 사용자의 트윗만 가져오는 함수를 만들어보자.

    먼저 Profile 컴포넌트가 렌더링 될 때, 즉, 프로필로 이동했을 때 어떠한 작업이 진행되어야 한다.

    그럴땐 useEffect의 두번째 인자에 빈 배열[]을 전달하면 된다.

     

    Profile.js

    import { useEffect } from "react";
    ...
     const onLogOutClick = () => 
            {
                authService.signOut();
                history("/");
            };
    
        //profile컴포넌트가 렌더링 된 이 후 실행되는 함수.
        useEffect(()=>{},[]);

     

     

    현재 로그인한 사용자의 트윗만 골라 화면에 출력하도록 코드 구현

     

    where 함수

    sql구문의 select로 데이터 조회시 특정 조건이 있을때 사용한 구문이었다.

    여기서도 마찬가지로 뭔가 찾는 역할을 함.

    where함수는 필드,조건,찾으려는값 순서로 인자를 전달하면 된다.

    아래의 코드를 보면 CreatorId 필드에서 userObj.uid와 동일한 값을 찾기 위한 기능을 코드로 작성한 것이다.

     

    Profile.js

    import { authService,dbService } from "fbase";
    
    const getMyNweets = async()=>{
            const nweets = await dbService.collection("nweets")
                            .where("creatorId","==",userObj.uid);
        }

     

     

    3)정렬 쿼리

    orderby로 생성날짜[작성날짜]를 기준으로 asc[오름차순]

     

    Profile.js

    const getMyNweets = async()=>{
            const nweets = await dbService.collection("nweets")
                            .where("creatorId","==",userObj.uid)
                            .orderBy("createdAt","asc");
        }

     

     

    4)필터링한 트윗목록을 콘솔에 출력하기

    출력하기 위해 get 함수를 사용한다.(쿼리문을 통해 얻은결과물을 가져오는 함수)

     

    Profile.js

    const getMyNweets = async()=>{
            const nweets = await dbService.collection("nweets")
                            .where("creatorId","==",userObj.uid)
                            .orderBy("createdAt","asc")
                            .get();
    
                            console.log(nweets.docs.map((doc)=>doc.data()));
        }
        
       
    
        //profile컴포넌트가 렌더링 된 이 후 실행되는 함수.
        useEffect(()=>{
            getMyNweets();
        },[]);

     

     

    에러 발생

     

    일부러 발생시킨 에러.

    파이어베이스에서 제공하는 데이터베이스 관련 함수중에는 파이어베이스가 받아들일 준비가 되어있지 않으면

    사용할 수 없는 것들이 있다고 한다.

    즉, 사전작업을 해야 한다는  것인데..

    지금 사용하고있는 orderBy 함수는 파이어스토어에서 색인(인덱스)작업이 되어있어야 사용가능함.

    하단에 출력된 링크 (you can crreate it here 다음)을 누르면 바로 이동.

    저장 선택
    생성 중.. [몇 분 소요됨] 완료되면 사용 설정됨 이라고 뜬다!

     

     

    다시 실행하면 myProfile 페이지에서 내가 쓴 트윗만 출력된다.

     

    만약 트윗을 한개도 안쓴 계정으로 로그인하여 myProfile에서 확인하면 배열은 빈 값이다.

     


    2.프로필 페이지 기능 보완하기

    로그인한 사용자의 이름을 표시하고 프로필을 업데이트 할 수 있도록 기능을 추가한다.

     

    1)네비게이션에 이름 넣기

    아래 화면에서 myProfile을 출력하는 곳은 Navigation 컴포넌트다.

    여기의 ㅇㅇ의 프로필 과 같이 로그인한 사용자의 이름을 출력하고자 한다~

    userObj 프롭스를 Navigation으로 보내주면된다. 

     

     

    Router.js

    <Router>
              {isLoggedIn && <Navigation userObj={userObj}/>}
                    <Routes>

     

     

    Navigation.js

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

     

     

    displayName은 회원가입으로 채워지지 않는 값이다.

    그래서 출력되지 않는 상황..현재 null 값이기 때문이다.

    이 값은 구글혹은 깃허브 로그인을 통해 채워지는 값이라서 그렇다.

    소셜로그인[구글]을 해서 확인해보자. 

    내 구글 닉네임이 정상적으로 출력된다..

     

    2)프로필 업데이트 기능 추가

    프로필 업데이트 폼 만들기

    프로필 표시를 위한 useState 만들기

    기본값은 userObj.displayName이고 업데이트 버튼을 누르면 newDisplayName이 바뀌도록 함.

     

    Profile.js

    import { useState,useEffect } from "react";
    ..
    const [newDisplayName,setNewDisplayName] = useState(userObj.displayName);
    
    return(
            <>
            <form>
            <input type="text" placeholder="display name"></input>
            <input type="submit" value="update profile"></input>
            </form>
                <button onClick={onLogOutClick}>Log out</button>
            </>
        );

     

    이어서 onChange 함수 작업

    const onChange= (event) =>{
            const{
                target:{value},
            }=event;
            setNewDisplayName(value);
        }
        
        <input onChange={onChange} type="text" placeholder="display name" 
        value={newDisplayName}></input>

     

    이 작업으로 프로필이 당장 업데이트 되는건아니고 submit 함수 형태만 잡는다 생각하자.

    const onSubmit = (event)=>
        {
            event.preventDefault();
        }
        
        ..
        .
       <form onSubmit={onSubmit}>

     

     

    3)userObj 관련 문서를 통해 프로필 업데이트

    지금까지 userObj를 값이 들어있는 읽기용도의 객체정도로 생각했을 것이다.

    실제 userObj에는 많은 함수가 들어있다. 그 함수들은 파베에서 제공하는 것이고..

    여기에있는 함수들을 사용하면 displayname 항목을 업데이트 할 수 있다.

    이 userObj는 어디서 얻는 값일까?

    상위 객체를 타고타고 올라가면 App.js에서 사용한 authService - onAuthStateChanged에서 인자로 전달안 user이다.

    파베 공식문서를 통해 확인하면

    updateProfile 함수에 displayName과 photoURL을 문자열로 전달하면 프로필을 업데이트 할 수 있다고 한다.

    지금은 유저명을 출력하고싶은거니 displayName만을 사용해보자.

    updateProfile함수도 비동기 작업이라 async-await 문을 사용한다.

    그리고 예외처리를 해야 하는데 만약 이름을 입력하지 않았을때 onSubmit이 동작하지 않아야 하므로

    if문에서 기존 네임과 새 네임을 비교해서 다를때만 업데이트 하도록 하였다.

     

    Profile.js

     const onSubmit = async(event)=>
        {
            event.preventDefault();
            if(userObj.displayName!==newDisplayName)
            {
                await userObj.updateProfile({displayName : newDisplayName});
            }
        }

     

     

     

    변경이 되긴되는데 실시간으로 반영되지않는다

    f5를 눌러서 변경된것을 확인.

     


    3.프로필 실시간 업데이트

    프로필을 실시간으로 반영하려면?

    리렌더링 하는 방법이 있다.

    -useState로 관리중인 상태가 업데이트 되는경우

    -프롭스로 받은 요소가 업데이트 되는 경우

     

    1)Navigation 컴포넌트 살펴보기

    리렌더링 해야 하는 컴포넌트는 Navigation 컴포넌트이다.

    코드를 보면 현재 이 컴포넌트에서는 관리중인 상태가 없고 받은 프롭스는 userObj 뿐이다.

    import {Link} from "react-router-dom";
    
    const Navigation = ({userObj}) =>{
    
        return(
            <nav>
                <ul>
                    <li>
                        <Link to="/">Home</Link>
                    </li>
                    <li>
                        <Link to="/profile">{userObj.displayName}의 Profile</Link>
                    </li>
                </ul>
            </nav>
        ); 
    };
    
    export default Navigation;

     

     

    2)refreshUser 함수 추가

    리렌더링을 위해 userObj를 업데이트 해보자.

    그런데 userObj는 파베 user를 받아 복사한 것이고, 

    프로필 업데이트는 파베에 영향을 주는것이라 userObj에는 여전히 이전 값이 남아있을 것이다.

    쉽게 말해 userObj는 스스로 업데이트된 프로필을 반영하지 못한다.

    이를 위해 새 프로필을 파베에서 받아와 userObj에 반영해야 함

    =>userObj를 새로고침하는 함수가 필요하므로 userObj를 관리하는 App.js 수정

     

    App.js

    //userObj 새로고침
      const refreshUser=()=>{
        setUserObj(authService.currentUser);
      };

     

    이 함수가 실행되면 인증모듈에서 authService.currentUser로 얻은 새 user를 userObj에 반영.

     

    3)refreshUser 함수 profile 컴포넌트로 보내기

    이 함수는 app.js가 아닌 profile 컴포넌트에서 실행해야 한다.

    approuter-prifle 순으로 이 함수를 프롭스를 통해 내려보내주고 profile 컴포넌트에서 실행되어야함.

     

    App.js

    {init? (
          <AppRouter 
            refreshUser={refreshUser}
            isLoggedIn={isLoggedIn} 
            userObj={userObj}/> 
          ): ("initializing...")
        }

     

    Router.js

    // 상위 컴포넌트에서 받은 프롭스를 구조분해 할당으로 사용한다. 
    const AppRouter = ({isLoggedIn,userObj,refreshUser}) =>
    ..
    <Route exact path="/profile" 
    	element={<Profile refreshUser={refreshUser} userObj={userObj}/>}>
    </Route>

     

    Profile.js

    const Profile = ({userObj,refreshUser}) => {
    ..
     const onSubmit = async(event)=>
        {
            event.preventDefault();
            if(userObj.displayName!==newDisplayName)
            {
                await userObj.updateProfile({displayName : newDisplayName});
                refreshUser();
            }
        }

     

     

    여기까지 했는데도 실시간 반영이 되지 않고, 이전처럼 f5로 새로고침을 해야 반영이 된다.

    왜그럴까?

    저자는 리액트의 한계때문에 그런거라고 한다.

    원래는 상태나 프롭스의 내용이 업데이트 되면 리렌더링이 발생한다.

    하지만 리액트는 상태나 프롭스의 내용물이 너무 많으면 그 안의 작은 변화를 인식하지 못한다 한다.

    userObj에 포함된 내용은 많았기 때문에 그 중 하나인 displayName이 변화되어도 인식하지 못한것이다.

    그래서 필요한 내용만 담은 객체로 쪼개야 한다.

     

    4)userObj 크기 줄이기

    userObj를 최초로 사용하는 App.js에서 수정작업을 해야한다.

    user 통째로 말고 그 안에 필요한 것만 뽑아서 반영.

    updateProfile 함수의 경우 profile컴포넌트에서 실행될때 입력되는 값을 고려해 재구성함.

     

    App.js

    // setUserObj(user);
            setUserObj({
              uid:user.uid,
              displayName:user.displayName,
              updateProfile:(args)=>user.updateProfile(args),
            });

     

     

    5)isLoggedIn 크기 줄이기

    위에서 언급한 문제와 동일한 이유로 isLoggedIn의 크기도 줄이자.

    isLoggedIn의 경우 userObj가 있는지 확인만 하면 되므로 boolean함수를 사용해 수정가능.

    Boolean함수는 인자로 받은 값을 검사해서 값이 있음 true 없으면 false 반환.

    이를 통해 기존 isLoggedIn 프롭스 적용.

     

    App.js

    // setIsLoggedIn(user);
    .
    .
    isLoggedIn={Boolean(userObj)}

     

    계속해서 refreshUser함수에 직접 구성한 사용자 정보 객체를 적용하기.

     

    App.js

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

     

    이렇게 해야 사용자 정보 객체가 동일하게 유지된다.

    즉, 이전부터 사용하던 userObj의 형태를 가볍게 바꾸면서도 동일한 형태로 유지하기 위해 위 작업을 진행한 것.

    이제 실시간으로 반영되는지 확인하자.

    이번엔 png가 아닌 gif로 확인했다. 기존 두더지고로케->농담곰으로 업데이트가 반영됨.

     

     

    '공부 > 클론코딩' 카테고리의 다른 글

    Day 7 - CSS 작업 (完)  (1) 2024.09.12
    Day 7 - 깃허브 작업  (0) 2024.09.12
    Day 5 - 로그인 기능 구현하기 1  (0) 2024.09.09
    Day 4 - 클론 코딩 시작 2  (2) 2024.09.09
    Day 3 - 클론 코딩 시작  (0) 2024.09.07
    댓글