[ react ] 작성자가 추가한 포스트보기 - accessToken useAuth함수

Suji Kang·2023년 10월 18일
0

🐾 email 주소 아이디가 추가한 포스트만 보여지게 하기

작성한사람만 자기 목록만 보이도록

post 요청을 할떄, company, position, startDate, endDate 밖에 안줬다. 로그인한 사람의 id도 담아줘야함.

🔎 id는 어떻게 가져올수있을까?

토큰 - 이메일주소가 필요하니까 header 에다가 담아서. accessToken에 담아 넘겨준다.

careerViewTable.js

const CareerViewTable = () => {
 //토큰이 저장되어있는 전역 상태변수 가져오기 
    const { accessToken, setAccessToken } = useContext(UserContext);

	const onAddCareer = async () => {
 		// 정상적으로 실행되는 코드
        try {
            let res = await axios.post('/api/career',
                {
                    company, position, startDate, endDate
                },
                {
                    headers: {
                        Authorization: `Bearer ${localStorage.getItem('accessToken')}`
                    }
                }
            );
            //res.data에는 방금 추가한 객체가 들어있음
            //성공했을때 뭔가 할것
            alert('추가 완료!');
            //  window.location.reload(); //새로고침

            //새로고침 대신 생각해 볼수있는것
            //방금 추가한 객체를 이미 20개의 객체가 요소로 들어있는
            //배열의 머지막 요소로 추가
            setCareerList([...careerList, res.data]);
            console.log([...careerList, res.data]);

        } catch (err) {
            console.log(err);
        }
    }
 return(
   <button onClick={onAddCareer}>추가</button>
  );
}

🔎 이메일은? 헤더에 들어있다. (email은 header에 있는 token 에 들어있음- email도 추가하자!)

app.js

//career 추가
app.post('/api/career', async (req, res) => {
    // email은 header에 있는 token 에 들어있음
    const token = req.headers.authorization.replace('Bearer ', '');
    let { email } = jwt.verify(token, process.env.JWT_SECRET) //이메일만 뽑아서 쓰겠다.

    const { company, position, startDate, endDate } = req.body;

    let sql = `
    insert into tbl_careers
    (email, company, position, start_date, end_date)
    values
    (
      ?, 
      ?, 
      ?, 
      str_to_date(?, '%Y-%m-%d'), 
      ${endDate === '' ? null : `str_to_date(?, '%Y-%m-%d')`}
    );
    `;

    let values = [email, company, position, startDate];
    if (endDate !== '') {
        values.push(endDate);
    }

    try {
        let [results] = await pool.query(sql, values);

        console.log(results);
        let [rows] = await pool.query('select * from tbl_careers WHERE  id=?', [results.insertId])
        console.log(rows);
        res.json('커리어 추가 완료!');

    } catch (err) {
        console.log(err);
        res.status(500).json('서버에서 오류 발생함');
    }

});

🔎 최초 렌더링 될떄, 여기서 이메일정보도 같이 넘겨주면 되겠지❗️(header에다가)

careerViewTable.js

