기록/환경세팅

[기록/환경세팅] 리액트 프로젝트에 AntD 설치 및 사용법

수빈2022 2024. 4. 22. 10:54

AntD는 컴포넌트 라이브러리 패키지입니다. 컴포넌트 라이브러리란, 컴포넌트를 재사용할 수 있도록 라이브러리로 제공하는 것입니다.


더보기

✅ AntD 사용한 리액트 프로젝트(이하, 리액트 앱, 앱) 개요

(개요 번호와 본문 번호는 관계 없습니다.)

 

1. CRA 사용해서 typescript 템플릿 기반의 리액트 앱 생성

자세한 설명은 [바로가기]를 눌러 주세요.

 

2. VS Code에서 workspace 열기

워크스페이스를 열면 Workspace Trust를 물어봅니다. Yes 눌러주세요. 자세한 설명은 [바로가기]를 눌러주세요.

워크스페이스 여는 방법 : File > Open Workspace from File...

 

3.components 폴더 생성

컴포넌트 폴더에는 AntD의 예제 코드로 만든 컴포넌트를 모아둘 예정입니다. 컴포넌트는 리액트에서 변경될 DOM Elements입니다. DOM Elements가 이해가 안 되면, "함수 비슷한 건데 HTML 태그처럼 사용할 수 있는 함수"이라고 이해하셔도 괜찮습니다. 자세한 설명은 [추후 포스팅 예정]입니다.

(좌) 기본 앱 구조 (우) /src/components 생성
components 폴더에는 컴포넌트가 있다.

4. 프로그램 흐름

  • 메인 페이지인 index에서 App 컴포넌트 랜더링
  • App 컴포넌트에서 다른 컴포넌트(SearchBox, CustomTable) 랜더링 <- App 파일 수정 예정
  • SearchBox, CustomTable 컴포넌트에 각각 AtnD 예제 코드 구현 <- 컴포넌트 파일들 생성 예정

 

컴포넌트 랜더링이라는 말이 이해가 안 되면, 함수 호출이라고 생각하시면 됩니다. 많이 다른 개념이긴 하지만 맥락은 비슷합니다. 자세한 설명은 [추후 포스팅 예정] 입니다.


1. atnd 설치

antd 패키지만 설치하면 별도의 세팅 없이 바로 사용 가능합니다. 아래 명령어를 사용하면 현재 프로젝트의 node_modules 폴더 아래에 antd 패키지를 설치하게 됩니다.

npm install antd
더보기

🗨️ antd 패키지 설치 확인됐는지 어떻게 확인하나요?

  •  방법1. 의존성 관리 파일 확인하기

리액트에서는 pacakge.json에서 패키지 의존성을 버전과 함께 관리하고 있습니다. 즉, 해당 프로젝트에서 사용되는 패키지 정보는 여기에 다 기술되어져 있어서 이 파일에서 antd가 검색되면 프로젝트에 패키지가 설치된 것 입니다. 아래 사진에서 package-lock.json, package.json에 antd 패키지가 추가된 것을 확인할 수 있습니다.

[바로가기 : Package.json과 Package-lock.json의 차이를 아시나요? (velog.io)]

(좌) antd 설치 전 package.json (우) antd 설치 후 package.json
(좌) antd 설치 전 package-lock.json (우) antd 설치 후 package-lock.json
  •  방법2. npm 명령어로 패키지 리스트 확인하기

 npm ls 명령어를 사용해서 해당 프로젝트에 설치된 패키지와 버전을 검색할 수 있습니다. npm ls는 node_modules 아래에 설치된 패키지 목록을 트리 구조로 보여주는 명령어입니다. ls 뒤에 패키지명을 입력하면 해당 패키지 정보만 보여줍니다.

[바로가기 : npm은 패키지를 어디에 설치합니까? (tistory.com)] 

  •  방법3. 브라우저에서 antd 컴포넌트 잘 나오는지 확인하기

 아래 방법을 따라서 AntD 공식 문서에서 예제를 가져온 다음, 실행시켜 보세요. 브라우저에 잘 나오면 antd 패키지가 프로젝트에 잘 설치된 겁니다.


