Web Project/dev

dev) 쇼핑몰 프로젝트 회고

이동탁 2023. 2. 14. 14:57

담당하여 구현한 기능 회고

 

- 로그인

 

import React, { useState } from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
import Button from "react-bootstrap/Button";
import Form from "react-bootstrap/Form";
import FloatingLabel from "react-bootstrap/FloatingLabel";
import axios from "axios";

const LoginForm = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  // email, password를 보내는 post 요청
  const handleSubmit = (e) => {
    e.preventDefault();

    const formdata = { email, password };

    const onSubmit = () => {
      axios
        .post("http://localhost:8080/login", { ...formdata })
        .then((res) => {
          if (res.data.role === "admin") {
            localStorage.setItem("adminToken", res.data.token);
            alert("관리자 로그인 되었습니다");
            // 로컬스토리지에 토큰이 들어온 상태를 인식시키기 위하여 새로고침으로 href로 이동
            window.location.href = "/";
          } else {
            localStorage.setItem("accessToken", res.data.token);
            alert("로그인 완료");
            window.location.href = "/";
          }
          // 로컬스토리지에 토큰이 들어온 상태를 인식시키기 위하여 새로고침으로 href로 이동
        })
        .catch((err) => {
          // 아이디 비밀번호를 따로 에러 출력시 보안상 문제가 생길 수 있어 통합하여 alert
          console.log(err)
          alert('아이디, 비밀번호를 확인해주세요.');
        });
    };

    onSubmit();
  };

  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
      }}
    >
      <Login>
        <FloatingLabel controlId="floatingInput" label="이메일" className="mb-3" onChange={(e) => setEmail(e.target.value)}>
          <Form.Control type="email" />
        </FloatingLabel>
        <FloatingLabel controlId="floatingPassword" label="비밀번호" onChange={(e) => setPassword(e.target.value)}>
          <Form.Control type="password" />
        </FloatingLabel>
        <div
          style={{
            display: "flex",
            justifyContent: "flex-end",
            padding: "20px",
          }}
        >
          <Button
            type="submit"
            style={{
              borderRadius: "5px",
              backgroundColor: "grey",
              borderColor: "grey",
            }}
            onClick={handleSubmit}
          >
            로그인
          </Button>
          <Button
            style={{
              marginLeft: "5px",
              borderRadius: "5px",
              backgroundColor: "grey",
              borderColor: "grey",
            }}
          >
            <Link
              to="/RegisterForm"
              style={{
                textDecoration: "none",
                color: "white",
              }}
            >
              회원가입
            </Link>
          </Button>
        </div>
      </Login>
    </div>
  );
};

const Login = styled.form`
  padding: 300px;
`;

export default LoginForm;

 

깨달은 점

 

1) JWT 토큰을 Local Storage에 담는 방식에 대한 이해

2) 해당 토큰에 role 등의 정보를 같이 담아와서 구분 후 key값을 다르게 주어 관리 하는 방법에 대한 이해

3) 로그인 시 오류 alert의 보안상 이슈에 대한 이해

4) 로그인 이후 SPA로 인해 Local Storage값의 변경이 적용 되지 않아 로그인 이후 홈으로 이동을 window.location.href로 하여 새로고침을 시켰고 따라서 무조건 적인 SPA가 아닌 기능에 따라 새로고침이 필요함을 깨달음

5) Form 태그를 사용하여야 키보드의 엔터 조작으로 로그인이 가능함을 깨달음

 

아쉬운 점

 

1) 정규식 표현에 대한 이해가 부족하여 정규식을 이용하여 이메일 비밀번호 검증 구현 X

2) async await를 이용하여 try catch문으로 구현하지 않은 점이 아쉬우나 다음 프로젝트에서는 구현 예정

3) 초반에 비밀번호 아이디가 틀릴 시 따로 alert을 하려고 했으나, 보안적인 문제 (비밀번호 아이디가 틀렸다고 따로 alert이 될 경우 해킹의 위험이 있음) 로 통합으로 error alert

4) JWT 토큰을 Local Storage에 담아서 구현 하였으나, JWT 토큰의 유효기간을 정하지 않아 자바스크립트로 접근하여 해킹하기 쉬운 단점이 있음, 이후 같은 방식으로 구현시 refresh token을 이용하여 토큰의 유효 기간을 두고 새로 발급받아 Local Storage로 넣는 로직을 구현할 예정

 

 


 

 

