반응형

 

구라제거기 : 한국형 보안설치프로그램 싹 다 갈아엎어 주세욤

https://teus.me/653?category=402265

 

MZK : 한국형 애드웨어 싹 다 갈아엎어 주세욤

https://malzero.xyz/  

 

ADWCleaner : 외국형 애드웨어 싹 다 갈아엎어 주세욤

https://www.malwarebytes.com/adwcleaner/

 

SV : 사이트 차단 싹 다 갈아엎어 주세욤

https://safevisit.org/  

goodbyedpi : 사이트 차단 싹 다 갈아엎어 주세욤2

https://github.com/Include-sys/GUI-for-GoodbyeDPI/releases

(안되면 VPN 돌리세욤)

 

ADGuard : 광고 싹 다 갈아엎어 주세욤

각 브라우저의 확장 프로그램 스토어 이용

https://adguard.com/ko/welcome.html

 

앱체크 : 랜섬웨어 굿바이~

http://blog.checkmal.com/

 

반응형
반응형
반응형
반응형

타입스크립트에서 가장 유용한 타입은 무엇일까요? 저는 any 라고 생각합니다. 항상 타입 검사를 만족시킨다는 특성이 타입스크립트에서도 자바스크립트 모듈을 손쉽게 사용할 수 있게 해주기 때문입니다. 이렇게 자바스크립트의 거대한 생태계를 그대로 활용할 수 있게 해준다는 점에서, any는 타입스크립트의 생산성을 높여주는 유용한 타입입니다.

declare const untypedModule: any;

그럼 타입스크립트에서 가장 유용하지 않은 타입은 무엇일까요? 저는 이 또한 any라고 생각합니다. 항상 타입 검사를 만족시킨다는 특성이 타입 검사의 의의를 퇴색시키기 때문입니다. 어떤 비정상적인 연산이라도 any 타입이 붙어버리면 타입 검사를 통해 걸러낼 수 없기 때문에 any는 프로그램의 안전성을 낮추는 유용하지 않은 타입입니다.

('something' as any) * 10;

마치 양날의 검 같군요, 잘 쓰면 빠르게 프로덕트를 개발할 수 있지만 자칫 잘못 쓰면 되려 버그 지옥에 빠지게 되니까요. 버그 지옥에 빠질 일 없이, 안전하게 any를 쓰는 방법은 없을까요?

있습니다! 그것도 안전한 any를 구현하는 방법이요! 지금부터 알려드리도록 하겠습니다.

안전한 any란?

먼저, 가장 중요한 질문을 던져 보도록 하겠습니다. 안전한 any란 무엇일까요? any의 어떤 성질이 any를 위험하게 만들고 어떤 성질이 any를 가치있게 만들까요?

이 질문에 답하려면 먼저 서브타입 관계(subtype relation)와 탑 타입(top type)을 알아야 합니다.

서브타입 관계

다른 한 타입을 포함하는 타입 슈퍼타입(supertype)이라고 하고, 슈퍼타입에 포함되는 타입 서브타입(subtype)이라고 합니다. ‘타입을 포함한다’의 기준은 타입 시스템 별로 다르지만 구조적 타입 시스템(structural type system)을 가진 타입스크립트의 경우, 한 타입이 다른 한 타입의 값을 모두 포함하고 있으면 그 타입을 포함한다고 합니다.

type Supertype = { x: boolean } type Subtype = { x: boolean, y: number }

위 코드에서, Supertype의 값은 타입이 boolean인 프로퍼티 x를 가진 객체입니다. Subtype의 값은 마찬가지로 타입이 boolean인 프로퍼티 x를 가지지만 동시에 타입이 number인 프로퍼티 y도 가지는 객체이지요.

유심히 살펴보니 Subtype의 값은 타입이 boolean인 프로퍼티 x를 가지는 객체이기도 합니다. 즉, 모든 Subtype의 값은 Supertype의 값이기도 한 것이지요. 이렇게 Supertype이 Subtype을 포함하기 때문에, Supertype은 Subtype의 슈퍼타입이고 Subtype은 Supertype의 서브타입이라고 할 수 있습니다.

이러한 슈퍼타입과 서브타입, 두 타입 간의 포함 관계를 서브타입 관계라고 하며 <:를 통해 서브타입 <: 슈퍼타입 형식으로 표현합니다.

Subtype <: Supertype number <: number | string

심화 문제 #1
StarWars <: Movie <: Entertainment라고 할 때, (starWars: StarWars) => Entertainment는 (movie: Movie) => Movie의 서브타입인가요? 그리고 왜 그렇게 생각하셨나요?

탑 타입

서브타입 관계를 따질 수 있는 타입 시스템에는 탑 타입이라 불리는 특별한 타입이 존재합니다.

탑 타입 모든 타입의 슈퍼타입으로 모든 타입의 값을 값으로 갖지만, 그렇기 때문에 모든 타입의 값에 대해 공통적으로 할 수 있는 연산 외에는 그 어떤 연산도 할 수 없다는 점이 특징입니다. 타입스크립트에서 탑 타입은 unknown입니다.

let top: unknown = 'a'; top = {}; top = 1; top + 1; // Wrong!

심화 문제 #2
왜 탑 타입의 값에 대해서는 모든 타입의 값에 대해 공통적으로 할 수 있는 연산 외에 어떤 연산도 할 수 없을까요?

모순적인 타입, any

any는 모든 타입의 슈퍼타입이기 때문에 탑 타입입니다. 그렇지만, any는 탑 타입의 ‘탑 타입의 값에는 모든 타입의 값에 적용 가능한 연산 외에 어떤 연산도 적용할 수 없다’라는 특징을 가지고 있지 않습니다. 이게 과연 가능한 일일까요?

아니요, 전혀 그렇지 않습니다. 탑 타입은 모든 타입의 값을 갖기 때문에 안전한 타입이 되려면 당연히 모든 타입의 값에 적용 가능한 연산만 적용할 수 있어야 함이 마땅하기 때문이죠. 즉, 탑 타입이면서 탑 타입의 특징을 가지고 있지 않은 타입의 존재는 모순 그 자체이며, 그렇기에 any는 위험한 타입입니다.

any의 가치

그럼 타입스크립트는 왜 이런 모순적인 타입을 가지고 있는 걸까요? 그 이유는 타입스크립트의 디자인 목표에서부터 추측할 수 있습니다.

타입스크립트의 개발 목표는 철옹성과 같은 안전한 타입 시스템을 도입해 자바스크립트에서 발생할 수 있는 모든 오류를 걷어내는 데 있는 게 아니라, 자바스크립트의 생산성을 보전하면서 오류가 될 수 있는 코드들을 걸러주는 거름망 같은 타입 시스템을 도입하는 데 있기 때문에 any와 같이 안전성을 해치지만 생산성을 보전하는 데에 도움이 되는 타입을 만들었다고 생각합니다.

그렇기에 any의 가치는 자바스크립트 코드를 그대로 사용할 수 있게 함으로서 타입스크립트의 생산성을 높여주는 데 있다고 할 수 있겠습니다.


다시 본론으로 돌아가, 계속해서 안전한 any를 정의해 보도록 하지요.

안전한 any는 그리 거창한 게 아닙니다. any의 가치를 보전하면서 any를 위험하게 만드는 성질을 제거한 타입을 안전한 any라고 할 수 있겠지요.

any를 위험하게 만드는 성질은 any가 탑 타입이면서 탑 타입의 특징을 가지지 않는다는 성질이고, any의 가치는 모든 자바스크립트 코드를 수용할 수 있다는 점에 있는데 이 특징은 any가 탑 타입이라는 성질에서 비롯된 것이니 탑 타입의 특징을 제대로 가지고 있는 any가 안전한 any라고 할 수 있겠습니다.

이렇게, 안전한 any가 무엇인지 정의하였으니 지금부터는 안전한 any를 만들기 위한 지식을 익히고 안전한 any를 만들어 보도록 하겠습니다.

제네릭

제네릭(generic)은 특정 개념의 정의에 타입 매개변수(type parameter)를 포함시킬 수 있게 해주는 기능입니다. 여러 타입을 가질 수 있는 일반적인 개념의 타입을 하나로 제한하지 않고, 여러 타입을 가질 수 있게 하기 위해 사용되는 기능입니다.

