☕️ [React] Webucks Project (1)

luneah·2021년 12월 4일
0

Webucks Project

목록 보기
6/10
post-thumbnail

[Mini Project] WeBucks-React

[Mission 1~5] 구현 (필수 구현 사항)
✨ 구현 기간 : 11/29~12/1

[Mission 0] 레이아웃 리액트로 옮기기

  • 기존에 구현한 레이아웃을 리액트로 옮겨주세요. sass 세션 이후에는 css 파일을 sass 파일로 변경해 주세요.

[Mission 1] 리스트 페이지 목데이터로 map 사용하여 구현하기

  • 리스트 페이지의 아래 부분을 컴포넌트 CoffeCard 로 분리해주세요.

  • 목데이터, map을 사용하여 CoffeeCard 컴포넌트를 활용해 리스트 페이지를 구현해주세요.

✔️ CoffeeCard 컴포넌트 만들기
[CoffeeCard.js]

function CoffeeCard(props) {

    return (
      <>
        <li>
          <div className="coffee-list__box">
            <img
              src={props.item.img} alt="커피"
              width="200px"
              height="200px"
             />
          </div>
          <p className="coffee-list__name">
             {props.item.name}
             <i className={heart === true
                ? 'fas fa-heart' : 'far fa-heart'}>
             </i>
           </p>
        </li>
     </>
   )
}

export default CoffeeCard;

✔️ 목데이터, map을 사용해 리스트 페이지 구현

import CoffeeCard from '../components/CoffeeCard';

function List() {
   const [mockdata, setMockData] = useState([]);

   useEffect(() => {
    fetch("http://localhost:3000/data/data.json")
      .then((res) => res.json())
      .then((res) => setMockData(res));
  }, []);

	<ul className="coffee-list">
          {mockdata.cold && mockdata.cold.map((e, i) => {
            return <CoffeeCard key={i} item={e} />;
          })}
        </ul>
}

[ Result ]

💡   map 함수 쓸 때 && 연산자 사용하는 이유 : 조건부 렌더링 시켜주기 위해서!
커피 리스트의 목 데이터가 두 개의 배열로 구성되어 있으므로 위의 cold 배열에서만 렌더링 해주기 위해서 && 연산자로 선행 조건이 참일 때 후행 조건을 실행하게 만들어준다!



[Mission 2] Login | 사용자 입력 데이터 저장

  • 다음의 순서에 맞게 코드를 작성하여 ID, PW <input>에 입력된 값을 state 에 저장해주세요.

    1. ID <input> 에서 onChange event 발생
    2. event 발생 시 handleIdInput 함수 실행
    3. handleIdInput 는 이벤트를 인자로 받음
    4. event가 일어난 요소에 담긴 value 값 (event.target.value)을 state에 저장
    5. 위의 과정을 PW <input> 에도 동일하게 적용
    6. 위 순서대로 완료 후 Add : Mission 1 - 사용자 입력 데이터 저장 기능 구현 commit message를 남긴 후 push 해주세요.

✔️ ID, PW 값 state에 저장

import { useState } from 'react';

function Login() {

  const [id, setid] = useState("");
  const [pw, setpw] = useState("");

  // id 값 변경
  const handleIdInput = e => {
    setid(e.target.value);
  }
  // pw 값 변경
  const handlePwInput = e => {
    setpw(e.target.value);
  }

  return (
    <div className="login-input">
        <input
            type="text"
            id="user-id"
            placeholder="전화번호, 사용자 이름 또는 이메일"
            onChange={handleIdInput} />
    </div>
    <div className="login-input">
        <input
          type="password"
          id="user-pw"
          placeholder="비밀번호"
          onChange={handlePwInput} />
    </div>
  )
}


[Mission 3] Login | 로그인 버튼 활성화 (validation)

  • 입력한 아이디와 비밀번호가 기준에 맞는 경우에만 로그인 버튼 색상이 활성화될 수 있도록 해주세요.
  • ex) ID - @ 포함 / PW - 8글자 이상
  • 삼항 연산자 적용해서 조건에 따라 버튼 색상에 변화를 주시기 바랍니다.
  • 위 순서대로 완료 후 Add : Mission 2 - 로그인 버튼 활성화 기능 구현 commit message를 남긴 후 push 해주세요.

✔️ 조건에 따른 로그인 버튼 활성화
- 버튼 활성화 조건 만족 시 input의 테두리색 변경해주는 기능 추가 구현
- 로그인 아이디 세션 스토리지에 저장

