[Mission 1~5] 구현 (필수 구현 사항)
✨ 구현 기간 : 11/29~12/1
CoffeCard
로 분리해주세요.✔️ 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 배열에서만 렌더링 해주기 위해서 && 연산자로 선행 조건이 참일 때 후행 조건을 실행하게 만들어준다!
다음의 순서에 맞게 코드를 작성하여 ID, PW <input>
에 입력된 값을 state 에 저장해주세요.
<input>
에서 onChange
event 발생handleIdInput
함수 실행handleIdInput
는 이벤트를 인자로 받음event.target.value
)을 state에 저장<input>
에도 동일하게 적용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> ) }
@
포함 / 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 ]
✔️ 좋아요 기능 구현
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 ]
[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값이 꼭 있어야 한다!