[nGrinder] 기본 테스트 스크립트 (Test Script) -E.T.C

요새 톰캣 설정에 잘 쓰고 있는 nGrinder 툴의 테스트 스크립트. 
(This is nGrinder's test script. Just add some for me on basic script)







[Go] 환경 구축하기 - Mac (Setting Go lang Environment on Mac) -GoLang(고 언어)

1. Go lang 인스톨러 다운로드/설치 (Download Go lang pkg)
- Download Url: https://golang.org/dl/
- Apple mac OS pkg 파일 다운로드 후 실행 및 설치(별도 설정 변경 없음)
  (Download Apple mac OSK pkg at Go download website. Then install package using downloaded file)


2. go env 체크 (checking "go env")
- 정상적으로 pkg가 설치되었다면, 터미널에서 "go env"를 입력한다. 
  (If you installed go pkg well, type "go env" on terminal)
- go env에 표시되는 환경 정보를 확인한다. 
  이때 GOPATH 항목이 앞으로 GO 작업을 할 공간이다. 
  

- GOPATH 변경을 원할 경우 .bash_profile 을 수정한다. (If you want change GOPATH, edit your .bash_profile)
  (1) 터미널에서 vi ~/.bash_profile (type "vi ~/.bash_profile" on terminal)
  (2) path 값 수정 (edit PATH value on .bash_profile)
      ex) export PATH="$GOPATH:/Users/username/go/bin:$PATH"
  (3) bash_profile 수정 후 "source ~/.bash_profile" 입력하여 변경사항 적용(after save edited .bash_profile, type "source ~/.bash_profile")
 

3. 디렉터리 체크 및 툴 설치(Checking directory and installing tool)
- go pkg 설치 후에, 곧바로 GOPATH에 필수 폴더가 생성되지 않는다. 
  수동으로 생성 또는 라이브러리 설치 후 생성된다.
  (Necessary folders are not made automatically after install go pkg.
   Make them manually yourself or They will be made after install library.)
- 터미널에서 아래 명령어를 입력하여 주요 Golang tools를 설치한다
  (Install golang tools using below command on terminal)
  >> go get golang.org/x/tools/cmd...
  *만약 설치 시 GOPATH를 설정하라는 메시지가 나오면, go pkg 설치 또는 GOPATH 설정값 을 확인한다. 
   (if you get message about checking GOPATH, you have to check go pkg install or GOPATH value)
- tools 설치 후에, GOPATH의 폴더 안에 아래와 같이 bin, pkg, src 폴더가 생성된 것을 확인할 수 있다. 
   (After golang tools installed, you can find bin,pkg, src folders on your GOPATH)
   
- 각 폴더는 아래와 같은 기능을 가지고 있다. (Each folders have different functions)
   (1) bin: 바이너리 파일(실행 파일)이 생성됨 (Binary files will be created in here)
   (2) pkg: 패키지 오브젝트 파일이 생성. 소스가 컴파일 된후 코드들이 들어감.
             (Package object files will be created. This files are compiled code) 
   (3) src: 소스 코드들이 들어감. 이 폴더 아래 자신만의 패키지 경로를 작성
             (Source codes will be located in here. You will make your package in here)

- src 폴더 안에 github.com, golang.com 폴더가 있는 것을 볼수 있다. 
  src안의 패키지 구성은 주로 src/도메인 형태로 생성한다. 
  만약 github.com에 올릴 거라면 src/github.com/githubid 형태로 하는 것을 추천한다. 
  (you can see github.com, golang.com folders in src.
   Normally package will be made likely a "src/domain".
   If you want to use github.com, recommend package structure like a "src/github.com/yourgithubid" )




Stream() >> collect() - supplier, accumulator, combiner - Java

1. Stream(): 
Array 또는 Collection(List 또는 Set)에 대해 Stream 처리를 하는 매서드
Dcument에는 순차 또는 병력 집계 작업을 하도록 해주는 매서드라 표현됨. 
즉, 람다를 활용하여 함수식으로 배열 또는 컬렉션을 처리(for, foreach 등) 해주는 매서드

