React 란

  • 웹사이트의 UI(화면)를 레고 블록처럼 부품화해서 조립할 수 있게 만든 페이스북(Meta) 개발 프런트엔드 라이브러리
  • 선언적 UI(Declarative UI) 패턴과 Virtual DOM을 활용해 상태(State) 변화에 따른 화면 업데이트를 효율적으로 처리해 주는 JavaScript UI 라이브러리
    • State 변경 감지 : 사용자의 입력이나 API 응답 등으로 인해 컴포넌트 내부의 상태(State)가 변경
    • 선언적 UI : UI를 상태(State)의 함수로 구현하여, 데이터가 주어지면 프레임웍이 알아서 화면을 그림
    • Virtual DOM : 실제 DOM을 직접 조작하는 대신 메모리에 가상 DOM 트리를 유지하며, 변경 사항 발생 시 변경된 최소 부분만 실제 DOM에 반영

React의 필요성

전통적인 방식: HTML 중심 (Imperative, 명령형)

  • 구조: 주로 MPA (Multi-Page Application) 방식.
  • 화면이 바뀌어야 하면 서버에서 새 HTML 파일을 통째로 가져오거나, 자바스크립트로 DOM을 일일이 건드림.
    • Where: “어떤 태그를 선택할까? (getElementById, querySelector)”
    • How: “어떤 속성을 바꿀까? (.innerText, .style.color)”
  • 단점: 서비스가 커질수록 “데이터-HTML” 사이의 연결 고리가 꼬여서 버그(싱크 불일치)가 발생할 확률이 매우 높음.
<!-- HTML 구조 (뼈대) -->
<div>
  <span id="counter">0</span>
  <button id="btn">증가</button>
</div>

<script>
        let count = 0; // 데이터(State)

  const counterEl = document.getElementById('counter');
  const btnEl = document.getElementById('btn');

// 명령: 버튼을 클릭하면...
  btnEl.addEventListener('click', () => {
// 1. 데이터를 바꾼다
count++;

// 2. 화면의 숫자를 찾아가서 바꾼다 (Where & How)
counterEl.innerText = count;

// 3. 조건에 맞는지 확인하고 스타일을 직접 바꾼다 (Where & How)
    if (count === 10) {
counterEl.style.color = 'red';
        }
        });
</script>

React 방식: 데이터 중심 (Declarative, 선언적)

  • 구조: SPA (Single-Page Application) 방식.
  • 브라우저는 처음에 빈 HTML 한 장만 받고, 나머지는 JS가 상황에 맞게 화면을 ‘조립’함.
  • 리액트 방식: “데이터가 바뀌었네? 그럼 이 데이터가 들어간 화면을 함수로 다시 돌려서 새로 그리자.”
    • “상태(Data)가 바뀌면 화면은 당연히 이렇게 보여야 해.“라고 미리 정의함.
    • “HTML을 먼저 만들자"가 아니라, “이런 데이터가 들어오면 이런 모양의 HTML을 생성하는 자바스크립트 함수를 만들자"라고 생각합니다.
    • UI = f(state): 상태가 변하면 함수(f)를 다시 실행해서 새로운 UI를 얻음
function Counter() {
    // 1. 상태(Data)를 정의한다.
  const [count, setCount] = useState(0);

    // 2. "UI = f(state)" : 데이터 상태에 따른 UI 모양을 선언한다.
    return (
            <div>
            {/* 데이터가 10이면 빨간색, 아니면 검정색이라고 미리 '선언' */}
            <span style={{ color: count === 10 ? 'red' : 'black' }}>
    {count}
      </span>

            {/* 데이터만 바꾼다. 화면 업데이트 명령은 따로 안 한다. */}
            <button onClick={() => setCount(count + 1)}>
            증가
            </button>
            </div>
  );
}

JSX

  • 역할 : JavaScript를 사용해서 HTML을 만드는 문법 확장
  • 정체 : 빌드 시 React.createElement()라는 Javascript 함수로 변환되는 문법적 설탕(Syntactic Sugar).
