React 게시글은 대부분 인프런의 '한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지' 강의를 기반으로 내용을 정리했습니다.
React에서 리스트 사용하기 1 - 리스트 렌더링 (조회)
바로 전에 만든 다이어리는 저장 버튼은 있지만 실제로 DB에 저장하거나 하진 않았다. 이번에는 배열을 이용하여 React에서 LIST를 렌더링하고 개별적인 컴포넌트로 만들어보자. (일반적으로 리액트를 사용하면 배열로 피드, 게시글, 리스트를 표시하는데 자주 사용한다.)
저장된 리스트를 보여줄 DiaryList.js를 생성한다.
const DiaryList = () => {
return (
<div className="DiaryList">
<h2>일기 리스트</h2>
</div>
)
};
export default DiaryList;
App.js
import './App.css';
import DiaryEditor from './DiaryEditor';
import DiaryList from './DiaryList'; // 추가
function App() {
return (
<div className="App">
<DiaryEditor/>
<DiaryList/> <!--추가-->
</div>
);
};
export default App;
일단 바로 일기를 저장하고 배열을 렌더링 하는 코드를 작성하면 이해하기 어려울 수 있기 때문에 임시 배열로 렌더링을 해보자.
App.js
import "./App.css";
import DiaryEditor from "./DiaryEditor";
import DiaryList from "./DiaryList";
const dummyList = [
{
id: 1,
author: "레호",
content: "안녕1",
emotion: 5,
created_date: new Date().getTime(), // 현재 시간을 밀리세컨즈로 반환
},
{
id: 2,
author: "홍길동",
content: "안녕2",
emotion: 3,
created_date: new Date().getTime(),
},
{
id: 3,
author: "유관순",
content: "안녕3",
emotion: 1,
created_date: new Date().getTime(),
},
];
function App() {
return (
<div className="App">
<DiaryEditor />
<DiaryList diaryList={dummyList} /> <!-- 생성한 임시 배열을 prop으로 전달-->
</div>
);
}
export default App;
DiaryList에서 prop을 전달받기 위해 코드를 작성한다.
DiaryList.js
const DiaryList = ({ diaryList }) => { // diaryList prop 받기
console.log(diaryList); // prop을 잘 받았는지 출력
return (
<div className="DiaryList">
<h2>일기 리스트</h2>
<h4>{diaryList.length}개의 일기가 있습니다.</h4>
</div>
);
};
export default DiaryList;
자 이제 List를 이용한 렌더링 작업을 해보자.
const DiaryList = ({ diaryList }) => {
console.log(diaryList);
return (
<div className="DiaryList">
<h2>일기 리스트</h2>
<h4>{diaryList.length}개의 일기가 있습니다.</h4>
<div>
{diaryList.map((it)=> (
<div>일기 아이템</div>
))}
</div>
</div>
);
};
export default DiaryList;
map의 순회를 이용해 diaryList 안의 요소 개수만큼 <div>일기 아이템</div>을 만든다.
일기 아이템이란 글자가 아닌 prop에서 넘겨받은 데이터들을 이용해 리스트를 구성한다.
const DiaryList = ({ diaryList }) => {
console.log(diaryList);
return (
<div className="DiaryList">
<h2>일기 리스트</h2>
<h4>{diaryList.length}개의 일기가 있습니다.</h4>
<div>
{diaryList.map((it) => (
<div>
<div>작성자 : {it.author}</div>
<div>일기 : {it.content}</div>
<div>감정 : {it.emotion}</div>
<div>작성 시간(ms) : {it.created_date}</div>
</div>
))}
</div>
</div>
);
};
export default DiaryList;
만약에 diaryList가 undefiend일 때는 제대로 동작할까?
App.js
<DiaryList diaryList={undefined} />
App.js에서 diaryList prop 데이터를 undefined로 보내면 error가 발생한다.
에러 내용은 undefined는 length를 쓸 수 없어 발생하는 에러이다. 이런 현상을 저번에 배운 defaultProps로 해결해보자.
DiaryList.js
DiaryList.defaultProps = {
diaryList: [],
};
이제 다시 App.js에서 undefined로 변경한 값을 dummyList로 변경해주자.
key Warning 해결하기
개발자 도구를 열어보면 자꾸 콘솔에 저런 빨간 경고 문구가 뜬다. 보면 unique한 key prop이 없다고 알려주는데 이 섬뜩한 경고문을 없애보자.
DiaryList.js
{diaryList.map((it) => (
<div key={it.id}>
<div>작성자 : {it.author}</div>
<div>일기 : {it.content}</div>
<div>감정 : {it.emotion}</div>
<div>작성 시간(ms) : {it.created_date}</div>
</div>
))}
코드 저장 후 페이지를 새로고침해서 보면 경고 문구가 사라진 걸 확인할 수 있다.
만약에 데이터에 고유한 id가 없을 경우에는 map 콜백 함수의 두 번째 파라미터인 index를 사용하면 된다.
{diaryList.map((it, idx) => (
<div key={idx}>
<div>작성자 : {it.author}</div>
<div>일기 : {it.content}</div>
<div>감정 : {it.emotion}</div>
<div>작성 시간(ms) : {it.created_date}</div>
</div>
))}
그런데 index를 key로 설정하면 만약 배열의 순서가 삭제나 수정으로 인해 변경되었을 때 react에서 문제가 생길 수 있다. 그러니 되도록 데이터에 고유한 아이디가 있는 경우에는 그 아이디로 key를 설정해주는 게 더 현명하다.
리스트의 경우 추후 수정, 삭제 등의 기능까지 추가할건데 그 코드를 DiaryList.js에 작성하면 매우 좋지 않은 방법이며 에러가 발생할 확률 또한 높아지기 때문에 일기 리스트 아이템은 별도의 컴포넌트로 분할하자.
DiaryItem.js를 생성한다.
item 컴포넌트에 prop을 넘겨주기 위해 코드를 수정한다.
DiaryList.js
import DiaryItem from "./DiaryItem";
const DiaryList = ({ diaryList }) => {
console.log(diaryList);
return (
<div className="DiaryList">
<h2>일기 리스트</h2>
<h4>{diaryList.length}개의 일기가 있습니다.</h4>
<div>
{diaryList.map((it) => (
<DiaryItem key={it.id} {...it} />
))}
</div>
</div>
);
};
DiaryList.defaultProps = {
diaryList: [],
};
export default DiaryList;
DiaryItem.js
const DiaryItem = ({ author, content, created_date, emotion, id }) => {
return (
<div className="DiaryItem">
<div className="info">
<span>
작성자 : {author} | 감정점수 : {emotion}
</span>
<br />
<span className="date">{new Date(created_date).toLocaleString()}</span>
</div>
<div className="content">{content}</div>
</div>
);
};
export default DiaryItem;
toLocaleString() 메서드의 경우 밀리세컨즈 시간을 사람이 읽기 편한 형태로 변환시켜주는 메서드이다.
이제 CSS를 입혀보자.
.DiaryEditor {
border: 1px solid gray;
text-align: center;
padding: 20px;
}
.DiaryEditor input,
textarea {
margin-bottom: 20px;
width: 500px;
padding: 10px;
}
.DiaryEditor textarea {
height: 150px;
}
.DiaryEditor select {
width: 300px;
padding: 10px;
margin-bottom: 20px;
}
.DiaryEditor button {
width: 500px;
padding: 10px;
cursor: pointer;
}
/* List */
.DiaryList {
border: 1px solid gary;
padding: 20px;
margin-top: 20px;
}
.DiaryList h2 {
text-align: center;
}
/* Item */
.DiaryItem {
background-color: rgb(240, 240, 240);
margin-bottom: 10px;
padding: 20px;
}
.DiaryItem .info {
border-bottom: 1px solid gray;
padding-bottom: 10px;
margin-bottom: 10px;
}
.DiaryItem .date {
color: gray;
}
.DiaryItem .content {
font-weight: bold;
margin-bottom: 30px;
margin-top: 30px;
}