- 회원가입

 

import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import styled from 'styled-components';
import { Button , Col , Form , Row } from 'react-bootstrap';
import axios from 'axios';

const RegisterForm = () => {
  const [email, setEmail] = useState('');
  const [name, setName] = useState('');
  const [password, setPassword] = useState('');
  const [pwcheck, setPwcheck] = useState('');
  const [phoneNumber, setPhoneNumber] = useState('');
  const [address, setAddress] = useState('');
  const navigate = useNavigate();

  // post로 데이터 등록
  const handleSubmit = (e) => {
    
    e.preventDefault();
    
    const formData = {
      email,
      name,
      password,
      phoneNumber,
      address
    }
    const onSubmit = () => {

      // 비밀번호가 같다면 회원가입 가능 다르면 alert
      password === pwcheck ?
      // formData로 묶은 값을 구조분해해서 전달
      (axios
        .post("http://localhost:8080/join", { ...formData })
        .then(() => {
          alert('회원가입이 완료되었습니다.')
          navigate("/LoginForm")
        })
        .catch((err) => {
          alert('에러가 발생했습니다. 다시 시도해주세요.')
        })) : (alert('비밀번호가 틀립니다. 다시 확인해 주세요.'))
    }
    onSubmit();
  }

  return (
    <Register>
    <Form style = {{ width : 600 }}>
      
          {/* 아이디 */}
          <Row className="mb-3" >
            <Form.Group as={Col} md="12" controlId="validationCustom02">
              <Form.Label>아이디</Form.Label>
              <Form.Control
                required
                type="text"
                placeholder="Elice@elice.com"
                defaultValue=""
                value= { email }
                onChange={ (e) => setEmail(e.target.value)}
                />
              <Form.Control.Feedback></Form.Control.Feedback>
            </Form.Group> 
          </Row>
          
          {/* 이름 */}
          <Row className="mb-3">
          <Form.Group as={Col} controlId="validationCustom02">
            <Form.Label>이름</Form.Label>
            <Form.Control
              required
              type="text"
              placeholder="홍길동"
              defaultValue=""
              value={ name }
              onChange={ (e) => setName(e.target.value)}
            />
            <Form.Control.Feedback></Form.Control.Feedback>
          </Form.Group>
          </Row>

          {/* 비밀번호 */}
          <Row className="mb-3">
          <Form.Group as={Col} controlId="validationCustom02">
            <Form.Label>비밀번호</Form.Label>
            <Form.Control
              required
              type="password"
              // type을 password로 해주어야 안보이게 타이핑 가능
              placeholder="*****"
              defaultValue=""
              value={ password }
              onChange={ (e) => setPassword(e.target.value)}
            />
            <Form.Control.Feedback></Form.Control.Feedback>
          </Form.Group>

          {/* 비밀번호 확인 */}
            <Form.Group as={Col} controlId="validationCustom02">
              <Form.Label>비밀번호 확인</Form.Label>
              <Form.Control
                required
                type="password"
                placeholder="*****"
                defaultValue=""
                onChange={ (e) => setPwcheck(e.target.value)}
                // onChange로 앞에 적은 password와 같은지 확인해야 함
              />
              <Form.Control.Feedback>비밀번호가 일치합니다.</Form.Control.Feedback>
            </Form.Group>
            </Row>

          {/* 전화번호 */}
          <Row className="mb-3">
          <Form.Group as={Col} controlId="validationCustom02">
            <Form.Label>전화번호</Form.Label>
            <Form.Control
              required
              type="text"
              placeholder="010-1234-5678"
              defaultValue=""
              value={ phoneNumber }
              onChange={ (e) => setPhoneNumber(e.target.value)}
            />
            <Form.Control.Feedback>Looks good!</Form.Control.Feedback>
          </Form.Group>
          </Row>

          {/* 주소 */}
          <Row className="mb-3">
          <Form.Group as={Col} controlId="validationCustom02">
            <Form.Label>주소</Form.Label>
            <Form.Control
              required
              type="text"
              placeholder="서울특별시 광진구 화양동 123-456 1동 2호"
              defaultValue=""
              value={ address }
              onChange={ (e) => setAddress(e.target.value)}
            />
            <Form.Control.Feedback>Looks good!</Form.Control.Feedback>
          </Form.Group>
          </Row>
          <div style={{
            display : 'flex',
            justifyContent : 'flex-end'
          }}>
            <Button type="submit" style = {{
              backgroundColor : 'grey',
              border : 'grey',
            }} onClick = { handleSubmit }>회원가입</Button>
        </div>
      </Form>
    </Register>
  );
};

