[Udemy:React] Section3: 리액트 핵심 (컴포넌트, JSX, 속성, 상태 ...)

Beanxx·2024년 10월 17일
1

Udemy:React

목록 보기
2/2
post-thumbnail

✅ Components

Components are UI Building blocks.

⇒ 리액트 애플리케이션은 컴포넌트를 결합하여 만들어지는 것

  • 컴포넌트는 복잡도와 상관없이 모든 리액트 애플리케이션에서 사용됨
  • UI를 여러 컴포넌트의 결합으로 여기는 것은 리액트에서만 적용되는 것은 아님

👀 React Component: HTML + CSS + JavaScript


👍 컴포넌트의 장점

1️⃣ Reusable building blocks

a. Create small building blocks & compose the UI from them
b. If needed: Reuse a building block in different parts of the UI

⇒ 복잡한 사용자 인터페이스를 관리하기 쉽도록 작게 분리하여 UI의 다른 위치에도 사용할 수 있게 해줌

a. Related HTML & JS code is stored together
b. Since JS influences the output, storing JS + HTML together makes sense

  • 비슷한 코드는 함께 저장됨
  • 유사한 코드가 대체로 함께 묶여있어서 개발 과정이 단순해짐

3️⃣ Separation of concerns

a. Different components handle different data & logic
b. Vastly simplifies the process of working on complex apps

  • 디자인과 개발 원칙을 따름 ⇒ 관심사 분리

👀 동작 순서: index.htmlindex.jsxApp.jsx



✅ JSX

JSX (JavaScript Syntax eXtension) : JS 문법 확장자
⇒ JS 파일 내에 HTML 마크업 코드를 작성하여 HTML 요소를 설명하고 생성할 수 있게 함

✋ But, 브라우저에선 사용 불가능 ⇒ 그치만, 리액트 개발자로서 작성하는 코드는 브라우저에 도달하기 전에 개발 서버에서 변환됨

‣ JSX 규칙

1️⃣ 함수 제목은 대문자로 시작 - PascalCase (e.g. MyHeader)

🤷‍♀️ why?
div, image, header와 같은 내장 요소는 소문자로 시작하는 반면,
커스텀 컴포넌트는 리액트에게 내장된 컴포넌트가 아니라는 것을 알리기 위해 무조건 대문자로 시작해야 함!!
(내장 요소들은 리액트에서 DOM 노드로서 렌더링되는 반면, 커스텀 컴포넌트는 단순 함수이므로 리액트에서 함수로서 실행됨)

2️⃣ 함수에서 렌더링 가능한 값이 반환되어야 함 (대체로 나중에 렌더링 될 HTML 마크업이 반환됨)


function Header () {
	return (
		<header>Header!!</header>
	)
}

function App() {
	return (
		<div>
			<Header />
		</div>
	)
}

JSX 목적: 컴포넌트로부터 생성되어야 하는 타겟 HTML 코드를 더 쉽게 정의할수 있도록 하는 것

JSX의 강점: JSX 코드 안에 일반 HTML 코드 같이 컴포넌트 함수 사용 가능!

👀 .jsx는 브라우저에선 지원되지 않는 파일 확장자이지만, 리액트 프로젝트에선 지원하기 때문에 작동 가능!
→ 개발 서버가 실행될 때 백그라운드에서 실행되는 빌드 프로세스에게 해당 파일이 JSX 코드를 포함하고 있다는 것을 알려줌
⇒ 즉, .jsx 확장자를 처리하는 것은 빌드 프로세스뿐 !


💡 index.jsx 파일은 HTML 파일에 가장 먼저 로딩되는 파일

// index.jsx

import ReactDOM from 'react-dom/client';
// 이로 인해 앱 컴포넌트가 결과적으로 렌더링됨
// => 앱 컴포넌트의 내용을 화면에 출력하는 것을 담당

const entryPoint = document.getElementById("root");
ReactDOM.createRoot(enyryPoint).render(<App/>);
// createRoot와 렌더링 메소드가 단 하나의 루트 컴포넌트 렌더링
<!-- index.html -->

<body>
	<!-- 여기에 위의 'root' id 요소가 매칭됨 -->
	<div id="root"></div>
	<script type="module" src="/src/index.jsx"></script>
</body>


✅ 커스텀 컴포넌트

  • 커스텀 컴포넌트는 실제로 렌더링된 DOM에 나타나지 않음 ⇒ 기본 HTML 요소만 있을 뿐!
  • 리액트는 모든 컴포넌트에서 나온 모든 JSX 코드를 결합하여 전반적인 DOM을 생성함 (즉, 화면에 보이는 요소)