// JSX 방식 (일반적)
return (
  <UserContext.Provider value={{ user, setUser }}>
    <Header />
    <Main />
  </UserContext.Provider>
);

// createElement 방식 (동일한 코드)
return React.createElement(
  UserContext.Provider,
  { value: { user, setUser } },
  React.createElement(Header),
  React.createElement(Main)
);

리액트(React)의 본질

1. 존재의 형태: “컴파일된 정적 자산”

  • 리액트 코드는 빌드 과정을 거치면 브라우저가 해석할 수 있는 HTML, CSS, JavaScript 파일로 변환됩니다.

2. 실행의 본질: “브라우저 위에서 동작하는 런타임 어플리케이션”

  • 사용자가 URL을 입력하면 브라우저는 리액트 프로그램 실팽하일 (index.html 과 JS파일 덩어리)를 다운받고 이를 실행시켜서리액트라는 동적인 앱을 실행합니다.
  • 브라우저 메모리에 리액트 엔진이 로드되고 App.tsx가 실행되는 순간, 리액트는 가장 먼저 주소창(Window Location)을 확인하여 하부 url에 맞는 컴포넌트를 호출하여 랜더링합니다.

3. 핵심 메커니즘: “데이터(상태) 기반의 UI 선언”

  • 리액트 앱이 실행된 후 하는 일은 상태(State)가 변하면 화면(DOM)을 자동으로 다시 그리는 것
  • 가상 DOM (Virtual DOM): 화면 전체를 무식하게 새로고침하지 않습니다. 메모리에서 바뀐 부분만 계산해서(Diffing) 진짜 화면에 살짝 반영합니다.
  • 컴포넌트 단위 설계: 화면을 함수 단위로 쪼개어 관리하므로, 대규모 시스템에서도 UI 로직이 꼬이지 않게 방어해 줍니다.

리액트의 주요특징

1. 신호의 상행: “Event Bubbling & Callback”

사용자님이 말씀하신 대로, 모든 클릭이나 입력은 트리의 가장 끝인 HTML 태그에서 시작됩니다. 하지만 HTML 태그 스스로는 데이터를 바꿀 능력이 없습니다.

  • 메커니즘: HTML 태그(예: <button>)에 전달된 이벤트 핸들러(함수)가 실행됩니다.
  • 전파: 이 함수는 보통 상위 컴포넌트에서 Props로 내려준 콜백 함수입니다. 클릭 신호가 이 콜백을 타고 트리 윗부분으로 ‘보고’됩니다.
  • 백엔드 비유: 하위 모듈에서 발생한 이벤트를 상위 컨텍스트의 Listener가 낚아채는 것과 같습니다.

2. 상태의 변화: “Single Source of Truth”

신호가 트리의 특정 높이에 도달하면, 거기서 기다리던 상태 업데이트 함수(setState)가 실행됩니다.

  • 중요 지점: 리액트 트리의 상단(또는 중간) 어딘가에는 이 UI의 ‘진실’을 쥐고 있는 State가 살고 있습니다.
  • 결과: 이벤트가 이 State를 건드리면, 리액트 엔진은 “어? 데이터가 바뀌었네? 그럼 이 아래 트리들은 다 다시 그려야겠다!“라고 판단합니다.

3. 데이터의 하행: “Re-rendering & Diffing”

상태가 바뀌면 이제 거꾸로 트리의 위에서 아래로 새로운 데이터가 전파됩니다.

  • Top-down 전파: 바뀐 데이터를 기반으로 컴포넌트 함수들이 재실행되면서 새로운 HTML 구조를 계산합니다.
  • Virtual DOM Diffing: 리액트는 메모리상에서 “이전 트리"와 “지금 계산한 트리"를 비교합니다.
  • 최종 반영 (Patch): 실제 HTML/CSS 중에서 정말로 값이 바뀐 딱 그 말단 부분만 브라우저에 업데이트합니다.

