문제상황 

회사에서 사이드 프로젝트를 만드는 과정에서 ant 디자인을 사용하였다. ant 디자인중에 Menu 컴포넌트를 사용했는데, 그리고 Menu에 Link 컴포넌트를 삽입했는데, 버튼을 누르면 자꾸 닫히는 현상이 일어났다. Link 컴포넌트를 넣으면 발생하는거같았다. 

 

처음 코드

export default function Test() {
  const router = useLocation();
  const category = router.pathname.split("/")[0];
  return (
    <>
      <Menu
        style={{ width: 240 }}
        mode="inline"
      >
        <SubMenu key="sub1" title={<Link to="/main">Main</Link>}></SubMenu>
        <SubMenu key="sub2" title={<Link to="/post">Post</Link>}>
          <Menu.Item key="item1">
            <Link to="/post/register">등록하기</Link>
          </Menu.Item>
        </SubMenu>
        <SubMenu key="/etc" title={<Link to="/etc">Etc</Link>}>
          <Menu.Item key="item2">
            <Link to="/post/register">등록하기</Link>
          </Menu.Item>
        </SubMenu>
      </Menu>
    </>
  );
}

 

해결된 코드

export default function Test() {
  const router = useLocation();
  const category = router.pathname.split("/")[0];
  return (
    <>
      <Menu
        style={{ width: 240 }}
        defaultOpenKeys={[category]}
        selectedKeys={[router.pathname]}
        mode="inline"
      >
        <SubMenu key="/main" title={<Link to="/main">Main</Link>}></SubMenu>
        <SubMenu key="/post" title={<Link to="/post">Post</Link>}>
          <Menu.Item key="/post/register">등록하기</Menu.Item>
        </SubMenu>
        <SubMenu key="/etc" title={<Link to="/etc">Etc</Link>}>
          <Menu.Item key="/etc/register">등록하기</Menu.Item>
        </SubMenu>
      </Menu>
    </>
  );
}

 

defaultOpenKeys

카테고리의 항목을 열리게 하려면 defaultOpenKeys 값에 SubMenu의 키값을 배열로 넣어야한다. 예를 들어 메뉴 버튼의 Post 카테고리에 등록하기 버튼을 누르고 항목을 Post 항목을 계속 열리게 할려면 SubMenu의 키값을 defaultOpenKeys에 계속 넣어주고 있어야한다. (만약 이것을 안넣어주면  다른 항목을 누를 때 마다 계속 닫히게 된다.)

 

selectedKeys

여기에 값을 넣어줘야 그리고 파란색 하이라이트 기능이 된다는 것을 알았다. 그래서 key값을 url로 만들고, rotuer.pathname을 사용하여 메뉴의 하위 항목을 누를 때마다 하이라이트 되도록 만들었다. (배열로 해야함)

서론

React로 회사 업무 사이트 Pagination 다른 프로젝트에서 직접 구현을 해봤는데, 뭔가 좀 더 간단한 라이브러리가 없을까 하여 구글링 도중에 react-paginate를 찾았다. 실제로 구현하는 것도 도움이 많이 되지만, 시간상 직접 구현하면 시간도 걸려서 라이브러리 도움을 받기로 했다. (사실 귀찮음이 컸음)
이 후 적용을 해보니 어렵지도 않고 매우 간단하였고, 독스 설명도 잘 있어서 매우 편리했다.

 

사용 방법

밑의 URL npm 에서 구글 독스로 봐도 되고, 아니면 해당 글을 참고하여 사용해도 좋을 것 같다.

https://www.npmjs.com/package/react-paginate

 

react-paginate

A ReactJS component that creates a pagination.. Latest version: 8.2.0, last published: a year ago. Start using react-paginate in your project by running `npm i react-paginate`. There are 625 other projects in the npm registry using react-paginate.

www.npmjs.com

 

리액트는 18.12.0 버전을 사용하였고, node 버전은 v14.17.0을 사용하였다. 

 

설치 방법

npm install react-paginate --save

설치 방법은 매우 간단하다. npm 사용하여 위의 명령어를 입력해주면 설치가 된다.

 

styled 컴포넌트와 같이 사용하기

나는 styled컴포넌트와 같이 사용하였는데, styled 컴포넌트와 어떻게 적용할지 고민을 하였는데, 매우 간단하였다. https://stackoverflow.com/questions/75532879/how-to-style-react-paginate-with-styled-components 

 

How to style React-paginate with Styled Components

