라이브러리 | 프레임워크 |
---|---|
개발 편의를 위한 도구의 모음 | 기반 구조까지 잡혀있음 |
공구 | 공장 |
React | Vue.js, Angular |
생태계가 풍부하다!
즉, 구글링 하기 좋다. 해당 기술에 대한 관심도 / 실제 사용 빈도 / 사용자 수가 많다.
Document Object Model
🤔 CodeSandBox (https://codesandbox.io/)
프론트엔드 코드를 작성하고 이것저것 시도해볼 수 있는 모래상자이다.
리액트 등 다양한 환경에 대한 기본 설정이 되어있다.
Static 환경에서 element를 생성하는 코드를 작성했다.
이제 위에서 작성한 코드를 CDN을 통해 리액트를 사용해서 바꾸어보자.
React-dom의 render
함수는 appendChild
와 같은 역할을 한다.
🤔 CDN (https://ko.reactjs.org/docs/cdn-links.html)
웹에서 사용되는 다양한 컨텐츠(리소스)를 저장하여 제공하는 시스템이다.
JSX와 Babel을 사용해서 위에서 작성한 element를 생성하는 코드를 더 단순하게 바꿀 수 있다.
🤔 spread(
...
)
객체(props
)에 있는 요소를 각각 분해해서 보여주는 것이다.<div id="root"></div> <script type="text/babel"> const rootElement = document.getElementById("root"); const text = "Hello, world!"; const titleClassName = "title123" const props = { className: titleClassName, children: text }; const customH1 = <h1 {...props} />; const element = customH1; ReactDOM.render(element, rootElement); </script>
위의 코드에서 다음 두 코드는 같은 표현이다. ...
을 사용해서 더 간단하게 표현했다.
const customH1 = ( { <h1 className={props.className} children={props.children} /> );
const customH1 = <h1 {...props} />;
지금까지 root
Element에 하나의 Element 밖에 넣지 못했다. 어떻게 다중 Element를 넣을 수 있을까?
root
Element에 투입될 부모 Element를 하나 만들고 그곳에 여러 Element를 넣었다.React.Fragment
를 사용하자!🤔
React.Frament
/<></>
자식 요소들을 묶어주는 역할만 하고, 실제로 그릴 때는 아무 내용도 남지 않는다.
React.Fragment
또는 <></>
를 통해서 여러 가지 Element를 주입할 수 있다.
다음 두 코드는 동일한 결과가 출력된다.
<div id="root"></div>
<script type="text/babel">
const rootElement = document.getElementById("root");
const element = (
<React.Fragment
children={[
React.createElement("h1", null, "Hi"),
React.createElement("h3", null, "Bye"),
React.createElement("h5", null, "Children")
]}
/>
);
ReactDOM.render(element, rootElement);
</script>
<div id="root"></div>
<script type="text/babel">
const rootElement = document.getElementById("root");
const element = (
<>
<h1>Hi</h1>
<h3>Bye</h3>
<h5>Children</h5>
</>
);
ReactDOM.render(element, rootElement);
</script>
👉 실행 결과
🤔 Custom Element
Upper case로 작성해야 한다. 소문자로 작성하게 되면 HTML에서 이미 사용되고 있는 Element들과 충돌을 일으킬 수 있는 확률이 있고, 리액트가 인지하기 어려울 수 있다.
함수를 사용해서 여러 개의 Element를 찍어낼 수 있다.
<div id="root"></div>
<script type="text/babel">
const rootElement = document.getElementById("root");
const paint = () => (
<>
<h1>Hi</h1>
<h3>Bye</h3>
</>
);
const element = (
<>
{paint()}
{paint()}
{paint()}
</>
);
ReactDOM.render(element, rootElement);
</script>
👉 실행 결과
위의 코드가 '이게 뭐가 편하지?' 라고 생각할 수도 있는데, 이는 Element가 계속 바뀌는 상황이 왔을 때 편리하게 작용한다.
<div id="root"></div>
<script type="text/babel">
const rootElement = document.getElementById("root");
const paint = (title, description) => (
<>
<h1>{title}</h1>
<h3>{description}</h3>
</>
);
const element = (
<>
{paint("Good", "good")}
{paint("Bad", "bad")}
{paint("Soso", "soso")}
</>
);
ReactDOM.render(element, rootElement);
</script>
👉 실행 결과
다음처럼 작성할 수도 있다.
<div id="root"></div>
<script type="text/babel">
const rootElement = document.getElementById("root");
// props를 인자로 넘긴 것.
// Uppercase로 작성해야 함.
const Paint = ({ title, description }) => (
<>
<h1>{title}</h1>
<h3>{description}</h3>
</>
);
const element = (
<>
<Paint title="Good" description="good" />
<Paint title="Bad" description="bad" />
<Paint title="Soso" description="soso" />
</>
);
ReactDOM.render(element, rootElement);
</script>
Children 제한이 없다는 것은 자기가 자기 자신을 계속 찍어낼 수도, 새로운 Children을 찍어낼 수도 있다는 것이다. 이는 확장성이 좋다는 의미이다!
첫 글자가 대문자면 큰 글자로, 소문자면 작은 글자로 출력하는 예제이다.
const Text
전체는 JS지만 JSX를 리턴한다.if(...)
는 JSreturn
은 JS<h1>, </h1>
은 JSX{text}
는 JS<div id="root"></div>
<script type="text/babel">
const rootElement = document.getElementById("root");
const Text = ({ text }) => {
if (text.charAt(0) === text.charAt(0).toUpperCase()) {
return (
<h1>
{text}
</h1>
);
} else {
return <h3>{text}</h3>;
}
};
const element = (
<>
<Text text="Text" />
<Text text="text" />
</>
);
ReactDOM.render(element, rootElement);
</script>
👉 실행 결과
숫자가 짝수이면 큰 글자로, 홀수이면 작은 글자로 출력하는 예제이다.
return ... ?
은 JS<h1>, </h1>
은 JSX{number}
는 JS:
는 JS<div id="root"></div>
<script type="text/babel">
const rootElement = document.getElementById("root");
function Number({ number }) {
return number % 2 === 0 ? <h1>{number}</h1> : <h3>{number}</h3>;
}
const element = (
<>
<Number number={1} />
<Number number={2} />
<Number number={3} />
<Number number={4} />
</>
);
ReactDOM.render(element, rootElement);
</script>
selected
을 사용해서 1~10 사이의 숫자 중 3을 선택해서 큰 글자로 출력하는 예제이다.
<div id="root"></div>
<script type="text/babel">
const rootElement = document.getElementById("root");
function Number({ number, selected }) {
return selected ? <h1>{number}</h1> : <h3>{number}</h3>;
}
const element = (
<>
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((number) => (
<Number number={number} selected={number === 3} />
))}
</>
);
ReactDOM.render(element, rootElement);
</script>
👉 실행 결과
리액트에서 Element를 그릴 때 자바스크립트의 장점을 사용할 수 있다.
바닐라 JS를 사용해서 1초마다 랜덤한 숫자가 생성되는 버튼을 만드는 예제이다.
<div id="root"></div>
<script>
const rootElement = document.getElementById("root");
function random() {
const number = Math.floor(Math.random() * (10 - 1) + 1);
const element = `
<button>${number}</button>
`;
rootElement.innerHTML = element;
}
setInterval(random, 1000);
</script>
👉 실행 결과
매번 버튼 자체를 새로 만든다.
그래서 포커싱이 유지되지 않고 새로 만들 때마다 포커싱이 사라진다.
이번에는 리액트로 1초마다 랜덤한 숫자가 생성되는 버튼을 만드는 예제이다.
<div id="root"></div>
<script type="text/babel">
const rootElement = document.getElementById("root");
function random() {
const number = Math.floor(Math.random() * (10 - 1) + 1);
const element = <button>{number}</button>;
ReactDOM.render(element, rootElement);
}
setInterval(random, 1000);
</script>
👉 실행 결과
언뜻 보면 바닐라 JS와 별다른 차이가 없어보이지만, 버튼을 선택했을 때 차이를 알 수 있다.
리액트로 구현한 코드는 버튼에서 변경되는 곳만 바꾼다. 즉, 버튼 전체를 바꾸는 것이 아닌 안의 내용만 바꾸는 것이다.
그래서 포커싱이 유지되는 것을 볼 수 있다.
리액트 엘리먼트는 불변객체(immutable)이다. 불변객체란 변하지 않는 객체를 뜻한다. 우리는 그저 ReactDOM.render(element, rooElement);
로 전달을 할 뿐, 변경 판단 및 반영은 리액트가 알아서 하는 것이다.
엘리먼트 타입이 바뀌면 이전 엘리먼트는 버리고 새로 그린다. 만약, 엘리먼트 타입이 같다면 key
를 먼저 비교하고, props
를 비교해서 변경사항을 반영한다.
DOM은 화면에 그려지거나 보여지는 것이 아닌 자체 논리적으로 브라우저가 미리 인지하고 있는 트리 개념이다. 그렇다면 리액트는 DOM을 어떻게 비교할까?
리액트는 'Virtual DOM'이라는 것을 가지고 있다. 이를 가지고 이전 가상 돔과 새로 들어온 가상 돔을 비교해서 업데이트를 하는 것이다.
돔에 계속 접근하는 것이 아닌 가상 돔을 가지고 변화를 반영한다.
https://www.w3schools.com/js/js_events.asp
버튼을 클릭하면 'pressed' 알림창이 나타나고, 마우스를 떼면 'bye' 알림창이 나타나는 예제이다.
<script type="text/babel">
const rootElement = document.getElementById("root");
const handleClick = () => alert("pressed");
const handleMouseOut = () => alert("bye");
const element = (
<button onClick={handleClick} onMouseOut={handleMouseOut}>
Press
</button>
);
ReactDOM.render(element, rootElement);
</script>
👉 실행 결과
간단한 검색창을 만드는 예제이다.
<script type="text/babel">
const rootElement = document.getElementById("root");
const state = { keyword: "", typing: false, result: "" };
const App = () => {
function handleChange(event) {
setState({ keyword: event.target.value, typing: true });
}
function handleClick() {
setState({
typing: false,
result: `We find results of ${state.keyword}`
});
}
return (
<>
<input onChange={handleChange} />
<button onClick={handleClick}>search</button>
<p>
{state.typing ? `Looking for ${state.keyword}...` : state.result}
</p>
</>
);
};
// 기존에 있는 state에 값을 넣어주는 함수
function setState(newState) {
// 첫 번째 인자의 값에 두 번째 인자의 값을 넣는다. (달라진 값만)
Object.assign(state, newState);
render();
}
function render() {
ReactDOM.render(<App />, rootElement);
}
render();
</script>
👉 실행 결과
Object.assign
: 첫 번째 인자(객체)에 두 번째 인자(객체)를 담는데, 변경이 있다면 덮어씌우고 변경이 없다면 그대로 유지한다.