본문 바로가기

공부일지/Project

비전공자의 프로젝트 만들기) 스케줄 프로젝트 만들기 - part1 오류의 위기 ......

728x90
반응형

스케줄 프로젝트 만들기 - part1

 

그동안 인강을 보여 공부하면서 배웠던 내용이 얼마나 내 것이 되었는지 확인하기 위해 혼자서 스케줄 프로젝트를 진행하였다.

 

일단 기획단에서 작업을 했는데 나는 기획을 배워본 적이 없어서 완벽한 기획은 아닐 것이다.

 

vscode에서 drawio를 설치해 간단한 프로젝트 구상도를 기획했다.

 

프로젝트 구성

이런 느낌의 구성으로 해야지 생각을 하고 이번엔 디자인 관련해서 서칭을 해보았다.

 

다양한 디자인을 서칭 하다 내가 원하는 느낌의 디자인을 찾게 되었다. 

https://dribbble.com/shots/15755836-Construction-Calendar

 

Construction Calendar

Happy Monday 😃 We continue to show you a part of our project. You can find the previous part here https://dribbble.com/shots/15732537-Construction-Messenger We are all in a hurry somewhere. We have many tasks, affairs, and meetings every day. All import

dribbble.com

깔끔하고 세련된 느낌의 디자인이었다. 디자인까지 서칭을 하고 이제 본격적으로 작업을 들어갔다.

 

프로젝트 폴더에 리액트를 설치 후 해당 프로젝트에서 사용할 라이브러리를 설치하였다.

 

  • react-icons
  • Semantic Ui React
  • data-fns
  • react-redux
  • styled-components
  • react-calendar

일단 위의 리스트를 사용할 것이다. 추후 다른 라이브러리들을 추가로 설치할 수도 있다.

 

지금 하는 일이 퍼블이라서 프로젝트할 때 퍼블먼저 하고 개발팀에게 전달하고 했는데 처음부터 개발을 목적으로 퍼블리싱을 하려니 UI를 작업하는데도 엄청 오래 걸렸던 거 같다.

 

우선 현재까지 작업한 폴더들의 구조를 보여주자면

현재까지 폴더 구조

 

  • assets : 컴파일단에서 필요한 이미지, 폰트, 영상 등을 보관하는 폴더이다. 일단은 폰트를 넣었는데 생각해 보니 컴파일 단에서 필요한 부분이 아니라 public 폴더로 옮길 예정이다.
  • component : 달력, 모달창 등 컴포넌트들을 모아둔 폴더이다.
  • reducers : redux관련 파일들이 있는 폴더이다.

 

App.js에서 state 및 데이터를 분배하고 있어서 app 폴더를 중심으로 작업을 진행했다.

function App() {
    const [currentDate, setCurrentDate] = useState(new Date());
    const [schedule, setSchedule] = useState([])

    return (
        <>
            <DateWrap>
                <DateHeader>
                    <div className="dateHeader__container">
                        <p className="dateHeader__container__month">{format(currentDate, "MMM")}</p>
                        <p className="dateHeader__container__year">{format(currentDate, "Y")}</p>
                        <div className="dateHeader__container__buttons-div">
                            <BiChevronLeft onClick={()=>setCurrentDate(subMonths(currentDate, 1))} />
                            <BiDotsHorizontal onClick={()=>setCurrentDate(new Date())} />
                            <BiChevronRight onClick={()=>setCurrentDate(addMonths(currentDate, 1))} />
                        </div>
                    </div>
                    <div>
                        <ModalPop setSchedule={schedule} />
                    </div>
                </DateHeader>
                <Calendar currentDate={currentDate} />
            </DateWrap>
        </>
    );
}

export default App;

const DateWrap = styled.div`
`;

const DateHeader = styled.div`
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom:40px; 
    .dateHeader__container {
        display: flex;
        align-items:center;
        justify-content:space-between;
        gap:5px;
        & > p {
            margin-bottom:0;
            font-family:'ONE-Mobile-Title';
            font-size:35px;
            font-weight: bold;
        }
        & .dateHeader__container__month{
            width:80px;
        }
        & .dateHeader__container__buttons-div{
            padding-top:5px;
            margin-left:40px;
            font-size:25px; 
            font-weight:light;
            box-shadow: 2px 2px 2px rgba(0,0,0,.1);
            border-radius: 5px;
            svg {
                overflow: inherit;
                cursor:pointer;
                margin:0 2px;
            }
        }
    }
`;

 

상단에 월, 년을 출력하고 화살표로 월을 이동가능하다. 화살표 사이에 버튼으로 현재 날짜의 월로 이동을 한다.

 

Calendar.js

 

const Calendar = ({currentDate}) => {
    const weekly = ['SUN','MON','TUE','WED','THU','FRI','SAT'];

    return (
        <CalendarBox>
            <div className='calendarBox__div'>
                {weekly.map((week, i)=>(
                    <CalendarWeekly key={i} week={week}>{week}</CalendarWeekly>
                ))}
            </div>
            <CalendarRow currentDate={currentDate} />
        </CalendarBox>
    )
}

