그 무섭던 리액트를 시작하겠다.
- React의 3가지 특징에 대해서 이해하고, 설명
- JSX가 왜 명시적인지 이해하고, 바르게 작성
- React 컴포넌트(React Component)의 필요성에 대해서 이해하고, 설명
- create-react-app 으로 간단한 개발용 React 앱을 만듦
이제 기본적인 리액트의 문법에 대해서 알아보겠따!
JavaScript XML : JS의 확장된 문법, 문자열도 아니고 HTML도 아님!
리액트에선 UI를 구성할 때 쓰는 문법, 리액트 Element를 만들 수 있음!
무조건 암기하자
import React from "react";
function App() {
const user = {
firstName: "React",
lastName: "JSX Activity"
};
function formatName(user) {
return user.firstName + " " + user.lastName;
}
// JSX 없이 활용한 React
// return React.createElement("h1", null, `Hello, ${formatName(user)}!`);
// JSX 를 활용한 React with Babel
return <h1>Hello, {formatName(user)}!</h1>;
}
핵심 : function app()
은 위에서 말한 기능과 요소가 함축된 컴포넌트임, JSX가 있고 없고를 보면 훨씬 깔끔하고 가독성도 뛰어나다!
class
가 아니라 className
필수: class
라고 하면 VJS(바닐라)의 클래스로 받아들임map()
必, map()
를 사용할 때는 반드시 "key" JSX 속성 必const posts = [
{ id: 1, title: "Hello World", content: "Welcome to learning React" },
{ id: 2, title: "Installation", content: "You can install React from npm" }
];
export default function App() {
// 한 포스트의 정보를 담은 객체 post를 매개변수로 받고
// obj를 JSX 표현식으로 바꿔 리턴해주는 함수 postToJSX
const postToJSX = (post) => {
return (
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
};
// postToJSX 함수를 이용하여 여러 개의 엘리먼트롤 표시
return (
<div className="App">
<h1>Hello JSX</h1>
{posts.map(postToJSX)}
</div>
);
}
핵심 : 필수 조건인 map()
활용 및 key: id
를 넣어줘 렌더링되게 했음
map()
의 활용'하드 코딩' 같이 노다가성이 띄는 코드들을 전부 작성하는 코드가 있다. 그 코드가 몇개 안된다면 좋겠지만 큰 사이트들은 몇백개에서 몇만개까지 가는 데이터가 우습게 오간다. 그럴 때 일손을 덜어주는 것이 map()
의 등장이다.
const posts = [
{ id : 1, title : 'Hello World', content : 'Welcome to learning React!' },
{ id : 2, title : 'Installation', content : 'You can install React via npm.' },
{ id : 3, title : 'reusable component', content : 'render easy with reusable component.' },
// ...
{ id : 100, title : 'I just got hired!', content : 'OMG!' },
];
function Blog() {
return (
<div>
<div>
<h3>{posts[0].title}</h3>
<p>{posts[0].content}</p>
</div>
<div>
<h3>{posts[1].title}</h3>
<p>{posts[1].content}</p>
</div>
{// ...}
<div>
<h3>{posts[99].title}</h3>
<p>{posts[99].content}</p>
</div>
{// ... 98 * 4 more lines !!}
</div>
);
}
위 코드는 하드 코딩을 한 결과다. 사용자 정의 컴포넌트 Blog()
를 보면 100개만 하더라도 빡세보이지 않는가? 저걸 더 효율적으로 옮겨보자
// 의사코드 : 열의 각 요소(post)를 특정 논리(postToElement 함수)에 의해 다른 요소로 지정(map)
function Blog() {
const postToElement = (post) => (
<div>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
// 코드 가독성을 위해 변수화
const blogs = posts.map(postToElement);
return <div className="post-wrapper">{blogs}</div>;
}
짧고, 효율적이고 보기 쉽다. 변수화 말고 return
에 써줘도 된다. 하지만 위의 코드는 오류가 뜬다! 왜? key
값이 없기 때문
function Blog() {
// postToElement라는 함수로 나누지 않고 아래와 같이 써도 무방
const postToElement = posts.map((post) => (
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
));
return <div className="post-wrapper">{postToElement}</div>;
}
map()
이 함수 표현식 안에 들어감과 동시에 key
값이 들어갔다. 효율적!
리액트는 컴포넌트 기반 개발 라이브러리다. 컴포넌트에 대해 더 자세히 말하자면 'UI를 구성하는 필수 요소', '리액트의 심장'이라고 할 정도로 중요하다. 그 중요성에 대해 알아보자!
- 컴포넌트 : 하나의 기능 구현을 위해 묶어놓은 여러 종류의 코드 모음
- 여러 컴포넌트를 묶어 앱 탄생!
- 기본적으로 리액트 앱은 최소 1개의 컴포넌트(root)를 가지고 있고, 모든 컴포넌트는 자식 컴포넌트를 가질 수 있다. 즉! 트리 구조를 띈다!
- 핵심⭐️ 수정이 용이하다! 예시를 통해 보자
이제 리액트를 시작하는데 있어 필요한 준비 동작을 만들어 보겠다!@
- Create React App 소개를 보고, Create React App 이 무엇인지 대략적으로 이해할 수 있다.
- npx create-react-app 으로 새로운 리액트 프로젝트를 시작
- create-react-app 으로 간단한 개발용 React 앱을 실행
리액트 랜덤 명언 앱 튜토리얼을 따라 간단한 리액트 랜덤 명언 앱을 만듦- Create React App으로 만들어진 리액트 프로젝트 구성을 대략적으로 이해
정의 : 리액트 SPA를 만드는데 쉽고 빠르게 개발할 수 있도록 하는 툴체인
만들려는 폴더 들어가서
npx create-react-app 폴더 이름
입력
만들면! react, react-dom, react-scripts 등등이 깔리며 'Happy Hacking'이란 메시지가 나오면 만들기 끝!npm run start
로 실행해보자
src
폴더에 코드를 입력한다!App()
에 아까 배웠던 JSX와 기본 코드들을 작성하면 됨!새로고침으로 만드는 간단한 명언 랜덤 제조
참고 : '핫리로딩'이라는 기능이 기본적으로 적용돼있어 바로바로 새로고침됨
// index.js
<App />,
document.getElementById('root')
// app.js
function App() {
// 배열 데이터 생성
const proverbs = [
"인생은 재미다.",
"메모를 하는 건 기억하기 위함이 아니라 잊기 위함이다.",
"우리가 배우는 의미는 그 자체로 익숙해지기 위해서다.",
"어린 아이에게 설명해주지 못하는 이론은 이해하지 못한 이론이다.",
"침묵도 하나의 언어다.",
"아무리 가까운 사이라도 어느정도 거리가 있어야한다.",
"사회에서 우선적으로 공감 후 해결책을 제시하는 것이 마땅하다.",
"인간 관계를 확장하고 유지하는 방법은 상대에게 내가 중요한 사람이 되었다는 인식을 심어주는 것이다.",
];
// 랜덤함수 제작
const getRandomIdx = (length) => {
return parseInt(Math.random() * length);
};
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
<code>src/App.js</code> 새로고침하면 새로운 명언을 볼 수 있습니다.
</p>
// 랜덤함수 사용
{proverbs[getRandomIdx(proverbs.length)]}
</header>
</div>
);
}
트위터와 비슷한 'Twittler'를 리액트로 코딩해보자!
├── /React Twittler State Props
│ ├── README.md
│ ├── /public # create-react-app이 만들어낸 파일로 yarn/npm start로 실행 시에 쓰임
│ └── /src # React 컴포넌트가 들어가는 폴더
│ ├── static # dummyData가 있는 폴더
│ │ └── dummyData.js
│ ├── App.css
│ ├── App.js # Twittler App이 작성
│ ├── index.js
├ package.json
└ .gitignore
const Sidebar = () => {
// 어썸 폰트 아이콘 가져오기
return <section className="sidebar">{<i className="far fa-comment-dots"></i>}</section>;
};
const Counter = () => {
return (
<div className="tweetForm__input">
<div className="tweetForm__inputWrapper">
<div className="tweetForm__count" role="status">
{/* dummyTweet로 전달되는 데이터의 갯수 */}
{"Total : " + dummyTweets.length}
</div>
</div>
</div>
);
};
const Footer = () => {
return (
<div>
{<footer></footer>}
<img id="logo" src={`${process.env.PUBLIC_URL}/codestates-logo.png`} />
Copyright @ 2022 Code States
</div>
);
};
// TODO : Footer 함수 컴포넌트를 작성, 시멘틱 엘리먼트 footer가 포함
const Tweets = () => {
return (
<ul className="tweets">
{dummyTweets.map((tweet) => {
const isParkHacker = tweet.username === "parkhacker" ? "tweet__username tweet__username--purple" : "tweet__username";
return (
<li className="tweet" key={tweet.id}>
<div className="tweet__profile">{<img src={tweet.picture}></img>}</div>
<div className="tweet__content">
<div className="tweet__userInfo">
{/* {<span className="tweet__username">{tweet.username}</span>} */}
{/* {tweet.username === "parkhacker" ? (
<span className="tweet__username tweet__username--purple">{tweet.username}</span>
) : (
<span className="tweet__username">{tweet.username}</span>
)} */}
<span className={isParkHacker}>{tweet.username}</span>
{<span className="tweet__createdAt">{tweet.createdAt}</span>}
</div>
{/* 트윗 메세지 */}
<div className="tweet__message">{tweet.content}</div>
</div>
</li>
);
})}
</ul>
);
};
const Features = () => {
return (
<section className="features">
<div className="tweetForm__container">
<div className="tweetForm__wrapper">
<div className="tweetForm__profile"></div>
<Counter />
</div>
</div>
<Tweets />
<Footer />
</section>
);
};
const App = () => {
return (
<div className="App">
<main>
<Sidebar /> {/* ={Sidebar()} */}
<Features />
</main>
</div>
);
};
// ! 아래 코드는 수정하지 않습니다.
export { App, Sidebar, Counter, Tweets, Features, Footer };
map()
은 진짜 key
가 중요하구나! key
는 중복된 데이터들을 걸러주면서 적용하기 때문에 map()
에게 중요한 속성이구나!obj.id
key
는 반드시 변하지 않고, 예상 가능하며, 유일한 값 必 : 변하는 key
(Math.random()
으로 생성된 값 등)를 사용하면 많은 컴포넌트 인스턴스와 DOM 노드를 불필요하게 재생성하여 성능이 나빠지거나 자식 컴포넌트의 state
가 유실됨{Sidebar()}
랑 비슷한 의미let langs = ["JavaScript", "HTML", "Python"];
let viewLangs = () => {
return langs.map((it) => { //함수에 감싸져있음, viewLangs가 실행돼야 map이 실행되는 구조
return <p>{it}</p>;
});
};
return (
<div>
{viewLangs()} // 함수 그 자체이므로 실행을 넣어줌
</div>
);
let langs = ["JavaScript", "HTML", "Python"];
let viewLangs = langs.map((it) => { // 함수 호출 결과가 변수에 바로 할당됨
return <p>{it}</p>;
});
return (
<div>
{viewLangs}
</div>
);