타입스크립트 12일차 - 초간단 webpack

webpack

매우 짧게 webpack에 대해 알아보겠습니다.

webpack은 쉽게 말하면 모든 것을 합쳐줄게 라는 모듈 번들러(module bundler) 입니다.

webpack

개발은 파일을 나눠서 따로 진행하되 마지막 서비스할때는 하나로 합쳐서 하겠다는 컨셉인데요.

설치는 간단합니다. npm install webpack 하나면 되죠.

편하게 쓰기 위해서 npm install webpack-cli도 설치합시다.

그 뒤 webpack 소스파일명 목적파일명 이렇게만 써도 됩니다.

근데 모든 파일마다 그렇게 할 수는 없으므로 webpack.config.js나 package.json에 설정을 해서 간단하게 쓰는 편입니다.

모든 설정에 대해 알아보려면 여기에서 설정을 하는 법을 볼 수 있습니다.

그래서 왜 써요?

저의 부족한 머리로 열심히 생각해보면 파일 하나로 로드해놓고 다음에 불러올 떄 변동사항이 없다면 브라우저의 캐시기능을 이용할 수 있습니다.

js 파일하나 불러오는데 네트워크 통신을 20ms 정도 한다 치면 브라우저 캐시(메모리, 디스크)를 이용하면 0~1ms 사이에 끝마치게 됩니다.

이게 쌓이고 뭉치면 누구보다 쾌적한 서비스환경을 제공할 수 있습니다.

Weekly Learned - .dev + https + github page 연동하기

.dev

*.dev 라는 도메인이 있다.

구글이 2월말에 오픈한 도메인으로써 현재 구매가능한 도메인이다.

https://domains.google/tld/dev/ 中

딱봐도 뭔가 사야될것 같지 않은가????

그래서 GoDxxxx에서 샀다. 샀으니 써야하지 않겠읍니까?

아무튼 구매 후 로그인하고 DNS 관리로 들어갑니다.

DNS 클릭

그 후 레코드에서 github 서버의 아이피를 추가해줍니다. HTTPS기준으로 모두 다 등록해주셔야 합니다.

  • 185.199.108.153
  • 185.199.109.153
  • 185.199.110.153
  • 185.199.111.153

CNAME도 *.github.io 모두 등록해줍니다.

TTL은 1/2시간도 좋고 1시간도 좋고 편한대로 하시고 모두 등록되었으면 완료입니다.

그리고 가장 처음에 등록된 GoDxxxx IP는 문의해서 없애셔도 되고 타 네임서버를 등록했다가 다시오면 없어집니다.

저런식으로 모두 등록되면 여기서는 모두 완료됩니다.

이렇게 설정되면 끝

github pages

여기서는 기본적으로 새롭게 받은 도메인을 등록해줘야 되는게 전부입니다.

Custom Domain에 여러분이 받은 도메인을 https://www를 빼고 *.dev만 등록해주세요.

Enforce HTTPS는 기본적으로 등록 안되는게 맞습니다.

이런식으로 되면 ok

그다음에 하루정도 기다리면 정상적으로 업데이트 되고 완료됩니다.

그다음 잘 쓰면 됩니다.

타입스크립트 11일차 - RXJS

RXJS

요즘 엄청난 이슈를 몰고 있는 반응형 프로그래밍 모델을 자바스크립트로 만든 라이브러리 입니다.

특히 비동기 코드를 제어하는데 강점이 있으며 수많은 비동기와 함께 해야 하는 자바스크립트와 잘 맞는다네요.

반응형 프로그래밍이란?

위키피디아에서는 다음과 같이 소개하고있네요 Reactive Programming is an asynchronous programming paradigm concerned with data streams and the propagation of change. 대충 번역하면 데이터 스트림 및 변경/전파와 관련된 비동기 프로그래밍 패러다임이다 라고 하는데 무슨소리인지 영..

아무튼 비동기 프로그래밍에서 데이터스트림을 효과적으로 다루기 위해 나온 것이라는 사실은 알 수 있을까요.

특히 이러한 프로그래밍을 사용하기 위해서 옵저버블 패턴, 이터레이터 패턴, 함수형 프로그래밍을 사용하고 있다고 합니다.

덧붙여 리액티브 프로그래밍을 사용하면 코드의 추상화 수준을 높여주기 때문에 우리가 받아야할 이벤트 연관성에만 집중해서 프로그래밍 할 수 있다고 합니다.

간단하게 시작하기

일단 서드파티 라이브러리 이므로 깔아야합니다.

npm install @reactivex/rxjs를 입력해 최신버전을 깝니다.

잘 설치됐는지 테스트를 해봅시다.

1
2
import * as Rx from "@reactivex/rxjs";
Rx.Observable.of("1","2","3").subscribe(v => console.log(v));

해당 코드의 결과는 1,2,3 이렇게 순차적으로 찍힙니다.

간단하게 클릭이벤트도 만들어봅시다.

1
2
var button = document.querySelector('button');
button.addEventListener('click', () => console.log('Clicked!'));

이러면 HTML로 버튼하나를 만들어서 클릭했을때 clicked alert이 발생할것입니다.

즉, RxJS에서는 키를 입력할 때마다 데이터가 생성되고 그 데이터를 연산자에 의해 처리하고 이 데이터를 구독해서 재처리함으로써 데이터를 소비하게 됩니다.