The react-paginate not works with display flex to show the paagination in horizontal position. I try this JS code <PaginationContainer> <Pagination previousLabel={&...

stackoverflow.com

위의 stackoverflow를 참고하여 작성하였다. 

 

실제 예시

 

styled.ts

import styled from "styled-components";
import ReactPaginate from "react-paginate";

export const StyledPagenate = styled(ReactPaginate)`
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 90px;
  font-weight: 400;

  .page-number {
    width: 30px;
    height: 40px;
    border: 1px solid gray;
    margin-left: 6px;
    margin-right: 6px;
    cursor: pointer;
  }

  .previous {
    width: 100px;
    height: 40px;
    border: 1px solid rgba(0, 0, 0, 0.3);
    display: flex;
    justify-content: center;
    align-items: center;
    font-weight: 600;
  }

  .next {
    width: 70px;
    height: 40px;
    border: 1px solid gray;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-left: 3px;
    font-weight: 600;
  }

  a {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .active {
    background-color: #ffb372;
  }
`;

 

styled styled 컴포넌트와 react-paginate를 import를 해준 후에, styled(ReactPaginate)를 해주면 된다. 이 후 컴포넌트에서 다음과 같이 적용한다.

 

component.tsx

export default function Component({ data }: IProps) {
  const [currentPageIndex, setCurrentPageIndex] = useState(1);
  const [itemOffset, setItemOffset] = useState(0);

  const itemCountPerOnePage = 12; // 페이지당 보여줄 아이템 갯수
  const dataCount = data!.length; // 

  const totalPageCount = Math.ceil(dataCount / itemCountPerOnePage);
  const endOffset = itemOffset + itemCountPerOnePage;
  const currentItems = hadoopList!.slice(itemOffset, endOffset);

  const handlePageClick = (event: PageChangeEvent) => {
    const newOffset = (event.selected * itemCountPerOnePage) % dataCount;
    setItemOffset(newOffset);
  };

  return (
    <>
        {data &&
          data!
            .slice((currentPageIndex - 1) * 12, currentPageIndex * 12)
            .map((item) => <DataItem item={item} key={data._id} />)}
        <S.StyledPagenate
          breakLabel="..."
          nextLabel="Next >"
          onPageChange={handlePageClick}
          pageRangeDisplayed={3}
          pageCount={totalPageCount}
          previousLabel="< Previous"
          renderOnZeroPageCount={null}
          pageClassName="page-number"
          previousClassName="previous"
          nextClassName="next"
          activeClassName="active"
        />
    </>
  );
}

page 숫자, previous, break, next 버튼을 커스터 마이징 하고싶다면, 위의처럼 className을 지정해주고, styled 컴포넌트에서 원하는 값으로 적용해주면 된다.

 

결론

밑에는 실제로 적용된 모습이고, data는 보여주고싶지만.. 회사 프로젝트였어가지고 따로 data들에 대한 항목들은 생략하였다.

Recoil SelectorFamily에 대해 알아보려 한다.

 

정의

Recoil 공식 사이트에 SelectorFamily에 대해 다음과 같이 정의가 되어있다.

A selectorFamily is a powerful pattern that is similar to a selector, but allows you to pass parameters to the get and set callbacks of a selector. The selectorFamily() utility returns a function which can be called with user-defined parameters and returns a selector. Each unique parameter value will return the same memoized selector instance.

 

해당 글을 번역기를 돌려보면 다음과 같다.

selectorFamily는 강력한 패턴으로, selector와 유사하지만 get 및 set 콜백에 매개변수를 전달할 수 있다는 점에서 차이가 있습니다. selectorFamily() 유틸리티는 사용자 정의 매개변수를 사용하여 호출될 수 있는 함수를 반환하며, 그 결과로 selector를 반환합니다. 각 고유한 매개변수 값에 대해 동일한 메모이제이션된 selector 인스턴스를 반환합니다.

 

즉. Recoil Selector와 차이점은 매개 변수를 넣을 수 있다는 것이다.

 

사용법 알아보기

 

다음은 atoms라는 파일에 RecoilState를 examState 정의하고, 

selectorFamily를 examSelector라는 이름으로 정의하였다.

 

atoms.ts

export const examState = atom<number[]>({
  key: "exam"
  default: [1, 2, 3]
});

export const examSelector = selectorFamily({
  key: "examKey",
  get:
    ({ param1, param2 }: { num1: number; num2: number}) => // {param1, param2} 객체로 써주기
    ({ get }) => {
      const nums = get(examState);
      const result = nums.map((num)=> num1 * param2 * param2));
      return result
    },
});

 

위의 코드를 간단하게 설명하면, param1 param2를 객체 형태로 매개 변수로 받고 examState 배열 안의 원소 값에 param1 param2를 곱해주는 것이다. 참고로 매개변수로 보낼 때는 객체 형태로 해줘야 한다는 것을 잊지 말자.

 

