React로 minishop 만들기(1)

seul_velog·2022년 5월 1일
0

✍️ React 강의를 듣고 코드를 작성하면서 새로 알게된 점, 메모한 내용들을 남겨보자.



1. 리액트에서 Bootstrap 사용하기

  • React-Bootstrap

  • bootstrap을 통해 간편하게 navbar와 container를 가져와서 사용해 보았다.

  • 사용과정 ▼
    1) npm install 설치
    2) css 코드 넣기 (js 혹은 html에 넣는 방식으로 가능하다.)
    3) import 해오기 (대문자로 시작하는 컴포넌트명을 전부 import 한다.)
    4) 사용법을 참고하여 원하는 코드를 가져와서 사용

    import { Navbar, Container, Nav, Row } from 'react-bootstrap';



2. 이미지 넣기

2-1. JSX에서 넣기

import mainImg from './img/main.jpg';
style={{ backgroundImage: 'url(' + mainImg + ')' }}
  1. import 해오면서 변수명 지정하기
  2. 필요한 곳에 style 속성 주기 (이부분 문법이 익숙하지 않으니 유의하자. 🧐 )

2-2. CSS에서 넣기

// css
.main-bg {
  height: 300px;
  background-image: url('./img/main.jpg');
  background-size: cover;
  background-position: center;
}

2-3. 외부 호스팅 이미지 가져오기

  • 외부에 호스팅해둔 이미지라면 이미지 절대주소만 넣어주면 된다.
    <img src='https://kseul.github.io/imgs/img.jpg' alt='이미지' />

2-4. html에서 public 폴더 이미지 사용하기

  • /이미지경로 로 해주면 된다.
    <img arc="/logo.png" width="80">
  • import 를 따로 해주지 않아도 되어서 편하지만 권장되는 방식은 아래와 같다.
    <img src={process.env.PUBLIC_URL + '/logo.png'} />
  • ❓ 왜 이렇게 사용할까? 🤔 ✍️ 리액트로 개발을 끝내면 build 작업을 한다.
    - src의 파일은 리액트 사이트 발행 전 html, js, css 파일을 압축한다.(bundling) 이때 모든 파일은 압축되거나 파일명이 변하는 식으로 변경이 될 수 있다.
    - 하지만 public 안의 파일은 압축이 되지 않으므로 그 형태나 이름이 온전히 보존 되기 때문에 이미지나 txt, json 등 수정이 필요없는 static 파일들의 경우에는 public 폴더에 보관해도 상관없다. ✍️ 리액트로 만든 html 페이지를 배포할 때 a.com 경로에 배포하면 문제 없지만 http://a.com/어떤경로/ 경로에 배포하면 파일을 찾을 수 없다고 나올 수 있다. 이때 /어떤경로/ 를 뜻하는 process.env.PUBLIC_URL 를 더해주면 된다고 한다. (RCA)


3. 다른 파일에서 변수&함수 가져오기

3-1. 한개만 내보낼 때

  • 다른 js파일에서 변수를 export 하고 import 해서 가져온다.
  • 이때 원하는 변수명으로 지정하여 가져올 수 있다.
// b.js
let a = 10;
export default a // 변수명을 적어준다.

// a.js
import isA from './data.js';  // js는 생략해도 가능

❗️ ✍️ 가져올 때 default로 내보냈는데 {} 를 사용해서 {isA} 이런식으로 가져오니 오류가 발생했다. 주의하자.


3-2. 여러개를 내보낼 때

  • 여러개를 export 하려면 export{변수1, 변수2} 와 같이 해야한다.
  • 가져올 때도 따로 작명한 변수를 쓸 수 없고 그대로 사용한다.
    🧐 import { a as isA } from "my-module.js" 과 같이 따로 직접 지정을 할 수 있지 않을까? 필요할 때 알아봐야 겠다.
// b.js
let a = 10;
let b = 10;
export { a, b }; 


// a.js
import { a, b } from './data';



4. 컴포넌트 분리하기

📌 과제)

  • 상품목록을 컴포넌트로 만들고 데이터바인딩 하기
  • 반복적인 html이나 컴포넌트에서 map 반복문 사용하기.

기존의 상품 리스트를 보여주는 코드는 아래와 같았다.