const identity: <A>(a: A) => A // 타입 A와, 그 타입의 값 a를 받고 A 타입의 값을 반환하는 함수 = a => a; identity<number>(1); // 1 as number identity<boolean>(true); // true as boolean

제네릭에 대한 다른 해석

앞서 보여드린 예시의 identity 함수는 타입 A와 그 타입의 값인 a를 받아 A 타입의 값을 반환하는 함수입니다. 하지만 모든 타입 A에 대해 (a: A) => A 타입을 갖는 함수가 존재한다는 선언으로도 볼 수도 있습니다.

제네릭에 대한 다른 해석은 다음과 같이 의사 코드로 표현할 수 있습니다.

const identity: <forall A>(a: A) => A // 모든 타입 A에 대해 (a: A) => A 타입을 갖는 함수 = a => a;

여기서 forall이 모든 타입(A)에 대해 개념((a: A) => A 타입의 함수)이 존재함을 선언한다고 하여 forall을 보편 양화사(universal quantifier, a.k.a ∀)라고 부릅니다. 또한, 이렇게 보편 양화사를 통해 표현된 타입의 값은 무한한 곱 타입(product type)이나 교차 타입(intersection type)의 값으로도 볼 수 있습니다.

const identity: & ((a: number) => number) & ((a: boolean) => boolean) & ((a: string) => string) & ... = a => a;

심화 문제 #3
무한한 곱 타입의 값으로 identity 함수를 표현해 보세요. 무한한 교차 타입의 값으로 표현된 경우와 같다고 할 수 있나요? 왜 그렇게 생각하셨나요?

심화 문제 #4
<forall A>(a: A) => void 타입과 (a: <forall A>A) => void 타입은 같은가요? 같다면 같은 이유를, 다르다면 다른 이유를 설명해 보세요.

심화 문제 #5
<forall A>() => A 타입과 () => <forall A>A 타입은 같은가요? 이 문제 또한 같다면 같은 이유를, 다르다면 다른 이유를 설명해 보세요.

보편 양화사의 짝

대수 데이터 타입(algebraic data type)에서 곱 타입의 짝이 합 타입(sum type)인 것 처럼, 보편 양화사에게도 무한한 합 타입으로 나타낼 수 있는 짝이 있습니다. 바로 for some 혹은 there exist라고도 불리는 존재 양화사(existential quantifier, a.k.a ∃)이지요. 존재 양화사는 보편 양화사와 달리 모든 타입이 아닌 어떤 타입에 대해 개념이 존재함을 나타내는 양화사입니다.

identity 함수의 forall을 존재 양화사를 나타내는 forsome으로 바꾸어 봅시다.

const identity2: <forsome A>(a: A) => A // 어떤 타입 A에 대해 (a: A) => A 타입을 갖는 함수 = a => a;

외형적으로는 forall이 forsome으로 바뀌었다는 점 외에는 차이가 없습니다만, 의미론적으로 둘 사이에는 큰 차이가 존재합니다. 바로 identity2 함수는 호출될 수 없다는 점이지요. 왜냐면 타입이 호출할 수 있을 만큼 구체적이지 않기 때문입니다.

이 사실은 identity2함수의 타입을 무한한 합 타입(sum type)으로 표현하면 더욱 잘 드러납니다.

const identuty2: | ((a: number) => number) | ((a: boolean) => boolean) | ((a: string) => string) | ... = a => a;

보시다시피 identity2의 타입은 ((a: number) => number) | ((a: boolean) => boolean) | ...으로, 호출할 수 있을 만큼의 구체적인 타입이 아닙니다.

identity2의 타입과 같이 존재 양화사를 통해 정의된 타입을 existential type이라 부르는데요, 이런 타입들은 한 가지 문제를 가지고 있습니다. 바로 한번 existential type으로 업캐스팅(upcasting)을 하면 타입 시스템이 이전 타입을 잊어 버리기 때문에 다시는 다운캐스팅(downcasting)을 할 수 없다는 점입니다.

다시 다운캐스팅 될 수 없기 때문에 existential type으로 업캐스팅 된 값에는 더 제한적인 연산, 즉 existential type을 무한한 합 타입으로 보았을 때 existential type을 구성하는 모든 타입에 대해 가능한 연산만 수행할 수 있습니다. 이러한 제약을 반영해, existential type의 값에 대해 함수를 적용하는 연산은 일반적인 값들과 달리 $나 pipe 함수와 같은 부류의 함수가 아닌 eliminator라는 부류의 함수로 추상화되곤 합니다.

구체적으로, eliminator는 처리하고자 하는 값과 그 값의 타입을 구성하는 모든 타입의 값을 처리할 수 있는 함수를 받아, 그 값을 함수에 적용하여 얻은 결과를 반환하는 함수입니다. 위 identity2 함수와 같은 함수들을 처리하는 eliminator는 아래와 같이 정의할 수 있습니다.

const elimIdentityFunction: <forall R>(id: <forsome A>(a: A) => A, f: <forall A>(x: (a: A) => A) => R) => R = (id, f) => f(id);

심화 문제 #6
elimIdentityFunction(identity2, identity)는 실행 가능한 코드인가요? 왜 그렇게 생각하셨나요?

안전한 any 만들기

지금까지가 안전한 any를 만들기 위해 필요한 배경 지식이었습니다. 이제부터 안전한 any를 만들어 보도록 하지요.

우리의 목표인 안전한 any는 모든 타입의 슈퍼타입, 즉 탑 타입입니다. 그렇기에 모든 타입을 포함해야 하고, 이는 아래와 같이 합 타입을 통해 표현할 수 있겠습니다.

type SafeAny = number | boolean | string | ...;

합 타입을 보면 무언가가 떠오르지 않으시나요? 앞서 무한한 합 타입으로도 취급할 수 있는 것에 대해 이야기했었잖아요. 네! 존재 양화사요!

그러고 보니 타입스크립트에서 타입은 무한히 존재합니다. 이는 배열 타입을 통해 쉽게 증명할 수 있지요.

type ArrayOfNumber = Array<number> type ArrayOfArrayOfNumber = Array<ArrayOfNumber> type ArrayOfArrayOfArrayOfNumber = Array<ArrayOfArrayOfNumber> type ArrayOf...ArrayOfNumber = Array<ArrayOf...ArrayOfNumber>

어떤 자연수 n에 대해 n차원 배열 타입이 존재할 때, n+1 차원의 배열 타입이 존재하며, 1차원의 배열 타입은 항상 존재하니 귀납적으로 모든 자연수에 대해 그 자연수를 차원으로 하는 배열 타입이 존재함을 알 수 있지요. 또한 정수는 무한하니 배열 타입이 무한히 존재함을 이를 통해 알 수 있지요.

다시 탑 타입으로 돌아가 이야기를 계속하자면, 타입 시스템에 있는 타입이 무한하다면 탑 타입은 무한한 합 타입으로 표현될 수 있다는 이야기입니다. 즉, 존재 양화사를 이용해서 탑 타입을 정의할 수 있다는 말이지요.

type SafeAny = <forsome A>A

이것이 바로 탑 타입이자 우리가 지금까지 찾던 안전한 any 타입입니다! 하지만 이 정의에는 문제가 하나 있습니다. 바로 타입스크립트에는 forsome과 같은 직접적으로 존재 양화사를 나타내는 방법이 없다는 점입니다. 위 SafeAny는 우리의 상상 속 타입 시스템에는 존재하나 타입스크립트의 타입 시스템에는 존재하지 않지요.

하지만 걱정 마세요, 존재 양화사는 보편 양화사를 통해 표현될 수 있습니다. 제네릭이란 이름으로 타입스크립트에 있는 우리의 친구를 통해서요!

보편 양화사로 존재 양화사를 표현하는 방법

여기, 존재 양화사로 정의된 함수가 있습니다.

const discard: (_: <forsome A>A) => undefined = _ => undefined;

이 함수의 타입을 무한한 합 타입으로 풀어서 보면

