간단한 블로그 형태 만들기

✒️ 2025-05-28 10:04 내용 수정


1. 데이터를 항목별로 분리했을 때

/* eslint-disable */
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css';
import {useState} from 'react';

function App() {
  
  let [title, setTitle] = useState(['부평 맛집', '오늘의 날씨', '봄 패션']);
  let [date, setDate] = useState(['2024-01-15', '2024-02-27', '2024-03-05']);
  let [likeHit, setLikeHit] = useState([0,0,0]);
  let [content, setContent] = useState(['A동', '맑음', '코트']);
  
  let [pick, setPick] = useState(0);
  let [detail, setDetail] = useState(false);
  let [inputTitle, setInputTitle] = useState('');
  let [inputDate, setInputDate] = useState('');
  let [inputContent, setInputContent] = useState('');


  function LikeUp(i) { // 좋아요 버튼을 누르면 좋아요가 1씩 증가
    let copy = [...likeHit];
    copy[i]++;
    setLikeHit(copy);
  }

  function postAdd() { // 새 글을 작성하면 각 state에 새 내용을 Array의 push()로 추가
    if(inputTitle != '' && inputDate != '' && inputContent != ''){ // 값이 있을 때만 추가 
	  // 기존 state를 깊은 복사로 복사하기
      let copyTitle = [...title];
      let copyLikeHit = [...likeHit];
      let copyDate = [...date];
      let copyContent = [...content];

	  // 복사한 배열에 새 state 내용을 push로 추가하기
      // 가장 앞에 추가하고 싶다면 unshift()
      copyTitle.push(inputTitle);
      copyLikeHit.push(0);
      copyDate.push(inputDate);
      copyContent.push(inputContent);

	  // setState 함수로 복사한 배열 내용으로 state 업데이트하기
      setTitle(copyTitle);
      setLikeHit(copyLikeHit);
      setDate(copyDate);
      setContent(copyContent);

	  // input 데이터들과 input value 리셋
      setInputTitle('');
      setInputDate('');
      setInputContent('');
      document.getElementsByClassName("input-title")[0].value = '';
      document.getElementsByClassName("input-date")[0].value = '';
      document.getElementsByClassName("input-content")[0].value = '';
    }
  }

  return (
    <>
      <section className='section sec'>
        <div className='container-lg'>
          <h2 className='title'>My Post</h2>
          <div className='row row-cols-2 justify-content-center'>
            <div className='col new-post p-4 m-3'>
	        // 입력을 받으면 input 내의 있는 값을 setState 함수들로 새로 업데이트 해준다.
              <label>제목</label>
              <input type='text' className='form-control input-title' onChange={(e)=>{setInputTitle(e.target.value)}}></input>
              <label>날짜</label>
              <input type='date' className='form-control input-date' onChange={(e)=>{setInputDate(e.target.value)}}></input>
              <label>내용</label>
              <textarea className='form-control input-content' onChange={(e)=>{setInputContent(e.target.value)}}></textarea>
              // 버튼을 누르면 업데이트 된 내용들을 새로 state의 데이터에 추가해준다.
              <button className='btn btn-success' onClick={()=>{postAdd()}}>확인</button>
            </div>
          </div>
          <div className='row row-cols-2'>
            {
              title.map((el, i) => {
                return(
	            // 아래에 선언한 함수에서 태그 내용을 만들고, 여기선 state 데이터들을 넘겨준다.
                  <Box key={i} title={title} date={date} 
                  likeHit={likeHit} LikeUp={LikeUp} i={i} 
                  pick={pick} setPick={setPick}
                  detail={detail} setDetail={setDetail}></Box>
                );
              })
            }
          </div>
          <div className='row'>
            {
	            // 항목을 눌렀을 때만 렌더링 되도록 설정한다.
              (detail) ? <Detail i={pick} title={title} date={date} content={content}></Detail> : null
            }
          </div>
        </div>
      </section>
    </>
  );
}