<Container>
  <Row>
    <Col>
      <img
        src='https://codingapple1.github.io/shop/shoes1.jpg'
        alt='shoes1'
        width='80%'
      />{' '}
      <h4>{shoes[0].title}</h4> <p>{shoes[0].content}</p>
    </Col>
    <Col>
      <img
        src='https://codingapple1.github.io/shop/shoes2.jpg'
        alt='shoes2'
        width='80%'
      />{' '}
      <h4>{shoes[1].title}</h4> <p>{shoes[1].content}</p>
    </Col>
    <Col>
      <img
        src='https://codingapple1.github.io/shop/shoes3.jpg'
        alt='shoes3'
        width='80%'
      />{' '}
      <h4>{shoes[2].title}</h4> <p>{shoes[2].content}</p>
    </Col>
  </Row>
</Container>;

여기서 Component를 만들어서 따로 분리해 보았다. ▼

// App.js
<Container>
  <Row>
    {shoes.map((list, i) => ( // 1)
      <Product shoes={shoes} i={i} key={shoes[i].id}></Product>
    ))}
  </Row>
</Container>

// Product
function Product({ shoes, i }) { // 2)
  return (
    <Col>
      <img
        src={`https://codingapple1.github.io/shop/shoes${i + 1}.jpg`} // 3)
        alt='shoes'
        width='80%'
      />{' '}
      <h4>{shoes[i].title}</h4> <p>{shoes[i].content}</p> // 3)
    </Col>
  );
}
  • 먼저 Product라는 컴포넌트를 만들고 기존 App.js 로 불러올 수 있도록 export, import 해준다.

  • 1) Product 컴포넌트가 중복적으로 사용되지 않도록 map() 을 사용했다.
    또, useState를 통해 받아온 데이터를 props로 넘겨 주었다. 이 때 키도 같이 넘겨준다.

  • 2) Product의 파일에서 해당하는 데이터를 참조할 수 있도록 props를 받아온다.
    여기서 props를 생략, 간단하게 가져오기 위해 { shoes, i } 와 같이 할당해 보았다.

  • 3) 과 같이 map() 을 통해 받아온 데이터와 인덱스 데이터를 넣어줌으로써 동적으로 데이터가 바인딩 되도록 구현해 보았다. 다행히 잘 작동되는 것을 볼 수 있었다. 😀




5. 라우팅 하기

페이지를 나누어서 사용자에게 각각의 페이지를 보여준다고 가정한다.

  1. 리액트를 사용하지 않았을 경우
    : html 파일을 만들고 상세페이지 내용을 채운 뒤 html 파일을 전송한다.

  2. 리액트를 사용할 경우
    : 리액트는 기본적으로 SPA 로써, html을 하나만 사용한다. (index.html)
    따라서 디테일 페이지를 컴포넌트로 만들어 놓고 상세페이지 내용을 채운 뒤 기존 html을 비우고 원하는 페이지(컴포넌트)를 보여준다.

📌 이때, 페이지를 구분하는 라우팅을 쉽게 할 수 있도록 도와주는 리액트 라이브러리(react-router-dom)를 사용할 수 있겠다.

import { Routes, Route, Link } from 'react-router-dom';
<Routes>
  <Route
  path='/'
  element={
    <>
      ...
    </>
  }
  />
  <Route path='/detail' element={<div>상세페이지</div>} />
</Routes>
  • 이런식으로 Route를 작성하면 페이지 경로에 따라서 다른 컴포넌트가 보일 수 있게된다. 😀
  • 그런데 사용자는 해당 페이지의 경로가 /detail 인지 혹은 /about 등.. 인지 알 수가 없다.
  • 따라서 보통은 페이지 이동기능 버튼을 만들게 된다. <Link> 태그를 사용하자.
<Link to='/'></Link>
<Link to='detail'>상세페이지</Link>

📌 과제) 컴포넌트 분리하기

function App() {
  let [shoes] = useState(data);

  return (
    <div className='App'>
      <Navbar bg='dark' variant='dark'>
        <Container>
          <Navbar.Brand href='#home'>ShoeShop</Navbar.Brand>
          <Nav className='me-auto'>
            <Link className='nav-menu' to='/'>
              Home
            </Link>
            <Link className='nav-menu' to='detail'>
              Detail
            </Link>
          </Nav>
        </Container>
      </Navbar>

      <Routes>
        <Route path='/' element={<Home shoes={shoes} />} />
        <Route path='/detail' element={<Detail />} />
      </Routes>
    </div>
  );
}
  • <Routes> <Route> <Link> 를 통해서 라우팅을 구현했다.
  • pages 폴더를 만들고 <Home> <Detail> 컴포넌트를 분리해서 보기쉽게 작성해 보았다. 😀



6. useNavigate 적용하기

useNavigate 훅은 페이지의 이동을 도와준다.

import { Routes, Route, Link, useNavigate, Outlet } from 'react-router-dom';
  • import 해온 뒤 작성한다.
  • useNavigate 더 알아보기 ✍️
