ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • UI 표현하기
    FE/React 2025. 10. 23. 14:07

    React 는 사용자 인터페이스(UI)를 렌더링하기 위한 JS 라이브러리

    • 버튼, 텍스트, 이미지와 같은 작은 요소 로 구성
    • React를 통해 작은 요소들을 재사용 가능하고 중첩할 수 있는 컴포넌트로 조합가능

    🧩 Component

    React 애플리케이션은 컴포넌트라고 불리는 독립된 UI 조각들로 이루어짐

     

    React 컴포넌트는 마크업을 얹을 수 있는 JavaScript 함수

    • 마크업, CSS, Javascript를 앱의 재사용 가능한 UI 요소인 사용자 정의 컴포넌트로 조합

     

    ↗️ 컴포넌트 import / export

    컴포넌트의 가장 큰 장점은 재사용성이다.

    • 컴포넌트를 조합해 또 다른 컴포넌트를 만들 수 있다.

    컴포넌트를 이렇게 여러 번 중첩하면 다른 파일로 분리해야 하는 시점이 있다.

    • 분리를 하여 파일을 더 찾기 쉽고 재사용하기 편리

     

    📍 export

    • 함수 컴포넌트를 export
    • default 또는 named export 방식을 사용
    function Profile() {
      return (
        <img
          src="test.jpg"
          alt="test"
        />
      );
    }
    
    export default function Gallery() {
      return (
        <section>
          <Profile />
          <Profile />
          <Profile />
        </section>
      );
    }
    • default 방식으로 export

     

    📍 import

    • 컴포넌트를 사용할 파일에서 import
    • default 또는 named 로 import
    import Gallery from './Gallery.js';
    
    export default function App() {
      return (
        <Gallery />
      );
    }
    • default 방식으로 import

     

    ✅ 한 파일에서 여러번 import, export 하는 방법

    • default 키워드 없이 export
    • named export
    export function Profile() {}
    
    ----
    
    import { Profile } from './Gallery.js';

     

    🤖 함수 정의

    function Profile() { .. }

     

    ⚠️ 주의 사항

    • React 컴포넌트는 일반적인 Javascript 함수이지만 이름은 대문자로 시작해야 한다.
    • 그렇지 않으면 작동하지 않음

     

    📌 마크업 추가

    return <img src="test.jpg" alt="test" />;
    • <img /> 는 HTML 같아 보이지만 실제로는 JS이다.
    • JSX라고 함

     

    return (
      <div>
        <img src="test.jpg" alt="test" />
      </div>
    );
    • 마크업이 return 키워드와 같은 라인에 있지 않은 경우 괄호로 묶는다.

     

    🌐브라우저에 표시되는 내용

    export default function Gallery() {
      return (
        <section>
          <Profile />
          <Profile />
          <Profile />
        </section>
      );
    }
    
    ----- ⬇️ 브라우저에 표시
    
    <section>
      <img src="https://test.jpg" alt="test" />
      <img src="https://test.jpg" alt="test" />
      <img src="https://test.jpg" alt="test" />
    </section>

     

    ✅ 대소문자에 주의

    • <section> 은 소문자이므로 React는 HTML태그를 가리킨다고 이해
    • <Profile /> 은 대문자 P로 시작하므로 React는 Profile이라는 컴포넌트를 사용하고자 한다고 이해

    📖 JSX 로 마크업 작성하기

    • JSX와 HTML의 차이점
    • JSX로 정보를 보여주는 방법

    JSX는 Javascript를 확장한 문법

    • JS 파일을 HTML과 비슷하게 작성할 수 있게 해준다.

     

    🔍 React에서 마크업과 렌더링 로직을 같이 사용하는 이유

    이전까지 HTML에 내용, CSS에 디자인, JS로 로직을 작성

    • 분리된 파일로 관리

    웹이 더욱 인터랙티브해지면서 로직이 내용을 결정하는 경우가 많아져 JavaScript가 HTML을 담당하게 됨

    • React에서 렌더링 로직과 마크업이 같은 위치에 있게 된 이유

     

    🤖 React 컴포넌트

    • React 가 브라우저에 마크업을 렌더링할 수 있는 JS 함수

     

    👍 JSX는 HTML과 비슷해보이지만 더 엄격하고 동적으로 표현할 수 있도록 해준다.

     

    ↪️ HTML 을 JSX로 표현해보기

    <h1>Todos</h1>
    <img
      src="test.jpg"
      class="photo"
    >
    <ul>
        <li>li1
        <li>li2
        <li>li3
    </ul>
    • 위 코드를 구현하기 위해 알아야할 JSX 규칙이 존재

    🚀 규칙 1 - 하나의 루트 엘리먼트로 반환

    한 컴포넌트에서 여러 엘리먼트를 반환하기 위해서는 하나의 부모 태그로 감싸야 한다.

    function Test() {
      return (
          <h1> hello </h1>
          <img src="" alt="" className="photo" />
          <ul>
            <li></li>
            <li></li>
            <li></li>
          </ul>
      );
    }
    -----
    
    ❌ JSX expressions must have one parent element.

     

    function Test() {
      return (
        <>
          <h1> hello </h1>
          <img src="" alt="" className="photo" />
          <ul>
            <li></li>
            <li></li>
            <li></li>
          </ul>
        </>
      );
    }
    • Fragment : <> </>

    🤔 왜 여러 JSX 태그를 하나로 감싸줘야 할까??

     

    💡 JSX는 내부적으로 일반 JS 객체로 변환되기 때문에 하나의 배열로 감싸지 않은 하나의 함수에서는 두 개의 객체를 반환할 수 없다.

    따라서 다른 태그나 Fragment로 감싸지 않으면 두 개의 JSX 태그를 반환할 수 없음.

     

     

    🖥️ 실제 Babel 을 통해서 변환된 모습을 확인해보자

    import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
    function Test() {
      return /*#__PURE__*/_jsxs(_Fragment, {
        children: [/*#__PURE__*/_jsx("h1", {
          children: " Hedy "
        }), /*#__PURE__*/_jsx("img", {
          src: "",
          alt: "",
          className: "photo"
        }), /*#__PURE__*/_jsxs("ul", {
          children: [/*#__PURE__*/_jsx("li", {}), /*#__PURE__*/_jsx("li", {}), /*#__PURE__*/_jsx("li", {})]
        })]
      });
    }
    • 여러 JSX를 children 배열로 감싸 Fragment로 반환

     

    🚀 규칙 2 - 모든 태그는 닫아주기

    JSX에서는 태그를 명시적으로 닫아야한다.

    • <img>처럼 자체적으로 닫아주는 태그는 반드시 <img /> 형태로 작성

     

    🚀 규칙 3 - camel Case

    JSX는 JS로 바뀌고 JSX에 작성된 어트리뷰트(속성)는 JS 객체의 key가 된다.

    컴포넌트에 어트리뷰틀르 변수로 읽고 싶을 경우 제한이 존재

    • JS 변수명에 대시를 포함하거나 class 와 같은 예약어를 사용할 수 없다.

    💡 따라서 React에서 HTML, SVG 어트리뷰트 대부분이 캐멀 케이스로 작성

    • stroke-width ⇒ strokeWidth
    • class ⇒ className

     

    🧩 문자열 어트리뷰트 “” 로 전달 / 동적 - {}

    • ‘’ 나 “” 로 문자열 어트리뷰트를 JSX에 전달
    <img src="ddd" />

     

    동적으로 지정하기 위해서는 {} 를 사용

    const image = "";
    
    <img src={image} />

     

    📌 {} 로 Javascript 사용

    • 중괄호 사이에 JS를 사용할 수 있다.
    export default function TodoList() {
      const name = 'beomsic';
      return (
        <h1>{name}'s To Do List</h1>
      );
    }

     

    ↖️ {{ }} 으로 객체를 전달

    export default function TodoList() {
      return (
        <ul style={{
          backgroundColor: 'black',
          color: 'pink'
        }}>
          <li>Improve the videophone</li>
        </ul>
      );
    }
    • 객체를 전달하려면 다른 괄호를 이용해서 객체를 감싼다.

     

    🤖 컴포넌트에 props 전달하기

    React 컴포넌트는 props를 이용해 서로 통신

    • 부모 컴포넌트는 props를 통해 정보를 자식 컴포넌트에게 전달
    • 객체, 배열, 함수를 포함한 모든 JS 값을 전달할 수 있다.

    1️⃣ 자식 컴포넌트에 props 전달

    export default function Profile() {
      return (
        <Avatar
          person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
          size={100}
        />
      );
    }
    • person 객체, size (숫자)

    ❗ Avatar 컴포넌트 내에서 props를 읽을 수 있다.

     

    2️⃣ 자식 컴포넌트 내부에서 props 읽기

    function Avatar({ person, size }) { // 구조 분해 할당
      // 전달된 props 사용 가능
    }
    
    --- 
    function Avatar(props) {
       let person = props.person;
       let size = props.size; 
    }

     

     

    props에 기본값을 지정할 수도 있다.

    function Avatar({ person, size = 10 }) {
      // 전달된 props 사용 가능
    }

     

    🧩 JSX spread 문법으로 props 전달

    • 부모 컴포넌트에서 자식 컴포넌트로 모든 props를 전달해야 할 경우
    • props를 하나하나 설정을 해주게 된다면 반복되는 코드가 발생하게 될 것이다.

     

    💡 spread를 통해 해결

    function Profile({ person, size, ....}) {
      return (
          <div className="card">
              <Avatar
                person={person}
                size={size}
                ...
              />
            </div>
        ) 
    }
    
    ---- spread 사용
    function Profile(props) {
      return (
          <div className="card">
              <Avatar {...props} />
            </div>
        ) 
    }

    ⚠️ 이런 spread 가 많이 사용된다는 것은 문제가 있는 것!!

    • 컴포넌트를 분리해야 함을 나타낸다.

     

    ⬆️ 자식을 부모에게 JSX로 전달

    <Card>
      <Avator />
    </Card>
    • 컴포넌트를 중첩

     

    JSX 태그내에서 콘텐츠를 중첩하게 되면 부모 컴포넌트는 해당 콘텐츠를 children이라는 props로 받게된다.

    import Avatar from './Avatar.js';
    
    function Card({ children }) {
      return (
        <div className="card">
          {children}
        </div>
      );
    }
    
    export default function Profile() {
      return (
        <Card>
          <Avatar
            size={100}
            person={{
              name: 'beomsic',
            }}
          />
        </Card>
      );
    }

    여기서 Card 컴포넌트는 Avatar 컴포넌트로 설정된 children prop를 받아서 이를 div 에 렌더링한다.

     

    ✅ 조건부 렌더링

    if, &&, ?: 와 같은 자바스크립트 문법을 사용해 조건부로 JSX를 렌더링

     

    조건에 따라 다른 JSX를 반환하는 방법

    • prop 값을 통해 다른 JSX를 반환
    function Item({isPacked, name}) {
      if (isPacked) return <li> ✅ </li>;
      return <li> ❌ </li>;
    }
    
    export default function PackingList() {
      return (
          <>
              <Item isPacked={true} name="item1" />
              <Item isPacked={false} name="item2" />
          </>
      )
    }

     

    ✔️ 삼항 조건 연산자

    자바스크립트의 조건 연산자를 사용

    if (isPacked) {
      return <li className="item">{name} ✅</li>;
    }
    return <li className="item">{name}</li>;
    
    --- 위 코드를 삼항 조건 연산자를 이용해서 간단히 수정
    return (
      <li className="item"> 
        {isPacked ? name + ' ✅' : name}
      </li>
    )

    📌 논리 AND 연산자 (&&)

    &&

    React 컴포넌트에서는 조건이 참일 때 일부 JSX를 렌더링 하거나 그렇지 않으면 아무것도 렌더링하지 않을 때를 나타낼 수 있다.

    • && 을 이용
    return (
      <li className="item"> 
        {name} {isPacked && '✅'}
      </li>
    )
    • isPacked 가 true라면 체크 표시를 렌더링하고 그렇지 않으면 아무것도 렌더링을 하지 않는다는 의미

     

    ⚠️ && 왼쪽에는 boolean 값을 주어야 한다.

     

     

    📂 리스트 렌더링

    데이터들에서 유사한 컴포넌트를 여러 개 표시하기 위한 방법

    • JS 배열 메서드를 사용

     

    📌 배열을 데이터로 렌더링

    <ul>
      <li>Creola Katherine Johnson: mathematician</li>
      <li>Mario José Molina-Pasquel Henríquez: chemist</li>
      <li>Mohammad Abdus Salam: physicist</li>
      <li>Percy Lavon Julian: chemist</li>
      <li>Subrahmanyan Chandrasekhar: astrophysicist</li>
    </ul>
    • 리스트 항목은 데이터 값만 다르다.

     

    🤔 다른 데이터들을 통해서 동일한 컴포넌트의 여러 인스턴스를 표시해야 한다면??

     

    💡 자바스크립트 객체, 배열에 저장하고 map() filter() 메서드를 사용해 컴포넌트 리스트를 렌더링

     

     

    1️⃣ 데이터를 배열로 표시

    const people = [
      'Creola Katherine Johnson: mathematician',
      'Mario José Molina-Pasquel Henríquez: chemist',
      'Mohammad Abdus Salam: physicist',
      'Percy Lavon Julian: chemist',
      'Subrahmanyan Chandrasekhar: astrophysicist'
    ];

     

    2️⃣ people 배열을 map()을 이용해서 JSX 노드 배열로 매핑 (listItems)

    const listItems = people.map(person => <li>{person}</li>);

     

    3️⃣ ul 로 매핑한 컴포넌트의 listItems 반환

    return <ul>{listItems}</ul>;

     

    ✔️ 최종 코드

    const people = [
      'Creola Katherine Johnson: mathematician',
      'Mario José Molina-Pasquel Henríquez: chemist',
      'Mohammad Abdus Salam: physicist',
      'Percy Lavon Julian: chemist',
      'Subrahmanyan Chandrasekhar: astrophysicist'
    ];
    
    export default function List() {
      const listItems = people.map(person =>
        <li>{person}</li>
      );
      return <ul>{listItems}</ul>;
    }

     

    ⚠️ Warning: Each child in a list should have a unique “key” prop.

    위 코드에서 위와 같은 에러가 발생!!

     

    📌 필터링 하기

    filter() 메서드를 이용

    const people = [{
      id: 0,
      name: 'Creola Katherine Johnson',
      profession: 'mathematician',
    }, {
      id: 1,
      name: 'Mario José Molina-Pasquel Henríquez',
      profession: 'chemist',
    }, {
      id: 2,
      name: 'Mohammad Abdus Salam',
      profession: 'physicist',
    }, {
      id: 3,
      name: 'Percy Lavon Julian',
      profession: 'chemist',
    }, {
      id: 4,
      name: 'Subrahmanyan Chandrasekhar',
      profession: 'astrophysicist',
    }];
    
    const chemists = people.filter(person =>
      person.profession === 'chemist'
    );

     

    🔑 Key를 사용해서 리스트 순서대로 유지

    ⚠️ Warning: Each child in a list should have a unique “key” prop.

     

    위에서 발생했던 에러를 해결해보자.

     

    각 배열 항목에 대해서 고유하게 식별할 수 있는 문자열이나 숫자를 key로 지정을 해주어야 한다! ⭐

    key를 통해 각 컴포넌트가 어떤 배열 항목에 해당하는지 React에게 알려주어 나중에 일치하도록 한다.

     

    배열이 정렬등 이동하거나 삽입되거나 삭제될 경우 중요

    • Key를 잘 사용하면 React가 어떤 일이 일어났는지 추론하고 DOM 트리를 업데이트하는데 도움이 된다.
    import { people } from './data.js';
    import { getImageUrl } from './utils.js';
    
    export default function List() {
      const listItems = people.map(person =>
        <li key={person.id}>
          ...
        </li>
      );
      return <ul>{listItems}</ul>;
    }

     

    🧩 Key 값

    데이터 항목마다 다른 key 값을 제공해주어야 합니다.

    • 데이터베이스의 데이터 (데이터베이스의 key, ID)
    • 로컬에서 생성된 데이터일 경우
      • 증분되는 번호나
      • crypto.randomUUID()
      • uuid 패키지

     

    ☑️ Key 규칙

    1. key는 형제간에 고유해야 한다.
      • 다른 배열의 JSX 노드에는 동일한 key로 해도 상관없다.
    2. key는 변경되어서는 안된다.
      • 목적에 어긋나는 행위
      • 렌더링중 key를 생성하면 안된다!!

     

    🌈 컴포넌트 순수하게 유지하기

    순수 함수는 오직 연산만을 수행

    컴포넌트를 엄격하게 순수함수로 작성하면 코드가 점차 커지더라도 예상밖의 동작이나 버그를 피할 수 있다.

     

    📍순수 함수

    • 함수가 호출되기전 존재했던 어떤 객체나 변수가 변경되지 않음
    • 같은 입력이 주어지면 같은 결과를 반환

     

    🤖 리액트는 모든 컴포넌트가 순수 함수일것이라 가정

    • React 컴포넌트에 같은 입력이 주어지면 같은 JSX를 반환한다

     

    ⚠️ 사이드 이펙트

    React의 렌더링 과정은 순수해야하고 컴포넌트는 JSX만 반환해야 했으며 렌더링 이전에 존재했던 객체나 변수를 변경해서는 안된다.

    let guest = 0;
    
    function Cup() {
      // 나쁜 지점: 이미 존재했던 변수를 변경하고 있습니다!
      guest = guest + 1;
      return <h2>Tea cup for guest #{guest}</h2>;
    }
    
    export default function TeaSet() {
      return (
        <>
          <Cup />
        </>
      );
    }
    • 컴포넌트의 밖에 선언된 Guest라는 변수를 읽고 수정

     

    📌 프로퍼티로 변수를 넘겨 해결

    function Cup({ guest }) {
      return <h2>Tea cup for guest #{guest}</h2>;
    }
    
    export default function TeaSet() {
      return (
        <>
          <Cup guest={1} />
          <Cup guest={3} />
        </>
      );
    }

     

    💡 정리

    컴포넌트는 순수해야 한다.

     

    1️⃣ 렌더링 전에 존재했던 객체나 변수를 변경하지 않고 자신이 맡은 일을 한다.

    2️⃣ 같은 입력이 들어온 경우 항상 같은 JSX 결과를 반환해야 한다.

     

    🌴 트리로 UI 이해하기

    React 앱은 중첩된 많은 컴포넌트로 구성되어 있다.

    • React는 어떻게 앱의 컴포넌트 구조를 추적하나?

     

    🌴 트리로서의 UI

    UI를 트리로 모델링

    트리는 component와 UI 사이의 관계 모델로 UI는 종종 트리 구조를 사용해 표현된다.

     

    • ex: 브라우저는 HTML (DOM)과 CSS(CSSOM)를 모델링하기 위해 트리구조를 사용

     

    React도 역시 컴포넌트 간의 관계를 관리하고 모델링하기 위해 트리 구조를 사용

     

    ⭐ 렌더 트리

    컴포넌트의 주요 특징은 다른 컴포넌트의 컴포넌트를 구성하는 것이다.

    • 컴포넌트 중첩

     

    컴포넌트를 중첩시 부모 컴포넌트와 자식 컴포넌트의 개념이 생기고 각 부모 컴포넌트는 다른 컴포넌트의 자식이 될 수 있다.

    이런 관계를 React 앱을 렌더링 할 때 렌더 트리라고 알려진 트리로 모델링할 수 있다.

    import FancyText from './FancyText';
    import InspirationGenerator from './InspirationGenerator';
    import Copyright from './Copyright';
    
    export default function App() {
      return (
        <>
          <FancyText title text="Get Inspired App" />
          <InspirationGenerator>
            <Copyright year={2004} />
          </InspirationGenerator>
        </>
      );
    }

    위 코드를 렌더 트리로 나타내면 아래와 같다.

     

     

    React 앱에서 최상위 컴포넌트와 리프 컴포넌트가 무엇인지 식별하는데 도움이 된다.

     

    🧩 조건부 렌더링을 하면 Prop 값으로 인해 컴포넌트가 다른 자식 컴포넌트를 렌더링할 수 있다.

     

    🔗 모듈 의존성 트리

    트리로 모델링 할 수 있는 다른 관계는 앱의 모듈 의존성이다.

    • 컴포넌트를 분리한 후 로직을 별도의 파일로 분리하면 컴포넌트, 함수, 상수를 내보내는 JS 모듈을 만들 수 있다.

    각 노드는 모듈, 각 가지는 해당 모듈의 Import 문을 나타낸다.

     

     

    'FE > React' 카테고리의 다른 글

    React의 State 업데이트 큐  (0) 2025.10.23
    React 개요  (0) 2025.10.23

    댓글

Designed by Tistory.