2. 공식 사이트 예제 가져오기 : 검색창

아래의 Antd의 공식 사이트에서 컴포넌트 예제를 통해 사용법을 익혀보도록 하겠습니다.

 

Components Overview - Ant Design

An enterprise-class UI design language and React UI library with a set of high-quality React components, one of best React UI library for enterprises

ant.design


검색어를 입력하면 테이블에 조회 결과가 뜨는 화면을 간단하게 만들어 보겠습니다.

먼저, 입력창이 있어야 하니까 Data Entry 카테고리의 Input 예제에서 검색창을 찾아보겠습니다.

체크 표시한 4번째의 검색창이 마음에 드니까 해당 코드를 가져와 보겠습니다. '< >' 버튼을 통해 코드를 미리볼 수 있습니다. 번개 모양과 정육면체 모양의 아이콘은 온라인 코드 편집기로 연결됩니다. 아래 설명을 적어 놓겠습니다.

더보기

✅ AntD에서 온라인 코드 편집기 지원

AntD에서는 코드의 동작을 확인해 볼 수 있도록, Stackblitz와 CodePen과 연동되어져 있습니다. 해당 온라인 코드 편집기를 통해 쉽게 코드를 확인해 볼 수 있습니다.

나머지 메뉴 설명

3번째 메뉴인 Open in a new window는 예제 코드 실행 결과를 새로운 창으로 보여줍니다. 마지막 메뉴인 'Show code'는 위와 같이 코드를 현재 창에서 보여줍니다.

 

 

아래 코드는, 위 이미지에서 체크한 검색창 예제 코드만 분리한 겁니다. components 폴더 아래에 SearchBox.tsx 파일을 만들어서 코드를 붙여넣으면 됩니다.

import React from 'react';
import { Input, Space } from 'antd';
import type { SearchProps } from 'antd/es/input/Search';

const { Search } = Input;

const onSearch: SearchProps['onSearch'] = (value, _e, info) => console.log(info?.source, value);

function SearchBox() {
  return (
    <Space direction="vertical">
      <Search placeholder="input search text" onSearch={onSearch} enterButton />
    </Space>
  );
};

export default SearchBox;
더보기

🗨️ 예제에서 원하는 검색창을 못 찾겠습니다.

원하는 예제 코드를 찾지 못 하겠다면 아래 그림을 참고해주세요. 태그 단위로 검색창을 세서 찾으시면 됩니다. HTML 태그처럼 생긴 Space 태그는, antd에서 제공하는 컴포넌트입니다. 컴포넌트를 단순하게 이해하자면, HTML 태그를 확장시킨 거라고 봐도 괜찮습니다. Space 컴포넌트(이해가 안 된다면, 태그라고 읽어도 괜찮습니다) 아래에 자식 컴포넌트로 Search 컴포넌트가 정의되어져 있습니다. Search 컴포넌트가 검색창으로 재사용되고 있기 때문에, 위에서부터 순서대로 세서 원하는 예제 코드를 찾으시면 됩니다.

더보기

🗨️ 오류가 나는데 어떻게 하나요?

아래처럼 import 구문의 from 문 아래에 빨간 줄이 생겼다면 해당 프로젝트에 antd 라이브러리가 설치되지 않은 것입니다. 경로를 확인해서 다시 설치를 진행해 주세요. 설치 명령어 확인은 [바로가기]를 눌러 주세요.

(좌) test 프로젝트에서 antd 라이브러리 미설치 오류 (우) vite_... 프로젝트에서 antd 라이브러리를 설치함

 

아래처럼 'import type' 구문에서 오류가 났다면, 파일 확장자를 ts 또는 tsx로 변경해서 빠르게 해결 가능합니다. 더 자세한 설명을 확인하려면 [바로가기]를 눌러 주세요.

 

