Webucks-React 2

Webucks-React 2

Webucks React 프로젝트는 React로 코드를 다시 작성한 것이 아니라 HTML, CSS, JavaScript로 만든 Webucks 프로젝트의 코드를 떼와 붙인 것입니다.

컴포넌트, state, props 등의 개념을 더 잘 이해하기 위해 Webucks 프로젝트를 처음부터 React로 만들어보았습니다.

프로젝트 세팅

npx create-react-app webucks

npm install react-router-dom --save

npm install sass --save

먼저 CRA를 이용해 리액트 프로젝트를 만들고 react-router-dom 과 sass 를 설치합니다. 그리고 필요없는 파일과 폴더를 지우고 필요한 파일과 폴더를 모두 만들었습니다.

라우터로 페이지들을 연결하는 작업도 미리 해주었습니다.

[Mission 1] 로그인 페이지 레이아웃 구현

// Login.js import React from 'react'; import './Login.scss'; function LoginInput(props) { return ( ); } function LoginButton() { return ( 로그인 ); } function LoginForm() { return ( ); } function ForgetPw() { return ( 비밀번호를 잊으셨나요? ); } function Login() { return ( WeBucks ); } export default Login;

기능별로 최대한 컴포넌트를 나누어 구성했습니다.

그다음 sass 를 이용해 스타일을 입혀주었습니다. (sass 신세계다...)

// Login.scss .login { * { box-sizing: border-box; } background-color: #eeefef; height: 100vh; padding-top: 100px; .login__inner { display: flex; flex-direction: column; align-items: center; width: 350px; height: 450px; margin: 0 auto; padding-top: 20px; background-color: #fff; border: 2px solid #dddddd; border-radius: 5px; } h1 { font-family: 'Dancing Script', cursive; font-size: 50px; } // background-color: #eeefef; .login-form { .login-input { input { width: 280px; height: 35px; margin-bottom: 15px; padding-left: 10px; background-color: #f7f7f7; border: 2px solid #e0e0e0; border-radius: 5px; } input::placeholder { color: #a0a0a0; font-weight: bold; font-size: 12px; } } .login-button { button { width: 280px; height: 35px; margin-bottom: 90px; border: none; border-radius: 5px; background-color: #add3ea; color: #fff; } } } .forget-pw { font-size: 13px; color: #99b4c7; } }

구현 결과

[Mission 2] 로그인 페이지 기능 구현

Mission 1에서는 로그인 페이지의 정적인 레이아웃만 구현했습니다. 이제 기능을 추가해보겠습니다.

id에 @가 포함되고, password가 8자 이상이 되면 버튼이 활성화 되도록 해주세요.

이 기능을 구현하기 위해 먼저 id와 password의 적합성 상태를 나타낼 변수를 만들어줍니다.

// LoginForm() const [isIdValidate, setIsIdValidate] = useState(false); const [isPwValidate, setIsPwValidate] = useState(false);

그리고 컴포넌트로 보낼 props를 만들어주었습니다. name 어트리뷰트를 이용해 input 태그의 type 을 지정합니다. validate 어트리뷰트에는 적합성 상태 변경 함수를 넣어주었습니다.

// LoginForm()

다음은 LoginInput 컴포넌트입니다.

// LoginInput() function LoginInput(props) { const loginInputChange = (e) => { if (props.name === 'text') { if (e.target.value.includes('@')) { props.validate(true); } else { props.validate(false); } } else { if (e.target.value.length >= 8) { props.validate(true); } else { props.validate(false); } } }; return ( ); }

input 태그에 입력 이벤트가 발생하면 loginInputChange 함수가 실행됩니다.

loginInputChange 함수는 props.name 이 text 일 때와 password 일 때를 나누어 e.target.value 값의 적합성을 검사하고 적합하다면 setter 함수를 이용해 적합성의 상태를 true 로 바꿔줍니다.

LoginForm 컴포넌트에서 LoginButton 컴포넌트에 isValidate 라는 어트리뷰트를 넣어주었습니다.

// LoginForm()

안의 값이 둘 다 true 일 경우 즉, 아이디와 비밀번호가 모두 적합하다면 true 값이 들어가게 됩니다.

버튼의 disabled 속성은 true 일 경우 버튼이 비활성화 되고 false 일 경우 버튼이 활성화되기 때문에 속성값으로 !props.isValidate 을 적어주었습니다.

로그인 버튼이 활성화 된 상태에서 버튼을 누르면 list 페이지로 이동해야하기 때문에 react-router-dom 에서 { Link } 를 import 해 오고 버튼을 로 감싸줍니다.