리액트(React)의 주요 요소

  • 엘리먼트(Element) = 리액트가 관리하는 객체
    • 역할: 화면에 무엇이 나타나야 하는지 기술하는 “설명서"입니다.
    • 본질: 일반적인 자바스크립트 객체(Plain Object)입니다.
    • 동작: 컴포넌트 함수를 실행하거나 JSX를 해석하면 이 객체가 만들어집니다.
  • 컴포넌트(Component) = 엘리먼트를 반환하는 함수
    • 역할: “UI의 재사용 가능한 단위"이자 “설계도"입니다.
    • 본질: 자바스크립트 함수(또는 클래스)입니다.
    • 동작: 호출 시 JSX(결국 Element)를 반환 .
  • JSX: 빌드 시 React.createElement()라는 Javascript 함수로 변환되는 문법적 설탕(Syntactic Sugar).
  • State : 변경 시 리렌더링을 트리거하는 컴포넌트 자신의 데이터
  • Props: 부모로부터 읽기 전용으로 전달받는 데이터
  • Hooks: React 내장 기능을 사용하는 API
  • Virtual DOM: state 변경을 감지해 변경된 부분만 실제 DOM에 반영하는 렌더링 메커니즘

리액트(React) 초기화

브라우저에 그려지는 흐름

  1. index.html: 브라우저가 처음 받는 유일한 HTML 파일입니다. 내용은 거의 비어 있고, React 앱이 들어갈 자리인 <div id=“root”></div>와 JS 번들을 불러오는 <script>만 있습니다
  2. main.tsx (또는 index.tsx): JS 번들의 시작점입니다. createRoot(document.getElementById(‘root’)).render(<App />)로 root 엘리먼트에 <App />을 붙입니다.
  3. App.tsx: 화면에 실제로 그려지는 UI의 최상위 부모 컴포넌트입니다.
  4. React 앱은 APP 함수를 실행하면서 자식 컴포넌트 함수를 props(파라미터)를 넘기면서 실행합니다.
  5. 자식들은 또 자식들을 실행하여 Tree를 형성하고, 이 결과가 DOm으로 그려집니다
  6. 각 컴포넌트는 useState로 자기상태를 갖고, React는 자기상태를 변경하는 setter를 외부에 노출하여 리렌더링을 트리거합니다.

App.tsx 주요역할

  • 전역 레이아웃 구성 : 헤더(Header), 푸터(Footer), 사이드바처럼 모든 페이지에서 공통으로 보이는 레이아웃 프레임을 구성합니다.
  • 라우팅(Routing) 설정: React Router 등을 사용해 APP에서 URL에 맞는 컴포넌트까지 순차적으로 실행합니다.
  • 전역 상태 및 프로바이더(Provider) 관리: Redux, React Query, ThemeProvider, AuthProvider 등 앱 전역에서 쓰이는 상태 관리 도구를 최상위에서 감싸줍니다.

컴포넌트

  • 정의 : JSX를 반환하는 javascript 함수
  • 정체 : 특정 데이터 (State, Props) 변경시 함수를 함수를 재실행하여 결과물을 UI에 반영하는 특수목적 함수
  • 재실행 트리거 :
    • 입력(Props): 부모로부터 전달받는 함수의 파라미터. 부모가 재실행되면 Props가 재전달된다.
    • 기억(State): 리액트 메모리에 저장되는 데이터. State가 변경되면 재실행된다.

리액트의 컴포넌트 관리방식

컴포넌트 선언

  • 컴포넌트 등록규칙 :
    • 함수명이 대문자로 시작하여 React가 인식할 수 있어야함
    • React 트리에 등록된 부모로부터 호출되어야함.
// 컴포넌트 선언 (정의)
const CustomSelectBox = ({ value, onChange, options, width }) => {
  return <Select>...</Select>
}
  • 리액트가 ‘렌더링 트리’라는 명단에 컴포넌트를 올리고 상태가 변하면 상태를 구독하는 컴포넌트를 리랜더링함
  • 등록시의 동작
    • 1. 호출 시점을 결정함 (Scheduling) : 트리에 컴포넌트를 등록하면 적절한 시점에 대신 호출해 줍니다.
    • 2. “기억"을 부여함 (State Management) : 일반 함수는 실행이 끝나면 내부 변수가 다 사라집니다. 하지만 리액트가 관리하는 컴포넌트 함수는 실행이 끝나도 useState 같은 도구를 통해 내부의 데이터(상태)를 리액트 메모리에 저장해둡니다.
    • 3. 변화를 감지하고 다시 실행함 (Re-rendering) :컴포넌트 내부의 데이터가 바뀌면, 리액트는 해당 함수를 다시 호출(재실행)합니다.

