컴포넌트 단위 개발의 이해

1. 컴포넌트 기반 개발의 이해

1.1 컴포넌트 기반 개발의 중요성 및 이점

컴포넌트 기반 개발은 현대 프론트엔드에서 중요한 개념임. UI를 작은 단위로 나누어 관리하는 것이 핵심.

  1. 재사용성: 한 번 만든 컴포넌트를 여러 곳에서 재사용 가능. 같은 코드를 반복해서 작성할 필요 없게 됨.

  2. 유지보수성: 각 컴포넌트는 독립적으로 관리되므로, 특정 기능만 수정해도 다른 부분에 영향을 주지 않음.
    덕분에 버그 수정이나 기능 추가가 훨씬 수월해짐.

  3. 모듈화: 작은 단위로 나눠 코드 가독성이 높아지고, 각 컴포넌트의 역할이 명확해짐. 대규모 프로젝트에서도 효율적으로 관리 가능.

1. 2 React, Vue, Svelte가 채택한 컴포넌트 기반 개발 방식

현대 프레임워크들은 모두 컴포넌트를 중심으로 개발이 이뤄짐.

React, Vue, Svelte는 각기 다른 방식으로 컴포넌트를 처리하지만, 기본 원리는 동일.

React

React는 페이스북이 2013년에 발표한 라이브러리임. Virtual DOM과 JSX를 사용해 UI를 관리.

상태 변화에 따라 컴포넌트가 다시 렌더링됨.


function Button({ label, onClick }) {
  return <button onClick={onClick}>{label}</button>;
}
Vue

Vue는 2014년에 에반 유(Evan You)가 만든 프레임워크

템플릿 기반 UI 정의가 특징.

하나의 .vue 파일에 HTML 템플릿, 로직, 스타일을 모두 작성.

React보다 직관적이고 간단함.


<template>
  <button @click="handleClick">{{ label }}</button>
</template>

<script>
export default {
  props: ['label'],
  methods: {
    handleClick() {
      alert('Clicked!')
    }
  }
}
</script>
Svelte

Svelte는 2016년에 리치 해리스(Rich Harris)가 발표한 프레임워크.

다른 프레임워크와 달리 Virtual DOM을 사용하지 않고, 빌드 타임에 UI 업데이트를 컴파일하여 성능을 최적화.

즉, 상태 변경 시 직접 DOM을 업데이트함.


<script>
  let count = 0;
  function increment() {
    count += 1;
  }
</script>

<button on:click={increment}>Clicked {count} times</button>

Svelte는 컴파일러 기반이기 때문에 런타임 오버헤드가 적고, 더 빠른 성능을 제공함.

1.3 UI와 비즈니스 로직의 분리 원칙

컴포넌트 기반 개발의 중요한 원칙 중 하나는 **UI와 비즈니스 로직을 분리하는 것.

UI는 화면에 보이는 부분, 로직은 데이터 처리나 상태 관리 등을 담당함.

  • UI는 사용자에게 보여지는 부분으로, 컴포넌트 단위로 나누어 관리됨. 이렇게 하면 코드가 더 직관적이고 유지보수가 쉬워짐.

  • 비즈니스 로직은 컴포넌트의 상태나 동작을 관리하는 역할을 함. 이를 독립된 함수나 모듈로 분리해, UI와 로직이 서로 독립적으로 동작하게 만듦.

React와 같은 프레임워크에서는 상태 관리와 UI를 쉽게 분리할 수 있음.

예를 들어, 상태 관리 로직과 UI 렌더링 로직을 각각 나누어 코드를 관리할 수 있음.


// 상태 관리 로직 분리
function useCounter() {
  const [count, setCount] = useState(0);
  const increment = () => setCount(count + 1);
  return { count, increment };
}