// 각 포스트 박스의 내용을 만드는 함수
function Box(props) {
// App에서 <Box>로부터 넘어온 데이터들과 함수를 가져온다.
  let {i, title, date, likeHit, LikeUp, pick, setPick, detail, setDetail} = props;

// 항목을 눌렀을 때 이전에 선택한 pick과 동일한 숫자면 닫기를
// 다른 숫자라면 그대로 보여지도록 설정한다.
  function detailOpen(i) {
    if(pick == i) {
      setDetail(!detail);
    } else {
      detail = true;
      setDetail(detail);
    }
    setPick(i);
  }

  return(
    <div className='col box py-3'>
      <div className='gt'>
	    // 제목을 누르면 상세 항목 박스를 보이도록 설정한다.
        <h3 className='post-title' onClick={()=>{detailOpen(i)}}>{title[i]}</h3>
	    // 이모티콘을 누르면 좋아요 숫자가 올라가도록 설정한다.
        <h5 className='like-btn'><span className='emoji' onClick={()=>LikeUp(i)}>👍</span>{likeHit[i]}</h5>
        <p>{date[i]}</p>
      </div>
    </div>
  )
}

// 각 포스트의 상세 내용을 보는 박스를 만드는 함수
function Detail(props) {
// App에서 <Detail>로부터 넘어온 데이터들을 가져온다.
  let {i, title, date, content} = props;

  return(
    <div className='detail'>
      <div className='gt'>
        // 박스 안에 상세 내용을 출력한다.
        <h3>{title[i]}</h3>
        <p>{date[i]}</p>
        <p>{content[i]}</p>
      </div>
    </div>
  )
}

export default App;
*{margin:0; padding:0; box-sizing: border-box;}
ul, ol, li{list-style: none;}
a{text-decoration: none;}

