1 - Frontend

SpaceONE Development Guides

1.1 - Software Architecture

Software Architecture

Frontend S/W Architecture

1.1.1 - Frontend Error Handling

How To Handle Error

Error 구분에 따른 처리 (API)


다양한 API Error를 처리하기 위해 미리 에러 타입을 정의한다.

  • NotFoundError : 리소스를 찾을 수 없을 때 (status: 404)

  • BadRequestError : 잘못된 요청을 보낼 때 (status: 400)

  • AuthenticationError: 인증이 실패했을 때 (status: 401)

  • AuthorizationError: 권한 검증에 실패했을 때 (status: 403)

  • APIError: 이외의 모든 API Error 및 서버 에러 (status: 500 등)

이 에러들을 axios api client의 interceptor에서 받아 핸들링한다. 위에 명시된 에러 이외에 다른 에러 코드를 정의할 수도 있다.


이 에러들을 SpaceConnector(axios api client)의 interceptor에서 받아 핸들링한다.

axios의 interceptor에서 error가 왔을 때, 각 statusCode를 보고 판별하여 위에 미리 정의해둔 Error로 throw해준다.

Error Handler (Client)


Client 쪽에서 API client를 통해 넘어온 Error의 유형을 판단하여 다음과 같이 핸들링한다.

AuthenticationError의 경우

Token이 유효한 지 확인하고, 유효하지 않을 경우 Authentication Error Handler를 호출하여 에러를 처리한다.

Authentication Error Handler는 다음과 같은 동작을 한다.

  • Session 만료 모달을 보여줌

  • User의 Session을 만료시킴

AuthorizationError의 경우

Authorization Error Handler를 호출한다. 이 핸들러는 다음과 같은 동작을 한다.

상단에 권한이 없다는 경고창을 노출시킴

NoResourceError의 경우

No Resource라는 토스트를 보여주고, redirect url이 있을 시 해당 url로 이동시킨다.

APIError의 경우

콘솔창에 API Error라고 명시된 로그를 남긴다.

BadRequestError의 경우

페이지에서 넘겨준 Error Message가 적힌 toast 경고창을 띄운다.


Error를 추가하는 방법


  1. SpaceConnector(console-core-lib > space-connector/error.ts)에 새로운 에러를 정의한다.

  2. SpaceConnector의 Error Interceptor(console-core-lib > space-connector/api.ts)에 새로운 에러를 throw해주는 부분을 추가해준다.

  3. API의 error가 아니라 Client 자체에서 필요한 에러의 경우 Client(console > common/error.ts)에 새로운 에러를 정의하고, 에러가 발생할 시 해당 에러를 throw한다.

  4. Error Handler(console > common/composables/error/errorHandler.ts)에 새롭게 추가된 Error를 핸들링하는 코드를 추가한다.

Error를 사용하는 방법


에러 클래스를 작성하고, 그 에러 클래스를 이용해 만든 에러 핸들러를 실제 프로젝트에서는 아래와 같이 사용한다.


  • list 등 일반적인 작업
try {
    특정 동작
} catch (e) {
    ErrorHandler.handleError(e);
}
  • create/update 등 실패 시 toast message를 보여주어야 하는 작업
try {
    특정 동작
} catch (e) {
    ErrorHandler.handleRequestError(e, 번역을 위한 i18n key값 or string);
}

1.1.2 - Frontend Authentication

How To Authenticate SpaceONE User

인증 플로우


SpaceONE은 현재 도메인 설정에 따라 Google Oauth2, Keycloak, KB SSO 세 가지의 인증 중 하나를 선택하여 제공하고, 인증 플로우는 다음과 같습니다.
![](/docs/developers/frontend/software_design/authentication/authentication_img/authentication_uml.png)
1. 우선 해당 도메인에서 어떤 Sign-in 방식(ID/PW인지, 외부 인증을 제공하는 지, 제공한다면 어떤 인증방식인지)을 사용하는 지 체크합니다. 2. 그 후, 각각 다른 Sign-in 방식에 맞는 UI를 보여주고, 해당 템플릿에서 Authenticator를 상속받은 커스텀 인증 모듈의 메소드를 호출합니다. 3. 각각의 커스텀 Auth들은 각자 필요한 Sign-in 절차를 수행한 후, 상속받은 Authenticator의 기본 Sign-in 로직을 수행합니다.

이후 이루어지는 인증 플로우는 다음과 같습니다.

Authenticator


Authenticator는 다음과 같은 구조를 가집니다.


최상위 Authenticator는 아주 기본적인 signIn과 signOut 메서드만 가지는 추상 클래스입니다.