<Navbar bg='dark' variant='dark'>
  <Container>
    <Navbar.Brand href='#home'>ShoeShop</Navbar.Brand>
    <Nav className='me-auto'>
      <Nav.Link
        onClick={() => {
          navigate('/');
        }}
        className='nav-menu'
        to='/'
      >
        Home
      </Nav.Link>
      <Nav.Link
        onClick={() => {
          navigate('/detail');
        }}
        className='nav-menu'
        to='detail'
      >
        Detail
      </Nav.Link>
    </Nav>
  </Container>
</Navbar>



7. 404페이지

유효하지 않은 페이지에 접근했을 때 미리 만들어둔 페이지를 보이도록 한다.

  • path='*' 을 이용한다.
<Route path='*' element={<div>없는 페이지 입니다.</div>} />



8. Nested Routes

  • 페이지 안에서 또 그 안의 다른 페이지들을 보여주고 싶을 때 사용할 수 있겠다.
    ex.) /about 페이지 안에서 /about/member 페이지와 /about/location 페이지 등을 보여주고 싶을 때
// 아래 두 가지 코드는 같은 동작을 한다.
// 1)
<Route path='/about/' element={<About />} />
<Route path='member' element={<div>멤버정보</div>} />
<Route path='/about/location' element={<About />} />


//2) nested
<Route path='/about/' element={<About />}>
  <Route path='member' element={<div>멤버정보</div>} /> //  /about/member
  <Route path='location' element={<About />} /> //  /about/location
</Route>


// About
const About = () => {
  return (
    <div>
      <h4>About 페이지 / 회사 정보</h4>
      <Outlet></Outlet>
    </div>
  );
};
  • Nested Routes 문법을 사용하면 route 작성이 조금 더 간단해진다.
  • 또 만약 /about/member 경로로 접속할 경우 element가 2개가 보이게 된다.( aboutabout/member )
    ❗️ 단, <Outlet> 을 통해서 <About> 내부 어디에 보여줄지 작성을 해주자.
  • <Outlet> 은 nested routes의 element 를 보여주는 장소이다.

📌 과제) nested routes 활용하기

<Route path='/event/' element={<Event />}>
  <Route path='one' element={<p>첫 주문시 5% 할인쿠폰 지급</p>} />
  <Route path='two' element={<p>생일기념 쿠폰받기</p>} />
</Route>


const Event = () => {
  return (
    <div>
      <h4>오늘의 이벤트</h4>
      <Outlet></Outlet>
    </div>
  );
};
  • 앞에서 배운 내용을 토대로 nested routes를 구현했다. 😀

✍️ 리액트 라우터

뒤로가기 버튼 이용이 가능해진다.
페이지 이동이 쉽다. (UI 스위치 조작이 쉽다.)

✍️ nested 는 언제 사용할까?

여러 유사한 페이지가 필요할 때 사용하면 좋다.




9. Styled-components

스타일컴포넌트 사용하기

  1. npm 설치를 한다.
npm install styled-components
  1. 사용하고 싶은 컴포넌트 상단에서 import 해온다.
import styled from 'styled-components'

ex.)

let YellowBtn = styled.button`
  background: ${(props) => props.bg};
  color: ${(props) => (props.bg == 'blue' ? 'white' : 'black')};
  padding: 10px;
`;

  <YellowBtn bg='blue'>버튼</YellowBtn>

✍️ 스타일컴포넌트의 장점 : 스타일이 다른 js파일로 오염되지 않는다.(css module로도 가능) 추가문법을 통해 props로 재활용이 가능하다.

→ 리액트는 코드를 하나로 합쳐주므로 style을 모든 곳에서 가져다 쓸 수 있기 때문에 스타일이 겹치는 일이 발생할 수 있다. 하지만 스타일컴포넌트에서 사용되는 스타일은 해당 페이지 내부에서만 사용되므로 다른 파일로 오염되는 것을 방지할 수 있다.

✍️ 스타일컴포넌트의 단점 : JS파일이 복잡해지고, 이 컴포넌트가 styled인지 일반 컴포넌트인지 구분이 어려워진다. 라이브러리 지식이 필요하다. JS파일간 중복 디자인이 많이 필요하면 import해와서 사용할 수 있는데, 그럼 기존 CSS사용과 크게 다르지 않다는 점이 있다.

따라서 상황에 따라 어떤 방법으로 스타일을 적용할 건지 고민해볼 필요가 있겠다! 🤔

profile
기억보단 기록을 ✨

0개의 댓글