그러므로 RxJS 처리의 핵심은 입력된 데이터 스트림을 연산자로 처리해 구독자에게 결과를 전달하는 것입니다.

그럼 책+인터넷을 참고해서 그렇게 중요한 연산자에 대해 알아보겠습니다.

연산자

연산자는 현재 Observable을 기반으로 새롭게 Observable을 생성하는 순수한 함수입니다. 순수한 함수라는 말은 언제나 같은 값을 넣으면 같은 값이 나오는 함수형 프로그래밍에서 자주 쓰이는 말입니다.

아무튼 RxJS에서는 스트림을 처리하기 위해 다음과 같은 부류의 연산자를 제공합니다.

  • Creation Operators
  • Transformation Operators
  • Filtering Operators
  • Combination Operators
  • Multicasting Operators
  • Error Handling Operators
  • Utility Operators
  • Conditional and Boolean Operators
  • Mathematical and Aggregate Operators

Creation Operators

생성연산자는 데이터 스트림을 생성하기 위해 제공합니다. 이벤트나 값을 시퀀스 형태로 생성해줍니다.

대표적으로 create, from, of가 있습니다.

create는 Custom Observable을 생성해 구독자에게 통지하는 기능을 제공합니다.

from은 객체 대부분을 Observable로 변환하는 기능을 가지고 있습니다.

of는 받은 인수를 내보내는 연산자입니다. 무슨말이냐면 처음 예제코드와 같이 받은 인수 1,2,3을 하나씩 구독자에게 내보냅니다.

Transformation Operators

변형 연산자는 말그대로 데이터 스트림을 입력받아 형태를 가공하는 연산자입니다.

대표적으로는 map이 있습니다. 형태도 다양합니다. concatMap, MergeMap, switchMap 등등등…

1
Rx.Observable.from([1,2,3]).map(val => val+10).subscribe(val => console.log(val));

이런 예제 코드가 있다고 하면 입력받는 1,2,3을 map으로 가공하게 됩니다.

Filtering Operators

필터링 연산자는 조건에 부합하는 값만 함수로 전달해줍니다. 말그대로 필터역할을 합니다.

대표적인 연산자로는 filter, take등이 있습니다.

filter는 x > 2라는 조건이 있다면 말그대로 x가 2를 넘는 값만 돌려줍니다.

take는 주어진 인수의 갯수만큼만 출력됩니다.

Combination Operators

콤비네이션 연산자는 Observable간의 연결처리를 수행할 수 있도록 해줍니다.

대표적인 연산자로 concat, merge등이 있습니다.

둘 다 생각하시고 많이 쓰셨던 그대로입니다.

Multicasting Operators

하나의 스트림을 여러 구독자에게 값을 보냅니다. 말그대로 다중출력 입니다.

대표적인 연산자로 multicast가 있습니다.

Error Handling Operators

오류가 발생했을때 처리하는 연산자입니다.

catch, retry가 있는데 catch는 오류가 발생한순간 받아서 주어진 처리를 진행하는 연산자입니다.

retry의 경우는 인수가 주어지는데 주어진 인수만큼 다시 반복하고 진행합니다.

이런식으로 작동합니다.

Utility Operators

각종 유틸기능을 담은 함수입니다.

delay, toArray, Timeout(주어진 시간이 지나면 out)등 여러가지 유틸리티 기능을 담고 있습니다.

Conditional and Boolean Operators

찾는 기능이나 조건에 관련된 연산자가 들어있습니다.

find, defaultIfEmpty, every등이 있습니다.

Mathematical and Aggregate Operators

숫자와 관련된 연산자입니다.

count, max, min, reduce가 있습니다.

스케줄러

스케줄러는 구독이 시작될 때와 알림이 올 때 제어하는 구성요소입니다. queue, asap(as soon as possible), async 세 가지의 유형이 존재하며 따로 제공하지 않으면 RxJS는 스스로 스케줄러를 선택합니다.

스케줄러는 세가지 특징을 갖고 있습니다.

  • 스케줄러는 데이터 구조로써 우선 순위, 다른 기준에 따라 작업을 저장하고 대기열에 넣는 방법을 갖고 있습니다.
  • 스케줄러는 작업이 실행되는 위치 또는 시점을 나타냅니다.
  • 스케줄러는 가상 시계를 가지고 있습니다. 그래서 해당 시간에만 나타나도록 할 수 있습니다.

스케줄러는 이러한 특징을 활용해 관찰자가 어떤 컨텍스트에서 Observer에게 통지를 전달할지 정의하고 있습니다.

Hot vs Cold Observables

관련 문서를 찾아서 보던 중 Hot vs Cold Observables를 모르면 언젠가 기술부채를 갚을 날이 올것이다 라는 말을 봤습니다.

그래서 한번 찾아봤는데 Cold Observable은 관찰자가 구독할때 까지 자원을 생성하지 않고 Hot Observable은 구독 여부에 상관없이 진행되다가 구독하면 그 때부터 볼 수 있도록 합니다.

뭔가 어려운데.. Cold는 시작버튼을 눌러야 진행한다고 보면 되고 Hot은 내가 구독하던말던 쭉 진행하다가 필요한 순간부터 받으면 될 것 같다.

어느 누구는 Cold는 영화관이고 Hot은 케이블 영화채널이라고 비유하기도 한다. 모르겠다…

그래서 결론은 왜 RxJS죠?

타입스크립트 10일차 - 서드파티 라이브러리와 타입 정의 파일

