코딩/위코드 코딩학습

[위코드] TIL(Today I am Learned) -31(기업협업)(DatePicker 완성)

카슈밀 2020. 8. 25. 10:59
반응형

- 오늘의 할일.

코드 리팩토링.

기능 구현 상담 후 수정할 것 있는지 확인 후 merge할 것.

 

-코드 리뷰

3일간 날 괴롭히던 datepicker의 기능을 대부분 구현했다.

기존에 하드 코딩으로 2019를 표시했던 것을 이제 자동으로 시간이 지나도 알아서 작동되게 구현하였고,

WRDS처럼 2007년 페이지를 기준으로 했던 것처럼, 내가 만든 부분도 13년으로 설정해서

현재는 2007년 내년에는 2008년 내후년은 2009년으로 자동으로 바뀌게 만들었다.

 

원래 J쿼리로 만들어진 것이라 구현이 매우 빡셌다.

자료도 대부분 J쿼리 전용이라...

이에 참고한 자료가 HackerOne에서 만든 React-datepicker 자료

https://reactdatepicker.com/

 

React Datepicker crafted by HackerOne

 

reactdatepicker.com

 

가장 많은 도움을 받았다(?)고 생각할 수 있다.

왜냐하면 이것을 보고 기초틀을 잡아서 설정하였기에...

 

그럼에도 불구하고 어려운 부분이 있었다.

왜냐하면 용어들이 React용이 아니라 대부분의 제이쿼리용 언어로만 접할 수 있어서...

자료를 찾느라 오히려 고생했고, React 소스코드로 들어가도 해당 내용을 date | null로만 표시되어 양식이 어떤지 몰랐기에 헤맸다.

뜨문 뜨문 stackoverflow에서 자료들이 나왔고, 이를 바탕으로

특정 날짜를 설정할 때는 new Date("날짜")를 쳐야하는 것을 알게되었다.

내가 쓴 것은 Date Range였다. 여기에

연도가 1925~2020년까지라서 연도를 따로 설정할 수 있게 showMonth, showYear를 추가하였고,

일일히 선택하기 힘드니 스크롤 기능(dropdownMode)를 추가하여 선택기능을 추가하였다.

다하고 보니 마지막에 마우스 이벤트로 글씨가 뜨길래 hover겠구나했더니

moseEnter와 mouseLeave를 줘야하는 것이었다.

처음에는 datepicker 자체한테 먹였지만 먹히질 않아서 원래는 없던 div를 새로 만들어 여기에 마우스 이벤트를 먹였다.

css로 아무것도 먹일게 없고, datepicker를 className으로 컨트롤하다보니 그 위에 하나를 만들면 따로 따로 먹여야할 것이라고 생각해서 어찌하나 했는데 그냥 div를 먹이는 것으로 간단하게 해결하였다.

마우스 이벤트 작동되는 상황

마우스 이벤트를 작동되는 상황을 보여주기 위해서 일시정지하다보니 검은 색으로 나왔다.

이렇게 이벤트는 깔끔하게 끝났고

이외에도 어려웠던 점이 DateRange가 2개이다보니 mindate, maxdate가 까다로웠는데, 의외로 간단하였다.

첫번째 mindate에는 해당범위의 시작일을 주고, maxdate는 2번째에서 작동되는 enddate를 주고,

2번째는 mindate에 첫번째에 있는 startdate값을, maxdate에는 해당범위의 마지막일 을 주면 되는 것이었다.

이 내용이 없어서 매우 해맸다.. 나쁜 datepicker... ・ั﹏・ั 

