공지사항 게시판

essential·2023년 5월 1일
1

project

목록 보기
1/4

어려워 보였던 걸 미리 작성해놨는데 ...

  1. 공지사항 게시판 내 게시글 검색
  2. 관리자 접속 시에만 글 작성 버튼 노출

먼저 2번인 관리자인 사용자에게만 버튼을노출 시키는 건

function NoticeBoard () { const [session, setSession] = useState("admin"); //버튼 보이게 하기 위해 작성 추후 삭제 //const session = sessionStorage.getItem("id"); 로그인 id 받아올 때 쓰면 됨

이런 방식으로 sessionStorage 사용해서 사용자의 ID 가 admin 이면 보이게 하는 걸로 타협 봤다.
권한을 나누거나 하는 방식은 나중에 구현하는 걸로…


검색 기능은 어떻게.. 구글링 하며 보고 있는데 이해가 잘 .. 안 간다.
일단 백단을 구현하지 않아서 테스트가 어렵고 공지사항 리스트를 보면서 수정하기 위해
리스트 내용에 mockdata를 넣어놔서 그런지 더 난해했다.

내가 원하는 검색 기능은 사용자가 입력하면 검색 페이지 이동이 아닌 리스트를 새로 보여주는?
그런 걸 원했는데… 처음에 구조를 NoticeBoard 안에 CardList,SearchBar 컴포넌트를 두려고 짜서
데이터를 NoticeBoard 에서 받아야할지 SearchBar에서 받고 CardList 에게 넘겨서 그걸 불러와야하는지 뭐 어쩌라는 건지? 모르겠어서 일단.

SearchBar 는 컴포넌트화 하지말고 NoticeBoard 컴포넌트 안에서 구현하기로 했다 ,,
NoticeBoard 에는 (공지사항.이 네글자와 렌더링 하는 코드만 있었는데 차라리 나은 선택일지도)
정확한 용어로 설명하고 싶은데 그것도 어렵고 지금 총체적 난국이라는 느낌…

총체적 난국의 대표적인 예 .

NoticeBoard.js

import React, { useState, useEffect } from "react";
import styled from "styled-components";
import CardList from "../CardList";
import Button from "react-bootstrap/Button";
import { BsSearch } from "react-icons/bs";
import { Form } from "react-bootstrap";
import axios from "axios";

const SearchBar = styled(Form.Control)`
  width: 214px;
  height: 25px;
  padding: 5px 30px 5px 10px;
  border-radius: 20px;
`;

const NoticeContainer = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
`;

const BoardWrapper = styled.div`
  display: flex;
  align-items: center;
  margin-bottom: 5px;
  margin-top: 20px;
  flex-direction: column;
`;

const TitleSearchWrap = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  width: 400px;
`;

const Title = styled.h2`
  font-size: 25px;
  margin: 0;
  text-align: center;
`;

function NoticeBoard () {
  const [session, setSession] = useState("admin");
  
  //버튼 보이게 하기 위해 작성 추후 삭제
  //const session = sessionStorage.getItem("id"); 로그인 id 받아올 때 쓰면 됨 

  const [search, setsearch] = useState([
    {
      id: 5,
      title: "테스트입니다",
      content: "테스트",
      date: "2023-04-25",
      author: "관리자2",
      views: 10,
      comments: 2,
      thumbnailUrl: "https://via.placeholder.com/150",
    },
  ]);

  const [cardDatas, setCardData] = useState([]);
  const [userInput, setUserInput] = useState("");

  // 데이터 로딩
  useEffect(() => {
    axios
    .get("/search")
    .then((response) => {
      setCardData(response.data);
    })
    .catch((error) => {
      console.error(error);
    });
  }, []);

  //props로 넘겨줄 handleChange 메소드 정의
  const handleChange = (e) => {
    setUserInput(e.target.value);
  };
  const filterCardData= cardDatas.filter((data) => {
    if(!data.title.includes(userInput)) {
      return cardDatas;
      } else
        return data.title.includes(userInput);
  });

  //

  return (
    <NoticeContainer>
        <BoardWrapper>
          <TitleSearchWrap>
            <Title>공지사항</Title>
            {/* <SearchBarcm /> */}
            {search.map((search) => (
        <div style={{ position: "relative" }}>
          <BsSearch
            style={{
              position: "absolute",
              left: "88%",
              top: "50%",
              transform: "translateY(-58%)",
              fontSize: "18px",
              color: "#6c757d",
              cursor: "pointer", 
            }}
            onClick={handleChange}
          />
          <SearchBar
            type="text"
            placeholder="검색어를 입력하세요"
            onChange={handleChange} // 검색어 입력 시 검색 로직 호출
          />
        </div>
      ))}
          </TitleSearchWrap>
          <CardList data={filterCardData} />
          {session === "admin" && <Button variant="primary" size="sm">글쓰기</Button>}
        </BoardWrapper>
    </NoticeContainer>
  );
};

export default NoticeBoard;

CardList.js

import React, { useState, useEffect } from "react";
import styled from "styled-components";
import axios from "axios";

const NoticeList = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: 500px;

  .cardti {
    display: flex;
    align-items: center;
    margin-left: 10px;
    margin-top: 5px;
    vertical-align: middle;
  }
`;

const NoticeCard = styled.div`
  display: flex;
  flex-direction: row;
  border: 1px solid #c9c1c1;
  border-radius: 10px;
  padding: 10px;
  margin: 10px 0;
  width: 100%;
  height: 100%;
`;

const Thumbnail = styled.img`
  width: 130px;
  height: 85px;
  margin-right: 10px;
  margin-top: 12px;
  flex-shrink: 0;
`;

const ContentContainer = styled.div`
  display: flex;
  flex-direction: column;
  height: 100%;
  flex: 1;
`;

const CardTitle = styled.div`
  margin: 0;
`;

const CardComments = styled.div`
  margin-left: 4px;
  font-size: 14px;
  color: #999;
  line-height: 21px;
`;

const CardInfoContainer = styled.div`
  display: flex;
  justify-content: space-between;
  margin-top: 3px;
  flex-direction: column;
`;

const CardInfo = styled.p`
  margin: 0 10px;
  font-size: 12px;
`;

const CarDdate = styled(CardInfo)`
  margin-top: 10px;
  text-align: right;
`;

const CardList = (props) => {
  const [lists, setlists] = useState([
    {
      id: 1,
      title: "테스트입니다.",
      content: "테스트입니다.",
      date: "2023-04-25",
      author: "홍길동",
      views: 10,
      comments: 3,
      thumbnailUrl: "https://via.placeholder.com/150",
      recommends: 10,
    },
    {
      id: 2,
      title: "테스트입니다",
      content: "테스트",
      date: "2023-04-25",
      author: "홍길동",
      views: 10,
      comments: 2,
      thumbnailUrl: "https://via.placeholder.com/150",
      recommends: 10,
    },
    {
      id: 3,
      title: "테스트입니다",
      content: "테스트",
      date: "2023-04-25",
      author: "admin",
      views: 10,
      comments: 5,
      thumbnailUrl: "https://via.placeholder.com/150",
      recommends: 10,
    },

    {
      id: 4,
      title: "슬기",
      content: "테스트",
      date: "2023-04-25",
      author: "관리자",
      views: 10,
      comments: 2,
      thumbnailUrl: "https://via.placeholder.com/150",
      recommends: 10,
    },

    {
      id: 5,
      title: "테스트입니다",
      content: "테스트",
      date: "2023-04-25",
      author: "관리자2",
      views: 10,
      comments: 2000,
      thumbnailUrl: "https://via.placeholder.com/150",
      recommends: 10,
    },
  ]);

  useEffect(() => {
    axios
      .get("/lists")
      .then((response) => {
        setlists(response.data);
      })
      .catch((error) => {
        console.error(error);
      });
  }, []);
  return (
    <NoticeList>
      {lists.map((notice) => ( //props 수정
        <NoticeCard key={notice.id}>
          <Thumbnail src={notice.thumbnailUrl} alt={notice.title} />
          <ContentContainer>
            <div className="cardti">
              <CardTitle>{notice.title}</CardTitle>
              <CardComments>({notice.comments})</CardComments>
            </div>
            <CardInfoContainer>
              <CardInfo>작성자 {notice.author}</CardInfo>
              <CardInfo>조회수 {notice.views}</CardInfo>
              <CardInfo>추천 수 {notice.recommends}</CardInfo>
            </CardInfoContainer>
            <CarDdate>{notice.date}</CarDdate>
          </ContentContainer>
        </NoticeCard>
      ))}
    </NoticeList>
  );
};

export default CardList;

SearchBarcm.js

import React, { useState, useEffect } from "react";
import styled from "styled-components";
import axios from "axios";

const NoticeList = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: 500px;

  .cardti {
    display: flex;
    align-items: center;
    margin-left: 10px;
    margin-top: 5px;
    vertical-align: middle;
  }
`;

const NoticeCard = styled.div`
  display: flex;
  flex-direction: row;
  border: 1px solid #c9c1c1;
  border-radius: 10px;
  padding: 10px;
  margin: 10px 0;
  width: 100%;
  height: 100%;
`;

const Thumbnail = styled.img`
  width: 130px;
  height: 85px;
  margin-right: 10px;
  margin-top: 12px;
  flex-shrink: 0;
`;

const ContentContainer = styled.div`
  display: flex;
  flex-direction: column;
  height: 100%;
  flex: 1;
`;

const CardTitle = styled.div`
  margin: 0;
`;

const CardComments = styled.div`
  margin-left: 4px;
  font-size: 14px;
  color: #999;
  line-height: 21px;
`;

const CardInfoContainer = styled.div`
  display: flex;
  justify-content: space-between;
  margin-top: 3px;
  flex-direction: column;
`;

const CardInfo = styled.p`
  margin: 0 10px;
  font-size: 12px;
`;

const CarDdate = styled(CardInfo)`
  margin-top: 10px;
  text-align: right;
`;

const CardList = (props) => {
  const [lists, setlists] = useState([
    {
      id: 1,
      title: "테스트입니다.",
      content: "테스트입니다.",
      date: "2023-04-25",
      author: "홍길동",
      views: 10,
      comments: 3,
      thumbnailUrl: "https://via.placeholder.com/150",
      recommends: 10,
    },
    {
      id: 2,
      title: "테스트입니다",
      content: "테스트",
      date: "2023-04-25",
      author: "홍길동",
      views: 10,
      comments: 2,
      thumbnailUrl: "https://via.placeholder.com/150",
      recommends: 10,
    },
    {
      id: 3,
      title: "테스트입니다",
      content: "테스트",
      date: "2023-04-25",
      author: "admin",
      views: 10,
      comments: 5,
      thumbnailUrl: "https://via.placeholder.com/150",
      recommends: 10,
    },

    {
      id: 4,
      title: "슬기",
      content: "테스트",
      date: "2023-04-25",
      author: "관리자",
      views: 10,
      comments: 2,
      thumbnailUrl: "https://via.placeholder.com/150",
      recommends: 10,
    },

    {
      id: 5,
      title: "테스트입니다",
      content: "테스트",
      date: "2023-04-25",
      author: "관리자2",
      views: 10,
      comments: 2000,
      thumbnailUrl: "https://via.placeholder.com/150",
      recommends: 10,
    },
  ]);

  useEffect(() => {
    axios
      .get("/lists")
      .then((response) => {
        setlists(response.data);
      })
      .catch((error) => {
        console.error(error);
      });
  }, []);
  return (
    <NoticeList>
      {lists.map((notice) => ( //props 수정
        <NoticeCard key={notice.id}>
          <Thumbnail src={notice.thumbnailUrl} alt={notice.title} />
          <ContentContainer>
            <div className="cardti">
              <CardTitle>{notice.title}</CardTitle>
              <CardComments>({notice.comments})</CardComments>
            </div>
            <CardInfoContainer>
              <CardInfo>작성자 {notice.author}</CardInfo>
              <CardInfo>조회수 {notice.views}</CardInfo>
              <CardInfo>추천 수 {notice.recommends}</CardInfo>
            </CardInfoContainer>
            <CarDdate>{notice.date}</CarDdate>
          </ContentContainer>
        </NoticeCard>
      ))}
    </NoticeList>
  );
};

export default CardList;

아직 작성 중이긴 한데 …… 수정은 백단 구현하면 해보는 걸로 하고 CardDetail 페이지 구현을 해야할 것 같다 ㅎㅎ

profile
essential

0개의 댓글