컴포넌트 호출

  • 호출위치 : HTML 태그와 동일하게 다른 컴포넌트 안에 나열
  • 요구인자 : JSX 태그 안에 속성으로 이름=값 형식으로 전달
// 컴포넌트 사용 (호출) ← 지금 이게
<CustomSelectBox
value={instance.aiServiceId.toString()}
onChange={e => onUpdateInstance(...)}
options={serviceOptions}
width='200px'
  />

부모 컴포넌트: “전화번호(함수)를 만들고 전달한다”

부모는 자기의 상태를 바꿀 수 있는 함수를 만들어서 자식에게 넘겨줍니다.

function Parent() {
const [message, setMessage] = useState("대기 중...");

// 1. 자식이 호출할 함수를 정의함
const handleChildSignal = (data) => {
setMessage(data); // 자식이 준 데이터로 부모의 State를 변경
};

// 2. 자식에게 'onSignal'이라는 이름으로 이 함수를 배달함
return <Child onSignal={handleChildSignal} />;
}

자식 컴포넌트: “이벤트가 터지면 그 전화번호로 전화를 건다”

자식은 부모가 준 함수가 뭔지 정확히 몰라도 됩니다. 그냥 “일 터지면 이거 실행해"라고 받았으니 실행만 할 뿐입니다.

function Child(props) {
// 3. 버튼 클릭이라는 '브라우저 이벤트'가 발생하면
// 부모가 보내준 함수(props.onSignal)를 실행함
return (
<button onClick={() => props.onSignal("자식이 보낸 메시지!")}>
부모에게 신호 보내기
</button>
);
}

컴포넌트 훅

  • 리액트 컴포넌트는 UI 함수이므로 리렌더링 때마다 지역 변수값이 사라진다. 때문에, 어떤 값을 리렌더링 속에서도 저장하기위해서는 리엑트 엔진의 메모리공간을 사용해야한다.
  • 훅 : 갈고리(Hook)를 걸어 리액트 메모리 내 데이터를 다루는 메커니즘이라서 ‘훅’이라고 부릅니다. 컴포넌트가 React 기능 (상태, 생명주기 등) 을 포함한 로직을 재사용 가능하게 추출한 함수

훅사용법

  • 문법상으로 use로 시작하는 React 함수만 훅으로 리엑트 엔진이 인식합니다.

다양한 React 내장 hook

  • useState : 컴포넌트 내 상태 관리
  • useEffect : 사이드이펙트 (데이터 fetch, 이벤트 등록)
  • useRef : DOM 참조
  • useContext : context 접근
  • useCallback : 함수를 메모이제이션
  • react-query : API 호출이 자동으로 캐싱, 로딩 상태, 에러 처리, 재시도 같은 기능을 제공

Custom hook (직접 만든 것)

  • useCameras → CamerasContext 접근 + 관련 로직
  • useFlowPlan → API 호출 (react-query)
  • useLayout → 레이아웃 관련 상태

주요 훅의 종류

useState (상태: 기억력)

  • 역할: 컴포넌트 안에 변하지 않는 저장소를 만듭니다.
  • 구현 : 관리할 변수와 변수를 변경하는 함수명을 선언하면 react가 변경함수를 생성하고 이것을 활용해서 상태를 변경한다.
  • 리액트에서 생성한 setConfig 함수를 통하지않고, 직접 config 값을 변경하면 리액트 엔진이 변경을 감지하지 못함.
const [config, setConfig] = useState(null)

config    = 읽기 전용 현재값
setConfig = 값 변경 함수 (호출하면 리렌더링 발생)
null      = 초기값

