안녕하세요 오늘은 국내 클라우드 중 NBP (Naver Business Platform) 의 맛을 보기 위해서 회원 가입을 하고 접속

 

해보았습니다.

https://www.ncloud.com/intro/feature

 

NAVER CLOUD PLATFORM

cloud computing services for corporations, IaaS, PaaS, SaaS, with Global region and Security Technology Certification

www.ncloud.com

제가 생각 하기엔 국내 최고의 클라우드를 서비스 하는 곳이라고 생각되어집니다.

 

새로 가입을 하게 되면은 서비스를 이용하기 위해서 결제 수단을 등록 하게 되는데요

 

결제 수단등록 시 1 년간 Micro Server 를 무료로 사용하실수 있습니다.

https://www.ncloud.com/support/notice/all/387  // 마이크로 서버 공지 

 

NAVER CLOUD PLATFORM

cloud computing services for corporations, IaaS, PaaS, SaaS, with Global region and Security Technology Certification

www.ncloud.com

1년이 지나게되면은 유료 가격이 측정 되니 꼭 유의해주세요!!

1시간     19 원 

월         13,000원 

 

프리티어 1년 짜리 Micro Server 에 node.js를 세팅해보려고 합니다. 

 

좌측 상단에 Console 이라는 창을 눌러서 NBP Console 에 접속 한 다음에 

 

Server -> Server 에 들어가시게 되면

서버 생성이라는 화면을 볼수 있습니다. 

 

이 부분에서는 centos -7.3-64를 선택 할건데요  

 

서버 타입 : Micro

 

로 꼭 설정해주셔야 합니다. (하지만 돈 나가용!)

 

이후에 해야 하는 것들은

 

인증키 설정 (절대 잊어 버리시면 안됩니다.)

ACG(Access Control Group) 설정 

공인 IP 할당( Public IP )

 

사실 이부분은 제가 따로 작성할 필욘 없을 것 같습니다. 이미 NBP 공식 블로그에서 너무 자세히 설명이 되어있어서

https://blog.naver.com/n_cloudplatform/221030710983

 

[이렇게 사용하세요!] 네이버 클라우드 플랫폼으로 손쉽게 웹 서버(APM) 구축하기

​네이버 클라우드 플랫폼을 이용하여 나만의 웹페이지 만들기​​블로그, 카페, 티스토리 등 컨텐츠를 올...

blog.naver.com

그리고 위의 url에 없는 포트포워딩과 ACG 공인 IP 가이드 자료입니다.

http://docs.ncloud.com/ko/compute/compute-2-2-v2.html

 

설명서 - NAVER CLOUD PLATFORM

서버 접속 절차 서버 접속 절차는 3단계로 구성됩니다. 리눅스 서버 접속 윈도우 서버 접속 포트 포워딩이란? 네이버 클라우드 플랫폼에서 구매한 서버는 비공인 IP가 할당됩니다. 포트 포워딩은 인터넷을 통해 비공인 IP가 할당된 서버에 접속하기 위한 기능을 제공합니다. 서버 접속을 위한 공인 IP는 고객당 1개가 할당되며 직접 설정한 외부 포트번호로 서버를 구분합니다. 아래와 같이 외부 포트 번호로 1024와 1025를 설정하는 경우, 포트 번호와 연결된

docs.ncloud.com

여기까지 완료가 되면 이제 접속을 할수 있게 되는데요

 

Micro Server는 말그대로 정말 아무것도 없는 형태로 만들어집니다. 위의 작업이 다 되었다면 Putty 를 통해

 

접속 합니다.

유저 생성

useradd -m user명 (-m은 홈 디렉토리를 같이 생성하는 옵션입니다. test라면 /home/test 가 같이 생성되는)

passwd user명 (위의 생성한 유저의 패스워드를 설정 합니다.)

 

권한 설정

최초 실행시에는 sudoers 권한이 없기 때문에 관리자 권한이 필요하면 매번 root로 들어가야되는데 

sudoers를 설정해 놓으면 sudo를 통해 권한을 빌려와서 사용할수 있습니다. 그렇기 때문에 해당 권한을 설정해줍니다.

 

su user명 ( 계정변경 )

 

su root(루트 계정으로 변경)

 

cd /etc/ (etc 경로로 이동)

 

chmod u+x sudoers (열어서 수정하기 위한 권한 입력)

 

vi sudoers ( sudoers 열기 )

su user명 ( 계정변경 )

su root(루트 계정으로 변경)

cd /etc/ (etc 경로로 이동)

chmod u+x sudoers (열어서 수정하기 위한 권한 입력)

vi sudoers ( sudoers 열기 )

 

## Allow root to run any commands anywhere 밑에
계정명 ALL=(ALL) ALL  

 

** 만약에 못찾으신다면 vi로 여신 후  / 를 입력 후

 

입력 입력하여서 위치를 찾을수 있습니다. 저는 oqa라는 계정에 추가 하였는데요  위와같이 입력 후 


ESC + : wq!  ( 강제 저장 후 종료 )

 


chmod 440 sudoers (기본값 원복)

su 계정명 ( 계정이동 - root가 아닌 원래의 계정으로 이동 ) 

     -> 이제 sudoers가 있기 때문에 root의 권한을 빌릴수 있습니다.

 


sudo yum install -y yum-utils 
( 레포지토리 추가 )  

sudo yum-config-manager \ --add-repo \ https://docs.docker.com/engine/installation/linux/repo_files/centos/docker.repo
( docker stable 레포 추가) 

sudo yum makecache fast 
( update yum package index ) 

(CentOS 7) 
sudo yum -y install docker  

docker -v 
버전 확인 ( 1.13.1 build b2f74b2/) 

sudo service docker start 
( 도커 시작 ) 

sudo chkconfig docker on 
(부팅 시 자동실행)  

