SW 품질속성 예제

|

품질속성 6가지 항목

image

각각의 항목에 대한 구체적인 예시들은 다음과 같습니다.


가용성 시나리오 예시

항목
Source 생명신호 모니터링
Stimulus 서버가 반응하지 않음
Artifact 프로세스
Environment 정상적인 운영
Response 운영자에게 보고하고 운영을 계속한다.
Measure 중단 시간 없음


상호운영성 예시

항목
Source 운송 정보 시스템
Stimulus 현재 자극 위치 전송
Artifact 교통 통제 시스템
Environment 런타임 이전에 알려진 시스템
Response 트래픽 모니터 시스템은 현재 위치와 다른 정보를 결합한다.
구글 맵스 위에 올려놓고 브로드캐스트한다.
Measure 99.9% 정확하게 시간이 포함된 우리 정보


변경 용이성 시나리오

항목
Source 개발자
Stimulus UI를 변경하기 원한다.
Artifact 코드
Environment 설계 시
Response 변경을 하고 단위 테스트를 수행한다.
Measure 3시간 이내


성능 시나리오

항목
Source 사용자
Stimulus 트랜잭션 시작
Artifact 시스템
Environment 정상 운영
Response 트랜잭션이 처리됨
Measure 평균 2초의 지연 시간


보안 시나리오

항목
Source 원격 위치에 있는 불만을 가진 직원
Stimulus 임금률 변경 시도
Artifact 시스템 안에 있는 데이터
Environment 정상 운영
Response 시스템은 감사 트레일을 유지한다.
Measure 하루 안에 정확한 데이터로 복구되고, 공격의 근원을 식별한다.


테스트 용이성 시나리오

항목
Source 단위 테스터
Stimulus 코드 단위 완료
Artifact 코드 단위
Environment 개발
Response 수집된 결과
Measure 3시간 안에 85% 경로 커버리지


사용 편의성 시나리오

항목
Source 사용자
Stimulus 새로운 App을 다운로드한다.
Artifact 시스템
Environment 런타임
Response 사용자가 생산적으로 App을 사용한다.
Measure 측정 2분 동안 시험한다.

간단한 Wallpaper 만들어보기

|

Wallpaper

안드로이드에서 간단한 Wallpaper를 만들어보는 예제입니다.


AndroidManifest.xml

먼저 다음 Permission을 추가해줍니다.

    <uses-permission android:name="android.permission.SET_WALLPAPER" />
    <uses-feature
            android:name="android.software.live_wallpaper"
            android:required="true"/>

그리고 Wallpaper는 android.service를 사용하기 때문에 service도 등록합니다.

    <service
        android:name=".service.snowdeer.SimpleWallpaperService"
        android:enabled="true"
        android:label="@string/app_name"
        android:permission="android.permission.BIND_WALLPAPER">
        <intent-filter>
            <action android:name="android.service.wallpaper.WallpaperService" />
        </intent-filter>

        <meta-data
            android:name="android.service.wallpaper"
            android:resource="@xml/wallpaper" />
    </service>


BaseWallpaperService.kt

import android.content.Context
import android.service.wallpaper.WallpaperService
import android.view.MotionEvent
import android.view.SurfaceHolder
import android.view.WindowManager

interface InteractiveEngine {
    fun setScreenSize(width: Int, height: Int)
    fun onSurfaceCreated(holder: SurfaceHolder)
    fun onDestory()
}

open abstract class BaseWallpaperService : WallpaperService() {

    abstract fun handleScreenSize(width: Int, height: Int)
    abstract fun handleSurfaceCreated(holder: SurfaceHolder)
    open fun handleTouchEvent(event: MotionEvent) {}

    override fun onCreate() {
        super.onCreate()

        val window = getSystemService(Context.WINDOW_SERVICE) as WindowManager
        val display = window.defaultDisplay
        val width = display.width
        val height = display.height

        handleScreenSize(width, height)
    }

    override fun onCreateEngine(): Engine {
        return WallpaperEngine()
    }

    inner class WallpaperEngine : WallpaperService.Engine() {
        override fun onSurfaceCreated(holder: SurfaceHolder) {
            handleSurfaceCreated(holder)
        }

        override fun onTouchEvent(event: MotionEvent?) {
            event?.let {
                handleTouchEvent(event)
            }
        }
    }
}


SimpleWallpaper.kt

import android.view.MotionEvent
import android.view.SurfaceHolder
import com.snowdeer.wallpaper.service.BaseWallpaperService

class SimpleWallpaper : BaseWallpaperService() {
    val engine: InteractiveEngine = RandomColorEngine()
    
    companion object {
        const val TAG = "NormalWallpaperService"
    }

    override fun handleScreenSize(width: Int, height: Int) {
        engine.setScreenSize(width, height)
    }

    override fun handleSurfaceCreated(holder: SurfaceHolder) {
        engine.onSurfaceCreated(holder)
    }

