처음으로 오픈소스 기여한 후기
date
May 18, 2023
slug
contribue-open-source-review
status
Public
category
JS / TS
tags
FrontEnd
React
summary
자바스크립트 프로젝트를 타입스크립트로 마이그레이션해서 PR을 올렸어요
type
Post
Created Time
May 18, 2023 11:19 AM
파일과 미디어
![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2Fd34413a3-be85-4b30-b7a7-c1a9e70c124b%2Faf3bed9f-058e-4e6b-bceb-5e6f58d7ff1e%2FUntitled.png%3Ftable%3Dblock%26id%3D2a61d045-7675-403b-950e-c1f173d56639%26spaceId%3Dd34413a3-be85-4b30-b7a7-c1a9e70c124b%26expirationTimestamp%3D1726207200000%26signature%3DjtLV_lBBkbduSMqqpusYcpLMHMaMItlXyCdcQg2J8YU?table=block&id=2a61d045-7675-403b-950e-c1f173d56639&cache=v2)
오픈 소스 첫 기여를 했다.
동기
회사에서 쓰고 있는 라이브러리였는데, 옛날에 만들어진 데다가 좀 하꼬 라이브러리인지라
Javascript
와 리액트 클래스 컴포넌트
로 구성된 라이브러리였다.옛날에는 그냥 CRA로 구성된 자바스크립트 프로젝트만 구축해왔기 때문에 상관 없었지만, 이제 나의 주도로
Next.js
와 Typescript
환경에서 코딩을 하다보니 이런 문제점이 눈에 들어오기 시작했다.Typescript
프로젝트 에서 타입스크립트를 지원하지 않는 라이브러리를 사용하려면 declare module 이런 거를 해서 직접 타입을 적어줘야 해서 조금 귀찮은 부분이 있다. 또, 클래스 컴포넌트에서 componentWillReceiveProps
같이 deprecated된 api를 사용하고 있어서 매번 켤 때마다 이상한 경고 문구가 뜨는 것도 좀 짜증 포인트..그래서 한번 오픈소스 기여도 해볼 겸, 회사 사람들 편의성 증진도 해볼 겸. 이 라이브러리를 일단은 타입스크립트로 마이그레이션하는 계획을 세워본 것이다.
진행
🗒️ 공식 문서 확인하기
일단 타입을 먹여야 하기 때문에 github 페이지에서 문서를 확인해보았다.
![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2Fd34413a3-be85-4b30-b7a7-c1a9e70c124b%2F849a9b87-290c-4a84-8b89-313f62734be8%2FUntitled.png%3Ftable%3Dblock%26id%3Dc5421d51-6d09-4f84-849a-af87ec3be93f%26spaceId%3Dd34413a3-be85-4b30-b7a7-c1a9e70c124b%26expirationTimestamp%3D1726207200000%26signature%3DUqbeDMaHKXugBMWMHq1DacCe-jLcOlREDsoTkENnlOI?table=block&id=c5421d51-6d09-4f84-849a-af87ec3be93f&cache=v2)
다 좋은데, 눈에 띄는 것들이 보였다.
onCloseTab
, onTabClick
, onBeforeTabClick
같은 녀석들은 함수 파라미터 정보만 적혀있고, 파라미터 타입과 리턴 타입은 전혀 적혀 있지 않았다. 이건 뭐.. 일단 대충 예상으로 끼워 맞출 수 있다지만..renderClose
이 녀석은 타입이 Function이라고만 있고, 어떤 파라미터가 들어가는지 전혀 적혀 있지가 않다. 거기다가 기본 값이 ‘X’ 라고 하는데. 그럼 Function이거나 string이라는 거 아니야?굉장히 혼란스러운 녀석이 아닐 수 없었다.
어쩌겠는가. 코드를 직접 까보면서 확인해봐야지.
🧾 코드
![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2Fd34413a3-be85-4b30-b7a7-c1a9e70c124b%2Fcdca76a0-3e6c-4cfb-8af8-d854864fff37%2FUntitled.png%3Ftable%3Dblock%26id%3Dd00a675e-7f53-4539-b3f3-f2f4ee1fd14b%26spaceId%3Dd34413a3-be85-4b30-b7a7-c1a9e70c124b%26expirationTimestamp%3D1726207200000%26signature%3DWlpKxQHrGMxB4XKFo-nf2MVCxzejy2-GSwEs3q0R1TI?table=block&id=d00a675e-7f53-4539-b3f3-f2f4ee1fd14b&cache=v2)
구조는 굉장히 단순했다..
/src/index.js
에 모든 코드가 집대성 되어 있었고, /demo/src/index.js
에 데모용 홈페이지가 들어가 있었다.하지만 조금 걱정되는 부분은 CRA나 Vite 같은 거로 구성된 레포지토리가 아니라,
nwb
라는 녀석으로 만들어진 프로젝트였다. 음.. 처음 보는 녀석이라서 조금 걱정되었으나 일단 코드 수정 먼저 들어가보는 수밖에.![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2Fd34413a3-be85-4b30-b7a7-c1a9e70c124b%2F2ad6fc11-82c1-474f-a388-1e3f2db0fd81%2FUntitled.png%3Ftable%3Dblock%26id%3D167f9785-8727-4235-8d50-4d2d069520eb%26spaceId%3Dd34413a3-be85-4b30-b7a7-c1a9e70c124b%26expirationTimestamp%3D1726207200000%26signature%3DKLz2bL19SPXmx2_eZ-UugbGRGX855UxXLgUiwjrXwOg?table=block&id=167f9785-8727-4235-8d50-4d2d069520eb&cache=v2)
프로젝트는 리액트 16 버전으로 구성되어 있으며, 클래스형 컴포넌트였다.
나는 리액트 18 시절에 입문했다보니 리액트 클래스 컴포넌트에 대해서는 잘 몰랐는데, 특히나 클래스 컴포넌트의 타입이 어떻게 되는지에 대해서는 전혀 일면식이 없었다.
조금 찾아볼라고 검색을 해봤는데, 은근히 클래스형 컴포넌트의 타입에 대해서는 찾기가 아주 조금 어려웠다.
React.Component<Props, State>
와 같이 제네릭으로 이 컴포넌트의 props와 state의 타입을 먹여주는 방식이 좀 신선했다.props야 공식문서의 API 항목에 잘 설명이 되어 있었지만, state 같은 경우는 내부 구현이기 때문에 타입을 알기가 어려웠다. 어쩔 수 없다.
console.log
를 여기저기 찍어가며 이 state가 어떤 값인지 알아내면서 전진하자.![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2Fd34413a3-be85-4b30-b7a7-c1a9e70c124b%2F18663487-1152-44c2-93bf-3b4afd7158ab%2FUntitled.png%3Ftable%3Dblock%26id%3D0724d92d-e6ca-49b3-ac76-393770624c09%26spaceId%3Dd34413a3-be85-4b30-b7a7-c1a9e70c124b%26expirationTimestamp%3D1726207200000%26signature%3DiWPm81QxBJg60LOATE5jyr47lyhQf1slxhGbp7zrc-E?table=block&id=0724d92d-e6ca-49b3-ac76-393770624c09&cache=v2)
어찌저찌 타입을 먹여가다 보니, 제작자가 의도한 state의 타입이 이런 것이었겠구나 생각해보는 재미가 은근히 있었다.
살짝 애먹었던 부분은 객체를 string 변수로 키 값을 얻어오는 부분이었는다.
const obj = {
title: 'hello',
message: 'world!',
author: 'myc',
}
function getByKey(key: string) {
return obj[key];
}
자바스크립트에서는 당연히 이런 코드가 잘 작동한다.
하지만 타입스크립트에서는 작동하지 않는다.
![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2Fd34413a3-be85-4b30-b7a7-c1a9e70c124b%2F7e48515c-c8f0-42f8-a33b-a66eaf5b116b%2FUntitled.png%3Ftable%3Dblock%26id%3Dcd2f064a-c03d-4c70-86bf-326e61c64c04%26spaceId%3Dd34413a3-be85-4b30-b7a7-c1a9e70c124b%26expirationTimestamp%3D1726207200000%26signature%3DagphM4pdXnNE5OSq30sbtQ2-2daEk3v7YeGip04vYdk?table=block&id=cd2f064a-c03d-4c70-86bf-326e61c64c04&cache=v2)
interface ReactCloseableTabsData {
[key: string]: any;
tab: string;
component: React.ReactNode;
id: string;
closeable: boolean;
}
그렇기 때문에 특별히 타입을 지정을 이렇게
[key: string]: any
와 같이 지정해주어야 했다.🪮 빌드하기
열심히 하다보니 타입스크립트로 옮기는 과정이 끝났다.
하지만 여기서도 문제가 있었으니..
nwb
가 타입스크립트를 지원을 기본적으로 하지 않는다.![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2Fd34413a3-be85-4b30-b7a7-c1a9e70c124b%2F59043049-8d8b-4e94-b4e7-384e1ac2caca%2FUntitled.png%3Ftable%3Dblock%26id%3D4bd39202-8c43-4506-8473-f67b34cd383c%26spaceId%3Dd34413a3-be85-4b30-b7a7-c1a9e70c124b%26expirationTimestamp%3D1726207200000%26signature%3DWRHHqRSMWDiZDeJIMjOhGcOuLmhxItxxx5xm5J42wMo?table=block&id=4bd39202-8c43-4506-8473-f67b34cd383c&cache=v2)
이런.. 하지만 그렇다고 포기할 수는 없다.
nwb의 공식 문서를 찾아보다보니 webpack의 설정을 수동으로 할 수 있는 부분이 있었고, 여기에
ts-loader
를 이용해서 타입스크립트 컴파일을 붙일 수 있겠다는 생각이 들었다.결론부터 얘기하자면.. 성공했다.
module.exports = {
type: 'react-component',
npm: {
esModules: true,
umd: {
global: 'ReactCloseableTabs',
externals: {
react: 'React'
}
}
},
webpack: {
extra: {
entry: {
index: './src/index.tsx',
demo: './demo/src/index.tsx',
},
output: {
filename: '[name].js',
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
},
module: {
rules: [{test: /\.tsx$/, loader: 'ts-loader'}],
},
},
},
}
ts-loader
를 설치하고 nwb.config.js
파일에 다음과 같이 코드를 추가해주니 빌드가 잘 작동했다.그 과정에서도 문제가 있었는데, entry를 그냥
src/index.tsx
로 지정하니 그 경로만 번들링을 해서 데모 페이지가 전혀 뜨지 않게 되는 문제가 있었고, entry를 demo/src/index.tsx
로 지정하니 데모 페이지만 번들링이 되는 문제가 있었다.조금 찾아보다보니 entry를 여러 개 할 수 있다는 정보를 보고 다음과 같이 구성하였고 결국 성공했다.
만세!
🛎️ 마무리
마지막으로 Pull Request를 올리고 마무리했다.
과연 이 PR이 한번에 받아들여질지, 아니면 뭔가 문제가 있어서 추가 수정을 해야 할 지는 모르겠지만..
일단 오픈소스 거의 첫 경험을 한 기분이라 기분이 좋다.
앞으로도 뭔가 기여를 해볼 경험이 많지 않을까? 하는 생각을 조금이나마 해본다.