버전명 보는 법 cat /etc/*-release 

curl -fsSL https://get.docker.com/ | sudo sh 
도커 설치  


자 여기까지 Docker 설치까지 완료 되었습니다.

다음포스팅에서는 간단하게 Node.js 를 Docker를 통해서 설치 해보도록 하겠습니다.

 

안녕하세요 Selenium - Web Driver ( Node.js Version ) 에서 SendKeys 에 대해서 간단하게 알아 보려고 합니다.

 

처음에 SendKeys 에 대해서 사용하게 되는 경우 대부분  자동화 도중에 어떠한 값을 "입력" 하게 되는 경우에

 

사용하게 되면서 시작 하게 될 것 같습니다.

 

// DOM select targetElement
let targetElement = driver.findElement(By.css('div .target'));

// targetElement input String 
await targetElement.sendKeys('Automation Input');

하지만 위와같은 방식 말고 다른 방법으로도 사용이 가능합니다.

 

Automation Copy and Paste with SendKeys 

// DOM select targetElement
let targetElement = driver.findElement(By.css('div .target'));

// targetElement input String 
await targetElement.sendKeys('Automation Input');

// Copy and Paste One Take
await targetElement.sendKeys(Key.CONTROL,'a'); // All Select String
await targetElement.sendKeys(Key.CONTROL,'c'); // Block String to Copy 
await targetElement.sendKeys(Key.CONTROL,'v'); // Paste

뭔가 해당 사항만 봤을 때는  'Automation Input' 이 입력되면 Automation Input 이 들어가고

앞에 Control 값이 있을때 해당 값과 키 값을 같이 누르는 것처럼 보입니다.

 

하지만 실제로는 어떻게 동작하는지 알면 왜 위와 같이 동작하는지 이해하기 편합니다.

 

위의 예시코드는 WebElement.sendKeys 였지만 실제로는 Actions 객체로도 가능한데요

이번 포스팅에서는 WebElement.SendKeys 지만 Actions의 SendKeys 도 비슷한 부분들이 많아

우선 Actions의 SendKeys  부터 간단하게 열어 보았습니다.

 

// Selenium github 

https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/lib/input.js#L572

Selenium 은 오픈소스 이다 보니까 코드 가 공개 되어있는데요

 

SendKeys 의 구현체를 발췌 해 보았습니다.

sendKeys(...keys) {
    const actions = [];
    for (const key of keys) {
      if (typeof key === 'string') {
        for (const symbol of key) {
          actions.push(
              this.keyboard_.keyDown(symbol),
              this.keyboard_.keyUp(symbol));
        }
      } else {
        actions.push(
            this.keyboard_.keyDown(key),
            this.keyboard_.keyUp(key));
      }
    }
    return this.insert(this.keyboard_, ...actions);
  }

1. sendKeys(...keys) -> ...keys 로 spread 연산자 입니다. 들어오는 모든 키들을 파라미터로 받습니다.

   이부분이 이해가 안될수 있는게 

function sendKeys(keys){
	console.log(keys);  // automation
}
sendKeys('automation');

이와같은 형태 아닌가? 라고 잠깐 착각 할수 있습니다. 하지만 Spread Operator 를 사용하게되면 조금 이야기가 달라 질 수 있습니다.

function sendKeys(...keys){
	console.log(keys); // ['automation']
}
sendKeys('automation');

위와의 차이점은 배열(Array) 로 들어온다는 점입니다. 이 뜻은  

A, automation 과같이 입력이 된다는 뜻이 됩니다.

function sendKeys(...keys){
	console.log(keys); // ['CONTROL_A','automation']
}
sendKeys('CONTROL_A','automation');

미리 정의해둔 컨트롤(Control) 이나 쉬프트 (Shift) 값이 들어왔을때 조합키 형태로 사용할 수 있도록 

 

여기에서 잠깐 보고 가야 할 부분이 위에 Key.CONTROL 은 lib/input 에 선언이 되어있기 때문에 사용하기 위해서

 

코드의 최상단에

const { Key } = require('selenium-webdriver/lib/input');

로 가져오신 후 사용하시면 됩니다.

/lib/input 선언부

위와같이 되어있기 때문에 사용하고 싶은 부분들을 사용하시면 됩니다.

 

이제 SendKeys 에서 입력 키들을 배열로 받을수 있도록 처리가 되어 있습니다. 

 

간단하게 CONTROL 만 선언해서 돌려 본다면

 

var Key = {
	CONTROL : '\uE009'
}
function sendKeys(...keys){
   const actions = [];
	 for(const key of keys){
		if(typeof key==='string'){
	     	for(const symbol of key){
				console.log(`symbol ${symbol}`);
            }
        }
		else{
		   console.log(`not Symbol ${key}`);
        }
    }
}

sendKeys(Key.CONTROL,'automation');

테스트 결과

보시면 CONTROL 부분은 심볼로 처리되고 그 외에 있는 문자열들은 따로따로 나눠서 되어있습니다.

미리 선언해놓은 심볼들은 내부적으로 다시 연결하여서 그에 맞는 조합키를 입력 합니다. (해당 내용은 이번 포스팅에서는 제외하고 하겠습니다.) 

 

그리고 해당 값들을 통해서 KeyDown 과 KeyUp을 사용하여 진행합니다.

 

"Automation" 이 파라미터로 넘어간다면

 

A Key Down 

A Key Up

u Key Down

u Key Up 

.... 이 순서로 진행이 된다고 볼수 있습니다.

 

그래서 테스트 해 보았습니다. 

 // 첫번째 요소에 Shift 키와 함께 
 await targetElement.sendKeys(Key.SHIFT,'automation');
 // Result : AUTOMATION
 
 // 두번째 요소에 소문자 입력시 
 await subtargetElement.sendKeys('automation');
 // Result : automation
 

위처럼 현재 Element 에 조합키 + 소문자 입력후 다른 Element 에 automation 만 입력시 어떻게 될지 확인해보았을 시

 

입력중인 위젯에 대해서는 Shift키 심볼로 인해 keyDOWN 상태가 유지되어 대문자로 나오지만

 

새롭게 지정된 요소에서는 해당 키 내용이 없기 때문에 소문자가 그대로 나타납니다.

 this.sequences_ = new Map([
      [this.keyboard_, []],
      [this.mouse_, []],
    ]);
	
    insert(device, ...actions) {
    this.sequence_(device).push(...actions);
    return this.sync_ ? this.synchronize() : this;
  }

위의 sendKeys 마지막 부분 요소에 보면 insert하는 부분이 있게 되는데요 위의 코드는 Selenium 자체코드입니다.

 

Actions.SendKeys 의 경우 관련된 액션과 키 값들을 정리 후 한꺼번에 리턴합니다.  

 

Actions의 경우 perform 이 호출될시 리턴된 액션들을 순차적으로 실행하는 형태이기 때문에 그때 순차적으로 실행됩니다.

 

WebElement의 SendKeys 의 경우

 

async sendKeys(...args) {
    let keys = [];
    (await Promise.all(args)).forEach(key => {
      let type = typeof key;
      if (type === 'number') {
        key = String(key);
      } else if (type !== 'string') {
        throw TypeError('each key must be a number of string; got ' + type);
      }

      // The W3C protocol requires keys to be specified as an array where
      // each element is a single key.
      keys.push(...key.split(''));
    });

    if (!this.driver_.fileDetector_) {
      return this.execute_(
          new command.Command(command.Name.SEND_KEYS_TO_ELEMENT)
              .setParameter('text', keys.join(''))
              .setParameter('value', keys));
    }

    keys =
        await this.driver_.fileDetector_.handleFile(
            this.driver_, keys.join(''));
    return this.execute_(
        new command.Command(command.Name.SEND_KEYS_TO_ELEMENT)
            .setParameter('text', keys)
            .setParameter('value', keys.split('')));
  }

위와 같은 형태를 띄고있는데요 Actions 과 유사한점이 있지만 다른점이 있다면 바로 Command 를 통해서 실행한다는점입니다.

눈에 띄는 것은 맨 첫줄에 Promise all 부분인데요 들어오는 키 형태중에 조합형태로 Promise 를 리턴하는 경우가 있을 경우에 대비해서 만들어 놓은 것으로 보입니다.

 

위에서 Shift를 눌러놓은 상태에서 automation 시 AUTOMATION 으로 나온후 타 요소에 갈땐 다시 SHIFT가 없는 상태였는데요

 

한 요소에서 같이 될 경우

        await targetElement.sendKeys(Key.SHIFT,'automation');  // AUTOMATION
        await targetElement.sendKeys('ppap'); // ppap

위와같이 보시면 Commander에서 실행 후 다시 초기화가 되는형태로 보여집니다.

 

마지막 궁금했던 테스트입니다.

 await targetElement.sendKeys(Key.CONTROL,'a',Key.CONTROL,'c',Key.CONTROL,'v','v');
 
 await targetElement.sendKeys(Key.CONTROL,'a','c','v','v');
 

그럼 한줄로 그냥 할수 있을거같은데 어떻게 동작하나 보았습니다. 

실제로 이번에 포스팅한 계기는 복사 붙여넣기 자동화 케이스를 만들다가 작성하게 된 경우입니다.

그렇기 때문에 on-copy 와 on-paste 의 이벤트가 Callback 되는지에 중점을 두게 되는데

 

위의 2케이스 전부 콜백은 동작합니다.  v를 한번 더 넣은 경우를 붙여넣기가 블록상태에선 블록만 해제되니까 테스트용으로 두번 넣었습니다. (붙여넣기 확인용)

 

1번 케이스의 경우 결과가 c 로만 나타나게 됩니다.

Ctrl 누르고 a -> 전체 블록 지정    ( 중요하게 보아야 할점은 KeyDOWN  이라는 점 )

다시 Ctrl 누르고 c  ( 한번 더 눌렸기 때문에 keyDOWN Ctrl 이 UP이 되고 c만 입력 )

다시 Ctrl 누르고 v  ( 다시 누르면서 붙여넣기 )

 

결과 : 몇몇 Text 관련 Element 에서는 focus를 주고 난 후에 입력이 되는 경우가 있어서 저는 위처럼 focus를 주고 입력합니다.   

 

긴글 읽어주셔서 감사합니다!!

  // focus in Textarea
        await this.driver.executeScript('arguments[0].focus();',targetElement);
        await targetElement.sendKeys(Key.CONTROL,'a','c','v');

 

우선 TypeScript 백문이 불 여일 타 라고 docs가 너무 잘되어있는 사이트가 있어서 한번 다 입력하면서 이것저것 내용들을 작성해볼까 합니다. 기존에 바닐라 JS도 TS로 이어서.. 진행하려고 합니다. 

 

앞으로 작성할 Type Script 일지에 관련된 URL을 2가지만 적고 가려고합니다.

 

// TypeScript Documentation korea Language 

https://typescript-kr.github.io/pages/tutorials/TypeScript%20in%205%20minutes.html

 

TypeScript 한글 문서

TypeScript 한글 번역 문서입니다

typescript-kr.github.io

 

TypeScript 설치 

Node.js와 NPM 이 설치되어있다는 가정하에 다음 명령어로 설치합니다.

npm i -g typescript

TypeScript 설치 확인 

npm ls -g --depth=0

TypeScript @3.4.5 가 확인됩니다.

자 설치가 확인됐으면 Lint를 설치하러 갑니다.

사실 Lint를 없어도 TypeScript는 tsc라는 명령어를 통해서 컴파일을 하게 되는데 이 부분에서 에러가 있다면 알아서 다 잡아줍니다. (에러가 있다고 컴파일이 안되진 않습니다. 경고를 통해서 알려줄 뿐)

 

test.tsc 파일을 생성하고 아래와 같은 코드를 입력합시다. 

function greeter(person:string){
	return 'hello, ' + person;
}

greeter(1);

와 같이 적었을 경우 에러를 vsc자체에서는 보여주지 않습니다. 하지만 tsc 명령어를 통해서 빌드하게 되면 아래와 같이 에러 부분을 보여주게 됩니다.

 

아래의 명령어를 입력하게 되면 TypeScript 가 컴파일을 해주게 됩니다.

tsc test.ts

에러는 났지만 정상적으로 컴파일이 된 상태 

이외 기본적인 튜토리얼들은 TypeScript 공식을 참조 

 

하지만 언제나 많은 코드들을 매번 컴파일하는 거보다 에디터에서 미리 볼 수 있으면 좋기 때문에 Visual Studio Code에 확장으로 TSLint를 설치합니다.

 

TSlint 

Ctrl + Shift + X (입력)

가장 다운로드 많은 것을 설치하자!(deprecated 는 제외)

이제 위의 greeter를 다시 가보면 아래 사진과 같이 잘못된 부분을 잡아 주는 효과를 볼 수 있습니다.

 

TSLint의 효과!!

사실 Lint는 해당 내용보다는 autofix라고 해서 자동으로 값이나 필요한 구문들을 도와주는 기능이 핵심이라고 볼 수 있습니다. (C 나 JAVA에서 Assist라고도 하는)

 

tslint 확장을 제어하기 위해서 tslint 모듈도 설치합니다.

 

npm i -g tslint

tslint --init

tslint.json 에 있는 설정을 통해서 여러 가지 룰이나 룰에 적용되는 위치 등을 설정할 수 있습니다. (tslint.json이나 tsconfig.json 은 경험을 쌓다가 한 번에 정리하도록 하겠습니다.)

 

tslint 사용 전에 eslint와 문제로 인해서 vsc에서 수정하여야 할 부분이 있습니다.

settings 관련 내용

https://code.visualstudio.com/docs/getstarted/settings

 

Ctrl + Shift + P  -> settings 입력 - > 기본 설정 : 설정 열기 (JSON)

 

설정 파일(settings.json)에 아래 값을 입력합니다.

 "editor.formatOnSave": false,
  "tslint.autoFixOnSave": true, 

Unknown configuration setting 이 뜨게 되는 경우가 있는데요 

 

네... deprecated 됐다고 합니다.  프리티어로 변경합시다.

https://marketplace.visualstudio.com/items?itemName=eg2.tslint

 

TSLint (deprecated) - Visual Studio Marketplace

vscode-tslint (deprecated) Note: This extension has been deprecated in favor of the vscode-typescript-tslint-plugin. To learn about the differences between vscode-tslint and the new extension please refer to this document. Integrates the tslint linter for

marketplace.visualstudio.com

npm uninstall -g tslint 

제거 

 

VSCode Prettier Configuration( 프리티어 세팅 )

 

1. Prettier VSC에서 설치 

2. Ctrl + Shift + P  -> settings -> 기본 설정 (JSON)

"editor.formatOnSave": true 입력

3. npm 프리티어 설치 

npm i tslint-config-prettier

4. tslint.json 수정

{
  "defaultSeverity": "error",
  "extends": ["tslint:latest", "tslint-config-prettier"],
  "jsRules": {},
  "rules": {
    "semicolon": true
  },
  "rulesDirectory": []
}

5. test.js 작성 

let test = "test" 

6. 저장 시 자동으로 ; 가 붙는 현상 확인 

 

 

tslint.json rules

https://palantir.github.io/tslint/rules/

tsconfig.json 

https://www.typescriptlang.org/docs/handbook/tsconfig-json.html

tsconfig Schema 타입 스크립트 스키마 파일입니다.

http://json.schemastore.org/tsconfig

 


tsconfig.json

TypeScript에서 사용되는 환경 설정이라고 보시면 됩니다. 컴파일 옵션 (webpack 유사)

해당 파일을 생성하기 위해서 아래의 명령어를 입력합니다.

tsc --init

tsconfig.json

위의 옵션들은 너무 설명들이 많은데 간단하게 옆에 주석처리는 되어있습니다.

그리고 또 하나 문제점이 같은 위치에 ts와 js가 나눠져 있는데  코드를 작성하는 부분과 빌드된 부분들을 나눠야 할 것으로 보입니다.

 

결론은 TypeScript 세팅을 위해서

 

1. VSC에서 프리티어 확장을 설치 

2. npm에서 TypeScript (글로벌)와  tslint-config-prettier를 설치 후 vscode settings.json 에 

              "editor.formatOnSave": true, 추가 

3. tsc--init으로 tsconfig.json 생성 tslint.json으로 기본 룰 생성 

2. npm i prettier 설치

3. package.json 내에 prettier key 설정

package.json

 

  "prettier": {
    "printWidth": 80,
    "useTabs": false,
    "tabWidth": 2,
    "bracketSpacing": true,
    "semi": true,
    "singleQuote": false
  }
}

 

4. editor.formatOnSave true 설정

 

TypeScript 시작 전 환경 설정 완료

 

** 추가 사항  저는 사실 TypeScript 만 프리티어가 적용됐으면 하는데  기존에 사용하던 js들도 적용이 되서

아래와 같이 기본 formatOnSave는 true로 놓고  javascript 만 false하였습니다. 

 

 "editor.formatOnSave": true,
  "[javascript]": {
    "editor.formatOnSave": false
  }

위 방법 또는

 "editor.formatOnSave": false
"[typescript]": {
    "editor.formatOnSave": true,
  },

 

 

Jenkins로 빌드 후 배포 

Jenkins로 서버 내부에서 빌드를 완료하게 되면 그에 대한 산출물이 남습니다.

저 같은 경우는 웹 프로젝트가 남게 되는데요.  *. jar 파일 또는 webApplication/ 형태의 폴더가 남게 됩니다.

하지만 빌드 서버에서는 의미가 없게 되는데  WAS(JEUS) 로 디플로이 된 서버는 다른 곳에 있기 때문입니다.

 

그래서 Jenkins로 빌드를 하고 남은 산출물을 빌드 서버로 배포를 해주어야 합니다. 해당 작업도

우리 Jenkins가 도와주는데요 SSH plugin 을 이용하면 너무 쉽게 해당 작업을 하실수 있습니다.

 

Publish Over ssh

해당 plugin 을 설치 하여야 합니다.

 

우선 Jenkins 홈에서 Jenkins 관리를 들어갑니다.

 

Jenkins 홈 - Jenkins 관리 

 

Jenkins 관리의 홈 - 플러그인 관리 로 이동 

 

 

Publish Over SSH 플러그인

 

플러그인을 설치 완료 하였다면 Jenkins 관리 -> 시스템 설정 -> Publish Over SSH로 이동합니다.

 

정상적으로 플러그인이 설치가 되었다면 시스템 설정에서 over SSH 로 검색 시 아래와 같은 상태를 확인하실 수 있습니다.

 

시스템 설정(System Configuration) 에서 Publish over SSH plugin 확인

이 부분에서는 어디로 배포할지 해당 서버에 대한 정보를 적어 놓는 데요 

만약에 A서버와 B서버가 있다면 2개를 추가해야 됩니다.

 

SSH 키 이용시 

  • Passphrase : SSH 연결 시 암호를 사용하고 있다면 암호를 설정합니다.
  • Path to Key : SSH 접속시 ssh key를 관련하여서 접속할 수도 있는데 요 이 SSH 키 를 이용하여 로그인 시 개인키 파일의 경로를 설정합니다.
  • Key : Path to key 와 같은데 키가 text file로 존재하게 될 경우 이 부분에 입력합니다.
  • Disable exec : 체크아웃 SSH 명령을 사용하지 않습니다.

SSH 키 이용하지 않을 경우 

  • Name : SSH 연결 시  (젠킨스 빌드 시에 어떤 서버인지 확인하기 위한 ) 이름을 설정합니다. 

Name 설정 화면 

위와 같이 설정하였다면 젠킨스 빌드 시에는 아래와 같이 메뉴에 이름이 표시됩니다. 

 

Jenkins Build 에서 설정 

  • HostName : SSH로 연결하는 실제 서버의 IP 나 도메인 이름을 지정합니다. (실제 보내야 할 서버 )
  • UserName : SSH 로 연결하는 서버의 사용자 계정 입력 ( IP와 포트나 도메인으로 접속해도 계정명을 아야 하기 때문)
  • Remote Directory : 원격 서버에 로그인한 후에 초기에 어디로 이동할지 선택 (* 필수로 있어야 하며 이 부분은 배포될 경로입니다.) * 저 같은 경우는 /home/계정명/publish로 지정하였습니다. 

Send build artifacts over SSH

이렇게 설정한 후에 Jenkins의 Item에서 빌드를 작업 중  빌드 후 조치  

 

탭에서 send build artifacts over SSH를 선택합니다.

Send build artifacts over SSH 설정

 

  • SSH Server : Jenkins System Configuration(시스템 설정)에서 설정하였던 서버(배포할 서버)의 Name을 선택합니다.

 

  • Source files : 위에 설정한 SSH Server 에보낼 파일을 설정합니다. 저는 gsAuth.war로 직접 지정하였지만 ant 패턴으로 **/*. jar 와같이 jar파일을 전부 보내는 등의 방법이 가능합니다. (여러 개의 파일을 배포할 경우)

 

  • Remove prefix : 배포되는 서버에 생성해서는 안 되는 파일 경로의 prefix 부분으로 현재 작업하고 있는 위치가 test/folder/gsAuth.war인데 단순히 gsAuth 로만 배포가 하고 싶을 경우 test/folder를 입력하게 되면 앞에 부분이 제거된다.

 

  • Remote directory :  파일이 업로드되는 경로인데 Jenkins System Configuration에서 설정한 Remote Directory 가 기준이 된다. /home/계정명/publish 였기 때문에 이 부분에 libs를 적게 되면 /home/계정명/publish/libs 가 최종경로가 된다.

 

  • Exec command : 현재 서버에서 실행될 커맨드 라인을 실행합니다. 위의 사진에서는 echo로 테스트하였지만 실제 사용 시에는 touch 나 >>> 을 이용하여서 배포된 날짜 및 정보를 서버에 적어 놓습니다. 

 

