본문 바로가기
React

230705 리액트 hnm 쇼핑몰 사이트

by hyerin1201 2023. 7. 5.

 

핵심내용

*가상서버 : json 데이터를 화면에 출력하는 방법..

*로그인 여부 : 출력되어지는 페이지 > 렌더링

*검색기능 

*배포 : 가상서버 데이터 배포 


 

https://www.npmjs.com/package/json-server

 

json-server

Get a full fake REST API with zero coding in less than 30 seconds. Latest version: 0.17.3, last published: 3 months ago. Start using json-server in your project by running `npm i json-server`. There are 315 other projects in the npm registry using json-ser

www.npmjs.com

1. 제이슨 서버 npm 설치 아래 명령어 터미널에 입력

npm install -g json-server

 

2. db.json 파일 만들기

(root 폴더에) -

.

 

3. 터미널에 명령어 입력 ( 파일 읽기)

json-server --watch db.json

 

4. 터미널에 명령어 입력 (3004 포트)

가상의 서버를 만든 후. json 파일을 읽어 출력시켜주는 명령어

(3004포트로 만든 후 새 터미널을 열어 npm start 실행시켜야한다 화면이보여지고 랜더링 되어지는 페이지는 3000포트 두가지 포트를 사용해야함)

배포를 할때도 다른방법이 필요함(가상서버에서 데이터를 끌어오기 때문에)

$ json-server --watch db.json --port 3004

/2 2번값,.각각의 값만 가져올 수도 있음

 

5. fetch 함수 사용하여 비동기방식useEffect ( 컴퍼넌트가 랜더링이 될때마다 보여줄때 ) 사용

json 데이터 불러오기 

(ProductAll.js 에 데이터를 가져온다)

  const getProduct = async () => {
    let url = `http://localhost:3004/products`;
    let response = await fetch(url);
    let data = await response.json();
    console.log(data);
  };

  useEffect(() => {
    getProduct();
  }, []);

컴포넌트가 랜더링 될때 마다 getProduct( ) 함수 실행

콘솔에 찍히는 데이터는 아래와 같다

 

6. 데이터를 출력*- useState ( ) 사용

제이슨 데이터를 배열 타입으로 되어있기에 useState의 초기값을 가져올땐 배열타입으로 가져온다

const [productList, setProductList] = useState([ ]);

*웹브라우저 화면에 데이터를 출력하고자 할때 : useEffect ( ) 

*웹브라우저 화면에 출력되고 있는 내용이 변경되었을때 변경된 데이터를 다시 출력 및 어딘가에 저장 할 때 : useState( )

 

 

7. 새로운 컴포넌트 ProductCard.js 파일 생성 (각각의 데이터를 하나씩 보여줄 컴포넌트 생성)

ProductAll.js 에 ProductCard.js 파일 import 및 리턴(랜더링) 부분에 삽입.

 

ProductAll.js에

*리액트 부트스트랩을 사용하여 그리드 정렬 (레이아웃)

import { Container, Row, Col } from "react-bootstrap";

 

 

8. 로그인페이지를 눌러야만 상세페이지를 볼 수 있도록 / 로그인을 했는지 안했는지 확인할 수 있도록 설정하기.

(첫 메인은 로그인이 안된 상태)

 

 

*useState ( ) 사용

App.js에서 실행.

  const [authenticate, setAuthenticate] = useState(false);
  //true- 로그인 false - 로그인 안된 상태
<Route
          path="/login"
          element={<Login setAuthenticate={setAuthenticate} />}
        />

Login 페이지로 데이터 전달.

 

Login.js

  const loginUser = (e) => {
    e.preventDefault();
    console.log("login user");
    setAuthenticate(true);
    navigate("/"); //로그인 버튼을 누르면 다시 홈으로
  };

클릭이 되면 기본값 -> true 로변경

 

App.js

  const [authenticate, setAuthenticate] = useState(false);
  //true- 로그인 false - 로그인 안된 상태
  useEffect(() => {
    console.log("Login", authenticate);
  }, [authenticate]); //로그인값이 변경될때 마다 useEffect 콜백함수 실행

콘솔창 출력값

 

8-1 로그인이 된 사람만 아이템 클릭시 상세페이지를 보도록 / 안된사람이 클릭시 로그인페이지로 연결

Route - redirecting ( 조건부 랜더링 걸기) 위한 컴포넌트 생성 -> PrivateRouter.js파일 생성

 

App.js

<Route path="/product/:id" element={<ProductDetail />} />

기존의 코드를

<Route
          path="/product/:id"
          element={<PrivateRouter authenticate={authenticate} />}
        />

로 변경 및 로그인 확인 값 전달

 

PrivateRouter.js

import React from "react";
import ProductDetail from "./ProductDetail";
import { Navigate } from "react-router-dom";

