페이지 단위가 아닌 컴포넌트 단위로 나누어 생각하고 개발하는 방법을 알아보자.
React란?
- 웹, 데스크탑, 모바일 등 다양한 플랫폼에서 활용할 수 있는 프론트엔드 라이브러리
- 프론트앤드 개발을 위한 JavaScript 오픈소스 라이브러리
React의 특징
1. 선언형 :
- 리액트는 구조, 디자인, 동작을 나눠서 개발하는 것을 지양
- 리액트는 한 페이지를 보여주기 위해 HTML / CSS / JS 로 나눠서 적기 보다는 하나의 파일에 명시적으로 작성
- JSX를 활용한 선언형 프로그래밍을 지향
- JSX(HTML + JS가 결합한 문법) 기반으로 명시적으로 코드를 작성 , 직관적으로 코드 작성이 가능
- 코드를 자세히 분석하지 않고도 의도를 분명히 알 수 있게 작성
2. 컴포넌트 기반 :
- 컴포넌트(component) : 하나의 기능 구현을 위한 여러 종류의 코드 묶음
- 따라서 기술적 분류에 따라 코드를 분리하지 않고, 기능 별로 코드를 분리하기를 권장
- 컴포넌트로 분리하면 서로 독립적이고 재사용이 가능하기 때문에 기능 자체에 집중하여 개발할 수 있음
- 컴포넌트 간 의존성이 없어져서 독립적으로 작동
- 기능별로 나눠진 컴포넌트를 미리 만들어두고 이후에 해당 컴포넌트를 재사용할 수 있어 효율적
- 기획이나 디자인이 변경되어도 쉽게 고칠 수 있음
* 코드만 읽어도 어플리케이션을 떠올릴 수 있다. (선언적 + 컴포넌트 기반)
3. 범용성 :
- 리액트는 JavaScript 프로젝트 어디에든 유연하게 적용될 수 있음
- 다른 자바스크립트 프레임워크로 제작된 웹어플리케이션에 리액트를 추가해서 개발할 수 있음 (예: Angular 등)
- 리액트는 UI 구성요소 라이브러리라서 기존코드 일부를 고칠 수 있음
- Facebook에서 관리되어 안정적이고, 대중적임
- 리액트 네이티브로 모바일 개발도 가능
JSX
- JSX는 JavaScript XML의 줄임말
- JSX는 문자열도 아니고 HTML도 아님
- React에서 UI를 구성할 때 사용하는 문법으로 JavaScript를 확장한 문법
- 이 문법을 이용해서 React 엘리먼트를 만들 수 있음
그러나 JSX는 JavaScript가 확장된 문법이지만, 브라우저가 바로 실행할 수 있는 JavaScript 코드가 아니다. 그렇기 때문에 브라우저가 이해할 수 있는 JavaScript 코드로 변환을 해주어야 한다.
“Babel”
이때 이용하는 것이 바로 “Babel”이다.
- Babel은 JSX를 브라우저가 이해할 수 있는 JavaScript로 컴파일함
- 컴파일 후, JavaScript를 브라우저가 읽고 화면에 렌더링할 수 있음
DOM과 비교했을 때 React의 장점
1. React에서는 DOM과 다르게 CSS, JSX 문법만을 가지고 웹 애플리케이션을 개발할 수 있다.
- 즉, JSX를 사용하면 JavaScript 만으로 마크업(markup) 형태의 코드를 작성하여 DOM에 배치할 수 있게 된다.
- = 마크업과 로직을 분리하지 않고 UI를 만들 수 있음
- 컴포넌트 하나를 구현하기 위해서 필요한 파일이 줄어들었고, 한눈에 컴포넌트를 확인할 수 있게 되었다.
- 다시 말해, JavaScript 문법과 HTML 문법을 동시에 이용해 기능과 구조를 한눈에 확인할 수 있다.
* 주의할 점은 JSX는 HTML처럼 생겼지만, HTML이 아니기 때문에 앞서 언급했던 “Babel”을 이용한 컴파일 과정이 필요하다. (JSX는 HTML 문법을 일부 차용했지만, 다른 문법을 사용)
2. JSX는 createElement 등의 메서드를 사용하지 않고도 React 엘리먼트를 만들 수 있다.
React에서는 JSX를 이용해서 앞서 보았던 DOM 코드보다 명시적으로 코드를 작성할 수 있다.
JSX 없이는 React 요소를 만들 수 없는 걸까?
물론 JSX 없이도 React 요소를 만들 수 있다. 다만 코드가 복잡하고, 가독성이 떨어진다. JSX를 사용함으로써 코드를 이해하기 쉬워진다. Babel이 알아서 이 JSX 코드를 변환해 주니 편리하게 이용하면 된다.
DOM과 React로 엘리먼트 생성하기
1. DOM으로 엘리먼트 생성하기
DOM에서 엘리먼트를 생성하려면 먼저 HTML(index.html)과 JavaScript(index.js)를 연결하는 작업을 진행해야 한다.
const user = {
firstName: "HTML",
lastName: "DOM"
};
function formatName(user) {
return user.firstName + " " + user.lastName;
}
const heading = document.createElement("h1");
heading.textContent = `Hello, ${formatName(user)}`;
document.body.appendChild(heading);
2. React 로 엘리먼트 생성하기
한 개의 JavaScript 파일 안에서 HTML과 JavaScript로 나누어졌던 두 가지 일을 한 번에 처리하고 있다.
import React from "react";
import "./styles.css";
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 (이 방법을 권장!)
return <h1>Hello, {formatName(user)}!</h1>;
}
export default App;
하나의 파일에서 웹 애플리케이션의 구조와 동작을 한 눈에 파악할 수 있는 덕분에 JSX는 개발자가 코드만 바라보는게 아니라, 구조를 바라볼 수 있게 돕는다.
JSX 활용법
1. JSX에서 여러 엘리먼트를 작성하고자 하는 경우, 최상위에서 opening tag와 closing tag로 감싸주어야 한다.
2. React에서 CSS class 속성을 지정하려면 "className"으로 표기해야 한다.
* 만약 class로 작성하게 된다면 React에서는 이를 html 클래스 속성 대신 자바스크립트 클래스로 받아들이기 때문에 주의
-> 리액트에서는 HTML 처럼 클래스 이름을 지정하려고 하면, 아래처럼 에러가 난다. 리액트 공식 문서의 JSX 속성 정의 부분에도 이 이야기가 잘 나와있다.
3. JSX에서 JavaScript를 쓰고자 한다면, 꼭 중괄호를 이용해야 한다.
- JSX는 JavaScript를 확장한 문법이기 때문에 JavaScript 기본 문법을 활용할 수 있다. JavaScript 기본 문법을 활용하려면, JSX 내부에서 중괄호({}) 내에 JavaScript 표현식을 작성하면 됩니다.
- JSX에서 변수를 쓸 때 중괄호 {} 를 사용한다
*중괄호를 사용하지 않으면 일반 텍스트로 인식한다.
(1) 중괄호 내에 표현식 사용
const jsx = <div>{(1 + 1 === 2) ? (<p>정답</p>) : (<p>탈락</p>)}</div>
(2) 중괄호 내에 변수 사용
4. React 엘리먼트가 JSX로 작성되면 "대문자"로 시작해야 한다.
- 이렇게 대문자로 작성된 JSX 컴포넌트를 따로 사용자 정의 컴포넌트라고 부른다.
* 소문자로 시작하게 되면 일반적인 HTML 엘리먼트로 인식을 하게 된다.
5. 조건부 렌더링은 if문이 아닌 삼항연산자를 이용해야 한다.
6. map 함수 사용시 key JSX 속성을 사용해야 한다.
- React 에서 여러 개의 HTML 엘리먼트를 표시할 때는 "map()" 함수를 사용한다. (map 메서드는 아래에서 자세히 정리)
- map 함수를 사용할 때는 반드시 "key" JSX 속성을 넣어야 한다.
* "key" JSX 속성을 넣지 않으면 리스트의 각 항목에 key를 넣어야 한다는 경고가 표시된다.
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>
);
};
return (
<div className="App">
<h1>Hello JSX</h1>
{posts.map(postToJSX)}
</div>
);
}
key 속성
React에서 map 메서드 사용 시, key 속성을 넣지 않으면 아래와 같이 리스트의 각 항목에 key를 넣어야 한다는 경고가 표시된다. key 속성의 위치는 map 메서드 내부에 있는 엘리먼트 즉, 첫 엘리먼트에 넣는다.
function Blog() {
// postToElement라는 함수로 나누지 않고 아래와 같이 써도 무방합니다.
const blogs = posts.map((post) => (
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
));
return <div className="post-wrapper">{blogs}</div>;
}
key 속성값이 반드시 id가 되어야 할까? id가 존재하지 않으면 어떻게 해야 할까?
key 속성값은 가능하면 데이터에서 제공하는 id를 할당해야 한다.
key 속성값은 id와 마찬가지로 변하지 않고, 예상 가능하며, 유일해야 하기 때문이다.
정 고유한 id가 없는 경우에만 배열 인덱스를 넣어서 해결할 수 있다.
배열 인덱스는 최후의 수단(as a last resort)으로만 사용한다.
-> Math.random() 등으로 무작위로 생성된 값을 key로 지정하게 되면, key가 예상 불가능하며, 시시각각 변화하게 된다.
그렇게 되면 React는 많은 컴포넌트 인스턴스와 DOM 노드를 불필요하게 리렌더링(재생성)하고, 이에 따라 성능 이슈가 발생하거나 자식 컴포넌트의 state가 유실될 수 있다. (더 자세한 내용에 대해서는 공식 문서를 참고)
[정리]
map 함수로 배열 형태로 저장된 여러 엘리먼트들을 렌더링하는 경우가 많다. 이 때, 각각의 최상위 엘리먼트들에 key prop이 요구된다. 이는 React가 해당 엘리먼트들의 변경 사항을 알아채기 쉽게 돕기 위함이다.
map 메서드
map 메서드는 React에서 여러 데이터를 렌더링 할 때 사용한다.
김코딩의 블로그 포스트가 두 개밖에 없다면 아래 코드로 충분하다. 이렇게 직접 모든 데이터를 코드에 작성하는 것을 하드코딩(hard coding)이라고 부른다.
const posts = [
{ id: 1, title: "Hello World", content: "Welcome to learning React!" },
{ id: 2, title: "Installation", content: "You can install React via npm." },
];
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>
);
}
하지만 김코딩의 블로그 포스트가 100개인 경우, 블로그 포스팅이 늘어날 때마다 매일 코드를 변경해야만 한다. 데이터가 변경될 때마다, 알아서 렌더링하기 위해 React에서는 배열 메서드 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>
);
}
map을 이용한 반복
배열 메서드 map은 (1) 배열의 각 요소를 (2) 특정 논리(함수)에 의해 (3) 다른 요소로 지정(map)하는 것이다.
현재 posts의 요소는 블로그 포스트의 id, title, content로 나눌 수 있다. 이 정보를 브라우저에서 React로 보여주려면 JSX를 활용해서 이 데이터를 적절히 넣어야 한다. 단순한 문자열에 불과했던 posts의 요소를 HTML 엘리먼트로 이 정보의 구조를 잘 짜 놓은 모습으로 매핑하면 되겠다.
반복적으로 작성해야 하는 부분만 골라서 배열의 요소로 넣으면 React가 이를 인지하고 모든 요소를 렌더링한다. 컴포넌트를 재사용성 있게 만들면 단순 반복되는 코드를 간결하게 작성할 수 있다.
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 문 안에서 map 메서드를 사용할 수는 없을까? 사용할 수 있다. JSX를 사용하면 중괄호 안에 모든 표현식을 포함할 수 있기 때문에 map 메서드의 결과를 return문 안에 인라인으로 처리할 수 있다.
(코드 가독성을 위해 변수로 추출할지 아니면 인라인에 넣을지는 개발자가 판단해야 할 몫)
map을 사용할 수 있는 다~~~~~양한 방법
무조건 정리해야만 속이 시원했냐!!!!!!!!!!!!!!!!!!
그렇다.. 시원하다..
구현하고자 하는 것 (key는 편의상 생략) | |
<div> <p> JavaScript </p> <p> HTML </p> <p> Python </p> </div> |
|
let langs = ["JavaScript", "HTML", "Python"]; let viewLangs = langs.map ((it) => { return <p>{it}</p>; } ); return ( <div> {viewLangs} </div> ); |
let langs = ["JavaScript", "HTML", "Python"]; return ( <div> { langs.map((it) => { return <p>{it}</p>; }) } </div> ); |
map 함수 호출 결과를 viewLangs 변수에 잘 담아주었다. 1. map 안에서 리턴 2. 바깥에서 변수에 넣어서 리턴 |
중괄호{}를 사용하여 JavaScript를 내부에 표현해 주었기 때문에 올바르게 작동한다. 주의할 점은 화살표함수에서 중괄호{}를 쓰게 되면 꼭 return 이 존재해야 한다. return 이 없다면 undefined 를 반환하게 된다. 1. map 안에서 리턴 2. 전체 리턴 |
let langs = ["JavaScript", "HTML", "Python"]; return ( <div> { langs.map((it) => ( <p>{it}</p> )) } </div> ); |
let langs = ["JavaScript", "HTML", "Python"]; let viewLangs = () => { return langs.map((it) => { return <p>{it}</p>; }); }; return ( <div> { viewLangs() } </div> ); |
화살표함수에서 () 를 사용하면 () 안의 값이 return 값이 된다. 1. 변수 없이 map을 바로 넣어서 전체 리턴 |
viewLangs 가 화살표 함수 표현식으로 선언되어 있기 때문에 함수를 호출하는 연산자 () 를 써야 작동한다. {viewLangs()} 1. map 안에서 리턴 2. viewLangs 함수 안에서 호출되면 리턴 3. 호출된 viewLangs 함수를 담고 전체 리턴 |
** 다른 예시 export default function App() { const postToJSX = (post) => { return ( <div key={post.id}> <h3>{post.title}</h3> </div> ); }; return ( <div className="App"> <h1>Hello JSX</h1> {posts.map(postToJSX)} </div> ); } |
** 다른 예시 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>; } |
1. 일단 map 함수를 만들고 return 2. 전체 리턴 직전에 posts 배열을 적용해서 같이 리턴 |
1. 일단 map 함수를 만들고 2. 그 함수를 posts 배열을 적용해서 blogs 변수에 할당 3. 그뒤 blogs 변수를 넣어서 전체 리턴 |
** 다른 예시
{ dummyTweets.map((tweet) => { return (
<li className="tweet" key={tweet.id}>
<div className="tweet__profile">
<img src = {tweet.picture}></img></div>
<div className="tweet__userInfo">
<span className={tweetUserNameClass}>{tweet.username}</span>
</div>
<div className='tweet__message'>{tweet.content}</div>
</div>
</li>
);
})}
|
** 다른 예시
</div classname="tweet__profile">
</img src={tweet.picture} > </div classname="tweet__content">
</div classname="tweet__userinfo">
. . . </li> ); |
1. 통채로 리턴
|
1. 리턴문 안에서 {} 안에 map 함수 구현 * 화살표 함수이기에 return 과 {}를 빼도 된다. * () 도 빼도 된다... !!! |
함수 컴포넌트를 만드는 다양한 방법
const Hello = () => { return ( <div> 안녕하세요 </div> ) } |
const Hello = () => { const name = 'na' return ( <div> 안녕하세요 {name}입니다.
) } |
const Hello = () => { return ( [<div> 안녕하세요 </div>,
<div> 반갑습니다. </div>] ) } |
변수에 담길 수 있는 하나의 JSX 표현식이기 때문에 감싸는 태그가 존재하지 않아도 된다. | 중괄호를 사용하는 문법 코드이다. 변수에 할당한 값을 {}에 넣어줬기 때문에 올바른 문법이다. | 배열의 각 요소는 각각 변수에 담길 수 있는 하나의 JSX 표현식이다. |
const Hello = () => { return ( ) } |
틀린 사용법이다. 두 개 이상의 엘리먼트가 존재하기 때문에 전체를 감싸주는 태그가 있어야 한다. |
'FE > React' 카테고리의 다른 글
[React] React Router로 간단한 메뉴 페이지 구현하기 (0) | 2023.03.23 |
---|---|
[React] React SPA (0) | 2023.03.23 |
[React] React Twitter Intro - 컴포넌트 기반 개발하기 (0) | 2023.03.23 |
[React] 컴포넌트 기반 개발의 중요성 (0) | 2023.03.22 |
[React] create-react-app 활용하여 간단한 React 앱 만들기 (0) | 2023.03.22 |