TypeScript로 개발한 라이브러리를 npm에 배포하기

node 설치가 필요합니다. 그리고 라이브러리를 위한 개발 폴더를 위해 다음과 같은 명령 수행이 필요합니다.

mkdir -p ~/dev/my-libary
cd ~/dev/my-libary
npm init -y
npm install -D typescript tsup
mkdir src

src/index.ts에 다음 코드를 입력합니다.

export function add(left: number, right: number): number {
  return left + right;
}

index.ts는 진입점이 되므로 사용자에게 공개할 것들을 export하는 것이 주목적입니다. 실제 API에 대한 코드는 별도의 코드 파일을 통해 제공하는 것이 일반적입니다.

tsconfig.json 파일을 생성해 다음처럼 입력합니다.

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "noEmit": true,
    "verbatimModuleSyntax": true
  },
  "include": ["src"]
}

package.json 파일의 내용을 아래처럼 구성합니다.

{
  "name": "@YOUR_NPM_ID/my-libary",
  "version": "1.0.1",
  "description": "타입스크립트로 만든 테스트 라이브러리",
  "type": "module",
  "main": "./dist/index.cjs",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.cjs",
      "default": "./dist/index.js"
    }
  },
  "files": [
    "dist"
  ],
  "scripts": {
    "build:js": "tsup src/index.ts --format esm,cjs --clean",
    "build:types": "tsc -p tsconfig.build.json",
    "build": "npm run build:js && npm run build:types",
    "check": "tsc --noEmit",
    "prepack": "npm run check && npm run build"
  },
  "keywords": [
    "typescript",
    "library"
  ],
  "license": "MIT",
  "devDependencies": {
    "tsup": "^8.5.1",
    "typescript": "^7.0.2"
  }
}

타입스크립트 코드의 타입 오류를 검사하고 빌드 하기 위해 다음 명령을 수행합니다.

npm run check
npm run build

dist 폴더가 생성되고 이 폴더 안에 index.js, index.cjs, index.d.ts 가 생성되어야 합니다.

실제 npm에 배포하기 위한 파일 생성을 시험해 보기 위해 다음 명령을 수행합니다.

npm pack --dry-run

출력되는 내용에서 dist/index.js, dist/index.cjs, dist/index.d.ts, package.json이 tar로 묶이는지 확인하면 됩니다. 만약 README.MD와 LICENSE 파일을 package.json 파일이 위치한 같은 폴더에 생성해 놨다면 이 2개의 파일들도 tar로 묶여야 합니다. tar로 묶인 결과를 tarball이라고 합니다.

실제 tarbal를 생성하기 위해 다음 명령을 수행합니다.

npm pack

그럼 your-npm-id-my-libary-1.0.0.tgz 파일이 생성됩니다.

your-npm-id-my-libary-1.0.0.tgz 파일을 npm에 배포하기에 앞서 이 파일로 라이브러리가 제대로 설치 되는지 확인하기 위해 새로운 프로젝트 풀더를 다음 절차를 참고하여 생성합니다.

mkdir -p ~/dev/my-libary-consumer
cd ~/dev/my-libary-consumer
npm init -y
npm install ../your-npm-id-my-libary-1.0.0.tgz

index.js 파일을 생성하고 다음 코드를 입력합니다.

const { add } = require("@YOUR_NPM_ID/my-libary");

console.log(add(10, 20));

다음 명령으로 코드가 정상적으로 실행되는지 확인합니다.

node index.js

이제 npm 사이트에 배포하기 위해 npm에 로그인합니다. 라이브러리 프로젝트에서 다음 명령을 실행합니다.

cd ~/dev/my-libary
npm login
npm whoami

자신의 npm ID가 출력되어야 합니다. 배포하기에 앞서 my-libary가 등록된적이 있는지 확인하기 위해 다음 명령을 실행합니다.

npm view @YOUR_NPM_ID/my-libary version

등록한 적이 없으므로 404 오류가 나와야 합니다.

실제 배포하기에 앞서 다시 한번 더 검증하고 배포합니다.

npm run check
npm pack --dry-run
npm publish --access public

이제 라이브러리를 테스트하기 위한 폴더에서 다음처럼 npm 사이트로부터 라이브러리를 설치해 테스트해 봅니다.

cd ~/dev/my-math-kit-consumer
npm uninstall @YOUR_NPM_ID/my-libary
npm install @YOUR_NPM_ID/my-libary
node index.js

라이브러리가 변경되면 버전업을 하고 다시 npm에 재배포해야 합니다. 다음 절차를 따릅니다.

cd ~/dev/my-math-kit
npm version patch
npm publish

위의 경우 버전업 레벨이 patch입니다. 다음을 참고하여 버전업 합니다.

  • patch: 버그 수정 (1.0.0 → 1.0.1)
  • minor: 하위 호환되는 기능 추가 (1.0.0 → 1.1.0)
  • major: 호환성을 깨는 변경 (1.0.0 → 2.0.0)