function Login() {
 
  // 비밀번호 정규식
  const num = pw.search(/[0-9]/g);
  const en = pw.search(/[a-z]/gi);
  const specialChr = pw.search(/[`~!@#$%^&*|₩₩₩'₩";:₩/?]/gi);

  // 버튼 활성화 id, pw 조건
  const condition = (id.includes('@'))
  && (pw.length >= 8 && num > -1 && en > -1 && specialChr > -1);
 
  return (
    <div className="login-input">
        // 아이디 input
        <input 
           onChange={handleIdInput}
           style={{ borderColor: id.includes("@") ? "green" : "#e3e3e3" }}/>
    </div>
    <div className="login-input">
        // 비밀번호 input
        <input
          onChange={handlePwInput}
          style={{ borderColor : (pw.length >= 8 && num > -1 && en > -1 && specialChr > -1)
          ? "green" : "#e3e3e3" }} />
          <button type="button" id="user-pw__btn">🙉</button>
     </div>
     <div>
        // 로그인 버튼
        <button
           style={{ backgroundColor: condition ? "#0096f6" : "#C0DFFD" }}>
           로그인
        </button>
     </div>
  )
}

[ Result ]



[Mission 4] 커피 상세 페이지 좋아요 기능 구현하기

  • 우측 상단의 하트 버튼을 눌렀을 때 하트의 색이 붉은색으로 변하게 구현하면 됩니다. 다시 누르면 하트가 원래대로 돌아옵니다.
  • 상세 페이지 화면 보기

✔️ 좋아요 기능 구현

function Deatil() {

  const [heartClick, setHeartClick] = useState(false);
 
  return (
    <div className="detail-page__content-title">
       <i
         className={heartClick ? 'fas fa-heart' : 'far fa-heart'}
         id="detail-page__content-heart"
         onClick={() => setHeartClick(!heartClick)}>
       </i>
    </div>
  )
}

[ Result ]



[Mission 5] 커피 상세 페이지 댓글 기능 구현하기

  • input창에 리뷰를 작성하고 엔터를 누르면 리뷰(댓글)이 추가되도록 구현해주세요.(엔터가 아니라 리뷰 추가 버튼을 구현하셔도 됩니다)
  • 상세 페이지 화면 보기

[ReviewComment.js]
- 댓글창 아이디는 로그인 창에서 입력했던 아이디로 불러오게 구현

function ReviewComment(props) {

   const [heart, setHeart] = useState(false);
   
   return (
       <div className="nutrition-info__review-comment__inner">
          <div className="commentText">
             <span>{props.user ? props.user : (sessionStorage.getItem("id"))}</span>: {props.text}
          </div>
          <i
             className={heart === true ? 'fas fa-heart fheart' : 'far fa-heart'}>
          </i>
          <i className="fas fa-trash"></i>
       </div>
    );
}

export default ReviewComment;

[CommentData.json]
- 기존에 있던 댓글을 목 데이터로 만들어줌

[
    {
        "id": 1,
        "user": "coffee_lover",
        "text": "너무 맛있어요!"
    },
    {
        "id": 2,
        "user": "CHOCO7",
        "text": "오늘도 화이트 초콜릿 모카를 마시러 갑니다."
    },
    {
        "id": 3,
        "user": "legend_dev",
        "text": "진짜 화이트 초콜릿 모카는 전설이다. 진짜 화이트 초콜릿 모카는 전설이다. 진짜 화이트 초콜릿 모카는 전설이다."
    }
]

✔️ 댓글 기능 구현
- 아무것도 입력하지 않았을 때는 엔터 눌러도 업로드되지 않는 조건 추가 구현

function Deatil() {

  const [commentsMock, setCommentsMock] = useState([]);

  // 기존 댓글 불러옴
  useEffect(() => {
    fetch('http://localhost:3000/data/commentData.json', {
      method: 'GET',
    })
      .then((res) => res.json())
      .then((res) => {
        setCommentsMock(res);
      });
  }, []);

  const [inputText, setinputText] = useState([]);

  // 엔터 누르면 리뷰 추가
  const pressEnterEvent = (e) => {
    if (e.keyCode === 13 && e.target.value.length !== 0) {
      let copy = [...inputText];
      copy.push(e.target.value);
      setinputText(copy);
      e.target.value = "";
    }
  };

  return (
    <div className="nutrition-info__review-comment">
       {commentsMock.map((commentMock, index) => {
       return <ReviewComment
                key={index}
                text={commentMock.text}
                user={commentMock.user} />;
        })}
                   
        {inputText.map((e, i) => {
        return (
          <ReviewComment
            key={i} text={e} index={i}
            inputText={inputText}
            setinputText={setinputText} />);
        })}
    </div>
    <input type="text" id="input-review"
      placeholder="리뷰를 입력해주세요."
      onKeyDown={(e) => { pressEnterEvent(e); }} />
  )
}

[ Result ]

💬   map 함수를 쓸 때는 요소 간의 중복을 피하고 만약 오류가 발생했을 때는 그 부분을 빠르게 확인하기 위해서 key값이 꼭 있어야 한다!

profile
하늘이의 개발 일기

0개의 댓글