이렇게 세팅이 완료되면 간단하게 원하는 파일을 빌드 후 배포를 할 수 있을 것으로 보입니다.

 

추가적으로 확인해야 할 부분이 있다면

 

Test Configuration으로 테스트 시 위와 같이 나오게 될 수가 있습니다. SSH 키를 사용하지 않고 확인하기 때문인데요

고급 탭을 눌러 줍니다.

Use password authentifaction, or use a differecnt key를 체크한 후에 해당 서버의 계정에 대한 비밀번호를 입력 후

Test Configuration 하게 되면

 

Success 화면

해당 화면을 확인하실 수 있습니다.

 

앞으로 하나씩 더 추가해서 더 좋은 CI CD 환경을 구성할 수 있도록 해봅시다.

create-react-app (cra)가 v2.0가 된지 벌써 몇달이 됐죠 곧 3.0으로 간다고 하는데 현재는 v2.0 버전을 쓰고 있으니까요~


eject는 프로젝트 막바지에 cra버전은 그대로 fixed하고 eject로 따로 포크떠서 진행할 예정인데요


그전에 sass 모듈이 cra 2.0에서는 기본 지원이여서 너무 편했지만!




import styles from './RecentPostWrapper.scss';


그냥 이렇게 ! 모듈을 불러오면 만사 OK!!


