Inner Circle 두 번째 프로젝트 '오픈소스 라이브러리 프로젝트' 활동에서 만든 라이브러리.
https://www.npmjs.com/package/react-split-table
react-split-table
A React component for creating split tables with resizable columns and rows.. Latest version: 0.0.20, last published: a day ago. Start using react-split-table in your project by running `npm i react-split-table`. There are no other projects in the npm regi
www.npmjs.com
아래의 순서로 진행되었다.
1. github action 설정
2. 기능 개발
3. 테스트 코드 작성
4. readme.md 작성
5. npm 배포
과정
1. github action 설정
깃헙은 클라우드 서버를 준비해놓고, 명령어가 들어가있는 깃헙 액션 파일을 만들면 그 서버에서 명령어가 실행되도록 한다.
클라우드 서버의 OS 종류는 직접 선택할 수 있으며, 나는 보편적으로 많이 사용되는 우분투 리눅스를 사용했다.
다음은 main으로 PR이 발생하면 테스트 코드를 실행하도록 하는 깃헙 액션 설정 파일이다.
name: Test when PR is created
on:
pull_request:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository (깃허브 저장소에 있는 소스 코드를 서버에 가져옴)
uses: actions/checkout@v3
- name: Setup Node.js (서버에 Node.js 18버전 설치해서 맞춰줌)
uses: actions/setup-node@v3
with:
node-version: "18"
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
정말 필수 기능만 들어가도록 간결하게 설정한 파일이다. (괄호 안은 그냥 설명임. 제외하고 쓰기)
npm install 대신 npm ci를 사용할 수도 있다.
npm ci는 빠른 대신 package-lock.json이 없거나 package.json과 다르면 에러가 발생한다.

깃허브에 Action 탭에 들어가면 지정해놓은 name에 따라 위와 같이 태스크가 진행됨을 눈으로 볼 수 있다.
2. 기능 개발
소스코드는 깃헙에 있음.
리더님께서 말씀해주셔서 이번에 안 사실이지만, debounce 같은 기능은 그냥 라이브러리를 사용하는 게 편하다.
예를 들어서 lodash.debounce 등...
이유는 이미 검증된 라이브러리일 가능성이 크다 보니 내가 직접 테스트 코드를 작성하지 않아도 되기 때문.
직접 라이브러리를 개발하는 거니 무조건 의존성이 적은 게 좋다고 생각했는데 꼭 그렇진 않은 것 같다.
용량 문제는 어떨까?
lodash 라이브러리 전체를 install하면 용량이 상당히 크겠지만...