const discard: (_: number | boolean | string | ...) => undefined = _ => undefined;

discard 함수는 number 타입이나 boolean 타입이나 string 타입이나 … 타입의 값을 받아서 undefined 타입의 값으로 바꾸는 함수, 즉 어떤 타입의 값이든 전부 undefined 타입의 값으로 바꾸는 함수라는 것을 알 수 있습니다.

discard 함수의 구현만 보면 이는 더 명백하게 드러납니다.

_ => undefined

여기, 이번에는 제네릭으로 정의된 함수가 있습니다.

const discard2: <A>(_: A) => undefined = _ => undefined;

제네릭을 이용한 선언은 보편 양화사를 이용한 선언으로도 볼 수 있으니 위 코드를 보편 양화사를 이용한 코드로 바꾸어 보겠습니다.

const discard2: <forall A>(_: A) => undefined = _ => undefined;

또한 보편 양화사를 통해 표현된 타입은 무한한 교차 타입으로 표현할 수도 있으니 다시 코드를 바꾸어 보겠습니다.

const discard2: & ((a: number) => undefined) & ((a: boolean) => undefined) & ((a: string) => undefined) & ... = _ => undefined;

바꾼 코드를 보면 discard2 함수가 number 타입의 값을 받아 undefined 타입의 값으로 바꾸는 함수이면서, boolean 타입의 값을 받아 undefined 타입의 값으로 바꾸는 함수이고 … 타입의 값을 받아 undefined 타입의 값으로 바꾸는 함수임을 알 수 있습니다. 그리고 이는 곧 discard2 함수가 어떤 타입의 값이든 전부 undefined 타입의 값으로 바꾸는 함수라는 말과 같지요.

이번에도 discard2 함수의 구현만 보면 이는 더 명백하게 드러납니다.

_ => undefined

지금까지 보셨다시피, number 타입이나 boolean 타입이나 string 타입이나 … 타입의 값을 받아서 undefined 타입의 값으로 바꾸는 함수의 타입은 number 타입의 값을 받아 undefined 타입의 값으로 바꾸는 함수이면서 … 타입의 값을 받아 undefined 타입의 값으로 바꾸는 함수의 타입과 같습니다. 즉, existential type의 값을 받는 함수는 그 타입을 구성하는 모든 타입에 대해 정의된 함수와 같다는 거지요.

이는 아래와 같이 일반화하여 표현할 수 있으며, 이것이 바로 존재 양화사를 보편 양화사로 표현하는 방법 중 하나입니다.

// T는 임의의 제네릭 타입 <forall B>(x: <forsome A>T<A>) => B = <forall B, forall A>(x: T<A>) => B

심화 문제 #7
forall B. forsome A. (x: A) => B 타입을 보편 양화사만 사용해서 표현해 보세요.


좋아요, 이제 존재 양화사를 보편 양화사로 대체하는 방법을 알아냈으니 SafeAny의 정의에서 존재 양화사를 제거할 수 있겠지요? 유감이지만, 아닙니다. SafeAny의 정의에서의 존재 양화사는 매개변수의 타입을 나타내는 데 쓰인 게 아니기 때문에 우리가 알아낸 방법으로 제거할 수 없습니다.

그렇다고 낙담하지는 마세요, 해결책이 있습니다. 바로 SafeAny의 값이 일반적인 값이 아니라, 컨티뉴에이션(continuation)이 되게 만드는 방법입니다.

값을 표현하는 함수

컨티뉴에이션은 값을 표현하는 함수로, 구체적으로는 함수를 받아 자신이 표현하는 값에 그 함수를 적용하는 함수입니다. 놀랍게도, 어떤 값의 컨티뉴에이션은 그 값과 본질적으로 같습니다. 왜냐하면 어떤 값과 그 값의 컨티뉴에이션은 모두 같은 목적으로 쓰일 수 있으며, 한 쪽이 다른 쪽으로 변환될 수도 있기 때문입니다.

예를 들어, 1과 1의 컨티뉴에이션은 다음과 같이 정의할 수 있습니다.

const one = 1; const contOfOne = f => f(1); one + 1 === contOfOne(n => n + 1) // 같은 연산을 수행할 수도 있습니다 one === contOfOne(n => n) && (n => f => f(n))(one)(n => n) === contOfOne(n => n) // 한 쪽이 다른 쪽 표현으로 변환될 수도 있습니다

심화 문제 #8
어떤 값을 받아 그 값의 컨티뉴에이션을 만드는 함수 to와, 컨티뉴에이션을 받아 그 컨티뉴에이션이 표현하는 값을 꺼내는 함수 from을 만들어 보시고, 만든 두 함수를 각기 다른 순서로 합성해 함수 id1과 id2를 만들어 보세요. id1과 id2는 각각 어떤 함수인가요?


SafeAny의 값이 모든 타입의 값에서 모든 타입의 값의 컨티뉴에이션이 되면 SafeAny의 정의를 <forall R>(f: (x: <forsome A>A) => R) => R로 바꿀 수 있고, 이 타입의 존재 양화사는 앞서 우리가 알아낸 방법을 통해 아래와 같이 보편 양화사로 치환할 수 있습니다!

type SafeAny = <forall R>(f: <forall A>(x: A) => R) => R

제네릭과 보편 양화사는 같으므로 보편 양화사를 표현하기 위해 사용한 의사 코드인 forall을 제거해 올바른 타입스크립트 코드로 만들어 줍시다.

type SafeAny = <R>(f: <A>(x: A) => R) => R

이렇게 우리의 안전한 any, SafeAny가 만들어졌습니다! 하지만 아직 모든 일이 끝난 건 아닙니다. SafeAny의 값이 모든 타입의 값이 아닌 모든 타입의 값의 컨티뉴에이션이기 때문에, 모든 타입의 값을 컨티뉴에이션으로 바꿔 주는 함수가 필요합니다.

모든 타입의 값을 컨티뉴에이션으로 바꿔 주는 함수는 아래와 같이 정의할 수 있습니다.

const safeAny: (x: <forsome A>A) => SafeAny = x => f => f(x);

이 함수의 존재 양화사를 보편 양화사로 치환하는 일을 마지막으로, 안전한 any가 완성됩니다.

type SafeAny = <R>(f: <A>(x: A) => R) => R const safeAny: <A>(x: A) => SafeAny = x => f => f(x);

심화 문제 #9
SafeAny의 eliminator를 만들고, 커링해 보세요. 어떤 함수가 보이시나요?

심화 문제 #10
() => string 타입의 toString 메서드가 있어 문자열로 바꿀 수 있는 모든 값을 담는 타입, HasShow를 구현해 보세요.

마무리

이번 글에서는 안전한 any 타입을 만들어 보며 보편 양화사와 존재 양화사에 대해 알아보았습니다. 일반적인 프로그래밍에서는 잘 쓰이지 않고 타입 레벨 프로그래밍에서나 주로 쓰이는, 많은 분들이 생소해 하실 법한 개념이라 최대한 쉽게 설명하고자 노력해 보았는데 그래도 어려운 부분이 남아있는 거 같아 읽으시는 내내 힘들지는 않으셨는지 걱정되네요.

이미 알고 계신 분도 있으시겠지만 사실 이번에 만든 안전한 any 타입은 타입스크립트에 이미 unknown 이란 이름으로 구현되어 있습니다. 심지어 더 사용하기에 편한 형태로요. 물론 그렇다고 해서 지금껏 공부한 개념들이 무용지물이 되는 것은 아닙니다. Existential type으로 탑 타입을 만드는 일만 할 수 있는 게 아니라 타입을 통해 값의 소코프를 결정하는 일(a.k.a ST Trick)이나, 정적 타입 시스템에서 동적 타이핑을 구현하는 일과 같은 재미있는 일들을 많이 할 수 있거든요. 시간 나실때 이런 것들을 직접 찾아보시면서 만들어 보시는 것도 좋을 거 같습니다.

그럼 저는 이만 여기서 글을 마무리하도록 하겠습니다. 긴 글 읽어주셔서 감사합니다.

