자판기웹페이지를 React로 구현
- 기존에 제작했던 프로젝트를 리액트로 리팩토링 작업
- 자판기 시스템과 같이 음료를 선택해 음료의 금액만큼 지불
- 아래 이미지와 같은 피그마파일을 타겟으로 UI 제작
- 자체적으로 과제 미션을 만들어 제작
사용기술
React, styled-components
웹페이지 확인하러 가기
React_Vending-machine 코드 github 확인
피그마 파일
*Figma 저작권은 제주코딩베이스캠프에게 있습니다.
비록 한페이지로만 만든 작은 프로젝트지만 바닐라 자바스크립트로 하다보니 불편했던 점이있다.
- 코드의 복잡성
- 일일히 DOM에 직접 접근해야 하기 때문에 코드가 지저분해지는 것 같다.
- 모든 JS코드가 하나의 파일에서 관리 (해당 부분은 모듈로 관리가능하기 때문에 제외)
=> 컴포넌트로 분리하여 관리
- 중복되는 HTML코드
- 컴포넌를 사용함으로써 중복되는 코드들을 한 번에 해결이 된다.
- 상태관리
- 상태관리라는 개념을 몰랐는데, 리액트를 공부하면 알게되었다.
바닐라 자바스크립트론 그저 데이터를 변경한다면, 아무 제어 없이 DOM에 접근해 바꾸기만
하면 땡이였다.
상태관리라는 개념이 들어온다면, 조금 더 안전하게 관리할 수 있다.
어떤 코드로 인해 해당 값이 바뀌었는지 추적하기가 쉬워 관리가 좋다.
2023-01-08 노션페이지
1. 컴포넌트의 설계 시 어떤 기준으로 나누면 좋을 지
- 고려했던 부분
- 여러 상태들과 상태의 변화에 영향을 받는 컴포넌트들의 구분
- A라는 상태가 업데이트될 때 어떤 컴포넌트들이 영향을 받고 어떻게 연결이 되었는지
- 해당 컴포넌트끼리 묶어 놓는다면, 관리하기 쉬울 것이라 생각함
- UI를 중심으로 구분하기
- File Setting
.root
├── public
└── src
│ ├── img
│ ├── app.js
│ ├── components
│ │ ├── common (공통컴포넌트)
│ │ └── in (왼쪽 큰블럭 : 자판기)
│ │ └── out (오른쪽 큰블럭 : 나의 정보)
- styled-components로 이미지 url를 들고 올때 상단에 주소를 import해서 가져온다
import logoImg from './img/logo.png'; const Logo = styled.h1`background: no-repeat center / 100% 100% url(${logoImg});`
- 웹페이지의 전체를 백그라운드로 처리할려고
height: 100%
를 사용해도 먹지 않는다.해결방법으로
height: 100vh
를 사용했다
하지만 문제점으로 스크롤이 생기는 부분까지 되지 않는다
- 그 이유는 vh의 단위는 보이는 화면을 기준으로 했기 때문에 화면을 넘어서 스크롤이 생긴 부분은 적용되지 않는다
- ⇒ 해결방법으로
height: 130vh
을 사용
2023-01-09 노션페이지
1. 이슈: 상위 태그(빈 div태그)에 백그라운드 컬러를 지정
⇒ 하위 태그 아래 margin부분에 적용되지 않음
2. 이유: 상위태그의 height가 내부 태그에 맞춰서 크기를 조정
- margin끼리 맞닿을 때 상쇄
3. ⇒ 자식 요소의 마진이 더 크든 작든 상관없이 상쇄된 마진은 부모 박스의 바깥으로만 렌더링이 됩니다. (부모를 벗어남)
4. 해결방법: padding 또는 border 값을 주어 벽을 만들어준다.
관련 블로그
2023-01-10 노션페이지
2023-01-11 노션페이지Styled-components에서 Global CSS 적용하는 방법 알아보기
- 글로벌 CSS를 사용하는 이유
- 공통된 작업을 하나하나 하는 것이 비효율
- 만약 전체 수정이 필요할 때 컴포넌트 하나하나 찾아가며 수정해야 함
ThemeProvider 사용
- ThemeProvider 태그를 컴포넌트 최상단에 감싸준다
- 프롭스로 theme를 설정
- Context API를 이용해 해당 프롭스의 값들은 하위에 있는 styled컴포넌트들은 모두 사용가능
- 자판기에서 적용할 수 있는 부분: 버튼 디자인, 컬러
- createGlobalStyle() 사용
- createGlobalStyle() 함수로 생성한 전역 스타일 컴포넌트를 애플리케이션의 최상위 컴포넌트에 추가해주면 하위 모든 컴포넌트에 해당 스타일이 일괄 적용
Reset css
- 역할: HTML코드의 태그들의 CSS영역 기본 값들을 초기화
- 사용이유: 웹브라우저 마다 defalt 값이 달라 브라우저에 따라 보여지는 화면이 달라짐 (크로스 브라우징)
- 사용 방법 https://im-designloper.tistory.com/77
- [ 기존에 사용하던 방법 ( css 파일 추가 ) ]
- [ npm으로 설치하는 방법 ] (React 사용 시)
2023-01-12 노션페이지
1. 컴포넌트 export 주의
- export defalt App O (함수로만 보내기)
- export defalt X
- 글꼴 적용 방법
- 구글웹폰트에 사용할 글꼴 검색
- 찾아서 추가하고 복사
- index.html파일에 link태그 추가
- CSS 선택자 정리 (nth-:nth-child())
- 부분 클래스 적용
- &:nth-child(n + 4) { margin-top: 12px; } ⇒ 4번째부터 적용
- &은 자신을 가리킴
- styled component 확장
- const StyledButton2 = styled(StyledButton)
- 기존 styled로 만든 컴포넌트를 확장할 때 () 묶어서 사용
- 백틱 안에 추가할 코드 작성
- 주의할 점: export 할 때 일반 컴포넌트 처럼 함수에 넣어서 반환하면 안됨! (그냥 변수 그대로 )
- 추가 설명 코드 노션 작성
2023-01-15 노션페이지
1. styled component 확장이 아닌 조건에 따른 적용
- 조금의 코드때문에 확장하는 것 보다 조건에 따른 변경코드 적용 가능
- 기존의 컴포넌트를 사용하고 프로퍼티만 지정해주면 됨
- ⇒ 쓸 때 없는 컴포넌트 생성을 없앰
- 여러 줄의 CSS 코드를 조건부로 보여주고 싶다면 css 를 사용해야합니다.
//예시 import styled, { css } from 'styled-components'; const Circle = styled.div` width: 5rem; height: 5rem; border-radius: 50%; // 허그라는 프롭스가 있을 시 아래 적용 ${props => props.huge && css` width: 10rem; height: 10rem; `} `;
- JSX 주석 사용법
{/* 멀티라인으로 사용하고 표현식으로 묶기 */}
- 태그 안에 프롭의 주석을 남기고 싶을 때
<div id = 3 // 주석입니다 ></div>
- 드롭다운 구현
- styled-component로 hover 구현 ⇒ css기능만 조작이 가능하고 js의 기능은 조작하지 못함
해결방법:
-useState 를 이용한 hover 기능 구현
- onMouseOver (마우스가 컴포넌트에 있을 때
- onMouseOut ( 마우스가 컴포넌트에 없을 때
- Styled로 생성한 컴포넌트의 선언 위치
- styled-components 선언은 Component 함수 스코프 외부에 위치 해야하는 규칙을 갖고 있습니다.
- 관련 블로그
2023-01-17 노션페이지
input컴포넌트 상태 관리
- 기존에 사용했던 프롭스를 지정해 switch문을 이용함
- 2023-01-16 노션페이지 변경 전 코드 확인 가능
- 추가관리가 어려움 ⇒ 생성된 input 컴포넌트 하나하나 다 지정해주어야 함
- ⇒ 해결방법
하나의 스테이트로 관리하기const [inputForm, setInputForm] = useState({ deposit: '', changeCoin: '', }); function inputHandleChange(e) { setInputForm((prev) => ({ ...prev, // 스프레드 문법을 쓰는 이유 // 정적인 값을 할당 시 타겟이 아닌 값에 영향을 줌 [e.target.name]: e.target.value, // e.target.name과 같은 프로퍼티 키를 가진 벨류를 변경 })); }
이벤트에 따른 상태변화 필요 값 정리
- 어떤 이벤트에는 어떤 값들이 활용되는지 파악
- 정리한 이유: 이벤트 코드 작성 시 확인
ex) 입금 이벤트 =>{ 소지금, 잔액, input에 적힌 값 (입금하고자 하는 값)} 필
값 잔액반환 코인반환 획 득 코인변환 코인입금 금액입금 아이템 선택 소지금 O O O 소지코인 O O O 잔액 O O O O 잔여코인 O O O O 장바구니리스트 O O 장바구니 총금액 O O 할인된 금액 O O 내 리스트 O 구매한 총금액 O 입금 금액 O 입금 코인 O 장바구니 아이템 개수 O O
2023-01-18 노션페이지
1. 코인변환 이벤트 다시 작성
기존의 방법
- 자판기에 입금되어있는 코인과 현금,
- 입금되지않은 코인, 현금
의 각자 스테이트를 만들어 사용
- 추후 다른 많은 스테이트 사용으로 복잡 가능성이 있음- 변경 방법
- 관련 있는 데이터끼리 객체로 묶어 관리
- (소지한 코인, 자판기 잔여 코인)
const [coin, setCoin] = useState({outCoin: 0, inCoin: 0})
- (소지한 현금, 자판기 잔여 현금)
const [money, setMoney] = useState({outMoney: 0, inMoney: 0})
2022-02-08 노션페이지
2022-02-09 노션페이지
- 스타일이 하나만 사용되는 컴포넌트라도 인라인 작성보다 따로 styled컴포넌트를 생성하는 것이 좋다.
사이드 이펙트 분리
(목표 상태가 변하면 알아서 다른 상태도 렌더링)
- input상태 분리 ⇒ 자판기 안에 금액이 변경될 대, 소지금이 변경될 때 자동으로 리렌더링
- depositReducer안에서 사용되는 상태변환은 useEffect를 사용해서 해결하기
- 자판기에 금액(코인) 입금 시
- 자판기에 입금된 금액(inCash) 변경 시 자동으로 소지한 금액(outCash)이 변경
- 자판기에 입금된 금액(코인) 반환 시 ⇒ useEffect로 사용 X
- 자판기에 입금된 금액(inCash) 변경 시 자동으로 소지한 금액(outCash)이 변경
- 입금된 금액을 소지한 금액에 더 해야 하지만 입금된 금액이 0으로 바뀌고 Effect가 실행돼 전에 있던 값을 더할 수 가 없음
- 원래 동작 :
outcash = outcash + incash
⇒incash = 0
- Effect로 사용 시 :
incash = 0
⇒outcash = outcash + incash
incash
의 변경으로 Effect가 실행되기 때문
에러발생
- 내용
장바구니에 있는 아이템들을 구매(획득영역으로 이동) 시 장바구니에 총금액이 같이 업데이트되지 않음
=> 장바구니에 담긴 아이템이 없기 때문에 0원으로 업데이트 되야 함- 원인
총금액 표시 부분값(basketTotal
)을useRef
로 만들고
그리고 사이드이펙트로 분리함
=> useEffect에 의존성 배열에basketList
를 넣어basketList
변경 시basketTotal
도 업데이트 되는 사이드이펙트useEffect(() => { if (basketList.itemInList.length > 0) { basketTotal.curret = basketList.itemInList.reduce( (a, b) => a + b.price * basketList.count[b.name], 0 ); } else basketTotal.curret = 0; }, [basketList, basketTotal]);
해결방법
=> useEffect에 전달된 콜백 함수는 컴포넌트가 렌더링된 후에 실행된다.
=> 그러므로 값이 마지막에 변경이 되었고, 이미 basketList로 인한 화면 레던링이 끝났고, useRef의 특성으로 변경이 되어도 화면이 렌더링되지 않음
⇒ state로 변경
소감
- React로 처음 프로젝트를 진행하면서 상태관리 라이브러리를 쓰는 이유를 대충 알게된 것 같다
- (Prop Drilling)
하나의 상태를 여러군데 사용이될 때 부모컴포넌트에서 관리했는데 그런 부분에서 코드가 복잡해지는 것 같다- 조금 더 공부해서 깔끔한 코드를 작성하고 싶다