\(@^0^@)/

관리자 상품 페이지 제작 본문

프로젝트&웨비나 회고/프리온보딩 FE course

관리자 상품 페이지 제작

minjuuu 2022. 2. 3. 16:38
728x90

두 번째 과제는 주어진 사이트의 관리자용 상품 페이지를 제작하는 것.
아무래도 페이지를 제작하는 것이다 보니, 해야 될 마크업이 상당해서 2일로는 벅찬 과제였다ㅠ.ㅠ

회고 순서는
1. 이번 과제를 통해 새로 알게 된 기능들과 코드를 정리하고
2. 개인적으로 리팩터링 하고 싶은 부분들과 리팩터링 후 새로 알게 된 기술들


💎 작업을 위해 고려했던 사항들

1. mui 달력 라이브러리 사용
2. 카테고리 리스팅 ( 체크박스를 선택 시 오른쪽에 선택된 카테고리만 따로 리스팅 됨)
3. 이미지 업로드 (1, 업로드 시 이미지 프리뷰 2. 업로드 시 이미지 타이틀들을 보여 줌)
4. 필터 태그 검색 ( 검색창 포커스 시 모든 필터태그 제공, 검색 시 검색 결과 제공)


1. mui 달력 라이브러리 사용
Material-ui 사이트에서 이미 만들어져있는 DateRangePicker와 DatePicker를 가져와
style을 주어 주어진 과제와 최대한 비슷하게 구현해보려 하였고,
(따로 작성한 mui에 style 주는 법 : https://dev-minju.tistory.com/125)
onChange로 value 값을 받아왔다.

onChange로 받아온 state를 console에 찍어 데이터를 불러온 모습.


2. 카테고리 리스팅 ( 체크박스를 선택 시 오른쪽에 선택된 카테고리만 따로 리스팅 됨)

  • 왼쪽의 전체 리스트에서 체크를 하면, 오른쪽에 리스트가 뜨고
  • 체크된 리스트에서 삭제버튼을 누르면 카테고리가 사라지면서 전체 리스트에서도 체크박스가 해지되어야 함.


mock data를 만들어 카테고리 리스트에 추가하고,
그 리스트의 state관리를 위해 selectedTag라는 state를 하나 만들어주었음.

const [selectedTag, setSelectedTag] = useState([]);


카테고리의 체크박스에 onChange를 걸어서
만약에 클릭한 체크박스가 (e.target.checked)  리스트 배열(SelectedTag) 안에 포함이 안되어있다면 오른쪽에 나타나게 포함시켜주고,
만약에 왼쪽 리스트에서 카테고리를 체크했다가 다시 취소한다면 오른쪽 카테고리 리스트 배열(SelectedTag)안에서 index를 찾아서 Delete함수를 실행시켜, 리스트에서 일치하는 해당 index를 찾아, 삭제해주었다.

오른쪽 체크 리스트에서 하나의 카테고리를 삭제 시 Delete핸들러가 작동되는데,
체크된 카테고리 리스트 배열(SelectedTag)에서 filter를 돌려,
배열 안의 각 요소 (tag)가 클릭한 tag와 같지 않은 것들만 다시 카테고리 리스트 배열 안에(setSelectedTag) 넣어주었다.
(value값이 같은 경우에는 삭제됨)


왼쪽 기존의 카테고리 리스트를 ref로 감싼 상태에서 체크박스(input)에 forEach를 돌려서,
왼쪽 리스트에서 체크박스를 해지시킬 (내가 삭제하려고 클릭한) 카테고리를 추적하여 value를 구하고,
왼쪽의 리스트에서 일치하는 해당 value와 일치하는 카테고리의 체크박스를 false로 만들어 체크가 해지되게 해 준다.


현재 우리는 프론트엔드로만 이루어져서 과제를 하는 중이므로, 업로드된 이미지를 저장할 공간이 필요하여
팀원들과 토론을 하여 Cloudinary라는 클라우드 회사를 선택하여 이미지 관리를 하였음.
나는 서버 대신 Firebase를 사용해본 적은 있는데, Cloudinary는 이번에 처음 사용하는 SaaS 회사!

Cloudinary 란? : https://en.wikipedia.org/wiki/Cloudinary

Cloudinary 사용법 : https://ichi.pro/ko/reactlo-cloudinarye-imiji-eoblodeu-15681670366341

Cloudinary 사용법 :  https://www.youtube.com/watch?v=Y-VgaRwWS3o


3-1. 이미지 업로드 ( 이미지를 첨부 시 이미지 프리뷰를 보여 줌)

const uploadImage = (files) => {
  console.log(files[0]);
};

return (
  <div>
    <input
      type="file"
      onChange={(event) => {
        uploadImage(event.target.files);
      }}
    />
  </div>
  }
)

input 태그의 type을 file로 주면, 이미지를 첨부할 수 있는 형식으로 바뀌는데,
그 태그 안에 onChange핸들러를 주어 target을 콘솔로 찍어보면 아래와 같이 이미지를 첨부한 file의 정보가 나옴.