debounce 하나만 가져온다면 얼마 안한다.
3. 테스트 코드 작성
Vitest와 React Testing Library를 이용했다. 특정 DOM 요소의 상태를 확인하기 위해서는 jest-dom도 설치해야 한다.
describe("onCellOverflowing 이벤트가 호출되었을 때", () => {
describe("마지막 테이블인 경우", () => {
it("맨 마지막 컬럼을 테이블로 분리한다", async () => {
render(<TableWrapper {...dummyProps} />);
savedOnCellOverflowing(0);
await waitFor(() => {
expect(screen.getAllByTestId("table")).toHaveLength(2);
});
const [table0, table1] = receivedPropsList.slice(-2).map(p => p.info);
const table0ColumnTitles = table0.columns.map((c: any) => c.title);
const table1ColumnTitles = table1.columns.map((c: any) => c.title);
expect(table0ColumnTitles).not.toContain("Job");
expect(table1ColumnTitles).toContain("Job");
});
});
});
늘 그랬듯 given-when-then 패턴으로 작성한다.
요새는 AI가 테스트 코드를 그런대로 잘 짜줘서 예전보다는 짜기가 수월해졌으나 시간을 많이 잡아먹는건 똑같다.
4. readme.md와 LICENSE 파일 작성
readme에는 라이브러리에 대한 설명이나 설치 방법, 사용 방법(예시 코드) 등이 들어가면 좋다.
LICENSE는 정해진 폼이 있어서 가져다 쓰면 되고 나는 MIT로 등록하였다.
5. npm 배포
개인적으로 제일 힘들었던 작업. 일단 프로젝트 막판이라 시간이 너무 부족해서 쪼들리며 작업했다. 이거랑 깃헙 액션을 이번에 처음 해봤는데 깃헙 액션은 생각보다 까다로운 점이 없었지만 배포가 정말 힘들었다... 특히 타입스크립트를 사용했기에 tsconfig 설정하는게 상당히 귀찮았다.
먼저 package.json을 보자.
{
"name": "react-split-table",
"license": "MIT",
"version": "0.0.20",
"type": "module",
"description": "A React component for creating split tables with resizable columns and rows.",
"homepage": "https://github.com/ParkBible/react-split-table",
"keywords": [
"react",
"table",
"split",
"resizable",
"columns",
"rows"
],
"scripts": {
"dev": "vite",
"build": "tsc -b tsconfig.app.json && vite build",
"lint": "eslint .",
"test": "vitest run",
"test:watch": "vitest"
},
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"devDependencies": {
"@eslint/js": "^9.21.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@types/node": "^22.14.1",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react": "^4.3.4",
"@vitest/coverage-v8": "^3.1.1",
"c8": "^10.1.3",
"debounce": "^2.2.0",
"eslint": "^9.21.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^15.15.0",
"jsdom": "^26.1.0",
"prettier": "3.5.3",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"typescript": "~5.7.2",
"typescript-eslint": "^8.24.1",
"vite": "^6.2.0",
"vitest": "^3.1.1"
},
"files": [
"dist",
"package.json",
"README.md",
"LICENSE",
"types",
"styles"
],
"main": "dist/index.js",
"module": "dist/index.js",
"types": "types/src/index.d.ts"
}
유의해야 될 점
1)
라이브러리 프로젝트에서는 dependencies 대신 peerDependencies를 써야 한다.
예를 들어, 만약 dependencies에 18.0.1 버전의 React를 넣어놓으면, 내 라이브러리를 설치하는 사람한테 해당 버전의 React가 같이 깔려버린다.
하지만 peerDependencies는 사용자가 직접 준비해놓아야 하는 라이브러리를 표시해놓은 것이다.
나는 17~19 버전의 리액트에서 이 라이브러리가 지원되도록 표시했다.
내 라이브러리를 쓰는 사용자는 해당 버전의 리액트를 알아서 준비해놓으면 되는 것이다.
2)
scripts.build를 보면 tsc와 vite 명령 두가지가 들어가있다.
tsc는 타입스크립트 프로젝트에서만 필요한 것으로 타입스크립트를 자바스크립트로 변환한다. -b는 빌드 효율을 높이는 옵션이다.
중요한 것은 라이브러리에는 .d.ts 파일(타입 정보가 포함된 파일)이 포함되어야 한다는 점이다. 그래야 사용자가 내 라이브러리를 import 해서 쓸 수 있다.
.d.ts 파일을 만들려면 설정을 따로 해주어야 하는데, 이건 tsconfig.json에서 설정한다. 나는 tsconfig.app.json에 설정해두었다.
vite build는 프로젝트를 번들링하는 명령어이다.
3)
files에는 npm에 올라갈 파일들을 설정한다. 모두 프로젝트 루트에 있어야 한다.
types 라는 디렉토리에는 .d.ts 파일들이 올라가있는데, 사실 이 디렉토리는 dist에 있는 게 보편적이다.
하지만 나는 vite build 명령어가 실행되면 dist 안에 있는 types 디렉토리가 모조리 지워진 후 다시 만들어지지 않는 문제가 있었고 때문에 프로젝트 루트에 types를 만든 것이다.
하지만 찾아보니 vite-plugin-dts 라는 라이브러리를 쓰면 좀더 쉽게 설정이 가능하다고 한다.
plugins: [
dts({
outputDir: "dist/types", // 타입은 dist/types/ 밑에 생성
tsConfigFilePath: "./tsconfig.app.json", // tsconfig 경로
insertTypesEntry: true, // .d.ts 파일 생성할지
skipDiagnostics: true, // 타입 체크는 생략 (속도 + 안정성)
}),
],
vite.config.ts에 위와 같이 쓰면 된다고 하는데... 나중에 한번 해봐야겠다.
그리고 styles 폴더도 files에 같이 되어있는데...
사실 이건 번들러가 알아서 dist에 index.css를 만들어줘서 굳이 할 필요가 없는 작업이었다.
사용자는 그냥 import "react-split-table/dist/index.css"; 로 쓰면 된다.
불필요하니까 빼야겠다.
6. 사용
import { TableWrapper } from "react-split-table";
import { Column, Row } from "react-split-table/types/src/constants/tableInterfaces";
import "react-split-table/styles/table.css";
const ticketColumns: Column[] = [
{ order: 1, title: "ticketId" },
{ order: 2, title: "customer" },
{ order: 3, title: "subject" },
{ order: 4, title: "status" },
{ order: 5, title: "assignedTo" },
];
const ticketRows: Row[] = [
{
order: 1,
ticketId: "#20250401",
customer: "Alice Kim",
subject: "Cannot reset password",
status: "Open",
assignedTo: "Support Team A",
},
{
order: 2,
ticketId: "#20250402",
customer: "Ben Choi",
subject: "Error 500 on checkout page",
status: "In Progress",
assignedTo: "Backend Team",
},
{
order: 3,
ticketId: "#20250403",
customer: "Charlie Park",
subject: "Refund request",
status: "Resolved",
assignedTo: "Billing Dept",
},
{
order: 4,
ticketId: "#20250404",
customer: "Dana Lee",
subject: "Profile picture upload issue",
status: "Pending Customer",
assignedTo: "Frontend Team",
},
{
order: 5,
ticketId: "#20250405",
customer: "Eric Kim",
subject: "Login loading problem",
status: "Open",
assignedTo: "Platform Team",
},
];
export const Test = () => {
return (
<>
<TableWrapper
columns={ticketColumns}
rows={ticketRows}
className="table1"
/>
</>
);
};
보다시피 import할 때 depth가 매우 깊은데... 이는 package.json 설정을 통해 줄일 수도 있다.
하지만 그 전에 dist 패키지 정리를 하면 해결될 수도 있으니까 그것부터 먼저 해야겠다...
7. 유지보수 계획
- dist 패키지에 필요한거 모두 정리하기, styles 디렉토리는 제외하기
- 테스트코드 마저 작성하고 커버리지 90% 넘겨서 커버리지 뱃지 달기
- 로직 리팩토링하기 (바빴어서 코드가 좀 지저분함)
- 추가 기능 고려하기
(+)

올린지 하루됐는데 뭐예요 무서워요..
'IT > React & Next.js' 카테고리의 다른 글
| [React] 훅 안에서 정의한 함수의 클로저 문제 (0) | 2025.10.28 |
|---|---|
| [Infra] Biome.js VSCode 세팅 (1) | 2025.08.31 |
| [Next.js] RCC, RSC 활용 예시 (0) | 2025.03.03 |
| [Next.js] SSR에서의 Streaming HTML (0) | 2025.02.24 |
| [Next.js] Shared Component (0) | 2025.02.20 |