읽을거리

  • Thinking with Types
    타입 레벨 프로그래밍 책입니다. 대수 타입에서 시작해 의존 타입까지 다룹니다. 이론적인 측면보다는 하스켈을 통한 실용적인 측면에서 이야기를 펼쳐나가기 때문에 논리학이나 타입 이론을 잘 모르는 독자도 큰 문제 없이 읽을 수 있다는 장점이 있습니다.
  • Existential type-curry
    보편 양화사로 존재 양화사를 표현하는 법에 대한 글입니다. 수학적인 증명을 포함해, 더 엄밀하고 정확한 설명이 담겨 있습니다.
  • Quantified Types as Products and Sums
    각 양화사로 표현된 타입을 각각 무한한 곱 타입과 합 타입으로 보는 관점에 대한 토막글입니다.
  • Existential vs. Universally quantified types in Haskell
    존재 양화사로 정의된 타입과 보편 양화사로 정의된 타입의 차이를 묻는 스택오버플로 질문글입니다. 두 답변이 있는데 둘 다 존재 양화사에 대해서 잘 설명하고 있으니 더 알고 싶으시다면 읽어보셔도 좋은 글입니다.

 

출처: https://overcurried.netlify.com/%EC%95%88%EC%A0%84%ED%95%9C%20any%20%ED%83%80%EC%9E%85%20%EB%A7%8C%EB%93%A4%EA%B8%B0/?fbclid=IwAR2EUYFiCUe_FBMt-secTBAdOhWG6y4dFPKwN4KnGcSW-lmJoBpWYQGBWJ0

반응형
반응형

Have you ever wondered what makes a computer fast? Is there one particular part of a computer that makes it fast? A computer’s power and speed come from many different components within the system. We will discuss the most critical parts of a computer and how it makes a computer speedy.

Photo Source: laptopmag

What Are the Different Parts That Make a Computer Fast?

The processor or CPU is one of the main components of a computer. The overall speed of a computer or “clock speed” of a computer is usually determined by how fast it processes data. We are multi-tasking every day, and this is no different when we are on our computers. As you’re reading this, you most likely have multiple windows open at the same time. The more capable your computer is at processing, and executing more instructions per second will determine how fast your computer is.

The first computer processor had a processing speed of 740 kHz and was able to process 92,000 instructions per second. This may sound like many instructions per second, but today’s processors are multi-core GHz processors and can process more than 100 billion instructions per second. The more powerful your processor is, it will be a primary contributing factor to how fast your overall computer is. A Dual Core or a Quad-Core should be enough for your everyday needs.

Photo Source: itpro

Another critical aspect of a computer’s speed is the Cache. While today’s machines can execute 100 billion instructions every second, the processor is usually waiting on instructions from the Hard Drive and RAM. Since these are slower types of memory, the computer uses Cache to transfer data between the CPU, memory, and all of the components within the computer. The cache is the fastest type of memory inside a computer. The level 2 and level 3 cache will help the computer store and send more instructions. The more cache a computer has will help make the computer run more efficiently.

Photo Source: techblick

The next part of what makes a computer run faster is RAM or Random Access Memory. RAM is the computer’s short-term data storage. This stores the information your computer is actively using. This short-term memory allows your computer to access information more quickly. If you are using multiple programs at the same time, the more RAM you will need.

Bus speed is another important part of what makes a computer faster. A bus is a circuit that connects the different parts of the motherboard. The more data the bus can handle at one time will allow the information to travel faster. Bus speed is measured in megahertz. Bus speed refers typically to the front side bus or FSB. The FSB is what connects the CPU to the Northbridge (a chipset that communicates with the computer processor and controls interaction with memory). Since FSB is a middleman of sorts, its speed can drastically affect a computer’s performance.

Photo Source: digitaltrends

The Hard Drive is also one of the main parts of a computer that contributes to its speed. A Hard Disk Drive or HDD has moving parts, which may result in some slower read and write times (mainly depending on HDD specs). A 5400 RPM hard drive will be slower than a 7200 RPM hard drive. A solid-state drive or SSD has no moving parts but instead uses flash memory, which often time reads and writes faster than a traditional HDD. Although one is newer than the other, there is still a debate on which is better: HDD vs. SSD. There are benefits to both types of hard drives.  A hard drive with a larger cache will allow data to be handled more efficiently.

Photo Source: blazinglist

The last piece of hardware vital to a computer’s speed is the Video Card or Graphics Processing Unit (GPU). Replacing the GPU may be the thing that adds the most speed to your computer. The GPU also needs access to RAM to function. Onboard graphics cards dedicate part of its system RAM to graphics functions. Adding a completely separate GPU will potentially add speed to your current onboard RAM, and it will also have its own set of RAM. Because of this, the graphics processing and overall system multitasking will see a boost in speed.

Photo Source: colocationamerica

Besides having the best hardware, having the latest operating system will also contribute to a faster computer. This also includes the latest software updates. Software updates often repair security holes and bug repairs or fixes, which can all make your computer run slower. Keeping a computer operating system up-to-date is a vital part of its speed, efficiency, and security.  Another reason to have the latest operating system and software is that new technology is being introduced every day, and having the proper system to support the new technology is a must.

How Fast Does Your Computer Need to Be?

Computers are quite powerful these days. Most have more power than the average user will ever need. However, we all want the best. The convenience of having a quick computer makes our lives easier. Whether it’s checking email or scrolling through the Internet, most new computers are powerful enough to handle our daily tasks. We don’t want to wait when inspiration is flowing or when you’re in the middle of a project. We don’t want to wait for our computers when we’re ready to go.

This is especially true if you edit pictures and videos, use your computer to record music, or if you’re a gamer, your computer may need an upgrade from many out of the box setups. The computer works harder during these tasks and might need an upgrade, but computers are fast enough to handle most of the things we throw at them these days.

Photo Source: lifewire

Conclusion

A computer’s speed is the sum of its parts. A computer is an intricate system that uses many different parts to perform the various functions we ask of it. So, if someone ever asks you, “What makes a computer fast?” it’s all of the above. You will need the best processor or CPU, cache, RAM, bus speed, Hard Drive, GPU, and the latest software and operating system. All of these aspects, working together in harmony, are what makes a computer fast. If one of these aspects isn’t as fast or powerful as the rest of the others, your computer may experience a loss of speed. Sometimes cleaning up your computer’s drives and troubleshooting your network may be all it takes to speed up your computer just enough.

반응형
반응형

들어가며

calendar 프로젝트를 진행하면서 날짜 관련정보를 다루었다(Date 객체). 프로젝트 중에 날짜 관련 data를 쉽게 다룰수 있는 라이브러리를 발견하여 이에 대해서 간단히 정리해보고자 한다. Moment.JS 는 날짜(Date)형식의 데이터의 파싱, 검증, 조작 그리고 화면에 출력하는 작업을 위해 만들어진 아주 유용한 라이브러리이다.

프로젝트를 통해 느낀 Date object의 불편함 & Moment.JS의 편리함

Date object와 관련된 method를 사용하면서 불편한점이 많았다. 한가지 예를 들면 ISO 포맷형태의 날짜data(ex. 2017-01-25)를 얻기 위해서, 시간이 까지 포함된 ISOString형태(ex. 2017-01-25T12:25:00.000z)에서 T를 기준으로 split method를 사용하여 ISO 포맷형태의 날짜를 얻을수 있었다. 코드는 아래 와 같다.(바로 구하는 method는 없는것 같다.)

var d= new Date(); // 출력형태 Tue Feb 07 2017 23:25:32 GMT+0900 (KST) var ISOData = d.toISOString(); // 출력형태 2017-02-07T13:39:45.148Z var ISODate = ISOData.split("T",1); // 출력형태 2017-02-07

Date object를 사용하면서 부분적인 시간과 날짜 정보를 얻기 위해서 위와 같이 문자열을 쪼개어서 얻는것이 나이스(?)하지 못하다고 느꼈다. 위와 같은 코드를 Moment.js를 사용하여 구하려면 어떻게 하면 될까?

var ISODate = moment().format('YYYY[-]MM[-]DD');

위와 같이 보다 직관적인이고 간단한 코드를 통해서 ISO 포맷 형태의 날짜 정보를 얻을수 있다.