// UI 로직 분리
function Counter() {
  const { count, increment } = useCounter();
  return (
    <div>
      <p>{count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}
	

Svelte도 비슷하게 로직을 컴포넌트와 분리해 관리할 수 있음. 데이터와 UI를 간결하게 처리할 수 있으며,

상태 변화에 따라 빠르게 DOM을 업데이트할 수 있음.


<script>
  let count = 0;
  function increment() {
    count += 1;
  }
</script>

<button on:click={increment}>Clicked {count} times</button>

이처럼 UI와 로직을 분리하면, 코드의 가독성과 유지보수성이 높아지고, 확장 가능성이 커짐.


컴포넌트 계층 구조와 연결 방식

컴포넌트 기반 개발에서는 컴포넌트 간의 계층 구조와 데이터 흐름이 중요한 역할을 함. 상위 컴포넌트는 상태를 관리하고, 하위 컴포넌트에 데이터를 전달하여 화면을 구성함. 이때, 각 컴포넌트는 독립적으로 동작할 수 있으며, 재사용 가능하게 설계됨.

1. 컴포넌트 계층 구조

컴포넌트는 상위(Parent)와 하위(Child) 관계로 구성되며, 트리 구조로 구성된다고 볼 수 있음. 상위 컴포넌트는 하위 컴포넌트를 포함하여 데이터를 주고받음.

예시 계층 구조:

  • App 컴포넌트 (상위 컴포넌트)

    • Header 컴포넌트 (하위 컴포넌트)

    • Main 컴포넌트

      • Sidebar 컴포넌트

      • Content 컴포넌트

2. 컴포넌트 간의 연결 방식

React 같은 라이브러리에서는 단방향 데이터 흐름이 일반적임. 즉, 상위 컴포넌트가 데이터를 하위 컴포넌트에 전달하고, 하위 컴포넌트는 이벤트를 발생시켜 상위 컴포넌트의 상태를 변경함.

  • 상위 컴포넌트가 상태 관리: 상위 컴포넌트는 state와 같은 데이터를 관리하며, 이 데이터를 하위 컴포넌트에 props로 전달함.

  • 하위 컴포넌트에서 이벤트 처리: 하위 컴포넌트는 상위에서 받은 데이터를 기반으로 UI를 렌더링하고, 이벤트가 발생하면 상위 컴포넌트로 콜백 함수를 호출하여 상태를 변경함.

이러한 단방향 흐름은 데이터의 흐름을 명확하게 하고, 디버깅을 쉽게 만들어줌.


3. **바닐라 JS로 컴포넌트 구조를 만든다면? **

React와 같은 라이브러리를 사용하지 않고, 바닐라 JavaScript에서 컴포넌트 계층 구조와 상호작용을 구현할 수도 있음. 이때, 각 컴포넌트를 함수로 만들어 관리하고, DOM 조작을 통해 상태를 관리하고 UI를 업데이트함.

함수형 컴포넌트 구현 예시:

아래와 같이 부모와 자식을 계층적으로 구조화 시킬수 있다.

<div id="app"></div>

<script>
  // 하위 컴포넌트 정의 (ChildComponent)
  function ChildComponent(count, onIncrement) {
    const container = document.createElement("div");
    const countDisplay = document.createElement("p");
    const button = document.createElement("button");

    countDisplay.textContent = count;
    button.textContent = "Increment";

    button.addEventListener("click", onIncrement);

    container.appendChild(countDisplay);
    container.appendChild(button);

    return container;
  }

  // 상위 컴포넌트 정의 (ParentComponent)
  function ParentComponent() {
    let count = 0;

    function increment() {
      count += 1;
      update();
    }

    function update() {
      const app = document.getElementById("app");
      app.innerHTML = ""; // 기존 DOM 초기화

      // ChildComponent 호출하여 count와 increment 전달
      const child = ChildComponent(count, increment);
      app.appendChild(child);
    }

    // 초기 렌더링
    update();
  }

  // ParentComponent 렌더링
  ParentComponent();
</script>

요약

  • 컴포넌트 계층 구조는 상위-하위 관계로 구성되며, 상위 컴포넌트는 상태를 관리하고 하위 컴포넌트에 데이터를 전달함.

  • 바닐라 JavaScript에서도 함수형 컴포넌트 방식을 사용해, 상위-하위 컴포넌트 간 상호작용을 구현할 수 있음. 이를 통해 React와 유사한 데이터 흐름과 상태 관리를 흉내낼 수 있음.