230627 - React(์‡ผํ•‘๋ชฐ)

๋ฐฑ์Šน์—ฐยท2023๋…„ 6์›” 27์ผ
0

๐Ÿšฉ React

๋ฆฌ์•กํŠธ์™€ ํŒŒ์ด์–ด๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‡ผํ•‘๋ชฐ ์‚ฌ์ดํŠธ ๊ตฌํ˜„ํ•˜๊ธฐ 2์ผ์ฐจ

๐Ÿ“ ์„ค๋ช…

  • react์™€ firebase๋ฅผ ์ด์šฉํ•˜์—ฌ ์‡ผํ•‘๋ชฐ ์‚ฌ์ดํŠธ ๊ตฌํ˜„ํ•˜๊ธฐ
  • ์ƒ๋‹จ ํ—ค๋”์™€ ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ”, firebase ๋กœ๊ทธ์ธ/๋กœ๊ทธ์•„์›ƒ ๊ตฌํ˜„
  • ๋กœ๊ทธ์ธํ•˜๋ฉด ๋กœ๊ทธ์•„์›ƒ์œผ๋กœ ๊ธ€์”จ ๋ณ€๊ฒฝ๋˜๊ฒŒ ๊ตฌํ˜„(๋ฐ˜๋Œ€๋„ ๊ตฌํ˜„)


โœ’๏ธ ์ฝ”๋“œ ์ž‘์„ฑ

์ž…๋ ฅ

App.js

import './App.css';
import { Outlet } from 'react-router-dom';
import Navbar from './components/Navbar';

function App() {
  return (
    <>
      <Navbar />
      <Outlet />
    </>
  );
}

export default App;



index.css

@tailwind base;
@tailwind components;
@tailwind utilities;

@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+KR&family=Playfair+Display&display=swap");

body {
  margin: 0;
  font-family: Noto Sans KR;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;

  @apply text-slate-900;
}

#root {
  @apply w-full text-inherit;
}



index.js

import React from "react";
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import "./index.css";
import App from "./App";
import NotFound from "./pages/NotFound";
import Home from "./pages/Home";
import AllProducts from "./pages/AllProducts";
import NewProduct from "./pages/NewProduct";
import ProductDetail from "./pages/ProductDetail";
import MyCart from "./pages/MyCart";

const router = createBrowserRouter([
  {
    path: "/",
    element: <App />,
    errorElement: <NotFound />,
    children: [
      { index: true, path: "/", element: <Home /> },
      { path: "/products", element: <AllProducts /> },
      { path: "/products/new", element: <NewProduct /> },
      { path: "/products/:id", element: <ProductDetail /> },
      { path: "/cart", element: <MyCart /> },
    ],
  },
]);

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);



Navbar.jsx

import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { HiPencilAlt } from "react-icons/hi";
import { login, logout, onUserStateChange } from "../api/firebase";
import User from "./User";
import Button from "./ui/Button";

// * 1-1. useState ์„ ์–ธ
export default function Navbar() {
  const [user, setUser] = useState();

  // * 2. ํ™”๋ฉด์ด ๋งˆ์šดํŠธ๋  ๋•Œ(reload๋  ๋•Œ) ๋กœ๊ทธ์ธ์ด ๋˜์–ด์žˆ๋Š”์ง€ ์•„๋‹Œ์ง€ ์ƒํƒœ๋ฅผ ์•Œ์•„๋ณด๋Š” ํ•จ์ˆ˜ ํ˜ธ์ถœ
  useEffect(() => {
    onUserStateChange((user) => {
      setUser(user);
      console.log("user? : ", user); // admin์„ ๋งŒ๋“ค๊ณ  ์‹ถ์€ ์œ ์ €์˜ uid๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์„ฑ
    });
  }, []);

  // * 1-2. onClick์— login ํ•จ์ˆ˜๋ฅผ ๋„ฃ์ง€ ์•Š๊ณ  ์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๋Š” ์ด์œ ๋Š” firebase.js์— ์žˆ๋Š” user๋ฅผ ๋ฐ›์•„์™€์„œ useState์— ์ง‘์–ด๋„ฃ๊ธฐ ์œ„ํ•จ
  /**
   * ๋กœ๊ทธ์ธํ•  ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ํ•จ์ˆ˜
   */
  // ๋ฆฌํŒฉํ† ๋ง
  // const handleLogin = () => {
  //   login().then(setUser);
  // };
  // const handleLogout = () => {
  //   logout().then(setUser); // useState์˜ user๋ฅผ ๋น„์šด๋‹ค. (null ์ƒํƒœ๋กœ ๋งŒ๋“ฆ)
  // };

  return (
    <div className="border-b border-t-slate-300">
      <div className="w-full max-w-screen-2xl m-auto">
        <header className="flex justify-between items-center p-5">
          <h1 className="text-3xl font-logoFont tracking-widest">
            RALPH<span className="pl-3 md:pl-6">LAUREN</span>
          </h1>

          <nav className="flex items-center gap-4">
            <Link to="/products">Product</Link>
            <Link to="/cart">Cart</Link>
            {/* // * 5. isAdmin์ด true์ผ ๋•Œ๋งŒ ๋ณด์ด๋„๋ก */}
            {user && user.isAdmin && (<Link to="/products/new"><HiPencilAlt /></Link>)}

            {/* // *3. User.jsx - user๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ์‹คํ–‰ */}
            {user && <User user={user} />}

            {/*// * 1-2. */}
            {/* // * 2-1. ๋”ฐ๋กœ ์„ ์–ธํ•˜์ง€ ์•Š๊ณ  firebase ์•ˆ์— ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ”๋กœ ํ˜ธ์ถœ */}
            {/* // * 6. Button ์ปดํฌ๋„ŒํŠธ๋กœ ์ „ํ™˜ */}
            {!user && <Button onClick={login} text={"login"} />}
            {user && <Button onClick={logout} text={"logout"} />}
          </nav>
        </header>
      </div>
    </div>
  );
}