다른 예를 들자면 ISO 포맷형태의 TimeString간의 시간차이를 구하기 위해서는 내가 아는바로는 시간차이를 바로 구할수있는 method는 없었고, 역시 문자열 조작과 기존 Date object를 통해서 시간차이를 계산할수 있었다.

 

var time1 = "2017-02-01T12:25:00Z"

var time2 = "2017-02-04T12:20:00Z"

 

function dateFromISO(isostr) {

var parts = isostr.match(/\d+/g);

return new Date(parts[0], parts[1] - 1, parts[2], parts[3], parts[4], parts[5]);

};

 

var ParsedTime1= dateFromISO(time1);

var ParsedTime2= dateFromISO(time2);

var timeDifference = parsedTime2-parsedTime1 ; // 출력 단위 밀리초(ms)

 

위와 같이 정규표현식을 사용하여 기존ISOString 형태 시간 데이터를 쪼개어 배열에 저장하고, Date object를 통해서 시간정보로 가공하였다. 시간차이를 계산하기 위해서 이와같이 시간정보로 바꿔서 계산해야 한다.(이 역시 곧바로 구하는 method는 없는것 같다.) 위와 같은 함수를 생각해내지 못하면 해결(?)하지 못할수도 있다. 나이스(?)하지 못하다..

위와 같은 코드를 역시 Moment.JS를 사용하여 구하면 어떻게 될까?

 

var time1 = "2017-02-01T12:25:00Z"

var time2 = "2017-02-04T12:20:00Z"

var ms = moment(time2,"YYYY-MM-DDTHH:mm:ssZ").diff(moment(time1,"YYYY-MM-DDTHH:mm:ssZ")); // 출력 단위 밀리초(ms)

 

ISOstring형태의 시간정보를 다른 시간형태의 시간정보로 가공하지 않아도 되고, diff라는 method를 통해서 시간차이를 구하였다. 얼마나 직관적이고 간편한가!

나는 Javascript를 배우는 중이라서 다른 라이브러리를 사용하지 않은채 개발하였지만, 시간 관련 정보를 다루는 프로젝트를 개발하는 사람들에게 Moment.JS를 적극 추천한다.

Moment.JS의 method에는 많은것들이 있다. 이중에서 몇가지만 간단히 정리하고자 한다.

현재시간

현재시간을 얻기 위해서는 매개변수 없이 아래와 같이 호출하면 된다.

 

var now = moment();

 

문자열(String)을 통한 시간정보 생성

문자열(ex. "1989-03-15")을 통해서 시간을 생성하고자 할때는 아래와 같이 호출하면 된다.

 

var time = moment("2017-01-01");

 

위와 같이 " YYYY-MM-DD "형태의 string 외에도 " YYYY-MM-DD HH:MM:SS "등과 같은 형태의 시간문자열이 있을수 있다. 가능한 문자열 형태는 이 링크에서 확인하기를 바란다. 만약불가능한 형태의 시간 string이라면 아래 method를 사용하여 true , false 값으로 확인할수 있다.

 

var time = momnet("시간문자열").isValid(); //return true or false

 

정형화된 시간정보(Format Dates)

아래코드와 같이 자신이 원하는 형태의 시간정보를 생성할수 있다.

 

moment().format('MMMM Do YYYY, h:mm:ss a'); // February 8th 2017, 6:30:39 pm

moment().format('dddd'); // Wednesday

moment().format("MMM Do YY"); // Feb 8th 17

moment().format('YYYY [escaped] YYYY'); // 2017 escaped 2017

moment().format(); // 2017-02-08T18:30:39+09:00

 

상대적인 시간(Relative Time)

특정시간정보가 지금으로부터 어느정도의 시간거리를 가지고 있는지 알수 있다.

 

moment("20111031", "YYYYMMDD").fromNow(); // 5 years ago

moment("20120620", "YYYYMMDD").fromNow(); // 5 years ago

moment().startOf('day').fromNow(); // 19 hours ago

moment().endOf('day').fromNow(); // in 5 hours

moment().startOf('hour').fromNow(); // 34 minutes ago

 

달력관련 날짜 및 시간정보

 

moment().subtract(10, 'days').calendar(); // 01/29/2017

moment().subtract(6, 'days').calendar(); // Last Thursday at 6:36 PM

moment().subtract(3, 'days').calendar(); // Last Sunday at 6:36 PM

moment().subtract(1, 'days').calendar(); // Yesterday at 6:36 PM

moment().calendar(); // Today at 6:36 PM

moment().add(1, 'days').calendar(); // Tomorrow at 6:36 PM

moment().add(3, 'days').calendar(); // Saturday at 6:36 PM

moment().add(10, 'days').calendar(); // 02/18/2017

 

배열(Array)를 이용한 시간정보 생성

배열을 통해서 시간정보를 생성할수도 있다.

배열은 입력된 배열의 길이 만큼 앞에서부터 길이만큼 아래 배열에서 끊어서 의미를 확인하면된다.

[year, month, day, hour, minute, second, millisecond]

 

예제코드

moment([2010, 1, 14, 15, 25, 50, 125]); // February 14th, 3:25:50.125 PM

moment([2010]); // January 1st

moment([2010, 6]); // July 1st

moment([2010, 6, 10]); // July 10th

 

시간차이 계산

월차이를 구하는 코드 예제를 보자. 간편하다

 

moment('2016-06','YYYY-MM').diff('2015-01','month'); //17

 

여러가지 메소드와 경우의 수가 존재하므로 설명없이 예제코드들을 나열해보록 하겠다. 필요한 예제들만 골라서 사용하면 되겠다.(설명이 필요할것 같은 부분만 주석으로 설명하겠다.)

 

moment().format() //2017-02-08T18:44:10+09:00

moment().toString() //Wed Feb 08 2017 18:44:10 GMT+0900

moment('2013-11-16') - moment('2013-11-15') //86400000

moment().endOf('day') //1486565999999

 

var now = moment('2013-11-22'); //finish와 같은날로 지정

var start = moment('2013-11-10');

var finish = moment('2013-11-22');

var event = moment('2013-11-16').endOf('day'); //2013년 11월 16일의 끝나는 시점. 2013년 11월 16일 11시 59분 59초

 

event.diff(start) //604799999 event시점에서 start시점을 뺀 시간 단위 밀리초 (시간정보는 시간이 흐를수록 값이 커진다.)

event.diff(finish) //-432000001

 

moment().format('YYYY-MM-DD') //2017-02-08

moment().format('YYYY') //2017

moment().format('MM-DD') //02-08

moment(start).add('years',1).calendar() //11/10/2014 start시점에 1년을 더한다.

 

now.diff(start) > 0 && now.diff(finish) < 0 //false now시점이 start와 finish 사이에 있는지

 

moment('Tue Dec 23 12:02:08 EST 2014').format() //2014-12-24T02:02:08+09:00

moment().format('MMMM Do [at] h:mm:ss a') //February 8th at 6:44 pm [] 괄호로 감싸서 데이터 중간에 문자나 특수문자 입력가능

moment('2015/04/02 12:00').subtract('hours',9).format('YYYY/MM/DD hh:mm') // 2015/04/02 03:00 시점에서 9시간을 빼고 정해진 포맷대로 출력

 

moment('1/1/0001').format() // 2001-01-01T00:00:00+09:00

moment('0001-01-01').format() //0001-01-01T00:00:00+09:00

moment().format('MMMM D, YYYY') // February 8, 2017

 

now.isBetween(start, finish, '(]')) //true start 시점 < now시점 <= finish시점인지

 

위와 같이 몇가지만 간단히 정리해보았지만 여러가지 조합과 method가 있으니 더 많은 정보를 얻고자 하는 사람은 아래 사이트를 참고하길 바란다.

 

https://momentjs.com

 

Moment.js | Home