useEffect

  • 역할: “렌더링(화면 그리기)이 끝난 직후"에 수행할 별도의 작업을 예약합니다. 내가 감시하는 값이 바뀔 때만 실행되도록 조절할 수 있습니다.
  • 동작 원리: 함수가 재실행될 때, 리액트는 방금 들어온 count 값과 메모리에 저장해둔 이전 count 값을 비교합니다.
useEffect(() => {
  // 실행할 코드
}, [의존성 배열])
useEffect(() => {
  localStorage.setItem(
    LAYOUT_STORAGE_KEY,
    JSON.stringify({
      companyId,
      companyName,
      companyNo,
      loginId: layoutUserInfo?.loginId ?? ''
    })
  )
}, [companyId, companyName, companyNo, layoutUserInfo])
//  ↑ 이 중 하나라도 바뀌면 위 코드 실행
  • 정리함수등록 : 내가 지금 준 함수를 실행하고, 그 함수가 뭔가를 반환하면, 그 반환된 걸 나중에 실행할 정리 함수로 등록해둬 ( 리액트와 정해진 약속)

useRef

  • 역할: 리렌더링을 유발하지 않으면서, 컴포넌트의 전 생애주기(Lifecycle) 동안 유지되는 값을 저장하는 비밀 상자

useCallback

React가 상태 변화를 감지하면 컴포넌트 함수를 처음부터 다시 실행시키고, 그 과정에서 내부 함수들도 새로 만들어지는데, useCallback은 그걸 막는 것.

  • 사용케이스 : 함수의 내용은 안 변했는데, 주소가 바뀌었다고 감시자들이 오탐을 내는 것
    • 함수가 useEffect 의존성 배열에 들어갈 때 :
    • 함수를 React.memo로 감싼 자식에게 prop으로 넘길 때
const [userId, setUserId] = useState(1);

const fetchUser = useCallback(() => {
  api.get(userId);   // ← userId를 쓰고 있음
}, [userId]);        // ← 그러면 userId 바뀔 때만 함수 새로 만들어

useContext 훅

  • Props Drilling 이슈 : React에서 데이터는 부모에서 자식으로만 흐릅니다. 부모와 자식 중간에 있는 컴포넌트가 Props 변경으로 불필요한 렌더링이 발생합니다. 이를 막기 위해서 Context API는 중간 단계 없이 데이터를 ‘브로드캐스팅(Broadcasting)‘하는 레이어를 깔아줍니다.
createContext() 호출
       ↓
RuntimeConfigContext (객체) 생성
       ├── .Provider  → 값을 공급하는 컴포넌트
       └── .Consumer  → 값을 꺼내는 구식 방법 (현재는 useContext 사용)

React Context는 생성 → 제공 → 소비 3단계로 동작합니다.

① React.createContext<T>(defaultValue: T) : Context 객체 생성

  • 제네릭 <T> : 이 Context가 담을 값의 타입
  • 인자 defaultValue : Provider로 감싸지 않은 컴포넌트가 Context를 읽으려 할 때만 사용
  • 생성시기 : 모듈이 import되는 시점에서 실행되므로, import하는 컴포넌트의 렌더링보다 항상 먼저 Context 객체가 생성
// UserContext.js (별도 모듈)
const UserContext = React.createContext(null);  // ← 모듈 레벨, App 렌더링 전 실행
// null은 Provider 없이 소비할 경우의 기본값

export default UserContext;

② Provider value={{ user, setUser }} :

  • Context 에 정의된 타입에 맞는 값을 value를 설정하고, 이를 하위트리에 제공
  • 컴포넌트 내부 실행 시점이라 useState로인해 변경된 값과 set함수를 value에 담아 자식트리로 전달 가능
function App() {
  const [user, setUser] = useState({ name: "상화", role: "admin" });

  return (
    <UserContext.Provider value={{ user, setUser }}>
      <Header />
      <Main />
    </UserContext.Provider>
  );
}

③ useContext : Provider의 value에 담긴 값을 꺼내오는 Hook. 단순히 값을 꺼내오는 것뿐만 아니라 구독 등록도 동시에 일어남