const PrivateRouter = ({ authenticate }) => {
  return authenticate == true ? <ProductDetail /> : <Navigate to="/login" />;
};

export default PrivateRouter;

조건부랜더링 실행

 

ProductCard.js

const ProductCard = ({ item }) => {
  const navigate = useNavigate();
  const showDetail = () => {
    navigate(`/product/${item.id}`); //상품고유의 아이디값 url 연결 설정
  };
  return (
    <div onClick={() => showDetail()} style={{ cursor: "pointer" }}>
      <img src={item.img} />
      <div>Conscious Choice</div>
      <div>{item.title}</div>
      <div>{item.price}</div>
      <div>{item.new == true ? "신제품" : ""}</div>
    </div>
  );
};

각 카드에 클릭하면 연결값 설정

 

 

9. 프로덕트 상세페이지 설정

url 의 데이터값 을 불러오기 

import React from "react";
import { useEffect } from "react";
import { useParams } from "react-router-dom";
//파라미터값을 가져오기위한 훅

const ProductDetail = () => {
  let { id } = useParams(); //파라미터 값은 객체값을 반환
  const getProductDetail = async () => {
    let url = `http://localhost:3004/products/${id}`;
    let response = await fetch(url);
    let data = await response.json();
    console.log(data);
  }; //외부데이터 불러오기
  useEffect(() => {
    getProductDetail();
  },  [ ]);
  return <div>상품 상세페이지</div>;
};

export default ProductDetail;

위 코드 콘솔창에 찍히는 값은 아래와 같음

 

9-1 불러온 데이터 출력해준다.

const ProductDetail = () => {
  let { id } = useParams(); //파라미터 값은 객체값을 반환
  const [product, setProduct] = useState(null); //초기값이 null이면 에러가뜸.그래서 []을 넣으면 해결되거나
  const [loading, setLoading] = useState(false); //로딩값을 하나더 만들고
  const getProductDetail = async () => {
    let url = `http://localhost:3004/products/${id}`;
    let response = await fetch(url);
    let data = await response.json();
    console.log(data);
    setLoading(false); //로딩값
    setProduct(data);
  }; //외부데이터 불러오기
  useEffect(() => {
    getProductDetail();
  }, []);
  if (loading || product == null) return <h1>Loading</h1>; //로딩값출력

  return (
    <Container>
      <Row>
        <Col className="product-detail-img">
          <img src={product.img} />
        </Col>
        <Col>
          <div className="product-info">{product.title}</div>
          <div className="product-info">{product.price}</div>
          <div className="choice">
            {product.choice ? "Conscious Choice" : ""}
          </div>
          <Dropdown className="drop-down">
            <Dropdown.Toggle variant="outline-dark" id="dropdown-basic">
              사이즈 선택
            </Dropdown.Toggle>

            <Dropdown.Menu>
              {product.size.map((item) => (
                <Dropdown.Item href="#/action-1">{item}</Dropdown.Item>
              ))}
            </Dropdown.Menu>
          </Dropdown>
          <Button className="add-button" variant="dark">
            추가
          </Button>
        </Col>
      </Row>
    </Container>
  );
};

 

 

10. 검색기능

Navbar.js 

  const search = (e) => {
    if (e.key == "Enter") {
      let keyword = e.target.value;
      console.log("keyword", keyword);
      navigate(`/?q=${keyword}`) // url 뒤에 쿼리값을 붙임
    }
  };

값을 부여한 이유 => ProductAll.js 에서 useSearchParams( ) 사용

        <div className="search-box">
          <FontAwesomeIcon icon={faSearch} />
          <input
            type="text"
            placeholder="제품 검색"
            onKeyPress={search}
          ></input>
        </div>

 

ProductAll.js

const ProductAll = () => {
  const [productList, setProductList] = useState([]);
  const [query, setQuery] = useSearchParams(); // url값, url업데이트 함수
  const getProduct = async () => {
    let searchQuery = query.get("q") || ``; //get을 사용하여 q뒤의 값을 가져온다.
    console.log("쿼리값은?", searchQuery);
    let url = `http://localhost:3004/products?q=${searchQuery}`; //url값 수정..!!
    console.log(url);
    let response = await fetch(url);
    let data = await response.json();
    //console.log(data);
    setProductList(data); //초기값 = data
  };

  useEffect(() => {
    getProduct();
  }, [query]); //의존성 배열에 쿼리값 추가

  return (
    <div>
      <Container>
        <Row>
          {productList.map((item) => (
            <Col md={3} sm={12} key={item.id}>
              <ProductCard item={item} />
            </Col>
          ))}
        </Row>
      </Container>
    </div>
  );
};

 

 

11. 로그인이 되었을때 로그인 -> 로그아웃으로 변경되도록

App.js