Format Dates moment().format('MMMM Do YYYY, h:mm:ss a'); moment().format('dddd'); moment().format("MMM Do YY"); moment().format('YYYY [escaped] YYYY'); moment().format(); Relative Time moment("20111031", "YYYYMMDD").fromNow(); moment("20120620", "YYYYMMDD"

momentjs.com

출처: https://github.com/jinyowo/JS-Calendar/wiki/**-moment.js-(%EB%82%A0%EC%A7%9C%EA%B4%80%EB%A0%A8-%EC%9E%91%EC%97%85%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC)

반응형
반응형

VueJS and Axios GitHub – axios/axios: Promise based HTTP client for the browser and node.js work beautifully together for making HTTP requests. However, uploading files with VueJS and Axios can be a little bit challenging since it requires uploading files through an AJAX type system. I’ll be honest, I hate doing file uploads, but they are a necessity for the majority of applications. This tutorial should be a quick start guide on the process and a few tips I’ve learned when handling file uploads to make your life a little easier.

Prerequisites

In this tutorial I’m going to be using Axios v0.16.2 and VueJS v2.1.0 for doing the file uploads. On the back end you can use the framework that you want, or language that you want. I’ll talk about using PHP/Laravel to process files and more or less pseudo-code the backend process. The biggest take aways will be how to do the uploading of the file with VueJS and Axios.

I’m also going to assume you are using a modern browser that supports the FormData object: FormData – Web APIs | MDN. This is what makes the process a whole heck of a lot easier.

Uploading a Single File

So first, we will start with a single file to get things going. I just built a simple component that contains a file input:

<template> <div class="container"> <div class="large-12 medium-12 small-12 cell"> <label>File <input type="file" id="file" ref="file" v-on:change="handleFileUpload()"/> </label> <button v-on:click="submitFile()">Submit</button> </div> </div> </template>

If you take a look at the input[type="file"] I added a few attributes. The first is a ref attribute that gives this input a name. We can now access this input from within VueJS which we will in a second.

The next is the v-on:change="handleFileUpload()" attribute. When the user uploads a file, this gets called and we can handle the file. We will be implementing this method in the next step.

The last element in the simple form is a button that calls a method submitFile() when clicked. This is where we will submit our file to the server and we will be implementing this method as well.

 

Handle User File Upload

The first thing we want to do is add the handleFileUpload() method to our methods object which will give us a starting point for implementation:

<script> export default { methods: { handleFileUpload(){ } } } </script>

What we will do in this method is set a local variable to the value of the file uploaded. Now since we are using a modern browser, this will be a FileList object: FileList – Web APIs | MDN which contains File objects: File – Web APIs | MDN. The FileList is not directly editable by the user for security reasons. However, we can allow users to select and de-select files as needed, which we will go through later in the tutorial.

Since we are setting a local piece of data, let’s add that right now to our Vue component. In our data() method add:

data(){ return { file: '' } },

Now we have something to set in our handleFileUpload() method! Let’s go back and add the following code to the handleFileUpload() method:

methods: { handleFileUpload(){ this.file = this.$refs.file.files[0]; } }

What this does is when the file has been changed, we set the local file variable to the first File object in the FileList on the input[type="file"]. The this.$refs.file refers to the ref attribute on the the input[type="file"]. This makes it easily accessible within our component.

Submit To Server Through Axios

Now it’s time to submit our file through the server through Axios! On our button, we have a submitFile() method we need to implement, so let’s add this to our methods:

submitFile(){ },

Now this is where we implement our axios request.

The first thing we need to do is implement a FormData object like this:

let formData = new FormData();

Next, what we will do is append the file to the formData. This is done through the append() method on the object: FormData.append() – Web APIs | MDN. What we are doing is essentially building a key-value pair to submit to the server like a standard POST request:

formData.append('file', this.file);

We just append the file variable that we have our data stored in. Now I’m not going to go into validations, but before we go any further, if you were to put this in production and the file needed to be added, I’d add a validation here to make sure the file variable contains an actual file.

Now we can begin to make our axios request! We will be doing this through the post() method. If you look at their API (GitHub – axios/axios: Promise based HTTP client for the browser and node.js), you see the post() method contains 3 parameters. The third parameter is a config for the request which is awesome because we can add other headers to it.

Our completed request should look like:

axios.post( '/single-file', formData, { headers: { 'Content-Type': 'multipart/form-data' } } ).then(function(){ console.log('SUCCESS!!'); }) .catch(function(){ console.log('FAILURE!!'); });

The first parameter is the URL we will be POSTing to. For this example, I have a URL set up on my server which is /single-file. The next parameter is a key-value store of the data we are passing. This is our FormData() which we built to have our file. The third parameter is probably the key to making this all work. This is adding the multipart/form-data header we need to send the file to the server.

If you are used to file uploading, this is usually an attribute on the form you are submitting like <form enctype="multipart/form-data"></form>. Without this header, the POST request will ignore the file.

Now with the rest of our request, we process a callback method on a successful request which can be used to display a notification and we process a callback on failure which can be used to alert the user of an unsuccessful upload.

On the server side, you can access the file through the key of file which is the first parameter of the formData.append('file', this.file); method.

In PHP it’d be: $_FILES['file'] and in Laravel, you can use the Request facade and access it through Request::file('files') and do whatever server side processing you need.

Our SingleFile.vue component used for testing looks like this:

<template> <div class="container"> <div class="large-12 medium-12 small-12 cell"> <label>File <input type="file" id="file" ref="file" v-on:change="handleFileUpload()"/> </label> <button v-on:click="submitFile()">Submit</button> </div> </div> </template> <script> export default { /* Defines the data used by the component */ data(){ return { file: '' } }, methods: { /* Submits the file to the server */ submitFile(){ /* Initialize the form data */ let formData = new FormData(); /* Add the form data we need to submit */ formData.append('file', this.file); /* Make the request to the POST /single-file URL */ axios.post( '/single-file', formData, { headers: { 'Content-Type': 'multipart/form-data' } } ).then(function(){ console.log('SUCCESS!!'); }) .catch(function(){ console.log('FAILURE!!'); }); }, /* Handles a change on the file upload */ handleFileUpload(){ this.file = this.$refs.file.files[0]; } } } </script>

The next section, we will handle multiple files. This isn’t anything super different, but I’ll point out the changes!

Uploading Multiple Files

Handling multiple files is very similar to a single file. What we will do is begin with a template that looks like this in our Vue component:

<template> <div class="container"> <div class="large-12 medium-12 small-12 cell"> <label>Files <input type="file" id="files" ref="files" multiple v-on:change="handleFileUploads()"/> </label> <button v-on:click="submitFiles()">Submit</button> </div> </div> </template>

Besides the ref attribute name change and the id changing to files, the most important attribute is we added multiple to our input[type="file”]. This allows the user to cmd (ctrl) + click to select multiple files at once. A super slick way to upload files. In the next section we will be allowing the user to remove files and select more files if they made a mistake 😉 but for now, it’s super slick.

 

Multiple File handleFileUploads() method

This is very similar to an individual file, except we will be adding all of the files to our array if the user selects more than one. First, let’s add our data store to our Vue component and give it variable named files:

/* Defines the data used by the component */ data(){ return { files: '' } },

Now we have a local variable to store our files to. We can now do our handleFileUploads() method:

/* Handles a change on the file upload */ handleFilesUpload(){ this.files = this.$refs.files.files; }

What this does is grab all of the files in the FilesList from our files upload and stores it locally.

Implement submitFiles() method

We are ready to submit all of our files to the server now! First, let’s add our submitFiles() method to the methods array:

/* Submits all of the files to the server */ submitFiles(){ },

Like in the last method, we will initialize the FormData() object first:

/* Initialize the form data */ let formData = new FormData();

Now, what we will do is loop over all of the files selected and add them to the files array we are going to submit to the server. The files array will be a key in the formData() object we will be sending to the server:

/* Iteate over any file sent over appending the files to the form data. */ for( var i = 0; i < this.files.length; i++ ){ let file = this.files[i]; formData.append('files[' + i + ']', file); }

We are now ready to send our files to the server through Axios:

/* Make the request to the POST /multiple-files URL */ axios.post( '/multiple-files', formData, { headers: { 'Content-Type': 'multipart/form-data' } } ).then(function(){ console.log('SUCCESS!!'); }) .catch(function(){ console.log('FAILURE!!'); });

There we go! Now we are allowing users to upload multiple files using Axios and VueJS through an AJAX call.

On the server side, you can access the files through the key of files which is the first parameter of the formData.append('files[' + i + ']', file); method.

In PHP it’d be: $_FILES['files'] and in Laravel, you can use the Request facade and access it through Request::file('files') and do whatever server side processing you need. You can loop through all of the files now to allow for multiple uploads.

Our MultipleFiles.vue component should look like:

<template> <div class="container"> <div class="large-12 medium-12 small-12 cell"> <label>Files <input type="file" id="files" ref="files" multiple v-on:change="handleFilesUpload()"/> </label> <button v-on:click="submitFiles()">Submit</button> </div> </div> </template> <script> export default { /* Defines the data used by the component */ data(){ return { files: '' } }, methods: { /* Submits all of the files to the server */ submitFiles(){ /* Initialize the form data */ let formData = new FormData(); /* Iteate over any file sent over appending the files to the form data. */ for( var i = 0; i < this.files.length; i++ ){ let file = this.files[i]; formData.append('files[' + i + ']', file); } /* Make the request to the POST /multiple-files URL */ axios.post( '/multiple-files', formData, { headers: { 'Content-Type': 'multipart/form-data' } } ).then(function(){ console.log('SUCCESS!!'); }) .catch(function(){ console.log('FAILURE!!'); }); }, /* Handles a change on the file upload */ handleFilesUpload(){ this.files = this.$refs.files.files; } } } </script>

The next step we will allow users to edit the files they have selected so they don’t accidentally upload a file they don’t want.

Allowing Users to Edit Selected Files Before Uploading

When uploading multiple files, it’s very common that you accidentally select a file you do NOT want to upload. This sounds simple enough to resolve until you find out you can’t directly edit the FileList object for security reasons. However, you can transform it and edit the new list as an array and allow users to change the files they want uploaded.

First, let’s reuse the template from the multiple files component:

<template> <div class="container"> <div class="large-12 medium-12 small-12 cell"> <label>Files <input type="file" id="files" ref="files" multiple v-on:change="handleFilesUpload()"/> </label> <button v-on:click="submitFiles()">Submit</button> </div> </div> </template>

Hide File Input

The first thing we will do is hide the actual file input. This is because we will be making a simple design interface to allow users to select the files they want. I just added a style tag that moves the input off of the screen. This is because it’s a security issue to trigger a click on the hidden file input.

<style> input[type="file"]{ position: absolute; top: -500px; } </style>

Next, I added a button that triggers a click on the input:

<div class="large-12 medium-12 small-12 cell"> <button v-on:click="addFiles()">Add Files</button> </div>

So when this is clicked, we trigger a click on the file element. We need to implement the addFiles() method in our Vue component like this:

addFiles(){ this.$refs.files.click(); }

This will fire a click on the files input and the user will be prompted with a file selection box where they can select the files we want.

Implement handleFilesUpload()

This is where things get a little bit different. Like the first two examples, we will add a local variable to add files to:

data(){ return { files: [] } },

We want this as an array so we can push files onto it.

Now, when the user selects some files to upload, we will push them on our local files variable:

let uploadedFiles = this.$refs.files.files; /* Adds the uploaded file to the files array */ for( var i = 0; i < uploadedFiles.length; i++ ){ this.files.push( uploadedFiles[i] ); }

We do this through a loop instead of pushing the entire chunk onto the files array because otherwise we’d have groups based on what was selected. You can add validations here as well so the user doesn’t upload the same file multiple times if you want as well.

Display Currently Uploaded Files

In this use case, we want users to remove files they updated by accident, so we need to display the currently uploaded files.

To do that, we will head back into our template and add the following code:

<div class="large-12 medium-12 small-12 cell"> <div v-for="(file, key) in files" class="file-listing">{{ file.name }} <span class="remove-file" v-on:click="removeFile( key )">Remove</span></div> </div> <br>

What this does is iterate over all of the files we’ve currently added and displays them to the user. A couple things to note.

First, the v-for="(file, key) in files". What this does is iterate over the files that we’ve uploaded and grabs the key which is the index of the file in the files array and the file itself. We then display the name of the file with: {{ file.name }} which is part of the individual file object. There’s more information in the object which is documented here: File – Web APIs | MDN

Next, we add a removeFile(key) method which will remove the file from the file array. When the file is removed, the reactive nature of VueJS will update our listing.

Implement removeFile() Method

This method will remove the file from our uploaded files array. First, let’s add the method to our methods array:

removeFile( key ){ }

The method accepts the key in the files array of the file we are removing. The full implementation of this method will be:

removeFile( key ){ this.files.splice( key, 1 ); }

What this does is splice the files array at the index of the file we are removing and remove 1 entry from the array. When we do this, our list will re-render through VueJS keeping everything in sync. Since we are using a local files array, we can modify this at will. The next and final thing we have to do is submit our files to the server that the user has selected!

Submit Files To Server

From here, we’ve allowed the user to modify the files they have selected, we just have to allow them to submit to the server for processing.

First, let’s add the submitFiles() method to our methods object:

submitFiles(){ }

Like the rest of the examples, let’s first create our FormData() object:

/* Initialize the form data */ let formData = new FormData();

Now, let’s add all of the chosen files to the form data:

/* Iterate over any file sent over appending the files to the form data. */ for( var i = 0; i < this.files.length; i++ ){ let file = this.files[i]; formData.append('files[' + i + ']', file); }

This iterates over the files that the user has selected and prepares to submit them to the server.

Now, we can run the axios.post() method to submit the files to our endpoint:

axios.post( '/select-files', formData, { headers: { 'Content-Type': 'multipart/form-data' } } ).then(function(){ console.log('SUCCESS!!'); }) .catch(function(){ console.log('FAILURE!!'); });

This sends all of our form data to the server with the files that the user has uploaded! If you were to run this as an example, you can see that after you remove a file, it’s no longer sent to the server.

Like before, on the server side, you can access the files through the key of files which is the first parameter of the formData.append('files[' + i + ']', file); method.

When using Laravel and the Request facade, you can access the selected files the user has uploaded with the following method: Request::file('files'). In straight up PHP it’d be $_FILES['files']. You can now do any processing you want!

Our SelectFiles.vue component should look like:

<style> input[type="file"]{ position: absolute; top: -500px; } div.file-listing{ width: 200px; } span.remove-file{ color: red; cursor: pointer; float: right; } </style> <template> <div class="container"> <div class="large-12 medium-12 small-12 cell"> <label>Files <input type="file" id="files" ref="files" multiple v-on:change="handleFilesUpload()"/> </label> </div> <div class="large-12 medium-12 small-12 cell"> <div v-for="(file, key) in files" class="file-listing">{{ file.name }} <span class="remove-file" v-on:click="removeFile( key )">Remove</span></div> </div> <br> <div class="large-12 medium-12 small-12 cell"> <button v-on:click="addFiles()">Add Files</button> </div> <br> <div class="large-12 medium-12 small-12 cell"> <button v-on:click="submitFiles()">Submit</button> </div> </div> </template> <script> export default { /* Defines the data used by the component */ data(){ return { files: [] } }, /* Defines the method used by the component */ methods: { /* Adds a file */ addFiles(){ this.$refs.files.click(); }, /* Submits files to the server */ submitFiles(){ /* Initialize the form data */ let formData = new FormData(); /* Iteate over any file sent over appending the files to the form data. */ for( var i = 0; i < this.files.length; i++ ){ let file = this.files[i]; formData.append('files[' + i + ']', file); } /* Make the request to the POST /select-files URL */ axios.post( '/select-files', formData, { headers: { 'Content-Type': 'multipart/form-data' } } ).then(function(){ console.log('SUCCESS!!'); }) .catch(function(){ console.log('FAILURE!!'); }); }, /* Handles the uploading of files */ handleFilesUpload(){ let uploadedFiles = this.$refs.files.files; /* Adds the uploaded file to the files array */ for( var i = 0; i < uploadedFiles.length; i++ ){ this.files.push( uploadedFiles[i] ); } }, /* Removes a select file the user has uploaded */ removeFile( key ){ this.files.splice( key, 1 ); } } } </script>

There ya go! You can now allow users to adjust their mistakes if they select a file they don’t want to upload.

Gotchas and Recommendations

A few things to point out when uploading using FormData.

Adding additional POST data to the Request

You can always include more information than just files with your post. When you build your FormData you can add additional text or other fields like this:

/* Initialize the form data */ let formData = new FormData(); /* Iteate over any file sent over appending the files to the form data. */ for( var i = 0; i < this.files.length; i++ ){ let file = this.files[i]; formData.append('files[' + i + ']', file); } /* Additional POST Data */ formData.append('first_name', 'Dan'); formData.append('last_name', 'Pastori');

The first_name and last_name fields will be accessible on the server just like a normal post request!

 

Arrays with FormData()

Now since we are configuring our request before we send it to the sever, arrays get accessed differently. You will either need to account for this when building your FormData object. In VueJS, when working with arrays, you can’t just do:

this.coolData = ['one', 'two', 'three']; formData.append('cool_data', this.coolData);

or you will get an [object Object] on the server side. You can either iterate over your cool data and push it on a nicely organized array or you can do a JSON.stringify() method on the data which will convert it to JSON before sending it to the server.

this.coolData = ['one', 'two', 'three']; formData.append('cool_data', JSON.stringify( this.coolData ) );

You will just need to decode it before you can access it. In PHP, that method would be json_decode($json).

Clearing local files on success

When axios returns success, another quick tip is to reset your local files array back to nothing. This makes sure that if you are doing things in a single page application type way or any AJAX driven way, the user who initially submitted the files doesn’t try to re-send a different group of files and gets the first group of files that still exist in the array sent along as well. You can simply clear your local files like this:

this.files = '';

Conclusion

After running through this a few times, uploading files with VueJS and Axios through AJAX becomes a little easier to grasp! Hopefully this tutorial has helped a little. There is always room for expansion and you can do progress uploads and other cool features.

This type of file uploading process comes in handy when developing an API Driven Application, especially when using VueJS. We are working on a book that will tie in this feature along with a whole bunch more API Driven ideals. We will go start to implementation to launch of an API Driven Application and point out common gotchas and tips. Sign up here to stay in the loop as the book progresses: Server Side Up General List

반응형
반응형

윈도우 PC에서 원격접속을 할 수 있다는건 많은 분들이 알고 계실겁니다. 인터넷의 발달로 원격접속 하여 집, 회사에 있는 컴퓨터에 접속해서 필요한 파일을 쉽게 접근할 수 있어서 적지 않은 사람들이 사용하고 있습니다. 크롬 브라우저를 통해 원격접속이 가능하다는 사실을 아시는 분이 생각보다 많지 않아서 ‘Chrome Remote Desktop’에 대해서 알아보도록 하겠습니다.

Remote Desktop은 크롬북 뿐만아니라 크롬 브라우저를 사용할 수 있는 환경이라면 운영체제에 상관없이 이용할 수 있는 장점이 있습니다. Ubuntu 환경에서 윈도우 PC에 접속할 수도 있듯이 말이죠. 크롬북은 크롬 브라우저를 이용하니깐 당연히 Remote Desktop을 이용할 수 있습니다.

그럼 지금 부터 크롬북을 사용하는데 윈도우 PC에 접속하고 싶을 때  Chrome Remote Desktop를 이용하는 과정을 알아보도록 하겠습니다. Chrome 웹 스토어에서 애플리케이션 설치부터 사용과정까지 알아보도록 하겠습니다.

  • ‘Chrome 원격 데스크톱’ 애플리케이션 설치하기

    Chrome 웹 스토어(https://chrome.google.com/webstore/category/apps)에 접속하여 ‘chrome remote desktop’를 설치합니다. ‘Chrome 원격 데스크탑’이란 한글명을 가진 애플리케이션을 설치하면 됩니다.
    애플리케이션은 접속하고자하는 PC와 접속하려는 PC에 모두 설치를 하셔야 원격 접속이 가능합니다.
    이해를 돕기 위해 포인투에서는 Poin2 chromebook을 이용하여 개인 윈도우 PC에 접속하고자 합니다. 이 때 Poin2 chromebook과 개인 윈도우 PC에 ‘Chrome 원격 데스크탑’을 설치하도록 합니다.
    아래 사진은 크롬북과 윈도우 PC에 설치된 ‘Chrome 원격 데스크톱’의 모습입니다.

     

  • 애플리케이션 로그인하기

    설치된 ‘Chrome 원격 데스크톱’ 애플리케이션을 실행하여 계정정보를 입력합니다. 윈도우 PC와 크롬북에 각각 동일한 계정으로 로그인 해야합니다.
    ‘Chrome 원격 데스크탑’은 계정정보를 이용하여 원격접속을 하는 방식이기 때문에 꼭 동일한 계정으로 로그인하여 원격접속을 진행해야 합니다.

  • 액세스 권한 승인

    로그인하여 컴퓨터 액세스 권한에 관한 내용을 승인한 후 사용하면 크롬북에서 윈도우 PC에 원격접속하여 이용할 수 있습니다.

     

  • PIN번호 설정하기

    로그인 후 접속하고자하는 컴퓨터(윈도우PC)에서 PIN번호를 설정하여 접속하려고하는 컴퓨터(Chromebook)에서 PIN번호 확인을 통해 ‘Chrome 원격 데스크톱’을 이용할 수 있도록 합니다.
    계정의 해킹등 본인외 외부에서의 접속을 차단하기 위해 PIN번호는 최소 6자리 이상을 입력하여 사용하면 됩니다.

  • ‘Chrome 원격 데스크톱’ 이용하기


‘Chrome 원격 데스크톱’을 이용하기 위한 모든 과정을 마치고 접속하고자 하는 컴퓨터(윈도우 PC)를 위의 모습과 같이 이용할 수 있습니다. 원격 데스크톱을 이용하기 위해서 크롬북과 윈도우 PC 모두 인터넷에 연결되어 있어야 하는거 꼭 잊지 마시길 바랍니다.

‘Chrome 원격 데스크톱’을 통해 언제 어디서든 편리하게 컴퓨터를 이용해보면 어떨까요? 아.. 그리고 안드로이드 스마트폰에서도 ‘Chrome 원격 데스크톱’을 이용하여 언제나 접속 가능합니다.

반응형
반응형

에버노트 개인 서비스 등급이 2018년 4월 경 기존 베이직(무료) - 플러스(월 3000원) - 프리미엄(월 6000원) 등급에서 베이직(무료) - 프리미엄(월 6000원)으로 플러스가 폐지가 되었습니다.  플러스 등급만으로도 충분히 사용할 수 있었는데 선택지가 프리미엄밖에 업게 되니 부담스럽더라고요.

 

그래서 우회 결제로 에버노트를 한달에 약 550원 정도로 저렴하게 사용할 수 있는 방법을 소개합니다.

필요 프로그램은 구글 크롬 + Hola VPN 확장프로그램이 필요합니다.

에버노트 우회 결제 방법

국가 우회를 하기 위해 구글 크롬 확장프로그램인 Hola VPN 을 설치하기 위해 크롬 웹 스토어에 접속합니다.

https://chrome.google.com/webstore/category/extensions?hl=ko

 

검색창에 hola vpn을 검색하여 Chrome에 추가를 클릭하여 설치합니다.

 

 

vpn 국가를 아르헨티나를 선택하여 적용합니다.

그 다음, 에버노트 결제 링크로 이동하여 결제할 계정으로 로그인을 합니다.

https://www.evernote.com/subscriptions

 

 

 

프리미엄 기준 ARS 18.33/월으로 2019년 1월 12일 기준 약 555원입니다. 

프리미엄으로 업그레이드를 클릭하여 진행합니다.

 

 

 

이제 결제 정보를 입력한 후 진행하면 끝입니다.

원래 가격보다 1/9 정도로 구매가 가능하니 정말 저렴하게 사용가능합니다.

국가/지역은 대한민국으로 선택하여 결제해도 상관없습니다.

 

 

 

결제 완료 !

1년 단위 결제하여 ARS 220으로 하였습니다. 약 6600원



출처: https://jun7222.tistory.com/490 [leverage 블로그]

반응형

+ Recent posts