여기의 섹션 컴포넌트는
제가 이 프로젝트에 추가한 첫 번째 컴포넌트입니다
이 컴포넌트는 속성을 전달하는 패턴을 보여줍니다
여기에 추가로 보여드릴 또 하나의 컴포넌트는
특수 탭 컴포넌트입니다
이 컴포넌트가 이쪽 탭 영역을 모두 제어할 것입니다
여기에 있는 탭 시스템을 보면
각 탭은 세 가지 요소의 묶음인 것을 볼 수 있습니다
이것은 메뉴 바, 탭 버튼
그리고 메뉴 바 아래의 탭 내용입니다
그 내용을 포함할 수 있는 것은
이것처럼 div 혹은
다른 HTML 요소가 되겠습니다
물론 이것처럼 그냥 메뉴 바 아래에 있을 수도 있습니다
이 경우에도 오류가 있는 구조는 전혀 아닙니다
특히 저희의 기본적인 앱에 있어서는 충분한 구조입니다
하지만 더 규모가 큰 앱이라면
해당 앱의 다양한 위치에서
이런 형태의 탭이 사용될 수도 있습니다
그러므로 재사용이 가능한 탭 컴포넌트를 사용하는 것이 좋습니다
새로운 파일을 생성하겠습니다
이름은 임의로 tabs.jsx로 하겠습니다
먼저 tabs라는 함수를 내보내겠습니다
그리고 프래그먼트(fragment)를 하나 반환하겠습니다
이 안에는 내장되어 있는 메뉴(menu) 요소가 있어서
버튼을 모두 감싸는 역할을 합니다
그 아래에는 실제 내용이 들어갑니다
각 탭 버튼을 누를 때마다
그에 맞는 다른 내용을 보여줄 것입니다
즉 이것은 또다른 래퍼 컴포넌트(wrapper component)입니다
이 컴포넌트는 특정 JSX를 시행하여
HTML 구조까지 시행하게 됩니다
여기에서도 속성 구조 분해 할당을 사용하여
자식 속성을 가져올 수 있습니다
이 특수 자식 속성을 통해
버튼을 눌렀을 때 보이는
내용을 설정할 수 있습니다
이제 Examples.jsx로 돌아와서
이 Tabs 컴포넌트를 사용해 보겠습니다
그 전에 이 import문을 작성해야 하는데
탭 파일(tabs.jsx)에서 탭을 가져오기 위해서입니다
이제 tabContent를 가지고 오겠습니다
이 Examples 컴포넌트에서 제어되고 있는 내용이고
또 이 컴포넌트에 두는 것이 좋습니다
Tabs 컴포넌트에 두지 않는 이유는
이 컴포넌트의 목적이
모든 종류의 탭에 적용되어
앱의 다양한 위치에 있는 내용을 제어하는 것이기 때문입니다
그러므로 그 내용을 여기에서 제어하기보다는
Tabs 컴포넌트가 사용되고 있는
그 위치에서 제어하는 것이 좋습니다
Examples 컴포넌트로 다시 넘어와서
이 동적인 tabContent를 잘라낸 후에
여기 Tabs 사이에 붙여넣겠습니다
이제는 여기 있는 TabButton 모두
탭 컴포넌트로 옮겨보겠습니다
물론 모두 선택한 후 잘라내어
이 쪽 메뉴에 붙여넣을 수도 있지만
문제는 이 클릭에 대한 내용에 있습니다
이 TabButton 내에 있는 클릭을
현재 사용 중이고 현재 선택되어 있는 컴포넌트에서 제어해야 하는데
이것은 컴포넌트 데이터에 따라 결정되며
여기에서 모든 탭과 관련된 상태를 제어합니다
하지만 그 상태는 이 탭 컴포넌트에
포함되어 있지 않은데, 그 이유는
이 래퍼 컴포넌트(wrapper component)는 아주 단순한 형태라
이 안에서 제어되는 내용에 대한 지식이 없기 때문입니다
그래서 TabButton을 탭 컴포넌트로 옮기는 작업이
예상보다 복잡해지는 것입니다
여기에 추가로 받을 속성을 몇 개 작성하겠습니다
먼저 selectedTopic 속성을 받고
또 onSelectTab 속성도 받을 수 있습니다
이 속성은 탭 버튼을 클릭할 때마다 활성화됩니다
하지만 속성을 추가하면 일어나는 일은
단순히 코드가 더 복잡해지는 것 이상으로
Tabs 컴포넌트의 재사용을 더욱 어렵게 만듭니다
그 이유는 이제 이 컴포넌트를
selectedTopic이 적용되지 않는 곳에는
전혀 사용할 수 없기 때문입니다
즉 이것은 적절한 해결방법이 아닙니다
여기에 속성을 잔뜩 추가하지 않고도
버튼을 작동시킬 수 있습니다
더 나은 해결방법은
이 버튼을 모두 그대로
Examples.jsx에 두는 대신 JSX 코드로
Tabs 요소에 전달하는 것입니다
이 동적인 tabContent를 전달하는 것과 같은 원리입니다
유일한 문제점이 하나 있습니다
하나뿐인 특수 자식 속성을
내용을 위해 이미 사용 중이라는 것입니다
여기서 속성을 추가해서 버튼을 가져오겠습니다
이 두 메뉴 태그 사이에 작성하겠습니다
즉 JSX 코드를 위한 자리를 남겨두어야
탭 컴포넌트에서 해당 작업이 가능하다는 뜻입니다
그러므로 여기에 속성을 한 추가하겠습니다
이름은 임의로 'buttons' 으로 하겠습니다
그리고 그 버튼을 여기에서 내보내겠습니다
이제 Examples.jsx에서
탭 컴포넌트를 사용하는 곳에
이 버튼 속성을 추가할 수 있습니다만
문자열이나 숫자에 설정하는 것이 아니라
특정 JSX 코드에 설정하는 것입니다
리액트에서는 이 작업이 가능합니다
이미 언급한 적이 있듯이, JSX 코드 혹은
JSX 요소는 정규값으로서
코드에 여느 값처럼 사용되기 때문입니다
이 TabButton을 모두 복사해 오겠습니다
잘라낸 후에 버튼의 값으로서 여기 붙여넣겠습니다
문제가 하나 있는데, 여기에서
다수의 형제 요소가 한데 모여
하나의 값으로 사용되고 있는데, 이것은 JSX에서 허용되지 않습니다
이전에 배웠듯이, JSX 코드를 하나의 값으로서 사용할 때는
그 위치가 컴포넌트 속에
값이 반환되는 자리이거나
변수에 값이 저장되는 자리일지라도
오직 하나의 루트(root) 요소만이
그 위치에 허용됩니다
그러므로 여기에 div를 추가해서
이 탭 버튼을 감싸는 용도로 사용하겠습니다
아니면 div를 사용하지 않고
이 특수 프래그먼트(fragment)를 사용할 수도 있다고 배웠죠
이제 이 버튼들을 특수 속성으로 설정하겠습니다
코드가 덜 완성된 것처럼 보일 수 있지만
이것은 꽤나 자주 쓰이는 패턴입니다
이 패턴을 사용함으로써
이제 두 개의 슬롯(slot)을 설정하여
탭 컴포넌트, 즉 메인 내용 슬롯을
자식 요소를 통해 설정할 수 있습니다
이 버튼 슬롯은 또한
커스텀 버튼 속성을 통해 설정할 수 있습니다
이후에 속성을 더 추가하거나
컴포넌트 내 필요한 만큼 슬롯을 더 설정할 수도 있습니다
이것은 JSX 내용이 더 늘어났을 경우에 해당합니다
지금은 버튼과 자식 요소만 있으면 되고
버튼을 메뉴 요소로 감싸기만 하면 됩니다
이제 탭을 감싸는 코드(tabs wrapper)가 훨씬 간결해졌습니다
여기에서 이 구조가 시행되는데
버튼이 메뉴 요소 안에 있고
메뉴 아래에는 자식 요소가 있습니다
이제는 이 탭을 가져와서
섹션에 포함된 Examples 컴포넌트에서 사용할 수 있습니다
이제는 Tabs 아래의 메뉴 요소를 삭제하여
구조를 더 깔끔하게 수정할 수 있고
이 재사용 가능한 탭 컴포넌트에서
이 구조를 시행할 수 있습니다
앱을 재실행해 보겠습니다
이전과 동일하게 모두 잘 작동합니다
이 패턴이 불필요해 보일 수 있지만
이 앱과 달리 복잡한 작업에는 꼭 필요한 내용입니다
그리고 컴포넌트 내 다양한 슬롯 설정은
리액트에서 또한 아주 중요한 개념이므로
리액트 개발 시에 꼭 알아야 하는 내용입니다