body{background-color: #d4d4d4;}

.header{
  width: 100%; 
  padding: 30px 0;
  background-color: #444;
}

.header a{color:#fff;}

.sec{
  width: 100%;
  padding:50px 0;
}

.sec .new-post{
  background-color: #fff;
  border-radius: 10px;
  box-shadow: 0 0 10px #7e7e7e;
}

.sec .new-post label{
  margin:10px 0;
}


.sec .new-post .btn{
  margin-top:10px;
  float: right;
}

.sec .gt{
  padding: 30px;
  background-color: #fff;
  border-radius: 10px;
  box-shadow: 0 0 10px #7e7e7e;
}

.sec .post-title{
  -ms-user-select: none; /* 글을 누를 때마다 드래그가 되지 않도록 설정 */
  -moz-user-select: none;
  -khtml-user-select: none;
  -webkit-user-select: none;
  user-select: none;
  cursor: pointer; /* 글에 마우스를 올려 놓으면 포인터 모양으로 바뀌게 설정 */
}

.sec .post-title:hover{
  color:blue;
}

.like-btn{
  -ms-user-select: none;
  -moz-user-select: none;
  -khtml-user-select: none;
  -webkit-user-select: none;
  user-select: none;
}

.like-btn .emoji{
  padding-right: 10px;
  cursor: pointer;
}

.detail{
  padding: 30px;
}

react blog 1.png

react blog 2.png

react blog 3.png

react blog 4.png

react blog 5.png


2. 데이터를 객체 형태로 작성했을 때

/* eslint-disable */
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css';
import {useState} from 'react';

function App() {
  // 항목별로 각각 데이터를 만들었을 때
  let [title, setTitle] = useState(['부평 맛집', '오늘의 날씨', '봄 패션']);
  let [date, setDate] = useState(['2024-01-15', '2024-02-27', '2024-03-05']);
  let [likeHit, setLikeHit] = useState([0,0,0]);
  let [content, setContent] = useState(['A동', '맑음', '코트']);
  let [pick, setPick] = useState(0);
  let [detail, setDetail] = useState(false);
  
  let [inputTitle, setInputTitle] = useState('');
  let [inputDate, setInputDate] = useState('');
  let [inputContent, setInputContent] = useState('');

  // 객체 배열인 state 객체 만들기
  let [post, setPost] = useState([
    {
      title: '오늘 배운 내용',
      date: '2024-02-27',
      likeHit: 0,
      content: 'React로 블로그 만들기'
    },
    {
      title: '오늘 날씨',
      date: '2024-02-15',
      likeHit: 2,
      content: '매우 추움'
    },
    {
      title: '부평 맛집',
      date: '2024-03-05',
      likeHit: 10,
      content: '프랭크버거'
    },
    {
      title: '봄 의상 추천',
      date: '2024-04-01',
      likeHit: 5,
      content: '긴 상하의에 코트'
    }
  ]);

  let [inputTitle2, setInputTitle2] = useState('');
  let [inputDate2, setInputDate2] = useState('');
  let [inputContent2, setInputContent2] = useState('');

  function LikeUp(i) {
    let copy = [...likeHit];
    copy[i]++;
    setLikeHit(copy);
  }

  // 항목별 데이터 배열에 새 데이터를 추가할 때
  function postAdd() {
    if(inputTitle != '' && inputDate != '' && inputContent != ''){
      let copyTitle = [...title];
      let copyLikeHit = [...likeHit];
      let copyDate = [...date];
      let copyContent = [...content];

      // 가장 앞에 추가하고 싶다면 unshift()
      copyTitle.push(inputTitle);
      copyLikeHit.push(0);
      copyDate.push(inputDate);
      copyContent.push(inputContent);

      setTitle(copyTitle);
      setLikeHit(copyLikeHit);
      setDate(copyDate);
      setContent(copyContent);

      setInputTitle('');
      setInputDate('');
      setInputContent('');
      document.getElementsByClassName("input-title")[0].value = '';
      document.getElementsByClassName("input-date")[0].value = '';
      document.getElementsByClassName("input-content")[0].value = '';
    }
  }

  // 객체 배열에 데이터를 새로 추가할 때
  // 위의 과정과 비교하면 훨씬 간단하다
  function postJsonAdd() {
    if(inputTitle2 != '' && inputDate2 != '' && inputContent2 != '') {
      let copy = [...post];

      let newPost = {
        title: inputTitle2,
        date: inputDate2,
        likeHit: 0,
        content: inputContent2
      }

      copy.push(newPost);
      setPost(copy);

      setInputTitle2('');
      setInputDate2('');
      setInputContent2('');
      document.getElementsByClassName("input-title2")[0].value = '';
      document.getElementsByClassName("input-date2")[0].value = '';
      document.getElementsByClassName("input-content2")[0].value = '';
    }
  }

  // 날짜 또는 추천수 기준 정렬
  function sortPost(num) {
    let copy = [...post];
    let date_select = document.getElementsByClassName("date-sort")[0].value;
    let likeHit_select = document.getElementsByClassName("likeHit-sort")[0].value;
    let date_default = document.getElementsByClassName("default-option")[0];
    let likeHit_default = document.getElementsByClassName("default-option")[1];

    if(num == 1) { // 날짜 기준 정렬 시
      if(date_select == '오름차순') { // 오름차순 선택
        copy.sort((a, b) => { // (a.date - b.date) 로 작성해도 무관
          if(a.date < b.date) return -1;
          if(a.date > b.date) return 1;
          return 0;
        });
      } else if (date_select == '내림차순') { // 내림차순 선택
        copy.sort((a, b) => { // (b.date - a.date) 로 작성해도 무관
          if(a.date < b.date) return 1;
          if(a.date > b.date) return -1;
          return 0;
        });
      }
    } else if(num == 2) { // 추천 기준 정렬 시
      if(likeHit_select == '오름차순') { // 오름차순 선택
      copy.sort((a, b) => { // (a.date - b.date) 로 작성해도 무관
        if(a.likeHit < b.likeHit) return -1;
        if(a.likeHit > b.likeHit) return 1;
        return 0;
      });
      } else if (likeHit_select == '내림차순') { // 내림차순 선택
        copy.sort((a, b) => { // (b.date - a.date) 로 작성해도 무관
          if(a.likeHit < b.likeHit) return 1;
          if(a.likeHit > b.likeHit) return -1;
          return 0;
        });
      }
    }
    setPost(copy); // 정렬한 배열을 setState 적용
  }

  return (
    <>
      <section className='section sec'>
        <div className='container-lg'>
          <h2 className='title'>My Post</h2>
          <div className='row row-cols-2 justify-content-center'>
            <div className='col new-post p-4 m-3'>
              <label>제목</label>
              <input type='text' className='form-control input-title' onChange={(e)=>{setInputTitle(e.target.value)}}></input>
              <label>날짜</label>
              <input type='date' className='form-control input-date' onChange={(e)=>{setInputDate(e.target.value)}}></input>
              <label>내용</label>
              <textarea className='form-control input-content' onChange={(e)=>{setInputContent(e.target.value)}}></textarea>
              <button className='btn btn-success' onClick={()=>{postAdd()}}>확인</button>
            </div>
          </div>
          <div className='row row-cols-2'>
            {
              title.map((el, i) => {
                return(
                  <Box key={i} title={title} date={date} 
                  likeHit={likeHit} LikeUp={LikeUp} i={i} 
                  pick={pick} setPick={setPick}
                  detail={detail} setDetail={setDetail}></Box>
                );
              })
            }
          </div>
          <div className='row'>
            {
              (detail) ? <Detail i={pick} title={title} date={date} content={content}></Detail> : null
            }
          </div>
        </div>
      </section>
      
      <hr></hr>
	  // 비교를 위해 객체 배열로 만든 블로그는 아래에 따로 section을 작성함
      <section className='sec'>
      <div className='container-lg'>
          <h2 className='title'>Memo Post</h2>
          <div className='row row-cols-2 justify-content-center'>
            <div className='col new-post p-4 m-3'>
            <label>제목</label>
              <input type='text' className='form-control input-title2' onChange={(e)=>{setInputTitle2(e.target.value)}}></input>
              <label>날짜</label>
              <input type='date' className='form-control input-date2' onChange={(e)=>{setInputDate2(e.target.value)}}></input>
              <label>내용</label>
              <textarea className='form-control input-content2' onChange={(e)=>{setInputContent2(e.target.value)}}></textarea>
              <button className='btn btn-success' onClick={()=>{postJsonAdd()}}>확인</button>
            </div>
          </div>
          <div className='btn-wrap'>
            <label>날짜</label>
            <select className='select date-sort' onChange={()=>{sortPost(1)}}>
              <option className='default-option'>----</option>
              <option>오름차순</option>
              <option>내림차순</option>
            </select>
            <label>추천수</label>
            <select className='select likeHit-sort' onChange={()=>{sortPost(2)}}>
              <option className='default-option'>----</option>
              <option>오름차순</option>
              <option>내림차순</option>
            </select>
          </div>
          <div className='row row-cols-2'>
            {
              post.map((el, i) => {
                return(
                  <Post key={i} post={post} i={i} setPost={setPost}></Post>
                );
              })
            }
          </div>
        </div>
      </section>
    </>
  );
}

// 항목별로 데이터 배열을 만들었을 때
function Box(props) {
  let {i, title, date, likeHit, LikeUp, pick, setPick, detail, setDetail} = props;

  function detailOpen(i) {
    if(pick == i) {
      setDetail(!detail);
    } else {
      detail = true;
      setDetail(detail);
    }
    setPick(i);
  }

  return(
    <div className='col box py-3'>
      <div className='gt'>
        <h3 className='post-title' onClick={()=>{detailOpen(i)}}>{title[i]}</h3>
        <h5 className='like-btn'><span className='emoji' onClick={()=>LikeUp(i)}>👍</span>{likeHit[i]}</h5>
        <p>{date[i]}</p>
      </div>
    </div>
  )
}

// Box에서 넘겨주는 데이터 수를 비교했을 때 훨씬 간단하다.
function Post(props) {
  let {i, post, setPost} = props;

  function likeUp(i) {
    let copy = [...post];
    copy[i].likeHit++;
    setPost(copy);
  }

  return(
    <div className='col box py-3'>
      <div className='gt'>
        <h3 className='post-title'>{post[i].title}</h3>
        <h5 className='like-btn'><span className='emoji' onClick={()=>{likeUp(i)}}>👍</span>{post[i].likeHit}</h5>
        <p>{post[i].date}</p>
        <p>{post[i].content}</p>
      </div>
    </div>
  )
}

function Detail(props) {
  let {i, title, date, content} = props;

  return(
    <div className='detail'>
      <div className='gt'>
        <h3>{title[i]}</h3>
        <p>{date[i]}</p>
        <p>{content[i]}</p>
      </div>
    </div>
  )
}

export default App;

react blog 6.png

react blog 7.png

react blog 8.png