-
React001-3 리액트 기초 및 연습React/React 2023 2022. 12. 31. 09:09728x90
섹션3-리액트 기초 및 연습
컴포넌트
- 리액트의 A-Z
- 모든 사용자 인터페이스들은 컴포넌트로 구성된다.
- 웹사이트에서 몇가지 빌딜 블록을 확인할 수 있다. 그중에서도 같은 아이템이 반복되는 것들을 볼 수 있는데같은 아이템이지만 다른 데이터를 다루는 아이템들을 볼 수 있다. 이것들이 다 컴포넌트이고 사용자 인터페이스에서 재사용할 수 있는 빌딩 블럭이다.
- 재사용이 가능
- 컴포넌트 안에 다른 컴포넌트들이 들어갈 수 있다. 컨테이더, 버튼, 입력창, 출력값 등등
컴포넌트는 어떻게 만들어 지는가?
모든 컴포넌트는 html css js를 합쳐 만드는 것이다.
선언적 접근방식으로 만들어진다. - 항상 원하는 최종상태, 목표상태 또는 다양한 상황에 따라 다른 목표상태를 정의하는 것이 중요하다.
자바스크립트에서 하는 것처럼 직접 구체적인 DOM을 업데이트 하는 지침들을 작성할 필요가 없다.
리액트와 리액트 컴포넌트로 작업할 때는 최종 상태와 어떤 상황에서 어떤 상태가 사용되어야 하는지 정의하면 되고 리액트는 이 작업을 숨겨서 처리한다.
리액트 프로젝트 시작하기
create-react-app https://create-react-app.dev/
Create React App
Set up a modern web app by running one command.
create-react-app.dev
이 툴은 리액트 프로젝트를 생성하는데 사용할 수 있는데 일부 기본적인 리액트 코드 파일이 미리 설정된 폴더들과 상용화할 수 있는 리액트 앱을 구축할 수 있도록 돕는 환경설정들이 제공된다.
리액트앱을 개발하는것과 실행하는 것 사이에 몇가지 변환과 최적화 단계가 있기 때문
먼저 nodejs를 다운받아야 한다. https://nodejs.org/en/
Node.js
Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.
nodejs.org
일단은 LTS 버젼으로 다운을 받았다.
- 리액트 프로젝트 폴더를 정해주고 shift 우클릭을 해서 터미널을 열어준다.
- 아래를 입력해 리액트 애플리케이션을 만들어준다.
npx create-react-app react-complete-guide(프로젝트 이름)
- 기다리다가 이런 화면이 나오면 성공적으로 생성된 것이다.
5년차에 사고 말 것이다 해피해킹! - 만들어진 폴더로 이동해주고 npm start로 실행한다.
cd react-complete-guide(프로젝트 이름) npm start
- localholt:3000으로 접속이 되는데 아래와 같은 화면을 볼 수 있다.
- vscode를 열어주고 생성한 폴더를 열어준다.
- 터미널에서 ctrl+c를 눌러 서버를 종료해 주고 만들어져 있는 리액트 파일들을 정리해 준다.
- vscode에서 터미널을열서
npm install //node_modules 파일이 생긴다. npm start //서버 시작
이제 자동으로코드를 감지해서 보여줄 것이다.
index.js
이제 리액트 프로젝트를 시작하는데 전부 JS코드일 것이다.
리액트 프로젝트는 index.js파일이 가장 먼저 시작되고 이것을 거쳐서 실행된다. 이건 특별한 리액트의 기능이기도 한데 바닐라 자바스크립트에서는 css를 임포트해서 사용하지 않는데 여기서는 가능하다.
- react-dom라이브러리로부터 ReactDom을 가져온다. package.json에 보면 react와 react-dom을 찾을 수 있다.
- index.js에 가져온 ReactDom에서 createRoot라는 메소드를 호출하고 있다. 이 메소드는 리액트를 사용하여 구축할 전체 사용자 인터페이스의 메인엔트리 포인트 혹은 메인 훅을 생성한다.
public/index.html
이곳에서는 거의 작업을 하지 않지만 index.html이라는 중요한 파일이 자리하고 있다.
오직 이 html 파일만이 리액트 애플리케이션에서 사용될 것이다. 이런spa에서는 한개의 html파일만 사용하고 그 외 웹페이지상의 모든 사용자 인터페이스 관련 변경사항은 리액트가 처리한다.일반html 구조와 같이 body와 div가 보인다. id로 root이 있지만 아무런 컨텐츠가 없다. 구축할 ui가 들어가고 렌더링될것이다.
index.js에서 root.render를 통해 렌더 시킬 것. 그러니까 아까 div 내부가 root.render(???) ???가 들어갈 될 경로가 될 것이라는 것
App.js
function App() { return ( <div> <h2>Let's get started!</h2> </div> ); } export default App;
"컴포넌트" root라는 id를 갖는 요소가 있는 곳에 렌더링 되는 컴포넌트
App이라는 함수가 담겨있고 이 App을 내보낸다. 이렇게 내보낸 App.js를 index.js에서 불러오는 것.
return() 안에 작업을 하게 될 공간 JSX를 사용한다.
JSX
JavaScript Xml을의미한다.
브라우져에서 실행되는 변환된 코드가 복잡하게 생기는데 JSX를 통해 그런 코드를 작성하지 않고 간단하게 마크업처럼 해서 작성할 수 있게 만들어진 공간이다.
리액트 작동방식
function App() { return ( <div> <h2>Let's get started!</h2> <p>This is also visible!</p> </div> ); } export default App;
return() 안에 html 마크업을 넣어주면 그것을 리액트가 자바스크립트로 변환해서 클라이언트에 쏴주는 것
컴포넌트 만들기
가계부 웹앱을 만들어 볼 것이다.
맨 위에 가장 중요한 App컴포넌트가 있고 아래에는 어떤 종류의 ui인터페이스를 갖는 컴포넌트를 가질 수 있다.
- src폴더 안에 컴포넌트 들을 정리할 components 폴더를 만들어준다.
- components안에ExpenseItem.js 파일을 만들어준다.
- h2태그를 호출하고 밖으로 보내줄 수 있는 컴포넌트를 만든다
function ExpenseItem(){ return <h2>Expense item!</h2> } export default ExpenseItem;
- App.js에 임포트해주고 마크업까지 해주면 웹에서 볼 수 있다.
import ExpenseItem from "./components/ExpenseItem"; function App() { return ( <div> <h2>Let's get started!</h2> <p>This is also visible!</p> <ExpenseItem /> </div> ); } export default App;
항상 이 순서로 하면된다.
- 컴포넌트를 생성하고
- 익스포트하고
- 임포트하고
- 마크업!
더 복잡한 JSX 작성
ExpenseItem컴포넌트가 그냥 title 뿐이 아니라 더 복잡한 컴포넌트가 되게 만들어 주고 싶다.
function ExpenseItem(){ return <div>Date</div> <div><h2>Title</h2></div> <div>Amount</div> } export default ExpenseItem;
이런식으로 작성하면 에러가 나온다.
리액트 컴포넌트의 JSX에는 중요한 규칙이 있는데 반환하는 문장마다 또는 JSX코드 조각마다 반드시 한 개의 루트 요소를 갖게 하는 것 위 코드에는 여러개의 div태그가 있어서 여러개의 루트요소를 갖게 되어 에러가 뜨는 것이다.
- 가장 간단한 해결 방안으로는 div태그로 전체를 감싸주고 그 밖을 소괄호로 감싸주어 한 문장이라고 선언해 주는 것이다.
function ExpenseItem(){ return ( <div> <div>Date</div> <div><h2>Title</h2></div> <div>Amount</div> </div> ); } export default ExpenseItem;
기본CSS추가
특정한 컴포넌트의 css코드를 위해서 css파일을 추가할 수 있다.
css코드를 읽어와야 한다. 간단하게 아래와 같이 할 수 있다
그리고 그동안 디자인을 입히는 건 하드코딩으로 전체 페이지를 할 정도 많이 연습하고 했기 때문에 그냥 css 파일을 복사해서 사용하려고 한다.
import './ExpenseItem.css';
JSX에서는 class를 사용하는 것이 아니라 className을 사용해 클래스를 준다 class는 자바스크립트에서 이미 예약된 단어이기 때문이다.
import './ExpenseItem.css'; function ExpenseItem() { return ( <div className="expense-item"> <div>December 30th 2022</div> <div className="expense-item__description"> <h2>Car Insurance</h2> <div className="expense-item__price">$300</div> </div> </div> ); } export default ExpenseItem;
동적 데이터 출력 및 표현식 작업
가계부에 하나의 데이터만 있지 않을거고 그 데이터는 가변적이길 원할 것이다.
하드코드한걸 재사용 할 수 있게 바꿔주고 넘어가도록 하자
일반 자바스크립트로 추가해주기 JSX안에 중괄호를 이용해 적용해준다.
이제 하드코딩한걸 전부 바꿔준다.
import "./ExpenseItem.css"; function ExpenseItem() { const expenseDate = new Date(2022,2,30); const expenseTitle = 'Car Insurance' const expenseAmount = 300; return ( <div className="expense-item"> {/* 아래는 Date객체이기 때문에 텍스트로 출력하면 안된다. */} <div>{expenseDate.toISOString()}</div> <div className="expense-item__description"> <h2>{expenseTitle}</h2> <div className="expense-item__price">${expenseAmount}</div> </div> </div> ); } export default ExpenseItem;
props를 통해 데이터 사용하기
컴포넌트를 어떻게 재 사용 할 수 있을까?
그냥 복사해서 사용할 수 도 있겠지만 다른 값을 가질 수 없다. 컴포넌트가 고정되어 똑같은 데이터가 보여질 것이다.
App.js에 오브젝트들을 추가해줬다.
데이터는 외부에서 받아서 설정할 수 있도록 하고 싶다. props를 사용한다.
props를 지정해 주고 props.title 로 title을 불러와 준다 import "./ExpenseItem.css"; function ExpenseItem(props) { return ( <div className="expense-item"> {/* Date객체이기 때문에 텍스트로 출력하면 안된다. */} <div>{props.date.toISOString()}</div> <div className="expense-item__description"> <h2>{props.title}</h2> <div className="expense-item__price">${props.amount}</div> </div> </div> ); } export default ExpenseItem;
아래 데이터는 이제 밖에서 받아오기 때문에 필요가 없다!
const expenseDate = new Date(2022,2,30); const expenseTitle = 'Car Insurance' const expenseAmount = 300;
컴포넌트에 일반 자바스크립트 추가하기
현재는 date가 너무 안예쁘게 나와서 좀 예쁘게 달력모양으로 바꿔보려고 한다.
밖에서 받아오는 데이터를 형식에 맞게 출력하기 위한 노력 보기가 한결 낫다. 컴포넌트 나누기
컴포넌트가 점점 커지고 있는걸 볼 수가 있다.(작동은 잘 하고 있지만) 한번 나눠주면 가독성이 더 좋아지지 않을까?
- ExpenseDate.js라는 컴포넌트를 추가해주고 필요한 코드를 잘라와 붙여넣어 주었다.
function ExpenseDate(props) { const month = props.date.toLocaleString("en-US", { month: "long" }); const year = props.date.getFullYear(); const day = props.date.toLocaleString("en-US", { day: "2-digit" }); return ( <div> <div>{month}</div> <div>{year}</div> <div>{day}</div> </div> ); } export default ExpenseDate;
- ExpenseItem.js에는 ExpenseDate를 import 해주고 컴포넌트를 추가해 줬다.
import ExpenseDate from "./ExpenseDate"; ... return ( <div className="expense-item"> <ExpenseDate date={props.date} /> //props를 다시 전달해 줘야 한다. ...
- 그리고 css파일을 적용시켜주고 className까지 지정해준다.
import './ExpenseDate.css'; function ExpenseDate(props) { const month = props.date.toLocaleString("en-US", { month: "long" }); const year = props.date.getFullYear(); const day = props.date.toLocaleString("en-US", { day: "2-digit" }); return ( <div className="expense-date"> <div className="expense-date__month">{month}</div> <div className="expense-date__year">{year}</div> <div className="expense-date__day">{day}</div> </div> ); } export default ExpenseDate;
숙제
- 지출 표시를 담당하는 새 컴포넌트를 생성하세요.
- 해당 컴포넌트에 여러 개의 ExpenseItem 컴포넌트를 추가하세요.
- 앱 컴포넌트에 지출 데이터를 그대로 유지하면서 해당 데이터를 새로 생성한 컴포넌트로 전달하세요.
정답
App.js
import Expenses from "./components/Expenses"; function App() { const expenses = [ { id: "e1", title: "Toilet Paper", amount: 94.12, date: new Date(2020, 7, 14), }, { id: "e2", title: "New TV", amount: 799.49, date: new Date(2021, 2, 12) }, { id: "e3", title: "Car Insurance", amount: 294.67, date: new Date(2021, 2, 28), }, { id: "e4", title: "New Desk (Wooden)", amount: 450, date: new Date(2021, 5, 12), }, ]; return ( <div> <h2>Let's get started!</h2> <Expenses item={expenses} /> </div> ); } export default App;
Expenses.js
import ExpenseItem from "./ExpenseItem"; function Expenses(props) { return ( <div className="expenses"> <ExpenseItem title={props.item[0].title} amount={props.item[0].amount} date={props.item[0].date} /> <ExpenseItem title={props.item[1].title} amount={props.item[1].amount} date={props.item[1].date} /> <ExpenseItem title={props.item[2].title} amount={props.item[2].amount} date={props.item[2].date} /> <ExpenseItem title={props.item[3].title} amount={props.item[3].amount} date={props.item[3].date} /> </div> ); } export default Expenses;
숙제 후기
다 해놓고 안돼서 엄청 헷갈린다고 생각했는데 오타였다. 나 리액트 꽤나 잘 하는 걸지도?
컴포지션의 개념(children prop)
현재는 아주 구체적인 컴포넌트들을 가지고 있다.
props를 통해 모든 것을 설정하는 컴포넌트가 아니라 컴포넌트의 열고 닫는 태그 사이에 있는 컨텐츠를 전달하고 싶을 때는 어떻게 할까?
- Card.js 컴포넌트를 만들어준다 props를 정해주고 props.childres을 꼭 정해줘야 한다. 자식 컨텐츠를 보여주는것이다.
import './Card.css' function Card(props){ //card 가 어디가서든 적용되게 하는 것 const classes='card ' +props.className; return( <div className={classes}> {props.children} </div> ) } export default Card;
Card.css에 카드와 관련있는 css들을 복사해 넣어주고 다른 곳에 겹치는 걸 삭제해준다.
.card{ border-radius: 12px; box-shadow: 0 1px 8px rgba(0, 0, 0, 0.25); }
ExpensesItem.js 에 div를 Card로 바꿔주면 Card.js에 적용된 div와 css가 적용이 된다.!!!!! 이건 좀 신기했다.
import Card from "./Card"; import ExpenseDate from "./ExpenseDate"; import "./ExpenseItem.css"; function ExpenseItem(props) { return ( <Card className="expense-item"> <ExpenseDate date={props.date} /> <div className="expense-item__description"> <h2>{props.title}</h2> <div className="expense-item__price">${props.amount}</div> </div> </Card> ); } export default ExpenseItem;
추가
- 과거에는 React를 항상 import 해야 했었다고 한다(JSX에서 필요해서)
react.createElement() 가 자동으로 생성되게 해야 했기 때문 - return시 wrapper로 감싸주는 이유도 비슷한데 렌더링할 때 wrapper가 없으면 불가능 하기 때문이다.
- 더 많은 컴포넌트를 가지게 되면 더 많은 폴더로 나눠서 저장할 수 있다.
- 몇가지 자바스크립트 구문
- 화살표 함수
728x90'React > React 2023' 카테고리의 다른 글
Vue 3-1 웹팩 사용하기 (1) 2023.02.06 React 002-1 State,Event (0) 2023.01.02 React 001-2 리액트에 많이 사용되는 자바스크립트 (0) 2022.12.31 React 001-1 리액트란? (1) 2022.12.31 React 000 (0) 2022.12.30