<DateWrap>
                <div 
                  onMouseEnter={()=>setMini(true)}
                  onMouseLeave={()=>setMini(false)}
                >
                  <DatePicker
                    className="datepickersize"
                    selected={startDate}
                    onChange={date => setStartDate(date)} 
                    selectsStart
                    startDate={startDate}
                    endDate={endDate}
                    minDate={new Date("1925-12-31")}
                    maxDate={new Date(endDate)}
                    dateFormat="yyyy-MM-dd"
                    dropdownMode="select"
                    peekNextMonth
                    showMonthDropdown
                    showYearDropdown
                    placeholderText=" Effective date"
                  />
                </div>
                <HiddenMiniDate mini={mini}>
                  Minimum allowed date: 1925-12-31
                </HiddenMiniDate>
                <SpanBox>to</SpanBox>
                <div
                  onMouseEnter={()=>setMaxi(true)}
                  onMouseLeave={()=>setMaxi(false)}
                >
                  <DatePicker
                    className="datepickersize"
                    selected={endDate}
                    onChange={date => setEndDate(date)}
                    selectsEnd
                    startDate={startDate}
                    endDate={endDate}
                    changeMonth="true"
                    changeYear="true"
                    minDate={new Date(startDate)}
                    maxDate={new Date(`${lastYear}-12-31`)}
                    dateFormat="yyyy-MM-dd"
                    dropdownMode="select"
                    peekNextMonth
                    showMonthDropdown
                    showYearDropdown
                    placeholderText=" Expiration date"
                  />
                </div>
                <HiddenMaxDate maxi={maxi}>
                  Maximum allowed date: {lastYear}-12-31
                </HiddenMaxDate>
              </DateWrap>

 

이제 step2의 버튼을 누르면 해당 값을 리턴하게 만들기 위해서 설정하였다.

의외로 간단했던(?) 것인데, 처음에는 객체로 따로 저장하려고 valuedetector에서 setCheck({ [id] : value })로 객체로 저장했지만,  checked ==== "ticker" 부분에서 인식을 못하길래 뭐지 했는데

객체값으로 저장되어 따로 checked.(ticker, permco) === "ticker" 등으로 저장경로가 길어지길래

그냥 값만 저장해버렸다.

그랬더니 버튼을 누르면 해당 값이 나오는!

import React, { useState } from "react";
import styled from "styled-components";
import DatePicker from "react-datepicker";