하지만 기본적으로 node-sass는 설치 해주어야 합니다!


"dependencies": {
"classnames": "^2.2.6",
"cross-env": "^5.2.0",
"include-media": "^1.4.9",
"node-sass": "^4.11.0",
"query-string": "^6.3.0",
"react": "^16.8.3",
"react-dom": "^16.8.3",
"react-redux": "^6.0.1",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"react-scripts": "^2.1.8",
"redux": "^4.0.1",
"redux-saga": "^1.0.2",
"redux-thunk": "^2.3.0"
},


현재 저는 윈도우에서 사용하고있는데 일단 컴포넌트들에 대한 path 를 잡기 위해서


cross-env를 사용하고 있습니다.


cross-env를 설치 한 후에! package.json 에 start script 에 


"start": "cross-env NODE_PATH=src react-scripts start",


이와같이 cross-env를 통하여서 src를 루트로 잡아 주게 됩니다.



보시면 PageTemplate.js 에서 호출하고 있지만 Blog Header를 components/common/BlogHeader로 호출하고 있습니다.


cross-env가 없었다면  ../BlogHeader로 호출했을 것 같습니다. 더 짧아서 좋은거 아니냐! 할수 있지만  


같은 경로에 있기 때문에 그렇지 다른 경로에 있다면 루트 경로가 지정되어있지 않은건 상당한 불편함과 개발 진행이 늦어질수 있습니다.


자 그렇다면 Style을 사용하기위해 SASS를 쓰는데 이에 대한 util.scss는 어떻게 될까요?




@import '../../../styles/utils.scss';

 


???????     ../ ../ ../ ../ 이게 도대체 무엇인가요!!!! 컴포넌트도 이처럼 


@import 'style/utils.scss';


 이렇게 하고싶은걸요..


그러는 와중 CRA공식 문서를 보다보니 이런 글이..




NODE_PATH 처럼 node-sass는 SASS_PATH 를 지원한다고????? ../../ 너무 거슬렸는데;;;


(** 여기서 유의 저는 cra1.0에서 이것저것 해보다가 2.0으로 넘어왔다)


.env 파일을 수정하라고 되어있지만   윈도우에서 수정 부분을 이 아닌 것으로 보여 


// @import '../../../styles/utils.scss';
@import 'styles/utils.scss';


그냥 수정하니 .. 지원된다... 그냥 되는거였습니다... 공식홈페이지 자주보겠습니다.


@import '../PageTemplate/PageTemplate.scss';


확실한지 확인하기 위해서 해당 scss를 수정하겠습니다.


// @import '../PageTemplate/PageTemplate.scss';
@import 'components/common/PageTemplate/PageTemplate.scss';


음.. 잘되네요


** cra2초반버전에서는 안되는것으로 보여서


공식 홈 다시 보니! 2.1.2 밑으로 쭈욱 내리다 보면




!!!!!!!!! SASS_PATH???





#5917을 따라 들어가 보니


그렇다 추가 된 것이였다.

https://github.com/facebook/create-react-app/pull/5917




공식 홈을 자주보는 것을 생활화 합시다!!!


https://facebook.github.io/create-react-app/docs/adding-a-sass-stylesheet








안녕하세요 최근에 알게된 코딜리티 사이트에서 가입하고 나면 하는 튜토리얼 알고리즘이 있는 것 같아


기록 차 남깁니다!! 



