본문 바로가기

vscode-with-tistory

vscode extension 개발일기3: 블로그 작성1

주의

본 글은 2021년 8월 19일날 작성된 개발일기로서 현재 버전에서 제공하는 기능이랑 상이할 수 있다.

블로그 선택

문제

티스토리에는 1개의 계정으로 여러개의 블로그를 관리하는 기능이 존재한다. 여러개의 블로그가 전재할 경우 어떤 블로그를 메인 페이지로 구성을 할까에 대한 고민을 하였다.

해결방안

사용자들이 자주 사용하는 블로그는 디폴트 블로그로 사용할테니 디폴트 블로그에 포스팅 하는 것으로 결정하였다.

코드

interface BlogInfo {
    name: string;
    url: string;
    secondaryUrl: string;
    nickname: string;
    title: string;
    description: string;
    default: "Y" | "N";
    blogIconUrl: string;
    faviconUrl: string;
    profileThumbnailImageUrl: string;
    profileImageUrl: string;
    role: string;
    blogId: number;
    statistics: {
        post: number;
        comment: number;
        trackback: number;
        guestbook: number;
        invitation: number;
    };
}

export const getBlogInfo = async (): Promise<BlogInfo| undefined> => {
    try {
        if (checkaccess_token()) {
            const {
                data: {
                    tistory: {
                        item: { blogs },
                        status,
                        error_message,
                    },
                },
            } = await axios.get(API_URI.BlogInfo, {
                data: {
                    access_token,
                    output: "json",
                },
            });
            if (status === 200 && error_message === undefined) {
                for (let item of blogs) {
                    if (item.default === "Y") {
                        vscode.window.showInformationMessage(
                            `Hello ${item.nickname}`
                        );
                        return item;
                    }
                }
            } else {
                throw new Error(error_message);
            }
        }else{
            throw new Error("Not Exist Token");
        }
    } catch (error) {
        vscode.window.showErrorMessage("Request Failed");
    }
};
  • status가 정확하거나 error_message가 존재하지 않을 경우 블로그 정보를 받아왔다고 생각하여 블로그 정보를 탐색하며 기본 블로그를 탐색
  • 이때 블로그 정보를 인터페이스로 작성하여 코딩하기 편리하도록 함

블로그 정보

아래는 내용을 적용한 깃의 커밋로그

get blog && modify Access_Token · dev-green-flamingo/vscode-with-tistory@0fd6e6c


Token저장

설정 페이지를 통해 OAuth2.0을 위한 정보를 입력받음으로서 ClientID를 직접 만들지 않고 사용자가 직접 발급받아서 사용하도록 유도한다. 또한 토큰도 설정으로 저장시켜서 vsocde가 종료되더라도 값을 유지한다. 프로젝트 내부에 파일을 만들어 이를 저장할까 생각하였으나 해당 과정을 수행하면 사용자가 프로젝트를 삭제하면 저장된 정보가 모두 제거되고 사용자가 직접 사용하지 않지만 파일로 남아있엇 불편함을 느낄 것 같아서 파일로 저장하지 않았다.

아래는 해당 과정을 수행하기 위한 삽질한 기록이다.

Workspace API

변수

  • vscode.workspace.fs

    • 로컬 혹은 외부의 파일과 상호작용하는 파일시스템.
    • 반환값: FileSystem

    VS Code API

  • vscode.workspace.isTrusted

    • 현재 workspace의 내용물의 접근을 신뢰한 여부. 사용자가 직접 신뢰를 하였으면 true값을 반환한다.
    • 반환값: boolean
  • vscode.workspace.name

    • workspace 이름
    • 반환값: string | undefined
  • vscode.workspace.notebookDocument

    • VSC의 모든 notebook document를 반환
    • 반환값: readonly Notbook Document[]

    VS Code API

  • vscode.workspace.rootPath

    • 에디터로 연 폴더 이름
    • 현재 workspaceFolders로 대체되었다.

Configuration

package.json에 contributes속성 내에 configuration을 추가하여 설정 UI를 만들어서 사용자로부터 입력을 받아 세밀한 사항을 설정할 수 있다는는 것을 찾았다. 설정 UI를 통해 지정된 값들은 setting.json에 저장된다. configuration에 값을 지정하기 위한 속성은 다음과 같다. 모든 사항을 작성한 것은 아니니 참고만 하길 바란다.

title

설정 부분의 헤더를 담당하며 일반적으로 extension에 1개만 존재한다. 단 title에 Extension, Configuration, Settings라는 단어는 사용할 수 없다.

{
	"configuration": {
		"title": "vscode-with-tistory"
	 },
}

소제목

값이 정해져있지 않고 패턴만 만족하면 된다. 패턴은 title.subtitle1.subtitle2 방식으로 작성된다.