2. collect():
대상 스트림의 요소들을 전달받은 형태로 mutable reduction 작업을 함(즉, 전달 받은 collection 형으로 변환)
(Mutable reduction: https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#MutableReduction)

만약 파라미터에 collector 만 넘겨주면, 해당 collector 형태로 값을 리턴 (예: collect(Collectors.toList());),

파라미터에 supplier, accumulator, combiner 전달 가능

supplier - 공급자, 해당 collect가 실질적으로 연산 후 리턴될 형태
accumulator - 축적자, 해당 collect가 실질적으로 작업하는 내용
combiner - 결합자, 해당 collect가 완료 후 작업할 작업 

여기서, combiner는 실질적으로 병렬(parallel)일 경우에 동작한다. 
즉 위에서 supplier랑 accumulator가 각각 공급자, 축적자 라고 한 경우는 병렬 작업일 경우 좀더 이해가 쉽다. 

아래와 같이 parallel이 아닌 순차로 처리할 경우, 세번째 파라미터에 정의된 combiner는 동작하지 않으며, 
결과적으로 각 스트림 안의 값들을 두번째 파라미터 accumulator에 정의된 내용으로 순차적으로 처리 후 
첫번째 supplier 파라미터 값으로 리턴한다. 

Source>
List<String> list2 = Arrays.asList("adf", "bcd", "abc", "hgr", "jyt", "edr", "biu");
String collect = list2.stream().collect(StringBuilder::new, (res, elem) -> {
System.out.printf("Before Accumulator!!>> res=%s, elem=%s\n", res, elem);
res.append(" ").append(elem);
System.out.printf("After Accumulator!!>> res=%s, elem=%s\n", res, elem);
}, (res1, res2) -> {
System.out.printf("Before Combiner!!>> res1=%s, res2=%s\n", res1, res2);
res1.append(res2.toString());
System.out.printf("After Combiner!!>> res1=%s, res2=%s\n", res1, res2);
}).toString();
System.out.println("collect=" + collect);
결과>
Before Accumulator!!>> res=, elem=adf
After Accumulator!!>> res= adf, elem=adf
Before Accumulator!!>> res= adf, elem=bcd
After Accumulator!!>> res= adf bcd, elem=bcd
Before Accumulator!!>> res= adf bcd, elem=abc
After Accumulator!!>> res= adf bcd abc, elem=abc
Before Accumulator!!>> res= adf bcd abc, elem=hgr
After Accumulator!!>> res= adf bcd abc hgr, elem=hgr
Before Accumulator!!>> res= adf bcd abc hgr, elem=jyt
After Accumulator!!>> res= adf bcd abc hgr jyt, elem=jyt
Before Accumulator!!>> res= adf bcd abc hgr jyt, elem=edr
After Accumulator!!>> res= adf bcd abc hgr jyt edr, elem=edr
Before Accumulator!!>> res= adf bcd abc hgr jyt edr, elem=biu
After Accumulator!!>> res= adf bcd abc hgr jyt edr biu, elem=biu
collect= adf bcd abc hgr jyt edr biu

combiner 부분은 호출이 되지 않음("res1=%2, res2=%s" 미출력)
각각의 요소들이 accumulator에 따라 순차적으로 결합, 최종 결과만 첫번째 파라미터인 StringBuilder에 따라 문자열 형태로 값이 리턴된다. 
(순차적으로 supplier로 전달된 형태에 accumulator로 처리/누적)


만약 parallel()을 붙혀 병렬 처리하면, 
List<String> list2 = Arrays.asList("adf", "bcd", "abc", "hgr", "jyt", "edr", "biu");
String collect = list2.stream().parallel().collect(StringBuilder::new, (res, elem) -> {
System.out.printf("Before Accumulator!!>> res=%s, elem=%s\n", res, elem);
res.append(" ").append(elem);
System.out.printf("After Accumulator!!>> res=%s, elem=%s\n", res, elem);
}, (res1, res2) -> {
System.out.printf("Before Combiner!!>> res1=%s, res2=%s\n", res1, res2);
res1.append(res2.toString());
System.out.printf("After Combiner!!>> res1=%s, res2=%s\n", res1, res2);
}).toString();
결과>

Before Accumulator!!>> res=, elem=jyt
Before Accumulator!!>> res=, elem=abc
After Accumulator!!>> res= abc, elem=abc
Before Accumulator!!>> res=, elem=adf
After Accumulator!!>> res= adf, elem=adf
Before Accumulator!!>> res=, elem=biu
After Accumulator!!>> res= biu, elem=biu
Before Accumulator!!>> res=, elem=bcd
After Accumulator!!>> res= bcd, elem=bcd
Before Accumulator!!>> res=, elem=edr
After Accumulator!!>> res= edr, elem=edr
Before Accumulator!!>> res=, elem=hgr
After Accumulator!!>> res= hgr, elem=hgr
Before Combiner!!>> res1= edr, res2= biu
Before Combiner!!>> res1= bcd, res2= abc
After Combiner!!>> res1= bcd abc, res2= abc
After Accumulator!!>> res= jyt, elem=jyt
Before Combiner!!>> res1= adf, res2= bcd abc
After Combiner!!>> res1= adf bcd abc, res2= bcd abc
After Combiner!!>> res1= edr biu, res2= biu
Before Combiner!!>> res1= hgr, res2= jyt
After Combiner!!>> res1= hgr jyt, res2= jyt
Before Combiner!!>> res1= hgr jyt, res2= edr biu
After Combiner!!>> res1= hgr jyt edr biu, res2= edr biu
Before Combiner!!>> res1= adf bcd abc, res2= hgr jyt edr biu
After Combiner!!>> res1= adf bcd abc hgr jyt edr biu, res2= hgr jyt edr biu
collect= adf bcd abc hgr jyt edr biu

병렬로 각각의 요소들에 대해 Accumulator가 연산하여 supplier의 형태로 축적을 한다. 
이후 각각의 Accumulator가 만든 결과들을 Combiner가 결합하면서 최종적으로 하나의 결과를 만들어낸다. 

참고주소:
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#collect-java.util.function.Supplier-java.util.function.BiConsumer-java.util.function.BiConsumer-
https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#MutableReduction
https://stackoverflow.com/questions/29959795/how-does-combiner-in-stream-collect-method-work-in-java-8


[Javascript] Mobile 모바일 접속 체크 (check mobile access)(prepare userAgent freezing by using client hint) - jQuery & Javascript & CSS

1. navigator.platform 값으로 비교. 

해당 값에 접속한 OS 정보가 담겨져 있음(정확히는 브라우저가 컴파일 된 플랫폼 정보가 담겨져 있음)
접속 구분하려는 값을 특정 한 후, 해당 값들이 navigator.platform에 있는지 indexOf 처리함
(아래에서 win16, win32, win64는 bit별 윈도우, mac, macintel 은 맥OS 값) 

ex) 
function isMobile(){
return "win16|win32|win64|mac|macintel".indexOf(navigator.platform.toLocaleLowerCase()) <0;
}

