어드민 상품 등록 페이지

HyunHo Lee·2022년 1월 28일
1

개요

웹 사이트의 어드민 페이지에서 상품 등록하는 부분을 설계하게 되었다. 여러가지의 UI와 요구사항이 있었는데, 팀원들과 역할분담을 진행하니 할일이 많이 줄었다.
역할 분담 하기전에는 역할 분담 자체도 막막했다.

배포 주소 : https://wanted-admin-product.netlify.app/


초기 셋팅

npx create-react-app 프로젝트 이름
npm install styled-components

CRA를 이용하여 react 프로젝트를 생성했다. 그리고 스타일은 styled-conponents를 사용하기로 결정했다.

npm install -D prettier eslint-config-prettier eslint-plugin-prettier

prettier와 eslint를 설치하여 팀원들과 코딩 스타일을 조금 맞추었다.

styles에는 GlobalStyles을 넣어 css reset를 적용했다.

export const COLOR = {
  MAIN: '#352F6E',
  MAIN_LIGHT: '#AFA8E6',
  TAG: '#E8F7D4',
  BG: '#E3E3E3',
  BG_LIGHT: '#EFEFEF',
};

constants의 index에 팀원들과 자주 사용하게 될 색상들을 넣었다.

목 데이터를 위한 디렉터리도 생성했다.

사실 상품 등록 페이지는 ProductRegist 페이지에서 구현해야한다. 하지만 4명의 팀원이 기능에 따른 컴포넌트를 구현하게 되는데, 그때마다 ProductRegist 페이지에 추가하며 작업을 하게 되면 컨플리트가 자주 일어날 것이다.

그래서 생각한 방법이 각자 작업할 페이지를 생성하고, 여기서 기능에 따른 컴포넌트 구현을 한 뒤, 모두 구현하면 ProductRegist 페이지에 컴포넌트들을 추가하고 각자 작업했던 페이지는 삭제하는 것이다. 이렇게 하면 4명의 팀원은 기능을 구현할 동안 ProductRegist 페이지를 건들일이 없어져 수월하게 프로젝트를 설계할 것이다.


상품 정보 고시

상품 정보 고시에 대한 UI에 대해 먼저 설명하겠다.


  • 빨간 박스 부분
    <SectionBlock title="상품 정보 고시" bg>
      <ProductInformation />
    </SectionBlock>

다른곳에서도 비슷하게 사용되는 제목-내용 구조를 SectionBlock 레이아웃을 생성하였고, 여기에도 적용했다. ProductInformation 컴포넌트는 children으로 회색 배경에 위치하게 된다.


  • 파란 박스 부분
    <Container>
      <InformationContainer>
        <Information createdNum={createdNum} />
        <AddItem />
      </InformationContainer>
      <Add onClick={onClickAdd}>+ 정보고시 추가</Add>
    </Container>

Information 컴포넌트는 정보 고시 정보를 입력하는 부분 이고, createdNum은 정보고시 추가를 누를 경우 증가하는 숫자이다. 맨 위에 위치하고 있는 정보고시 0 에서 0이 createdNum이다. 오른쪽의 삭제 버튼은 정보고시를 추가 하고, 추가된 정보고시를 삭제하고 싶을 경우의 버튼인 것 같다.

AddItem은 자유롭게 입력할 수 있는 정보고시 항목이다.


  • 노랑 박스 부분

input에 내용 미 입력시 해당 항목 자체가 스크린에 노출되지 않도록 설계해야 한다. 데이터 어떻게 할지 고민중


  • 초록 박스 부분