혹시 컴포넌트를 분리해서 작성하는 과정 중에, 아래같은 오류가 나셨나요? 컴포넌트는 대문자로 시작해야 됩니다. searchBox -> SearchBox로 변경해 주세요. 더 자세한 설명은 [바로가기]에서 확인 가능합니다.

App 파일에 SearchBox 컴포넌트를 불러옵니다.

import SearchBox from './components/SearchBox'

function App() {
  return (
    <SearchBox />
  );
};

export default App;

 

저는 App3 파일에 불러왔습니다.

 

index 또는 main 파일에서 App 컴포넌트를 불러오시면 됩니다. 기본 프로젝트일 경우, 수정하실 내용은 없습니다. 저는 App3 컴포넌트를 만들었기 때문에 아래처럼 <App />을 <App3 />로 수정하겠습니다.

 

아래처럼 서버에서 원하는 검색창이 잘 보인다면, antd는 잘 설치된 겁니다. 같은 방법으로 계속 하겠습니다.


3. 공식 사이트 예제 가져오기 : 테이블

AntD 검색창에 table을 검색해서 나온 예제를 이용하겠습니다. 테이블 예시 코드는 제가 보여주고 싶은 항목에 맞게 임의로 변경했습니다.

//./components/Table.tsx
import React, { useEffect, useState } from "react";
import { Table, Tag } from "antd";
import type { TableProps } from "antd";

// 테이블의 컬럼을 객체로 정의
interface DataType {
  key:     number;
  name:    string; //이름     : 홍길동
  birth:   string; //생년월일 : 1999.01.01(만25세)
  skGrade: string; //기술등급 : 특급
  role:    string; //주역할   : 개발
  skill:   string; //사용기술 : Java, C, ProC, 웹스퀘어
  updInfo: string; //최종수정 : 홍길동/2024.04.22
}

// *주의: Typescript는 함수나 컴포넌트의 매개변수에 타입을 적어주지 않으면 오류 발생
// *오류: Binding element 'skGrade' implicitly has an 'any' type.
interface PropsCustomTag {
  skGrade: string;
}

// 기술등급을 태그 모양으로 보여주는 함수형 컴포넌트
// : 이 컴포넌트는 '테이블의 컬럼을 정의할 때 render'에서 사용 예정 (78번 줄 참고)
const CustomTag = ({ skGrade }: PropsCustomTag) => { // 컴포넌트 매개변수 : 객체!! {}로 나타낼 수 있음
  let color = "";
  if (skGrade === "초급") {
    color = "green";
  } else if (skGrade === "중급") {
    color = "geekblue";
  } else if (skGrade === "고급") {
    color = "gold";
  } else if (skGrade === "특급") {
    color = "magenta";
  }
  
  // JSX를 return하면 함수형 컴포넌트! 그렇지 않으면 일반 함수!
  // JSX에서 변수를 사용하고 싶을 땐, {변수명}으로 나타낼 수 있음
  // -> JS 문법의 {}는 객체를, JSX 문법 {}는 변수를 나타냄 (서로 다른 걸 표현)
  return (
    <Tag color={color} key={skGrade}>
      {skGrade}
    </Tag>
  );
};