function StepOne() {
  const date = new Date();
  const lastYear = date.getFullYear() - 1;
  const defalutYear = date.getFullYear() - 13;
  const [ startDate, setStartDate] = useState(new Date(`${defalutYear}-01-01`));
  const [ endDate, setEndDate] = useState(new Date(`${lastYear}-12-31`));
  const [ mini, setMini ] = useState(false);
  const [ maxi, setMaxi ] = useState(false);
  const [ check, setCheck ] = useState("");
  const valuedetector = (e) => {
    const { value } = e.target
    setCheck(value)
  }

  return(
    <>
      <div>
        <StepOnePart>
          <StepOneTitle>
            <BoldWrite>Step 1:</BoldWrite>
            <SpanTitle> Choose your date range.</SpanTitle>
          </StepOneTitle>
          <DateRangeWrap>
            <DateRangeRow>
              <DateRangeTitle>Date range</DateRangeTitle>
              <DateWrap>
                <div 
                  onMouseEnter={()=>setMini(true)}
                  onMouseLeave={()=>setMini(false)}
                >
                  <DatePicker
                    className="datepickersize"
                    selected={startDate}
                    onChange={date => setStartDate(date)} 
                    selectsStart
                    startDate={startDate}
                    endDate={endDate}
                    minDate={new Date("1925-12-31")}
                    maxDate={new Date(endDate)}
                    dateFormat="yyyy-MM-dd"
                    dropdownMode="select"
                    peekNextMonth
                    showMonthDropdown
                    showYearDropdown
                    placeholderText=" Effective date"
                  />
                </div>
                <HiddenMiniDate mini={mini}>
                  Minimum allowed date: 1925-12-31
                </HiddenMiniDate>
                <SpanBox>to</SpanBox>
                <div
                  onMouseEnter={()=>setMaxi(true)}
                  onMouseLeave={()=>setMaxi(false)}
                >
                  <DatePicker
                    className="datepickersize"
                    selected={endDate}
                    onChange={date => setEndDate(date)}
                    selectsEnd
                    startDate={startDate}
                    endDate={endDate}
                    changeMonth="true"
                    changeYear="true"
                    minDate={new Date(startDate)}
                    maxDate={new Date(`${lastYear}-12-31`)}
                    dateFormat="yyyy-MM-dd"
                    dropdownMode="select"
                    peekNextMonth
                    showMonthDropdown
                    showYearDropdown
                    placeholderText=" Expiration date"
                  />
                </div>
                <HiddenMaxDate maxi={maxi}>
                  Maximum allowed date: {lastYear}-12-31
                </HiddenMaxDate>
              </DateWrap>
            </DateRangeRow>
          </DateRangeWrap>
        </StepOnePart>
        <div>
          <MarginTop>
            <div>
              <Strongtitle>Step 2:</Strongtitle>
              <SpanContent> Apply your company codes.</SpanContent>
            </div>
            <div>
              <Unorderedlist>
                <Inputlist>
                  <InputBtn 
                    id="TICKER"
                    type="radio"
                    value="TICKER"
                    autocomplete="off"
                    checked={ check === "TICKER"}
                    onChange={valuedetector}
                  />
                  &nbsp;
                  <LabelName id="TICKER">TICKER</LabelName>
                </Inputlist>
                <Inputlist>
                  <InputBtn 
                    id="PERMNO"
                    type="radio"
                    value="PERMNO"
                    autocomplete="off"
                    checked={ check === "PERMNO"}
                    onChange={valuedetector}
                  />
                  &nbsp;
                  <LabelName id="PERMNO">PERMNO</LabelName>
                </Inputlist>
                <Inputlist>
                  <InputBtn 
                    id="PERMCO"
                    type="radio"
                    value="PERMCO"
                    autocomplete="off"
                    checked={ check === "PERMCO"}
                    onChange={valuedetector}
                  />
                  &nbsp;
                  <LabelName id="PERMCO">PERMCO</LabelName>
                </Inputlist>
                <Inputlist>
                  <InputBtn
                    id="CUSIP"
                    type="radio"
                    value="CUSIP"
                    autocomplete="off"
                    checked={ check === "CUSIP"}
                    onChange={valuedetector}
                  />
                  &nbsp;
                  <LabelName id="CUSIP">CUSIP</LabelName>
                </Inputlist>
                <Inputlist>
                  <InputBtn
                    id="NCUSIP"
                    type="radio"
                    value="NCUSIP"
                    autocomplete="off"
                    checked={ check === "NCUSIP"}
                    onChange={valuedetector}
                  />
                  &nbsp;
                  <LabelName id="NCUSIP">NCUSIP</LabelName>
                </Inputlist>
                <Inputlist>
                  <InputBtn
                    id="HSICCD"
                    type="radio"
                    value="HSICCD"
                    autocomplete="off"
                    checked={ check === "HSICCD"}
                    onChange={valuedetector}
                  />
                  &nbsp;
                  <LabelName id="HSICCD">HSICCD</LabelName>
                </Inputlist>
                <Inputlist>
                  <InputBtn
                    id="SICCD"
                    type="radio"
                    value="SICCD"
                    autocomplete="off"
                    checked={ check === "SICCD"}
                    onChange={valuedetector}  
                  />
                  &nbsp;
                  <LabelName id="SICCD">SICCD</LabelName>
                </Inputlist>
              </Unorderedlist>
            </div>
          </MarginTop>
        </div>
      </div>
    </>
  )
}

export default StepOne;

//Step One부분
const StepOnePart = styled.div`
  margin-top: 30px;
`;

const StepOneTitle = styled.div`
  width: 100%;
`;

const BoldWrite = styled.strong`
  font-size: 20px;
  font-weight : bold;
`;

const SpanTitle = styled.span`
  font-size: 20px;
`;

//데이트피커 부분
const DateRangeWrap = styled.div`
  margin-left: -15px;
  margin-right: -15px;
`;

const DateRangeRow = styled.div`
  width: 50%;
  position: relative;
  min-height: 1px;
  padding: 0 15px;
`;