✅ 동적 값 출력

태그 내 중괄호 사이에 자바스크립트 표현 추가 가능

const description = customFun(2);
    
<p>{description}</p>

<img src="src/assets/react.png" alt="react icon" />

// -> 최적의 방법이 아님!
// 배포시 이미지가 사라질 수도 있음 
// 배포 과정에서 모든 코드가 변환 및 최적화되고 함께 묶여짐
// 묶여지는 과정에서 이와 같이 로딩된 이미지 파일이 무시될 수 있고, 배포 과정에서 유실될 수 있음
// 또한, 추가적인 최적화 단계를 사용할 수 없음

// ---------------------------------------

// ⇒ 최적의 방법으로 이미지 불러오기
import reactImg from './assets/react.png';

// reactImg: 사용할 경로를 포함하는 변수
<img src={reactImg} alt="react icon" />


✅ Prop

props: 데이터를 컴포넌트로 전달하고 그 데이터를 그 곳에 사용 가능

⇒ key-value 쌍들을 모두 가진 하나의 객체를 props 매개변수를 통해 얻는 것임!


<CoreConcept
  title={CORE_CONCEPTS[0].title}
  description={CORE_CONCEPTS[0].description}
  image={CORE_CONCEPTS[0].image}
/>

// ----> spread syntax로 축약하여 표현 가능

/* 변수를 불러와 동적 설정 가능 → 1개의 컴포넌트로 다양한 항목 표시 가능 */
<CoreConcept {...CORE_CONCEPTS[0]} />
<CoreConcept {...CORE_CONCEPTS[1]} />
<CoreConcept {...CORE_CONCEPTS[2]} />
<CoreConcept {...CORE_CONCEPTS[3]} />
function CoreConcept(props) {
  return (
    <li>
      <img src={props.image} alt={props.title} />
      <h3>{props.title}</h3>
      <p>{props.description}</p>
    </li>
  );
}

// ----> 구조 분해 할당으로 축약하여 표현 가능

// 구조 분해 할당을 통해 props 설정 가능
// {} : 1번째 매개변수 함수를 구조 분해할 때 사용
// 독립된 변수로 제공
function CoreConcept({ image, title, description }) {
  return (
    <li>
      <img src={image} alt={title} />
      <h3>{title}</h3>
      <p>{description}</p>
    </li>
  );
}


✅ CSS style file

✋ Header.css 파일에 있는 스타일이 <Header/> 컴포넌트에서 import했다고 해서 해당 컴포넌트로 범위가 제한된 것은 아니므로 전체 페이지 내 헤더 태그에 스타일이 적용됨에 주의!

👀 컴포넌트별 CSS 파일로 구분하면 어떤 스타일이 어떤 컴포넌트에 적용되는지 구분하는 것과 스타일 조절하는 것에 있어서 so easy~!
(✋ 해당 컴포넌트에 자동적으로 한적적으로 적용되는 것이 아니라는 점에 주의!)

<section id="examples">
  <h2>Examples</h2>
  {/* menu: 버튼 목록 만드는데 사용 가능 */}
  <menu>
	  {/* 이렇게 감싸서 컴포넌트 구축하는 것을 'Components Composition ; 컴포넌트 합성' */}
	  <TabButton>Components</TabButton>
	</menu>
</section>


✅ Children Prop

children prop : 리액트에서 설정한 prop으로, 어느 특정한 속성에 의해 설정한 prop이 아님!

  • 컴포넌트 텍스트 사이에 있는 내용
  • 필요에 따라 복잡한 JSX 구조가 들어갈 수도 있음
  • 👍 single piece of renderable content
export default function TabButton({children}) {
  return <li><button>{children}</button></li>
}

<TabButton>Components</TabButton>

‣ Attribute props

  • 👍 multiple smaller pieces of information
export default function TabButton({label}) {
  return <li><button>{label}</button></li>
}

<TabButton label="Components" />


✅ 이벤트 처리

export default function TabButton({ children, onSelect }) {
  // func name recommandation: e.g. handleClick or clickHandler
  function handleClick() {
    console.log("Hello World!");
  }

  return (
    <li>
      {/* 
		   - handleClick()으로 실행해선 안됨! -> 이 경우 함수가 바로 실행됨 함수를
		   - 값으로 사용하고자 하므로 함수 이름을 사용해야 함! 
      */}
      <button onClick={handleClick}>{children}</button>
    </li>
  );
}

🔸 이벤트 함수에 커스텀 인자 전달

function handleSelect(selectedButton) {
  console.log(selectedButton);
}