const Register = styled.div`
    display : flex;
    justify-content : center;
    padding : 150px;
`

export default RegisterForm;

 

깨달은 점

 

1) 정보를 담아 백엔드 DB로 axios를 이용하여 POST 전송하는 방법에 대한 이해

2) 작성한 비밀번호를 다시 한번 일치하는지 확인 함에 따라서 로그인 이전 본인이 설정한 비밀번호를 기억하게 할 수 있는 방법에 대한 이해

3) 리액트 부트스트랩에서 필요한 부분을 사용함으로서 CSS적인 부분을 좀 더 깔끔하게 사용이 가능

4) Form 태그를 사용하여 onChange한 정보를 담는 방법에 대한 이해

5) 담은 정보를 받는 백엔드 API와의 interface를 맞추어 주고 받는 방법에 대한 이해

 

아쉬운 점

 

1) 로그인과 마찬가지로 정규식 표현을 사용하지 못하였음

2) 도로명 주소 찾기 등의 상세한 주소 설정을 하지 못하였음

3) 리액트 부트스트랩의 세세한 값 조정이 불가능 하여 유지보수 시 리액트 부트스트랩 없이 사용하는 방법을 더욱 준비해 가야 된다고 느낌

 

 

 


 

 

- 회원정보 수정

 

import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import styled from "styled-components";
import Button from "react-bootstrap/Button";
import Col from "react-bootstrap/Col";
import Form from "react-bootstrap/Form";
import Row from "react-bootstrap/Row";

const UserUpdate = () => {
  const [email, setEmail] = useState("");
  const [phoneNumber, setPhoneNumber] = useState("");
  const [address, setAddress] = useState("");
  const [data, setData] = useState("");
  const token = localStorage.getItem("accessToken");
  const navigate = useNavigate();

  const handleUpdSubmit = (e) => {
    // 새로고침 방지
    e.preventDefault();

    const formUpdData = {
      email,
      phoneNumber,
      address,
    };
    const onUpdSubmit = () => {
      // formUpdData로 묶은 값을 구조분해해서 전달
      // post로 회원 정보 변경

      axios
        .post("http://localhost:8080/users/edit/:userId", { ...formUpdData }, { headers: { Authorization: token } })
        .then((res) => {
          console.log(res)
          alert("회원 정보가 수정되었습니다.")
          navigate("/UserInfo")
        })
        .catch((err) => {
          console.log(err)
          alert('에러가 발생했습니다. 다시 시도해주세요.')
        })
    };
    onUpdSubmit();
  };
  useEffect(() => {
    axios
      .get("http://localhost:8080/users/mypage", { headers: { Authorization: token } })
      .then((response) => {
        setData(response.data);
      })
      .catch((error) => {
        console.log(error)
        alert('에러가 발생했습니다. 다시 시도해주세요.')
      });
  }, []);

  return (
    <Update>
      <Form style={{ width: 600 }}>
        {/* 아이디 */}
        <Row className="mb-3">
          <Form.Group as={Col} md="12" controlId="validationCustom02">
            <Form.Label>아이디</Form.Label>
            <Form.Control
              required
              type="text"
              placeholder={JSON.stringify(data.email)}
              defaultValue=""
              value={email}
              onChange={(e) => setEmail(e.target.value)}
            />
            <Form.Control.Feedback></Form.Control.Feedback>
          </Form.Group>
        </Row>
        {/* 전화번호 */}
        <Row className="mb-3">
          <Form.Group as={Col} controlId="validationCustom02">
            <Form.Label>전화번호</Form.Label>
            <Form.Control
              required
              type="text"
              placeholder={JSON.stringify(data.phoneNumber)}
              defaultValue=""
              value={phoneNumber}
              onChange={(e) => setPhoneNumber(e.target.value)}
            />
            <Form.Control.Feedback>Looks good!</Form.Control.Feedback>
          </Form.Group>
        </Row>
        {/* 주소 */}
        <Row className="mb-3">
          <Form.Group as={Col} controlId="validationCustom02">
            <Form.Label>주소</Form.Label>
            <Form.Control
              required
              type="text"
              placeholder={JSON.stringify(data.address)}
              defaultValue=""
              value={address}
              onChange={(e) => setAddress(e.target.value)}
            />
            <Form.Control.Feedback>Looks good!</Form.Control.Feedback>
          </Form.Group>
        </Row>
        <BtnDiv>
          <Button
            type="submit"
            style={{
              backgroundColor: "grey",
              border: "grey",
            }}
            onClick={handleUpdSubmit}
          >
            정보 수정
          </Button>
        </BtnDiv>
      </Form>
    </Update>
  );
};

