본 글은 노마드코더의 "ReactJ로 영화 웹 서비스 만들기" 강의를 복습 정리한 글이다. 영상 강의에서 더 자세하고 쉬운 설명을 들을 수 있으니, 본 강의에 흥미가 생긴다면 영상 강의를 수강하자.
1. Introduction
ReactJS 는 JavaScript 라이브러리의 하나로서, UI 를 만들기 위해 사용된다. Facebook 의 소프트웨어 엔지니어 Jordan Walke 가 개발하였으며, 오픈 소스화 되었다. 즉, ReactJS 는 JavaScript 로 UI 작업을 하던 불편함을 해소하고자 만들어진 라이브러리라 볼 수 있다. 그렇다면 HTML, JavaScript 로 구현한 것과 HTML, ReactJS 로 구현한 것을 비교 해본다면 ReactJS 의 이점을 더 분명히 알아볼 수 있을 것이다.
먼저, HTML 과 JavaScript 로 이루어진 간단한 코드를 짠다.
<!-- vanilla.html -->
<html lang="en">
<body>
<span>Total Clicks: 0</span>
<button id="btn">Click me</button>
</body>
<script>
let count = 0;
const button = document.getElementById("btn");
const span = document.querySelector("span");
function handleClick() {
count++;
span.innerText = `Total Clicks: ${count}`;
}
button.addEventListener("click", handleClick);
</script>
</html>
위 코드는 버튼을 클릭했을 때, 클릭된 횟수가 페이지에 업데이트 되는 페이지를 구현한 것이다. 이제 같은 기능의 페이지를 ReactJS 로 어떻게 구현할 수 있고, JavaScript 로 짰을 때와 비교해서 어떤 점이 좋은지 알아보자.
2. Import ReactJS
JavaScript 는 브라우저에 기본적으로 내장되어있다보니 특별히 Import 를 시키는 경우가 없다. 하지만, ReactJS 는 라이브러리이기 때문에 Import 를 시켜주는 과정이 필요하다. 우선, ReactJS 로 간단히 구현할 HTML 파일을 "react.html" 이름으로 생성하고, 그 안에 다음 두 경로를 추가해준다.
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
src 속성에 있는 두 경로를 살펴보면 "react" 와 "react-dom" 이라는 이름의 라이브러리라는 것을 알 수 있다. "react" 는 React Element 를 모아둔 패키지이고, "react-dom" 은 React Element 를 HTML 에서 사용할 수 있도록 해주는 접착제 같은 역할이라 보면 된다. 그 외에 두 경로는 "development.js" 로 끝나는 형식의 주소이다. 지금은 개인적인 스터디용으로, 배포하지 않기 때문에 그대로 사용해도 되지만 사이트를 배포할 때는 "development.js" 를 "production.min.js" 로 바꿔줘야 한다.
위 코드를 삽입한 "react.html" 파일의 현재 상태는 다음과 같다.
<!-- react.html -->
<html lang="en">
<body>
</body>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
</html>
3. 간단한 문자열 출력
1) <div> 생성
이제 버튼과 텍스트를 출력시켜서, 버튼을 클릭할 때마다 텍스트에 클릭 수가 반영되는 기능을 구현한다. 하지만 그 전에, 간단하게 ReactJS 로 문자열을 띄우는 코드를 만들어보자. 우선, HTML 코드에서 ReactJS 의 Element 를 넣어줄 <div> 를 생성한다.
<!-- react.html -->
<html lang="en">
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
</html>
이제 React Element 를 생성해서, 위에 "root" 라는 id 값을 가진 <div> 에 넣어줄 것이다.
2) React.createElement()
<!-- react.html -->
<html lang="en">
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script>
const span = React.createElement("span");
</script>
</html>
"React.createElement("span")" 을 통해 span 을 생성한다. 보이는 것처럼 createElement 함수의 파라미터로 원하는 Element 의 이름을 넣어주면, 해당 이름의 React Element 를 생성할 수 있다. createElement 는 Element 의 타입 이름만 파라미터로 받을 수 있는 것이 아니다. 만약, span 에 원하는 속성을 넣고 싶다면 두번째 파라미터로 넣어주면 된다.
<!-- react.html -->
...
<script>
const span = React.createElement("span", { id: "react-span" });
</script>
...
id 속성 값으로 "react-span" 을 넣은 코드이다. 그 외에 배경색이나 마진 값 등등 기본적인 span 의 속성들을 설정해줄 수 있다. 이제 그 안에 출력할 문자열을 넣어줄 것이다. 그것은 createElement() 함수의 3번째 파라미터로 넣어준다.
<!-- react.html -->
...
<script>
const span = React.createElement("span", { id: "react-span" }, "Hello");
</script>
...
이렇게 생성된 span 은 다음과 같은 HTML 요소를 뜻하게 된다.
<span id="react-span">Hello</span>
그런데 지금까지 만든 React Element 를 생성하는 코드를 그대로 브라우저에 띄우면 아무것도 보이지 않는다. 왜냐하면, React 로 만든 Element 를 HTML 로 넣어주는 ReactDOM 을 아직 사용하지 못했기 때문이다. 이제 ReactDOM 으로 렌더링을 시켜주도록 한다.
3) ReactDOM.render() -> Root.reander(): react@18
본 강의에서는 ReactDOM.render() 함수를 사용해서 렌더링을 한다. 그러나 현재 배우면서 설치한 react 의 버전은 v18 이라, ReactDOM.render() 함수가 Deprecated 가 되었다. 그래서 다른 함수를 통해 렌더링을 해줘야한다. 본 글에서 복습 정리하는 것은 react@18 에 맞는 렌더링 방법이다.
react 18 버전에서는 Root 객체를 생성하여, 해당 객체를 통해 렌더링을 한다.
<script>
const rootDiv = document.getElementById("root");
</script>
위 코드는 처음에 HTML 코드에 추가했던 <div> 를 JavaScript 로 가져오는 코드이다. 이제 rootDiv 값을 ReactDOM 을 사용하여 Root 로 만들어준다.
<script>
const rootDiv = document.getElementById("root");
const root = ReactDOM.createRoot(rootDiv);
</script>
root 를 사용해서 span 을 render 해주면 만들었던 span 을 브라우저에 나타내준다. 이제 React Element 를 만들고, Render 하는 과정을 모두 모은 스크립트 코드는 다음과 같다.
<!-- react.html -->
<html lang="en">
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script>
const rootDiv = document.getElementById("root");
const root = ReactDOM.createRoot(rootDiv);
const span = React.createElement(
"span",
{ id: "react-span" },
"Hello, I'm Span"
);
root.render(span);
</script>
</html>
위 코드를 브라우저로 열어보면 다음과 같이 나타난다.
위 이미지는 브라우저의 모습과 콘솔창을 같이 캡쳐한 것이다. 특히 콘솔창에 [Elements] 창을 확인하면, ReactJS 코드로 만든 Element 가 HTML 로 생성되어 <div> 안에 생긴 것을 확인할 수 있다. 이처럼 ReactJS 는 스크립트 코드만으로 HTML 에 View 를 만들 수 있다.
4. Event Listener
이번에는 Button 을 추가하고, 클릭할 때마다 텍스트에 반영이 되도록 한다. 이로써 맨 처음에 비교하기 위해 만들었던 Vanilla JS 의 코드를 ReactJS 로 완전히 대체할 수 있게 된다.
우선, Button 을 생성하는 것은 위에서 했던 span 을 생성하는 것과 같다. createElement 함수를 사용해서 button 을 만들어준다.
<!-- react.html -->
...
<script>
const button = React.createElement("button", { onClick: () => console.log("Clicked") }, "Click me");
</script>
createElement 함수의 각 파라미터가 어떤 것을 의미하는지 span 을 만들때 설명한 바 있다. 그런데 span 을 생성할 때와 다른 점이라면, "onClick" 속성이 있다는 것이다. 위 코드처럼 ReactJS 의 Element 는 생성과 함께 Event Listener 를 선언해줄 수 있다. 위 버튼은 Click 이벤트가 발생하면, 콘솔창에 "Clicked" 문자열을 출력하는 것이다.
그런데 위에서 span 을 만들어서 렌더링하고, button 을 추가로 만들어서 렌더링하기 위해 render() 함수를 두번 호출하면 마지막에 호출할 때 넣은 파라미터의 element 만 나오게 된다. 이처럼 여러 element 를 div 안에 한번에 넣기 위해서는 어떻게 해야할지 알아보자.
<!-- react.html -->
...
<script>
const rootDiv = document.getElementById("root");
const root = ReactDOM.createRoot(rootDiv);
const span = React.createElement("span", null, "Total Clicks: 0");
const button = React.createElement("button", { onClick: () => console.log("Clicked") }, "Click me");
const container = React.createElement("div", null, [span, button]);
root.render(container);
</script>
...
위와 같이 container 라는 변수에 "div" element 를 만들어서, 그 안에 span 과 button 을 넣은 후 render() 함수에 파라미터로 전달하면 된다. createElement() 함수의 3번째 파라미터는 여러 children element 를 배열로 받을 수 있다. 위 코드를 브라우저에서 열어보면 다음과 같이 만들어진 것을 확인할 수 있다.
이제 Button 을 클릭했을 때, "Total Clicks: 0" 의 문자열에 변화가 생겨줘야한다. 그런데, 공식 문서에 따르면 "React Element are immutable" 이라고 말한다.
즉, 한번 생성된 React Element 는 이후에 속성이나 자식 값들을 수정 할 수 없다는 것이다. 그래서 위 공식 문서에서 제안한 방법은 "필요할 때마다 새롭게 생성하여 렌더링" 하는 방식이다.
// src : https://devdocs.io/react/rendering-elements
const root = ReactDOM.createRoot(
document.getElementById('root')
);
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
root.render(element);
}
setInterval(tick, 1000);
위 코드는 React 공식문서에서 렌더링된 element 를 업데이트하는 방법을 예시로 올려준 것이다. 위 코드를 응용해서 지금 필자가 만든 코드에 적용해보도록 한다.
<!-- react.html -->
...
<script>
const rootDiv = document.getElementById("root");
const root = ReactDOM.createRoot(rootDiv);
let count = 0;
function showCount(count) {
const span = React.createElement("span", null, `Total Clicks: ${count}`);
const button = React.createElement("button", { onClick: () => showCount(++count) }, "Click me");
const container = React.createElement("div", null, [span, button]);
root.render(container);
}
showCount(0);
</script>
...
Time Ticker 예시를 응용해서 Button 을 클릭할 때마다 Span 의 문자열이 업데이트 되는 코드를 만들어보았다. 실제로 브라우저에서 잘 작동하는 것을 확인할 수 있다.
5. JSX
이번에는 JSX 를 사용하여 createElement() 를 더 효율적으로 사용하는 방법에 대해 정리한다. JSX 는 JavaScript 를 확장한 문법으로, JavaScript 의 모든 기능을 포함하면서 React element 도 생성할 수 있다. 그래서 React 공식 문서에 따르면, React 와 JSX 를 같이 사용할 것을 권장하고 있다.
방금 완료했던 ReactJS 코드에 JSX 를 도입하면 어떻게 코드가 변하는지 살펴보자. 위에 ReactJS 코드에서 UI 부분만 추출한다.
const rootDiv = document.getElementById("root");
const root = ReactDOM.createRoot(rootDiv);
let count = 0;
const span = React.createElement("span", null, `Total Clicks: ${count}`);
const button = React.createElement("button", { onClick: () => console.log("Clicked") }, "Click me");
const container = React.createElement("div", null, [span, button]);
root.render(container);
위 코드는 위에서 구현했던 ReactJS 코드에서 UI 부분만 추출하여 정리한 것이다. 위 공식 문서 내용에 따르면, JSX 는 ReactJS 에서 UI 관련 작업에서 시각적인 도움을 준다고 설명한다. 그렇다면, UI 관련 코드에서 JSX 를 도입했을 때, 코드에 어떤 변화가 일어나는지 위주로 살펴보면 된다. JSX 를 도입한 코드는 다음과 같다.
const rootDiv = document.getElementById("root");
const root = ReactDOM.createRoot(rootDiv);
let count = 0;
// const span = React.createElement("span", null, `Total Clicks: ${count}`);
const span = (
<span>
Total Clicks: {count}
</span>
);
// const button = React.createElement("button", { onClick: () => showCount(++count) }, "Click me");
const button = (
<button
onClick={console.log("Clicked")}>
Click me
</button>
);
const container = React.createElement("div", null, [span, button]);
root.render(container);
span 과 button 부분을 보면, 주석처리된 부분이 ReactJS 코드이고, 새롭게 추가된 코드가 JSX 를 도입한 코드이다. 두 코드의 차이점은 한 눈에 알아볼 수 있다. JSX 가 적용된 코드는 마치 HTML 에서 Element 를 추가할 때와 같은 형식을 띄고 있다. 즉, JSX 는 ReactJS 코드에서 UI 와 관련된 부분을 HTML 처럼 구현할 수 있도록 도와주는 라이브러리라 볼 수 있다.
이제 페이지를 브라우저에 로딩하고, 콘솔을 확인해보자.
바로 적용될 것 같았지만, 콘솔에 에러가 나타나고 아무것도 보이지 않는다. 에러는 "SyntaxError" 이다. 즉, 방금 추가한 JSX 코드를 브라우저가 해석할 수 없다는 뜻이다. JSX 는 JavaScript 에서 확장된 새로운 문법이기 때문에, 기본 JavaScript 를 읽어내는 브라우저에서는 JSX 를 해석할 수 없다. 따라서, JSX 코드를 JavaScript 로 변환해주는 과정이 필요하다. 이 과정에는 Babel 이 필요하다.
Babel 은 JavaScript 에서 확장된 다양한 문법들을 기본 JavaScript 로 변환해준다. Babel 을 추가할 수 있는 다양한 방법이 있지만, 현재로써는 <script> 를 통해 넣어줘야한다. 다음 코드를 ReactJS 를 추가했던 코드 밑에 넣어준다.
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
그리고 UI 관련 코드들이 담겨있는 <script> 에 type 속성을 다음과 같이 추가하면 Babel 을 통해 JSX 코드를 인식할 수 있다.
<html lang="en">
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const rootDiv = document.getElementById("root");
const root = ReactDOM.createRoot(rootDiv);
let count = 0;
// const span = React.createElement("span", null, `Total Clicks: ${count}`);
const span = (
<span>
Total Clicks: {count}
</span>
);
// const button = React.createElement("button", { onClick: () => showCount(++count) }, "Click me");
const button = (
<button
onClick={console.log("Clicked")}>
Click me
</button>
);
const container = React.createElement("div", null, [span, button]);
root.render(container);
</script>
</html>
JSX 로 직접 구현한 코드는 <body> 안에 그대로 있다. 한편, <head> 안에는 직접 구현하지 않은 코드가 담겨있는 것을 확인할 수 있다. <head> 에 담겨진 코드가 바로 Babel 이 JSX 를 JavaScript 로 변환하여 넣어둔 코드이다. 브라우저는 <head> 에 담겨 있는 JavaScript 코드를 읽어내어 렌더링을 시켜주는 것이다.
이제 마지막으로 "container" 의 코드를 JSX 로 적용해보자. "container" 는 위에서 수정한 "span" 과 "button" 을 둘 다 가지고 있어야한다. 포함 해야한다는 것을 잠시 제외하고, "container" 부분을 JSX 로 수정하면 다음과 같다.
// const container = React.createElement("div", null, [span, button]);
const container = (
<div>
</div>
);
이제 <div> 안에 JSX 로 먼저 수정한 "span" 과 "button" 을 넣어준다. 이 때, "span" 과 "button" 을 함수 형태로 수정해줘야한다.
// const span = (
// <span>
// Total Clicks: {count}
// </span>
// );
const span = () => {
return <span>
Total Clicks: {count}
</span>
};
// const button = (
// <button
// onClick={console.log("Clicked")}>
// Click me
// </button>
// );
const button = () => {
return <button
onClick={console.log("Clicked")}>
Click me
</button>
};
그리고, <div> 에 넣어줄 때는 하나의 Element 인 것처럼 넣어주면 된다.
const container = (
<div>
<span />
<button />
</div>
);
이제 브라우저에서 렌더링이 정상적으로 되는지 확인한다.
예상했던 모습이 나오지 않는다. 내용을 보아하니, 기본적인 HTML Element 인 <span> 과 <button> 만 들어가있는 것을 확인할 수 있다. 지금까지 만들었던 "span" 과 "button" 의 네이밍이 기본적인 HTML Element 의 네이밍과 유사하여, 브라우저가 기본 Element 로 인식한 것이다. 따라서, ReactJS 로 만든 자기만의 Component 는 변수명을 무조건 대문자로 시작해줘야한다.
const rootDiv = document.getElementById("root");
const root = ReactDOM.createRoot(rootDiv);
let count = 0;
const Span = () => {
<span>
Total Clicks: {count}
</span>
};
const Button = () => {
<button
onClick={console.log("Clicked")}>
Click me
</button>
};
const container = (
<div>
<Span />
<Button />
</div>
);
root.render(container);
다시 브라우저로 확인해보자.
이전과 조금 달라졌지만, 원하는 모습으로 렌더링 되지 않았다. 한가지 빠뜨린 점이 있기 때문이다. 바로 "container" 도 Component 로 바꿔주지 않았기 때문이다. "Span" 과 "Button" 은 함수 형태로 바꿔주었지만, "container" 는 그렇지 않다. 따라서, "container" 도 "Span" 과 "Button" 처럼 바꿔준다. 최종적으로 수정한 코드는 다음과 같다.
const rootDiv = document.getElementById("root");
const root = ReactDOM.createRoot(rootDiv);
let count = 0;
const Span = () => {
return <span>
Total Clicks: {count}
</span>
};
const Button = () => {
return <button
onClick={console.log("Clicked")}>
Click me
</button>
};
const Container = () => {
return <div>
<Span />
<Button />
</div>
};
root.render(<Container />);
원하던 결과가 렌더링 된 것을 확인할 수 있다. ReactJS 와 JSX 를 같이 사용하는 방법에 대해 정리 해보자면 다음과 같다.
- ReactJS 와 JSX 를 같이 사용하여 하나의 Component 를 만들 수 있다.
- Component 를 생성할 때는 함수의 형태로 작성 해야하며, 함수의 이름은 대문자로 시작해야한다.
- Component 를 다른 곳에서 사용할 때는 HTML 의 Element 처럼 사용하면 된다.
ReactJS 와 JSX 에 대해 공부하면서 느낀점이 있다. HTML 문서에서는 대략적인 틀만 잡아주고, 그 안에 세세한 부분들은 모두 JS 파일에서 만들고 싶어서 이 둘을 사용하게 되지 않았나 싶다. HTML 과 Javascript 를 겉핥기 식으로 배운 후에 바로 ReactJS 로 넘어와서 체감이 확 되지는 않지만, 확실히 매력적인 문법이라는 것은 느낄 수 있었다. 좀 더 깊게 공부하여 React-Native 로 앱을 만드는 순간까지 정진...!
'노마드코더' 카테고리의 다른 글
[ReactJS로 영화 웹 서비스 만들기] ToDo List 만들기 (0) | 2023.06.09 |
---|---|
[ReactJS로 영화 웹 서비스 만들기] 5. Effects (0) | 2023.05.31 |
[ReactJS로 영화 웹 서비스 만들기] 4. Create React App (0) | 2023.04.27 |
[ReactJS로 영화 웹 서비스 만들기] 3. Props & PropTypes (0) | 2023.04.20 |
[ReactJS로 영화 웹 서비스 만들기] 2. State (0) | 2023.03.21 |