Notice
Recent Posts
Recent Comments
Link
JiSoo's Devlog
[Udemy React 완벽 가이드] Section 2 본문
컴포넌트
- 모든 사용자 인터페이스들은 결국 컴포넌트로 구성된다
- 각각의 컴포넌트를 구축하고 리액트에게 최종 사용자 인터페이스에서 어떻게 구성할 것인지 명령할 수 있다
- 리액트가 컴포넌트의 개념을 도입한 것은 재사용 가능하다는 점뿐만 아니라 우려사항들을 분리할 수 있기 때문이다
- 대부분의 리액트 컴포넌트들은 HTML과 자바스크립트를 결합하는 것에 관한 것으로 물론 CSS도 추가하지만 그게 초점은 아님
- 리액트는 HTML과 자바스크립트, CSS로 구성된 재사용 가능하고 반응하는 컴포넌트를 만들 수 있게 해준다
- 리액트는 선언적 접근 방식을 사용한다
- 최종 상태만 정의하고 어떤 상태를 사용해야 하는지만 정의한다
- 모든 UI는 결국 여러 빌딩 블록(=컴포넌트)으로 구성되며, 따라서 사용자 인터페이스를 '컴포넌트의 조합'이라고 생각할 수 있다
- 대상의 상태(UI)를 정의하면 해당 결과를 얻기 위해 어떤 JS 명령어를 실행해야 하는지를 React가 특정한다
- 일반적으로 해당 컴포넌트가 사용될 때 화면에 표시되는 HTML(정확히는 JSX) 코드를 반환하는 JS 함수를 말한다
리액트 프로젝트 생성
- npx create-react-app 새 프로젝트 생성
- npm start 개발 서버 구동
표준 리액트 프로젝트 분석하기
- index.js 파일에 있는 코드의 변형된 버전이 브라우저 실행 시 가장 먼저 실행된다
- ReactDOM이라는 객체를 react-dom이라는 서드 파티 라이브러리에서 가져온다는 뜻으로 이렇게 가져와야 해당 라이브러리 기능을 index.js 파일에서 활용할 수 있다
- './~~' 는 같은 폴더에 있는 파일
JSX
- 자바스크립트 안에 있는 Html 코드로 자바스크립트 XML을 의미한다
- 화면 뒷단에서 변환한다
- React 프로젝트에서만 활성화되는 특수한 비표준 구문이다
리액트 작동 방식
- 일반적인 자바스크립트에서 페이지에서 어떤 요소를 선택하려면 document.getElementById('root')를 입력해도 끝낸 것이 아니라 innerHTML=''를 설정해야 한다
- 리액트에서는 최종 상태를 정의하기만 하면 화면에 불러오기 위한 지시사항들을 뒷담에 생성할 것이다
사용자 지정 컴포넌트 만들기
- 리액트로 작업할 때 컴포넌트 트리를 만든다
- App.js는 루트 컴포넌트로 index.js라는 시작 파일에서 렌더링되는 기본적인 컴포넌트
- 리액트에 있는 컴포넌트는 단지 자바스크립트 함수일 뿐
- 컴포넌트는 html 코드를 반환하는 함수
- Html 코드를 러턴하는 함수인 컴포넌트를 생성해서 내보내고 사용하고 싶은 파일에서 임포트한다
JSX 코드 작성
- JSX 코드에서 반환하는 문장마다 또는 JSX 코드 조각마다 반드시 한 개의 루트 요소를 갖는다
기본 CSS 스타일 추가
- JSX 코드에서 클래스 이름을 추가하고 싶다면 자바스크립트에서처럼 class=""가 아니라 className="" 사용
JSX에서 동적 데이터 출력 및 표현식 작업
- 컴포넌트를 쓰는 게 코드를 분산시킨다는 목적도 있지만 재사용 가능성도 중요한 목적
- html 코드에서 고정된 값이 아니라 동적 위치 표시자를 이용한 값을 사용한다
- 날짜는 날짜 객체이기 때문제 텍스트 형태로 출력 불가 -> toISOString() 호출해 날짜를 문자열로 출
"props"를 통해 데이터 전달하기
- 컴포넌트 재사용
- 속성을 추가해 데이터를 사용자 지정 컴포넌트에 전달 가능
- 리액트는 우리가 컴포넌트로 사용하는 모든 컴포넌트에서 하나의 파라미터를 받도록 해준다
- 리액트가 자동으로 전달한 키값 쌍을 props 객체에 받는데 키는 App에서 정의된 속성 이름(title, amount, date)
- props 객체에서 접근하는 키는 우리가 선택한 속성 이름이어야 한다
- 동적인 props를 위해 값을 설정할 때 중괄호 구문을 사용한다
- props는 동적으로 설정된 값에만 국한되지 않는다
import ExpenseItem from "./components/ExpenseItem";
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>
<ExpenseItem
title={expenses[0].title}
amount={expenses[0].amount}
date={expenses[0].date}
></ExpenseItem>
<ExpenseItem
title={expenses[1].title}
amount={expenses[1].amount}
date={expenses[1].date}
></ExpenseItem>
<ExpenseItem
title={expenses[2].title}
amount={expenses[2].amount}
date={expenses[2].date}
></ExpenseItem>
<ExpenseItem
title={expenses[3].title}
amount={expenses[3].amount}
date={expenses[3].date}
></ExpenseItem>
</div>
);
}
export default App;
import "./ExpenseItem.css";
function ExpenseItem(props) {
return (
<div className="expense-item">
<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;
컴포넌트에 일반 JavaScript 논리 추가하기
- props를 사용하는 목적은 컴포넌트를 자유롭게 설정하고 재사용할 수 있게 만들기 위해서 데이터를 컴포넌트에 전달하기 위해 사용
import "./ExpenseItem.css";
function ExpenseItem(props) {
const month = props.date.toLocaleString("en-US", { month: "long" });
const day = props.date.toLocaleString("en-US", { day: "2-digit" });
const year = props.date.getFullYear();
return (
<div className="expense-item">
<div>
<div>{month}</div>
<div>{year}</div>
<div>{day}</div>
</div>
<div className="expense-item__description">
<h2>{props.title}</h2>
<div className="expense-item__price">${props.amount}</div>
</div>
</div>
);
}
export default ExpenseItem;
컴포넌트를 여러 컴포넌트로 분할
- 컴포넌트를 작고 집중적인 것으로 유지하는 것이 좋다
import "./ExpenseDate.css";
function ExpenseDate(props) {
const month = props.date.toLocaleString("en-US", { month: "long" }); //도우미 상수
const day = props.date.toLocaleString("en-US", { day: "2-digit" });
const year = props.date.getFullYear();
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;
- date 부분을 별도의 컴포넌트로 분리
- 인자로 props 꼭 써줘야 함
- App.js에서 date를 받을 때는 function의 인자로 props를 받아 전달받고 하위 컴포넌트인 ExpenseDate에 date를 넘겨줄 때는 저런 식으로 써줘야 함
컴포지션
- 일반적으로 작은 구성요소부터 시작해 사용자 인터페이스를 구축하는 방식
- 사용자 지정 컴포넌트를 일종의 컨텐츠를 감싸는 래퍼로 사용할 수 없다
- css 파일에서 별도의 래퍼 컴포넌트로 일부 중복되는 코드를 추출할 수 있다
- html 코드와 JSX 코드 <div>도 추출할 수 있다
- 더 복잡한 JSX 구조를 가진 모달창이나 경고창같은 래퍼 컴포넌트를 보게 된다면 그런 경우에, 추출이 가능하다는 것은 수많은 코드의 중복을 피할 수 있게 해주고 다른 컴포넌트를 깔끔하게 유지할 수 있게 해준다
- 컴포넌트를 결합할 때마다 우리는 합성을 이용하는 것이다
- props.children은 래퍼 컴포넌트를 생성하게 하며 특별한 컴포넌트라고 할 수 있고 종종 필요하다
- 이 특수한 children prop의 값은 항상 우리의 사용자 지정 컴포넌트의 시작 태그와 종료 태그 사이에 있는 콘텐츠가 된다
- 만약 Card 컴포넌트에서 className을 설정하고 효과를 내도록 하기 위해서는 Card 컴포넌트에 const classes = ~~ 이렇게 넣어줘야 한다
** Card는 보통 둥근 모서리에 옅은 그림자와 같은 요소들이 있는 컨테이너 모양을 의미한다
Card.js
import "./Card.css";
function Card(props) {
const classes = "card " + props.className; //card는 디폴트 클래스로 항상 적용된다, 밖에서 className으로 받는 것들은 문자열로 추가된다
return <div className={classes}>{props.children}</div>; //children은 예약어
}
export default Card;
//재사용할 수 있는 래퍼 컴포넌트
JSX
- 과거에는 항상 react를 임포트 했어야 했다
return React.createElement(
'div',
{},
React.createElement('h2', {}, "Let's get started!"),
React.createElement(Expenses, { items: expenses })
);
// JSX 코드의 대안
- 복잡하고 번거로움
- JSX 코드를 사용할 때 리액트가 사실은 내부에서 사용되고 있다는 것을 강조하기 위해 import React from "react"; 작성해 줌
대체 함수 문법
const App = () => {
const Expense = (props) => {
- 화살표 함수 사용 (짧아서 추천)
728x90
'Frontend > React' 카테고리의 다른 글
[Udemy React 완벽 가이드] Section 6 (2) | 2024.01.04 |
---|---|
[Udemy React 완벽 가이드] Section 5 (2) | 2024.01.04 |
[Udemy React 완벽 가이드] Section 4 (2) | 2024.01.04 |
[Udemy React 완벽 가이드] Section 3 (1) | 2024.01.04 |
[Udemy React 완벽 가이드] Section 1 (1) | 2024.01.04 |