// LoginButton() import { Link } from 'react-router-dom'; function LoginButton(props) { return ( 로그인 ); }

[Mission 3] 커피 리스트 페이지 레이아웃 구현

먼저 커피 리스트 페이지와 커피 디테일 페이지에서 사용될 Nav 컴포넌트를 만들어 주었습니다.

// Nav.js import React from 'react'; import './Nav.scss'; function Logo() { return ( WeBucks ); } function NavMenu() { return ( COFFEE MENU STORE WHAT'S NEW ); } function Nav() { return ( ); } export default Nav;

커피 리스트 페이지의 레이아웃을 구현하기 위해 컴포넌트를 어떻게 나눌지 생각해 보았습니다.

검은색 선: List 컴포넌트

컴포넌트 주황색 선: Nav 컴포넌트

컴포넌트 빨간색 선: CoffeeCategoryTitle 컴포넌트

컴포넌트 파란색 선: CoffeeCard 컴포넌트

// List.js import React from 'react'; import './List.scss'; import Nav from '../../components/Nav/Nav'; function CoffeeCard(props) { return ( {props.title} ); } function CoffeeCategoryTitle(props) { return ( {props.title} {props.content} ); } function List() { return ( {coffeeCategoryData.map((data, categoryIndex) => { return ( {coffeeCardData[categoryIndex].map((data, cardIndex) => { return ( ); })} ); })} ); } export default List;

그리고 상수데이터 형태로 필요한 데이터를 추가해 주었습니다.

const coffeeCardData = [ [ { id: 1, title: '토피 넛 콜드 브루', img: '/images/coffee1.jpg', }, { id: 2, title: '나이트로 바닐라 크림', img: '/images/coffee2.jpg', }, { id: 33, title: '나이트로 콜드 브루', img: '/images/coffee3.jpg', }, { id: 4, title: '돌체 콜드 브루', img: '/images/coffee4.jpg', }, { id: 5, title: '바닐라 크림 콜드 브루', img: '/images/coffee5.jpg', }, { id: 6, title: '벨벳 다크 모카 나이트로', img: '/images/coffee1.jpg', }, { id: 7, title: '시그니처 더 블랙 콜드 브루', img: '/images/coffee2.jpg', }, { id: 8, title: '제주 비자림 콜드 브루', img: '/images/coffee3.jpg', }, { id: 9, title: '마라탕', img: '/images/마라탕.jpg', }, { id: 10, title: '토피 넛 콜드 브루', img: '/images/coffee4.jpg', }, { id: 11, title: '콜드 브루 몰트', img: '/images/coffee5.jpg', }, { id: 12, title: '콜드 브루 오트 라떼', img: '/images/coffee1.jpg', }, { id: 13, title: '콜드 브루 플로트', img: '/images/coffee2.jpg', }, { id: 14, title: '프렌치 애플 타르트 나이트로', img: '/images/coffee3.jpg', }, ], [ { id: 15, title: '아이스 커피', img: '/images/coffee4.jpg', }, { id: 16, title: '오늘의 커피', img: '/images/coffee5.jpg', }, ], ]; const coffeeCategoryData = [ { id: 1, title: '콜드 브루 커피', content: '디카페인 에스프레소 샷 추가 가능 (일부 음료 제외)', }, { id: 2, title: '브루드 커피', content: '디카페인 에스프레소 샷 추가 가능 (일부 음료 제외)', }, ];

구현 결과

[Mission 4] 커피 상세 페이지 레이아웃 구현

상세 페이지도 컴포넌트를 나눠 봤는데요, 기능별로 컴포넌트를 나누는 것이 좋다고 하는데 어떻게 나눠야 할 지 아직 잘 모르겠습니다. 상세 페이지에서 '기능'은 댓글 밖에 없는 것이 아닌가? 하는 생각이 들어서 아래 그림처럼 나눠봤습니다.

이 상태로 만들면 저번 Webucks React 프로젝트의 상세 페이지와 유사한 형태의 코드를 짜게 될 것 같습니다. 물론 사람마다 컴포넌트를 나누는게 다르겠지만, '기능별로 컴포넌트를 나눈다' 라는 것을 확실히 이해하지 못한 상태에서 만들기보다, 예시를 좀 더 찾아보고 나중에 만드려고 합니다.

from http://dev-taeyeong.tistory.com/30 by ccl(A) rewrite - 2021-12-26 16:26:57