클론 하려 했는데 기획하게 된 React 개발 리뷰 (2)

5. Code Review

3) Creator Page 주요코드

  • 위의 사진이 Creator Page가 완성된 모습이다.
  • 사실 실제 클래스101의 크리에이터 페이지가 어떻게 생겼는지 난 아직도 잘 모른다.
    아무나 크리에이터가 될 수는 있었으니 아무 상품이나 등록되는 것이 아니기 때문에.... 확인할 방법이 없었다.
  • 그저 내가 만든 클래스가 상품이 되고, 강의가 되어야 한다는 것에 목적을 두고 직접 기획해서 만들기 시작했다.

  • 위 사진은 클래스101의 크리에이터 센터 라는 곳이다. 이곳의 디자인을 참고해서 꾸며보았다.

(1) Creator.js

: 최상위 부모로 전체 레이아웃 틀이 존재하며 모든 통신이 발생되는 곳이다.

// Creator.js
  const [selectedPage, setSelectedPage] = useState(0);
  const [selectedCategory, setSelectedCategory] = useState('');
  const [selectedSubCategory, setSelectedSubCategory] = useState('');
  const [selectedDifficulty, setSelectedDifficulty] = useState('');
  const [selectedClassName, setSelectedClassName] = useState('');
  const [selectedPrice, setSelectedPrice] = useState('');
  const [selectedDiscount, setSelectedDiscount] = useState('');
  const [uploadFirstImage, setUploadFirstImage] = useState();
  const [files, setFiles] = useState([]);
  const [selectedLecture, setSelectedLecture] = useState([{}]);
  const [lecturefiles, setLecturefiles] = useState([]);
  const [makeLecture, setMakeLecture] = useState([]);
  const [lectureImage, setLectureImage] = useState([]);
  const [lectureVideo, setLectureVideo] = useState([]);
  const [kitnames, setKitnames] = useState([]);
  const [kitImage, setKitImage] = useState([]);
  • 강의에 들어가는 자료들이 너무 너무 많다. 그 때문에 State도 위와 같이 어마어마하게 많다. 수많은 자식 컴포넌트가 있고, 손자, 증손자가 존재한다. 그때의 통신을 위한 데이터가 이곳으로 모이게 되어있다.
  • 리덕스를 배우기전이라 최상위 부모인 이 Creator가 이 모든 것을 관리하고 통신하고 있다.
  • 때문에 이것을 통신하는 Fetch 함수가 수도 없이 많이 존재하고 있다.(반복 코드이니 생략)
    if (selectedPage === 0) {
      const temp = JSON.stringify({
        categoryName: selectedCategory,
        subCategoryName: selectedSubCategory,
        difficultyName: selectedDifficulty,
        name: selectedClassName,
        price: selectedPrice,
        sale: selectedDiscount / 100,
      });
      let formData = new FormData();
      formData.append('body', temp);
      for (let i = 0; i < files.length; i++) {
        formData.append('files', files[i]);
      }
      formData.append('files', files);
      fetch('API', {
        headers: {
          authorization: localStorage.getItem('token'),
        },
        method: 'POST',
        body: formData,
      })
        .then((res) => res.json())
        .then((result) => {
          console.log(result);
        });
    } else if (selectedPage === 1) { /* 생략 */ }
  • 이런식으로 페이지 마다의 통신이 이루어진다.
   if (selectedPage === 0) { blabla }
  • 이때 위에서처럼 재밌는 방법으로 통신을 시도해보았다. 페이지 넘버를 스테이트 값에 넣고 그 값에 따라 통신 코드를 달리 보내게 만들어두었다.
  const temp = JSON.stringify({
    categoryName: selectedCategory,
    subCategoryName: selectedSubCategory,
    difficultyName: selectedDifficulty,
    name: selectedClassName,
    price: selectedPrice,
    sale: selectedDiscount / 100,
  })
  formData.append('body', temp);
  • 강의 설명등은 Json 텍스트 형태로 변형하여 FormData에 담아 전송하였고
  for (let i = 0; i < files.length; i++) {
    formData.append('files', files[i]);
  }
  • 이미지와 영상 등은 File 의 원본 형태(MultiPart)로 FormData로 담아 전송하였다.
  • 여기서 재밌는 코드가 FormData하나의 키 값에 append를 이용해서 어떻게 하면 여러 데이터를 담을 수 있을까?? 를 연구하다가... 그냥 반복문을 써서 한개의 키값에 계속 담으면 다 담아지더라!
// 렌더 부분 일부
  {selectedPage === 0 ? (
    <Introduce
      getSelectedCategory={(e) =>
      setSelectedCategory(e.target.value)}
      getSelectedSubCategory={(e) =>
      setSelectedSubCategory(e.target.value)}
      getSelectedDifficulty={(e) =>
      setSelectedDifficulty(e.target.value)}
      getSelectedClassName={(e) =>
      setSelectedClassName(e.target.value)}
      getSelectedPrice={(e) => 
      setSelectedPrice(e.target.value)}
      getSelectedDiscount={(e) =>
      setSelectedDiscount(e.target.value)}
      getFirstImage={(e) => setUploadFirstImage(e)}
      getUploadImage={(file) => setFiles(file)}
    />
  ) : null}
  • 스테이트를 이렇게 관리하면 자식 컴포넌트와 주고 받는 데이터가 어마어마하다. 리덕스가 반드시 필요해보인다.(당장 리펙토링이 필요해)

(2) Introduce.js

: 여기서는 강의를 상품으로 등록하는 페이지다.
상품 리스트와 상품 디테일 페이지에 보여줄 데이터를 input 하게 된다.
여기서 주요 코드는 상품 대표사진 3장을 담아야 하는 부분인데, 총 3개의 파일을 업로드 해야 하지만 한번에 3개의 파일을 다 담지 않고 1개씩 3번을 담아야하기 때문에 Form에 multiple 속성을 넣을 필요 없고 Form이 3개 필요했다.

  function onFileUpload(event) {
    let file = event.target.files[0];
    setFiles([...files, file]);
  }
  • 이때 업로드된 이미지와 미리보기 이미지는 별도의 함수를 작성했다.
    위의 코드는 파일의 원본 형태로 State에 담는 함수이다.
  const upLoadImage = (e) => {
    let reader = new FileReader();
    if (e.target.files[0]) {
      reader.readAsDataURL(e.target.files[0]);
      reader.onloadend = () => {
        const imageUrl = reader.result;
        setfirstUploadImage(imageUrl.toString());
        props.getFirstImage(imageUrl.toString());
      };
    }
  };
  • 위의 코드는 파일의 미리보기를 위해 FileReader() 를 불러오는 함수이다
// 렌더 부분
  <FirstForm method='post' encType='multipart/form-data'>
    <FirstLabel htmlFor='firstUpLoad'>
      <img 
        src={ firstUploadImage ? firstUploadImage : '/images/SH/noImage.png'} 
        alt='noimage' />
    </FirstLabel>
    <FirstInput
      type='file'
      id='firstUpLoad'
      onChange={(e) => { 
        onFileUpload(e);
        upLoadFirstImage(e);
      }}
    />
  </FirstForm>
  • 여기서 재밌는 점은 input의 라벨을 클릭하도록 만들었다. 그리고 그 라벨을 이미지로 만들어버렸다. 그리하면 라벨의 이미지를 클릭하면 파일 첨부창이 뜨게 된다.
    (원래 보통 이렇게 하는지는 잘 모르겠다)

이제 3부에서는 상품이 아닌 강의를 직접 만들러 가보자

좋은 웹페이지 즐겨찾기