MySQL

React, MySQL 13일차 (글 작성자 정보 보기)

수연 (Suyeon) 2023. 10. 30. 23:00
반응형

PedroTech님의 풀스택 강의 13일 차를 참고했습니다.

 

📌 Users 테이블의 id를 Posts와 연결하기

// ./server/models/Users.js

module.exports = (sequelize, DataTypes) => {
  const Users = sequelize.define("Users", {...
  });

  Users.associate = (models) => {
    Users.hasMany(models.Likes, {...
    });

    // Users의 id를 Posts와 1:N 관계로 연결합니다.
    Users.hasMany(models.Posts, {
      onDelete: "cascade",
    });
  };
  return Users;
};

Users 테이블의 id Posts 테이블의 id를1:N 관계로 연결해 줍니다.

 

 

 

📌 글 작성하는 화면 수정하기

// ./client/src/pages/createPost.js

import React from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import "./CreatePost.css";
import * as Yup from "yup";
import axios from "axios";
import { useNavigate } from "react-router-dom";

function CreatePost() {
  let navigate = useNavigate();

  const initialValues = {
    title: "",
    postText: "",
  };

  const validationSchema = Yup.object().shape({
    title: Yup.string().required(),
    postText: Yup.string().required(),
  });

  // Posts 테이블에 사용자 정보가 필요하기에 headers로 전달해줍니다.
  const onSubmit = (data) => {
    axios
      .post("http://localhost:3001/posts", data, {
        headers: {
          accessTocken: localStorage.getItem("accessTocken"),
        },
      })
      .then(() => {
        navigate("/");
      });
  };

  return (
    {/*
      사용자 이름은 따로 추가해줄 것이기 때문에
      사용자명 입력창은 삭제합니다.
    */}
    <div className="createPostPage">
      <Formik
        initialValues={initialValues}
        onSubmit={onSubmit}
        validationSchema={validationSchema}
      >
        <Form>
          <label htmlFor="inputTitle">Title: </label>
          <ErrorMessage name="title" component="span" />
          <Field
            autoComplete="off"
            id="inputTitle"
            className="createPostItem"
            name="title"
            placeholder="(Ex. Title...)"
          />

          <label htmlFor="inputContent">Post: </label>
          <ErrorMessage name="postText" component="span" />
          <Field
            autoComplete="off"
            id="inputContent"
            className="createPostItem"
            name="postText"
            placeholder="(Ex. Content...)"
          />

          <button type="submit" className="createPostBtn">
            Create Post
          </button>
        </Form>
      </Formik>
    </div>
  );
}

export default CreatePost;
기존과 달라진 점 설명
1. 사용자명 입력창, 변수 제거
: router에서 따로 사용자 이름과 id를 저장하기 때문에 제거했습니다.
2. 작성 시 사용자 정보 전달: axios.post 할 때 headers로 사용자 정보를 전달해 줘서 지금 로그인되어있는 사용자가 누구인지 식별 가능합니다.

 

 

 

📌 글 작성한 뒤 전달되는 router 수정하기(Posts)

// ./server/routes/Posts.js

const express = require("express");
const router = express.Router();
const { Posts, Likes } = require("../models");
const { validateTocken } = require("../middlewares/AuthMiddleware");

router.get("/", async (req, res) => {...
});

router.get("/byId/:id", async (req, res) => {...
});

router.get("/byuserId/:id", async (req, res) => {...
});

// 사용자 이름과 UserId를 추가해서 저장해줍니다.
router.post("/", validateTocken, async (req, res) => {
  const post = req.body;
  post.username = req.user.username;
  post.UserId = req.user.id;
  await Posts.create(post); //squelize function create
  res.json(post);
});

module.exports = router;
사용자가 작성한 글이 무엇인지 구별하기 위해 사용자 이름id를 추가해 줍니다.
사용자 정보를 headers로 전달된 데이터에서 가져옵니다.

 

 

 

📌 사용자 정보 가져오는 router 생성하기(Users)

// ./server/routes/Users.js

const express = require("express");
const router = express.Router();
const { Users } = require("../models");
const bcrypt = require("bcrypt");
const { sign } = require("jsonwebtoken");
const { validateTocken } = require("../middlewares/AuthMiddleware");

router.post("/", async (req, res) => {...
});

router.post("/login", async (req, res) => {...
});

router.get("/auth", validateTocken, (req, res) => {...
});

// 사용자 정보를 가져오는 router입니다.
router.get("/basicInfo/:id", async (req, res) => {
  const id = req.params.id;

  // exclude로 password라는 필드 데이터는 가져오지 않겠다는 의미입니다.
  const basicInfo = await Users.findByPk(id, {
    attributes: { exclude: ["password"] },
  });

  res.json(basicInfo);
});

module.exports = router;
http://localhost:1234/auth/basicInfo/10이 호출되면 사용자 정보를 가져옵니다.
exclude는 테이블에서 제외하고 싶은 필드를 지정해 줍니다.

 

 

 

📌 사용자가 작성한 글 가져오는 router 생성하기(Posts)

// ./server/routes/Post.js

const express = require("express");
const router = express.Router();
const { Posts, Likes } = require("../models");
const { validateTocken } = require("../middlewares/AuthMiddleware");

// Posts 테이블에 저장된 데이터를 다 가져옵니다.
router.get("/", async (req, res) => {
  // Likes를 배열 형식으로 포함해서 데이터를 가져옵니다.
  const listOfPosts = await Posts.findAll({ include: [Likes] });
  res.json(listOfPosts);
});