const Update = styled.div`
  display: flex;
  justify-content: space-around;
  padding: 150px;
`;

const BtnDiv = styled.div`
  display: flex;
  justify-content: flex-end;
`;

export default UserUpdate;

 

- 회원탈퇴

 

import React, { useState } from "react";
import FloatingLabel from "react-bootstrap/FloatingLabel";
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";
import axios from "axios";
import styled from "styled-components";

const UserDelete = () => {
  const [password, setPassword] = useState("");
  const token = localStorage.getItem("accessToken");

  const handleSubmit = (e) => {
    e.preventDefault();
    const formData = { password };
    const onSubmit = () => {
      axios
        .post("http://localhost:8080/users/delete/:userId", { ...formData }, { headers: { Authorization: token } })
        .then((res) => {
          console.log(res)
          localStorage.removeItem("accessToken")
          alert("회원 탈퇴 되었습니다.")
          window.location.href = "/"
        })
        .catch((err) => {
          console.log(err)
          alert('에러가 발생했습니다. 다시 시도해주세요.')
        })
    };
    onSubmit();
  };

  return (
    <div style={{ margin: "20%" }}>
      <Delete>
        <FloatingLabel controlId="floatingPassword" label="비밀번호" value={password} onChange={(e) => setPassword(e.target.value)}>
          <Form.Control type="password" />
        </FloatingLabel>
        <div
          style={{
            margin: "10px",
            display: "flex",
            justifyContent: "center",
          }}
        >
          <Button
            type="submit"
            onClick={handleSubmit}
            style={{
              padding: "8px",
              borderRadius: "8px",
              borderColor: "white",
              backgroundColor: "red",
              color: "white",
            }}
          >
            회원 탈퇴
          </Button>
        </div>
      </Delete>
    </div>
  );
};

const Delete = styled.div`
  display: flex;
  justify-content: center;
`;

export default UserDelete;

 

깨달은 점

 

1) 페이지에서 Token의 정보를 받아 Token에 담긴 내용을 토대로 회원을 검색하고 회원의 정보를 수정하는 방법

2) 회원이 발급받은 JWT Token과 해당 회원의 DB의 정보를 삭제하는 방법

3) 회원 탈퇴 시 회원의 비밀번호를 받아 그 비밀번호를 백엔드 서버에서 hash화 된 패스워드와 비교하여 일치하면 삭제하게 하는 방법

 

아쉬운 점

 

1) 해당 기능들을 axios의 다른 방식이 아닌 post방식으로 구현한 점

2) 아이디의 변경이 자주 있는 일이 아닌데 아이디의 변경까지 구현한 점

3) 회원 정보 수정 및 탈퇴에 있어서 디테일이 부족한 점

 

 


 

 

- 회원 , 비회원 , 관리자 기능 다르게 구현

 