업로드할 file의 정보와 cloudinary에 api 콜을 할 때 필수로 요구되는 고유의 값들을 formData 형식에 담고, (빨간 박스)
fetch를 이용하여 cloudinary에 post 형식으로 넘겨주었고 (주황 박스)
해당 이미지의 url을 state로 받아와서 화면에 뿌려주었다. (초록 박스)

그래서 만약 업로드 후, api를 보낸 cloudinary에 url이 존재한다면 해당 url을 통해 프리뷰를 보여줄 수 있도록!

const [ImageUrl, SetImageUrl] = useState();

return (
  <>
    {ImageUrl && <img src={ImageUrl} /> }
  </>
)

3-2. 이미지 업로드 리스팅 ( 이미지를 첨부 시 이미지 타이틀을 같이 보여줌)

프리뷰를 불러오지 않는 건 좀 더 간단하다.
3-1번처럼 onChange를 사용하여 해당 데이터의 name값만 가져오면 됨.

const uploadImage = (e) => {
  const files = e.target.files;
};

return (
  <div>
    <input
      type="file"
      onChange={uploadImage}
    />
  </div>
  }
)

가져온 name의 값을 빈 배열 안에 차곡차곡 넣고, 렌더 하는 쪽에서 그 배열을 map 돌려주면 완성!
삭제할 경우에는 삭제 버튼을 눌러야 삭제가 되기 때문에,
버튼에 클릭 핸들러를 만들어서 리스트 배열을 filter 함수로 돌리면서 삭제할 값과 나머지 값을 대응시킨다.
그리고, 삭제할 값을 제외한 나머지 값들은 배열 안에 남고, 삭제할 값만 제거시키면 됨.


4. 필터 태그 검색 ( 검색창 포커스 시 모든 필터 태그 제공, 검색 시 검색 결과 제공)

필터 태그의 input에 onChange, onFocus, onBlur 핸들러를 추가하여 구현하였는데,
onChange를 사용하여 내가 검색하고 있는 value 값을 추적하고,
onFocus를 사용할 땐, 초기값이 0인 state를 하나 추가하여 내가 input에 focus 시켰을 시 1로 만든다.

state가 1일 경우 ( input에 focus 되었을 경우)에는 위의 화면같이 tagData들을 렌더 시키도록 구현하였음.
state가 1이 되었을 때 검색을 하면 onChange가 나의 value값들을 추적하게 되는 것.
그 value값이 내가 만들어둔 전체 태그 리스트들과 match 함수로 대조하여
만약에 '가'를 적었을 때, 전체 태그 리스트들에서 '가'를 포함한 데이터가 있을 경우 만들어 둔 상수 안에 대입된다.

// 전체 태그 리스트
let all_tagsList = TAG_LIST;
// ['가', '가가', '가가 가가', '나', '나나', '나나 나나', '다', '다다', '다다 다다', '라', '마', '바', '사', '아']

// 만약에 '가'를 적었을 때,
// all_tagsList에서 '가'를 포함한 tag가 있을 경우 all_tagsList에 대입.
if (searchWords.length) {
  all_tagsList = TAG_LIST.filter((tag) => tag.match(searchWords));
  console.log(all_tagsList); // ['가', '가가', '가가 가가']
}

새롭게 대입한 all_tagsList를 map 돌려서 입력한 결과창에 보여주고
결과 태그들 중 하나를 선택할 경우, 클릭 핸들러를 통해 해당 태그가 저장한다. (저장된 필터 태그)
저장된 필터 태그를 삭제하려면 또 당연히 클릭 핸들러에 filter 함수를 사용하여 해당 태그는 삭제되고,
나머지 태그들은 배열에 담아서 보여준다.

onBlur (onFocus와 반대 이벤트)를 사용하여 focus 되지 않았을 경우,
1. onFocus에서 1로 만든 state를 다시 0으로 만들어 검색창 아래의 전체 태그 리스트 창을 없애고
2. onChange에서 검색하고 있는 value 값을 빈 값 (" ")으로 만들어준다.
3. blur 상태일 경우에 1번의 검색창은 닫히고, 저장된 필터 태그만 보여준다.


👩‍💻 리팩터링 하고 싶은 부분 ?


우리가 만든게 사실 사이트에서는 하나의 페이지 인데,
스크롤을 내리는 것보다 더 직관적으로 보여주기 위해서 5개의 페이지로 쪼갰고,
하단에 이전, 다음 버튼을 생성하여 그 버튼을 누르면은 콘솔에 입력한 데이터들이 찍히게 구현하였다.

그러다보니, navbar (상위 컴포넌트)에 있는 저장하기에 값을 보내려면 데이터를 전역으로 관리하거나, 
navbar에서부터 밑으로 props를 내려줘야하는데, 5개의 페이지에 모두 내려줘야 하기때문에
개인적으로는 그냥 하나의 페이지로 합치고, 값을 모두 입력하여 마지막에 저장하기 버튼을 한번만 눌러서
formData로 입력한 데이터를 모두 받아오는 구조로 리팩터링을 할 예정이다.

728x90

'프로젝트&웨비나 회고 > 프리온보딩 FE course' 카테고리의 다른 글

파일 쉐어사이트  (0) 2022.02.27
애니메이션 웹 사이트  (0) 2022.02.17
환율계산기  (0) 2022.01.26
프리 온보딩 프로젝트  (0) 2022.01.17