Using "navigator.platform". 
"navigator.platform" is showing about platform which browser is compiled on. 
Using indexOf, search platform information on navigator.platform
win16, win32, win64 are Windows. mac, macintel are MacOS. 
if that words are in navigator.platform, it mean that user access there using not mobile platform. 

2. navigator.userAgent 값으로 비교.
userAgent는 브라우저에서 서버로 전송하는 값으로, 서버에서 user를 특정(식별) 하기 위해 쓰인다. 
어플리케이션, 시스템 정보, 플랫폼 등의 정보를 가지고 있으며, 주로 아래와 같은 포맷을 가지고 있다. 

아래의 함수는 모바일 플랫폼 정보가 userAgent의 Platform 정보에 포함되어 있는지 찾는 방법이다. 

일반적으로 PC 환경에서도 모바일 환경 테스트를 하기 위해 userAgent의 값을 모바일 플랫폼 값으로 변경해서 쓸 경우가 있는데, 
이처럼 userAgent 값의 변경되는 환경에서 사용할 경우 이 방법을 쓰는 것이 나아 보인다. 

ex) 
function isMobile(){
return (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));
}


userAgent is shwoing "user-agent" header value which the browser sent to server. 
Server use "user-agent" header value for identify user. 
This values include datas about application, system information, platform, e.t.c. 