타입스크립트와 JS 서드 파티 라이브러리

자바스크립트의 오픈소스 생태계는 풍성하고 수많은 업체와 개발자가 자바스크립트 생태계를 발전시켜 왔습니다.

타입스크립트는 JS와 비교해도 그렇고 출시년도도 2012년도로 상당히 신생언어지만 ECMA스크립트의 표준을 따름으로써 자바스크립트의 생태계를 포함할 수 있게 되었습니다.

1
2
3
4
5
6
7
재미로 보는 몇가지 언어의 개발년도
Go lang : 2012
Dart : 2011
React : 2013
Vue : 2014
Kotlin : 2011
Swift : 2014

자바스크립트의 프로젝트의 대부분은 라이브러리로 공개되어 있습니다.

제 3자에 해당하는 개발자, 업체에 의해 개발되므로 서드 파티 라이브러리라고 불립니다. 대표적인 라이브러리는 jQuery, Angular, React, Vue 등이 있습니다.

https://gitstar-ranking.com/repositories - 깃허브 Star랭킹

이들 자바스크립트를 타입스크립트에서 사용하려면 *.d.ts 파일로 된 타입 정의 파일이 있어야 합니다.

타입스크립트 컴파일러는 타입 정의 파일을 사용해서 구조를 이해한 후 컴파일 합니다.

타입 정의 파일에 대한 저장소

JS기반으로 된 언어답게 외부 서드파티의 자바스크립트 라이브러리를 기본적으로 사용할 수 있습니다만 타입스크립트 프로젝트에서 인식하기가 어려우므로 타입 정의 파일이라는 중간 매개 파일이 필요합니다.

타입 정의 파일은 깃허브에 위치한 타입 정의 파일에 대한 저장소에서 확인할 수 있습니다. 4-4일 기준 4805개의 타입이 등록되어 있습니다.

이곳에는 타입스크립트로 포함해서 사용할 수 있도록 개발자들이 작성해놓은 타입파일이 저장되어 있습니다.

https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types

https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react - react의 타입저장소

라이브러리와 타입 정의 파일 설치하기

타입스크립트 정의 파일을 설치하는 방법은 1.x 대에서 사용하는 typing이라는 도구를 이용하는 방법과 2.x에서 사용하는 npm으로 자동 설치하는 방식이 있습니다.

뭘 쓸지 모르니 둘 다 알아야 한다고 합니다.

1.x 설치하기

테스트 환경설정을 위해 기본환경설정을 합니다.

tsc --init + npm init 를 실행 후 책에 나와 있는 underscore를 설치해 봅시다.

npm install --save underscore@1.8.3을 실행합니다.

설치한 underscore 라이브러리를 사용하려고 import 하는데 오류가 발생합니다.

what the f...

자바스크립트 패키지를 타입스크립트에서 인식시키려고 하면 타입 정의 파일을 설치해야 합니다.

먼저 1.x에서 쓰이는 typing 도구를 설치합니다. (npm install -g typings)

그 후 typings search underscore 명령어로 검색한 후 아래와 같은 검색결과 중 선택해서 쓰시면 됩니다.

1
2
3
4
5
6
Viewing 3 of 3

NAME SOURCE HOMEPAGE DESCRIPTION VERSIONS UPDATED
underscore dt http://underscorejs.org/ 2 2017-03-06T10:04:25.000Z
underscore-ko dt https://github.com/kamranayub/UnderscoreKO 1 2016-08-03T18:11:25.000Z
underscore.string dt https://github.com/epeli/underscore.string 2 2016-07-25T23:10:34.000Z

설치 명령어는 typings install dt~underscore --global --save 이런식으로 사용하시면 됩니다.

인식됨

근데 deprecated 되었으므로 2.x를 활용하는게 좋습니다.

deprecated

2.x~최신버전 설치하기

타입스크립트가 2.x 버전으로 업그레이드 되면서 @types/패키지명을 사용하여 사용이 가능해졌습니다.

기본 저장폴더는 node_modules 폴더에 설치되지만 tsconfig.json 파일에서 compilerOptions/typeRoots를 json형식으로 지정하여 원하는 폴더에 설치가 가능합니다.

compilerOptions/types형식으로 설치된 수많은 타입 중 원하는 형식의 타입만 가져와서 사용할 수도 있다고 합니다.

타입은 http://microsoft.github.io/TypeSearch/에서 검색이 가능합니다.

예를 들어 여러분이 underscore를 설치한다고 생각해봅시다.

npm show @types/underscore versions이라는 명령어로 해당 라이브러리의 버전을 볼 수 있으며 npm install --save-dev @types/underscore@버전이라는 명령어로 설치 가능합니다.

잘 설치해서 쓰시면 됩니다.

타입 정의 파일

타입 정의 파일은 타입이 없는 자바스크립트 라이브러리에 대한 타입 정보를 타입스크립트 컴파일러에게 전달해 컴파일 수행을 도와주는 파일입니다.

.d.ts 확장자를 사용하며 자바스크립트 라이브러리에는 없는 타입 정보나 구조 정보를 인식해 API로 사용할 수 있도록 도와줍니다.

타입스크립트에 쓰일 순수 JS에는 타입이 없으므로 직접 정의해서 파일을 별도로 배포해야 합니다. 컴파일러는 이 타입정의파일을 타입을 검사하거나 코드 어시스트, 컴파일 에러를 표시할 때 사용합니다.