// 테이블의 컬럼 정의(by. antd Table Component)
// - ["column"] : 테이블의 열을 나타내며, "이름"과 "키" 등의 속성을 가짐
// column 정의
// - title: 열의 제목
// - dataIndex: 열 데이터의 키
// - key: 열의 고유 키
// - render: 열 데이터를 어떻게 렌더링할지를 정의
const columns: TableProps<DataType>["columns"] = [
  {
    title: "이름",
    dataIndex: "name",
    key: "name",
  },
  {
    title: "생년월일",
    dataIndex: "birth",
    key: "birth",
  },
  {
    title: "기술등급",
    key: "skGrade",
    dataIndex: "skGrade",
    /* ---------------------------------------------------------
     컴포넌트는 props(properties의 줄임말)를 통해 인자를 전달받음 (props는 객체)
     = props에 값을 넣으면 컴포넌트의 인자로 맵핑(연결)됨
     -----------------------------------------------------------
     위 설명 자세히...
     props는 html의 attribute와 비슷하게 생김; 비슷하게 사용하면 됨
     - ex. <button type="submit" /> -> 태그명 : button
                                    -> 속성(attribute) : type
                                    -> 속성값(attribute value) : submit
     - ex. <compoName propsKey=value /> -> 컴포넌트명 : compoName
                                        -> props의 key : propsKey
                                        -> props의 값 : value
    따라서 props의 값에 변수를 사용하고 싶으면 JSX 문법을 따라 {변수명} 사용
    그러면 해당 컴포넌트의 인자로 props의 값이 넘어감
    --------------------------------------------------------- */ 
    render: (text) => <CustomTag skGrade={text} />,
  },
  {
    title: "주역할",
    dataIndex: "role",
    key: "role",
  },
  {
    title: "사용기술",
    dataIndex: "skill",
    key: "skill",
  },
  {
    title: "최종수정",
    dataIndex: "updInfo",
    key: "updInfo",
  },
];

// 실제 데이터
const data: DataType[] = [
  {
    key:     1,
    name:    "홍길동",
    birth:   "1999.01.01(만26세)",
    skGrade: "특급",
    role:    "개발",
    skill:   "Java, C, ProC, 웹스퀘어, JQuery, Oracle, mySQL, IBM DB2, Spring, BMX",
    updInfo: "홍길동/2024.04.22",
  },
  {
    key:     2,
    name:    "홍길동",
    birth:   "1999.01.01(만26세)",
    skGrade: "특급",
    role:    "개발",
    skill:   "Java, C, ProC, 웹스퀘어, JQuery, Oracle, mySQL, IBM DB2, Spring, BMX",
    updInfo: "홍길동/2024.04.22",
  },
  {
    key:     3,
    name:    "홍길동",
    birth:   "1999.01.01(만26세)",
    skGrade: "특급",
    role:    "PL",
    skill:   "Java, C, ProC, 웹스퀘어, JQuery, Oracle, mySQL, IBM DB2, Spring, BMX",
    updInfo: "홍길동/2024.04.22",
  },
  {
    key:     4,
    name:    "홍길동",
    birth:   "1999.01.01(만26세)",
    skGrade: "초급",
    role:    "테스터",
    skill:   "Java, C, ProC, 웹스퀘어",
    updInfo: "홍길동/2024.04.22",
  },
  {
    key:     5,
    name:    "홍길동",
    birth:   "1999.01.01(만26세)",
    skGrade: "중급",
    role:    "개발",
    skill:   "Java, C, ProC, Spring, BMX",
    updInfo: "홍길동/2024.04.22",
  },
  {
    key:     6,
    name:    "홍길동",
    birth:   "1999.01.01(만26세)",
    skGrade: "중급",
    role:    "개발",
    skill:   "Java, C, ProC, Spring, BMX",
    updInfo: "홍길동/2024.04.22",
  },
  {
    key:     7,
    name:    "홍길동",
    birth:   "1999.01.01(만26세)",
    skGrade: "고급",
    role:    "PM",
    skill:   "Java, C, ProC, Spring, BMX",
    updInfo: "홍길동/2024.04.22",
  },
];

// 위에서 정의한 테이블을 보여주는 함수형 컴포넌트 정의
// - 'columns' props 에는 정의한 컬럼이 들어감(title, render 등)
// - 'dataSource' props 에는 실제 데이터가 들어감
// 아래의 ': React.FC'는 변수의 타입을 선언하는 TypeSCript 문법 (선언 없어도 동작)
const CustomTable: React.FC = () => <Table columns={columns} dataSource={data} />;

export default CustomTable;

 

App 컴포넌트에 CustomTable 컴포넌트 추가합니다. 저는 App3에 추가하겠습니다.

 