export default Calendar

const CalendarBox = styled.div`
    width:100%;
    position: relative;
    & > .calendarBox__div {
        position: sticky;
        top:0;
        left:0;
        z-index: 3;
        margin-bottom:20px;
    } 
    & .calendarBox__div {
        display: flex;
        align-items:center;
        justify-content:space-between;
        background-color:#fff;
        gap:20px;
        letter-spacing: -1px;
    } 
`

const CalendarWeekly = styled.p`
    width: 100%;
    font-family: 'SUITE-Regular';
    text-align:left;
    padding:5px 5px 30px;
    box-sizing: border-box;
    color: ${props => props.week === "Sun" ? "red" : props.week === "Sat" ? "blue" : "black"};
`

달력 출력 컴포넌트는 크게 많은 내용은 없고 토, 일 색상 변경 정도가 있다.

처음에 작업할 때는 table태그를 사용해서 작업을 했는데 해당일을 클릭하거나 상단에 달력을 클릭했을 때 미니 달력이 나오고 오른쪽에 해당일의 일정 리스트가 출력되게 작업하기 위해 table태그는 맞지 않는 거 같아서 ul li를 이용해 작업하였다.

 

CalendarRow.js

import React from 'react'
import { addDays, endOfMonth, format, getWeeksInMonth, isSameMonth, startOfMonth, startOfWeek } from 'date-fns'
import { styled } from 'styled-components';

const CalendarRow = ({currentDate}) => {

    // 월의 시작일
    const monthStart = startOfMonth(currentDate);
    // 월의 마지막일
    const monthEnd = endOfMonth(monthStart);
    // 월의 첫번째 주 시작일
    const startDate = startOfWeek(monthStart);

    let dayArray = Array(7).fill(null);
    let weekArray = Array(getWeeksInMonth(currentDate)).fill(null)

    const currentMonth = new Date().getMonth() === currentDate.getMonth();
    let days = startDate;

    return (
        <CalendarDaysBox>
            {weekArray.map((week, i)=> (
                <ul className='calendarBox__div days-div' key={i}>
                    {dayArray.map((day, idx)=> (
                        days = addDays(days -1 , 1),
                        <CalendarDays 
                            key={idx}
                            className={
                                `days-div__content ${!isSameMonth(days, monthStart) ? 'ohterMonth' : ''} 
                                ${idx === 0 ? 'CalendarDays_sun' : idx === 6 ? 'CalendarDays_sat' : ''} 
                                ${Number(format(days, 'd')) === new Date().getDate() && Number(format(days, 'M')) === new Date().getMonth()+1 ? "calendarToday" : '' }`
                            }
                        >
                            <CalendarDay className='days-div__content__span'>
                                <span>{format(days,'d') < 10 ? "0"+format(days,'d') : format(days,'d')}</span>
                            </CalendarDay>
                        </CalendarDays>
                    ))}
                </ul>
            ))}
        </CalendarDaysBox>
    )
}

export default CalendarRow

const CalendarDaysBox = styled.div``

const CalendarDays = styled.li``

const CalendarDay = styled.div``

 

일부분은 date-fns에서 지원하는 기능을 이용해 월 시작일, 주 시작일을 구하고 일의 데이터를 넣기 위한 7개의 빈 배열과 해당 월의 주의 개수를 구해서 map을 사용해 하나씩 출력시켰다. 

for문을 사용해도 되지만 추후 key값을 이용해서 작업이 필요할 수도 있을 것 같아 map을 사용했다.

일의 box에 클래스 부분은 내용이 길어 해당 블로그에서는 한 줄씩 보이게 했고 코드를 보면 같은 달인, 토, 일 인지 오늘 날짜인지 체크해서 클래스를 출력하는 작업을 진행했다.

 

그리고 span으로 일자를 출력하는데  그 당시에 format에 dd를 넣으면 0이 출력된다는 걸 까먹고 현재 블로그를 작성 중에 기억이나 현재 코드는 수정하였다.

 

그리고 이제 스케줄 데이터를 저장할 modal 부분인데 현재도 Redux부분 작업을 진행 중이라 해당 페이지는 코드 작업 진행 중인 페이지이다.

 

ModalPop.js

