VSCode Extension 개발해보기

Visual Studio Code Extension 개발 경험을 공유하며, 텍스트 변환 기능을 중심으로 확장 가능한 코드 구조와 다양한 활용 사례를 소개합니다.

Contents


배경

2023년에는 Webstorm 에서 Visual Studio Code 로 ~~IDE~~ Text Editor 를 변경하였다. Webstorm 은 확실히 강력한 개발 도구이다. 특히 Webstorm 의 Test 관련된 기능들을 매우 유용하게 사용했는데, 프로젝트의 크기가 커지다 보면 IDE 의 속도가 급격하게 느려지는 현상이 있어, 비교적 경량의 Text Editor 인 Visual Studio Code 를 사용중이다. Sublime Text, NeoVim 등의 Text Editor 도 검토해 보았지만, 종합적으로 Visual Studio Code 가 가장 위험이 적은 선택이라고 판단했다. 그러다 문득 Visual Studio Code Extension 에 관심이 생겨, 주말에 간단히 시간을 내서 개발해보게 되었다.



기능 설계

Extension 의 기능은 단순하고 확장 가능해야 한다 를 원칙으로 삼고 기능 설계를 시작했다. 업무에서 자주 사용하는 text util 사이트를 대체하는 Extension 을 만드는 것을 목표로 삼았다. 평소에 Base64 Guru 사이트를 즐겨 사용하는데, 매번 브라우저를 통해 Text Convert 기능을 사용하는 데에 불편함을 느끼고 있었다.

투박하지만, 매우 유용하게 사용하고 있는 Base64 Guru

1차 구현 목표로 Base64 Encode, Decode 기능을 구현하고, 적절한 디자인 패턴을 사용하여 많은 Text Convert 기능으로 확장해 나가는 것을 목표로 잡았다.



코드 구조

Visual Studio Code - Your First Extension Visual Studio Code 공식문서에, 생각보다 Extension 을 만드는 진입장벽이 낮다는 사실에 놀랐다. Sublime Text 로 간단한 Build Script 를 개발했을 때에는, Text Editor API 문서화도 제대로 되어있지 않고 Reddit 등의 커뮤니티를 통해 정보를 얻어 개발했던 기억이 있는데, Visual Studio Code 는 확실히 상대적으로 친절했다.

Extension 의 기능을 단순하게 표현하면 다음과 같다.

  1. 유저는 변경하고자 하는 텍스트를 블록으로 선택한다. 만약 블록이 선택되어있지 않는다면, 현재 열린 에디터의 모든 텍스트를 선택한다.
  2. 유저는 어떠한 Text Converter 를 사용할지 선택한다. (ex. Base64, Hex, Convert JSON5 To Typescript Interface 등)
  3. Extension 은 유저가 선택한 Text Converter 를 사용하여, 유저가 Editor 에서 선택한 문자들을 변환한다. 유지보수를 용이하게 하기 위해, Extension 의 구조도 최대한 간단하고 확장 가능하도록 설계하였다. 다양한 Text Converter 를 추가할 수 있게 하기 위해, Strategy Pattern 을 활용하여 기능을 구현하였다.


다음은 Extension 의 메인 로직이다.

/**
 * A converter that converts text
 */
export interface Converter {
  /**
   * Determines if the converter should handle the command
   * @param command - The command to handle
   */
  shouldHandle(command: string): boolean;
  /**
   * Converts the text
   * @param text - The text to convert
   */
  convert(text: string): Promise<string>;
  /**
   * Called when an error occurs
   * @param error - The error that occurred
   */
  onError?(error: Error): void;
}

export class ConvertService {
  constructor(private readonly _convertors: Converter[]) {}

  convert(convertType: string, text: string) {
    const converter =
      this._convertors.find((it) => it.shouldHandle(convertType)) ??
      die(new Error("Converter not found"));

    try {
      return converter.convert(text);
    } catch (e) {
      const onError = converter.onError ?? this.defaultOnError;
      onError(e as Error);

      return text;
    }
  }

  /**
   * Default error handler
   *
   * @remarks This method is used when the converter does not have an error handler
   * @param error - Error
   */
  private defaultOnError(error: Error) {
    vscode.window.showErrorMessage(DEFAULT_ERROR_MESSAGE, {
      modal: true,
      detail: `${error.message}



${error.stack}`,
    });
  }
}

export const convertService = new ConvertService(
  COMMAND_HANDLERS.map((it) => it.converter)
);

위의 ConvertService 에 지원하는 Text Converter 들의 배열을 주입하면, 최소의 코드로 Extension 의 기능을 확장할 수 있게끔 로직을 구성하였다. 취미 개발로 유지보수될 코드이기 때문에, 코드의 가독성과 프로젝트 구조의 직관성을 가장 중요한 코드퀄리티의 요소로 선택하여 작업하였다. 현재는, 하나의 설정 파일 에 Text Converter 를 추가하기만 하면 Extension 의 기능을 확장할 수 있게 Extension 을 운영중이다.

취미 개발로 유지보수될 코드이기 때문에, 코드의 가독성과 프로젝트 구조의 직관성을 가장 중요한 코드퀄리티의 요소로 선택하여 작업하였다. 현재는, 하나의 설정 파일 에 Text Converter 를 추가하기만 하면 Extension 의 기능을 확장할 수 있게 Extension 을 운영중이다.



확장 가능성

Extension 의 시작은 base64, hex 등의 인코딩, 디코딩을 목적으로 한 단순한 Extension 이지만, 현재는 다음의 기능을 사용할 수 있다.

아래 Repository 를 통해 간단히 기능을 구현하였고, VSCode Marketplace 에도 Publish 해 보았다. 회사 팀원들, 여자친구가 업무에 잘 활용해 주고 계신데, 몇 개월 후에는 더 많은 사람들이 사용해 주었으면 좋겠다.



참고자료

Github Repository Visual Studio Code Marketplace Visual Studio Code - Your First Extension Base64.guru transform.tools


이것도 읽어보세요