User.jsx

import React from "react";

// * 3. ์œ ์ €๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” User.jsx ๋งŒ๋“ฆ
export default function User({ user: { displayName, photoURL } }) {
  // console.log("user: ", user);
  return (
    // shrink : ๋ถ€๋ชจ ์˜์—ญ์ด ์ค„๋ฉด ์ž์‹ item๋“ค๋„ ๊ฐ™์ด ์ค„์–ด๋“ฆ - shrink-0์€ ๊ทธ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ด์คŒ
    <div className="flex items-center shrink-0">
      <img
        className="w-10 h-10 rounded-full mr-2"
        src={photoURL}
        alt={displayName}
      />
      <span className="hidden md:block">{displayName}</span>
    </div>
  );
}



Button.jsx

import React from "react";

export default function Button({ onClick, text }) {
  return (
    <button
      className="bg-brand text-white py-2 px-4 rounded-full hover:brightness-200 text-sm"
      onClick={onClick}
    >
      {text}
    </button>
  );
}



firebase.js

import { initializeApp } from "firebase/app";
import {
  getAuth,
  signInWithPopup,
  GoogleAuthProvider,
  signOut,
  onAuthStateChanged,
} from "firebase/auth";
import { getDatabase, get, ref } from "firebase/database";

const firebaseConfig = {
  apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
  databaseURL: process.env.REACT_APP_FIREBASE_DB_URL,
};

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

// * 1-1. ํ•จ์ˆ˜ ์„ ์–ธ์€ firebase ์•ˆ์—์„œ ํ•จ
export function login() {
  return signInWithPopup(auth, provider) // ์•„๋ž˜ ๋ฆฌํ„ด๋œ user ๊ฐ’์„ ๋ฐ›์•„์™€์„œ ๊ฒฐ๊ณผ๊ฐ’์œผ๋กœ ๋‚ด๋ณด๋‚ด๊ธฐ ์œ„ํ•ด return ์ž‘์„ฑ
    .then((result) => {
      // ๋กœ๊ทธ์ธ ๋˜์—ˆ๋Š”์ง€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์–ด์˜ด
      const user = result.user;
      // console.log("user? : ", user);
      return user;
    })
    .catch(console.error);
}

export async function logout() {
  return signOut(auth) // null๊ฐ’์ด ๋ฆฌํ„ด๋จ
    .then(() => null); // null
  // .catch((error) => {});
}

// * 2. ํ™”๋ฉด์ด ๋งˆ์šดํŠธ๋  ๋•Œ(reload๋  ๋•Œ) ๋กœ๊ทธ์ธ์ด ๋˜์–ด์žˆ๋Š”์ง€ ์•„๋‹Œ์ง€ ์ƒํƒœ๋ฅผ ์•Œ์•„๋ณด๋Š” ํ•จ์ˆ˜ ์„ ์–ธ (callback)
export function onUserStateChange(callback) {
  // ๋งŒ์•ฝ user๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ
  onAuthStateChanged(auth, async (user) => {
    // ? 1. ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ํ•œ ๊ฒฝ์šฐ
    // user && adminUser(user);
    const updatedUser = user ? await adminUser(user) : null ;
    callback(updatedUser);
  });
}

// * 4. ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฌ์šฉ ์„ ์–ธ
const database = getDatabase(app);