/* 

해당 코드가 분석될 때 화살표 함수만 정의되기 때문에 화살표 함수 안의 코드는 아직 실행되지 않음
버튼이 클릭되어 함수가 실행되면 화살표 함수 코드가 실행됨
=> 리액트에서 이벤트에 따라 실행되는 함수를 정의하고 싶은데 어떻게 불려질지 그리고 
어떤 인수를 실행할지를 통제하고 싶을 떄 자주 사용하는 패턴 !

*/

<TabButton onSelect={() => handleSelect('components')}>Components</TabButton>

🤷‍♀️ Q: 이벤트로부터 독립적인 함수는 어떻게 구성/설정 할 수 있을까?

🎙️ A: 이벤트를 핸들링하는 함수의 실행을 다른 함수로 감싸면, 그 다른 함수가 이벤트 핸들링의 prop(속성)의 값으로 전달된다. so, 메인 함수는 바로 실행되지 않고, 이벤트 발생시에만 실행된다!



✅ UI Rendering

let tabContent = <p>Please select a topic.</p>;

// 위와 같이 state가 아닌 let 으로 선언한 값을 변경하는 경우
// 코드는 최초 1번만 실행되기 때문에 새로운 내용이 감지되지 않아 UI가 업데이트되지 않음

// 🚨 리액트는 컴포넌트 함수를 코드 내에서 처음 발견했을 때 1번 밖에 실행하지 않음 주의!
// => 함수가 실행되고 변수는 업데이트 되지만 UI는 업데이트되지 않음

// ✨ 일반적인 변수로는 UI 업데이트 불가 
// -> 컴포넌트 함수가 재실행되어야 한다는 것을 리액트에게 알려줄 방법을 찾아야 함 
// => state(상태) 필요!


✅ State 관리 & Hooks

  • 리액트 프로젝트에서 use로 시작하는 모든 함수 ⇒ React Hooks

  • React Hook은 일반 함수이지만, 리액 컴포넌트 함수 또는 다른 리액트 Hook 안에서 호출되어야 함

  • 컴포넌트 함수 안에서 바로 호출해야 하며, 다른 코드 안에 중첩되면 안됨 (내부 함수 안에 중첩하여 사용 ❌)
    ⇒ 컴포넌트 함수의 최상위에서 호출해야 함!

‣ Rules of Hooks

  1. Only call Hooks inside of Component Functions
  2. Only call Hooks on the top level

👀 useState는 배열을 반환하며, 요소는 항상 2개!

‣ useState

import { useState } from 'react';

const [selectedTopic, setSelectedTopic] = useState('Please click a button');
  • 1번째 요소: 이 컴포넌트 실행 주기의 현재 데이터 스냅샵
    • ⇒ 컴포넌트 함수가 처음 실행될 때 설정한 초기값이 저장되며, 다시 실행될 땐 업데이트된 값 저장
  • 2번째 요소: 항상 함수이며, 이 상태를 업데이트하기 위해 실행되어 저장된 값 업데이트
    • 리액트에게 여기에 있는 이 컴포넌트 함수를 다시 실행해야 함을 알려줌

⇒ 즉, useState hook을 통해 리액트에게 이 컴포넌트 함수가 다시 실행되어야 한다고 알릴 수 있음

💡 상태를 업데이트했어도 console.log 로그를 출력하면 상태 변경 전의 상태 값이 나옴

🤷‍♀️ why?
상태를 업데이트 시키는 함수인 setState를 부를 때 리액트는 이 상태 업데이트의 스케줄을 조정하며, 이 컴포넌트 함수를 재실행함

✨ So, App 컴포넌트 함수를 다시 실행하고 나서야 업데이트된 값 사용 가능
⇒ 그제서야 새로운 값을 사용할 수 있으므로 업데이트의 스케줄이 조정되자마자 로그를 출력하면 업데이트된 값이 보이지 않는 것임
⇒ 다시 말해서, 아직은 예전 App 함수에 있다는 것!



✅ 조건적 콘텐츠 렌더링

// 합쳐진 삼항연산자를 사용하는 것보다 이렇게 두 부분을 사용하는 것이 보다 읽기 편하고 이해하기 쉬운 코드!

{!selected && <p>Please select a topic.</p>}
{selected && <div id="tab-content">...</div>}

// --> 변수 사용

let tabContent = <p>Please select a topic.</p>;

if (selectedTopic) {
	tabContent = <div id="tab-content">...</div>;
}

{tabContent}


✅ Dynamic List

<ul>
  {CORE_CONCEPTS.map((conceptItem) => (
    <CoreConcept key={conceptItem.title} {...conceptItem} />
  ))}
</ul>
profile
FE developer

0개의 댓글