const DateWrap = styled.div`
  display: flex;

  .datepickersize{
    height: 36px;
    padding: 8px 12px;
    font-size: 12px;
    line-height: 1.5;
    background-color: #fff;
    border: 1px solid #ccc;
  }
`;

const DateRangeTitle = styled.label`
  color: black;
  text-align: right;
  display: inline-block;
  font-size: 14px;
  margin-bottom: 0;
  padding-top: 9px;
  height: 32px;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
`;

const HiddenMiniDate = styled.div`
  background-color: black;
  position: absolute;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  display: ${(props) => (props.mini? "flex": "none")};
  color: white;
  font-weight: normal;
  justify-content: center;
  align-items: center;
  width: 200px;
  height: 39px;
  top: -9px;
  left: -3.5px;
  margin-top: -3px;
  padding: 5px 0;
  opacity: 0.8;
  z-index: 1070;
  font-size: 12px;
  line-height: 1.4;

`;

const HiddenMaxDate = styled.div`
  background-color: black;
  position: absolute;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  display: ${(props) => (props.maxi? "flex": "none")};
  color: white;
  font-weight: normal;
  justify-content: center;
  align-items: center;
  width: 200px;
  height: 39px;
  top: -9px;
  left: 211px;
  margin-top: -3px;
  padding: 5px 0;
  opacity: 0.8;
  z-index: 1070;
  font-size: 12px;
  line-height: 1.4;
`;

const SpanBox = styled.span`
  line-height: 18px;
  padding: 8px 12px;
  font-size: 14px;
  font-weight: normal;
  color: #AAAAAA;
  text-align: center;
  background-color: #eeeeee;
  border: 1px solid #ccc;
  width: 9%;
  white-space: nowrap;
  vertical-align: middle;
`;

//파트 2부분
const MarginTop = styled.div`
  margin-top: 30px;
`;

const Strongtitle = styled.strong`
  font-size: 20px;
  font-weight: bold;
`;

const SpanContent = styled.span`
  font-size: 20px;
`;

const Unorderedlist = styled.ul`
  margin: 15px 0;
  padding-left: 0;
  list-style: none;
  display: flex;
`;

const Inputlist = styled.li`
  margin-right: 10px;
  padding-left: 5px;
  padding-right: 5px;
  height: 29px;
  display: flex;
  align-items: center;
`;

const InputBtn = styled.input`
  margin: 0;
`;

const LabelName = styled.label`
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-weight: normal;
  font-size: 14px;
`;

 

마지막으로 아직 코드리팩토링이 남았지만, 더 코드를 압축시키기 위해서 정리를 더 해봐야겠다.

리뷰를 보니까 원래 사이트의 속성을 그대로 넣었다보니 좀 쓸모없는게 많더라...

그것을 쳐내고, 나는 css에서 소스코드의 미관상 글자는 글자대로, 숫자는 숫자대로 모았더니;;;;

이걸 속성대로 모아 달라고 하는데 그렇게 바꿔야겠다.

최종수정한 소스태그

import React, { useState } from "react";
import styled from "styled-components";
import DatePicker from "react-datepicker";