에서 return문에 있는  <Navbar /> 컴포넌트에 Props로 데이터 전달한다.

<Navbar authenticate={authenticate} setAuthenticate={setAuthenticate} />

Navbar.js

에서 Props값을 가져온뒤  return 문에 로그아웃/ 로그인일 경우의 값을 출력해주도록 추가해준다.

  return (
    <div>
      <div className="nav-header">
        {authenticate ? (
          <div onClick={() => setAuthenticate(false)}>
            <FontAwesomeIcon icon={faUser} />
            <span style={{ cursor: "pointer" }}>로그아웃</span>
          </div>
        ) : (
          <div onClick={() => navigate("/login")}>
            <FontAwesomeIcon icon={faUser} />
            <span style={{ cursor: "pointer" }}>로그인</span>
          </div>
        )}
      </div>
      <div className="nav-logo">
        <img
          width={100}
        />
      </div>
      <div className="nav-menu-area">
        <ul className="menu">
          {menuList.map((menu, index) => (
            <li>
              <a href="#" key={index}>
                {menu}
              </a>
            </li>
          ))}
        </ul>
        <div className="search-box">
          <FontAwesomeIcon icon={faSearch} />
          <input
            type="text"
            placeholder="제품 검색"
            onKeyPress={search}
          ></input>
        </div>
      </div>
    </div>
  );

 

12. 배포 전 가상서버 생성

가상서버를 활용하여 데이터를 가져왔을때 배포하는 법 ( * Git )

 

https://my-json-server.typicode.com/

 

My JSON Server - Fake online REST server for teams

my-json-server.typicode.com/user/repo/posts/1 { "id": 1, "title": "hello" }

my-json-server.typicode.com

  1. Visit https://my-json-server.typicode.com/<your-username>/<your-repo> to access your server
  2.  https://my-json-server.typicode.com/Minhyerin/reactshoppingmall

ProductAll.js / ProductDatail.js에 사용된 가상서버 데이터 url 값에 해당 주소를 입력한다 

 

git bash 사용

- git init

- git remote add origin https://github.com/Minhyerin/reactshoppingmall.git

- git push

 

ssh 발급하는 법 git bash 에 

ssh-keygen 입력

-> c드라이브/사용자/adminstrator/.ssh 파일 생김

-> id_rsa: 비공개 privat 키

-> id_rea.pub : 공개 public 키

 

git bach 에

cat ~/.ssh/id_rsa.pub 입력

-> 키값이 나옴 (ssh로 시작) - 복사 - 깃헙에 추가


12-1 배포(호스팅)

*파이어베이스 사용 가능

(그동안 한 호스팅 방법 : 닷홈, 파일질라, 파이어베이스)

*git 에있는 데이터를 보다 쉽게 배포 하는 법 :  netlify

-> git으로 로그인

 


vsCode -> Git Bash 사용 (*Git Bash사용하는 이유 : CLI개념의 명령문을 쓸때는 리눅스 / Mac 중심으로 최적화되어있음. 따라서 powershell을 사용하면 제약이많음)

 

*Git이란? : 개발자가 작업한 소스코드를 저장할 수 있는 공간 - 버전관리를 위해 - 여러 다수의 개발자의 협업과 공유를 위해 

 

git 을 통해 데이터 전송하는 방법 (git bash 터미널)

1.사용자 이름 / 이메일 

git config --global user.name "your_name"

git config --global user.email "your_email"  -> 깃 가입시 입력한 이메일

 

1-1. 이름 / 이메일 정보 확인

git config --list

 

2.깃 초기화

git init

 

3. 깃 업로드할 데이터 선택

git add . : 현재 루트폴더안에 있는 모든 데이터 업로드

git add index.html : 특정파일만 업로드 

 

4. 깃 업로드하고자 하는 데이터 상태 확인

git status

 

5. 깃에 업로드할 데이터에대한 히스토리 만들기

git commit -m "message"

 

6. 깃 리모트 ( 올리고자 할 레퍼지토리와 연결)

git remote add origin (레퍼지토리 주소)

6-1 연결확인

git remote -v

 

7. 데이터 업로드하기

git push origin main

git push origin master (마스터 브랜치)

 

*깃 클론 하기 (데이터 받아오기)

팀리더가 새로운 레파지토리 생성 ->

git clone 레파지토리주소 작업할파일명(신규폴더생성)

-> 신규폴더로  이동

cd 폴더명

-> 루트폴더로 파일열기

code .

 

*클론해서 받아온 작업물 다시업로드 (협업시) ****

-> git checkout -b 브랜치이름

(master 가 아닌 브랜치하나 만들기)

-> 브랜치가 바뀐 상태에서 업로드

git push origin 브랜치이름

 

*기존 연결된 레파지토리에서 신규레파지토리 연결

-> git remote set-url origin 새로운레파지토리주소