// ? 2. ์‚ฌ์šฉ์ž๊ฐ€ ์–ด๋“œ๋ฏผ ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ํ™•์ธ -> isAdmin์„ user ์•ˆ์— ๋„ฃ์Œ
// ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์•ˆ์— ์žˆ๋Š” admins๋ฅผ ์ฐธ์กฐํ•˜๋Š” ํ•จ์ˆ˜
function adminUser(user) {
  // database ์•ˆ์— admins key๊ฐ€ ์žˆ์Œ
  return get(ref(database, "admins")) // ref(database์ด๋ฆ„, key๊ฐ’)
    // ๋งŒ์•ฝ snapshot(๊ฒฐ๊ณผ๊ฐ’)์ด ์กด์žฌํ•˜๋ฉด
    .then((snapshot) => {
      if (snapshot.exists()) {
        const admins = snapshot.val(); // snapshot์˜ value
        // admins๊ฐ€ user.uid๋ฅผ ํฌํ•จํ•จ
        const isAdmin = admins.includes(user.uid);
        return { ...user, isAdmin }; // ๋งŽ์€ user๋“ค์˜ ํ•ญ๋ชฉ ์ค‘์—์„œ isAdmin๋งŒ ๋ผ์›Œ๋„ฃ์Œ
      }
    });
}

/*
  1. ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ํ•œ ๊ฒฝ์šฐ
  2. ์‚ฌ์šฉ์ž๊ฐ€ ์–ด๋“œ๋ฏผ ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ํ™•์ธ
  3. ์‚ฌ์šฉ์ž์—๊ฒŒ ์•Œ๋ ค์คŒ
 */



js



์ถœ๋ ฅ

  • ์ด๋ฏธ์ง€๋กœ ๋Œ€์ฒด

  • ์ด๋ฏธ ๋กœ๊ทธ์ธ ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ž˜์™€ ๊ฐ™์ด ๋กœ๊ทธ์ธ ์ฐฝ์ด ๋œจ์ง€ ์•Š๊ณ  ๋ฐ”๋กœ ๋กœ๊ทธ์•„์›ƒ์œผ๋กœ ๋„˜์–ด๊ฐ


๐Ÿ”— ์ฐธ๊ณ  ๋งํฌ & ๋„์›€์ด ๋˜๋Š” ๋งํฌ






profile
๊ณต๋ถ€ํ•˜๋Š” ๋ฒจ๋กœ๊ทธ

1๊ฐœ์˜ ๋Œ“๊ธ€

comment-user-thumbnail
2023๋…„ 8์›” 31์ผ

์•ˆ๋…•ํ•˜์„ธ์š”. ๋“œ๋ฆผ์ฝ”๋”ฉ ์šด์˜์ž ์ž…๋‹ˆ๋‹ค.
์ˆ˜๊ฐ•์ƒ๋ถ„์ด ๋ฐœ๊ฒฌํ•˜์—ฌ ์‹ ๊ณ ๊ฐ€ ๋“ค์–ด์™€ ๋ธ”๋กœ๊ทธ์— ๋Œ€ํ•ด ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
ํ•ด๋‹น ๊ฒŒ์‹œ๊ธ€๊ณผ ๋ธ”๋กœ๊ทธ์— ์˜ฌ๋ฆฌ์‹  ๋‹ค์ˆ˜์˜ ๊ธ€๋“ค์ด ๋“œ๋ฆผ์ฝ”๋”ฉ ์•„์นด๋ฐ๋ฏธ ์œ ๋ฃŒ ๊ฐ•์˜์˜ ๋‚ด์šฉ์„ ์ •๋ฆฌ ํ•˜์‹ ๊ฑธ๋กœ ํ™•์ธ๋ฉ๋‹ˆ๋‹ค.
์ด๋Š” ์—„์—ฐํžˆ ์ €์ž‘๊ถŒ๋ฒ• ์œ„๋ฐ˜์ž…๋‹ˆ๋‹ค.
ํ•ด๋‹น ํฌ์ŠคํŠธ๋“ค์„ ๋น„๊ณต๊ฐœ ๋˜๋Š” ์‚ญ์ œ ์ฒ˜๋ฆฌํ›„ info@dream-coding.com ๋กœ ๋ฉ”์ผ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.
์‹œ์ผ๋‚ด์— ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์œผ์‹œ๋ฉด ๊ฐ•์˜ ์ทจ์†Œ ๋ฐ ๋ฒ•์  ๋Œ€์‘ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
๊ฐ•์˜ ์‹œ์ž‘์ „ ์ €์ž‘๊ถŒ๋ฒ•์— ๊ด€๋ จํ•ด์„œ ๋ธ”๋กœ๊ทธ์— ์ •๋ฆฌํ•˜์ง€ ๋ง์•„ ๋‹ฌ๋ผ๊ณ  ์•ˆ๋‚ดํ•ด ๋“œ๋ ธ์Šต๋‹ˆ๋‹ค.
https://academy.dream-coding.com/courses/player/react/lessons/1462

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