const AddItem = () => {
  const [items, setItems] = useState([0]);

  const onAddDel = num => {
    let countArr = [...items];
    let counter = countArr.slice(-1)[0];
    counter += num;
    if (num > 0) {
      countArr.push(counter);
    } else {
      countArr.pop(counter);
    }
    setItems(countArr);
    console.log(items);
  };

  return (
    <div>
      {items &&
        items.map((item, i) => (
          <Item key={`item-key-${i}`}>
            <TitleInp type="text" placeholder="항목 제목 자유 입력" />
            <DescInp type="text" placeholder="내용을 입력해주세요." />
            <ItemDel type="button" onClick={e => onAddDel(-1)}>
              삭제
            </ItemDel>
          </Item>
        ))}
      <ItemAdd type="button" onClick={e => onAddDel(1)}>
        + 항목 추가
      </ItemAdd>
    </div>
  );
};

정보고시의 항목을 추가할 수 있는 부분이다. 항목 추가를 누를 경우에 항목 제목, 내용, 삭제 가 위치한 한 줄이 모두 추가되야한다. 그리고 삭제를 누르면 추가된 항목들이 삭제된다.

const AddItem = () => {
  const [items, setItems] = useState([0]);

  return (
    <div>
      {items &&
        items.map((item, i) => (
          <Item key={`item-key-${i}`}>
            <TitleInp type="text" placeholder="항목 제목 자유 입력" />
            <DescInp type="text" placeholder="내용을 입력해주세요." />
            <ItemDel type="button" onClick={e => onAddDel(-1, items, setItems)}>
              삭제
            </ItemDel>
          </Item>
        ))}
      <ItemAdd type="button" onClick={e => onAddDel(1, items, setItems)}>
        + 항목 추가
      </ItemAdd>
    </div>
  );
};

onAddDel 함수는 다른곳에서도 사용하므로 utils에 넣어줬다.

정보고시 자체에도 이 기능이 있어야하기 때문이다.

    <Container>
      {items &&
        items.map((item, i) => (
          <InformationContainer key={`product-information-${i}`}>
            <Information createdNum={i} items={items} setItems={setItems} />
            <AddItem />
          </InformationContainer>
        ))}
      <Add onClick={e => onAddDel(1, items, setItems)}>+ 정보고시 추가</Add>
    </Container>

그래서 파란 박스 부분도 수정해주었다.


AutoComplete

이번에는 input에 텍스트를 입력할 경우 자동완성되는 검색 기능을 만들어보자.

{
  "filters": [
    "선물용",
    "가족",
    "선물셋트",
    "베스트",
    "홈셋트",
    "한우",
    "1++",
    "1+",
    "당일배송",
    "일반배송",
    "새벽배송",
    "숙성한우",
    "냉동한우",
    "냉장한우"
  ]
}

data 디렉터리에 filter에 사용할 데이터를 json형식으로 넣어주었다.

  const data = filter.filters;
  const [filteredItem, setFilteredItem] = useState([]);

  const autoComplete = e => {
    const currentValue = e.target.value;
    const filtered = data.filter(item => item.includes(currentValue));
    setFilteredItem(filtered);
  };

  const onSearch = () => {
    setFilteredItem([]);
  };

  return (
    <Container>
      <Filter>
        <p>필터 태그</p>
        <Search
          type="text"
          placeholder="필터태그를 검색해주세요."
          onChange={e => autoComplete(e)}
        />
        <button type="button" onClick={onSearch}>
          검색
        </button>
      </Filter>

      <ul>
        {filteredItem.map(item => (
          <li className="item" key={item}>
            {item}
          </li>
        ))}
      </ul>
    </Container>
  );
};

Search는 input 태그에 styled-components로 스타일을 입힌 것이다. onChange로 {e => autoComplete(e)} 을 해주고 있다. autoComplete 함수를 보면, data에 현재 입력한 값을 포함하는 값이 있는 것들을 리턴한다. data에 냉동한우, 냉장한우가 있을 경우 냉장을 입력하면 냉장한우가 리턴되는 것이다.

그리고 리턴된 값을 filteredItem에 넣어준다. 이제 filteredItem을 이용하여 map을 돌리며 UI를 출력해주면 된다.

검색 버튼을 누르면 filteredItem는 초기화해주자.

profile
함께 일하고 싶은 개발자가 되기 위해 달려나가고 있습니다.

0개의 댓글