13-3. 덧글 창 구성하기
이번엔 덧글 창을 열고 닫는 기능을 구현해보겠습니다. 포스트 하단의 덧글 아이콘을 누르면, 덧글 창이 열리고, 다시 누르면 닫히는 구조입니다. 덧글 창은, 따로 컨테이너 컴포넌트를 만들고, 그 다음에 해당 컨테이너 컴포넌트를 각 Post 컴포넌트에서 불러와서 렌더링 하게 되는데요, 그 이유는, 덧글의 상태와 포스트 데이터를 함께 다루게 되면, 덧글의 상태가 변경 될 때마다, (예: 인풋 입력) PostList 컴포넌트가 리렌더링이 되어 버벅거림이 발생 할 수 있습니다.
이 과정에서 Post 컴포넌트에서 shouldComponentUpdate 를 통하여, 불필요한 렌더링을 막게 되면, 버벅거림은 조금 해소되겠지만, 업데이트가 불필요한 컴포넌트들의 render 함수 실행은 방지 되더라도, PostList 컴포넌트에서 포스트 데이터가 포스트 컴포넌트 배열로 map 되는 과정은 계속 이뤄지기 때문에, 보여지는 포스트의 갯수가 많아지면 미세한 버벅거림이 계속해서 발생하게 됩니다.
추가적으로, 덧글 창을 구현하기 위해 만들어야 하는 메소드들은 3개가 있습니다 (handleChange, handleKeyPress - 엔터 키 감지를 위함, comment)
하지만, 컨테이너를 따로 만들어주면, 덧글에 관련된 상태가 업데이트 될 때, PostList 는 가만히 있고, 덧글을 위한 컨테이너 컴포넌트만 업데이트 되기 때문에 불필요한 렌더링을 많이 아낄 수 있게되죠.
만약에 덧글창을 위한 컨테이너를 따로 만들지 않으면 위 메소드들도 PostListContainer 에 정의를 해줘야되는데, 그렇게 하면 해당 컴포넌트의 소스코드가 너무 복잡해지게 됩니다.
덧글창을 위한 컨테이너를 만들면 이 때 필요한 메소드들과, 상태 연결을 따로 해줄 수 있으니, 가독성도 높아지고, 유지보수도 하기 쉬워지겠죠.
다른 방법으로는, 인풋의 상태를 다룰 때 컴포넌트의 로컬 state 를 사용하는 방법입니다. 인풋의 상태를, 덧글창 밖에서 핸들링 해야되는 경우는 거의 없기 때문에, 이 또한 괜찮은 방법입니다. 덧글 작성하는 로직을 PostListContainer 에서 준비를 해줘야 하긴 하겠지만, 나머지 이벤트 핸들러들은 덧글창을 위한 컴포넌트 내부에서 구현해주면 되기 때문에 큰 단점은 없습니다.
위와 같은 상황에서, 상태를 리덕스에 넣을지, 로컬 state 에 넣을지 결정하는것은 여러분의 몫입니다. 중요한점은, 불필요한 렌더링을 방지하는 것 입니다.
이 강의에선, 프리젠테이셔널 컴포넌트의 내부에서 컨테이너 컴포넌트를 사용하는 방법을 알아보기위해, 전자의 방법을 선택하도록 하겠습니다.
CommentBlock 컴포넌트 만들기
덧글창을 띄워주는 CommentBlock 컴포넌트를 만들어보겠습니다. 이 컴포넌트는, 회색 배경에, 인풋과 덧글 목록을 보여줍니다.
지금은 우선 인풋만 설정을 하도록 하겠습니다.
src/components/Shared/CommentBlock.js
import React from 'react';
import styled from 'styled-components';
import oc from 'open-color';
const Wrapper = styled.div`
background: ${oc.gray[0]};
`;
// 인풋을 감싸줍니다
const InputWrapper = styled.div`
padding: 0.75rem;
`;
// 기본 스타일이 무효화되고, 밑줄이 그어진 인풋, 포커스 됐을땐 밑줄 색상 변경
const Input = styled.input`
display: block;
background: none;
outline: none;
border: none;
font-size: 0.8rem;
width: 100%;
padding-bottom: 0.25rem;
border-bottom: 1px solid ${oc.gray[5]};
&:focus {
border-bottom: 1px solid ${oc.cyan[4]};
}
::placeholder {
text-align: center;
color: ${oc.gray[5]};
}
`
const CommentBlock = ({onChange, onKeyPress, value}) => (
<Wrapper>
<InputWrapper>
<Input
value={value}
onChange={onChange}
onKeyPress={onKeyPress}
placeholder="덧글을 입력 후 [Enter] 를 눌러 작성하세요"/>
</InputWrapper>
</Wrapper>
);
export default CommentBlock;
CommentBlockContainer 만들기
위 컴포넌트에 대한 컨테이너 컴포넌트를 만들고, Post 컴포넌트에서 불러와서 렌더링을 하겠습니다. 아직 리덕스 상태관리 설정을 하지 않은 상태이니, 임시적으로 덧글창이 언제든지 보여지도록 하겠습니다.
src/containers/Shared/PostList/CommentBlockContainer.js
import React, { Component } from 'react';
import CommentBlock from 'components/Shared/PostList/CommentBlock';
class CommentBlockContainer extends Component {
render() {
return (
<CommentBlock/>
);
}
}
export default CommentBlockContainer;
src/components/Shared/PostList/Post.js.
(...)
import CommentBlockContainer from 'containers/Shared/PostList/CommentBlockContainer';
(...)
const Post = ({post, onToggleLike}) =>{
(...)
return (
<Wrapper>
<PostHead>
<UserThumbnail image={`/api/users/${username}/thumbnail`}/>
<Username>{username}</Username>
<Count>#{count}번째 생각</Count>
<Time><TimeAgo date={createdAt} formatter={formatter}/></Time>
</PostHead>
<Content>
{content}
</Content>
<PostFooter likesCount={likesCount} liked={liked} onToggleLike={toggleLike}/>
<CommentBlockContainer/>
</Wrapper>
)
}
export default Post;
여기까지 진행을 하고 나면, 포스트에서 덧글 인풋을 볼 수 있게 됩니다.