    override fun handleTouchEvent(event: MotionEvent) {
        engine.handleTouchEvent(event)
    }

    override fun onDestroy() {
        super.onDestroy()
        engine.onDestory()
    }
}


RandomColorEngine.kt

import android.graphics.Color
import android.graphics.Paint
import android.os.Handler
import android.os.Looper
import android.view.SurfaceHolder
import com.snowdeer.service.InteractiveEngine
import kotlin.random.Random

class RandomColorEngine : InteractiveEngine {
    private val handler = Handler(Looper.getMainLooper())
    private var isRunning = false

    override fun setScreenSize(width: Int, height: Int) {

    }

    override fun onSurfaceCreated(holder: SurfaceHolder) {
        isRunning = true

        drawCanvas(holder)
    }

    override fun onDestory() {
        isRunning = false
    }

    private fun drawCanvas(holder: SurfaceHolder) {
        if (!isRunning) {
            return
        }

        try {
            val canvas = holder.lockCanvas()

            canvas?.let {
                val paint = Paint().apply {
                    val randomColor = Random.nextInt(16_777_216)
                        .toString(16)
                        .padStart(6, '0')
                    color = Color.parseColor("#$randomColor")
                    style = Paint.Style.FILL
                }
                canvas.drawPaint(paint)
                holder.unlockCanvasAndPost(canvas)

                handler.postDelayed({ drawCanvas(holder) }, 1000)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }

    }
}


WallpaperApplier.kt

import android.app.WallpaperManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent

data class WallpaperListItem(val name: String, val resId: Int, val cls: Class<*>)

class WallpaperApplier(val ctx: Context) {
    fun apply(wallpaper: WallpaperListItem) {
        setWallpaperService(wallpaper.cls)
    }

    private fun setWallpaperService(wallpaperService: Class<*>) {
        val intent = Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER)
        intent.putExtra(
            WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT,
            ComponentName(ctx, wallpaperService)
        )
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        ctx.startActivity(intent)
    }
}

Git pre-commit 사용법

|

pre-commit

이름에서 볼 수 있듯이 Git commit을 수행하기 전에 자동으로 특정 작업을 수행하도록 해주는 기능입니다. 보통 formatter 또는 linter 등을 실행해서 코드의 잠재적 문제 발견 또는 일관성있는 포맷을 유지하게 해줍니다.


설치 방법

pre-commit는 파이썬 패키지를 이용해서 설치할 수 있습니다.

$ pip3 install pre-commit

Collecting pre-commit
  Downloading pre_commit-2.12.1-py2.py3-none-any.whl (189 kB)
     |████████████████████████████████| 189 kB 813 kB/s
Collecting virtualenv>=20.0.8
  Downloading virtualenv-20.4.4-py2.py3-none-any.whl (7.2 MB)
     |████████████████████████████████| 7.2 MB 869 kB/s
Requirement already satisfied: toml in /usr/local/lib/python3.8/site-packages (from pre-commit) (0.10.2)
Collecting cfgv>=2.0.0
  Downloading cfgv-3.2.0-py2.py3-none-any.whl (7.3 kB)
Collecting nodeenv>=0.11.1
  Downloading nodeenv-1.6.0-py2.py3-none-any.whl (21 kB)
Requirement already satisfied: pyyaml>=5.1 in /usr/local/lib/python3.8/site-packages (from pre-commit) (5.4.1)
Collecting identify>=1.0.0
  Downloading identify-2.2.4-py2.py3-none-any.whl (98 kB)
     |████████████████████████████████| 98 kB 17.3 MB/s
Collecting filelock<4,>=3.0.0
  Downloading filelock-3.0.12-py3-none-any.whl (7.6 kB)
Collecting appdirs<2,>=1.4.3
  Downloading appdirs-1.4.4-py2.py3-none-any.whl (9.6 kB)
Requirement already satisfied: distlib<1,>=0.3.1 in /usr/local/lib/python3.8/site-packages (from virtualenv>=20.0.8->pre-commit) (0.3.1)
Requirement already satisfied: six<2,>=1.9.0 in /usr/local/lib/python3.8/site-packages (from virtualenv>=20.0.8->pre-commit) (1.15.0)
Installing collected packages: filelock, appdirs, virtualenv, nodeenv, identify, cfgv, pre-commit
Successfully installed appdirs-1.4.4 cfgv-3.2.0 filelock-3.0.12 identify-2.2.4 nodeenv-1.6.0 pre-commit-2.12.1 virtualenv-20.4.4

MacOS에서는 brew를 이용해서도 설치 가능하지만, 그냥 pip3를 이용하는게 더 편한 듯 싶습니다.


버전 확인

$ pre-commit -V

pre-commit 2.12.1


설정

pre-commit.pre-commit-config.yaml 설정 파일을 필요로 합니다. 아래 명령어를 이용해서 sample-config라는 템플릿으로 설정 파일을 만들 수 있습니다. git add 명령어로 프로젝트에 추가할 예정이니, 아래 명령어는 pre-commit를 적용하려는 프로젝트의 디렉토리에서 실행하세요.