const CareerViewTable = () => {
    const [careerList, setCareerList] = useState([]);

    useEffect(() => {
        //CareerViewTable이 최초 렌더링 될때 express 한테 careerList를 요청해서 받아온다.
        const fetchCareerList = async () => {
            try {
                let res = await axios.get('/api/career',
                    { headers: { Authorization: `Bearer ${localStorage.getItem('accessToken')}` } }); //토큰을 보내줘야한다.
                setCareerList(res.data);
            } catch (err) {
                console.log(err);
            }
        }
        fetchCareerList();

    }, []);

토큰이 req안에 들어가있다. sql,where email = ?를 설정해주고,
token을 받아온다. req 안에있는 headers에 authorization
그리고, 그 token안에email만 쓰겠다.

pool.query(sql, [email]) 👉 sql 실행할건데, 로그인한사람의 email을 넣어서 보여주세요.

app.js

app.get('/api/career', async (req, res) => {
    try {
        let sql = `
    select id, 
      email,
      company, 
      position, 
      date_format(start_date, '%Y년 %m월 %d일') start_date,
      date_format(end_date, '%Y년 %m월 %d일') end_date 
    from tbl_careers
    where email = ?
    `
        let token = req.headers.authorization.replace('Bearer ', ''); //token을 받아온다. req 안에있는 headers에 authorization.
        let { email } = jwt.verify(token, process.env.JWT_SECRET); // 그 token안에서 email만 쓰겠다. 

        // mysql가서 커리어 리스트 받아오고 , email이 일치하는 커리어만 받아오기
        let [results, fields] = await pool.query(sql, [email]);//sql 실행할건데, 로그인한사람의 email을 넣어서 보여주세요. 
        // 리액트한테 받아온 배열 응답하기
        res.json(results);
    } catch (err) {
        console.log(err);
        res.status(500).json('서버쪽 오류 발생');
    }
});

📌 근데 왜 localstorage에서 갖다 썼을까?

🔎 처음에 로그인 성공하면,
App.js그려지고 그안에 login.js

App.js속에 accessToken state변수를 만들고 ,

  • 근데 여기서 새로고침을 하면, App.js부터 다시 그려진다.
    그안에있는 accessToken도 그래서 다시그려지니까, 초기화가된다. state변수 초기값을 null 설정해놨기때문에, 초기화 되면 null값이 들어간다.

📌 accessToken상황을 체크해줘야함. (진짜 로그인 했는지 안했는지 확인)

accessToken 사용하는 컴포넌트가 있다면, 확인해줘야함

상황 1) 로그인을 안해서 null
-> localStorage에도 없음
상황 2) 로그인을 했는데 새로고침 했음
-> localStorage에는 있는데 accessToken에는 없음
=> useEffect( ) 를 써야한다

그런데❗️
여기서, 로그인을해야 볼수있는 컴포넌트(DashboardHeader, CareerViewTable)를 갈때마다 그럼 useeffect매번 get 요청해서 accessToken에 email확인해야하는가 ❓
(header에서도 필요하고, 경력페이지, 활동게시판, 할일목록 다 필요한데...)너무 불편하잖아그럼..
그렇기 때문에 우리에겐 custom hook 함수가 있지 (hook함수를 직접 만들 수 있다)😁

🐾 useAuth함수 2:30

📝 useEffect 의존성배열 속이 비어있으면 , 최초 한번만 실행된다.
accessToken이 존재하는지 안하는지 확인.

hooks.js

import {useContext, useEffect } from "react";
import {UserContext} from "../App";
import { useNavigate } from "react-router-dom";

// accessToken이 있는지 없는지 검사하는 hook함수
export const useAuth = ()=>{  //다른데서 사용가능하게 export해줌
  //전역 state변수 가져오기(App.js에서)
  const {accessToken, setAccessToken} = useContext(UserContext);
  const navigate = useNavigate();
  useEffect(()=>{
    //1. 로그인이 안된 상태 localStorage의 accessToken null, 
    //    전역 상태변수 accessToken null
    if(localStorage.getItem('accessToken') === null){
      alert('로그인이 필요한 페이지 입니다!');
      navigate('/login', {replace : true});
      return;
    }

    // 2. 로그인은 되었으나 새로고침한 상태 accessToken 있음,
    //    전역 상태변수 accessToken null
    if(accessToken === null){
      setAccessToken(localStorage.getItem('accessToken'));
      return;
    }

    // 3. 로그인 되었고, 새로고침도 안함
    // localStorage에 accessToken 있음,
    //  전역 상태변수 accessToken 있음
    
  }, [accessToken, setAccessToken, navigate]); //accessToken, setAccessToken, navigate 바뀌면 실행시켜줘
}

📝이제 useAuth() custom hook만든걸로 필요한곳에 사용해보자.

header.js

import { useAuth } from "../hooks/hooks";