결과를 확인하면 아래처럼 나옵니다.

더보기

✅ 소스 코드 조금 더 바꿔 보기(1)

생년월일을 표시하는 방법을 조금 바꿔보겠습니다. data 배열에서 birth에 "19990101"을 넣으면 화면에는 "1999.01.01 (만##세)"라고 뜨게 만들 예정입니다. 아래 소스에 주석을 달아 설명해 놓았습니다.

결과 이미지
import dateUtil from "../common/dateUtil";
import stringUtil from "../common/stringUtil";

// 일반 함수의 매개변수는 일반 변수를 쓴다.
const calcAgeColumns = (birth) => {
  if (stringUtil.isEmpty(birth)) {
    return "-";
  }
  // 오늘 날짜 구하기
  const todayObj = dateUtil.getSysdateObject();
  const todayStr = String(Object.values(todayObj));
  // 만 나이 구하기
  const age = dateUtil.calcAgeBirth(birth, todayStr);
  const result = dateUtil.getFormattedDate(birth, ".");

  return `${result} (만${age}세)`;
};

/* .... 이하생략 .... */

const columns: TableProps<DataType>["columns"] = [
  /* .... 이하생략 .... */
    {
    title: "생년월일",
    dataIndex: "birth",
    key: "birth",
    // 일반함수/컴포넌트 매개변수 표현차 이해 중요!
    // - 일반함수는 매개변수나 인자로 변수를 받을 때, JS 문법을 따름 --> 변수명
    // - 컴포넌트의 매개변수나 인자를 변수를 받을 때, JSX 문법을 따름 --> {변수명}
    //   --> 이유 : 컴포넌트는 props라는 객체를 통해 인자를 전달받기 때문!
    //   --> 이유 : JS 문법에서 객체는 {}를 사용해서 나타냄
    render: (text) => calcAgeColumns(text),
  },
  /* .... 이하생략 .... */
]
  
const data: DataType[] = [
  {
    /* .... 이하생략 .... */
    birth: "20161123",
    /* .... 이하생략 .... */
  }
]
/* .... 이하생략 .... */

 

✅ 소스 코드 조금 더 바꿔 보기(2)

본문에서는 AntD의 columns API 중 render에 대한 설명이 부족했습니다. 코드를 통해 확인해보도록 하겠습니다.

// render을 이해하기 위해 columns 배열의 원소 추가
const columns: TableProps<DataType>["columns"] = [
  /* .... 이하생략 .... */
    title: "",
    dataIndex: "updInfo", // dataIndex에 적은 객체key에 따라 text 내용 결정 (record, index는 이와 무관)
    key: "updInfo",
    render: (text, record, index) => (
      <div>
        {text} <a style={{ color: "blue" }}>{Object.values(record)}</a> {index}
      </div>
    ),
    /* ----------------------------------------------------------------------------- 
    render: function(text, record, index) => {}     (뇌피셜)
    - text: string   <-- 데이터
                         : dataIndex의 값에 따라 text 내용 결정
    - record: object <-- antd의 Table 컴포넌트에서 dataSource property를 row 단위로 가져옴 (그게 일반적으로, object)
                         : 일반적으로 dataSource에 배열을 넣기 때문에, record로 값을 가져올 때 dataSource의 인덱스 단위로 가져옴
                           위 예제에서 dataSource의 props는 data: DataType[] 였기 때문에, record에는 DataType 객체가 들어옴
                         object에 접근할 땐 키값으로 접근해야 됨. (i.e. record['birth'] 또는 record.birth)
                         : 그렇지 않고, 객체를 그대로 꺼내쓰면(i.e. <>{record}</>) 오류발생(Objects are not valid as a React child)
    - index: number <-- 행 인덱스
    *(_) : 자리 표시자 변수(사용되지 않는 매개변수 나타냄, i.e. """render: (_, _, index) => {}""")
     ----------------------------------------------------------------------------- */
  },
  ]