abstract class Authenticator {
	static async signIn(필요한 매개변수): Promise<void> {
    	// sign in 로직
    }

    static async signOut(): Promise<void> {
    	// sign out 로직
    }
}

export {
	Authenticator,
}

signIn을 위해 백엔드 인증 서버에 보낼 credentials라는 데이터가 필수적이고, userId와 userType(일반 User인지, 관리자인지, API만 사용하는 API user인지)을 부수적으로 받습니다. signOut에서는 vuex에 작성해놓은 signOut 메서드를 호출합니다.


## 커스텀 인증 구조
이제 기본적인 추상 클래스 작성이 끝났으니, 이 클래스를 상속받아 각 SSO에 맞는 Auth 클래스들을 작성합니다. 위의 구조도를 보면 알 수 있듯이, Auth를 구현하는 데에 필요한 것은 두 가지가 있습니다. 바로 폼 렌더링(SSO 버튼 등)을 위한 템플릿과, 메서드들을 구현한 module(.ts) 파일입니다.

커스텀 인증 클래스


각 커스텀 인증에 맞는 Sign In, Sign Out 로직과 추상 클래스 Authenticator의 인증(기본 인증)을 수행하는 로직이 필요합니다. Authenticator의 Sign In은 credentials라는 data를 필요로 하고, 이 credentials 안에 들어가는 데이터는 모든 커스텀 인증마다 상이합니다.
// custom-auth.ts