"contributes": {
		"commands": [
			{
				"command": "vscode-with-tistory.login",
				"title": "Login Tistory"
			},
			{
				"command": "vscode-with-tistory.get-blog",
				"title": "Get BlogInfo"
			}
		],
		"configuration": {
			"title": "Vscode-with-tistory",
			"properties": {
				"Vscode-with-tistory.token":{
					"description":"티스토리로 부터 받아온 액세스 토큰을 저장"
				}
			}
		}
	}

Untitled

description속성을 추가하여 소주제의 상세사항을 작성할 수 있다. 만약 마크다운 문법으로 작성하고 싶으면 description대신 markdownDescription속성을 사용하면 된다.

type

해당 속성이 가질 타입을 정의한다. 타입의 종류는 number, string, boolean으로 존재하며 만약 boolean타입일 경우 체크박스 형태로 UI에 나타난다.

"configuration": {
		"title": "vscode-with-tistory",
		"properties": {
		"vscode-with-tistory.OAuth2.RedirectURI":{
			"type":"string",
			"default":"http://localhost:5500/",
			"description":"티스토리 토큰 발급을 위한 Redirect URI"
		}
	}
}

scope

vscode에서 사용하는 extension, extension을 사용하기 위한 설정값들을 저장해서 사용할 수 있는 범위(scope)를 지정한다. 크게 범위를 나누자면 User 설정, Workspace 설정, Remote 설정이 있다. 각 설정은 다음과 같다.

  1. workspace 속성: 특정 workspace내에서만 적용되는 설정한다. 해당 설정을 사용하면 workspace내에 ${workspaceFolder}/.vscode/settings.json 파일이 생성되고 해당 파일 안에 설정값이 저장된다.

  2. User 속성: 어떤 workspace를 열던 간에 전역으로 결정되는 설정

  3. Remote 속성: 외부 서버에 VSCode관련된 설정, extension관련 정보, 소스코드등 개발에 필요한 정보를 저장해놓고 이를 로컬에서 SSH로 접근해서 사용하는 설정. 아래는 Remote Development 를 SSH를 이용하여 사용하는 방법을 설명하는 공식문서

    Developing on Remote Machines using SSH and Visual Studio Code

Remote속성 같은 경우 여러 개발자들이 모여 VSCode로 작업을 수행할 때 설정값, extension, 실행환경을 맞출 수 있어서 좋아보인다. 추후에 써보고싶다. VSCode extension에서 설정을 가져올 때 다음과 같은 속성을 가져온다.

  • application: VSCode전체에 적용되는 User 설정만 사용한다.
  • machine: Vscode를 사용하는 로컬, 원격 설정만 적용 가능하다.
  • machine-overridable: workspace혹은 폴더에 작성된 설정에 의해 로컬, 원격 설정을 덮어 씌워서 사용가능하다.
  • window(default): scope를 명시하지 않았을 때의 디폴트 설정다. 해당 scope는 user, workspace, remote settings중 설정을 모두 사용가능하다.
  • resource: 파일이나 폴더마다 설정을 지정할 수 있다.
  • language-overridable: 사용하는 프로그래밍 언어, 언어 버전에 따라 속성울 재적용할 수 있다.

Contribution Points


Configuration속성값 가져오기

사용자가 값을 설정했으면 이를 가져와서 extension에서 사용하기 위해서는 vscode.workspace.getConfiguration을 통해 WorkspaceConfiguration을 가져온다. 자세히는 설명하진 않고 간단히만 설명하겠다.

WorkspaceConfiguration

해당 객체는 다음과 같은 함수를 통해 setting.json에 저장된 값을 가져오거나 값을 수정할 수 있다.

  • get<T>(section: string): T | undefined
    • section속성을 가진 값을 반환해준다. 만약 값이 존재한다면 이를 반환하고 존재하지 않으면 undefined를 반환한다.
  • get<T>(section: string, defaultValue: T): T
    • section속성을 가진 값을 반환해준다. 만약 값이 존재한다면 이를 반환하고 존재하지 않으면 defaultValue를 반환해준다.
  • has(section: string): boolean
    • section속성의 존재 여부를 확인해준다.
  • update(section: string, value: any, configurationTarget?: ConfigurationTarget | boolean | null, overrideInLanguage?: boolean): Thenable<void>
    • section속성의 값을 value로 변경한다. 만약 여러 개의 속성이 존재하면 configurationTarget으로 설정을 지정하고 overrideInLanguage속성으로
    • configurationTarget: 어떤 설정에서 값을 찾아서 수정할지를 지정한다. 타입별로 다음과 같은 의미를 가진다.
      • true: global settings(User 설정)
      • false: workspace 설정
      • null 혹은 undefined: workspace folder 설정
      • ConfigurationTarget: 찾을 설정을 직접 지정한다. enum객체로 작성되어 있다.
    • overrideInLanguage: 요청된 languageID범위 내의 값을 갱신할지 결정한다. 만약 true이면 languageId에 속한 값을 갱신하고 undefined이면 languageId설정이 정의되어 있을때만 값을 갱신한다.

