Lucian Log
Blog
Computer Science
Algorithm
DB
Network
OS
General
AI
Blockchain
Concurrency
ETC
Git
Infrastructure
AWS
Docker
Java-Ecosystem
JPA
Java
Spring
JavaScript-Ecosystem
JavaScript
Next.js
React
TypeScript
Python-Ecosystem
Django
FastAPI
Python
SQLAlchemy
Software Engineering
Architecture
Culture
Test
Home
Contact
Copyright © 2024 |
Yankos
Home
>
JavaScript-Ecosystem
> React
Now Loading ...
React
React - Advanced tips
Programming patterns 리액트는 자주 사용되는 프로그래밍 패턴이 존재합니다. Scene 1 - Stateful components to stateless components Stateful component가 자신의 state setter 함수를 props로 child component에 전달하면, child component의 어떠한 event에 의해 해당 함수가 호출되어 parent component의 state를 변경합니다. 그리고 parent component는 변경된 state를 props로 또 다른 child component(=sibling component)에게 전달해 해당 child component에서 화면에 표시합니다. Scene 2 - Separating container components from presentational components State를 가지거나 calculation 등의 functional part를 담당하는 component는 container component로, 렌더링을 담당하는 component는 presentational component로 분리해야 합니다. 분리된 presentational component는 항상 container component에 의해서 렌더링되어야 합니다. Style Name Syntax 일반적인 JavaScript에서 style의 name은 hyphenated-lowercase로 이루어져 있습니다. const styles = { 'margin-top': '20px', 'background-color': 'green' }; 반면에, 리액트는 style name이 camelCase로 이루어져 있습니다. const styles = { marginTop: '20px', backgroundColor: 'green' }; Style Value Syntax 일반적인 JavaScript에서는 "450px", "20%" 처럼 숫자와 단위를 함께 적어 string 형태로 style value를 사용해야 합니다. 하지만, 리액트에서는 px에 한해서 생략이 가능하고, 이 경우 숫자도 string이 아닌 number 그대로 사용하는 것이 가능합니다. 물론 기존의 string 형태도 그대로 사용 가능합니다. { fontSize: 30 } 다만, 다른 단위를 사용하고 싶을 때는 기존의 string 형태로 사용합니다. { fontSize: "2em" } propTypes propTypes는 리액트에서 자주 사용되는 특징입니다. Prop이 전달될 것이 예상되는 component에 올바른 prop이 전달되었는지에 대한 validation을 도와주고, documentation을 통해 component의 상황을 한눈에 파악할 수 있도록 도와줍니다. import PropTypes from 'prop-types'; propTypes를 사용하기 위해선 'prop-types' 라이브러리를 import해야 합니다. import React from 'react'; import PropTypes from 'prop-types'; export class MessageDisplayer extends React.Component { render() { return <h1>{this.props.message}</h1>; } } // This propTypes object should have // one property for each expected prop: MessageDisplayer.propTypes = { message: PropTypes.string }; 그리고 미리 정의된 component에 위와 같이 property를 추가하는 방식으로 propTypes를 정의할 수 있습니다. 이 때, propTypes의 value는 object 형태여야 함을 유의합니다. 그리고 해당 object의 각각의 property는 component에 전달될 것이 기대되는 prop의 이름으로 설정합니다. Runner.propTypes = { message: PropTypes.string.isRequired, style: PropTypes.object.isRequired, isMetric: PropTypes.bool.isRequired, miles: PropTypes.number.isRequired, milesToKM: PropTypes.func.isRequired, races: PropTypes.array.isRequired }; PropTypes를 통해 설정할 수 있는 data type의 이름은 위와 같습니다. isRequired의 경우, prop이 잘 전달되는지 확인해서 만일 잘 전달되지 않으면 console에 warning을 띄어주는 역할을 합니다. const Example = (props) => { return <h1>{props.message}</h1>; } Example.propTypes = { message: PropTypes.string.isRequired }; 만일 function component에 propTypes를 추가하고 싶다면, 위와 같이 function component 자체의 property로 propTypes를 지정합니다. React forms import React from 'react'; import ReactDOM from 'react-dom'; export class Input extends React.Component { constructor(props) { super(props); this.state = { userInput: '' }; this.handleUserInput = this.handleUserInput.bind(this); } handleUserInput(e) { this.setState({userInput: e.target.value}); } render() { return ( <div> <input type="text" value={this.state.userInput} onChange={this.handleUserInput} /> <h1>{this.state.userInput}</h1> </div> ); } } ReactDOM.render( <Input />, document.getElementById('app') ); 일반적인 form은 유저가 input field에 계속 타이핑하더라도 submit 버튼을 누르기전까지는 서버에서 그 사실을 알지 못합니다. 즉, submit 이전까지 프론트가 알고 있는 input 정보와 서버가 알고 있는 input 정보 사이에 불일치가 존재합니다. 그러나 이러한 불일치는 웹사이트의 third part에서 해당 정보를 필요로 할 때, 프론트냐 서버냐에 따라 다른 결과를 내어 문제가 발생할 수 있습니다. 이를 해결하기 위해, 리액트 form은 모든 new character와 deletion에 대한 프론트 및 서버의 동기화를 지원하여 application의 모든 요소가 일관성 있게 동작하도록 합니다. 특히, 일반적인 <form> tag를 굳이 사용하지 않고 위 코드처럼 <input> tag만으로 이를 구현할 수 있습니다. Uncontrolled vs Controlled component Uncontrolled component란 스스로 state를 가지고 그 값을 기억하는 component를 말합니다. 반면에 controlled component는 스스로 state를 가지지 않고 다른 component에 의해 관리되어지는 component를 말합니다. 리액트에는 주로 controlled component가 많고 이러한 component는 스스로에 대한 정보를 props를 통해 얻게 됩니다. Reference Learn React - Codecademy
JavaScript-Ecosystem
· 2021-08-30
React - Hook
Functional components 지금까지 JavaScript의 클래스를 사용해서 정의한 리액트의 component들은 함수를 사용해서 정의할 수도 있습니다. 이를 function component라고 합니다. Function component는 간단하고 직관적이라는 장점이 있습니다. // A component class written in the usual way: class MyComponentClass extends React.Component { render() { return <h1>Hello world</h1>; } } // The same component class, written as a stateless functional component: const MyComponentClass = () => { return <h1>Hello world</h1>; } // Works the same either way: ReactDOM.render( <MyComponentClass />, document.getElementById('app') ); Function component는 위와 같이 함수 형태로 작성하며, render() 메서드를 사용하지 않고 JSX expression을 바로 리턴하는 방식으로 작성합니다. Function component는 props 역시 전달받을 수 있습니다. function WelshCorgi (props) { return ( <div> <p>{props.prompt}</p> </div> ); } ReactDOM.render( <WelshCorgi feed="High quality dog feed" />, document.getElementById('app'); ); props는 parameter로 정의해 전달받고, props.propertyName 형식으로 접근합니다. Hook Hook은 function component에서 component의 state와 이후의 렌더링 관련 side effects를 관리하도록 도와주는 함수들입니다. 클래스에서는 작동되지 않지만, function component에서 lifecycle적인 특징들도 관리할 수 있도록 도와줍니다. State hook - useState import React, { useState } from "react"; function Toggle() { const [toggle, setToggle] = useState('off'); return ( <div> <p>The toggle is {toggle}</p> <button onClick={() => setToggle("On")}>On</button> <button onClick={() => setToggle("Off")}>Off</button> </div> ); } useState는 리액트 라이브러리에서 제공하는 JavaScript 함수로, 호출 시 두 가지 value가 담긴 array를 리턴합니다. current state - the current value of this state state setter - a function that we can use to update the value of this state State에 대한 초깃값은 useState에 인자로 넣어진 값으로 설정할 수 있습니다. 초깃값이 중요하지 않은 경우, 인자를 넣지 않고 초깃값을 undefined 상태로 두어도 상관없으나 null 값이라도 넘겨주는 것이 가독성을 높이는 방법이 될 수 있습니다. useState를 사용해서 임의의 value를 인자로 state setter 함수를 호출하면, 현재 state를 새로운 state로 update할 수 있습니다. 특히 state setter 함수가 호출되면 리액트는 자동으로 해당 component를 다시 렌더링하므로 변경한 새로운 state value가 바로 반영됩니다. import React, { useState } from 'react'; export default function Counter() { const [count, setCount] = useState(0); const increment = () => setCount(prevCount => prevCount + 1); return ( <div> <p>Wow, you've clicked that button: {count} times</p> <button onClick={increment}>Click here!</button> </div> ); } 만일 기존의 state를 활용해 계산한 값으로 state를 update하고 싶다면, state setter 함수에 콜백 함수를 인자로 전달하면 됩니다. 위와 같이 기존 state count를 활용해 prevCount + 1 값으로 state를 update하고 싶다면, setCount(prevCount => prevCount + 1)처럼 콜백 함수를 state setter 함수의 인자로 넣어줍니다. 특정한 상황에서는 setCount(count +1) 같이 바로 값을 update할 수도 있지만, 콜백 함수를 사용하는 방법이 모든 상황에서 더 안전하다는 점을 유의합니다. import React, { useState } from "react"; const options = ["Bell Pepper", "Sausage", "Pepperoni", "Pineapple"]; export default function PersonalPizza() { const [selected, setSelected] = useState([]); const toggleTopping = ({target}) => { const clickedTopping = target.value; setSelected((prev) => { // check if clicked topping is already selected if (prev.includes(clickedTopping)) { // filter the clicked topping out of state return prev.filter(t => t !== clickedTopping); } else { // add the clicked topping to our state return [clickedTopping, ...prev]; } }); }; return ( <div> {options.map(option => ( <button value={option} onClick={toggleTopping} key={option}> {selected.includes(option) ? "Remove " : "Add "} {option} </button> ))} <p>Order a {selected.join(", ")} pizza</p> </div> ); } 만일 state의 값이 Array 타입인 경우, state를 update할 때 이전 state의 Array를 그대로 변경하지말고 새로운 Array로 변경 내역을 copy해서 state에 할당해야 함을 유의합니다. 위에서도 return prev.filter(t => t !== clickedTopping); 혹은 return [clickedTopping, ...prev];으로 새로운 Array를 만들어 리턴합니다. export default function Login() { const [formState, setFormState] = useState({}); const handleChange = ({ target }) => { const { name, value } = target; setFormState((prev) => ({ ...prev, [name]: value })); }; return ( <form> <input value={formState.firstName} onChange={handleChange} name="firstName" type="text" /> <input value={formState.password} onChange={handleChange} type="password" name="password" /> </form> ); } State의 타입이 Object인 경우에도 update할 state 값은 변경된 내역을 새로 copy한 Object가 되어야 합니다. 또 Object를 arrow function에서 return할 때는 {}가 겹치는 문제가 발생할 수 있기 때문에, 반환할 Object를 ()로 감싸줄 필요가 있습니다. Separate Hooks for Separate States function Subject() { const [state, setState] = useState({ currentGrade: 'B', classmates: ['Hasan', 'Sam', 'Emma'], classDetails: {topic: 'Math', teacher: 'Ms. Barry', room: 201}; exams: [{unit: 1, score: 91}, {unit: 2, score: 88}]); }); State와 같은 dynamic data를 다루기 위해서는 state 변수마다 각각 hook을 지정해 관리하는 것이 편합니다. 위와 같이 하나의 복잡한 Object를 state로 하여 하나의 hook으로 관리한다면, 복잡한 state들을 각각 copy할 때 매우 불편해집니다. function Subject() { const [currentGrade, setGrade] = useState('B'); const [classmates, setClassmates] = useState(['Hasan', 'Sam', 'Emma']); const [classDetails, setClassDetails] = useState({topic: 'Math', teacher: 'Ms. Barry', room: 201}); const [exams, setExams] = useState([{unit: 1, score: 91}, {unit: 2, score: 88}]); // ... } 따라서, 위와 같이 state 변수마다 hook을 만들어 관리한다면 훨씬 간단하고 쉽게 state를 관리할 수 있습니다. Effect hook Effect hook은 렌더링 이후의 side effects를 관리하는 함수입니다. fetch API를 통해 백엔드로부터 데이터를 받아오거나 DOM을 읽고 변화를 주는 등의 side effect를 발생시키는 작업들을 관리하며, 보통 다음 3가지 상황에서 사용합니다. Component가 DOM에 mount되어 렌더링될 때 State 혹은 props가 변화하여 component가 다시 렌더링 될 때 Component가 DOM에서 unmount되어 렌더링될 때 Effect hook - useEffect import React, { useState, useEffect } from 'react'; function PageTitle() { const [name, setName] = useState(''); useEffect(() => { document.title = `Hi, ${name}`; }); return ( <div> <p>Use the input field below to rename this page!</p> <input onChange={({target}) => setName(target.value)} value={name} type='text' /> </div> ); } useEffect는 component를 렌더링할 때마다 다른 함수를 호출하기 위해 사용합니다. 이로 인해, useEffect는 첫 번째 인자로 렌더링 후 호출할 목적의 콜백 함수를 받습니다. 그리고 이러한 콜백 함수를 effect라고도 부릅니다. 예를 들어, 위 코드에서는 () => { document.title = name; }가 effect입니다. Effect는 현재 state에도 접근할 수 있습니다. 다만 component 렌더링이 일어난 다음 DOM이 update되면 그 후 effect가 호출되므로, state도 update가 완료된 상태에서 접근하게 됩니다. Clean Up Effects 어떠한 effect들은 메모리 누수를 피하기 위하여 항상 제거하는 작업을 동반해주어야 합니다. 예를 들어, effect를 사용해 직접 DOM 내의 element에 event listener를 추가하는 경우, 원하는 작업이 끝나면 해당 event listener를 반드시 다시 제거해주어야 합니다. 그렇지 않으면 렌더링될 때마다 호출되는 effect hook의 특성으로 인해, 이후 발생하는 수많은 렌더링 상황마다 event listener가 의도치 않게 끊임없이 추가되어 메모리가 터지는 상황이 생길 수 있습니다. 따라서 다음과 같이 useEffect의 effect 내에서 event listener를 제거하는 함수를 반환하여, 추가했던 event listener를 제거해줍니다. useEffect(()=>{ document.addEventListener('keydown', handleKeyPress); return () => { document.removeEventListener('keydown', handleKeyPress); }; }) Effect가 반환하는 함수는 useEffect가 항상 clean up 함수로 간주하므로, 리액트는 effect 작업이 끝나면 자동적으로 이를 호출합니다. Dependency array Effect는 기본적으로 매 렌더링이 일어나는 상황마다 호출됩니다. 그러나 dependency array를 사용하면, effect를 원하는 때에만 호출하도록 설정할 수 있습니다. Dependency array는 useEffect의 두 번째 인자로 넣는 array를 말합니다. 만일 component가 mount되어 첫 번째 렌더링을 할 때만 effect hook을 호출하고 최종 렌더링에서 clean up하고 싶다면, 빈 array []를 useEffect()의 두 번째 인자로 넣어줍니다. 반면에, dependency array에 특정 변수를 요소로 넣는다면, 해당 변수의 값이 변할 때만 effect가 호출됩니다. useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // Only re-run the effect if the value stored by count changes Hook을 사용하는 규칙 더욱 복잡한 React 앱에서 혼란을 피하기 위해, hook은 다음과 같은 규칙을 지키며 사용합시다. Hook을 항상 top level에서만 사용합시다. 리액트는 function component 내에서 정의한 순서에 따라 hook과 함께 관리되는 data와 function들을 인식합니다. 따라서, conditions, loops, nested functions 안에서 hook을 사용하지 말아야 합니다. if (userName !== '') { useEffect(() => { localStorage.setItem('savedUserName', userName); }); } 조건문을 쓰고 싶다면 위와 같이 쓰지 말고, 다음과 같이 effect 내에서 사용해 동일한 결과를 얻을 수 있습니다. useEffect(() => { if (userName !== '') { localStorage.setItem('savedUserName', userName); } }); Hook은 react function component 내에서만 사용합시다. Function component이외에 hook을 사용할 수 있는 곳은 custom hook을 제외하고 존재하지 않습니다. Class component나 일반적인 JavaScript 함수 내에서 hook을 사용하지 맙시다. Separate Hooks for Separate States // Handle menuItems with one useEffect hook. const [menuItems, setMenuItems] = useState(null); useEffect(() => { get('/menu').then((response) => setMenuItems(response.data)); }, []); // Handle position with a separate useEffect hook. const [position, setPosition] = useState({ x: 0, y: 0 }); useEffect(() => { const handleMove = (event) => setPosition({ x: event.clientX, y: event.clientY }); window.addEventListener('mousemove', handleMove); return () => window.removeEventListener('mousemove', handleMove); }, []); Effect hook 역시 모든 로직을 한 곳에 모아두면 가독성이 떨어지고 복잡해집니다. 따라서 위와 같이 effect 마다 따로 hook을 만드는 것을 지향합니다. Reference Learn React - Codecademy
JavaScript-Ecosystem
· 2021-08-29
React - Component Lifecycle Methods
Component lifecycle methods 리액트의 수많은 component들은 각각 자신의 lifecycle을 가집니다. 보통 component의 lifecycle 다음과 같이 구성됩니다. Mounting, when the component is being initialized and put into the DOM for the first time Updating, when the component updates as a result of changed state or changed props Unmounting, when the component is being removed from the DOM 그리고 이러한 lifecycle 각각을 제어하기 위해 개발자들이 사용할 수 있는 lifecycle method들이 존재합니다. 대표적으로 constructor()와 render() 역시 lifecycle method에 해당됩니다! constructor()는 mounting phase에 첫 번째로 호출되는 메서드로, render()는 mounting과 updating phase에 자주 등장하는 메서드로 분류할 수 있습니다. componentDidMount() componentDidMount() 메서드는 mounting phase에서 마지막으로 호출되는 메서드입니다. Mounting phase 안에서 메서드들은 다음과 같은 순서로 호출됩니다. The constructor render() componentDidMount() componentDidMount()를 활용하면 1초씩 현 시각을 계속 알려주는 시계를 만들 수 있습니다. import React from 'react'; import ReactDOM from 'react-dom'; class Clock extends React.Component { constructor(props) { super(props); this.state = { date: new Date() }; } render() { return <div>{this.state.date.toLocaleTimeString()}</div>; } componentDidMount() { // Paste your code here. const oneSecond = 1000; setInterval(() => { this.setState({ date: new Date() }); }, oneSecond); } } ReactDOM.render(<Clock />, document.getElementById('app')); componentWillUnmount import React from 'react'; export class Clock extends React.Component { constructor(props) { super(props); this.state = { date: new Date() }; } render() { return <div>{this.state.date.toLocaleTimeString()}</div>; } componentDidMount() { const oneSecond = 1000; this.intervalID = setInterval(() => { this.setState({ date: new Date() }); }, oneSecond); } componentWillUnmount() { clearInterval(this.intervalID); } } componentWillUnmount 메서드는 unmounting phase에서 사용됩니다. Component가 완전히 없어지기 전에 호출되기 때문에, side-effect를 발생시키는 불필요한 비동기 함수를 종료하기 적합한 시기입니다. 위와 같이 시간을 지속적으로 업데이트하는 시계의 setInterval() 함수를 멈추려면, componentWillUnmount() 메서드에서 clearInterval()을 사용합니다. intervalID를 clearInterval()의 인자로 전달해주면 해당 setInterval() 함수를 종료시킵니다. componentDidUpdate Updating phase에서 주로 사용하는 메서드는 render(), componentDidUpdate입니다. Update는 props와 state의 변화가 일어날 때 발생하는 작업으로, update 관련한 로직은 componentDidUpdate에서 사용하는 것이 유용합니다. Reference Learn React - Codecademy this interactive diagram
JavaScript-Ecosystem
· 2021-08-28
React - Component Interacting
Component interacting React application은 몇 십에서 몇 백 개까지 component를 가질 수 있습니다. 각각의 작은 component들은 자신의 역할을 담당하면서 거대한 app을 구성하고 서로 상호작용함으로써 app을 동작시킵니다. Component 간 상호 작용 유형 Component가 다른 component를 렌더링하는 것 Component가 다른 component에게 정보를 전달하는 것 A component in a render function class WelshCorgi extends React.Component { render() { return <h1>Welsh Corgi wooooow!</h1>; } } class Dog extends React.Component { render() { return <WelshCorgi />; } } Component 클래스의 render() 메서드는 HTML-like JSX expression 뿐만 아니라 component instance 형태의 JSX expression도 리턴할 수 있습니다. import React from 'react'; import ReactDOM from 'react-dom'; class WelshCorgi extends React.Component { render() { return <h1>Welsh Corgi wooooow!</h1>; } } class ProfilePage extends React.Component { render() { return ( <div> <h1>All About Me!</h1> <p>I like Welsh Corgi!!!</p> <WelshCorgi /> </div> ); } } 이러한 속성을 활용하면 하나의 component 안에서 다른 여러 개의 component를 함께 렌더링할 수 있습니다. 위의 <WelshCorgi /> component 인스턴스는 ProfilePage component가 생성되면 그 안에서 렌더링됩니다. 따라서, ReactDOM.render()를 통해 ProfilePage 인스턴스 하나만 렌더링하면 내부에 있는 component들은 자동으로 함께 렌더링되게 됩니다. Component 안에 다른 component가 포함되어 렌더링될 수 있다는 특징은 리액트의 강력한 장점입니다! Props 부모 component가 자식 component에가 전달하는 정보가 담긴 객체를 props라고 합니다. 모든 component들은 자신의 props를 가지고 있으며, 이를 통해 부모 component로부터 전달받은 정보를 확인할 수 있습니다. 특정 component의 props를 보고 싶다면, this.props를 사용해 확인할 수 있습니다. <Greeting name="Frarthur" town="Flundon" age={2} haunted={false} myInfo={["top", "secret", "lol"]} /> Component에 prop을 추가하고 싶다면, 생성한 인스턴스에 속성으로 추가해주면 됩니다. 위의 코드는 <Greeting /> component 인스턴스에 name, town, age, haunted, myInfo 등의 props를 부여한 것입니다. 이 때 만일 string이 아닌 정보를 주고 싶다면, {}로 정보를 감싸서 속성을 부여해야 한다는 점을 유의합시다. 이렇게 추가한 속성들은 this.props.속성이름을 통해 접근할 수 있습니다. Event handler as prop 함수 역시 props로 넘길 수 있는데, 보통 event handler 함수가 이러한 특징을 활용해 prop으로 자주 전달됩니다. import React from 'react'; import ReactDOM from 'react-dom'; class Button extends React.Component { render() { return ( <button onClick={this.props.talk}> Click me! </button> ); } } class Talker extends React.Component { talk() { let speech = ''; for (let i = 0; i < 10000; i++) { speech += 'blah '; } alert(speech); } render() { return <Button talk={this.talk} />; } } ReactDOM.render( <Talker />, document.getElementById('app') ); Event handler는 render() 메서드와 비슷한 방식으로 임의의 이름의 메서드를 정의하고 필요한 component에 prop으로서 전달합니다. Naming convention of event handler Event handler를 prop으로 전달할 때, 임의로 naming해야 할 부분이 두 군데 생깁니다. 이 때, 반드시 따를 필요는 없지만 통용되는 naming convention이 존재합니다. 첫 번째는 event handler 메서드를 정의할 때인데, 이벤트의 타입에 따라 handleClick, handleHover 등으로 사용합니다. 두 번째는 prop name인데, 이벤트 타입에 따라 onClick, onHover 등으로 정의합니다. class MyClass extends React.Component { handleHover() { alert('I am an event handler.'); alert('I will listen for a "hover" event.'); } render() { return <Child onHover={this.handleHover} />; } } this.props.children 모든 component들은 props 객체 내에 children property를 가집니다. 앞서 self-closing tag로 만들었던 component들은 사실 <MyComponentClass></MyComponentClass>로 나뉘어 쓰이는 것 역시 가능합니다. 이 경우, this.props.children은 나뉘어 쓰이는 태그 사이에 존재하는 모든 것을 리턴합니다. import React from 'react'; import ReactDOM from 'react-dom'; class List extends React.Component { render() { let titleText = `Favorite ${this.props.type}`; if (this.props.children instanceof Array) { titleText += 's'; } return ( <div> <h1>{titleText}</h1> <ul>{this.props.children}</ul> </div> ); } } class App extends React.Component { render() { return ( <div> <List type='Dog'> <li>Welsh Corgi</li> <li>Dachshund</li> </List> <List type='Cat'> <li>Road cat</li> </List> </div> ); } } ReactDOM.render( <App />, document.getElementById('app') ); 위 코드의 경우, this.props.children은 각각의 List component 사이에 있는 모든 <li> element들을 리턴합니다. <List type='Dog'>의 경우 // return elements <li>Welsh Corgi</li> <li>Dachshund</li> <List type='Cat'>의 경우 // return element <li>Road cat</li> defaultProps class Example extends React.Component { render() { return <h1>{this.props.text}</h1>; } } Example.defaultProps = { text: 'Welsh Corgi' }; 만일 component 인스턴스에 요구되어지는 prop이 전달되지 않았을 때, 해당 prop은 화면에 아무것도 출력하지 않을 것입니다. 이 때, default 값을 prop에 지정해 화면에 항상 무언가가 출력될 수 있게 할 수 있습니다. 이를 위해 component 클래스의 defaultProps property를 사용합니다. defaultProps property에 원하는 props의 기본값들을 설정한 object를 지정하여 기본값 설정을 완료합니다. state 축구 경기에서 각 팀의 스코어 정보처럼 변할 수 있는 정보를 dynamic information이라고 합니다. 리액트 component는 이러한 dynamic information을 다뤄야 할 때, props와 state를 사용합니다. 그 중, state란 각각의 component가 가지고 있는 상태를 저장한 것을 뜻하며, component 내부에서 관리됩니다. 렌더링 결과물에 영향을 주는 정보를 갖고 있다는 부분에서도 props와 공통점이 있습니다. class Example extends React.Component { constructor(props) { super(props); this.state = { mood: 'decent' }; } render() { return <div></div>; } } <Example /> 초기의 state는 props와 달리 component 클래스의 constructor에서 state property를 지정해 설정합니다. 또한, 각각의 component들은 스스로의 state를 가집니다. 모든 component는 super()를 통해 항상 초기화 시켜야 합니다. 이후, state에 적절한 객체를 할당해 initial state를 설정합니다. class TodayImFeeling extends React.Component { constructor(props) { super(props); this.state = { mood: 'decent' }; } render() { return ( <h1> I'm feeling {this.state.mood}! </h1> ); } } Component 클래스 내에서 state에 접근하고 싶다면 this.state.name-of-property 형태로 접근합니다. 위의 this.state.mood는 ‘decent’ 값에 접근합니다. this.setState() Component의 현재 state를 바꾸고 싶다면, this.setState() 메서드를 사용합니다. setState()는 변경 요소가 담긴 객체를 첫 번째 인자로 받아 사용합니다. { mood: 'great', hungry: false } 현재 state의 상황이 위와 같다고 가정해봅시다. this.setState({ hungry: true }); 그리고 setState()를 사용해 hungry 상태를 변경합니다. { mood: 'great', hungry: true } 그 결과 위와 같이 hungry 상태만 true로 변경되었습니다. setState는 기본적으로 인자로 받은 객체에 담긴 요소들만 접근해 값을 변경하고 다른 원래의 요소들은 그대로 둡니다. setState()와 render() setState() 메서드에서 유의할 점은 이 메서드가 state를 변경한 후 자동적으로 .render() 메서드까지 호출한다는 부분입니다. 즉, setState()를 사용하면 state를 변경한 부분이 바로 화면에 반영됩니다. 따라서, setState()는 render() 메서드 안에서 호출되면 안됩니다. 이를 지키지 않으면 서로 끊임없이 호출하는 무한 루프에 빠지게 됩니다. Reference Learn React - Codecademy
JavaScript-Ecosystem
· 2021-08-27
React - Component
Component of React Component란 하나의 작업을 수행하는 재사용할 수 있는 작은 코드 뭉치를 의미합니다. 여기서 하나의 작업이란 대체로 HTML 코드를 렌더링하는 것을 말합니다. Necessary import Component를 사용하기 위해서는 React 객체를 import 해두어야 합니다. React 객체에는 리액트 라이브러리를 사용하기 위한 필수적인 메서드들이 담겨있습니다. JSX expression을 사용하는데도 React 객체가 반드시 필요하므로, 첫 줄은 항상 다음 코드로 시작하도록 합니다! import React from 'react'; 또한, component 사용을 위해 ReactDOM 객체도 import합니다. ReactDOM 객체는 React 객체와 마찬가지로 React와 관련된 메서드들을 가지고 있습니다. 그러나 React에는 순수하게 React만을 위한 메서드가 담겨있는 반면, ReactDOM은 React와 DOM의 상호작용을 돕는 메서드들이 담겨 있다는 차이점이 있습니다. 따라서, 다음 코드 역시 함께 사용합니다. import ReactDOM from 'react-dom'; 클래스를 활용한 Component 생성 리액트 component는 자바스크립트의 클래스 혹은 함수를 통해 생성할 수 있습니다. 여기서는 클래스 component에 초점을 맞추겠습니다. 클래스 component는 리액트 라이브러리의 Component 클래스를 상속받아서 정의합니다. 클래스를 사용하면 원하는 만큼 인스턴스로 component를 만들어 렌더링할 수 있다는 이점이 생깁니다. import React from 'react'; import ReactDOM from 'react-dom'; class MyComponentClass extends React.Component { render() { return <h1>Hello component</h1>; } } ReactDOM.render( <MyComponentClass />, document.getElementById('app') ); 위와 같이 React.Component를 상속받으면 새로운 component 클래스를 만들어 customizing할 수 있습니다. 여기서 React.Component는 React 객체의 property이며, Component는 클래스입니다. 여기서 또 하나 유의할 점은 새로 정의한 component 클래스 body에는 반드시 render() 메서드를, render() 메서드 내에는 주로 JSX expression을 반환하는 return statement를 정의해야 한다는 부분입니다. 해당 클래스에는 어떤 component를 만들 것인지 instruction을 제시해줘야 하기 때문에, 이를 위한 render() 메서드와 return statement를 필수적으로 정의합니다. 그리고 이렇게 만들어진 클래스를 활용해 component를 자유롭게 생성할 수 있습니다. 앞서 JSX element를 사용했듯이, 클래스의 이름을 사용해 <MyComponentClass /> 코드를 쓰면 component 인스턴스가 생성됩니다! 이렇게 생성한 component 인스턴스를 ReactDOM.render()에 인자로 던져주면, 해당 component를 화면에 렌더링할 수 있습니다. Component는 클래스에서 정의한 render() 메서드를 가지고 있기 때문에, ReactDOM.render()는 인자로 받은 component의 render() 메서드를 자동으로 호출하게끔 하여 JSX expression을 반환받고 화면에 렌더링합니다. Class component의 naming convention 새로 정의한 클래스 component의 이름은 첫 글자부터 대문자를 사용하는 UpperCamelCase를 따릅니다. 이것은 Java의 naming convention에서 차용되었으며, 원래의 JavaScript 클래스를 만들 때도 마찬가지의 convention을 따릅니다. UpperCamelCase를 사용하는 또 다른 이유는 리액트 자체적으로도 찾을 수 있습니다. JSX element는 HTML-like인 경우와 component인 경우로 나뉩니다. 이 때, UpperCamelCase로 쓰인 JSX element가 있다면, 해당 element가 component instance임을 쉽게 파악할 수 있습니다. ex) ShinyBrownHairOfWelshCorgi ex) <WelshCorgiLegComponent /> render() 메서드에 정의할 수 있는 것 class Random extends React.Component { render() { // First, some logic that must happen // before rendering: const n = Math.floor(Math.random() * 10 + 1); // Next, a return statement // using that logic: return <h1>The number of Welsh Corgi is {n}!</h1>; } } Component의 render() 메서드에는 항상 return statement가 와야 합니다. 다만 이에 더하여, 렌더링 직전의 간단한 계산 역시 둘 수 있는 위치입니다. class Random extends React.Component { // This should be in the render function: const n = Math.floor(Math.random() * 10 + 1); render() { return <h1>The number of Welsh Corgi is {n}!</h1>; } }; 그러나 위와 같이 render() 메서드 바깥에 변수를 정의하는 것은 syntax error를 유발하니, 메서드 안쪽에서 정의할 것을 유의해야 합니다. Event listener in a component class MyClass extends React.Component { myFunc() { alert('Stop it. Stop hovering my Welsh Corgi.'); } render() { return ( <div onHover={this.myFunc}> </div> ); } } 위와 같이 component 클래스의 메서드로 정의한 event handler 함수를 사용하여, event listener를 component에 정의할 수 있습니다. Event listener 속성에 this를 사용해 메서드를 부여하는 것으로 적용 가능합니다. Reference Learn React - Codecademy
JavaScript-Ecosystem
· 2021-08-26
React - JSX
React basic React.js는 Facebook 엔지니어들이 개발한 UI 개발 목적의 JavaScript 라이브러리입니다. 리액트의 컴포넌트 기반 개발은 Single Page Application을 비롯한 프론트 개발에 큰 변화를 이끌었으며, 근 5~6년간 자바스크립트 생태계의 가장 중요한 존재 중 하나로 자리해 왔습니다. 최근에는 더 효율적인 프론트 개발 라이브러리들이 많이 등장했지만, 리액트의 영향력은 여전히 직간접적으로 느껴집니다. JSX const h1 = <h1>Welsh Corgi!!</h1>; JSX는 리액트에 사용되기 위해 쓰여진 JavaScript의 syntax extension입니다. 보통 JavaScript 파일 속에 JavaScript 코드와 HTML 코드들이 혼용되어 쓰여진 것들로 통용되므로, JSX 코드에는 HTML같은 코드가 포함되지만 실제로 HTML은 아닙니다. 특히, JSX는 웹 브라우저가 바로 읽을 수 없습니다. 그러므로 JSX가 포함된 JavaScript 파일을 통상적으로 사용하려면, JSX compiler를 통해 일반적인 JavaScript 코드로 컴파일해야 합니다. JSX element <h1>Hello world</h1> 또한, JavaScript 파일 속에 HTML과 똑같이 생긴 위와 같은 코드들을 JSX element라고 부릅니다. JSX element는 JavaScript 코드로 간주되어, 변수에 저장되거나 함수의 인자로 입력되는 등 일반적인 모든 프로그래밍에 문제 없이 사용됩니다. const welshCorgi = <img src='images/welsh.jpg' alt='welsh corgi' width='600px' height='600px' />; JSX element에는 HTML 때와 마찬가지로 attribute 역시 적용할 수 있습니다. const welshCorgi = ( <a href="https://www.shinybrownhair.com"> <h1> Bow wow! </h1> </a> ); Nested한 형태도 기존 HTML처럼 사용할 수 있습니다. 다만, multi-line이 될 경우 ()로 감싸주어야 오류 없이 프로그래밍할 수 있음을 유의합시다. const dogs = ( <p>I am a Poodle.</p> <p>I am a Welsh Corgi. Nice to meet you!</p> ); 다만, JSX expression은 하나의 같은 element 단위가 되어야 하기 때문에, 위와 같이 두 개의 element를 한 번에 사용하는 것은 불가능합니다. 만일 위와 같이 쓰고 싶다면, 위 코드를 하나의 <div></div> 태그로 감싸서 코드가 올바르게 동작하도록 만드는 방법을 권장합니다. Rendering 렌더링(Rendering)이란 코드를 해석해서 화면에 띄우는 작업을 의미합니다. 렌더링은 보통 리액트와 관련된 메서드들을 모아둔 ReactDom 라이브러리의 ReactDOM.render() 메서드를 사용해 진행합니다. import React from 'react'; import ReactDOM from 'react-dom'; ReactDOM.render(<h1>Hello world</h1>, document.getElementById('app')); ReactDOM.render() 메서드에는 첫 번째 인자로 화면에 띄울 JSX expression을 사용합니다. 그리고 두 번째 인자로 해당 JSX expression을 띄울 container가 될 HTML 태그를 찾아 넘깁니다. <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <link rel="stylesheet" href="/styles.css"> <title>Learn ReactJS</title> </head> <body> <main id="app"></main> </body> </html> 예를 들어 위와 같은 index.html 문서가 있다면, <main id="app"></main> 태그 속에 첫 번째 인자로 넘긴 JSX expression이 위치해 화면에 렌더링됩니다. Virtual DOM const dog = <h1>Welsh Corgi</h1>; // This will add "Welsh Corgi" to the screen: ReactDOM.render(dog, document.getElementById('app')); // This won't do anything at all: ReactDOM.render(dog, document.getElementById('app')); ReactDOM.render()의 장점은 변경이 있는 DOM elements만 update한다는 점입니다. 수많은 DOM elements가 있을 때, 변경된 것들만 update하는 것은 React의 큰 이점입니다. React는 virtual DOM을 통해 이를 실현합니다. Virtual DOM이란 리액트에서 실제 DOM object와 대응되는 가벼운 카피 버전의 가상 DOM object를 말합니다. Virtual DOM은 실제 DOM과 같은 property들을 가지지만, DOM의 변화를 화면에 직접 띄우는 기능은 없기 때문에, 일반 DOM 조작보다 빠르다는 장점이 있습니다. 따라서, 리액트는 다음과 같은 방식으로 DOM을 update합니다. 전체 virtual DOM을 업데이트합니다. Update한 virtual DOM과 이전 virtual DOM의 snapshot을 비교하여 변화된 부분들을 확인합니다. 변화된 부분만 실제 DOM object에서 update합니다. 실제 DOM의 변화가 화면에 반영됩니다. DOM manipulation의 단점 과거 일반적인 자바스크립트 라이브러리들은 DOM manipulation을 할 때, DOM element 하나가 변경되면 모든 element들을 다시 update해야 해서 비효율적이었습니다. 덕분에 DOM이 커질수록 cost가 더욱 늘어났는데, 리액트의 virtual DOM 도입은 cost 문제를 혁신적으로 해결했습니다. 변경된 특정 DOM element만 update하는 virtual DOM의 특징이 DOM manipulation 속도를 혁신적으로 향상 됐습니다. Advanced syntax of JSX JSX의 문법은 대게 HTML과 동일하지만 미묘하게 다른 부분들이 존재하므로 유의해야 합니다. className <h1 className="dog">Welsh Corgi</h1> HTML에서 사용되는 class 속성은 JSX에서 className으로 사용합니다. 이는 JavaScript가 class를 예약어로 갖고 있어서 JSX를 JavaScript로 변압할 때 키워드가 겹치는 문제가 발생하기 때문입니다. 대신 className은 JSX가 렌더링될 때, class 속성으로서 자동으로 인식됩니다. self-closing tag Fine in HTML with a slash: <br /> Also fine, without the slash: <br> HTML에서는 <img> 태그나 <input> 태그 같은 요소들의 끝 부분 > 앞에 /를 쓰는 것이 선택적입니다. Fine in JSX: <br /> NOT FINE AT ALL in JSX: <br> 하지만, JSX에서는 self-closing tag에 /를 반드시 써줘야 합니다. (그렇지 않으면, 에러가 발생합니다.) JavaScript in JSX in JavaScript import React from 'react'; import ReactDOM from 'react-dom'; ReactDOM.render( <h1>{2 + 3}</h1>, document.getElementById('app') ); // Output on monitor: 5 JSX expression 안에 일반적인 JavaScript 코드를 사용하고 싶다면, {}를 사용합니다. {} 안에 위치한 코드들은 JSX expression 안쪽이라도 JavaScript 코드로 인식됩니다. 여기서 {}는 JSX나 JavaScript가 아니라, JavaScript injection into JSX의 시작과 끝을 나타내는 marker입니다. Event Listener function myFunc() { alert('Welsh Corgi!!!!'); } <img onClick={myFunc} /> JSX에서도 HTML과 같이 event listener를 사용할 수 있습니다. on을 접두어로 하는 속성들을 사용하면 event listener를 적용할 수 있는데, 해당 속성들의 값은 반드시 함수가 되어야 합니다. 또한, HTML에서 event listener의 이름들은 모두 소문자로 쓰이지만, JSX에서는 camelCase로 사용해야 합니다. Conditional statement JSX에는 if 구문을 삽입할 수 없습니다. 하지만, 이를 해결할 몇 가지 방법도 존재합니다. const sound = 'Bow wow!'; if (sound === 'Bow wow!') { message = ( <h1> Hey, good dog! </h1> ); } else { message = ( <h1> I like a lot of animal! </h1> ); } 먼저, JSX 바깥에서 if를 사용해 원하는 조건문을 만들 수 있습니다. const sound = 'Bow wow!'; const message = ( <h1> { sound === 'Bow wow!' ? 'Hey, good dog!' : 'I like a lot of animal!' } </h1> ); 혹은 삼항연산자(ternary operator)를 사용하면 JSX 내부에서도 조건문을 사용할 수 있습니다. React에서는 상당히 자주 사용되는 방법입니다. const tasty = ( <ul> <li>Dog feed</li> { !puppy && <li>Dog gum</li> } { age > 1 && <li>bone</li> } { age > 5 && <li>Dog ade</li> } { age > 7 && <li>Dog cookie</li> } </ul> ); 만일 어떤 조건에서만 action을 취하고 다른 때는 아무 것도 하지 않는 경우라면, && 연산자를 활용하는 것도 적합합니다. 즉, && 연산자의 왼쪽 expression이 true일 경우에만, && 연산자의 오른쪽 expression이 렌더링될 것입니다. 이러한 형태의 조건문도 React에서 자주 쓰이는 방식입니다. map() const dogs = ['Welsh Corgi', 'Poodle', 'Dachshund']; const listDogs = dogs.map(dog => <li>{dog}</li>); <ul>{listDogs}</ul> 만일 JSX element의 array를 만들고 싶다면, .map()을 사용하는 것이 유용합니다. React에서 자주 사용되는 방식이므로 기억해두면 좋습니다. // This is fine in JSX, not in an explicit array: <ul> <li>dog 1</li> <li>dog 2</li> <li>dog 3</li> </ul> // This is also fine! const liArray = [ <li>dog 1</li>, <li>dog 2</li>, <li>dog 3</li> ]; <ul>{liArray}</ul> 또한, <li> JSX element들이 담긴 array는 위의 {liArray} 같이 곧바로 <ul>과 함께 사용하는 것이 가능합니다. key 속성 <ul> <li key="li-01">Dog 1</li> <li key="li-02">Dog 2</li> <li key="li-03">Dog 3</li> </ul> <li> 태그들은 때때로 key 속성을 필요로 할 때가 있습니다. 특정 상황에서 key를 설정해두지 않으면 잘못된 순서로 list-item들이 나타날 수 있으므로, 다음과 같은 상황에서는 key 속성을 설정합니다. 각각의 list-item이 memory를 가질 경우 (to-do list와 같이 항목의 체크 여부를 기억해야 할 때) list-item이 섞일 가능성이 있을 때 key 속성을 설정할 때, key 속성의 값은 unique해야 합니다. React.createElement() React 코드를 JSX expression을 쓰지 않고도 사용할 수 있는 방법이 있습니다. const h1 = <h1>Welsh Corgi</h1>; 위의 JSX expression으로 표현하던 기존의 코드는 다음과 같이 새로 쓰일 수 있습니다. const h1 = React.createElement( "h1", null, "Welsh Corgi" ); React.createElement()을 사용하면 JSX expression을 쓰지 않고도 같은 기능을 하는 React 코드를 만들 수 있습니다. 사실 JSX element가 컴파일 될 때, 컴파일러는 내부적으로 해당 JSX element를 React.createElement() 메서드로 변형하여 호출합니다. 즉, JSX expression을 사용하기 전에는 항상 import React from 'react';로 React 객체를 import해야 하는데, 그 이유는 내부적으로 항상 React.createElement() 메서드가 사용 가능해야 하기 때문입니다. Reference Learn React - Codecademy Event Listener List - React.js
JavaScript-Ecosystem
· 2021-08-25
<
>
Touch background to close