$ pre-commit sample-config > .pre-commit-config.yaml


.pre-commit-config.yaml

해당 설정 파일은 다음과 같은 내용이 작성되어 있습니다.

# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
-   repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v3.2.0
    hooks:
    -   id: trailing-whitespace
    -   id: end-of-file-fixer
    -   id: check-yaml
    -   id: check-added-large-files

총 4개의 hook이 설정되어 있는 것을 볼 수 있습니다.


실행

이제 pre-commit run 명령을 실행해봅니다. 처음에는 해당 repo로부터 다운로드하는 시간이 있어서 약간의 시간이 걸립니다.

$ pre-commit run

[INFO] Initializing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Installing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
Trim Trailing Whitespace.............................(no files to check)Skipped
Fix End of Files.....................................(no files to check)Skipped
Check Yaml...........................................(no files to check)Skipped
Check for added large files..........................(no files to check)Skipped

그 이후 pre-commit run -a 명령어를 이용해서 모든 파일들을 한 번 검사해봅니다.

$ pre-commit run -a

...

다음 명령어를 이용해서 .pre-commit-config.yaml 파일을 git 에 추가해줍니다.

$ git add .pre-commit-config.yaml

$ git commit -m "pre-commit 적용"


git hook에 등록

마지막으로 git commit 할 때 자동으로 pre-commit가 실행되도록 합니다. 아래 명령어를 이용해서 git hook에 등록할 수 있습니다.

$ pre-commit install

Visual Studio Code Plugin `Project Dashboard`

|

Project Dashboard

vscode에서 다양한 프로젝트들을 다음 그림처럼 대시보드 형태로 관리할 수있게 해주는 플러그인입니다.

image

프로젝트 전환도 쉽고, 새 창으로 다른 프로젝트를 열기도 쉬워서 상당히 유용한 플러그인입니다.


설치 방법

설치는 vscode 플러그인 설치 메뉴에서 Project Dashboard를 검색해서 설치하면 됩니다.


단축키

대시보드를 띄우는 단축키는 리눅스에서는

Ctrl + F1 키를 누르면 됩니다.

맥에서는

command + F1 입니다.

BigSur에서 안드로이드로 USB 테더링하기

|

USB 테더링

기존에 포스팅한 내용이 있는데, BigSur 버전에서는 추가적인 별도의 작업을 해줘야 해서 포스팅을 다시 합니다.


HoRNDIS 9.2

기존과 마찬가지로 HoRNDIS를 설치해줘야 합니다. 최신 버전인 9.2 버전을 다운도르하면 됩니다. 다만, BigSur에서는 설치 마지막 단계에서 실패가 됩니다.


HoRNDIS 개발자 ID

다음 내용을 기억해줍시다. 생략해도 됩니다. 다만, 개발자 ID가 다를까봐 다시 한 번 확인하는 과정입니다.

$ sudo su

$ spctl -a -vv -t install /Library/Extensions/HoRNDIS.kext
/Library/Extensions/HoRNDIS.kext: accepted 
source=Notarized Developer ID 
origin=Developer ID Application: Joshua Wise (54GTJ2AU36)


복구 모드 진입

그런 다음 맥북 종료 후

command + R 버튼을 눌러서 재부팅하면 복구모드로 진입합니다. 그리고 터미널을 실행한 다음, 아래 명령어를 입력합니다.

$ csrutil disable
System Integrity Protection is off.

$ /usr/sbin/spctl kext-consent list
spctl: no kext consent configuration found.

$ /usr/sbin/spctl kext-consent add 54GTJ2AU36
$ /usr/sbin/spctl kext-consent list
Allowed Team Identifiers:
54GTJ2AU36


노멀 부팅

다시 맥북을 재부팅 합니다. 노멀 모드로 진입한 다음 다음 명령어를 실행합니다.

$ sudo su 
$ sqlite3 /var/db/SystemPolicyConfiguration/KextPolicy 
SQLite version 3.32.3 2020-06-18 14:16:19 
Enter ".help" for usage hints. 

sqlite> delete from kext_load_history_v3 where team_id='54GTJ2AU36'; 
sqlite> delete from kext_policy where team_id='54GTJ2AU36'; 
sqlite> .quit 

그 이후 다음 파일을 삭제해줍니다.

$ cd /Library/Extensions

$ sudo rm -rf HoRNDIS.kext


HoRNDIS 재설치

그 이후 다시, HoRNDIS 프로그램을 재설치해줍니다. 그리고 다음 권한을 허용해줍니다. System Preferences > Security & Privacy에서 General 탭 하단에서 Joshua Wise 개발자 항목을 허용해줍니다.

이제 재부팅 후 안드로이드 폰과 USB 케이블 연결 후 테더링을 실행해보면 잘 동작하는 것을 알 수 있습니다.