router.get("/byId/:id", async (req, res) => {...
});

router.get("/byuserId/:id", async (req, res) => {...
});

router.post("/", validateTocken, async (req, res) => {...
});

module.exports = router;
include다른 테이블에서 포함하고 싶은 데이터가 있을 때 사용합니다.
[Likes] 이렇게 하면 해당 데이터를 배열 형태로 가져옵니다.

 

 

 

📌 Profile 화면 생성, route 경로 지정하기

// ./client/src/pages/Home.js

import React from "react";
import axios from "axios";
import { useEffect, useState } from "react";
import { Link, useNavigate } from "react-router-dom";

function Home() {
  const [listOfPosts, setListOfPosts] = useState([]);
  let navigate = useNavigate();

  useEffect(() => {...
  }, []);

  const likeAPost = (postId) => {...
  };

  return (
    <div>
      {listOfPosts.map((value, key) => {
        return (
          <div className="posts" key={key}>
            <div className="title">{value.title}</div>
            <div
              className="body"
              onClick={() => {
                navigate(`/post/${value.id}`);
              }}
            >
              {value.postText}
            </div>
            <div className="footer">
              {/*
                UserId를 파라미터로 전달합니다.
              */}
              <Link to={`/profile/${value.UserId}`}>
                {value.username}
              </Link>
              <div className="likeContainer">
                <button
                  type="button"
                  className="likeBtn"
                  onClick={() => {
                    likeAPost(value.id);
                  }}
                >
                  Like
                </button>
                <label>{value.Likes.length}</label>
              </div>
            </div>
          </div>
        );
      })}
    </div>
  );
}

export default Home;
// ./client/src/pages/Profile.js

import React, { useEffect, useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import axios from "axios";

function Profile() {
  let { id } = useParams();
  const [username, setUsername] = useState("");
  const [listOfPosts, setListOfPosts] = useState([]);
  const navigate = useNavigate();

  useEffect(() => {
    // 사용자 정보를 가져옵니다.(UserId를 전달합니다.)
    axios.get(`http://localhost:3001/auth/basicInfo/${id}`).then((respose) => {
      setUsername(respose.data.username);
    });

    // 사용자가 작성한 글을 가져옵니다.(UserId를 전달합니다.)
    axios.get(`http://localhost:3001/posts/byuserId/${id}`).then((respose) => {
      setListOfPosts(respose.data);
    });
  }, []);

  return (
    <div className="profilePageContainer">
      <div className="basicInfo">
        <h1>Username: {username}</h1>
      </div>
      <div className="listOfPosts">
        {listOfPosts.map((value, key) => {
          return (
            <div className="posts" key={key}>
              <div className="title">{value.title}</div>
              <div
                className="body"
                onClick={() => {
                  navigate(`/post/${value.id}`);
                }}
              >
                {value.postText}
              </div>
              <div className="footer">
                {value.username}
                <div className="likeContainer">
                  <label>{value.Likes.length}</label>
                </div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

export default Profile;
// ./client/src/App.js

import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
import Home from "./pages/Home";
import "./App.css";
import CreatePost from "./pages/CreatePost";
import Post from "./pages/Post";
import Login from "./pages/Login";
import Registration from "./pages/Registration";
import Profile from "./pages/Profile";
import { AuthContext } from "./helpers/AuthContext";
import { useEffect, useState } from "react";
import axios from "axios";

function App() {

  const [authState, setAuthState] = useState({...
  });

  useEffect(() => {...
  }, []);

  const logout = () => {...
  };

  return (
    <div className="App">
      <AuthContext.Provider value={{ authState, setAuthState }}>
        <BrowserRouter>
          <div className="nav">...
          </div>

          <Routes>
            <Route path="/" exact element={<Home />} />
            <Route path="/createpost" exact element={<CreatePost />} />
            <Route path="/post/:id" exact element={<Post />} />
            <Route path="/login" exact element={<Login />} />
            <Route path="/registration" exact element={<Registration />} />
            
            {/* 사용자 정보를 볼 수 있도록 경로를 지정합니다. */}
            <Route path="/profile/:id" exact element={<Profile />} />
          </Routes>
        </BrowserRouter>
      </AuthContext.Provider>
    </div>
  );
}

export default App;
Home.js 설명
1. Link 태그
: App.js에서 지정한 경로로 UserId를 파라미터를 전달합니다.

Profile.js 설명

1. axios.get: Users와 Posts router로 UserId를 파라미터로 전달해서 사용자 이름과 사용자가 작성한 글 목록을 가져옵니다.

App.js 설명
1. <Route path="/profile/:id" exat element={<Profile/>}/>
: :id는 전달된 파라미터를 의미합니다. 그리고 해당 경로로 이동하면 Profile.js로 이동합니다.

 

 

 

📌 결과

작성자 이름을 클릭하기 전 화면입니다.

 

작성자 이름을 클릭한 화면입니다.

 

 

 

📌 느낀 점

각각의 테이블에서 id만 연결해 줘도 여러 정보를 가져올 수 있다는 걸 깨달았습니다..!

저는 직접 하나하나 다 넣을 생각을 했는데 덕분에 복잡하지 않게 데이터 스키마를 구현할 수 있을 것 같습니다.😊

다음은 사용자의 비밀번호를 수정하는 방법에 대해 배울 것 같은데 이번엔 제가 할 수 있는 것들은 미리 다 작성한 뒤 강의를 들으면 더 도움 될 것 같아 이런 식으로 공부를 해보려고 합니다.

728x90