이제 라이브러리를 테스트하기 위한 폴더에서 다음처럼 npm 사이트로부터 라이브러리를 업데이트해 테스트해 봅니다.

cd ~/dev/my-libary-consumer
npm update @YOUR_NPM_ID/my-libary
npm list @YOUR_NPM_ID/my-libary

라이브러리가 변경되면 이를 tarball로 묶고 테스트하는 프로젝트에서 tarball을 설치(npm을 통해서든, 파일을 통해서든)하는 것은 매우 번거롭습니다. 이 경우 npm link를 통해 쉽게 해결할 수 있습니다.

라이브러리 폴더에서 다음 명령을 수행합니다.

cd ~/dev/my-libary
npm run build
npm link

그리고 테스트 폴더에서는 다음 명령을 입력합니다.

cd ~/dev/my-libary-consumer
npm link @YOUR_NPM_ID/my-libary

라이브러리를 link 방식이 아닌 tarball로 설치하고 싶다면 링크를 해제해야 합니다. 방법은 아래와 같습니다.

cd ~/dev/my-libary-consumer
npm unlink @YOUR_NPM_ID/my-libary

jar 정리

  • sqljdbc4.jar : 마이크로소프트 SQL 서버(Microsoft SQL Server) 데이터베이스에 연결하기 위해 사용하는 JDBC(Java Database Connectivity) 드라이버
  • commons-io-2.4.jar : 파일과 디렉터리 조작, 입출력(I/O) 작업을 쉽게 하도록 돕는 Apache Commons IO 라이브러리
  • ellip2ellipsoid.jar : 타원체 간 변환 라이브러리
  • EPSG.jar : EPSG 좌표계 라이브러리
  • icu4j_3_2.jar : IBM이 개발하고 Unicode 협회가 관리하는 ICU4J(International Components for Unicode for Java) 라이브러리 (ArcSDE 에서 사용되는 것으로 판단됨)
  • javaproj-1.0.6-noawt.jar : 좌표계 변환 및 지도 투영 라이브러리
  • jpe_sdk.jar : ESRI 사의 ArcSDE 시스템에서 제공하는 자바 프로젝션 엔진(Projection Engine) API 클래스 파일들을 담고 있는 라이브러리
  • jsde_sdk.jar : ESRI 사의 ArcSDE 데이터베이스와 통신하기 위한 자바 클라이언트 및 지오메트리(Geometry) API 라이브러리
  • jsde_sdkres.jar : ESRI사의 ArcSDE Java SDK에서 사용하는 리소스(Resource) 라이브러리
  • jts-1.12.jar : 2차원 공간 기하학 정보를 다루는 라이브러리인 (JTS Topology Suite(Java Topology Suite)
  • postgis-1.5.0.jar : PostGIS 데이터베이스의 공간 정보(GIS) 데이터를 다루기 위해 사용하는 JDBC 드라이버 및 지오메트리(Geometry) 라이브러리
  • postgresql-42.2.2.jar : PostgreSQL 데이터베이스에 연결할 수 있도록 돕는 JDBC(Java Database Connectivity) 드라이버
  • jetty-all-9.4.57.v20241219-uber.jar : 이클립스 젯티(Eclipse Jetty) 9.4 버전대의 마지막 공개 릴리스 중 하나인 통합(Aggregate) 라이브러리
  • trove-2.1.0.jar : 고성능 컬렉션 라이브러리인 GNU Trove의 2.1.0 버전 라이브러리
  • shpwriter.jar : 자바(Java) 환경에서 GIS 데이터 포맷인 Shapefile(*.shp)을 생성하고 내보내기(Export) 위해 사용하는 라이브러리
  • kmlwriter.jar 자바(Java) 환경에서 KML을 생성하고 내보내기(Export) 위해 사용하는 라이브러리
  • json-simple-1.1.1.jar : 자바(Java)에서 JSON 데이터를 쉽게 읽고, 쓰고, 다룰 수 있게 해주는 경량 JSON.simple 라이브러리
  • ojdbc6.jar : 오라클 데이터베이스(Oracle Database)를 연결해 주는 오라클 JDBC 드라이버
  • sdoutil.jar : 오라클 데이터베이스(Oracle Database)의 공간 데이터(Spatial Data) 처리를 지원하는 자바 라이브러리
  • sdoapi.jar : 오라클 데이터베이스(Oracle Database)에서 공간 데이터(Spatial Data 및 기하학적 데이터)를 자바(Java) 애플리케이션과 연동하여 처리할 때 사용하는 라이브러리
  • mybatis-3.5.1.jar : 데이터베이스와 쉽게 연결하고 SQL을 다룰 수 있게 해주는 MyBatis 프레임워크 라이브러리
  • tibero6-jdbc.jar : 티베로 6(Tibero 6) 데이터베이스에 접속하여 쿼리를 실행할 수 있게 해주는 JDBC 드라이버
  • quartz-2.3.0-SNAPSHOT.jar : 주기적인 작업(Job) 스케줄링을 구현할 수 있게 해주는 오픈소스 라이브러리인 Quartz Scheduler의 2.3.0 개발 중(Snapshot) 버전 라이브러리
  • slf4j-api-1.7.7.jar : 다양한 로깅 프레임워크를 연결해 주는 추상화(Facade) 라이브러리인 SLF4J(Simple Logging Facade for Java) 라이브러리
  • javax.activation-1.2.0.jar : MIME 데이터(이메일 첨부파일, 이미지 등)를 관리하고 처리할 수 있게 돕는 JavaBeans Activation Framework(JAF) 라이브러리
  • javax.mail.jar : 이메일을 송수신할 수 있도록 지원하는 JavaMail API 라이브러리
  • http-client-4.5.13-all.jar : HTTP 통신을 수행하기 위해 사용하는 Apache HttpComponents HttpClient 라이브러리
  • string-similarity.jar : 두 문자열이 얼마나 비슷한지(유사도)를 계산할 수 있도록 도와주는 라이브러리

GeoService-Xr 설정값 정리

  • SqlFile : 서버 단에서 사용되는 SQL 문에 대한 함수화가 저장된 파일(json)
  • EncryptionFile : 웹앱 단위로 사용되는 암호화 키값이 저장된 파일(json)
  • WebHomeDir : 웹서버를 통해 제공되는 정적 파일이 저장된 디렉토리
  • SessionCheckingFlags : 세션 여부에 따라 서비스 호출을 허용할 것인지를 결정하는 파일(json)
  • GeoDataFile : DB 연결정보 및 공간 데이터 설정 파일(xml)
  • Port : 서버가 사용하는 포트
  • MainMemoryBuffersCount : 주 메모리 풀에 저장된 단위 메모리 개수
  • MainMemoryBufferSize : 주 메모리에 저장된 단위 메모리 크기(단위: 바이트)
  • SubMemoryBuffersCount : 보조 메모리 풀에 저장된 단위 메모리 개수
  • SubMemoryBufferSize : 보조 메모리 풀에 저장된 단위 메모리 크기(단위 : 바이트)
  • MyBatisConfig : MyBatis(SQL 매핑 프레임워크) 설정 파일(xml)
  • ValidationCheck : DB 커넥션 객체의 유효성 검증 여부
  • ValidationCheckTime : DB 커넥션 객체의 유효성 검증 여부가 true일 경우 검증을 수행할 시간 (크론 표현식)
  • LogLevel : 서비스 이용 로그 레벨 수준(0: 로그 안남김 / 1: 성공/실패 여부만 남김 / 2 : 매우 자세히 남김)
  • PlugIn : 서버가 제공하는 기본 서비스 이외의 특화된 서비스 모듈 설정 파일(json)
  • DBRepositoryForGeocoding : 지오코딩 서비스를 제공할 때 서비스 최적화를 위한 DB 구성 여부
  • AllowCORS : 서버가 CORS를 허용할지 여부
  • SSL_p12 : SSL을 위한 .p12 파일 경로
  • SSL_pw : SSL을 위한 .pw 파일 경로
  • PG_SecretKey : PG에 대한 비밀키 문자열
  • AdminEmail : 메일 전송 서비스에서 사용하는 발신자 메일 주소(현재 구글 메일만 가능)
  • AdminEmailPW : 메일 전송 서비스에서 사용하는 발신자 메일의 암호

TypeScript, 익명 클래스

타입스크립트, 정확히는 JavaScript도 익명 클래스를 정의할 수 있습니다. 다음처럼요.

const x = new class<Type> {
  content: Type;
  constructor(value: Type) {
    this.content = value;
  }

  say() {
    console.log(this.content);
  }
}("Hello World!");

x.say();

TypeScript, this 라는 타입에 관하여

타입스크립트에서는 클래스에서 사용되는 this라는 타입이 있습니다. 이 this 타입은 동적으로 현재 클래스에 대한 타입으로 결정됩니다. 애매하고 어렵죠? 예시를 통해 좀더 살펴보면..

class Box {
  content: string = "";
  sameAs(other: this) {
    return other.content === this.content;
  }
}
 
class DerivedBox1 extends Box {
  otherContent: string = "?";
}

class DerivedBox2 extends Box {
  otherContent: string = "?";
}
 
const base = new Box();
const derived1 = new DerivedBox1();
derived1.sameAs(base);

const derived2 = new DerivedBox2();
derived2.sameAs(base);

위의 코드에서 Box 클래스의 sameAs 인자의 other 타입이 this입니다. 이 Box 클래스를 상속받는 파생클래스들을 통해 sameAs를 사용할 경우 sameAs의 첫번째 인자인 other는 각 파생클래스의 타입이 됩니다. 즉 derived1sameAs 매서드의 정의는 다음과 같고…

sameAs(other: DerivedBox1): boolean

derived2sameAs 매서드의 정의는 다음과 같습니다.

sameAs(other: DerivedBox2): boolean