React - tumblbug Page (1)

김정욱·2020년 11월 12일
1

React

목록 보기
15/22
post-thumbnail

tublbug Page (1)

React를 이용해서 Tublbug Page를 제작할 계획
1단계 ) Navigation + Image Slider - 오늘 구현
2단계 ) Grid + 반응형

Navigation (내비게이션)

[ 결과물 ]


  • 퍼블리싱이 목적이기 때문에 틀만 제작
  • 구조는 아래와 같다

(Navbar.js)

import React from 'react'
import styled from 'styled-components';

const NavBarTamplate = styled.div`
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 1035px;
`;

...

function Navbar() {
    return (
        <>
            <NavBarTamplate>
                <NavBarLeft>
                    <Menu>
                        <i class="fas fa-bars"></i>
                    </Menu>
                    <Text1>프로젝트 둘러보기</Text1>
                    <Text2>프로젝트 올리기</Text2>
                </NavBarLeft>
                <Logo src="/imgs/logo.png"></Logo>
                <NavbarRight>
                    <Info>
                        <ImgSearch src="https://tumblbug.com/wpa/e5aa161342e919b420a40fc6e34cce08.png"></ImgSearch>
                        <Text3>로그인 / 회원가입</Text3>
                        <ImgUser src="https://tumblbug-assets.imgix.net/assets/user-account.png?s=08b1f9ecf24209994ac9b81900936c0e"></ImgUser>
                    </Info>
                </NavbarRight>
            </NavBarTamplate>
        </>
    )
}

export default Navbar

: 내비게이션을 왼쪽메뉴 / 로고 / 오른쪽메뉴 3가지로 구성하고
  flex disply에 space-between으로 레이아웃 배치

Image Slider (이미지 슬라이더)

[ 결과물 ]


[ 원리 ]

  • 정해진 슬라이더 부분이미지와 텍스트 부분을 따로 지정
  • 이미지 or 텍스트는 각각 하나의 크기 이상은 모두 overflow:hidden
  • 버튼 클릭시 각(이미지, 텍스트) 크기만큼(width:100%) x축으로 transform
  • 파일 구조 : Slider.js 내부에 Image.js와 SlideText.js존재
  • setInterval로 2초마다 자동으로 넘어감

[ 코드 ]

(App.js)

...
function App() {
  return (
    <>
      <NavTemplate>
        <Navbar></Navbar>
      </NavTemplate>
      <Template>
        <Slider object={object}></Slider>
      </Template>
    </>
  );
}

: Slider에게 이미지 리스트인 object를 props로 전달


(Slider.js)

function Slider({object}) {

    const [chIdx,setChIdx] = useState(0);

    const onClickLeftButton = ()=>{
        if(chIdx != 0){
            setChIdx(chIdx-1);
        }else{
            setChIdx(object.length-1)
        }
    }
    const onClickRightButton = ()=>{
        if(chIdx != object.length-1){
            setChIdx(chIdx+1);
        }else{
            setChIdx(0);
        }
    }

    return (
        <SliderTemplate>
            <ImageTemplate>
                {
                    object.map((obj,idx)=>(
                        <Image
                        key={`Image-${idx}`} chIdx={chIdx} obj={obj}

                        </Image>
                    ))
                }
            </ImageTemplate>
            <SideTextTemplate>
                {
                    object.map((obj,idx)=>(
                        <SideText
                        chIdx={chIdx}
                        obj={obj}
                        key={`SideText-${idx}`}
                        onClickLeftButton={onClickLeftButton}
                        onClickRightButton={onClickRightButton}>
                        </SideText>
                    ))
                }
            </SideTextTemplate>
        </SliderTemplate>
    )
  • chIdx : 현재 사용자가 선택한 인덱스를 가리키는 State
  • Slider안에 이미지를 먼저 쭉~ 나열하고, 그다음 텍스트를 쭉~나열
  • 어차피 ImageTemplate와 SiderTextTemplate에서 overflow:hidden이라서 하나만 보이게 할 것

(Image.js)

import React from 'react'
import styled, {css} from 'styled-components';

const Img = styled.img`
    width:100%;
    cursor: pointer;
    ${props =>
        css`
            transform: translate(-${(props.chIdx)*100}%,0px);
            transition: 0.5s;
        `
    }
`;
function Image({obj, chIdx}) {
        return (
            <>
                <Img chIdx={chIdx} src={obj.img}></Img>
            </>
        );
}

export default Image

: 선택된 인덱스 * width의 100%만큼 움직이게 한다. (중요)


(SideText.js)

import React from 'react'
import styled, {css} from 'styled-components';

const SideWrap = styled.div`
    background-color: #222222;
    display: flex;
    z-index: 2;
    flex-direction: column;
    justify-content: center;
    box-sizing: border-box;
    transition-property: background-color;
    transition-duration: 0.8s;
    ${props =>
        css`
            transform: translate(-${(props.chIdx)*100}%,0px);
        `
    }
    ${props =>
            props.chIdx == 1? css`background-color: #378eb5;` 
            :
                (props.chIdx == 2? css`background-color: #d77847;`
                :
                    (props.chIdx == 3? css`background-color: #f4adac;`
                    :
                        (props.chIdx == 4? css`background-color: #2fa789;`
                        : css`background-color: #222222;`)))
    };
