여러 프로젝트들에 공통으로 들어가는 컴포넌트들이 있는 폴더
📌 사용 사례
board 같은 곳에 글을 쓸 때 글쓰는 곳을 조금 더 예쁘게 꾸며주는 CKEditor와,
날짜를 선택하는 DatePicker , 글이 많을 때 페이지 번호를 매기는 Pagination,
Daum 우편번호 검색 서비스를 제공하는 postcode 등은 한 프로젝트에서만 적용되는 것이 아니라
공통적으로 여러 프로젝트에서 사용되어지는 것들이므로 common 폴더에 담긴다.
👉 container는 Smart component 라고도 불리는데,
redux와 소통하며 앱의 상태(redux state)를 제어
하는 역할.
redux와 연결하고, redux로부터 받은 상태 데이터를 component로 전달해주면 됨.
(Redux의 store 접근은 오직 container를 통해서만 이루어짐.)우리 프로젝트에서는 각 화면의 큰 틀이 담기는 느낌
📌 container 사용 사례
파일 : sighty_project/admin/src/container/admin/admin/index.jsx
→ 관리자 페이지 중 메인 페이지의 가장 하단만 첨부
export default connect((state) => {
return {
eSESSION: state.data.eSESSION.eSESSION,
eCOLUMN: state.data.eCOLUMN.eCOLUMN,
};
})(withRouter(index));
파일 : sighty_project/admin/src/container/board/faqManage/index.jsx
→ 게시판 관리의 faq 수정 화면
const index = (props) => {
const {dispatch, history, eSESSION} = props;
const imageUrl = Const.getImageUrl();
const [detail, setDetail] = useState({
faq_sj: '',
faq_cont: '',
faq_ctgry_no: '',
public_yn: 'Y',
});
const [categoryList, setCategoryList] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const faq_no = Const.getParameterByName('faq_no');
const checkValidation = [
{field: 'faq_sj', msg: '제목을 입력해주세요.'},
{field: 'faq_cont', msg: '내용을 입력해주세요.'},
{field: 'faq_ctgry_no', msg: '카테고리를 선택해주세요.'},
];
useEffect(() => {
getCategoryList();
}, []);
useEffect(() => {
if (faq_no) {
getDetail();
} else {
setIsLoading(false);
}
}, [faq_no]);
const getCategoryList = () => {
let params = {
skip: 0,
limit: 20
};
dispatch(ActionFaq.getCategoryList(params)).then((res) => {
setCategoryList(res.list);
}).catch((err) => {
let errorMsg = err.response.data.msg || err.response.data.error.message;
alert(errorMsg);
})
};
const getDetail = () => {
dispatch(ActionFaq.getDetail({faq_no: faq_no})).then((res) => {
console.log(res)
setDetail(res.detail);
setIsLoading(false);
}).catch((err) => {
console.log(err);
setIsLoading(false);
});
};
const handleStateValue = (field, value, index) => {
let newState = JSON.parse(JSON.stringify(detail));
if (field === 'duration') {
if (index == 1 && dateUtil.getLeftDaysFrom2(newState[field][0], value) < 0) {
alert('시작일 이후 날짜를 선택해주세요.');
return;
}
if (index == 0 && dateUtil.getLeftDaysFrom2(value, newState[field][1]) < 0) {
alert('종료일 이전 날짜를 선택해주세요.');
return;
}
if (dateUtil.getLeftDaysFrom2(new Date(), value) < 0) {
alert('오늘 이후 날짜를 선택해주세요.');
return;
}
newState[field][index] = value;
} else {
newState[field] = value;
}
setDetail(newState);
};
const handleSubmit = () => {
for (let item of checkValidation) {
if (detail[item.field] === '' || !detail[item.field]) {
alert(item.msg);
return;
}
}
let params = {
faq_sj: detail.faq_sj,
faq_cont: detail.faq_cont,
faq_ctgry_no: detail.faq_ctgry_no,
public_yn: detail.public_yn
};
if (faq_no) {
Object.assign(params, {faq_no: faq_no});
dispatch(ActionFaq.update(params)).then((res) => {
alert('FAQ 수정이 완료되었습니다.');
location.reload();
}).catch((err) => {
console.log(err);
let errorMsg = err.response.data.msg || err.response.data.error.message;
alert(errorMsg);
});
} else {
dispatch(ActionFaq.create(params)).then((res) => {
alert('FAQ 등록이 완료되었습니다.');
window.close();
window.opener.parent.location.reload();
}).catch((err) => {
console.log(err);
let errorMsg = err.response.data.msg || err.response.data.error.message;
alert(errorMsg);
});
}
};
return (
<div className={"faqManageContainer"}>
{/*타이틀*/}
<PageTitle pathNames={['게시판 관리', faq_no ? 'FAQ 수정' : 'FAQ 등록']}/>
<div className={"infoBox"}>
<div className={"infoTitle"}>{faq_no ? 'FAQ 수정' : 'FAQ 등록'}</div>
<div className={"infoItems"}>
<div className={"infoItem"}>
<div className={"field"}>카테고리</div>
<div className={"value"}>
{(categoryList || []).map((item, index) => (
<div className={"radio"}
onClick={(e) => Const.handleStateValue(detail, setDetail, 'faq_ctgry_no', item.faq_ctgry_no)}>
<input id={"radio_is_category_y"} type={'radio'} checked={detail.faq_ctgry_no === item.faq_ctgry_no}/>
<label htmlFor={"radio_is_category"}></label>{item.faq_ctgry_nm}
</div>
))}
</div>
</div>
<div className={"infoItem"}>
<div className={"field"}>제목</div>
<div className={"value"}>
<input className={"Text"} type={'text'} placeholder={'제목을 입력해주세요'} value={detail.faq_sj}
onChange={(e) => handleStateValue('faq_sj', e.target.value)}/>
</div>
</div>
<div className={"infoItem"}>
<div className={"field"}>내용</div>
<div className={"value"}>
{isLoading ? null : <CKEditor content={detail.faq_cont} onChange={(e, editor) => {
Object.assign(detail, {faq_cont: editor.getData()});
}}/>}
</div>
</div>
</div>
</div>
<div className={"fixed"}>
<div className={"fixedBox"}>
<div className={'publish'} onClick={(e) => {
e.preventDefault();
Const.handleStateValue(detail, setDetail, 'public_yn', detail.public_yn == 'Y' ? 'N' : 'Y')
}}>
<input id="cb_publish" type={'checkbox'} checked={detail.public_yn == 'N'}/>
<label htmlFor="cb_publish"></label>비공개
</div>
<div className={"submit"} onClick={() => handleSubmit()}>{faq_no ? '수정' : '저장'}</div>
</div>
</div>
</div>
)
};
export default connect((state) => {
return {
eSESSION: state.data.eSESSION.eSESSION,
};
})(withRouter(index));
👉 component는 Dumb component 또는 Presentional component라고도 불리는데,
상위 component로부터 props를 전달받아 보여주는 역할
즉, 사용자와 실질적으로 상호작용 하는 친구.우리 프로젝트에서는 pageTitle, 화면의 큰 틀 안에 담기는 table 형식의 목록, 파일 업로드 부분 등 container안의 '요소'들이 들어있는 느낌
📌 component 사용 사례
파일 : sighty_project/admin/src/component/table/termsTable/index.jsx
→ 약관 관련 페이지에서 목록을 띄워주는 table 스타일의 컴포넌트
const index = (props) => {
const { dispatch, history, list } = props;
const imageUrl = Const.getImageUrl();
let renderList = (list || []).map((item, index) => {
return (
<div className={"termsTableRow"}>
<div>{item.terms_id || '-'}</div>
<div>{item.title || '-'}</div>
<div className={"termsTableEdit"}>
<span onClick={() => props.handleEdit(item.terms_id)}>수정</span>
</div>
</div>
)
});
return (
<div className={"termsTableContainer"}>
<div className={"termsTableHeader"}>
<div>번호</div>
<div>약관명</div>
<div>약관 수정</div>
</div>
<div className={"termsTableBody"}>
{renderList}
</div>
</div>
)
};
쉽게 말해서 container가 일은 다하고,
component는 받아서 보여주기만 하는 역할을 한다고 생각하면 된다고 함 ..
리덕스를 사용할 때에는 특히 Container와 Component를 구분해주는 게 좋음.
why❓) Container는 앱의 상태를 관리하기 때문에 앱의 상태가 자주 바뀔수록 그에 따라 빈번하게 업데이트가 일어남. 그래서 필요없는 부분에 업데이트가 일어나지 않게 하려면 Container과 Component를 구분해주어야 함. *리액트는 컴포넌트 단위로 업데이트* 하기 때문.
(React) Container와 Component
[React] Container(smart component) vs Component(dumb component)