** 문제의 경우는 사이트에 들어가시면 좀 더 자세히 확인이 가능 합니다!!


// 코딜리티 사이트 

https://app.codility.com


1 Binary Gap


정수인 N에 대하여 바이너리 갭을 구하는 1번째 튜토리얼 입니다. 제로 (0) 의 값이 제일 많은 수를 구하면됩니다.

1과 1사이에 대해서만 


만약에 10000100 이라면  10000100 이 부분이 더 많으니까 리턴 은 4가 되어야 합니다.


하지만 1000000000 으로 된다면 다음 1이 없기 때문에 사이값을 지정할수 없어 리턴은 0이 됩니다.


또 다른 케이스를 볼까요?


11111111 와같이 되어도 0이 개수가 없기 때문에 리턴은 0이 됩니다.


실행 가능한 N의 정수는


  • N is an integer within the range [1..2,147,483,647].


와 같습니다. (사실 이부분 때문에 어떻게 풀어야 하나 고민 된 부분이 하나 있습니다. : -> 2진수 변환시 )


예시로 준 값에 대해서는 샘플 결과는


1041 -> 5


32 -> 0 


등등이 있습니다.


** 참고로 해야 할 점 

JavaScript 에서는 Number.prototype.toString을 통해서 2진수로 변환할 수 있습니다.


var numberValue = 1041;

numberValue.toString(2); // "10000010001"


이 방법을 쓰기 위해서 알고리즘을 하는 건 아니니까 최대한 지양 하도록 합시다 !



[풀이]

2진수 변환 을 우선 먼저 해주어야 하는데 일단 단순하게 생각해보았을 경우는

/2 로 나누면서 몫과 나머지를 계산 하는 방식입니다. 


단순 반복문


  while(N>=1){
      // share Check
      let share = N%2;
      binArr.push(share);
      
      // rest Check and Integer
      N = Math.floor(N/2);
      
  }
binArr = binArr.reverse();


우선 N 의 값은 1까지 나누어야 하기 때문에 1보다 낮아질 경우 까지 무한반복을 합니다.

나머지 값(share) 을 찾아서 배열에 추가


JS에서는 /2를 하게되면 실수형 연산이 되기때문에 Math.floor 로 소수점을 버립니다.


위와같이 계산하게되면 2진수는 반대이기 때문에 reverse를 호출하여 이진수 형태를 만듭니다.