결과이미지
render의 콜백함수의 매개변수 설명
dataIndex가 render의 text 인자에 들어가는 값 결정

dataUtil이나 stringUtil 및 위 예제를 확인하고 싶다면 깃허브에서 확인 가능합니다.

binigy97/testReactApp: React App based on TypeScript, Tailwind, AntD, React-Router-Dom (github.com)

 

GitHub - binigy97/testReactApp: React App based on TypeScript, Tailwind, AntD, React-Router-Dom

React App based on TypeScript, Tailwind, AntD, React-Router-Dom - binigy97/testReactApp

github.com


참고로, CustomTable 컴포넌트 이름을 Table로 하지 않은 이유는, antd의 Table 컴포넌트와 중복되기 때문입니다. 관련 설명은 [추후 포스팅 예정 : 컴포넌트의 이름과 파일명의 관계] 입니다.

import { Table, Tag } from "antd";

// 오류 발생 이유
// : antd에서 columns과 dataSource를 매개변수로 받는 Table 컴포넌트와
//   새로 정의하는, 매개변수가 없는 Table 컴포넌트의 이름이 겹쳐서 컴파일러가 구분하지 못 함
const Table: React.FC = () => <Table columns={columns} dataSource={data} />;

// 정상 동작 이유
// : 한 파일 내에 모든 컴포넌트 이름이 달라서 컴포넌트의 구분이 명확함
const CustomTable: React.FC = () => <Table columns={columns} dataSource={data} />;

*Tag 컴포넌트 색상 참고

 

Tag - Ant Design

An enterprise-class UI design language and React UI library with a set of high-quality React components, one of best React UI library for enterprises

ant.design


더보기

💬 Table.tsx 의 16, 52 번째 줄의 매개변수가 왜 다른가요?

하나는 JavaScript의 구문을 따른 것이고, 다른 하나는 라이브러리에 정의된 규칙을 따른 것이라서 동일한 매개변수더라도 표현하는 방법이 다릅니다.

// 16번째 줄 : 함수의 매개변수로 구조분해할당 받은 변수 사용
const CustomTag = ({ skGrade }) => {...}

const columns: TableProps<DataType>["columns"] = [
  {
    title: "기술등급",
    key: "skGrade",
    dataIndex: "skGrade",
    // 52번째 줄 : antd에 정의된 render에 매개변수 data 대신 별칭 사용
    render: ( skGrade ) => <CustomTag skGrade={skGrade} />,
  },
]
  • 16번째 라인

CustomTag 상수에 Arrow Function(화살표 함수)을 담은 형태입니다. 매개변수로 DataType 객체의 skGrade key의 값을 구조 분해 할당 받은 형태입니다. 자세한 설명은 [추후 포스팅할 예정]입니다.

  • 52번째 라인

antd의 API를 사용한 코드입니다. columns API는 테이블의 columns(열)에 대해 객체 형태로 정의해서 이를 배열의 원소로 담고 있습니다. 그중, render은 랜더링할 열 데이터에 대해 정의합니다. 일반적으로 익명함수 또는 화살표 함수를 사용하며 함수의 매개변수와 반환 타입이 정의되어져 있습니다.

render: function(text, record, index) => {return(...)}
- text: 데이터
- record: 전체 행 데이터
- index : 행 인덱스
- 반환타입 : 'ReactNode' 또는 'RenderedCell<DataType>'

 

정의에 따라 매개변수는 최대 3개까지 사용 가능하며, 각 매개변수의 이름은 별칭(alias)을 사용할 수 있으나 해당 자리에 맞춰서 사용해야 됩니다. 사용하지 않을 매개변수는 생략 또는 자리 표시자 변수(_)를 이용해 나타낼 수 있습니다.

render: function(_, record) => {return(...)}
- text 미사용
- record 매개변수로 사용
- index 미사용

자세한 설명은 [추후 포스팅 예정]입니다.


감사합니다. :)