const ModalPop = ({schedule}) => {

    const dispatch = useDispatch();

    const [modalPop, setModalPop] = useState(false)
    //제목
    const [scheduleTitle, setScheduleTitle] = useState('')
    // 날짜
    const [changeDate, setChangeDate] = useState('')
    //내용
    const [scheduleCnt, setscheduleCnt] = useState('')
    console.log(scheduleCnt)

    const changDate = (e) =>{
        return e && setChangeDate(format(e, "yyyy-MM-dd"))
    }

    const addSchedule = () => {
        dispatch(addSchedule());
    }

    const handleAddSchedule = () =>{

    }

    
    return (
        <PopupBox>
            <Modal
                onClose={()=>setModalPop(false)}
                onOpen={()=>setModalPop(true)}
                open={modalPop}
                trigger={<Button>일정 등록</Button>}
                style={{width:"95%", maxWidth:"500px"}}
            >
                <Modal.Header style={{fontFamily:'SUITE-Regular', fontSize:'17px', padding:'15px 20px'}}>일정 등록</Modal.Header>
                <ModalContainer>
                    <Form style={{padding:20, boxSizing:'border-box'}}>
                        <Modal.Content style={{display:'flex', gap:'20px', flexFlow:'row wrap', }}>
                            <Input transparent placeholder="제목" defaultValue={scheduleTitle} onChange={(e)=>setScheduleTitle(e.target.value)} />
                            <ModalCalendar>
                                <Input placeholder={format(new Date(), "yyyy-MM-dd")} readOnly transparent defaultValue={changeDate}  icon={<Icon name="calendar" alternate="true" outline="true" />}  />
                                <ReactDatePicker selected={new Date()} onChange={(date) => changDate(date)} />
                            </ModalCalendar>
                            <TextArea placeholder='내용' style={{ minHeight: 100, width:'100%' }} defaultValue={scheduleCnt} onChange={(e)=>setscheduleCnt(e.target.value)} />
                        </Modal.Content>
                    </Form>
                </ModalContainer>
                <Modal.Actions>
                    <Button color='black' onClick={() => setModalPop(false)}>취소</Button>
                    <Button
                        content="등록"
                        labelPosition='right'
                        icon='checkmark'
                        onClick={() => handleAddSchedule()}
                        positive
                    />
                </Modal.Actions>
            </Modal>
        </PopupBox>        
    )
}

export default ModalPop

const PopupBox = styled.div``

const ModalContainer = styled.div``

const ModalCalendar = styled.div``

modal컴포넌트 작업을 하면서  semantic 라이브러리 때문에 작업시간이 제일 오래 걸린 부분인 거 같다. 

해당 라이브러리를 사용할 때 css를 임포트 해주어야 하는데 p태그를 강제로 수정하는 css도 있고 해서 커스터마이징 하는데 시간이 조금 걸렸다. 

 

아직까진 데이터 전달받는 부분에서는 막힐 게 없어서 오류는 없었는데............

 

Redux를 사용하면 오류들이 막 나오기 시작했다... 역시 내가 예상했던 대로 Redux에서 많이 막혔다. 

 

Redux관련 소스들을 보자면

 

먼저 index.js

import { combineReducers } from 'redux'
import schedule from './schedule';

const rootReducer = combineReducers( {
    schedule
})

export default rootReducer

해당 페이지는 별게 없다.

 

schedule 페이지

export const ADD = "SCHEDULE/ADD";
export const DELETE = "SCHEDULE/DELETE";
export const EDIT = "SCHEDULE/EDIT";
export const SUCCESS = "SCHEDULE/SUCCESS";


export const addSchedule = scheduleData => ({type:ADD, scheduleData});

const initalSchedule = {
    scheduleList: []
}

const schedule = (state = initalSchedule, action) => {
    switch(action.type){
        case ADD :
            return {
                scheduleList:[...state, action.scheduleData]
            };
        default :
        return state
    }
}

export default schedule;

일단은 내용 추가 부분부터 작업하기 위해서 add만 코드를 작성하였다.

 

redux관련해서 구글링 해서 코드를 작성하였는데 

ADD, DELETE 등 action에 혹시 모를 중복네임이 있을 수 있어서 파일명을 넣었다.

 

addSchedule 이벤트를 만들어 타입과 값을 전달받아 넣는다.

 

기본값도 당연히 만들어서 밑에 이벤트에 값이 들어오지 않았을 때의 기본 값이 들어가게 했다.

 

switch문을 사용해서 action 타입별로 데이터를 처리해서 리턴해준다.

 

그리고 default를 사용하지 않으면 기본값이 undefined가 된다.

 

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import { applyMiddleware, compose, createStore } from 'redux';
import { Provider } from 'react-redux';
import logger from 'redux-logger';
import { composeWithDevTools } from 'redux-devtools-extension';
import App from './App';
import rootReducer from './reducers';
import 'semantic-ui-css/semantic.min.css'
import './index.css';

const store = createStore(rootReducer);

ReactDOM.render(
    <React.StrictMode>
        <Provider store={store}>
            <App />
        </Provider>
    </React.StrictMode>,
    document.getElementById('root'),
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals

Redux를 사용하기 위해 index파일도 수정해 주었다. ReactDom을 사용해서 렌더링 해준다.

 

store를 만들어서 reudcer을 불러와주고 해당 store을 provier를 사용해 자식들에게 전달해 주는 기능을 만들었다.

 

그런데...

이런 오류가 뜨네..................... 이번 프로젝트 일지는 여기까지 작성하고 오류를 수정하러 가봐야겠다...

 

728x90
반응형