View Asset Component
- HTML과 거의 유사한 뷰(View) 컴포넌트와 비즈니스 로직(Business Logic)만 담고 있는 컴포넌트를 만드는 것으로 역할을 완전히 분리하는 것
- 모든 컴포넌트를 뷰 파일과 로직 파일로 분리한다는 것이 특징
컴퓨터 프로그램에서 실세계의 규칙에 따라 데이터를 생성, 표시 저장, 변경하는 부분을 일컫는다.
데이터를 표시만 해주는 것. 화면에 나타나는 부분(UI)
다이어리를 작성하는 다음과 같은 컴포넌트가 있다.
다음 컴포넌트에서 비즈니스 로직과 뷰 로직을 분리해보자.
import { useEffect, useState, useContext } from 'react'
import { useFirestore } from '../../hooks/useFirestore';
import * as S from "./diaryForm.style";
import DragCont from '../../components/DragContainer/DragCont';
import { useDispatch, useSelector } from 'react-redux';
export default function DiaryForm({ uid }) {
const [title, setTitle] = useState('');
const [text, setText] = useState('');
const { addDocument, response } = useFirestore('myDiary');
const dispatch = useDispatch();
const status = useSelector(state => state.form)
const handleData = (event) => {
if (event.target.id === 'tit') {
setTitle(event.target.value);
} else if (event.target.id === 'txt') {
setText(event.target.value);
}
}
const handleSubmit = (event) => {
event.preventDefault();
console.log(title, text);
addDocument({
uid, title, text,
});
}
useEffect(() => {
if (response.success){
console.log(response)
setText('');
setTitle('')
}
}, [response.success])
return (
<DragCont>
<S.FormCont onSubmit={handleSubmit}>
<fieldset>
<S.FormTit>
일기 쓰기
<S.CloseBtn
onClick={() => {
dispatch({type: "toggleForm"})
}}
>x</S.CloseBtn>
</S.FormTit>
<S.FormContent>
<S.InpLabel htmlFor="tit">일기 제목 :</S.InpLabel>
<S.Inp id="tit" type="text" required value={title} onChange={handleData} />
<S.InpLabel htmlFor="txt">일기 내용 : </S.InpLabel>
<S.TxtArea id="txt" required value={text} onChange={handleData}></S.TxtArea>
<S.SubmitBtn><u>저</u>장하기</S.SubmitBtn>
</S.FormContent>
</fieldset>
</S.FormCont>
</DragCont>
)
}
import { useEffect, useState } from 'react'
import { useFirestore } from '../../hooks/useFirestore';
import DiaryFormView from './DiaryFormView';
export default function DiaryForm({ uid }) {
const [title, setTitle] = useState('');
const [text, setText] = useState('');
const { addDocument, response } = useFirestore('myDiary');
const handleData = (event) => {
if (event.target.id === 'tit') {
setTitle(event.target.value);
} else if (event.target.id === 'txt') {
setText(event.target.value);
}
}
const handleSubmit = (event) => {
event.preventDefault();
console.log(title, text);
addDocument({
uid, title, text,
});
}
useEffect(() => {
if (response.success){
console.log(response)
setText('');
setTitle('')
}
}, [response.success])
// view로 내려줄 props
const props = {
title,
text,
handleData,
handleSubmit,
}
return (
// props를 스프레드 문법으로 전달
<DiaryFormView {...props}/>
)
}
view에서 필요한 모든 데이터를 props의 형태로 view 영역에 전달한다.
props 정의 위 비즈니스 로직 부분은 수정된 부분 없이 동일하다.
import React from 'react'
import { useDispatch } from 'react-redux';
import DragCont from '../DragContainer/DragCont';
import * as S from "./diaryForm.style";
export default function DiaryFormView({
title,
text,
handleData,
handleSubmit,
}) {
const dispatch = useDispatch();
return (
<DragCont>
<S.FormCont onSubmit={handleSubmit}>
<fieldset>
<S.FormTit>
일기 쓰기
<S.CloseBtn
onClick={() => {
dispatch({type: "toggleForm"})
}}
>x</S.CloseBtn>
</S.FormTit>
<S.FormContent>
<S.InpLabel htmlFor="tit">일기 제목 :</S.InpLabel>
<S.Inp id="tit" type="text" required value={title} onChange={handleData} />
<S.InpLabel htmlFor="txt">일기 내용 : </S.InpLabel>
<S.TxtArea id="txt" required value={text} onChange={handleData}></S.TxtArea>
<S.SubmitBtn><u>저</u>장하기</S.SubmitBtn>
</S.FormContent>
</fieldset>
</S.FormCont>
</DragCont>
)
}
뷰 컴포넌트에서도 내려준 props의 형태 동일하게 받으면 된다. 기존 return 문 안에 있던 뷰 부분을 모두 가져오기만 했다.
비즈니스 로직에서 object 형태의 props를 정의해서 그대로 view 컴포넌트로 넘겨주었다.
const props = {
title,
text,
handleData,
handleSubmit,
}
넘겨줄 props를 하나로 모아 두었기 때문에 타입스크립트로 타입을 정의하기 너무나 편리하다.
타입스크립트의 interface로 위 props 객체 타입을 정의해줄 건데, view 컴포넌트에서도 사용할 것이기 때문에 export 키워드로 정의해준다.
export interface PropsType {
title: string;
text: string;
handleData: (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void
handleSubmit: (event: React.FormEvent<HTMLFormElement>) => void
}
// 비즈니스 로직 컴포넌트에서 정의한 interface import
import { PropsType } from './DiaryForm';
...
export default function DiaryFormView({
title,
text,
handleData,
handleSubmit,
// props 타입 통째로 import 해온 인터페이스로 적용!
}: PropsType) {
return (
// View .. . . .
)
}
예시로 첨부한 DiaryForm 컴포넌트의 VAC 패턴 도입과 타입스크립트 마이그레이션 과정을 피그잼으로 시각화해보았다. 그림이 너무 커서 아래 확대하기 보기 버튼을 클릭하면 글자가 잘 보인다...ㅎㅎ