function StepOne() {
  const date = new Date();
  const lastYear = date.getFullYear() - 1;
  const defalutYear = date.getFullYear() - 13;
  const [ startDate, setStartDate] = useState(new Date(`${defalutYear}-01-01`));
  const [ endDate, setEndDate] = useState(new Date(`${lastYear}-12-31`));
  const [ mini, setMini ] = useState(false);
  const [ maxi, setMaxi ] = useState(false);
  const [ check, setCheck ] = useState("");
  const valuedetector = (e) => {
    const { value } = e.target
    setCheck(value)
  }

  return(
    <>
      <div>
        <StepOnePart>
          <div>
            <BoldWrite>Step 1:</BoldWrite>
            <SpanTitle> Choose your date range.</SpanTitle>
          </div>
          <DateRangeWrap>
            <DateRangeRow>
              <DateRangeTitle>Date range</DateRangeTitle>
              <DateWrap>
                <div 
                  onMouseEnter={()=>setMini(true)}
                  onMouseLeave={()=>setMini(false)}
                >
                  <DatePicker
                    className="datepickersize"
                    selected={startDate}
                    onChange={date => setStartDate(date)} 
                    selectsStart
                    startDate={startDate}
                    endDate={endDate}
                    minDate={new Date("1925-12-31")}
                    maxDate={new Date(endDate)}
                    dateFormat="yyyy-MM-dd"
                    dropdownMode="select"
                    peekNextMonth
                    showMonthDropdown
                    showYearDropdown
                    placeholderText=" Effective date"
                  />
                </div>
                <HiddenMiniDate mini={mini}>
                  Minimum allowed date: 1925-12-31
                </HiddenMiniDate>
                <SpanBox>to</SpanBox>
                <div
                  onMouseEnter={()=>setMaxi(true)}
                  onMouseLeave={()=>setMaxi(false)}
                >
                  <DatePicker
                    className="datepickersize"
                    selected={endDate}
                    onChange={date => setEndDate(date)}
                    selectsEnd
                    startDate={startDate}
                    endDate={endDate}
                    changeMonth="true"
                    changeYear="true"
                    minDate={new Date(startDate)}
                    maxDate={new Date(`${lastYear}-12-31`)}
                    dateFormat="yyyy-MM-dd"
                    dropdownMode="select"
                    peekNextMonth
                    showMonthDropdown
                    showYearDropdown
                    placeholderText=" Expiration date"
                  />
                </div>
                <HiddenMaxiDate className="maxi" maxi={maxi}>
                  Maximum allowed date: {lastYear}-12-31
                </HiddenMaxiDate>
              </DateWrap>
            </DateRangeRow>
          </DateRangeWrap>
        </StepOnePart>
        <div>
          <MarginTop>
            <div>
              <Strongtitle>Step 2:</Strongtitle>
              <SpanContent> Apply your company codes.</SpanContent>
            </div>
            <div>
              <Unorderedlist>
                <Inputlist>
                  <InputBtn 
                    id="TICKER"
                    type="radio"
                    value="TICKER"
                    autocomplete="off"
                    checked={ check === "TICKER"}
                    onChange={valuedetector}
                  />
                  &nbsp;
                  <LabelName id="TICKER">TICKER</LabelName>
                </Inputlist>
                <Inputlist>
                  <InputBtn 
                    id="PERMNO"
                    type="radio"
                    value="PERMNO"
                    autocomplete="off"
                    checked={ check === "PERMNO"}
                    onChange={valuedetector}
                  />
                  &nbsp;
                  <LabelName id="PERMNO">PERMNO</LabelName>
                </Inputlist>
                <Inputlist>
                  <InputBtn 
                    id="PERMCO"
                    type="radio"
                    value="PERMCO"
                    autocomplete="off"
                    checked={ check === "PERMCO"}
                    onChange={valuedetector}
                  />
                  &nbsp;
                  <LabelName id="PERMCO">PERMCO</LabelName>
                </Inputlist>
                <Inputlist>
                  <InputBtn
                    id="CUSIP"
                    type="radio"
                    value="CUSIP"
                    autocomplete="off"
                    checked={ check === "CUSIP"}
                    onChange={valuedetector}
                  />
                  &nbsp;
                  <LabelName id="CUSIP">CUSIP</LabelName>
                </Inputlist>
                <Inputlist>
                  <InputBtn
                    id="NCUSIP"
                    type="radio"
                    value="NCUSIP"
                    autocomplete="off"
                    checked={ check === "NCUSIP"}
                    onChange={valuedetector}
                  />
                  &nbsp;
                  <LabelName id="NCUSIP">NCUSIP</LabelName>
                </Inputlist>
                <Inputlist>
                  <InputBtn
                    id="HSICCD"
                    type="radio"
                    value="HSICCD"
                    autocomplete="off"
                    checked={ check === "HSICCD"}
                    onChange={valuedetector}
                  />
                  &nbsp;
                  <LabelName id="HSICCD">HSICCD</LabelName>
                </Inputlist>
                <Inputlist>
                  <InputBtn
                    id="SICCD"
                    type="radio"
                    value="SICCD"
                    autocomplete="off"
                    checked={ check === "SICCD"}
                    onChange={valuedetector}  
                  />
                  &nbsp;
                  <LabelName id="SICCD">SICCD</LabelName>
                </Inputlist>
              </Unorderedlist>
            </div>
          </MarginTop>
        </div>
      </div>
    </>
  )
}

