저번 포스팅에 이어서 이번에는
React를 이용한 간단한 댓글쓰기를 구현해보도록 하겠습니다.
CSS는 참고만 해 주시면 감사하겠습니다!
작업하기전에 저같은 초보자들은 많이 헷갈리니까
관계도.. 트리를 그려서 `상태 끌어올리기` 관계를 잘 확인합니다
React 작업 전 생각해야 할 것들
1. Component 구성
허접하지만.. 그림판으로 그려봤습니다.. 최상위 root 인 App을 시작으로
뻗어나가고있습니다. 이그림을 Tree라고도 하며 처음 접할때는 많이헷갈리니
그림을 그리며 파악해나가는게 좋을듯합니다.
2. 상태 끌어올리기
`상위 컴포넌트의 상태 변경 함수 그 자체를 하위 컴포넌트로 전달하고,
이 함수를 하위 컴포넌트가 실행하는 것이다.
동일한 데이터에 대한 변경사항을 여러 컴포넌트에 반영해야할 필요가 있다.
이땐 가장 가까운 공통 조상(컴포넌트)에서 state를 끌어올리는 것을 권장한다. `
( https://nychicken.tistory.com/9 님 블로그를 참조했습니다 )
위의 Tree로 설명을 드리면 Form과 List는 공통 컴포넌트가 Comment가 있습니다.
이때 Form 컴포넌트에 state 가 변경이 되면 Form -> Comment -> List 로 동일한 state 데이터에
대한 변경사항을 전달하는것입니다. 흔히 Form -> Comment 까지 끌어올리게 되면
List는 Comment를 extends하고 있으므로 반영이 됩니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script
crossorigin
src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"
></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<link rel="stylesheet" href="./comment.css" />
</head>
</html>
1. 우선 React를 실행하기 위해 , script를 통해 React / ReactDOM / Babel CDN을 가져왔고 ,
CSS도 연결시켜주었습니다.
<body>
<div id="root"></div>
<script type="text/babel">
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App />)
</script>
</body>
2. ReactDOM.createRoot() 은 root.. 즉 전체적인 작업공간 설정..이라고 해야될까요?!
해서 공식문서를 까봤는데 영어로 되어있어서.. 번역해왔습니다..
react-dom 패키지는 앱의 최상위 수준에서 사용할 수 있고 필요한 경우
React 모델 외부로 나갈 수 있는 탈출구로 사용할 수 있는
DOM 관련 메서드를 제공합니다.
밑에 글들도 더 있지만 대충 요약해보면 최상위 root를 설정하여서 React의 메서드를 제공받는것 같습니다
<div id="root"></div> 를 최상위 root로 설정해놓고
root.render(<App />)을 통해 class App 을 생성해줍니다!
class App extends React.Component {
render() {
return (
<div>
<Comment />
</div>
)
}
}
3. Class App을 생성하여 React.Component를 상속 받았습니다.
return 으로 <div> <Comment /> </div> 를 하며 여기서 쓰인것은 HTML이 아닌
JSX 문법입니다.
class Comment extends React.Component {
constructor(props) {
super(props)
this.state = {
comment: [
{
userid: 'ambious12',
content: '안녕하세욤',
date: '2023-02-22',
},
{
userid: 'ambious123',
content: '안녕하세욤2',
date: '2023-02-22',
},
{
userid: 'ambious1234',
content: '안녕하세욤3',
date: '2023-02-22',
},
],
}
this.create = this.create.bind(this)
}
create(content) {
this.setState({
comment: [
{ userid: 'ambious12', content, date: '2023-02-22' },
...this.state.comment,
],
})
}
render() {
return (
<ul className="comment">
<CommentForm create={this.create} length={this.state.comment.length} />
<CommentList items={this.state.comment} />
</ul>
)
}
}
4. class Comment를 생성하였습니다. Tree에서 볼수있다싶이 Comment는
Form과 List를 그려줍니다.
constructor(props){
super(props)
this.state = {
comment :[
{},
{},
{},
]
}
}
- constructor()를 통해 Component의 초기설정을 해주었습니다.
- Comment의 초기상태에(this.state) comment를 배열안에 객체형태로 3개를 넣어주었습니다.
그리고 render()를 하게되는데
이때 ul 태그 안에 Form과 List를 넣어주었고 , Form안에는 input box를 넣어 댓글을 생성할것입니다.
create() 메서드 안에서 상태가 변경될때에 정의를 해주고있습니다.
● 위의 코드에서 주의 해야 할 사항 ●
1. return 으로 <CommentForm create={this.create} length={this.state.comment.length} />를 던져줄때
" create= {} / length = {} " 는 CommentForm으로 던지는 데이터입니다. 이렇게 데이터를 전달하면
CommentForm에서 this.props.create 혹은 this.props.length를 통해 상위 컴포넌트에서 데이터를
가져다 쓸수 있습니다.
2. 우리는 CSS에서 엘리먼트에 class를 줄때에 <ul class="comment"> 이렇게 주었을것입니다.
하지만 React에서는 class 라는 예약어가 존재하기때문에 class 대신 className을 사용하여
class를 주었습니다 " <ul className="comment">"
3. 메서드 바운딩처리! 이부분은 딥하게 들어가면 헷갈린다고.. 하는데 저는 속편하게
컴포넌트 안에서 메서드를 사용하게된다면, 그 컴포넌트의 costructor에서
" this.create = this.create.bind(this) "
를 사용하여 create.bind(this)처리를 하려고합니다.
※ 바운딩하지않으면 에러가 발생합니다 ※
바운딩 처리를 하지 않으실거면 해당 메서드를(create) 화살표 함수로 처리하는것도 하나의 방법입니다.
class CommentForm extends React.Component {
constructor(props) {
super(props)
this.submitHandler = this.submitHandler.bind(this)
this.state = {
value: '',
}
this.changeHandler = this.changeHandler.bind(this)
}
changeHandler(e) {
const { value } = e.target
this.setState({
...this.state,
value,
})
}
submitHandler(e) {
e.preventDefault()
this.props.create(this.state.value)
this.setState({ value: '' })
}
render() {
return (
<li className="comment-form">
<form onSubmit={this.submitHandler}>
<h4>
댓글쓰기 <span>({this.props.length})</span>
</h4>
<span className="ps_box">
<input
type="text"
onChange={this.changeHandler}
className="int"
value={this.state.value}
placeholder="댓글내용 입력해주세요"
/>
</span>
<input type="submit" value="등록" className="btn" />
</form>
</li>
)
}
}
5. CommentForm class를 생성하였습니다.
constructor에서 super를 통해 상속을 받고 , 해당 컴포넌트에서 사용할 메서드들을
bind 처리 하였습니다.
※ 지금 저희는 ul 태그 안에 CommentForm을 넣은것이고 , 안에서 li 태그 , form태그 , input 태그를 통해
실질적으로 댓글을 입력받고 , submit하는 CommentForm을 만들것입니다 ※
위에서 다 설명을 하였지만 CommentForm 에서 잘 보셔야 할것들은
1. 엘리먼트에 class를 줄때 className으로 주었다.
2. 메서드들을 bind 처리하였다.
3. input box에서 onChange를 사용하여 changeHandler() 메서드를 호출하였고 state상태를 계속해서
this.setState()하여 현재 상태를 랜더링하였습니다.
공식문서에서는 이를 ` 제어 컴포넌트 (Controlled Component) ` 라고 작성하였습니다.
제어 컴포넌트 (Controlled Component)
HTML에서 <input>, <textarea>, <select>와 같은 폼 엘리먼트는 일반적으로
사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트합니다.
React에서는 변경할 수 있는 state가 일반적으로 컴포넌트의 state 속성에 유지되며
setState()에 의해 업데이트됩니다.
우리는 React state를 “신뢰 가능한 단일 출처 (single source of truth)“로 만들어
두 요소를 결합할 수 있습니다. 그러면 폼을 렌더링하는 React 컴포넌트는 폼에 발생하는 사용자 입력값을 제어합니다.
이러한 방식으로 React에 의해 값이 제어되는 입력 폼 엘리먼트를
“제어 컴포넌트 (controlled component)“라고 합니다.
공식문서에서는 이렇게 정의하고있습니다.
6. CommentList와 CommentItem 를 보겠습니다. ( 계속해서 Tree를 잘 기억하며 보세요 초보자인 저는 너무 헷갈려서..ㅎ)
class CommentList extends React.Component {
loop(v, k) {
return (
<CommentItem
key={k}
userid="ambious12"
content={v.content}
date="2023-02-22"
/>
)
}
render() {
return <li>{this.props.items.map(this.loop)}</li>
}
}
class CommentItem extends React.Component {
render() {
return (
<ul className="comment-row">
<li className="comment-id">{this.props.userid}</li>
<li className="comment-content">{this.props.content}</li>
<li className="comment-date">{this.props.date}</li>
</ul>
)
}
}
CommentList에서는
- this.props.items(Comment에서 JSX문법으로 생성된 댓글들을 items라는 변수로 데이터를 전달했습니다. )
- items(댓글들)을 배열메서드 map을 사용하기위해 loop메서드를 따로 구현했습니다.
- loop 메서드는 value 와 key 값을 받으며 CommentItem (댓글 하나가 생성되는 창) 에 데이터
userid , content, date를 전달합니다.
※ key={k}는 Component에서 map 메서드를 사용하려면 적어주어야 합니다.
안적어도 작업이 진행이 되긴 하지만 console창에 에러가 발생합니다.. 자잘한 에러들은
없는게 최고니까요 ㅎㅎ
CommentItem class에서는 ul 태그와 li태그로 상속받은 부모 컴포넌트에서 받은 데이터들을 뿌려주기만 하면
됩니다.
CSS 코드입니다. 참고만 해주세요
* {
margin: 0;
padding: 0;
}
body {
font-family: 'Noto Sans KR', sans-serif;
font-weight: 300;
}
ul,
li {
list-style: none;
}
.comment {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
padding: 30px;
width: 600px;
margin: 0 auto;
}
.comment > li {
margin-top: 20px;
}
.comment > li:nth-child(1) {
margin: 0px;
}
.comment-row {
display: flex;
justify-content: space-between;
flex-direction: row;
}
.comment-row {
margin-top: 20px;
width: 100%;
}
.comment-row > li:nth-child(2) {
flex-shrink: 0;
flex-grow: 1;
padding-left: 25px;
z-index: 1;
width: 100%;
}
.comment-row > li:nth-child(2) {
width: 85px;
}
.comment-form > form {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
}
.comment-form > form > h4 {
width: 100%;
margin: 14px 0 14px 0;
}
.comment-content {
word-break: break-all;
padding-right: 25px;
}
.ps_box {
display: block;
position: relative;
width: 80%;
height: 51px;
border: solid 1px #dadada;
padding: 10px 14px 10px 14px;
background: #fff;
box-sizing: border-box;
}
.ps_box > input {
outline: none;
}
.int {
display: block;
position: relative;
width: 100%;
height: 29px;
padding-right: 25px;
line-height: 29px;
border: none;
background: #fff;
font-size: 15px;
box-sizing: border-box;
z-index: 10;
}
.btn {
width: 18%;
padding: 18px 0 16px;
text-align: center;
box-sizing: border-box;
text-decoration: none;
border: none;
background: #333;
color: #fff;
font-size: 14px;
}
.comment-delete-btn {
display: inline-block;
margin-left: 7px;
cursor: pointer;
}
.comment-update-input {
border: none;
border-bottom: 1px solid #333;
font-size: 16px;
color: #666;
outline: none;
}
구현된 화면입니다.
이렇게 React로 댓글을 구현해보았습니다.
Javascript으로 작업했을때보다 코드량이 현저하게
줄어든게 눈에 보입니다. 하지만 처음 Component 구성과
그 구성된것에서 관계가 참 힘듭니다.
구글링해보면 Component를 블록조립으로 말씀하시던데
그냥 많이해봐야 할것같습니다...
감사합니다!
'React' 카테고리의 다른 글
[React] Webpack에대해.. (0) | 2023.03.02 |
---|---|
[React] Babel에 대해서 (0) | 2023.02.27 |
[React] 공식문서의 주요개념 간단한 설명 (1) | 2023.02.23 |
[React] React의 사용방법 간단예제 (2) (0) | 2023.02.22 |
[React] React 란? 사용방법 소개 (1) (1) | 2023.02.21 |