class CustomAuth extends Authenticator {
    const signIn = async () => {
        // 각 SSO, 커스텀 인증에 필요한 Sign in 로직
        const credentials = { // 각 커스텀 인증에서 생성된 credentials }
        super.signIn(credentials);
    }

    const signOut = async () => {
        // 각 SSO, 커스텀 인증에 필요한 Sign Out 로직
        super.signOut();
    }
}

결론적으로, 커스텀 Auth class 내부에 상속받은 Authenticator(=super)의 함수들을 호출하여 SpaceONE의 인증을 동시에 수행할 수 있도록 합니다.

커스텀 인증 클래스 loader


이제 어떤 인증 클래스를 부를 건지 결정하는 loader를 간단하게 만들어보도록 합니다.
export const loadAuth = (authType?) => {
    if (authType === 'CUSTOM_AUTH') return CustomAuth;
    return SpaceAuth;
};

SignIn은 개별 템플릿을 가지지만, SignOut은 어디에서든 호출될 수 있기 때문에 위와 같은 loader를 사용하여 현재 도메인의 인증 타입을 넣으면 알맞은 인증 로직을 수행할 수 있도록 작성합니다.

커스텀 인증 템플릿


위의 작업을 하고난 후 마지막 작업은 폼 렌더링입니다.
//Custom Auth의 template(external/custom/template/CUSTOM_AUTH.vue)

<template>
	<div>Custom 로그인을 위한  버튼</div>
</template>

<script lang="ts">
 setup() {
 	//필요한 로직들
 	onMounted(async() => {
    		try {
            	await loadAuth('CUSTOM_AUTH').signIn(); //loader를 사용하여 customAuth 클래스의 signIn 함수 호출
            } catch (e) {
            	//에러 핸들링
            }
        }
    )
 }
</script>

각 커스텀 인증은 저마다의 폼 렌더링이 필요하기 때문에 위와 같이 해당 Auth에 맞는 커스텀 템플릿을 작성해줍니다.
그리고 마지막으로 커스텀 템플릿을 SignIn Page에서 보여줍니다. vue에는 라는 문법으로 다이나믹하게 컴포넌트를 불러올 수 있는 문법이 존재합니다.(https://kr.vuejs.org/v2/guide/components-dynamic-async.html) 해당 문법을 이용하여

//sign-in page
<component :is="component" class="sign-in-template"
	@sign-in="handleSignIn"
/>

이렇게 템플릿 부분에 작성해주고, 아래 스크립트 부분에서

//sign-in page
  const state = reactive({
  	...
	component: computed(() => {
                let component;
                const auth = state.authType;
                if (auth) {
                    try {
                        component = () => import(`./external/${auth}/template/${auth}.vue`);
                    } catch (e) {
                        //필요한 에러 핸들링.
                    }
                }
                return component;
            }),
  })

위와 같이 dynamic import 방식을 사용하여 컴포넌트를 렌더링합니다.

TL;DR

  1. 외부 인증 뿐만 아니라 자체 인증 또한 성공해야 하기 때문에 자체 인증만을 구현한 추상 클래스를 만듭니다.
  2. 해당 추상 클래스를 상속하여 구현한 커스텀 인증 클래스들을 만들어 해당 클래스 내부에서 커스텀 인증 로직을 처리하고, super class의 인증도 처리합니다.
  3. 해당 커스텀 클래스를 불러오기 위한 loader를 추가합니다.
  4. 커스텀 클래스의 폼을 렌더링하기 위한 template을 작성해줍니다.
  5. Sign In 페이지에서 다이나믹하게 해당 template을 불러서 렌더링합니다.

1.1.3 - Config Management

How To Manage Frontend Configuration

App에 필요한 구성 요소를 정의한다. 주로, 환경 별로 다른 값을 다룬다.

환경 별 우선순위

default.json이 우선적으로 적용된다.

development 환경일 경우, development.json의 값이 덮어 쓰여 적용된다.

환경 설정 방법

Dockerfile 수정

Dockerfile에서 npm run build 커맨드 전 NODE_ENV로 환경 설정 가능

...
ENV NODE_ENV development
...

Webstorm Configurations 수정

Run/Debug Configuration 설정 시 Environment 필드값 수정

Default Config 정보

NameDescription
CONSOLE_API콘솔에서 사용하는 API의 엔드포인트를 정의
GTAG_IDGoogle Analytics를 위해 사용
DOMAIN_NAME사이트 도메인 이름
DOMAIN_NAME_REF‘hostname’ 일 경우, 사이트 도메인 이름을 추출하여 Domain 정보 로드
ADMIN_DOMAIN
AMCHARTS_LICENSE차트 라이브러리인 amcharts의 라이센스 정보
MOCKMOCK API 사용 여부 및 MOCK API의 엔드포인트 정의
ASSET_PATHasset에 사용되는 엔드포인트 정보
DOMAIN_IMAGESignIn 페이지 및 GNB에 사용되는 이미지의 url 정의
DOCS관련 문서 링크를 만들기 위한 정보
- label, link 를 가진 객체 배열
- ejs template 문법을 지원
 - 제공 변수: lang (사용자 언어 코드. e.g. "en")
BILLING_ENABLEDbilling 서비스 이용 가능한 도메인 리스트 정의
CONTACT_LINKSignIn 페이지의 contact us 링크 정의
  • development.json 권장 예시
    {
        "VUE_APP_API": {
          "ENDPOINT": "https://sample.com"
        },
        "GTAG_ID": "DISABLED",
        "DOMAIN_NAME": "sample",
        "DOMAIN_NAME_REF": "config",
        "ASSETS_ENDPOINT": "https://sample-asset.com/assets/"
    }
    

Config 파일 위치

  • Default: <SOURCE_ROOT>/public/config/default.json
  • Each Environment: <SOURCE_ROOT>/public/config/<NODE_ENV>.json

Config 사용 방법

import config from '@/lib/config'

config.get(); // All Values
config.get('VUE_APP_API.ENDPOINT'); // Value of specific key

1.2 - 시작하기

이 페이지는 SpaceOne Console 개발을 시작하기 위한 안내문서입니다.

개발 환경 세팅

Fork

현재 스페이스원의 콘솔은 오픈소스로 운영중에 있습니다.

개발에 기여하기위해 먼저 console 레포지토리를 개인 github 계정에 포크해 줍니다.

Clone

이후 포크해온 레포지토리를 로컬로 클론해 줍니다.

서브모듈로 assets번역 관련 레포지토리가 사용중이기 때문에 함께 초기화합니다.

git clone --recurse-submodules https://github.com/[github username]/console

cd console

Run Console

콘솔을 실행 실행시키기 위해 npm으로 의존성을 설치합니다.

npm install --no-save

이후 개발환경을 위한 config 파일을 public/config/development.json 작성합니다. (config 파일 작성은 여기를 참고 하십시오)


마지막으로 스크립트를 실행합니다.

npm run serve

Build

배포 가능한 zip을 생성하려면 아래의 스크립트를 실행하시면 됩니다.

npm run build

차트 라이선스

콘솔은 내부적으로 모든 차트에 대해 amCharts를 사용합니다.

콘솔을 사용하기 전에 amCharts의 라이선스를 확인하시고, 자신에게 맞는 amCharts 라이선스를 구매하여 콘솔에서 사용하려면 아래와 같이 진행해주십시오.


public/config/default.json에 라이센스 키 추가

{
    "AMCHARTS_LICENSE": {
    "CHARTS": "",
    "MAPS": "",
    "TIMELINE": ""
    }
}

스타일


스타일 정의에 있어 SpaceOne Console은 tailwind csspostcss를 사용중에 있습니다.

SpaceOne의 color palette에 따라 tailwind 커스텀을 통해 적용되어 있습니다. (세부 정보는 storybook을 참고해주세요)

1.3 - Coding Convention

Frontend Coding Convention

프론트엔드 코딩 컨벤션에 대한 문서입니다. Console, Design System, Core Lib

Javascript - ECMAScript 2018(ES9)

ItemCategoryRuleExample
ClassPascalCaseclass myClass {}
FunctioncamelCaseconst myFunction = () => {}
VariableReadonly const
Enum
SCREAMING_SNAKE_CASEconst READONLY_CONST <br /> MY_ENUM {}
OtherscamelCasemyVariable

Typescript

ItemCategoryRuleExample
TypePascalCasetype MyType = type;
InterfacePascalCaseinterface MyInterface {}

File / Directory / URL

ItemRepoCategoryRuleExample
FilesConsoleVue ComponentsPascalCaseMyComponent.vue
PagesPascalCase
with suffix 'Page'
MyConsolePage.vue
Design SystemComponents
Related Files
PascalCase
with prefix 'P' (means 'Prime')
with its component name
PMyDSComponent.vue
PMyDSComponent.mdx
PMyDSComponent.stories
PMyDSComponent.scss
PMyDSComponent.pcss
Type Related Fileskebab-case
with suffix '-type'
type.ts
schema-type.ts
Other FilesCommonkebeb-casemy-config.ts
Directory
URL
Commonkebab-case/my-directory
/my-url/

Code

ItemRepoCategoryRuleExample
EventCommonNameVerb Root
If duplicated or needed -ed / -ing is allowed
update
update, updated
Two-way BindingIf the event needs two-way biding, emit 'update:xxx'$emit('update:code', code)
Arguments$event as the last argument$emit(foo, bar, $event)
CodeCommonHandler NameClear word with prefix 'handle'const handleOnClick = () => {}
Component NamePascalCase at script files
kebab-case at template
<my-component />
import MyComponent from @
Composition API1. Do not declare objects,variables inside return
2. The name of reactive variable should be state or xxxState (if needed)
3. Using variable inside setup() is recommended as reactive
1. const a = 1; return { a }
2,3. const state = reactive({})
ConsolePage script, style1. <script> lang should be 'ts'
2. <style> lang should be 'postcss' and 'scoped'
1. <script lang="ts">
2. <style lang="postcss" scoped>
Design SystemPage script, style1. <script> lang should be 'ts'
2. <style> lang should be 'postcss' BUT NO 'scoped'
1. <script lang="ts">
<style lang="postcss">
Storybook TitleDirectories + component name(PascalCase) with dash{ title: 'atoms/buttons/MyButton' ... }
Root Class NameComponent name should be written on root element's class with kebab-case<fragment class="p-my-button">
Core LibDescriptionDescription of each function should be written, by JS Doc/** @@function @name @description *@param descriptions **/

Additional Rules

  1. Array 에서 변수명 지정은 복수형보다 List 접미사를 지향합니다.
const policies: Array<string>; (X)
const plicyList: Array<string>; (O)
  1. enum 혹은 Object.freeze() 대신 as const 를 사용합니다.
const NOTIFICATION_UNIT = {
  PERCENT: 'PERCENT',
  ACTUAL_COST: 'ACTUAL_COST',
} as const;
  1. API 응답 결과값에 대한 interface 명은 xxxModel 이라고 명명합니다.
interface CostQuerySetModel {};
  1. init 할 때 실행할 함수는 setup 함수 최하단에 위치시키는 것을 지향합니다.

    async 인 경우에는 즉시실행함수로 작성합니다.

(async () => {
	await listCostQuerySet();
})();

return { ...toRefs(), ... }

Test Code

console과 Design System 에서는 공통적으로 vue test utils 를 사용합니다.

파일명: __test__/<대상 파일 명>.test.ts

테스트코드 템플릿

import { mount, createLocalVue } from '@vue/test-utils';
import CompositionApi, { defineComponent } from '@vue/composition-api';

const localVue = createLocalVue();
localVue.use(CompositionApi);

describe('', () => {
    const mockComponent = defineComponent({
        template: `
            <div>
            </div>
        `,
        setup() {
            return {};
        },

    });

    const wrapper = mount(mockComponent, { localVue });

    it('', () => {
        expect(wrapper.exists()).toBe(true);
    });
});

Lint

References

1.4 - Commit Convention

Frontend Commit Convention

프론트엔드 커밋 컨벤션에 대한 문서입니다. Console, Design System, Core Lib

커밋 메시지 구조

<타입>[적용 범위(선택 사항)]: <설명>

[본문(선택 사항)]

[꼬리말(선택 사항)]

커밋 메시지 구조적 요소

TypeDescriptionRemark
fixBug Fix
API 변경 사항 없이 내부 수정
PATCH
feat기능 추가
API 변경 (하위 호환)
MINOR
perf성능 향상을 위한 코드 변경MAJOR
BREAKING CHANGEAPI 의 변경, 큰 변화MAJOR
refactor내부적인 리팩토링앵귤러 컨벤션
ciCI 변경 (workflow, etc)앵귤러 컨벤션
buildbuild 관련 변경 (webpack, dependencies, etc)앵귤러 컨벤션
docs문서 작성, 수정앵귤러 컨벤션
style코드 의미적으로는 변하지 않는 커밋 (css, formatting, missing semi-colons, etc)앵귤러 컨벤션
revert이전 커밋으로 revert앵귤러 컨벤션
chore그 외 자잘한 수정앵귤러 컨벤션

주의 사항

  1. 반드시 커밋 메시지 구조에 맞는 메시지를 작성하여야 합니다.
    1. 컨벤션과 다를 시, commitLint 에 의해 commit 이 fail 할 수 있습니다.
  2. 타입 뿐 아니라 적용 범위, 설명, 본문, 꼬리말 모두 영어로 작성하는 것을 지향합니다.

References

2 - Plugins

SpaceONE Deep Dive

2.1 - Developer Guide

Developer Guide

Plugin 은 Protobuf 를 사용하는 어떤 언어로도 개발 가능하다.
Micro Service 와 Plugin 통신은 모두 Protobuf 를 기본으로 사용하기 때문이다. 기본적인 구조는 gRPC interface 를 사용한 서버 개발 과정과 동일한 방식이다.

Plugin 개발시 어떤 언어로든 (gRPC interface 가 사용 가능한 모든 언어) 개발이 가능하지만,
우리가 제공하는 Python Framework 를 사용한다면 더욱 손쉽게 개발 가능하다.
현재 기본적으로 제공되는 Plugin 들 모두 Python 기반의 자체 개발된 Framework 를 기반으로 개발되었다.

Framework 에 대한 기본적인 사용 방법에 대해서는 다음을 참고한다.

다음은, Plugin 개발시 기본적으로 확인해야 하는 개발 요건 사항이며, 각 페이지에서 단계별 상세 사항을 확인 가능하다.

2.1.1 - gRPC Interface 확인

먼저, 개발하고자 하는 Plugin 과 Core 서비스 간의 Interface 를 확인한다. interface 는 각각의 서비스 마다 구조가 다르다. 이에 대한 gRPC interface 정보는 API 문서에서 확인 가능하다. (SpaceONE API)

예를 들어 Identity 의 인증 용 Auth Plugin 을 개발한다고 가정해보자. 이때, Auth Plugin 의 Interface 정보를 확인해 보면 아래와 같다. (SpaceONE API - Identity Auth)





Identity 의 Auth Plugin 개발을 위해서는 총 4개의 API interface 를 구현해야 한다.
이 중, init 과 verify 는 모든 Plugin 이 동일하게 필요로 하는 inteface 이며,
나머지는 각각의 Plugin 마다 특성에 따라 다르다.

이 중에서 공통으로 구현이 필요한 init 과 verify 를 자세히 살펴본다.

1. init

Plugin 초기화.
Identity 의 경우 Domain 을 생성 할 때, 어떤 인증을 사용할지를 결정해야 하며 관련된 Auth Plugin 을 배포하게 된다.
최초 Plugin 배포시 (또는 Plugin 버전 업데이트시) Plugin 컨테이너가 생성 완료된 후 Core 서비스는 Plugin 에 init API 를 호출하게 된다.
이때, Plugin 은 Core 서비스가 Plugin 과 통신시 필요로 하는 metadata 정보를 반환하게 된다.
metadata 에 대한 정보는 Core 서비스 별로 다르다.

아래는 Google oAuth2 plugin 의 init 구현에 대한 python 코드의 예시이다.
return 값으로 metadata 를 반환하는데, 이때 identity 에서 필요한 다양한 정보를 추가하여 반환한다.

    @transaction
    @check_required(['options'])
    def init(self, params):
        """ verify options
        Args:
            params
              - options
        Returns:
            - metadata
        Raises:
            ERROR_NOT_FOUND:
        """
        
        manager = self.locator.get_manager('AuthManager')
        options = params['options']
        options['auth_type'] = 'keycloak'
        endpoints = manager.get_endpoint(options)
        capability= endpoints
        return {'metadata': capability}

2. verify

Plugin 정상 동작 확인. Plugin 이 배포된 후, init API 호출 이후에는 plugin 이 동작 실행 준비가 완료되었는지 확인 절차를 거치는데 이때 호출되는 API 가 verify 이다.
verify 단계에는 Plugin 이 정상적인 동작 수행 준비가 되었는지 확인하는 절차를 확인한다.

아래는 Google oAuth2 plugin 의 verify 구현에 대한 python 코드의 예시이다.
verify 행위는 Google oAuth2 동작시 필요한 값을 통해 실제 logic 이 수행하기 위한 준비단계가 정상 동작하기 위한 검증 수준의 코드를 필요로 한다.

    def verify(self, options):
        # This is connection check for Google Authorization Server
        # URL: https://www.googleapis.com/oauth2/v4/token
        # After connection without param.
        # It should return 404
        r = requests.get(self.auth_server)
        if r.status_code == 404:
            return "ACTIVE"
        else:
            raise ERROR_NOT_FOUND(key='auth_server', value=self.auth_server)

2.1.2 - Plugin Register

Plugin 개발이 완료되었다면, Plugin 배포를 준비해야 한다. SpaceONE 의 모든 Plugin 은 Container 로 배포되기 때문에, 개발이 완료된 Plugin 코드는 Container 배포를 위한 Image 로 빌드해야 한다. Container 빌드는 Dockerfile 을 활용해 docker build 를 수행 한 후, 결과물인 Image 는 Docker hub 와 같은 이미지 저장소에 업로드하도록 한다. 이때, 이미지 저장소는 SpaceONE 의 Microservice 인 Repository 서비스에서 관리하는 저장소에 업로드하도록 한다.


![](/ko/docs/developers/plugins/developer_guide/developer_guide_img/plugin_container_build.png)
이미지를 저장소에 업로드 하였다면, Microservice 중 Repository 서비스에 해당 이미지를 등록해야 한다. 등록 API 는 Repository.plugin.register 를 사용하도록 한다. ([SpaceONE API - (Repository) Plugin.Register](https://spaceone-dev.gitbook.io/spaceone-apis/repository/v1/plugin#register))

아래 예제는 Notification 의 Protocol Plugin 등록시 전달된 Parameter 내용이다. image 값에는 이전에 빌드된 이미지 주소를 넣어준다.

name: Slack Notification Protocol
service_type: notification.Protocol
image: pyengine/plugin-slack-notification-protocol_settings
capability:
  supported_schema:
  - slack_webhook
  data_type: SECRET
tags:
  description: Slack
  "spaceone:plugin_name": Slack
  icon: 'https://spaceone-custom-assets.s3.ap-northeast-2.amazonaws.com/console-assets/icons/slack.svg'
provider: slack
template: {}

이미지 등록의 경우, 아직 Web Console 에서 지원되지 않기 때문에 직접 gRPC API 를 사용하거나 spacectl 을 사용하도록 한다. 위와 같은 yaml 형태의 파일을 생성 후, 아래와 같이 spacectl 명령어로 이미지 등록이 가능하다.

> spacectl exec register repository.Plugin -f plugin_slack_notification_protocol.yml

이미지가 Repository 에 등록 완료되면 아래와 같이 확인 가능하다.

> spacectl list repository.Plugin -p repository_id=<REPOSITORY_ID>  -c plugin_id,name
plugin_id                              | name
----------------------------------------+------------------------------------------
 plugin-aws-sns-monitoring-webhook      | AWS SNS Webhook
 plugin-amorepacific-monitoring-webhook | Amore Pacific Webhook
 plugin-email-notification-protocol_settings     | Email Notification Protocol
 plugin-grafana-monitoring-webhook      | Grafana Webhook
 plugin-keycloak-oidc                   | Keycloak OIDC Auth Plugin
 plugin-sms-notification-protocol_settings       | SMS Notification Protocol
 plugin-voicecall-notification-protocol_settings | Voicecall Notification Protocol
 plugin-slack-notification-protocol_settings     | Slack Notification Protocol
 plugin-telegram-notification-protocol_settings  | Telegram Notification Protocol

 Count: 9 / 9

spacectl 의 자세한 사용 방법은 해당 페이지에서 확인 가능하다. Spacectl CLI Tool

2.1.3 - Plugin Deployment

등록된 Plugin 을 실제로 배포해서 사용하려면, Plugin 이미지를 바탕으로 Kubernetes 환경에 Pod 를 배포해야 한다.
이때, Plugin 배포는 해당 plugin 을 사용하고자 하는 서비스에서 자동으로 수행하게 된다.

예를 들어, Notification 의 경우 발생된 Alert 을 사용자에게 전달하기 위해 Protocol 이라는 객체를 사용하여 전달하게 되는데
이때, Notification 의 Protocol.create (Protocol.create) 명령을 수행시, Notification 이 자동으로 Plugin 을 배포하게 된다.

아래 예제는 Notification 에 Slack 으로 알람을 전송하기 위한 Slack Protocol 생성에 대한 Protocol.create 명령 파라미터 예시이다.

---
name: Slack Protocol
plugin_info:
  plugin_id: plugin-slack-notification-protocol_settings
  version: "1.0"
  options: {}
  schema: slack_webhook
tags:
  description: Slack Protocol

plugin_id 에는 Repository 에 등록한 plugin 의 ID 값을 넣고,
version 에는 Dockerhub 와 같은 이미지 저장소에 실제 이미지 업로드시 기입했던 이미지 tag 정보를 넣어준다.
만약 이미지 저장소에 여러 tag 를 가진 경우, 지정된 tag version 의 이미지로 plugin 배포를 수행한다.

위의 경우, version 을 "1.0" 으로 지정하였기 때문에 아래 tag 정보 중 "1.0" tag 이미지로 배포되기 된다.



해당 API 의 경우, Kubernetes 환경에 Service 와 Pod 를 생성해 배포하는 단계를 거치기 때문에 응답까지 약간의 시간이 걸린다.
실제 Kubernetes 환경에서 Pod 배포를 확인해 보면 아래와 같이 확인 가능하다.

> k get po
NAME                                                              READY   STATUS    RESTARTS   AGE
plugin-slack-notification-protocol_settings-zljrhvigwujiqfmn-bf6kgtqz   1/1     Running   0          1m

3 - Design System

클라우드포레 프로젝트의 디지털 경험을 통합하기 위한 오픈 소스 디자인 시스템입니다.

Overview

점점 더 많은 제품들이 더 나은 사용자 경험을 추구하기 시작했습니다. 이러한 상황에서 우리는 아래의 원칙을 기반으로 디자인 시스템을 구축했습니다.

디자인 시스템은 프로덕트 제작에 참여하는 모두가 디자인 문제를 더 빠르고 효율적으로 해결할 수 있도록 돕습니다. 또한 우리의 디자인 시스템은 여러 작업자가 모두 하나의 목소리로 이야기할 수 있도록 돕는 Single source of truth이며, 같은 방식으로 논의할 수 있도록 협의한 문법을 제공합니다.

Principle

사용자 중심

우리의 디자인은 제품의 최접점에서 고객과 커뮤니케이션 하는 언어로 사용자를 우선으로 합니다. 프로덕트를 개발하는 방식에서 포괄성, 접근성, 투명성을 중요하게 생각하며 개방적이고 접근하기 쉬운 디자인을 위해 노력합니다.

명확한 시각화

사용자는 멀티 클라우드의 복잡성 안에서 다양한 과업을 수행합니다. 어려운 문제를 간소화하고 복잡한것을 명확하게 표현해 사용자가 고민하지 않고 서비스를 사용할 수 있도록 도와주는것이 기본 원칙입니다. 필요할 때 필요한 것을 제공하는 디자인과 메시지로 고객의 목표 달성을 돕기 위해 노력합니다.

완벽 너머 지속성

언어는 상황과 환경을 반영해 계속해서 발전합니다. 우리의 디자인 시스템도 최선을 목표로 끊임없이 사용성을 검증 해 나가며 완벽 너머의 지속하는 힘을 지향합니다.

디자인 시스템의 발전에 기여하고 싶다면 아래의 리소스를 확인해주세요.

Resources

GitHub

디자인 시스템 저장소

Storybook

컴포넌트 라이브러리

Figma

공개 준비중

3.1 - 시작하기

이 페이지는 SpaceOne Design System 개발을 시작하기 위한 안내문서입니다.

개발 환경 세팅

Fork

현재 스페이스원의 콘솔은 오픈소스로 운영중에 있습니다.

개발에 기여하기위해 먼저 Design System 레포지토리를 개인 github 계정에 포크해 줍니다.

Clone

이후 포크해온 레포지토리를 로컬로 클론해 줍니다.

서브모듈로 assets번역 관련 레포지토리가 사용중이기 때문에 함께 초기화합니다.

git clone --recurse-submodules https://github.com/[github username]/spaceone-design-system

cd console

Run Storybook

콘솔을 실행 실행시키기 위해 npm으로 의존성을 설치하고, 스크립트를 실행해 줍니다.

npm install --no-save

npm run storybook

Build

배포 가능한 zip을 생성하려면 아래의 스크립트를 실행하시면 됩니다.

npm run build

스토리북


SpaceOne Design system은 Storybook을 제공하고 있습니다.

컴포넌트를 생성하면 해당 컴포넌트의 기능 정의를 Storybook을 통해 문서화합니다.

기본적으로 한 컴포넌트가 아래와 같은 구조로 구성되어 있습니다.

- component-name
    - [component-name].stories.mdx
    - [component-name].vue
    - story-helper.ts
    - type.ts

[component-name].stories.mdx 와 story-helper.ts

컴포넌트의 설명, 사용예시, Playground를 제공합니다.

mdx 포멧을 사용중이며 사용방법은 문서를 참고하십시오.

playground에 명시되는 props, slots, events와 같은 속성들은 가독성을 위해 story-helper를 통해 분리하여 작성하는 방식을 지향합니다.

차트 라이선스


SpaceONE 디자인 시스템은 내부적으로 amCharts for Dynamic Chart를 사용합니다.

디자인 시스템을 사용하기 전에 amCharts의 라이선스를 확인해주십시오.

자신에게 적합한 amCharts 라이선스를 구입하여 애플리케이션에서 사용하려면 라이선스 FAQ를 참조하십시오.

스타일


스타일 정의에 있어 SpaceOne Console은 tailwind csspostcss를 사용중에 있습니다.

SpaceOne의 color palette에 따라 tailwind 커스텀을 통해 적용되어 있습니다. (세부 정보는 storybook을 참고해주세요)

4 - Contribute

SpaceONE Project Contribution Guide

4.1 - Content 가이드

이 페이지는 Content Guide 작성 방법을 설명합니다.

새로운 페이지 생성

페이지 생성 위치의 상위 페이지로 이동 합니다. 오른쪽 하단의 '하부 페이지 생성' 버튼을 클릭합니다.

또는:
The docs repository 에서 fork 한 후 페이지를 생성합니다.

제목 및 파일 이름 작성

제목에서 밑줄(_)로 구분된 단어를 사용하여 파일 이름을 생성합니다. 예를 들어, 제목이 Project Management인 파일 이름은 project_management.md입니다.

Front matter에 필드 추가

Front matter에 필드를 추가합니다. Front matter은 페이지 상단의 삼중 파선 사이에 있는 YAML 블록 영역입니다. 다음은 예입니다.

---
title: "Project Management"
linkTitle: "Project Management"
weight: 10
date: 2021-06-10
description: >
  View overall status of each project and Navigate to detailed cloud resources.  
---

Front matter 변수 설명

변수설명
title컨텐츠 제목
linkTitle왼쪽 사이드바 메뉴에 노출
weight왼쪽 사이드바 메뉴의 순서를 정렬하는데 사용되는 변수. 더 낮은 weight 일수록 우선순위가 높음. 즉, 낮은 weight 일수록 컨텐츠가 먼저 노출. weight가 0 이면, 설정되지 않은 값으로 해석하기 때문에 0이 아니어야 함.
date생성날짜
description페이지 설명

Front matter에 관한 더 자세한 사항은 Front matter 을 방문해 알 수 있습니다.

Document 작성

Table of Contents추가

Document에 ##을 추가 하면, Table of Contents 리스트에 자동적으로 추가됩니다.

이미지 추가

작성하는 document와 동일한 계층에 파일명_img 이름의 폴더를 생성합니다. 예를 들면, project_management.md에 사용되는 이미지를 위한 project_management_img 디렉토리를 생성합니다. 디렉토리에 이미지를 추가하여 사용합니다.

Style guide

Style guide를 참고해서 document를 작성해주세요.

Pull request 요청

새로운 branch에 커밋을 하고, pull request 요청합니다.

4.2 - Style 가이드 (shortcodes)

이 페이지에서는 SpaceONE Markdown에서 사용할 수 있는 사용자 지정 Hugo shortcodes를 중점으로 설명합니다.

Heading 태그

##, <h2>부터 순차적으로 사용하는 것을 권장합니다. 시맨틱 마크업뿐만 아니라 스타일을 위한 것입니다.

코드 :

{{< link-button background-color="navy400" url="/" text="Home" >}}
{{< link-button background-color="white" url="https://cloudforet.io/" text="cloudforet.io" >}}

결과 :
Home cloudforet.io

Video

코드 :

{{< video src="https://www.youtube.com/embed/zSoEg2v_JrE" title="SpaceONE Setup" >}}

결과:

Alert

코드 :

{{< alert title="Note Title" >}}
	Note Contents
{{< /alert >}}

결과:

Reference