extension에서 속성에 접근할 때 getConfiguration을 통해 설정을 가져오고 get함수로 속성 이름을 통해 값을 가져온다. 또한 update함수로 프로퍼티와 값을 함께 주면 설정을 수정할 수 있다.

const configuration = vscode.workspace.getConfiguration(
        "vscode-with-tistory"
    );
const value: string | undefined = configuration.get(property);
configuration.update(property, 'update value');

위에는 기능을 구현하기 위해 정보를 검색한 뻘짓이며 지금부터 작성될 내용을 얻은 정보를 바탕으로 코딩을 수행한 내용이다.

dotenv모듈 겉어내기

기존엔 dotenv모듈을 이용하여 코딩을 수행하였다. BSD라이센스를 사용하는 모듈이라 개발 완료 후 배포할 때 라이센스 내에 저작권자를 명시하여서 배포하면 사용할 수 있다. 그러나 dotenv를 제거하는 이유는 다음과 같다.

  1. env파일을 같이 첨부해야 하는데 env파일을 같이 첨부하는 것이 좋지 않다고 생각한다. VSC에서 Configuration을 이용하여 setting.json에 값을 저장하여 사용하는 기능이 존재함에도 불과하고 env를 사용하는 것은 VSC를 제대로 이용하는 것이 아니다.

  2. client secret같은 중요정보가 string으로 하드코딩 되어서 배포하는 것은 추후에 티스토리 측에서 키관리에 대해 문제를 삼아 등록이 취소되는 안좋은 결과가 나올 것이라고 생각하였다.

  3. vscode에서 비슷한 기능인 Configuration기능을 제공해주는데 굳이 nodejs의 dotenv를 사용할 필요가 없다

위의 이유로 dotenv모듈을 제거하고 VSC의 Configuration기능을 제대로 이용하기로 하였다.

티스토리 등록 절차가 복잡하지 않기 때문에 사용자가 정보를 받아와서 등록하는 것이 위에서 생각한 문제들을 해결할 수 있을 것이라고 생각하여 dotenv를 겉어내고 설정에 인증에 필요한 정보를 입력하는 방향으로 바꾸었다.

이를 위해 package.json을 다음과 같이 수정하였다.

"configuration": {
			"title": "vscode-with-tistory",
			"properties": {
				"vscode-with-tistory.token":{
					"type":"string",
					"default":"",
					"description":"티스토리로 부터 받아온 액세스 토큰을 저장"
				},
				"vscode-with-tistory.OAuth2.ClientID":{
					"type":"string",
					"default":"",
					"description":"티스토리 토큰 발급을 위한 ClientID"
				},
				"vscode-with-tistory.OAuth2.ClientSecret":{
					"type":"string",
					"default":"",
					"description":"티스토리 토큰 발급을 위한 ClientSecret"
				},
				"vscode-with-tistory.OAuth2.RedirectURI":{
					"type":"string",
					"default":"http://localhost:5500/",
					"description":"티스토리 토큰 발급을 위한 Redirect URI"
				}
			}
		}

이를 위해 코드도 수정하였다. 수정내용이 많아 첨부하진 않겠다. 테스트를 수행해보면 다음과 같은 화면이 나온다.

Untitled

인증에 필요한 값을 작성하지 않으면 속성을 확인해달라는 요청 메세지를 준다. 아래 url은 해당 내용을 적용한 커밋 로그

delete dotenv module · dev-green-flamingo/vscode-with-tistory@4ae1040

client 분리

기존엔 client객체를 생성하여 extension.ts에게 전달해주고 extension.ts에서 서버를 동작, 종료시키는 코드를 만들어 사용하였다. 그러나 이러한 코드는 통일성을 주지 않는다고 생각하였다. 그래서 해당 부분을 분리하여 Client.ts에 넣었다.

import * as http from "http";
import { URL } from "url";
import { createAccessToken } from "./apis";

const client = http.createServer(
    (req: http.IncomingMessage, res: http.ServerResponse) => {
        const { url } = req;
        const params = new URL(`http://localhost:5500${url}`).searchParams;
        const [code, error] = [
            params.get("code"),
            params.get("error"),
        ];
        if (code !== null) {
            createAccessToken(code);
            res.writeHead(200);
            res.write(`close WebPage`);
        } else {
            res.writeHead(200);
            res.write(`Tistory Error: ${error}`);
        }
        res.end();
    }
);

export const runClient = () => {
    if (!client.listening) {
        client.listen(5500, () => {
            console.log("Start Client");
        });
    }
};
export const stopClient = () => {
    if (client.listening) {
        client.close(() => console.log("Stop Client"));
    }
};

Seperate Client · dev-green-flamingo/vscode-with-tistory@47739c2