`;

  ...

function SideText({chIdx, obj, onClickLeftButton, onClickRightButton}) {
    return (
        <>
                <SideWrap chIdx={chIdx}>
                    <TextBox>
                        <MainText>
                            <MainTxt1>{obj.mainTxt1}</MainTxt1>
                            <MainTxt2>{obj.mainTxt2}</MainTxt2>
                        </MainText>
                        <SubText>{obj.subTxt}</SubText>
                    </TextBox>
                    <ButtonAndPage>
                    <ButtonBox>
                        <ButtonLeft
                         onClick={onClickLeftButton}>
                            <i className="fas fa-chevron-left"></i>
                        </ButtonLeft>
                        <ButtonRight
                        onClick={onClickRightButton}>
                            <i className="fas fa-chevron-right"></i>
                        </ButtonRight>
                    </ButtonBox>
                    <PageCnt>{obj.idx+1}&nbsp;/&nbsp;5</PageCnt>
                </ButtonAndPage>
            </SideWrap>
        </>
    )
}

export default SideText
  • 전체를 책임지는 SideWrap에서 인덱스마다 조건부 css를 중첩으로 구현
  • 그 외에 특별한 특이사항은 없다

SetInterval 사용하기

react에서 setInterval을 사용하기는 바닐라JS때와 달리 조금 까다롭다.
왜냐하면 hooks에서 생명주기인 useEffect에 clearInterval을 지정해줘야 하며,
useRef로 사용해야 하기 때문이다


(Slider.js)

 ...

    const tmp = useRef();

    const onAutoIncrease = () => {
        if(chIdx != object.length-1){
            setChIdx(chIdx+1);
        }else{
            setChIdx(0);
        }
    }
    /* react에서 Interval 사용시 아래와 같이 사용해야함!! */
    useEffect(() => {
        tmp.current = onAutoIncrease;
      });

    useEffect(()=>{
        function tick() {
            tmp.current();
          }

          let id = setInterval(tick, 2000);
          return () => clearInterval(id);
    },[]).
  • 사용방법
    1) useRef()로 변수를 생성
    2) useEffect로 해당 ref에 콜백함수 연결
    3) 컴포넌트가 처음 마운트 되었을 때 ref를 사용한 콜백함수와 시간으로 변수에 setInterval을 할당!
    4) return되었을 때 setInterval을 clear!
  • useEffect에서 return은 컴포넌트가 소멸될 때를 의미하기 때문에,
    Interval을 clear해줘야 쌓이지 않는다

주의사항

[ Image / Text 통합시 ]

처음에 Image와 Text를 분리하지 않고 하나의 컴포넌트로 했더니
이미지 슬라이더가 넘어갈 때 Text크기만큼의 공백이 보였음

그래서 본 글에서는 Image와 Text 컴포넌트를 따로 분리함


[ 일부 속성에 Transition 적용 ]

    transition-property: background-color;
    transition-duration: 0.8s;

: 당연하긴 했지만 트랜지션을 한번에 주는것에 익숙해져 까먹고 있었음;

profile
Developer & PhotoGrapher

0개의 댓글