티스토리 뷰

React

[React] 댓글 CRUD 구현

ljy98 2022. 4. 22. 23:54

오늘은 React로 댓글 CRUD 기능을 구현해 보고자 한다. DB는 따로 연결하지 않고 임시 데이터를 이용하였다.

 

[그림 1] 브라우저에 나타나는 댓글의 모습

첫 번째 댓글은 원래 있던 댓글을 불러 온 것이고, 두 번째와 세 번째 댓글은 새로 작성한 것이다.

 

'X' 버튼을 클릭하면 삭제되고, 댓글의 내용을 클릭하면 수정할 수 있게 바뀐다. 

 

전체 컴포넌트의 구성은 App 컴포넌트 하위에 Comment 컴포넌트가 있고, Comment 컴포넌트 하위에 CommentForm과 CommentList 컴포넌트들이 있다. CommentForm은 입력하는 공간이고, CommentList는 작성한 댓글이 나타나는 공간이다.

 

이제 세부적인 코드를 살펴보고자 한다.

 

아래는 App.jsx 파일의 코드이다.

import React, {Component} from 'react';
import Comment from './components/comment/Comment';
import CommentForm from './components/comment/CommentForm';
import CommentList from './components/comment/CommentList';

class App extends Component{
    state = {
        list:[]
    };
    componentDidMount() {
        this.setState({
            ...this.state,
            list:[{userid: 'jenny1', content: '안녕하세요1', date: '2022-04-21', updateFlag: true}]
        });
    }
    addList = obj => {
        this.setState({
            ...this.state,
            list:[...this.state.list, obj]
        });
    };
    updateList = list => {
        this.setState({
            ...this.state,
            list
        });
    };
    render() {
        const {list} = this.state;
        return(
            <>
                <Comment value={this.state.value}>
                    <CommentForm addList={this.addList}/>
                    <CommentList list={list} updateList={this.updateList}/>
                </Comment>
            </>
        )
    }
};

export default App;

state에는 list라는 속성을 넣어준다. 이제 list의 속성 값이 어떻게 변하느냐에 따라 페이지에 나타나는 모습이 변화할 것이다.

 

CRUD 기능 중 Read를 구현하기 위해 componentDidMount()를 이용하였다. 이제 페이지가 최초로 렌더되었을 때 userid가 'jenny1'이고, content가 '안녕하세요1'인 게시글이 나타난다.

 

CRUD 기능 중 Create를 구현하기 위해 addList 함수를 선언하였다. addList 함수가 실행되면, state의 list에 원래 있던 내용에 새로운 obj가 추가될 것이다. 이는 CommentForm 컴포넌트에서 필요하기 때문에 <CommentForm/> 태그 안에 넣어준다.

 

CRUD 기능 중 Update를 구현하기 위해 updateList 함수를 선언하였다. updateList 함수가 실행되면, state의 list가 변수 list에 담긴 값으로 바뀐다. 이는 CommentList 컴포넌트에서 필요하기 때문에 <CommentList/> 태그 안에 넣어준다.

 

 

아래는 Comment.jsx 파일의 코드이다.

import React, {Component} from 'react';
import CommentForm from './CommentForm';
import CommentList from './CommentList';
import '../../assets/comment.css';

class Comment extends Component{
    render() {
        return(
            <ul className='comment'>
                {this.props.children}
            </ul>
        )
    }
};

export default Comment;

<ul> 태그 안에 this.props.children을 넣어준다. this.props.children은 위의 App 컴포넌트에서 적었던 <Comment>와 </Comment> 안에 있는 모든 내용을 말한다.

 

 

아래는 CommentForm.jsx 파일의 코드이다.

import React, {Component} from 'react';

class CommentForm extends Component{
    state = {
        value: ''
    };
    handleChange = e => {
        const {value} = {...e.target};
        this.setState({
            ...this.state,
            value
        });
    };
    handleSubmit = e => {
        e.preventDefault();
        const obj = {userid: 'jenny', content: this.state.value, date: '2022-04-25', updateFlag: true};
        this.props.addList(obj);
        this.setState({
            ...this.state,
            value: ''
        });
    };
    render() {
        return(
            <li className='comment-form'>
                <form onSubmit={this.handleSubmit}>
                    <span className='ps_box'>
                        <input 
                            type="text" 
                            className='int'
                            placeholder='댓글을 입력해 주세요.'
                            onChange={this.handleChange}
                            value={this.state.value}
                        />
                    </span>
                    <input type="submit" className='btn' value='등록'/>
                </form>
            </li>
        )
    }
};

export default CommentForm;

state에는 value라는 속성을 ''로 지정하였다.

 

handleChange 함수는 댓글을 입력 중일 때 입력한 값이 바뀔 때 마다 input 태그의 value 값을 그에 맞게 바꾸어 주기 위해 필요하다.

 