const DashboardHeader = () => {
  useAuth(); //만든 hook함수를 여기다 쓴다고 써줘
   // 전역state변수에 있는 토큰 값 가져오기
  const {accessToken, setAccessToken} = useContext(UserContext);
  
   useEffect(() => {
    let tmp = async () => {
      if(accessToken === null) return; //accessToken이 null이면 아래 코드 실행 안함 바로 종료
      try {
        let res = await axios.get('/api/loggedInEmail',
          { headers: { Authorization: `Bearer ${accessToken}` } } // 전역상태변수 accessToken 가져와서 사용가능.
        );
    }

    tmp();
    // axios.get('/api/users/로그인한사람id');
  }, [accessToken]); //accessToken 바뀌면, 다시 실행
}

그럼 이제 accessToken 전역변수 객체를 바로 가져와서 사용해도된다.

{ headers : {Authorization : `Bearer ${localStorage.getItem('accessToken')}`}}
=> {headers : {Authorization:`Bearer ${accessToken}`}} // 전역상태변수 accessToken 가져와서 사용가능.

근데, 또 문제점이 있지❗️ useAuth 낭비..
로그인이 안되어있으면, 전체가 안보이게
전체에다가 쓰면되지 않을까?
하나씩 하나씩 불러서 사용하는것보단?
이렇게 하나로 만들면 되지않을까?

🔎 이페이지가 로그인을 해야지만 보여지는 페이지

career.js

import DashboardLayout from "../../components/common/layout";
import { Title } from "../../styles/dashboard/career.styles";
import CareerViewTable from "../../components/career/careerViewTable";
import { useAuth } from "../../components/hooks/hooks";


const CareerPage = () => {
    useAuth();
        return (
            <DashboardLayout>
                <Title>나의 경력을 관리하세요</Title>
                <p>회사, 직위, 일자를 입력한 후 경력을 추가해 보세요!</p>
                <CareerViewTable />
            </DashboardLayout>
        )
    }
    export default CareerPage;

career page 안에, CareerViewTable로 넘어오기때문에, 여기도 전역변수 accessToken로 바꿔준다.

CareerViewTable .js

import { useContext } from "react";
import { UserContext } from "../../App";

const CareerViewTable = () => {
    const { accessToken, setAccessToken } = useContext(UserContext);
useEffect(() => {
        //CareerViewTable이 최초 렌더링 될때 express 한테 careerList를 요청해서 받아온다.
        const fetchCareerList = async () => {
            if (accessToken === null) return; //accessToken이 null이면 아래 코드 실행 안함 바로 종료
            try {
                let res = await axios.get('/api/career',
                    { headers: { Authorization: `Bearer ${accessToken}` } }); //토큰을 보내줘야한다.
                setCareerList(res.data);
            } catch (err) {
                console.log(err);
            }
        }
        fetchCareerList();
    }, [accessToken]);

예를들어, 간단하게 설명하자면, (보통은 더 길게 함수가 쓰이면 사용하면 좋지만, 이것은 예를 들을려고 하는것)

🔎 페이지 이동을 위한 hook 함수

다른데서도 사용가능하게
코드가 많아지면 복잡할수있으니까, 보기좋게 custom hooks를 만들어서 사용할수있다.

  • use로 시작하면 hook함수다.

hooks.js

// 페이지 이동을 위한 hook 함수
export const useMove = (dir)=>{ //다른데서 사용할수있게 export쓰는거 잊지말고.
  const navigate = useNavigate();
  return ()=>{navigate(dir)}
}

// 페이지 이동인데 뒤로가기 못하게 이동하는 hook함수
export const useReplace = (dir)=>{
  const navigate = useNavigate();
  navigate(dir, {replace : true});
}

원래대로라면,

index.js

import { useNavigate } from "react-router-dom";

const IndexPage = () => {
    let navigate = useNavigate();
    return (
        <>
            <h1>main page</h1>
            <button onClick={()=>{
                navigate('/login');
            }}>login</button>
            <button>회원가입하기</button>
            <button>커리어</button>
        </>
    )
}

export default IndexPage;

🔎 변경후

  • 실행 결과가 함수여야지 사용가능

index.js

import { useMove } from "../hooks/hooks";

const IndexPage = ()=>{
  let moveToLoginPage = useMove('/login');
  let moveToJoinPage = useMove('/join');
  let moveToCareerPage = useMove('/career');

  return(
    <>
      <h1>메인페이지 입니다</h1>
      <button onClick={moveToLoginPage}>로그인하기</button>
      <button onClick={moveToJoinPage}>회원가입하기</button>
      <button onClick={moveToCareerPage}>커리어</button>
    </>
  )
}

export default IndexPage;

Hook 함수❓

일반 함수에서 사용 🚫
콜백 함수에서 사용 🚫

  • 그럼 어디서 사용하면 될까?
    • useEffect 나 다른 use훅함수가 그려질때 사용
profile
나를위한 노트필기 📒🔎📝

0개의 댓글