By checking mobile platform names are included in navigator.userAgent, you can know mobile access or not.

Sometimes, even though PC environment, people change userAgent value from PC to Mobile for testing or e.t.c.
If you want checking that situation, I think, this way is better than navigator.platform.

3. navigator.userAgentData
userAgent의 문제는, 구글에서 조만간 개인정보 보호를 위해 향후 크롬에서 userAgent를 비활성 시킬 것이라 한다. 
또한 navigator.platform 값도 고정값으로 바뀐다고 한다. 
(아래 링크 참조)

Important issue is that Google will disabled userAgent property on future Chrome version for privacy security.  
Also navigator.platform will be changed to fixed vlaue.
(Check below link)

link: https://www.zdnet.com/article/google-to-phase-out-user-agent-strings-in-chrome/


향후 userAgent 대신 client hint를 사용한다고 하며, 이에 따른 개발 방법은 아래 링크를 통해 확인 가능하다. 
Instead of userAgent, you have to user client hint at future Chrome.
You can check how to user it below link.

link1: https://web.dev/user-agent-client-hints/
link2: https://d2.naver.com/helloworld/6532276


chrome에서 해당 테스트 기능을 활성화 하여 확인한 결과, 내 맥북 환경에서 platform과 userAgent가 각각 윈도우 정보로 표시됨을 확인할 수 있었다. 

After enable chrome Experiments value, I checked userAgent and platform property.
UserAgent and navigator.platform were shown "Windows" platform information even though my laptop is MacOS, Macbook.

이러한 이슈 대비를 위해, 본인은 userAgentData(새로 생성되는 속성)값도 같이 대응하여 아래와 같이 작성하였다.
For this issue, I made my code using userAgentData(new property on future Chrome).

function isMobile(){
if(navigator.userAgentData) return navigator.userAgentData.mobile;
else return (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));
}

브라우저가 userAgentData를 가지고 있을 경우 해당 값으로 모바일 체크를 하도록 했다(이 값은 navigator.platform처럼 사용자가 수정이 불가한 것 처럼 보인다.)
만약 userAgentData를 아직 가지고 있지 않다면, 기존처럼 userAgentData로 체크하도록 분기 처리 했다. 

If Browser has navigator.userAgentData property, this function will check navigator.userAgentData.mobile.
This value show user's platform is mobile or not. (I think user can't edit this value like navigator.platform)
If Browser doesn't have userAgentData yet, this function will check navigator.userAgent. 




[삽질일기] Spring Boot Cacheable 삽질 - Java

스프링 부트를 통해 프로젝트 개발 중, 변경사항은 적지만 자주 읽히는 부분에 대해 Cache를 적용



Controller>

public ResponseEntity<ObjectDTO> findAllObject(@RequestParam(value="objectName") String objectName){
CustomObject customObject = new CustomObject();

//~~생략~~

ObejctService.findAllObject(customObject);

//~~생략~~

}

Service>

@Cacheable(value="findAllObject", key="#customObject)
public List<ObjectDTO> findAllObject(CustomObject customObject){
//~~생략~~
}



이랬는데... 호출할때마다 계속 Cache가 안되고 새로 읽어옴. 
디버깅 하다보니 Cacheable의 key의 customObject의 주소값이 계속 바뀜(Cause... Controller 호출 시 CustomObject를 신규 생성 하므로...)
즉, @Cacheable 입장에서는 key가 계속 바뀌고 있었음. 

--> key 를 CustomObject의 속성값 조합으로 변경하여 Cache 적용   


1 2 3 4 5 6 7 8 9 10 다음


AD_1