handleSubmit 함수는 댓글 작성을 마치고 '전송' 버튼을 클릭했을 때 댓글 리스트에 작성한 댓글을 추가하기 위해 만들어 주었다. 댓글 작성 버튼을 클릭한 후에 state의 value 값은 다시 ''로 바뀌어 새로운 댓글을 작성할 수 있는 상태가 된다.

 

 

아래는 CommentList.jsx 파일의 코드이다.

import React, {Component} from 'react';

class CommentList extends Component{
    state = {
        value: '',
        update: null
    };
    input = React.createRef();
    handleClick = i => e => {
        this.setState({
            ...this.state,
            value: e.target.innerHTML,
            update: i
        });
    };
    updateChange = e => {
        const {value} = {...e.target};
        this.setState({
            ...this.state,
            value
        });
    };
    updateKeyDown = i => e => {
        if (e.key !== 'Enter') return;
        const {updateList, list} = this.props;
        const newList = [...list];
        newList[i].content = this.state.value;
        this.setState({
            ...this.state,
            update: null
        });
        updateList(newList);
    };
    deleteClick = k => {
        const {updateList, list} = this.props;
        const newList = [...list].filter((v, i) => i !== k);
        updateList(newList);
    };
    items = () => this.props.list.map((v, k) => {
        return(
            <ul className='comment-row'>
                <li className='comment-id'>{v.userid}</li>
                <li className='comment-content'>
                    {
                        this.state.update === k ?
                        <input
                            type='text'
                            className='comment-update-input'
                            onChange={this.updateChange}
                            onKeyDown={this.updateKeyDown(k)}
                            value={this.state.value}
                            ref={this.input}
                        />
                        : (<>
                            <span onClick={this.handleClick(k)}>{v.content}</span>
                            <span className='comment-delete-btn' onClick={() => this.deleteClick(k)}>X</span>
                        </>)
                    }
                </li>
                <li className='comment-date'>{v.date}</li>
            </ul>
        )
    });
    render() {
        return(
            <li>{this.items()}</li>
        )
    }
};

export default CommentList;

state에는 value와 update 속성이 있다.

 

Ref는 React.createRef() 라는 React 내장 함수를 통해 생성되고, React 엘리먼트의 Ref attribute를 통해 바인딩한다. Ref는 render() 메소드에서 생성된 DOM 노드 혹은 React Element에 접근하는 방법을 제공한다.

 

React의 일반적인 Data Flow에서 부모와 자식 컴포넌트가 interaction 할 수 있는 유일한 수단은 props이다.

(자식 컴포넌트를 수정하는 방법 : 새로운 props를 전달 → 자식 컴포넌트를 새롭게 렌더링)

 

하지만, 일반적인 Data Flow가 아니라 자식 컴포넌트를 직접 수정해야 할 때가 있다.

  • Focus, 텍스트 선택 영역 혹은 미디어의 재생을 관리할 때
  • Animation을 직접적으로 실행시킬 때
  • React에서 서드 파티 DOM 라이브러리를 같이 사용할 때

render() 메소드 안에서 ref가 엘리먼트에게 전달되었을 때, 해당 노드를 가리키는 참조는 생성된 ref 객체의 current 프로퍼티에 저장된다. ref에 대해서는 나중에 더 자세하게 다루겠다.

 

handleClick 함수는 수정하고 싶은 댓글을 클릭했을 때 실행된다. 이 때 value 값은 수정된 내용으로 바뀌고, update는 null에서 해당 댓글의 i 값으로 바뀐다.

 

updateChange 함수는 CommentForm.jsx의 handleChange 함수와 같은 역할을 한다. 즉, 댓글을 수정하는 중일 때 그 input 태그 안의 내용이 현재 작성 중인 내용으로 실시간으로 바뀐다.

 

updateKeyDown 함수는 댓글 수정이 끝난 후 Enter 키를 눌러서 확정하고 싶을 때 실행된다. Enter 키가 아닌 다른 키를 눌렀을 때에는 실행되지 않고, 오직 Enter 키를 눌렀을 때에만 updateList 함수가 실행되며 state의 update 값은 다시 null로 바뀐다.

 

deleteClick 함수는 댓글을 삭제하고자 할 때 (댓글 내용 우측의 'X'를 클릭했을 때) 실행된다.

 

items 함수는 this.props.list의 각각의 부분을 map 함수를 써서 댓글 하나하나로 mapping 해준다. this.state.update 값이 k일 때와 그렇지 않을 때를 삼항연산자를 써서 다르게 처리하였다. true일 때는 댓글을 수정 중이라는 뜻이고, input 태그에 onChange와 onKeyDown을 써준다. false일 때는 댓글을 수정하는 중이 아니라는 뜻이고, 원래 댓글의 내용과 삭제가 가능한 'X' 표시가 함께 나타난다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함