외부로 공개된 모듈은 보통 export로 선언되는데 이 모듈의 JS 구현 환경에 대한 정보를 declare 키워드를 이용해 선언합니다. 이러한 선언을 앰비언트 선언이라고 합니다.

Typescript 파일에서 생성되지 않는 변수 등을 선언한다고 보셔도 됩니다.

전역 스페이스 오염방지를 포함한 책에 나온 사용법은 다음과 같습니다.

1
2
3
4
5
6
7
declare module 모듈명 {
export interface url{
protocol?: string;
hostname?: string;
port?: string;
}
}

물론 모듈과 네임스페이스는 동등하게 사용되므로 네임스페이스를 사용해도 됩니다.

그런데 매번 선언하려면 귀찮으므로 축약해 선언할 수도 있습니다. declare mylibrary {...} 이런식으로 말이죠.

또한 선언되어있는 라이브러리를 import 한 후 다른 네임스페이스를 사용하면 확장또한 가능합니다.

1
2
3
4
5
6
7
import * as mylibrary from 'mylibrary';

declare module mylibrary{protocol
namespace myplugin{
//TODO
}
}

라이브러리 사용시 d.ts 파일을 사용해 호출하기

타입 정의 파일은 수동으로 구축도 가능하지만 만약 프로젝트가 타입스크립트 기반이라면 명령어를 통해 타입 정의 파일을 추출할 수 있습니다.

tsc ts파일명 -d --outDir 폴더명 으로 간단하게 입력이 가능합니다.

기본적으로 외부에서 접근할 필요가 있는 것에 대해서만 타입 정의를 하며 불필요한 파일에 대해서는 정의하지 않습니다.

책에 있는 예제를 쓴다면 다음과 같은 코드가 있습니다.

1
2
3
4
5
6
7
8
9
10
11
export namespace MyLibrary {

export function getMaxNumber(array: number[]): number {
return Math.max.apply(Math, array);
}

function getMinNumber(array: number[]): number {
return Math.min.apply(Math, array);
}

}

이럴 때 getMinNumber는 읽을 필요가 없으므로 tsc my.ts -d --outDir dts라고 입력한다면 다음과 같은 코드가 남습니다.

1
2
3
export declare namespace MyLibrary {
function getMaxNumber(array: number[]): number;
}

만약 getMinNumber가 필요하다면 export로 선언해 주면 됩니다.

이상으로 마치겠습니다.

타입스크립트 9일차 - 비동기

프로세스와 스레드

비동기에 관한 이야기를 하기 전에 OS시간에 배웠던 프로세스와 스레드의 간단한 차이에 대해 알아보겠습니다.

프로세스는 간단히 프로그램에 대한 인스턴스를 말합니다. 그래서 운영체제로 부터 실행할 공간을 할당받고 관리하에 놓이게 됩니다.

스레드는 간단히 프로세스 내에서 실행되는 동작의 흐름으로 보통 일(Task)의 단위라고 얘기합니다. 그래서 프로세스내에서 스레드가 실행되며 주어진 일을 진행하고 프로세스의 관리를 받습니다.

그런데 하나의 프로세스와 하나의 스레드만 가지고 실행을 하면 요즘 8코어도 가성비가 좋네마네 하는 세상인데 1개의 프로세스와 스레드만 실행하기에는 공간이 너무 남아돕니다.

그래서 프로세스를 여러개 쓰는 작업은 OS의 시스템 콜이 필요하게 되고 상대적으로 부담을 주게 됩니다. 그러다보니 그냥 스레드를 여러개 쓰고 프로세스 하나만 타이트하게 관리하는 것이 좋겠다는 생각을 하게 되었습니다. 물론 관리도 쉽고요.

이렇게 해서 멀티 프로세싱보다는 멀티쓰레드를 많이 사용하게 되었습니다.

쉽게 예를 들면 직원이 한명이 아닌 식당에서 주문을 했을 때 주문받는사람이 주문받고나서 음식만들고 서빙까지 한다면 너무 비효율적이겠죠. 손님이 1명이라면 나머지는 놀고 있는데 말이죠.

보다 못한 사장이 배분을 해서 너는 주문을 받고 서빙을 하고 너는 음식을 만들어라 너는 설겆이를 해라 이런식으로 분배를 해서 작업을 하는게 멀티스레딩방식입니다.

그런데 JS는 단일 스레드방식을 사용하고 있습니다. 즉, 똘똘한 1명이 주문도 받고 음식도 만들고 설겆이도 다하는 슈퍼맨이라는 것이죠.

슈퍼맨의 시간을 어떻게 알뜰하게 사용할 수 있을까 해서 나온 것이 비동기 방식입니다. 손님이 오기까지 메뉴판을 들고 대기하는게 아니라 손님이 오면 어서오세요 하면서 메뉴판을 가져다 주는 방식인것이죠.

슈퍼맨의 시간을 알뜰하게 쓰면서 주문받고 음식만들고 서빙하도록 순차적으로 할 수 있도록 JS에서 지원하는 세 가지의 방식을 알아보겠습니다.

타입스크립트인데 왜 JS 얘기를 하느냐는 당연히 타입스크립트가 JS기반이기 때문입니다.

callback

콜백방식은 받고 나면 해당 콜백을 실행해주는 방식입니다. 앞의 함수가 실행되면 파라미터로 받은 다음 함수가 실행되는 방식이죠.