function UserAvatar() {
  const { user } = useContext(UserContext);
  // 중간 컴포넌트를 거치지 않고 직접 꺼냄
  return <img alt={user.name} />;
}

리액트의 빌드결과물

  • 리액트 빌드 결과물은 실행 파일이 아니라 html, css, javascript로 이루어진 정적 자산(Static Assets)
  • 이 정적인 자산들은 클라이언트의 로컬에서 독립적으로 움직이기때문에 네트워크 통신 외에는 동적인 데이터를 가져올수 없다.

리액트 표준구조

  • 1. Container / Presentational 패턴
    • Container = 컴포넌트 트리 중 공통부모 하나가 비즈니스 로직 + 데이터 + API + 상태관리 담당
    • Presentational = 컴포넌트 트리 중 자식이 UI 렌더링만 담당, 데이터가 어디서 오는지 모름
  • 2. Functional Component + Hooks 패턴 : custom hook으로 로직 분리 + 컴포넌트는 UI만
    • React 16.8 이후 표준 방식 (예전엔 class component였음)
  • 예시 : 카메라 맵 하위에 FloorPlan과 childFloorPlan이 트리구조로 자리잡음.
// 1. interface / 상수 선언
interface IDialogProps { ... }
const INITIAL_DIALOG_PROPS = { ... }

// 2. 헬퍼 함수
const removeDuplicateCameras = () => { ... }

// 3. 컴포넌트 본체
const CamerasMap = () => {
  // state, ref, hook 선언
  const [flowPlan, setFlowPlan] = useState(...)
  const { companyNo } = useLayout()

  // 핸들러 함수들
  const handleImageMapClick = () => { ... }
  const handleChildFileUpload = () => { ... }

  // UI
  return (
      <Grid>
        <ImageMap ... />
        <ChildFlowPlanPanel ... />
      </Grid>
  )
}

리액트 표준 레이어

🖥️ 1. UI 레이어 (Presentational Layer)

  • 한 줄 요약: 눈에 보이는 껍데기를 만드는 영역
  • 주요 구성 요소: div, span, button 같은 플랫폼 전용 태그들, 그리고 순수하게 스타일(CSS)만 입혀진 UI 컴포넌트들.

⚙️ 2. 로직/상태 레이어 (Business Logic Layer)

  • 한 줄 요약: 백엔드에서 데이터를 가져오고, 계산하고, 상태를 바꾸는 순수한 자바스크립트 로직입니다.
  • 주요 구성 요소: Custom Hook(커스텀 훅), useState, useEffect 등.

🌐 3. 인프라/데이터 레이어 (Data Access Layer)

  • 한 줄 요약: 외부(백엔드 서버)와 소통하는 통로 영역
  • 주요 구성 요소: fetch, axios, TanStack Query(React Query), 혹은 전역 상태 관리 도구(Zustand, Redux 등).

전역상태관리

  • React 표준 : UI 상호작용을 통해서 React 컴포넌트 트리 내부에 존재하는 상태관리변수를 변경시키고 이를 통해서 리랜더링 제어
  • 전역상태관리 (Zustand/Redux) : React 외부의 자바스크립트 엔진 내에 독립된 상태변수를 두고 일반 JS함수가 상태변수에 변경을 가하면 React 컴포넌트가 이를 감지하여 리랜더링

컴포넌트 설계 방법론

Atomic Design

  • Atoms (원자) → Molecules (분자) → Organisms (유기체) → Templates (템플릿) → Pages (페이지) 분류체계로 아래 단계일수록 범용, 위로 갈수록 도메인을 안다
  • Atoms : 더 이상 쪼갤 수 없는 최소 단위
  • Molecules : Atom 여러 개 조합, 그 자체로 재사용 가능한 위젯
  • Organisms — Molecule/Atom 조합, 의미있는 섹션
  • Templates : 레이아웃만 있고 실제 데이터는 없는 뼈대, 어디에 뭐가 들어갈지 구조만 정의
  • Pages : Template에 실제 데이터 주입한 완성본, 실제 유저가 보는 화면