[react-ksmodal] 첫 npm 라이브러리 개발기

김상민·2023년 10월 18일
1

React

목록 보기
2/4

🔥 배경

프론트엔드 개발자로 여러 팀 프로젝트와 인턴 활동에 참여하다보니 이런 모달을 만드는 작업이 많았다.

이게 참 보기에는 별 거 아닌 UI같지만 타이머로 몇초동안 보여줄지도 정해야하고, 애니메이션도 넣고, 다른 컴포넌트와 겹치지 않게도 해야하고,,,😅 신경 써야하는 요소들이 꽤 있었다.

이런 작업들을 반복하는 것이 너무 비효율적인 것 같아 라이브러리들을 찾아보았다.
하지만 결국 타이머 설정을 직접하거나 모달을 보여주는 state를 직접 만들어서 관리해야 했기 때문에 큰 의미가 없었다.

그래서 직접 라이브러리를 만들어보기로 했다. 😎

📌 링크

일단 결과물 링크..

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

✏️ 고려사항

  • 누구든 쉽게 사용할 수 있어야한다.
  • 너무 많은 커스텀 기능을 넣지말자.
  • 용량은 최대한 작게하자.

기존 라이브러리들의 경우 활용 범위를 넓히기 위해 사용자에게 자유도를 주다보니 오히려 설정해야할 것들이 많다고 느꼈다.

간단한 기능을 재사용하기 위한 라이브러리인 만큼 제일 먼저 위와 같이 지켜야할 사항들을 만들었다.

📑 기능 명세

정말 간단하지만 메모장에 기능 명세도 만들었다.

💻 개발시작

⚙️ 프로젝트 세팅

최대한 가볍게 만들기 위해서 번들링 도구도 이용하지 않고 npm init으로 패키지를 만들기 시작했다.

그리고 의존성 모듈도 최소화하기 위해 ReactTypescript만 설치했다.

package.json

Typescript로 프로젝트기 때문에 빌드 스크립트를 tsc로 설정했다.

"scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "tsc"
 },

tsconfig.json

그리고 tsconfig.json을 만들어서 아래와 같이 설정했다. 나중에 이걸 엄청 건드렸음…

{
    "compilerOptions": {
        "jsx": "react",
        "target": "es5",
        "module": "commonjs",
        "outDir": "./lib",
        "rootDir": "./src",
        "strict": true,
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true
    },
    "include": ["src/**/*"]
}

이렇게 설정파일들을 만든 뒤에 드디어 코드 개발을 시작했다.

code

먼저 구현을 위한 react 프로젝트를 하나 만들어서 모달 기능을 다 구현을 하고, 이걸 재사용할 수 있는 component와 hook으로 분리했다.

<button onClick={handleShowMessage}>Show Message</button>
<CustomMessage isVisible={isVisible} />

위와 같은 형태로 사용하게 하고 싶었다.

hook

React-query를 사용하면서 useQuery를 hook으로 분리해본 경험을 살려 만들어보았다.

function useCustomMessage() {
  const [isVisible, setIsVisible] = useState(false);

  const showCustomMessage = (...props) => {
    ...
  };

  return { isVisible, showCustomMessage };
}

export default useCustomMessage;

component

function CustomMessage({ isVisible, message }) {
  return isVisible ? (
    <div
      style={{
        ...
      }}
    >
      {message}
    </div>
  ) : null;
}

factory fn

이렇게 만들다 보니 문득 모달을 여러개 만들때 영향을 서로 받지 않게 해야겠다는 생각이 들었고, 정보처리기사를 공부하며 봤던 팩토리 패턴을 사용해보기로 했다.

function KsmodalFactory({props...}: KsmodalProps) {
		return <Ksmodal props... />;
};

structure

이후 모듈별로 파일을 분리했더니 제법 그럴듯해졌다.

🧪 Test

구현을 마치고 여러 값들을 바꿔가며 잘 동작하는지 확인을 했다. 하지만 역시나 에러가 있었다.

Animation

입력한 duration만 가지고 timeout을 조정하다보니 fade-out animation이 동작하지 않았다.