(** 문제점이 있다면 아래의 조건이 없었을 경우 아래조건보다 더 커지게 되면

  • N is an integer within the range [1..2,147,483,647].
12831273127382173912739012739821739081273980213 // 1.2831273127382173e+46

위와같이 나타나서 2진수 계산이 되지 않습니다. (하지만 조건이 있기에 위와같이 단순계산하는게 속도상 제일 빠릅니다.)

재귀 함수


function s(d,v){
  const share = Math.floor(v / d);
  const rest = v % d;
  
  if(d > share){
    return '' + share + rest;
}else {
    return '' + s(d,share) + rest;
}
}

s(2,1041); // "10000010001"


재귀로 하게되면 위의 N의조건이 없었을시에는 계속 가능합니다. 마지막 콜스택에서

찾아 올라오기 때문에 reverse도 할 필요 없구요 (하지만 속도가 좀더 느리더군요)




갭 구하기 


갭을 구해야 합니다. 조건을 나름 정해 보았습니다.

- 1 다음부터 카운트를 해야 하고 다시 1을 만났을때 그사이에 0 의 값에 대한 카운트는 Fixed가 된다.

(Fixed시 기존에 카운트 값이 있다면 크기 비교 후 더 큰 쪽을 Fixed한다.)

- 1 다음부터 카운트를 하였는데 1이 다시 안나온다면 값을 0으로 Fixed 된다.

- 1 만 나왔을 경우 값은 계속 0을 유지한다. 

- 1이 나오게 되면 진행 중이라는 플래그 값이 필요 할 것 같다.(running)


Binary Gap 은 간단하기에 위와같은 풀이식을 적용한 솔루션 적어 놓겠습니다.



// you can write to stdout for debugging purposes, e.g.
// console.log('this is a debug message');

function solution(N) {
  // write your code in JavaScript (Node.js 8.9.4)
  let binArr = [];
  
  while(N>=1){
      // share Check
      let share = N%2;
      binArr.push(share);
      
      // rest Check and Integer
      N = Math.floor(N/2);
      
  }
  // binary Reverse
  binArr = binArr.reverse();
  // gap Max Value
  let currGapMax = 0 ;
  let running = false;
  
  // 1 1 0 0 0 0 0 0
  binArr.reduce(function(prev,curr,index,arr){
      // check Starting
      if(curr===1 && running ===true){
          currGapMax = currGapMax < prev ? prev : currGapMax;
          prev = 0 ;
          return prev;
      }
      // check Counting
      else if(running === true && curr===0){
          prev+=1;
          return prev;
      }
      else if(curr===1){
          running = true;
          return prev;
      }
  },0);
  
  
  return currGapMax;
  
}


조건이 단순 양수 값이였다면 재귀 형태를 사용 했을 것 같습니다.

(JS의 toString(2) 도 그런 것으로 보입니다.)

N의 조건을 두었기 때문에 위와같이 단순 계산을 사용하였습니다.


감사합니다.



안녕하세요 깍돌이 입니당 


최근 너무 바쁘다는 핑계(?) 로 포스팅을 못하고 있습니다. 물론 진짜 바쁘기도하구..  


요즘에 영어를 시작했어요 토이 프로젝트도 시작했구요 QA와는 별개로 개발적인 자기개발을 위해서 였습니다.


포스팅 할 주제는 정말 많이 찾아 놨는데(평소에 궁금했던 것들 ) 


늦게까지 일하구 퇴근해서 영어 강의 듣고 


토이 프로젝트 (React 기반) 를 하려다보니 진짜 전혀 시간이 평일에 나지 않습니다. ㅜㅜ


ML도 해보고싶고 저것도 해보고싶고 그러고 싶은데 일단 눈앞에 해결해야될 일이 있기 때문입니다.(FE 자기개발)


벌써 5년찬데 나이도 이제 계란 한판이고 푸념은 아니고 어떻게 살아왔나에 대한 글을 남기고 싶어서 이렇게 


지금 남기게 됐습니다. 지금 남겨야 잘 남길수 있을 것 같아서 ㅎㅎㅎㅎ



학부생 시절 



많은 시간이 흘렀네요  26살에 졸업하자마자 구직 활동을 했고  학사 시절엔 나름 이것저것 다해봤었는데


C,C++,C# 배울때는  Visual Studio 에 있는 spy 툴(현재 최신버전엔 없습니다.) 도 만들어보고 이외에도


테트리스, 그림판(윈7 , 윈xp), 작업관리자, 캐치마인드, Battle City (2D 탱크게임), 자료구조 공부한다고


코레일 예약 시스템도 해보고 Kinect for Developer로 모션인식(동상) 도 만들어보고 


JAVA 때는 위에 만들어봤던거 훑어보고(자바로 다시 제작)


자전거 네비게이션도 만들고 교내에서 상도 받아 보고 (금상)


마지막 학기때는 SpringFramework 로 쇼핑몰 만들고 졸작도 하고 이것 저것 참 많이 만들었습니다.


고등학교때는 공부를 잘안했는데요 머리가 나쁘기 보다는 그냥 흥미가 없었던거 같아요 


(학교에선 평균점수 밑으로 매 타작을 했는데 해당 과목은 애초에 안맞으려고 90가까이 맞았었거든요)


대학교 가서 컴퓨터공학과를 전공하면서 처음으로 코드 짜느라고 30시간도 안자보고 빌드된 바이너리 실행시켜서 제출


할때 는 그렇게 세상을 다 가진것 같더군요  그러다 졸업 시기가 왔습니다. 이것저것 만들어 본게 많긴했는데


주력으로 했던것이 없던 상태였었죠 그래서 마지막 웹프로젝트를 해서 그런지 학교 선배 및 동기들은 웹 SI를 하러 다 


떠났죠 학교 인프라가 좋지 않아 대부분 작은기업에서 SI를 시작했고 저도 그럴거라 생각 했는데 


저에게 코딩을 가르쳐 주셨던 은사님께서 회사는 좀 큰곳이지만 QA로 가더라도 갔으면 좋겠다 라고 하셔서 현재 회사에


몸 담게 되었네요. 



QA 회사 입사 



입사 후 4년이 지났습니다.  앞선 1년 반은  웹쪽이 아니였지만요 


C++ 쪽  API 쪽 unit Test  사실 이때까지 만 해도 C++ 은 누구보다 자신있었는데 .. 지금은 많이 잊어버렸네요  


(표준도 점점 나오고있던데 ... (C++17))


그러던 어느날  사내에서 web-front-end-FrameWork 제품이 사내에서 개발되고 맡을 


QA를 사내에서 물색 하는 도중 저에게 문의가 왔습니다.



"우리팀에서 그나마 코딩에 대한 거는 너가 잘 할수있을 것 같아 내 생각엔 너가 했으면 좋겠어 "



맡기전 날 들은 이야기였어요  QA로 오면서 개발에 대한 열망은 계속 있었는데 1년간 거의 Unit Test 나 UI 위주의 테스


트 를 하던 저에게 눈이 떠지는 이야기였죠 당연히 하겠다고 했습니다.  사실 C++쪽 UI 도 자동화 하려구 openCV 관련


서적들도 사놓고 준비하긴 했는데 나중에 봐야지 라는 심정으로요


또한 팀장님 뿐 아니라 여러 직원들도 관련된 일은 저한테 질문하거나 저에게 일이 배정되거나 가 시작되었습니다.


그런데 해당 프레임워크에서 놀라운 점이 발견되었죠


SPA? , Router ? , Controller ? HTML5 ? CSR? 


SPA기반??? 학부생 시절 SpringFramework 밖에 몰랐던 저는 서버가 없이 화면을 만든다는거에 이해가되지않았고


web-front-end의 트렌드를 읽지 못했죠 처음엔 이렇게 쓰는거구나 이렇게 나와야되는구나 이렇게 동작해야 되는구나 로


시작했습니다. 남들과 같이..


QA 의 모토 설정 


남들과 같은 건 오래 가지 않았습니다. 좀 더 책임감 있는 QA 를 하고싶었고 제가 맡은 제품을 누군가 받아서 개발을 


한다는 책임감이 생겼기 때문이죠.  "남들은 필요한 기능만 쓰지만 나는 다 할줄 알아야겠다" QA 할 때 제 모토였습니다.


있는 기능 만들어진 기능들을 다 사용해 볼수 있어야 한다고 생각했고 프레임워크 뿐 아니라 브라우저에 대한 지식도


필요하다고 생각했죠. 개발자들 따라다니면서 수도없이 질문했습니다. 왜? 이렇게 만들었는가 왜? 왜


(물론 컨피던스같은 음료라도 한잔 주면서요 ㅎㅎ)


QA를 하기위해서 TC관리 Suite관리도 중요하고 배포 프로세스를 확립하는것도 중요합니다. 그전에 앞서 제품을 이해


하지 못한다면 이슈 분석이나 관리가 될까요 ?


일단 책부터 사서 읽기 시작했습니다.


인사이드 자바스크립트,모던 자바스크립트, Do it node.js ,MDN, W3C, HTTP 완벽 가이드,

node.js 교과서


사용하는 개발자들이 생겨나면서 같이 고민하여야 할 부분이 들어났고 프론트 관련 개인공부가 진행되다보니


프론트가 재밌더군요 3년 내내 부사수없이 사수없이 일했지만 재밌게 일했던거 같아요.


그리고 QA에 하면서 도달은 곳이 있죠 "자동화" 웹이라는 플랫폼이 제가 생각하기에 자동화가 어렵지 않은 부분이라고


생각 했고 단순한 몇개의 케이스만 하는 것은 생각보다 어렵진 않았습니다. 하지만 많은 케이스들에 대한 자동화가 


되어야 하고 이에 대한 리포팅까지 이루어짐을 고려하다 보니 디자인 패턴 및 자료구조도 자동적으로 공부하게 되더구


최근에는 거의 도달했다고 생각됩니다. (IE11쪽 트러블 슈팅만 좀 ... )


이것저것 새롭게 해보는 것을 좋아하는 편이라 


git을 써서 자동화 코드도 사내에서 관리하고 프레임워크로 만드는 코드도 관리가 필요했으면 해서 git도 쓰고


배포 도 자동화 해보고 싶어 jenkins도 사용하구요


개인 github도 관리하고싶은 욕심이 생겨 github에 잔디도 심고있습니다. ㅎㅎ 


https://github.com/lgance


몇년만에 저의 일을 대신해줄 친구들이 들어왔습니다. 저 혼자 달려왔던 고독했던(?) 길을 이 친구들은 좀더


편하게 달려갔으면 하는 바람이 있어 하나부터 열까지 다 알려주려고 하고있습니다.


부사수도 없고 사수도 없었기에 누군가에게 설명할 기회도 없었고 기술적인 이야기 도 나눌 기회가 없었습니다.


(이건 정말 하고싶었던건데요 ) 


코드리뷰도 마찬가지고 혼자만의 코드로 있어 오픈소스 코드들을 보며 적지않은 충격을  받았고 


현재는 점점 나아지고있다고 생각 합니다.


NAVER Boost Course


최근에는 NAVER Boost Course 에서 Front-End 코드 리뷰어로도 활동하고 있습니다.(이정돈 적어도 되겠죠?)

(다른 FE리뷰어분들이 리뷰 프로젝트가 뜨면 바로 가져가서 많이는 못하고있지만요 )


(NAVER Boost Course 의 코스 설계가 정말 좋습니다. 한번 가서 기본기를 다지는 것 도 좋아요 강의는 무료!)

https://www.edwith.org/boost-course/intro



수강생들에게 제가 알고있는 모든 것들에 대해서 한줄한줄 리뷰를 적을 때 마다 매우 기분이 좋았는데 


고마웠다는 답글 까지 받으니 매우 뿌듯 하더군요 하길 잘했다는 생각도 들었습니다. 보람찬 일이에요


후계자(?) 분들이 오시니 이제 다시 새로운 것에 대한 열망이 생기더군요  


이제는 QA가 아니라 web-front-end Developer 로써 직접 만들어서 운영 했으면 한다 라는


생각이 요즘에 들기 시작했습니다.  ( 그래서 사이드 프로젝트를 시작했습니다. )



더 많은 것을 경험하고 더 많은 문제와 직면 할수 있을 것 같기 때문입니다.


세상에 못할 것 없고 안되는 것 없습니다. 현재까지 살아오면서 한번도 쉽게 된적 없었고


앞으로도 그렇지 않을거라 생각 합니다. 



터닝포인트를 가지기 위해서 저를 한번 소개 해볼까 했는데 


지금회사가 첫 이력서 였고 입사를 해서 쭉 일하다 보니 저를 다시 소개할 기회가 없었습니다.


최근에 한번 영문으로 자기소개서를 작성해야 할 기회가 있어서 작성 하게되었는데


자기소개서를 5줄 정도로 짤막하게 작성해봤습니다. (언젠가 쓸일이 있겠죠?)


마지막에 적었던 글귀가 아직도 기억이 나서 지금 글을 작성하게 되었고  이 글에 해당 말머리는 남깁니다.



저는 어떠한 일을 하든 자부심을 가지고 일합니다. 


새로운 것에 대한 열망도 있고 물론 재미있거든요. 뭔가 시간이 갈수록  이해가 더 잘되는건 제 착각인지 모르겠지만 .ㅎ


QA업무를 하면서 R&D 와 Enginner 와 , CS 등 여러 사람들과의 


커뮤니케이션도 정말 많은 도움이 되었네요  처음에는 연락조차 힘들었는데 어느샌가 자연스러워졌습니다. ㅎㅎ


앞으로도 열심히 하겠습니다.


긴글 읽어주셔서 감사합니다.


감사합니다.


- Web-Front-End Framework QA  깍돌이 






'비공개 카테고리 및 미사용 카테고리 > 옛날 일기장' 카테고리의 다른 글

번아웃  (0) 2018.10.19
[이달의 배움] 2018년 10월  (0) 2018.10.03
[이달의 배움] 2018년 8월  (0) 2018.08.21
파이썬  (0) 2016.07.27
마우스저거 참  (0) 2016.07.20

오랜만에 브라우저 변화에 대해 글을 쓰게되는데요 !! 바로바로 Chrome Browser 72 버전 릴리즈입니다.

(글은 19년에 적고 있으니 많이 늦었네요 ㅜㅜ 실제 패치내역은 2018년 11월에 나온 패치내역 입니다.)


그래도.. Stable은 현재 72 버전이니까 지금이라도 적어야 겠습니다.


해당 큰 변경사항에 대해서 목차로 먼저 말씀드리자면 아래와 같습니다.




Visualize performance metrics


Highlight text nodes


Copy the JS path


Audits panel updates





1. Visualize performance metrics 


자 뭔가 거창해 보입니다. 성능에 대한 시각화 라고 되어있는 데 내용을 보자면 Page Load에 대한 기록 후에


DevTools는 Timings 섹션에 DOMContentLoaded 및 First Meaningful Paint 와 같은 성능 측정을 표시해준다고 하네요


네이버를 통해 확인 해 보았습니다.



F12(개발자 도구창) -> Performance -> Ctrl E(시작) -> 네이버 접속 -> Ctrl E (종료) -> 


결과 화면 중앙에 Timings 에 보시면  FCP FMP DCL   L 이렇게 4가지가 보이실 겁니다.


순서대로 말씀드리자면


FCP - First Contentful Paint


FMP - First Meaningful Paint


DCL - DOMContentLoaded Event


L   - Onload Event 


에 대해서 측정을 해서 보여준다고 하네요 ! performance Monitoring 때부터 점점 개발자 들이 쓸수 있는 기능들을 만들어 줘서 너무 좋은 것 같습니다.


2. Highlight text nodes


텍스트 노드의 강조 표시?? 내용 을 보자면 DOM 트리의 텍스트 노드 위로 마우스를 올려다 놓게 되면


DevTools 에서 뷰포트의 텍스트 노드를 강조 표시한다고 하네요





지금 저의 마우스는 Dev Tools의 Element 탭에 "  Highlighting a text node ~ 에 있습니다.


자동으로 해당 부분만 #text로 잡아서 하이라이트를 해주는 기능이네요!


3. Copy the JS path


JS 경로로 복사! 라는 기능인데요 간단합니다. DevTools에는 수많은 Element 들이 나타나게 되는데요


이 Element 들에 대한 CSS Selector를 자동으로 만들어서 복사 해놓는 작업 입니다.


예시를 보시죠




저기 보시면 figcaption 이라는 태그를 통해 작성해보겠습니다.



자 이제 복사를 했으니 붙여넣기를 하면?



document.querySelector('#gc-wrapper > div.devsite-main-content.clearfix > article > article > div.devsite-article-body.clearfix > figure:nth-child(13) > figcaption')


자동으로 querySelector와 CSS Selector를 넣어 줍니다.


보기 불편하니 두줄로 바꾸겠습니다.


document.querySelector('#gc-wrapper > div.devsite-main-content.clearfix > article > '+
'article > div.devsite-article-body.clearfix > figure:nth-child(13) > figcaption')


자동화 할때 매우 유용 할것으로 기대됩니다.  -> 빠르게 복사해서 케이스 단위로 Element -> Element-> Element 로 바로바로 셀렉터를 만들수 있을 것 으로 보여집니다. (이거때문에라도 Chrome72는 포스팅을 해야만 했죠..!!)



4. Audits panel updates


패널 업데이트 검사에 대한 이야기인데요 


우선 F12(DevTools) 를 연후에 Ctrl + Shift + P 



이렇게 입력 또는 Audits을 찾아서 들어오신다음에 Run audit  


 을 하시면 됩니다.




그리고 결과를 보면 이와같은 현재 페이지에 대한 내역이 나오게 되는데요


이중 Best Pratice 에 보시면



자바스크립트 라이브러리를 탐지 하는 기능이 추가 되었습니다. 


사이트 몇개를 추가적으로 탐지 해보겠습니다.


우선 Vue.js  사이트(공식홈)에 대해서 확인해본 결과



zepto.js  와 Vue가 보이긴하네요


마지막으로 reactjs.org를 확인해보겠습니다. 어느정도 잘 탐지가 되는것 같긴하네요 



Chrome 72 버전 릴리즈였습니다.~

출근해서 코드 를 작성후에 평소와 같은 git작업




git status ~ 체크 


git add * 


git commit -m"솰라 솰라 이번주의 변경사항~"


git push origin master 


Error




??? 왜지? 잘되던 git이?


확인해보니 서버를 옮겼었습니다. 기존에 123.15 였다면 지금은 105.17 인거죠 gitlab의 경우 외부 접속을 하기 위한 


지정으로




sudo -s vi /etc/gitlab/gitlab.rb 


에 external_url 로 지정을 해놓을수가 있습니다.  이부분을 수정했습니다.

## GitLab URL

##! URL on which GitLab will be reachable.~~ 

##! For more details on configuring external_url see:

##! https://docs.gitlab.com/om~~

external_url 'http://111.111.105.17:8888'




저장 후 나가기를 위해서 ( ESC + :  > wq 입력 엔터 )  


gitlab을 사용하시는 분들은 알겠지만 db로 postgresql  rails  WAS도 nginx 등등 여러가지들을 한꺼번에 


구동시켜주어야 합니다. 이 구동을 위해서 gitlab-ctl 이라는 요녀석이 있는데 이녀석이 에 명령어로


command line 에서  gitlab-ctl reconfigure 를 입력하게되면 gitlab.rb를 시작으로 configure 파일들을 다시 읽음으로


적용과 함께 재시작 해주는 기능을 합니다.


그래서 


sudo -s gitlab-ctl reconfigure


Error  : not found command gitlab-ctl 


?? 알수 없는 명령어라니  6개월간 잘쓰던 git이 이상해졌습니다.


일단 gitlab-ctl 이 명령어가 없는거라면 환경변수(env_path) 설정이 문제일수 있으니 바이너리 먼저 찾아 보도록 합시다


find / -name 'gitlab-ctl'


result : nothing to do 


OMG!! 어떻게 된 일일까요??? (-__-) !  원인 파악이 잘되지않습니다. 해당 서버는 공용 서버(shared Server) TT 


기존에 설치된 ce버전으로 업그레이드를 시도해보았습니다.


sudo -s yum -y install gitlab-ce -> gitlab -ce upgrade


Error: nothing to do 


search sentence list


gitlab-ctl 을 찾기 위해서 여러가지 검색 조건을 찾아서 검색을 해보았습니다.


gitlab-ctl only install


git bin directory has been removed


gitlab start without gitlab-ctl 


gitlab-ctl error


gitlab-ctl not exist 등등


... etc


마지막으로 시도한  (last challenge)


gitlab-official home page에 있던  gitlab-ee upgrade입니다.


// 패키지 fork 


curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.rpm.sh | sudo bash


// gitlab EE upgrade


sudo yum install gitlab-ee -y


gitlab-ctl









이제 정상적으로 나오네요 감격 입니다. ㅜㅜ



이런일이 나도 복구 할수 있도록 백업 본을 하나 떠 놓도록 하겠습니다.


gitlab-rake gitlab:backup:create

해당 백업의 위치는 /var/opt/gitlab/backups/$timeStamp_version-eegitlab_backup.tar 로 있게 됩니다.



다시 gitlab 을 쓸수 있게 되었네요 ee로 업그레이드하는 바람에 좀더 최신의 느낌이긴 하지만요





다른 타 프로젝트들도 다행히 다 잘살아있네요







QA업무 중 TC(일명 : TestCase) 에 대한 구조를 짜는 도중


오랜만에 자료구조에 대해 고민하게 되었습니당... ㅎㅎ(사실 이런 고민거리 너무 좋은.. ㅎㅎ)


QA 업무 중 자료구조 ? 1탄

https://ipex.tistory.com/entry/QA-%EC%97%85%EB%AC%B4-%EC%A4%91-%EC%9E%90%EB%A3%8C-%EA%B5%AC%EC%A1%B0?category=666717


여기에 있는 TestCase 를 전체 순회 하는 작업을 하게 되었는데


일을 하다보니 정말 시간이 안나서 저녁먹고 7시쯤 됐나 이슈를 연구소에 다 전달하고 하니


시간이 붕 떠버렸습니다. 그래서 퇴근을 하려고했지만 해당 순회에대한 작업이 안되서 계속 신경이 쓰여버리는 바람에


시간을 내서 한번 level-순회에 대해서 조사를 해보고 실행해 보았습니다.


일단 구조는 전체 포스팅했지만 대강 이런 구조입니다.


장점이라 함은


특정위젯에 특정 카테고리에 있는 케이스를 가져올때 너무 편합니다.


예를들어


widget 에 setFocus API를 가져와야 한다.


obj.widget.api.setFocus

obj["widget"]["api"]["setFocus"]  두 방식다 사용할수 있기 때문이죠 되도록이면 

"전자"(obj.widget.api.setFocus) 의 방법을 사용하는게 좋다고 하네요!


하지만 문제 되는점은 특정 위치에서 전체 케이스를 가져올 때 입니다.


widget 에 api 를 전부 가져와야 한다!


모든 tc를 가져와야 한다.

결국은 특정 "Root"를 기준으로 Leaf Node 를 가져올수 있어야 한다는 의미가 됩니다.


다음 설명은 그림을 지나서 슈슉


자 이제 여기서 LeafNode(마지막 노드) 를 가져와야합니다. 마지막 노드! 여기서는 테스트케이스 겠네요


level - 순회 트리에서 중요한거는


(preOrder, postOrder, inOrder)전위순회,후위순회,중위순회 와는 다르게 "레벨" 별로 순회를 해야한다는 점입니다.


그렇기 때문에 등장하는 개념이 바로 큐(Queue) 입니다.



레벨 트리 순회는 위의 그림처럼 레벨별로 순회를 하여야 합니다.


A 레벨 순회 -> B레벨 순회 -> C레벨 순회 -> D레벨 순회


재귀형태로 도는 기본 순회랑은 조금 다르다고 볼수있습니다.


각 레벨별로 큐(Queue)를 두어서 해당 큐(Queue)에서 레벨을 관리 할수 있도록 하는 방법입니다.


그렇기 때문에 시나리오를 그려본다면


순회를 할 함수 ! 


리프노드인지 체크하는 함수 !


2개 정도가 필요할것 같구


순회 하는 함수에서는 처음으로 Root 노드를 파라미터로 받아 Child(자식) 값이 있을 경우 해당 내용을 큐에 넣고


큐는 다시 앞에서 부터 꺼내서 Child(자식)이 있으면 다시 그 자식들을  큐(Queue) 에 넣고 없다면 leafNode라는 배열에 


추가 해주는 방식을 사용하였습니다.


말로만 백날해봐야 백문의 불여일견이죠


코드로 보자면


1번 리프노드 체크


function isLeaf(node){
var leafKey = Object.keys(node);
var isLeaf = true;
var length = leafKey.length;

for(let i=0;i<length;i++){
if(typeof node[leafKey[i]]==='object'){
isLeaf = false;
}
}
return isLeaf;
}


생각보다 단순하죠  해당 노드의 내부 Value 들이(js Object는 key : value 로 구성됩니다.) 전부 object가 아니면 되는


케이스입니다. 사실 리프노드의 체크 유무는 원하는 상태에 따라 다를수 있기 때문에 저는 이번에 이런식으로


체크했다고 아시면 될 것 같습니다.


2번 루트 노드 순회 


function levelTree(obj){
var Queue=[];
var leafNode = [];
Queue.push(obj);
while(!__isEmpty(Queue)){
// dequeue
var root = Queue.shift();
console.log(root);
// 리프 노드가 맞다면
if(isLeaf(root)){
leafNode.push(root);
}
// 리프 노드가 아니라면 해당 자식들을 전부 큐에 추가
else{
let keyArr = Object.keys(root);
let length = keyArr.length;
for(let i=0;i<length;i++){
Queue.push(root[keyArr[i]]);
}
}
}
return leafNode;
}


사실 루트 노드 순회도 너무 별게 없다보니 이걸.. 적어야 하나 말아야 하나 고민했습니다.


순회 하기전에 Queue를 하나 만듭니다.


Q : [] 겠네요


그리고 바로 obj 를 추가합니다. 


만약 var obj = {

 "test":{},
"testb":{} 

}

이라면


Q : [obj] 가 되겠죵


그리고 while문을 돌리게 되는데 __isEmpty는 제가 따로 구현한 형태입니다. 비어있는지 아닌지만 판별해서 return 


while -> 간단하게 설명하면 Queue가 비어있으면 나가는 게 조건입니다. 


Queue생성후 push 를 해주었기 때문에 비어있지 않아 while 문으로 들어가게 됩니다.


그리고 Queue이기 때문에 js에서는 pop이 아닌 shift를 빼옵니다. 그래야 Queue의 FIFO 구조가 성립이 되기때문이죠


var obj = {
"widget":{
"api":{
"setFocus":{
"contents:":"setFocus쥬",
"tc":"WIDGET_API_SETFOCUS",
"result":"fail"
}
},
"event":{},
}
,
"widget2":{
"attr":{},
"event":{},

}
}


해당의 obj가 들어온다고 가정하고 그림을 그리면 Queue는 Q , Root는 현재 방문중인 노드로 R이라고하겠습니다.


while문 전에


Q [ obj ]

R  undefined


Q.shift 후


Q []   

R obj ("widget","widget2") 키값 2개 리프노드가 아님


Q에 추가


Q [ "widget", "widget2"]

R  -


Q.shift()


Q ["widget2"]


R  "widget"     ("api","event") 2개 있음  역시 리프노드가 아니기 때문에 push


Q에 추가 


Q ["widget2","api","event"]


R   - 


Q.shift()


Q ["api","event"]


R "widget2"   "attr","event" 2개를 가지고있으며 역시 리프노드가 아님 push


Q에 추가


Q ["api","event","attr","event"]


R


Q.shift()


Q ["event","attr",event"]


R "api"      "setFocus"를 가지고있음 리프노드가 아님


Q에 추가


Q ["event","attr","event","setFocus"]  


> 이후에 전부 하위 자식들을 추가하다가 setFocus를 만났을때


Q ["setFocus", .....] 에서 Q.shift()


Q [......] 


R "setFocus" 리프노드를 확인하면 내부값이 전부 string으로만되어있어 리프노드 true가 나타나게됨



leafNode.push(root);


체크가확인되면 리프노드에 추가


위의 방식으로 계속 순환후 함수에서는 leafNode를 return


** 실행 결과




테스트용이였지만 단순하게 {}로만 되어있는것들도 리프노드로 가져왔네요 문제 될건 아니지만 이런 경우도 나중에 다시


수정해야 할 부분이겠죠!


트리순회는 이만 마치도록 하겠습니당



전체 코드 (테스트용 코드이며 업무시에는 모듈 형태로 변환하여야 합니다.)




function __isEmpty(array){
return array.length===0 ? true : false;
}
function getShow(){
var leafArray = levelTree(obj);
console.warn(leafArray);

}

function levelTree(obj){
var Queue=[];
var leafNode = [];
Queue.push(obj);
while(!__isEmpty(Queue)){
var root = Queue.shift();
console.log(root);
if(isLeaf(root)){
leafNode.push(root);
}
else{
let keyArr = Object.keys(root);
let length = keyArr.length;
for(let i=0;i<length;i++){
Queue.push(root[keyArr[i]]);
}
}
}
return leafNode;
}
function isLeaf(node){
var isLeaf = true;

var leafKey = Object.keys(node);
var length = leafKey.length;

for(let i=0;i<length;i++){
if(typeof node[leafKey[i]]==='object'){
isLeaf = false;
}
}
return isLeaf;
}

























+ Recent posts