컴포넌트에서 사용할 때는 다음과 같다.

 

Home.tsx

export default function Home() {
  const exam = useRecoilValue(examSelector({ num1 : 3, num2: 4 }));
  console.log(exam); // [1 * 3 * 4, 2 * 3 * 4, 3 * 3 * 4]

  return (
    <>
      <>
        <ExamPage exam={exam}>
        />
      </>
    </>
  );
}

 

먼저 위에서 정의한 selectorFamily를 사용하기 위해서는 다음과 같이 불러오면 된다.
useRecoilValue(selectorFamily(객체 매개변수));  ex) useRecoilValue(examSelector({ num1 : 3, num2: 4 }));

https://www.npmjs.com/package/tailwind-styled-components

 

tailwind-styled-components

Create tailwind css react components like styled components with classes name on multiple lines. Latest version: 2.2.0, last published: 23 days ago. Start using tailwind-styled-components in your project by running `npm i tailwind-styled-components`. There

www.npmjs.com

위의 사이트 내용을 토대로 작성하였음

 

1-1) 설치 방법

npm i -D tailwind-styled-components

 

1-2) 테스트

import tw from "tailwind-styled-components"

const Container = tw.div`
  w-full 
  h-32
  bg-red-300
`;

const Navbar = () => {
  return (
    <>
      <Container />
    </>
  )
};

export default Navbar;

해당 코드가 잘 작동하는지 테스트해보자.

 

 

2-1) 설치를 한 후에, 비쥬얼 스튜디오 코드를 사용중이라면 다음의 익스텐션도 설치

> Tailwind CSS IntelliSense

 

 

2-2) https://code.visualstudio.com/docs/getstarted/settings

 

Visual Studio Code User and Workspace Settings

How to modify Visual Studio Code User and Workspace Settings.

code.visualstudio.com

위의 URL을 확인 후 json파일에 다음과 같이 밑에 추가로 해당 내용을 넣어주자.

"tailwindCSS.includeLanguages": {
    "typescript": "javascript", // if you are using typescript
    "typescriptreact": "javascript"  // if you are using typescript with react
},
"editor.quickSuggestions": {
    "strings": true // forces VS Code to trigger completions when editing "string" content
},
"tailwindCSS.experimental.classRegex": [
    "tw`([^`]*)", // tw`...`
    "tw\\.[^`]+`([^`]*)`", // tw.xxx<xxx>`...`
    "tw\\(.*?\\).*?`([^`]*)" // tw(Component)<xxx>`...`
]

 

2-3) 밑에 처럼 자동완성이 되는지 확인

 

 

react typescript can't resolve './app' 

tsconfig.json 파일이 없어서 발생하는 에러

프로젝트가 시작하는 루트 디렉토리에 tsconfig.json 파일을 만들고 복붙해주고 저장하면 에러 해결

{
    "compilerOptions": {
      "target": "es5",
      "lib": [
        "dom",
        "dom.iterable",
        "esnext"
      ],
      "allowJs": true,
      "skipLibCheck": true,
      "esModuleInterop": true,
      "allowSyntheticDefaultImports": true,
      "strict": true,
      "forceConsistentCasingInFileNames": true,
      "noFallthroughCasesInSwitch": true,
      "module": "esnext",
      "moduleResolution": "node",
      "resolveJsonModule": true,
      "isolatedModules": true,
      "noEmit": true,
      "jsx": "react-jsx"
    },
    "include": ["src/**/*.tsx", "src/**/*.ts", "src/**/*.d.ts"]

  }

 

공식 문서에도 있지만 좀 더 세세하게 설치 하는 방법을 적어두려한다. 

 

1. tailwindcss 설치 방법

npx create-react-app my-project
cd my-project

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p // tailwind.config.js 생성 됨

 

위의 명령어 대로 입력 하면 tailwind.config.js가 생성된다.

 

 

2. tailwind.config.js 생성이 되면 다음과 같이 작성

// tailwind.config.js

module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

 

위의 파일의 content부분이 비어있을텐데, 이 부분을 위의 코드대로 수정해주자.

 

3. index.css파일에 모듈 추가

// index.css

@tailwind base;
@tailwind components;
@tailwind utilities;

.....

그리고 간헐적으로 저렇게 수정한 후 index.jsx 파일에 index.css 모듈을 추가 안해줘서 안되는 경우가 있는데 꼭 넣어주자.

 

4. 프로젝트 시작

yarn start

 

5. 테스트

export default function App() {
  return (
    <h1 className="text-3xl font-bold underline">
      Hello world!
    </h1>
  )
}

+ Recent posts