이를 해결하기 위해 어쩔 수 없이 상태를 하나 더 만들고 useEffect로 animation이 끝난 뒤 modal의 상태를 변경하도록 timeout을 한번 더 걸었다.

const [isRendered, setRendered] = useState(isVisible);

    useEffect(() => {
        if (isVisible) {
            setRendered(true);
            setTimeout(() => {
                setRendered(false);
            }, duration + 500);
        }
    }, [isVisible, duration]);

    return (
        <div
            className
            style={{
                display: isRendered ? "block" : "none",
                restprops...
            }}
        >
            {message}
        </div>

더 좋은 해결 방법이 있을 것 같지만 당시 생각나던 건 이 방식이었다.

📢 Publishing

코드가 잘 동작하는 것을 확인하고 배포를 시작했다.

설치 및 사용방법, 예제 코드, API 문서를 Readme에 작성하고 build를 했다.

⛔️ build

역시나 한 번에 되지 않았다…

의존성이 충돌해서 빌드가 불가능하다는 에러가 나왔고 환경 세팅에 두려움이 있던 나는 일단 구글링부터 했다.

tsconfig에 아래와 같은 옵션을 추가하라는 답변을 보고 추가한 뒤 빌드를 했더니 성공은 한 것 같았다.

"skipLibCheck": true

하지만 옵션의 이름에 skip이 들어간 것이 찝찝해서 좀 더 찾아보았고 스킵하는 것보다 충돌하는 패키지를 없애는 것이 더 깔끔하다는 결론을 내리고 다시 옵션을 없앴다.

에러 메세지를 살펴보니 패키지에서 사용하지도 않는 react-native와 충돌한다는 문구가 있었다.

경로를 따라 들어가보니 react-native가 global로 설치되어 있어서 발생한 것이었다.

이를 삭제하고 깔끔하게 빌드에 성공했다. 😎

⛔️ error

npm에 처음으로 publish를 하고 아주 뿌듯해하며 테스트 프로젝트에 설치를 했다.

하지만 import가 되지 않았다..

번들링 도구를 지금이라도 써서 만들어야하나 100번정도 고민됐다…😂

그래도 여러 방법으로 해결책을 조사하고 빌드된 파일을 찬찬히 살펴보니 index.d.ts가 없다는 것을 깨달았다.

tsconfig에 아래와 같은 옵션을 추가하고 다시 빌드하니 index.d.ts가 생겼다.

"declaration": true

⛔️ error2

이제 다 되었다고 생각하고 다시 배포 후 설치를 했다.

하지만 여전히 import가 되지 않았다.

원인을 찾기 위해 package.json의 속성들을 조사했다. 그렇게 많이 들여다본 파일인데 속성들에 대해 모르고 넘어갔다는 것이 부끄러웠다.

"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"files": [
        "lib"
],

패키지를 다운 받고 import를 할때 빌드 파일의 경로를 알 수 있도록 package.json에 위와 같은 옵션을 수정했다.

⛔️ error3

정말 다 된 줄 알았다..

리액트 서버를 켜자마자 이번엔 css 파일을 찾을 수 없다는 에러가 나왔다.

다시 빌드 파일을 살펴보니 css 파일이 없었다.

일반적으로 TypeScript에서 CSS 파일을 import할 때 CSS 모듈을 사용한다면, 이러한 CSS 모듈 파일은 JavaScript 파일로 컴파일되지만 별도의 CSS 파일로 빌드되지는 않습니다. 따라서 lib 폴더에 CSS 파일이 생성되지 않습니다.

다행히도 빌드 디렉터리에 css 파일을 집어넣으니 잘 작동했다.

👏 Published

드디어 첫 npm 라이브러리를 완성해서 배포했다.

효율적으로 개발할 수 있는 도구를 직접 만들었다는 것도 좋았지만 개발하면서 패키지나 빌드도구의 작동 방식을 좀 더 공부하게 된 점이 정말 의미있었다.

profile
성장하는 웹 프론트엔드 개발자 입니다.

0개의 댓글