const BodyRoutes = () => {
  const Token = localStorage.getItem("accessToken");
  const AdminToken = localStorage.getItem("adminToken");
  const [categories, setCategories] = useState([]);

  useEffect(() => {
    axios
      .get(`http://localhost:8080/categories`)
      .then((response) => {
        setCategories(response.data.searchAll);
      })
      .catch((error) => {
        alert(error);
      });
  }, []);

  return (
    <div>
      <Router>
        <Container>
          <LogoDiv>
            <NavLink to="/">
              <img src={logo} alt="Logo" />
            </NavLink>
          </LogoDiv>
          <NavUl>
            {categories.map((category) => {
              return (
                <li key={category.categoryId}>
                  {AdminToken || !AdminToken === "null" ? <></> : <NavLink to={`/categories/${category.categoryId}`}>{category.name}</NavLink> }
                </li>
              );
            })}
            {/* AdminToken이 admin값일때 관리자페이지 노출 */}
            {/* <></>이 아닌 다른 방법으로 노출을 조정할 수 있을지 고민 */}
            <li>{AdminToken || !AdminToken === "null" ? <NavLink to="/AdminMain">관리자페이지</NavLink> : <></>}</li>
          </NavUl>
          <IconUl>
            {/* admin일때 사람아이콘 출력 x , 유저일때 usermain, 비회원일때 loginForm >> 삼항 연산자에 삼항 연산자를 넣어서 코드의 가독성이 조금 떨어 질 것 같아서 고민 */}
            {AdminToken || !AdminToken === "null" ? (
              <></> 
            ) : (
              <li>
                {Token || !Token === "null" ? (
                  <div>
                  <NavLink to="/UserMain">
                    <span className="material-symbols-outlined">person</span>
                  </NavLink>
                  <NavLink to="/Favorites">
                  <span className="material-symbols-outlined" style = {{
                    margin : '10px'
                  }}>favorite</span>
                  </NavLink>
                  <NavLink to="/payments/cart">
                  <span className="material-symbols-outlined">shopping_bag</span>
                  </NavLink>
                  </div>
                ) : (
                  <div>
                  <NavLink to="/LoginForm">
                    <span className="material-symbols-outlined">person</span>
                  </NavLink>
                  <NavLink to="/Favorites">
                  <span className="material-symbols-outlined" style = {{
                    margin : '10px'
                  }}>favorite</span>
                  </NavLink>
                  <NavLink to="/payments/cart">
                  <span className="material-symbols-outlined">shopping_bag</span>
                  </NavLink>
                  </div>
                )}
              </li>
            )}
            <li>
              {AdminToken || !AdminToken === "null" ? (
                <button
                  onClick={() => {
                    localStorage.removeItem("adminToken");
                    alert("관리자 로그아웃 되었습니다.");
                    window.location.href = "/";
                  }}
                  style={{
                    padding: "10px",
                    borderRadius: "5px",
                    borderColor: "white",
                    backgroundColor: "grey",
                    color: "white",
                  }}
                >
                  관리자 로그아웃
                </button>
              ) : ( <></>
              )}
            </li>

// 전체 BodyRoutes 중에 구현한 부분만 가지고 왔습니다.

 

! 따로 코멘트 할 만큼의 부분은 없으나 삼항 연산자 이외의 방법 더욱 고민해보기

 

 

- 장바구니에서 주문하기 시 가격 주문페이지로 넘어오기

 

import React, { useState, useEffect, useMemo } from "react";
import styled from "styled-components";
import { useNavigate } from "react-router-dom";
import { CARTLIST_KEY, NO_SHIPPING_FEE_PRICE } from '../../../constants/key'
import { formatCurrency } from '../../../lib/utils'


const Container = styled.div`
    display : flex;
    padding : 10px 80px;
`
const CartInfo = styled.div`
    width : 60%;
    margin : 10px;
    padding : 10px;
    box-shadow: 0 5px 10px grey;
    & h3 {
        border-bottom: 1px grey solid;
        padding-bottom: 10px;
      }
    & label {
        display : block;
        padding-bottom : 20px;
    }

    & input {
        width : 80%;
    }

    & h6 {

        font-weight : bold;
        weight : 30%;

        
    }
`
const CartItem = styled.div`
    border : 1px solid gray;
    & div {
        display : inline;
        margin : 15px;
    }
`

const ImgDiv = styled.div`
    border : 1px solid red;

    & img {
        width : 100px;
    }
`

const FormPaymentInfo = styled.form`
  box-shadow: 0 5px 10px grey;
  padding : 10px;
  width : 40%;
  margin : 10px;
  & h3 {
    border-bottom: 1px grey solid;
    padding-bottom: 10px;
  }

  & h4 {
    border-top: 1px grey solid;
    padding-top: 10px;
  }
  
  & button {
    width : 100%;
    height : 35px;
    background : grey;
    border : none;
    color : white;
  }
`
const DeleteAllBtn = styled.button`
    padding : 10px;
    border-radius : 5px;
    border-color : white;
    background-color : grey;
    color : white;
`

const Cart = ()=>{
    const navigate = useNavigate();
    const Token = localStorage.getItem("accessToken");
    const [items, setItems] = useState([]);
    const [isLoaded, setLoaded] = useState(false);
    const [countObject, setCountObject] = useState({});

  
    useEffect(() => {

        const savedCartList = localStorage.getItem(CARTLIST_KEY)

        const cartList = savedCartList ? JSON.parse(savedCartList) : []

        setItems(cartList)


        // count가 각 1개씩 들어가도록 초기세팅
        const newCountObject = cartList.reduce((acc, current) => {
            acc[current.id] = 1

            return acc
        }, {})

        setCountObject(newCountObject)

        setLoaded(true)
    }, []);

    const deleteHandler = (id) => {
        const savedCartList = localStorage.getItem(CARTLIST_KEY)
        const cartList = savedCartList ? JSON.parse(savedCartList) : []
        // 불러오고
        console.log(cartList)
        const filterCartList =  cartList.filter((a) => a.id !== id)
      
        console.log('filterCartList : ', filterCartList)
      
        setItems(filterCartList)
        localStorage.setItem(CARTLIST_KEY, JSON.stringify(filterCartList));
        alert("삭제하기 완료!")
       }
      
    const totalCount = useMemo(() => {
        return  Object.values(countObject).reduce((acc, current) => {
            acc = acc + parseInt(current)
            return acc
        }, 0)
    }, [countObject])

    const totalItemPrice = useMemo(() => {
        return items.reduce((acc, current) => {
            acc = acc + (parseInt(current.price) * parseInt(countObject[current.id]))

            return acc
        }, 0)
    }, [countObject, items])

    const shippingFee = useMemo(() => {
        return totalItemPrice >= NO_SHIPPING_FEE_PRICE ? 0 : 3000
    }, [totalItemPrice])

    const totalPrice = useMemo(() => totalItemPrice + shippingFee, [totalItemPrice, shippingFee])

    
    const SubmitHandler = (e)=>{
        e.preventDefault();
        
        // navigate에 state담아서 전달
        { Token || !Token === "null" ? navigate('/payments/order' ,{
            state : {
                // 값들이 formatCurrency로 변환되었으므로 풀어서 값을 전달
                ItemTotalCount : totalCount,
                ItemPrice : totalItemPrice,
                ItemShippingFee : shippingFee
            }}) : 
                    navigate('/LoginForm') }
    }
    if (!isLoaded) return <></>

    return <>
    <Container>
        <CartInfo>
            {items.map((item)=>{
                return <CartItem>
                    <ImgDiv><img src={item.imgUrl} alt="썸네일" /></ImgDiv>
                    <div>{item.productName}</div>
                    <div>{item.price}원</div>{" X "}
                     <input type="number" name="sku" onChange={(e) => {
                        const newCountObject = {...countObject}

                        newCountObject[item.id] = e.target.value

                        setCountObject(newCountObject)
                    }} defaultValue={countObject[item.id]}/>{" = "}
                    <div>{item.price*countObject[item.id]}원</div>
                    <button onClick = {() => deleteHandler(item.id)}>삭제하기</button>
                </CartItem>
            })}
            <DeleteAllBtn
                  onClick = {() => {
                      localStorage.removeItem(CARTLIST_KEY)
                      alert('장바구니 목록이 모두 삭제되었습니다.')
                      // 로컬스토리지에 토큰이 삭제된 상태를 인식시키기 위하여 새로고침으로 href로 이동
                      window.location.href = '/payments/cart'
                  }}
                  >전체삭제</DeleteAllBtn>
        </CartInfo>
        <FormPaymentInfo onSubmit={SubmitHandler}>
            <h3>결제정보</h3>
            <h5>상품수   {totalCount} 개</h5>
            <h5>상품금액  {formatCurrency(totalItemPrice)}원</h5>
            <h5>배송비  {formatCurrency(shippingFee)}원</h5>
            <h4>총 결제금액 {formatCurrency(totalPrice)}원</h4>
            <button>구매하기</button>
        </FormPaymentInfo>
    </Container>
    </>
}

export default Cart;

- 장바구니

 

import React, { useState , useEffect} from "react";
import styled from "styled-components";
import { useNavigate , useLocation } from "react-router-dom";
import axios from "axios";
import { formatCurrency } from '../../../lib/utils'

const Container = styled.form`
    display : flex;
    padding : 10px 80px;
`
const OrderInfo = styled.div`
    width : 60%;
    margin : 10px;
    padding : 10px;
    box-shadow: 0 5px 10px grey;
    & h3 {
        border-bottom: 1px grey solid;
        padding-bottom: 10px;
      }
    & label {
        display : block;
        padding-bottom : 20px;
    }

    & input {
        width : 80%;
    }

    & h6 {

        font-weight : bold;
        weight : 30%;

        
    }
`

const PaymentInfo = styled.div`
  box-shadow: 0 5px 10px grey;
  padding : 10px;
  width : 40%;
  margin : 10px;
  & h3 {
    border-bottom: 1px grey solid;
    padding-bottom: 10px;
  }

  & h4 {
    border-top: 1px grey solid;
    padding-top: 10px;
  }
  
  & button {
    width : 100%;
    height : 35px;
    background : grey;
    border : none;
    color : white;
  }
`


const Order = () => {

    const navigate = useNavigate();
    const [data, setData] = useState("");
    const [name, setName] = useState('');
    const [phoneNumber, setPhoneNumber] = useState('');
    const [address, setAddress] = useState('');
    const Token = localStorage.getItem("accessToken");
    // useLocation으로 전달 받은 key호출
    const location = useLocation();
    const TotalCount = location.state.ItemTotalCount
    const ItemPrice = location.state.ItemPrice
    const ShippingFee = location.state.ItemShippingFee
    // 값의 계산식을 위하여 parseInt로 계산
    const TotalItemPrice = parseInt(ItemPrice) + parseInt(ShippingFee)
    
    useEffect(() => {
        axios
        .get("http://localhost:8080/users/mypage", { headers: { Authorization:  Token } })
        .then((response) => {
            setData(response.data);
            setName(data.name)
            setPhoneNumber(data.phoneNumber)
            setAddress(data.address)
        })
        .catch((error) => {
            alert(error);
        });
    }, []);

    const submitHandler = (e) => {
        e.preventDefault();


        const formData = {
            name, phoneNumber, address
        }

        axios
        .post("http://localhost:8080/order", { ...formData }, { headers: { Authorization: Token } })
        .then((res) => {
            console.log(res.data)
            alert("주문완료!")
            navigate('/orderComplete')
        })
        .catch((error) => {
            console.log(error)
            alert('에러가 발생했습니다. 다시 시도해 주세요.');
        });
    }



    return  <Container onSubmit={submitHandler}>
        <OrderInfo>
            <h3>배송지 정보</h3>
            <label>
                {/* placeholder로 정보를 보이게 한 후 같은 값의 value를 post로 전송 */}
                <h6>이름</h6>
                <input type="text" placeholder = {JSON.stringify(data.name)}/>
            </label>
            <label>
                <h6>연락처</h6>
                <input type="tel" placeholder = {JSON.stringify(data.phoneNumber)}/>
            </label>
            <label>
                <h6>주소</h6>
                <input type="text" placeholder = {JSON.stringify(data.address)}/>
            </label>
        </OrderInfo>
        <PaymentInfo>
            {/* 풀어서 전달된 값을 저장한 후 formatCurrency */}
            <h3>결제정보</h3>
            <h5>상품수   {TotalCount} 개</h5>
            <h5>상품금액  {formatCurrency(ItemPrice)}원</h5>
            <h5>배송비  {formatCurrency(ShippingFee)}원</h5>
            <h4>총 결제금액 {formatCurrency(TotalItemPrice)}원</h4>
            <button>구매하기</button>
        </PaymentInfo>
    </Container>
}

export default Order;

- 결제하기

 

! 해당 방식도 따로 코멘트할 점 보다는 useLocation으로 값을 전달하는 방법을 이후에 활용하여 적용해 보거나 혹은 Local Storage에

값을 잠깐 전달하여 다음 페이지에서 꺼내 쓰고 삭제하는 방식을 구현해 보는 것이 좋을 것 같다.