export default StepOne;

//Step One부분
const StepOnePart = styled.div`
  margin-top: 30px;
`;

const BoldWrite = styled.strong`
  font-size: 20px;
  font-weight : bold;
`;

const SpanTitle = styled.span`
  font-size: 20px;
`;

//데이트피커 부분
const DateRangeWrap = styled.div`
  margin: 0 -15px;
`;

const DateRangeRow = styled.div`
  width: 50%;
  position: relative;
  min-height: 1px;
  padding: 0 15px;
`;

const DateWrap = styled.div`
  display: flex;

  .datepickersize{
    height: 36px;
    padding: 8px 12px;
    font-size: 12px;
    line-height: 1.5;
    background-color: #fff;
    border: 1px solid #ccc;
  }
`;

const DateRangeTitle = styled.label`
  color: black;
  display: inline-block;
  margin-bottom: 0;
  padding-top: 9px;
  height: 32px;
  text-align: right;
  font-size: 14px;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
`;

const HiddenMiniDate = styled.div`
  display: ${(props) => (props.mini || props.maxi? "flex": "none")};
  position: absolute;
  justify-content: center;
  align-items: center;
  color: white;
  background-color: black;
  width: 200px;
  height: 39px;
  top: -9px;
  left: -3.5px;
  margin-top: -3px;
  padding: 5px 0;
  opacity: 0.8;
  z-index: 1070;
  line-height: 1.4;
  font-size: 12px;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
`;

const HiddenMaxiDate = styled(HiddenMiniDate)`
  left: 211px;
`;

const SpanBox = styled.span`
  color: #AAAAAA;
  background-color: #eeeeee;
  border: 1px solid #ccc;
  font-size: 14px;
  text-align: center;
  width: 9%;
  line-height: 18px;
  padding: 8px 12px;
  white-space: nowrap;
  vertical-align: middle;
`;

//파트 2부분
const MarginTop = styled.div`
  margin-top: 30px;
`;

const Strongtitle = styled.strong`
  font-size: 20px;
  font-weight: bold;
`;

const SpanContent = styled.span`
  font-size: 20px;
`;

const Unorderedlist = styled.ul`
  margin: 15px 0;
  padding-left: 0;
  list-style: none;
  display: flex;
`;

const Inputlist = styled.li`
  margin-right: 10px;
  padding:0 5px;
  height: 29px;
  display: flex;
  align-items: center;
`;

const InputBtn = styled.input`
  margin: 0;
`;

const LabelName = styled.label`
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 14px;
`;

익스텐드 스타일태그를 통해서 코드를 압축한 모습.

인상 깊었어요 ㅎㅎ

중복되는 태그때문에 이것을 어떻게 해결해야할지 고민이었는데, 도움을 준 신영재(PM)에게 감사합니다.

const HiddenMiniDate = styled.div`
  display: ${(props) => (props.mini || props.maxi? "flex": "none")};
  position: absolute;
  justify-content: center;
  align-items: center;
  color: white;
  background-color: black;
  width: 200px;
  height: 39px;
  top: -9px;
  left: -3.5px;
  margin-top: -3px;
  padding: 5px 0;
  opacity: 0.8;
  z-index: 1070;
  line-height: 1.4;
  font-size: 12px;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
`;

const HiddenMaxiDate = styled(HiddenMiniDate)`
  left: 211px;
`;
728x90