1
2
3
손님왔다('어서오세요!', function (){
주문을 받는다();
}

이러한 함수가 있다면 손님왔다() 함수가 실행 된 후 주문을 받는다() 함수가 실행되게 함수를 작성합니다. 그러면 손님이 오면 -> 주문을 받는다()가 되는거죠. 손님이 없을때 주문을 받지 않아도 됩니다.

그런데 콜백을 너무 많이 작성하면 알아보기가 힘들어집니다.

전체적인 식당로직을 작성해 본다면 이런식이 되겠죠.

1
2
3
4
5
6
7
8
9
10
11
일을한다('인사', function () {
일을한다('메뉴판가져다주기', function () {
일을한다('주문받기', function () {
일을한다('요리', function () {
일을한다('서빙', function () {

});
});
});
});
});

로직이 여러 단계로 분리되어있어 콜백을 작성하다보니 계단형식의 callback hell이 만들어 졌습니다.

그렇다고 하나의 함수로 모으는 행위는 OOP의 원칙에 좋지 않은 행동이고요.

그래서 좀 더 쉽게 쓰는 방법이 등장했습니다.

promise

ES6에서 서드파티로 지원되었던 프로미스가 공식적으로 지원되기 시작하였습니다.

can i use promise?

조금 더 쉽게 만들어줍니다.

사용방식은 promise.then(할일).catch(오류시할일)로 정의됩니다.

위의 코드를 바꿔보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
일을한다().then(
인사하기
).then(
메뉴판가져다주기
).then(
주문받기
).then(
요리
).then(
서빙
).catch(
고객, 아직도 문제있어요?
)

똑같은 인사하기 ~ 서빙까지 진행하는데 1열로 보니까 좀 더 쉽게 표현이 됩니다.

근데 .then이 체인처럼 길게 주르르륵 늘어지게 보이는것도 마음에 안드네요.

그래서 좀 더 단순하게 바꾸는 방법을 정의했습니다.

자세한 문법은 따로 검색해보시길 바랍니다.

async/await

async/await 방식은 좀 더 간단하게 표현할 수 있도록 도와주는 방식입니다.

함수에 async만 달아주고 각각 일(함수)마다 await만 달아주면 마치 위에서 부터 차례대로 실행되는 명령형 프로그래밍 마냥 실행할 수 도와줍니다.

can i use async/await?

사용법은 걍 await 할일()이면 끝납니다.

1
2
3
4
5
6
7
8
9
10
11
async 일을한다(){
try {
await 인사하기();
await 메뉴판가져다주기();
await 주문받기();
await 요리();
await 서빙();
} catch(문제생김) {
고객, 뭐가불만이예요?
}
}

try~catch안의 함수만 보시면 훨씬 간단해지고 보기 쉬워진것을 알 수 있습니다. 보시면 인사하기 부터 서빙까지 일직선으로 표현만 하면 됩니다.

단, 주의할 점이 조금 있습니다만 오류를 검출하려면 try~catch 문을 사용해야하며 async 함수를 외부에서 호출해줘야합니다.

그리고 async도 내부적으로는 프로미스를 사용하기 때문에 프로미스 문법을 사용해야 합니다. then() 이런것 말이죠.

마무리

타입스크립트는 최신 함수를 전부 지원합니다. 골라서 사용합시다..

내가 promise/async,await를 이용해 짱짱맨을 만들어 내갰어 라고 생각해도 IE11 때문에 어차피 쓰기 힘들어요 ㅎㅎ

Weekly Learned - list에서 object 지우는 방법

문득 list에서 조건에 맞는 오브젝트를 지우는 방법에 대해 답변을 달던 중 과연 몇가지가 있을까 생각해봤습니다.

더 있을수도 있지만 세가지가 바로 생각이 났는데 이 방법에 대해 테스트 해볼려고 합니다.

방식은 개인적으로 많이 쓰는 getter/setter 방식의 vo에서 메소드를 이용해 지우도록 테스트 해봤습니다.

먼저 5000000개의 vo 리스트를 준비해봤습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class testVO {
private int id;
private String name;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

간단한 테스트에 lombok은 설정부터 귀찮아서 그냥 generate 메뉴를 이용해 작업을 합니다.

그리고 테스트 데이터를 준비합니다.

1
2
3
4
5
6
7
8
for (int i = 1; i <= 5000; i++) {
for (int j = 1; j <= 1000; j++) {
testVO vo = new testVO();
vo.setId(j);
vo.setName(j + "abcd");
list.add(vo);
}
}

1~1000까지의 데이터를 5000번 반복해서 입력합니다. 예를 들면 id가 1이고 name이 “1abcd”인 vo가 1000개있겠죠.

실제환경에서는 하나도 없을수도, 한개만 있을수도, 여러개가 있을수도 있지만 통제를 했습니다.

list.remove(i)

list를 for문으로 돌면서 가져오는 코드는 다음과 같죠.

1
2
3
4
5
for (int i = 0; i < list.size(); i++) {
if (list.get(i).getName().equals("500abcd")) {
list.remove(i);
}
}

리스트를 돌면서 500abcd와 같은 값을 가져와서 지웁니다.

위 아래는 System.currentTimeMillis()메소드를 찍어서 구분을 했으며 실행 결과는 다음과 같이 나옵니다.

list.remove(i)

결과는 5694ms로 나오네요.

list.remove(object o)

이번엔 foreach문을 돌면서 지워보겠습니다.

코드는 다음과 같습니다.

1
2
3
4
5
6
7
8
Iterator<testVO> iter = list.iterator();

while (iter.hasNext()) {
testVO vo = iter.next();
if (vo.getName().equals("500abcd")) {
list.remove(vo);
}
}

foreach로 수정하려고 했으나 Iterator를 이용하기로 했습니다.

list를 직접 수정하면 오류가 나기에 list2를 따로 만들어서 Iterator이용은 list1 지우는 것은 list2를 지웁니다.

list.remove(obj)

결과는 11735ms가 나오네요.

list.removeif(java.util.function.Predicate)

java8의 자랑 람다식을 활용한 removeif 예제입니다.

해당 조건에 맞는 오브젝트를 전부 지웁니다.

1
list.removeIf(vo -> vo.getName().equals("500abcd"));

앞의 식에 비해 확줄어든 코드가 느껴지시나요?

돌려보겠습니다.

list.removeif()

결과는 103ms네요. 믿기지 않아서 3번돌려봤습니다.

내부 최적화가 엄청난것같네요.

결과

오늘부터 lambda쓰러갑니다..

타입스크립트 8일차 - 제네릭

제네릭

다른 자바, C# 같은 언어에서 사용되었던 제네릭이 타입스크립트에 들어왔습니다.

제네릭은 함수나 클래스를 선언할 때 타입을 고정시키지 않고 사용시 명시해 타입을 유연하게 사용하도록 도와주는 방법입니다.

제네릭에 대해 알아보겠습니다.

any타입을 쓰면 안될까?

1
2
3
function anyFunc(strs: any, strs2: any): any{
return strs + strs2;
}

위와 같은 함수가 있다고 가정해봅시다.

any의 앖이 숫자만 들어온다면 두 숫자의 합이 출력될 것이고, 문자열이 들어온다면 문자열이 합쳐진 결과가 나올 것입니다.

만약 나는 숫자를 썼는데 '1'의 문자열이 들어간다면 왜 안되지? 하면서 시간을 쓸 수 있습니다.

즉, 타입스크립트의 타입안전성을 보호하지 못하는 것이죠.

사용해보기

예제 코드를 만들어 보겠습니다.

1
2
3
4
5
6
7
8
function genericFunc<T>(arr1: T[], arr2: T[]){
return arr1.concat(arr2);
}

let array1 = [1,2];
let array2 = [3,4];
let resultArray = genericFunc<number>(array1, array2);
console.log(resultArray.join(","));

위와 같은 코드를 찍어보면 1,2,3,4가 출력될 것입니다.

함수를 호출 시 <타입>을 넣어준다면 안의 함수에 따로 타입을 선언하지 않아도 자동으로 할당되어 실행되게 됩니다.

즉, arr1과 arr2에게 타입이 같이 선언되어 한쪽은 문자열, 한쪽은 숫자 이런식으로 안전성이 깨질일이 없이 안전성을 갖추게 됩니다.

마지막으로 T는 Object 혹은 Any와 같이 어떠한 타입이 들어와도 상관없다는 약속된 표시입니다.

상속을 활용한 제네릭

제네릭에서 특정 타입을 상속한 클래스나 타입을 받을 수 있습니다.

<T extends 타입> 이런식으로 특정 타입을 상속한 제네릭을 사용 가능합니다. 반드시 any, object같은 all free 타입이 아니라 특정 타입만 받을 수도 있습니다.

<T extends 타입1 | 타입2> 이런식으로도 유니언 타입도 이용 가능합니다.

마무리

이런식으로 제네릭을 사용하면 타입안전성을 보충할 수 있습니다.

OOP 언어와 같은 이유로 제네릭을 만들었으므로 같은 목적으로 사용하면 될 것 같습니다.

타입스크립트 7일차 - 고급타입

유니언 타입

유니언 타입은 2개 이상의 타입을 받게 할 수 있는 타입입니다. 간단하게 | 연산자로 정의하시면 됩니다.
예를 들면 string | number 이런 식으로 가능하겠죠.

이 유니언 타입은 함수 파라미터나 변수뿐만 아니라 함수의 리턴값에도 사용 가능한 타입입니다.

그런데 유니언타입으로 받지만 특정 변수타입의 함수를 사용해야 될 경우가 있습니다. indexOf라는 함수가 대표적이겠네요. 내가 찾아야할 문자가 어디에 위치하는지 찾는 함수입니다.
이럴 경우에 typeof연산자를 활용하여 먼저 체크해주신다면 충분히 해당 함수를 안전하게 사용할 수 있습니다.
변수일 경우는 문제가 그렇게 되지 않습니다만 함수의 파라미터로 받을 경우 문제가 생기게 됩니다. 뭐가 들어올지 모르기 때문이죠.

typeof가 없는 함수

typeof가 있는 함수

위의 같이 typeof로 타입의 안전성을 확보해줘야 하니 주의하시면 되겠습니다.

문자열 리터럴 타입

변수를 대입할 때 문자열로 지정하시면 해당 문자열만 받도록 설정할 수 있습니다.

let event: "key" 라고 선언된다면 event 변수에는 반드시 “key”라는 문자열만 들어갈 수 있습니다.

non-nullable

보통 변수값이 현재 지정되지 않았다거나 할당되지 않았다는 것을 표현하기위해 null/undefined를 사용합니다.

하지만 이런 경우 타입의 안전성을 해칠 가능성이 있으므로 엄격하게 Null 검사를 진행할 수 있습니다.

tsconfig.json에 strictNullChecks: true 옵션을 추가하는 것인데요.

이렇게 되면 직접적으로 null이나 undefined를 특정 변수에 받겠다고 선언하지 않은 변수에 대해 검사를 통해 null/undefined를 걸러냅니다.

타입 에일리어스

타입스크립트는 타입에 대해 별칭을 짓는 기능을 가지고 있습니다.

type 식별자 = 타입으로 만들 수 있고 식별자와 타입은 동일한 기능을 수행합니다.

즉, type stringAS = string이라고 선언한다면 stringAS를 쓰든 string을 쓰든 둘 다 string으로만 받는다는 뜻이 됩니다.

이렇게 편리한 기능이지만 간단한 타입으로는 만들어 봐야 괜히 복잡하기만 해지니 복잡한 타입을 정리할 때 사용하면 좋을 것 같습니다.

타입 추론

타입스크립트에서는 값을 할당할 때 타입을 명시하지 않으면 타입 추론을 통해 타입을 결정합니다.

즉, 자바스크립트에서 사용되는 것과 같이 자동으로 컴파일러가 문자냐 숫자냐 오브젝트냐 판단한다는 의미입니다.

배열의 경우에는 처음에 추론될 때 각 값에 대해 타입이 다르면 object 타입으로 정해져서 이것저것 넣는 것에 대해 문제가 없으나 변수의 경우는 처음 값이 할당되면 해당 타입으로 할당되기 때문에 다른 타입의 값을 할당할 수 없으니 조심하셔야 합니다.

타입 캐스팅

타입 캐스팅은 명시적으로 선언한 캐스팅 코드로 인하여 변경되는 것을 의미합니다.

null, undefined를 제외한 기본 래퍼 객체를 이용해 캐스팅이 가능하며 string, number, boolean, symbol 종류로 캐스팅이 가능합니다.

문자 -> 숫자로 변경하고자 한다면 parseInt, Number등 여러가지 캐스팅 함수가 있지만 제대로 캐스팅되지 않을 경우를 대비해 NaN등의 체크를 해주는 것도 잊지 말아야 하겠습니다.

해당 캐스팅함수는 자바스크립트의 기능을 이용하기 때문에 컴파일 후에도 코드가 그대로 유지됩니다.

타입 어설션

타입 어설션은 타입스크립트 컴파일러가 타입스크립트 문법에서 주어진 정보를 이용해 컴파일을 수행하며 타입을 변경하는 방법입니다.

선언방식은 크게 <>, as 방식이 있으며 캐스팅 후 JS코드에는 남지 않게 됩니다.

간단하게 코드를 작성해보면 다음과 같습니다

1
2
3
let myNum: any;
let num1: number = <number>myNum;
let num2: number = myNum as number;

둘 다 허용되지만 <> 방식은 리액트의 JSX 문법과 유사해 충돌할 수 있으니 as방식을 추천한다고 책에서 말하고 있습니다.

제가 리액트를 안해봐서…

마무리

이상으로 타입과 관련된 day7을 마치겠습니다.

다음시간은 제네릭입니다.

Weekly Learned - 라즈베리파이 + yona 설치기

곧 집에서 알바 프로젝트를 뛸 수도 있을것 같아서 일정관리를 위해 이슈트래커를 써보려고 yona를 설치해보기로 했습니다.

집에 노는 라즈베리파이를 쓰고자 했는데, 추천하는 마리아DB로 하기로 했는데..

그런데 https://raspberrypi.stackexchange.com/questions/77646/install-mariadb-10-2-on-raspbian-stretch 이 URL을 보면 마리아db 10.2 이후로 arm 컴파일본을 지원하지 않는다고 합니다.

그래서 10.1이 최신버전이네요. 아무튼 일단 설치해보도록 했습니다만.. db를 설치하고 /etc/my.cnf 에서 다음과 같이 환경설정을 하면

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[client]
default-character-set=utf8mb4

[mysql]
default-character-set=utf8mb4

[mysqld]
innodb-file-format=barracuda
innodb_file_format_max=barracuda
innodb-large-prefix=1
init-connect='SET NAMES utf8mb4'
lower_case_table_names=1
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

그 후 실행을 하니 오류가 발생… 아무튼 검색을 해서 https://github.com/yona-projects/yona/issues/365 같은 문제를 알아냈습니다.

방법은 10.2를 설치하던지 관련된 내용을 지우고 다시 실행해 보던지 선택인데… 어차피 라즈베리파이가 사양 상 접속자가 많이 들어올 수 없기 때문에 결국 H2 버전으로 가기로 했습니다.

결론은 라즈베리파이는 걍 h2합본 배포판을 설치하는게 낫지 않나 하는 생각이 들었던 1주일이었습니다.

타입스크립트 6일차 - 모듈

모듈의 필요성

제가 지금 다니는 회사 말고 전에 회사를 다녔을 때 만줄이 넘는 페이지를 본 적이 있었습니다.

언어는 ASP였고 레거시 시스템을 가지고 유지보수를 했는데 전에 만들었던 사람이 함수를 한군데 다 짱박아 놓는 바람에 nodepad가 버벅거릴정도로 라인수가 많은 페이지를 열었던 엄청난 경험이었습니다.

찾기를 해도 한참돌다가 찾아지는 그 오묘함이란 말도 못할 경험이었죠.

그래서 필요한 부분만 따로 떼서 유지보수와 작업을 수월하도록 만드는 모듈화가 필요한 것입니다.

타입스크립트에서는 세 가지의 이점을 누릴 수 있습니다.

유지보수의 용이성

쉽게 말하면 만줄짜리 열고 버벅거리는것을 느끼면서 찾을래? 백줄짜리 열고 빠르게 찾을래? 이 뜻인것 같습니다.

애초에 요즘 모듈화 안하면 욕먹기 딱좋죠..

전역 스코프 오염을 방지

JS namespace에 변수이름이나 함수이름을 중복해 선언할 수 없는 규정이 있는데 내가 이 이름을 쓰고싶은데 못쓰는 경우를 방지하기 위해서 입니다.

특히 JS의 전역 공간(global namespace)를 침범하지 않게 되므로 중요합니다.

재사용성 향상

모듈화를 잘 해놓으면 현재 프로젝트 뿐 아니라 다른 프로젝트에서도 가져다 쓸 수 있습니다.

저는 JAVA의 문자열 유틸을 가지고 다른분들이 미리 만들어 놓은 모듈을 가져다 잘 쓰고 있습니다.

내부모듈과 외부모듈

타입스크립트에서는 내부모듈과 외부모듈로 구분하게 됩니다.

버전 1.5가 등장하면서 네임스페이스라는 특징과 ES2015 모듈의 특징이 추가 되었고 ECMA스크립트 표준 용어집에 따라 두 가지로 나뉘게 되었습니다.

이 두가지의 모듈은 내부모듈과 외부모듈로 나뉘게 되었는데 각각에 대해서 보겠습니다.

내부모듈

네임스페이스라고 불리는 단위의 이름 공간입니다.

따라서 같은 네임스페이스의 공간이라면 별도의 참조문 없이 참조할 수 있는데 파일이 다르더라도 네임스페이스가 같다면 이름을 중복할 수 없습니다.

물론 네임스페이스가 다르다면 문제 없습니다.

네임스페이스는 전역 스코프에 속하지만 전역 스코프와는 독립된 공간이라는 점이 특징입니다. 그래서 라이브러리 단위의 모듈을 구성할 때 사용하면 좋습니다.

네임스페이스란?

하나의 독립된 이름 공간을 만들고 여러 파일에 걸쳐 선언된 이름 공간을 공유하도록 만들 수 있는 방식입니다.

선언할 때 module이라고도 부르지만 표준인 namespace로 더 많이 부릅니다.

사용법은 다음과 같습니다.

1
2
3
4
5
namespace a {
function b(){
//TODO...
}
}

이러한 네임스페이스의 선언명만 같다면 hello1.ts도 hello2.ts도 같은 네임스페이스를 사용할 수 있습니다.

예를 들면, 인터페이스와 이를 구현한 클래스를 분리해서 각각 파일로 만든다고 쳤을 때 한번에 폴더를 전부 컴파일하면 문제는 없습니다.
그런데 클래스만 컴파일 하게 된다면 인터페이스를 찾을 수 없기 때문에 문제가 발생합니다.
그래서 타입스크립트에서는 명시적으로도 네임스페이스가 같지만 물리적으로는 다른 파일을 참조할 수 있게 문법을 제공합니다.

/// <reference path="파일경로">의 문법을 사용하여 참조하면 됩니다.

네임스페이스의 이름 확장

네임스페이스에서는 예외로 .을 하용합니다.

점(.)을 사용하면 네임스페이스간의 계층을 만들 수 있습니다.

1
2
3
namespace Animal{...}
namespace Animal.Pet{...}
namespace Animal.Pet.dog{...}

이런식으로 상위부터 하위로 차례대로 선언해주시면 됩니다.

물론 한파일내라면 어떻게든 참조는 가능하기에 문제는 없습니다만 논리적으로 저렇게 쓰도록 추천하고 있습니다.

외부모듈

export로 주로 선언되는 모듈을 외부모듈이라 합니다. 대부분의 모듈이라 하면 외부모듈을 말하는 것이죠.

export를 쓸 수 있는 대상은 변수, 함수, 클래스, 네임스페이스까지 가능하며 공간이 파일 내부로 제한되는 특징이 있습니다.

즉, export로 선언하지 않아서 외부 모듈이 되지 않으면 전역스코프의 공간을 공유하기 때문에 이름 충돌이 발생합니다.

타입스크립트의 경우는 표준인 ES2015외 CommonJS, AMD 등 여러가지 형식도 같이 지원하고 있습니다.

모듈선언과 import

타입스크립트에서 추가된 ES2015의 모듈시스템은 export나 import를 통해 모듈을 선언하고 호출합니다.

간단한 사용법은 다음과 같습니다.

1
2
3
4
5
6
7
//1개짜리일경우
export '노출하고자 싶은 모듈의 이름'
import 불러올 모듈명 from '파일경로'

//여러개를 노출하고 싶은경우
export {'모듈명1', '모듈명2', ...}
import {'불러올 모듈명1', '불러올 모듈명2', ...} from '파일경로'

만약 함수나 인터페이스 단위를 개별적으로 노출하려면 export interface ...이나 export function ...식으로 가능하며 ES6로 컴파일하면 해당 선언문은 그대로 유지됩니다.

이상으로 간단하게 모듈에 대한 설명을 마치겠습니다.