create-react-app
을 이용해서 앞서 배웠던 여러가지 webpack 설정과 의존성 관리가 끝나있는 프로젝트를 만들 수 있었다. npx create-react-app [app 이름]
명령 한 줄로 간단하게 만들 수 있는 점이 인상깊다.
최근의 React에서는 함수 형태의 컴포넌트 사용이 권장된다고 한다. 하나의 함수가 컴포넌트 역할을 하며, return
값으로 html
과 유사한 형태의 JSX
를 반환한다. JSX
를 사용해서 App을 구성할 때 주의할 점은 최상위 노드가 반드시 하나로 구성해야하는 것이다. 이렇게 하는 이유는 virtual DOM에서 변화를 효율적으로 감지하기 위해서라고 한다.
최상위 노드를 하나로 만들기 위해서<div>
태그 등을 이용해서 감싸는 것이 렌더링 된 html
구조를 복잡하게 한다고 생각 할 수 있다. 이 경우 이름이 없는 태그(<>
</>
)로 감싸 렌더링 되지않는 최상위 노드를 만들 수 있다. 이름 없는 태그는 <React.Fragment>
의 단축 문법이다.
JSX 안에 {}
를 추가하여 표현식을 넣을 수 있다. 표현식은 text node, node의 attribute 등에 위치할 수 있다.
Vue의 v-if
같은 특별한 구문 없이 앞서 말한 표현식을 이용한다. 표현식 안에 논리곱연산자 또는 삼항연산자를 사용할 수 있다.
예시
const isVisible = false; return ( <div> { isVisible && <p> Optional Rendering </p> } </div> )
표현식 안에 map()
, filter()
같은 Array method를 사용하여 리스트 데이터를 반복되는 요소로 만들 수 있다. 이 때 렌더링 효율성을 위해서 key
attribute를 설정하는 것이 권장된다. key
값은 id와 같이 중복되지 않는 값으로 설정한다.
예시
// map을 이용해서 JSX로 이루어진 Array를 반환 const listData = ['a', 'b', 'c', 'd']; return ( <ul> { listData.map(data => <li>{data}</li>) } </ul> )
<!-- 다음과 같이 렌더링 된다. --> <ul> <li>a</li> <li>b</li> <li>c</li> <li>d</li> </ul>
Vue와 마찬가지로 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전송할 때 props
를 사용할 수 있다.
예시
// 상위 컴포넌트 function App() { return ( <div> <Component name="kiko" /> </div> ) } // 하위 컴포넌트 function Component(props) { return ( <div>Hello! my name is {props.name}</div> ) }
위 처럼 상위 컴포넌트는 하위컴포넌트의 attribute로 값을 전달하며, 하위 컴포넌트는 모든 값들을 하나의 props
객체로 입력받는다.
특수한 props
로 children
값이 있는데, 이 값은 컴포넌트의 자식 노드를 값으로 전달받는다. Vue의 slot
과 비슷한 역할을 하는 것으로 이해했다.
예시
// 상위 컴포넌트 function App() { return ( <div> <h1>This is App</h1> <Component name="kiko"> <h2>This is Children</h2> </Component> </div> ) } // 하위 컴포넌트 function Component(props) { return ( <div> <h1>Hello! my name is {props.name}</h1> {props.children} </div> ) }
<!-- 이렇게 렌더링 된다. --> <div> <h1>This is App</h1> <div> <h1>Hello! my name is kiko</h1> <h2>This is Children</h2> </div> </div>
propType
를 사용하여 props
의 type과 필수 여부를 지정해 줄 수 있다.
예시
import propType from 'prop-types' function Component(props) { return ( <div> <h1>Hello! my name is {props.name}</h1> {props.children} </div> ) } // props 객체 안의 name은 string type이어야 하며 반드시 입력되야 한다. // 그렇지 않을 시에 error가 발생한다. Component.propType = { name: propType.string.isRequired }
defaultProps
또는 구조분해할당을 사용해서 props
의 기본값을 지정할 수 있다.
예시
function Component(props) { return ( <div> <h1>Hello! my name is {props.name}</h1> {props.children} </div> ) } Component.defaultProps = { name: 'unknown' }
function Component({name = 'unknown'}) { return ( <div> <h1>Hello! my name is {props.name}</h1> {props.children} </div> ) }
반응형 데이터를 만들고 관리하기 위해 사용하는 함수이다. useState(defaultValue)
를 사용하면 반응형 데이터와 해당 데이터의 값을 변경시 사용하는 setter 함수를 배열에 담아 반환한다. setter 함수를 사용해서 값을 변경할 시 컴포넌트가 리렌더링된다.
예시
// 주로 구조분해할당을 이용한다. const defaultValue = 0 const [value, setValue] = useState(defaultValue) console.log(value) // 0 setValue(12) console.log(value) // 12
useEffect
는 컴포넌트가 렌더링을 수행한 뒤에 필요한 side effect를 실행하기 위한 hook이다.
useEffect(sideEffectFunction, [])
위와 같은 형태로 사용할 수 있으며 첫번째 인자로 입력한 함수가 렌더링이 될 때 실행되고 두번째 인자에 따라서 추가적인 실행을 할 수 있다. 두번째 인자인 배열에 useState
를 통해서 얻은 데이터 값을 넣으면, 해당하는 값의 변경이 생길때마다 반복해서 실행한다. 첫번째 인자인 함수에서 함수를 반환하면, 반환한 함수는 컴포넌트가unmount
될 때 실행하며 가이드에서는 이 동작을 clean-up
이라고 설명한다.
예시
import { useEffect, useState } from 'react' function Component() { const [state, setState] = useState(0) useEffect(() => { console.log('rendering') // 컴포넌트의 렌더링이 완료된 뒤 실행 return () => { console.log('unmount') // 컴포넌트가 제거(unmount)될 때 실행 } }, []) useEffect(() => { console.log('state change') // state 값이 변경될 때마다 실행 }, [state]) }
useRef
는 하나의 컴포넌트가 반환하는 JSX안의 자식요소에 접근하기 위해서 주로 사용한다. 또한 setState와 다르게 값이 변경되어도 리렌더링이 되지 않기 때문에 지역변수로도 사용이 가능하다. 이 부분에 대해서는 아직 이해가 더 필요하다.
이전에 들었던 Vanilla JS에서의 컴포넌트 구조와 아주 유사하다는 느낌을 받았다. 그래서 그런지 Vue보다 조금 더 익숙하고 어렵지 않게 컴포넌트를 구성할 수 있을 것 같은 자신감도 약간 느껴진다. React는 심화 과정도 있는 만큼 지금 확실하게 정리하는 것이 좋아보인다.