ReactJs, TypeScript, Vite 휴가 추천 토이 프로젝트
ReactJs, TypeScript, Vite를 사용해휴가(연차)를 효율적으로 사용할 수 있게
추천해주는 [휴가 추천 토이 프로젝트]를 만들어보았다.
이번 프로젝트는 TypeScript와 React를 학습하는 것에 포커스를 두었다.
TypeScript는 (변수 : 타입 ) 형태로 구성되어 있다.
매개변수 Object의 타입을 알고있다면 Object안에 속한 속성을 빠르게 찾아 접근가능하며
타입을 제한하여 메소드의 진입을 막을수 있다.
문제는 API를 만드는 백엔드가 주도권을 갖고 있으므로
프론트는 받아서 사용하는 주체이므로 API가 구조적으로 변경되거나
타입을 모를때 지정하고 싶지 않을때가 발생된다.
이때는 any로 받아버리면 된다.
아래는 DateType이라는 Object를 정의한것이다.
Java에서는 String year; String Month; ... 로 정의가 될것이다.
react-calendar (https://www.npmjs.com/package/react-calendar) 모듈을 사용했으며
오픈소스 이므로 가이드를 보며 필요한 props를 주입시켜
커스터마이징 한다.
export type DateType = {
year: string,
month: string,
holidayArray: Array<RtnArrType>
holidayRecArray: any,
selectHoliday: string
}
const CalendarComponents = (dateType: DateType) => {
const [value, onChange] = useState(new Date());
return (
<Calendar onChange={onChange}
className={["box"]}
view="month"
onClickMonth={(value, event) => (
event.preventDefault(),
event.stopPropagation()
)}
onClickDay={(value, event) => (
event.preventDefault(),
event.stopPropagation()
)}
defaultValue={[new Date(Number(dateType.year), Number(dateType.month))]}
tileContent={({ activeStartDate, date, view }) => view === 'month'? <TitleContent activeStartDate={activeStartDate} date={date} holidayArray={dateType.holidayArray}/> : null}
tileClassName={({activeStartDate, date, view }) => changeColorDate(activeStartDate, date, view, dateType)}
calendarType="US"
formatDay={(locale, date) => moment(date).format("D")} />
)
}
useState를 사용하여 상태관리가 가능하게 할수 있다.
{{ }} expression을 사용해 데이터 바인딩을 해준다.
fetchData를 통해 공공 휴일데이터 정보를 가져와 화면을 렌더링한다.
const App = () => {
const [selectHoliday, setSelectHoliday] = useState("2");
const [selectedOption, setSelectedOption] = useState(year);
const [holidayArray, setHolidayArray] = useState<Array<RtnArrType>>([]);
const [holidayRecArray, setHolidayRecArray] = useState([]);
const [data, setData] = useState(null);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const fetchData = async () => {
setError(false);
setLoading(true);
try {
let reqUri = rtnReqUriAppend(selectedOption)
const response = await axios.get(reqUri);
let rtnArray = response?.data?.response?.body?.items?.item
let holidayArray = rtnArray.filter((arr:RtnArrType) => arr.isHoliday == "Y");
setHolidayArray(holidayArray)
console.log(holidayArray)
const arrDayArr:any = recHolidayCalc(holidayArray, selectHoliday)
setHolidayRecArray(arrDayArr)
} catch (err) {
setError(true);
}
setLoading(false);
};
useEffect(() => {
}, []);
return (
<div className="App">
<p aria-hidden="true" style={{textAlign: "center"}}>
<Placeholder.Button xs={6} aria-hidden="true">
직장인 연차 추천 애플리케이션 - 연차 효율적으로 쓰고 여행 가기!<br/>
연차 사용 추천일이 파란색으로 선택됩니다.
</Placeholder.Button>
</p>
연도: <Form.Select aria-label="Default select example2" onChange={e => {
setSelectedOption(e.target.value);}}
value={selectedOption} className='selectBox1'>
<option>휴가 갈 연도를 선택</option>
{selectOptionData.map((item:number, index:number) =>(
<option key={item} value={item}>{item}</option>
))}
</Form.Select>
사용가능 휴가일수: <Form.Select aria-label="Default select example1" onChange={e => {
setSelectHoliday(e.target.value);}}
value={selectHoliday} className='selectBox1'>
<option>사용가능한 휴가 일수를 선택</option>
{selectHolidayCnt.map((item:number, index:number) =>(
<option key={item} value={item}>{item}</option>
))}
</Form.Select>
<Button variant="success" className='holidaySearch1' onClick={() => {
fetchData();
}
}>휴가 검색!!</Button>
<div>
{loading ? (
<div style={{ height: '100vh', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<BeatLoader size={70} color="green"/>
</div>
) :
error? (
<Alert variant="danger" onClose={() => setError(false)} dismissible>
<Alert.Heading>Error</Alert.Heading>
<p>휴일데이터를 갖고 오지 못했습니다.</p>
</Alert>
) : (
<>
<div style={{ display: 'flex', flexWrap: 'nowrap' }}>
{items1.map((item1: string, index: number) => (
<CalendarComponents key={index} year={selectedOption} month={item1} holidayArray={holidayArray} holidayRecArray={holidayRecArray} selectHoliday={selectHoliday}></CalendarComponents>
))}
</div>
<div style={{ display: 'flex', flexWrap: 'nowrap' }}>
{items2.map((item2: string, index: number) => (
<CalendarComponents key={index} year={selectedOption} month={item2} holidayArray={holidayArray} holidayRecArray={holidayRecArray} selectHoliday={selectHoliday}></CalendarComponents>
))}
</div>
<div style={{ display: 'flex', flexWrap: 'nowrap' }}>
{items3.map((item3: string, index: number) => (
<CalendarComponents key={index} year={selectedOption} month={item3} holidayArray={holidayArray} holidayRecArray={holidayRecArray} selectHoliday={selectHoliday}></CalendarComponents>
))}
</div>
</>
)}
</div>
</div>
)
}
소스를 수정하며 발생되는 불필요한 기본 설정이나 스타일 등을 node_modules 안에 접근해서 적절하게 수정해준다.
오픈소스를 사용하며 발생되는 불가피한 영역인듯하다.
Vite 공식 가이드에 나온대로 배포 준비를 하고 (https://vitejs.dev/guide/static-deploy.html)
github 정적 페이지 배포를 통해 배포해준다.
배포된 Deploy URL
https://shlee0882.github.io/react-ts-calendar-app/
repo
https://github.com/shlee0882/react-ts-calendar-app
package.json
{
"name": "react-ts-calendar-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@reduxjs/toolkit": "^1.9.2",
"axios": "^1.3.2",
"bootstrap": "^5.2.3",
"react": "^18.2.0",
"react-big-calendar": "^1.6.3",
"react-bootstrap": "^2.7.0",
"react-calendar": "^4.0.0",
"react-dom": "^18.2.0",
"react-redux": "^8.0.5",
"react-spinners": "^0.13.8"
},
"devDependencies": {
"@types/moment": "^2.13.0",
"@types/react": "^18.0.26",
"@types/react-big-calendar": "^0.38.4",
"@types/react-calendar": "^3.9.0",
"@types/react-dom": "^18.0.9",
"@vitejs/plugin-react": "^3.0.0",
"typescript": "^4.9.3",
"vite": "^4.0.0"
}
}