Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Use JavaScript more strictly (feat. TypeScript, flow)

737 views

Published on

[DevFest Seoul 2018] JavaScript 에 Type System 을 도입하는 대표적인 방법인 Flow 와 TypeScript 를 알아본다. TypeScript 의 strict 모드를 이용해서 좀 더 견고한 JavaScript 어플리케이션을 만드는 방법에 대해 공유한다.

Published in: Technology
  • Be the first to comment

Use JavaScript more strictly (feat. TypeScript, flow)

  1. 1. 이 웅재, Studio XID, Inc. @2woongjae Korea Use JavaScript More Strictly (feat. TypeScript, flow)
  2. 2. 발표자 소개 • Software Engineer, Studio XID, Inc. • ProtoPie Studio Party • TypeScript Korea Organizer • Microsoft MVP (TypeScript / JavaScript)
  3. 3. 발표에 앞서 개발을 하면서, 다음과 같은 상황을 겪으신 적이 있는지 같이 생각해보는 시간을 가지겠습니다.
  4. 4. 이런 에러들을 보신 적이 없다면 ? 1. 자바스크립트 개발자가 아니실거에요
  5. 5. 이런 에러들을 보신 적이 없다면 ? 1. 자바스크립트 개발자가 아닐거에요 2. 아니면, 당신은 기계입니다? a. 모든 소스를 다 알고 있다 b. 첨보는 코드를 사용할 때, 모든 맥락을 다 확인하고 사용
  6. 6. 이런 에러들을 보신 적이 없다면 ? 1. 자바스크립트 개발자가 아닐거에요 2. 아니면, 당신은 기계입니다? a. 모든 소스를 다 알고 있다 => 기억력의 한계 (어제 쓴 것도 기억 안남) b. 첨보는 코드를 사용할 때, 모든 맥락을 다 확인하고 사용 => 리소스의 한계 (일정은 기다려주지 않음)
  7. 7. Human Error => Bug
  8. 8. Type System 이요? JavaScript 에는 타입이 없나요? 아니요. 있습니다. JavaScript 에서 데이터 타입은 실행 중에 정해지고, 자유롭게 변경이 됩니다. 그래서 JavaScript 는 느슨한 타입 (loosely typed) 언어, 혹은 동적 (dynamic) 언어라고 합니다. 이러한 특징이 유연함을 주지만, 앞에서 알아본 여러가지 휴먼 에러를 일으키는 요인이 됩니다. Korea
  9. 9. Static Type Checker Korea
  10. 10. TypeScript November 15, 2012 v0.8.1 … v3.1.6 Microsoft Open Source With VSCode Flow November 19 2014 v0.1.0 … v0.86.0 Facebook Open Source With React
  11. 11. Type Checker (flow) 타입 체크만 함 Compile (babel, flow-remove-types) 자바스크립트 런타임에서 사용가능하도록 타입 체크를 위한 문법을 제거하는 일 Compile (typescript) (타입 체크를 포함하여) 타입스크립트 문법에 따라 작성된 파일을 자바스크립트로 변환
  12. 12. JavaScript 의 컴파일 런타임에 동작 가능한 자바스크립트 소스를 만들고, 최적화 하는 일 - 전통적인 컴파일과는 다른 점이 있지만, 어째든 컴파일에 하는 행위는 정말 실행할 자바스크립트를 만드는 과정이다. - 바벨 쓰시는 분, 웹팩 쓰시는 분 - 여러분들은 이미 컴파일이라는 개념을 알고 있고, 컴파일이라는 과정을 하고 있다. Korea
  13. 13. Library 의 Type 은 ? flow-typed 와 @types Flow - flow-typed 이란 레포지토리에서 관리하고 가져다 사용하고 있음. - 638 개 @types - 써드파티 매니저들을 지나 npm 패키지로 제공되고 있는 숫자가 4000여개 이상 - https://github.com/DefinitelyTyped/DefinitelyTyped - 라이브러리에서 직접 관리해서 제공하는 케이스가 많아지고 있음 Korea
  14. 14. Nominal typing vs Structural typing 명목적 타이핑 vs 구조적 타이핑 Nominal - 자료형의 호환성과 등가성은 선언이나 자료형의 이름에 의해 명시적으로 결정된다. (Wikipedia) - C ++, Java 및 Swift 와 같은 언어는 주로 명목적 자료형 시스템을 사용합니다. Structural - 자료형의 호환성과 등가성은 자료형의 구조로 결정된다. - 같은 구조로 되어 있으면 같은 타입처럼 사용할 수 있다. Korea
  15. 15. Nominal typing
  16. 16. Structural typing
  17. 17. Nominal typing vs Structural typing Flow - 함수 및 클래스가 JavaScript에서 이미 사용 된 방법에 따라 결정. - 자바스크립트는 객체 지향적 특징과 함수형 특징을 둘다 가짐. - 클래스는 객체지향적 특징에 따라 명목적 타이핑 - 함수와 객체는 함수형의 경향에 따라 구조적 타이핑 TypeScript - 구조적 타이핑을 따르고 있음. - 같은 구조지만 이름이 다를 경우 다른 타입으로 사용해야할 상황에 대한 특수 사례 연구가 있음. Korea
  18. 18. 선택? Flow - 그 시작처럼 리액트와 함께 많이 쓰이며, - 전체의 타입 적용 보다는 리액트의 PropTypes 를 대체하는 용도인 리액트의 state 에 대한 타입 체크에 주로 사용되고 있습니다. - 나머지 사용 사례가 크게 증가하고 있지 않음. - 그래서 이렇게 부분적으로 타입을 적용하고 기존 자바스크립트의 체계 안에서 사용하길 원한다면, 선택지가 될 수 있을 것 같습니다. TypeScript - 타입스크립트의 경우는 전체 프로젝트를 타입스크립트라는 언어 하에서 좀더 엄격하게 타입 시스템을 적용하고 활용하고 싶으면, 가장 좋은 선택지가 될 것입니다. - 또한 타입스크립트가 개발 환경 내에서 개발을 진행하게 됩니다. Korea
  19. 19. 안전한 코드를 위한 노력 - 지금부터는 프로토파이를 만들면서 좀 더 안전한 코드를 만들기 위해 어떤 작업을 했는지 코드 레벨에서 함께 알아보도록 하겠습니다. - 이런 노력은 테스트를 통해서 좋은 프로그램을 만들기 위한 노력과는 다른 결의 노력입니다. - 타입시스템은 만능이 아니고, 당연히 테스트를 대체하지 못합니다. - 단지 실수를 방지하면서 문법적으로 안전하게 해주는 하나의 추가적인 노력(낮은 수준의 자발적 방어)이라고 생각해야 합니다. Korea
  20. 20. TypeScript Strict Korea
  21. 21. tsconfig - strict Enable all strict type checking options. - 모든 strict type checking 옵션을 활성화 합니다.
  22. 22. All Strict Checking Options • No Implicit 시리즈 • noImplicitAny - suppressImplicitAnyIndexErrors • noImplicitThis • Strict 시리즈 • strictNullChecks • strictFunctionTypes • strictPropertyInitialization • alwaysStrict
  23. 23. noImplicitAny Raise error on expressions and declarations with an implied any type. - 명시적이지 않게 any 타입을 사용하여, 표현식과 선언에 사용하면, 에러를 발생. Korea
  24. 24. 타입스크립트가 추론을 실패한 경우, any 가 맞으면, any 라고 지정하라. 아무것도 쓰지 않으면, 에러를 발생 이 오류를 해결하면, any 라고 지정되어 있지 않은 경우는 any 가 아닌 것이다. (타입 추론이 되었으므로)
  25. 25. suppressImplicitAnyIndexErrors Suppress --noImplicitAny errors for indexing objects lacking index signatures. See issue #1232 for more details. - noImplicitAny 사용할 때, 인덱스 객체에 인덱스 signature 가 없는 경우 오류가 발생 하는데 이를 예외처리 합니다. - https://github.com/Microsoft/TypeScript/issues/1232#issueco mment-64510362 Korea
  26. 26. obj['foo’] 로 사용할 때, 인덱스 객체라 판단하여, 타입에 인덱스 시그니처가 없는 경우, 에러를 발생시킵니다. 이때 suppressImplicitAnyIndexErrors 옵션을 사용하면, 이런 경우 예외로 간주하여, 에러를 발생시키지 않습니다.
  27. 27. noImplicitThis Raise error on this expressions with an implied any type. - 명시적이지 않게 any 타입을 사용하여, this 표현식에 사용하면, 에러를 발생합니다. Korea
  28. 28. 첫번째 매개변수 자리에 this 를 놓고, this 에 대한 타입을 어떤 것이라도 표현하지 않으면, noImplicitAny 가 오류를 발생시킨다. JavaScript 에서는 매개변수에 this 를 넣으면, 이미 예약된 키워드라 SyntaxError 를 발생시킨다. call / apply / bind 와 같이 this 를 대체하여 함수 콜을 하는 용도로도 쓰입니다. 그래서 this 를 any 로 명시적으로 지정하는 것은 합리적입니다. (물론 구체적인 사용처가 있는 경우 타입을 표현하기도 합니다.)
  29. 29. Class 에서는 this 를 사용하면서, noImplicitThis 와 관련한 에러가 나지 않습니다. (당연) Class 에서 constructor 를 제외한 멤버 함수의 첫번째 매개변수도 일반 함수와 마찬가지로 this 를 사용할 수 있습니다.
  30. 30. strictNullChecks In strict null checking mode, the null and undefined values are not in the domain of every type and are only assignable to themselves and any (the one exception being that undefined is also assignable to void). - strictNullChecks 모드에서는, null 및 undefined 값이 모든 유형의 도메인에 속하지 않으며, 그 자신을 타입으로 가지거나, any 일 경우에만 할당이 다능합니다. - 한 가지 예외는 undefined 에 void 할당 가능 Korea
  31. 31. strictNullChecks 를 적용하지 않으면, 모든 타입은 null, undefined 값을 가질 수 있습니다. string 으로 타입을 지정해도, null 혹은 undefined 값을 할당할 수 있다는 것입니다.
  32. 32. strictNullChecks 를 적용하면, 모든 타입은 null, undefined 값을 가질 수 없고, 가지려면 union type 을 이용해서 직접 명시 해야 any 타입은 null 과 undefined 를 가집니다. 예외적으로 void 타입의 경우 undefined 를 가집니다.
  33. 33. strictNullChecks 를 적용하지 않고, 어떤 값이 null 과 undefined 를 가진다는 것을 암묵적으로 인정하고 계속 사용하다 보면, 정확히 어떤 타입이 오는지를 개발자 스스로가 간과할 수 있습니다. 정말로 null 과 undefined 를 가질 수 있는 경우, 해당 값을 조건부로 제외하고 사용하는 것이 좋습니다. 이 옵션을 켜고 사용하는 경우, 사용하려는 함수를 선언할 때부터 매개변수와 리턴 값에 정확한 타입을 지정하려는 노력을 기울여야 하고, 기울이게 될 것입니다.
  34. 34. strictFunctionTypes Disable bivariant parameter checking for function types. - 함수 타입에 대한 bivariant 매개변수 검사를 비활성화합니다. - ?? Korea
  35. 35. 출처 : https://www.stephanboyer.com/post/132/what-are-covariance-and-contravariance (공변성과 반공변성에 대한 이해를 돕기 위한 자료)
  36. 36. 반환 타입은 공변적(covariant) 인자 타입은 반공변적(contravariant) 그런데 타입스크립트에서 인자 타입은 공변적이면서, 반공변적인게 문제! 이 문제를 해결하는 옵션이 strictFunctionTypes 옵션을 켜면, 에러가 안나던걸 에러 나게 함.
  37. 37. 이전에는 위와 같은 코드도 에러를 발생시키지 않았지만, 이제는 에러가 발생하게 됩니다.
  38. 38. strictPropertyInitialization Ensure non-undefined class properties are initialized in the constructor. - 정의되지 않은 클래스의 속성이 생성자에서 초기화되었는지 확인합니다. - 이 옵션을 사용하려면 --stritNullChecks를 사용하도록 설정해야 합니다. Korea
  39. 39. constructor 에서 초기 값을 할당한 경우 => 정상
  40. 40. constructor 에서 안하는 경우 - 보통 다른 함수로 이니셜라이즈 하는 경우 (async 함수) - constructor 에는 async 를 사용할 수 없다.
  41. 41. alwaysStrict Parse in strict mode and emit "use strict" for each source file - 각 소스 파일에 대해 JavaScript 의 strict mode 로 코드를 분석하고, "엄격하게 사용"을 해제합니다. Korea
  42. 42. https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Strict_mode syntex 에러가 ts error 로 나온다.
  43. 43. 컴파일된 JavaScript 파일에 “use strict” 추가 됨.
  44. 44. ProtoPie Strict 전환 사례로 안전한 코드 알아보기 Korea
  45. 45. 작업 규모 - 4372 개의 에러 - 480 여개의 .ts 파일 - 2주일 동안 4명의 개발자가 타입 수정 - 35가지 종류의 에러 Korea
  46. 46. 에러 타입 별 빈도 • 1위. TS7006 Parameter ‘어쩌구' implicitly has an 'any' type. • 2위. TS2322 Type ‘어쩌구' is not assignable to type ’저쩌구’. • 3위. TS2564 Property ‘어쩌구' has no initializer and is not definitely assigned in the constructor. • 4위. TS2345 Argument of type ‘어쩌구’ is not assignable to parameter of type ‘저쩌구’. • 5위. TS2531 Object is possibly 'null’. • 6위. TS7017 Index signature of object type implicitly has an 'any' type.
  47. 47. 1위. TS7006 – 1161건 (26.5%) Parameter ‘어쩌구’ implicitly has an ‘any’ type. - 타입 추론에 실패하여 any 지만, 실제로 타입을 알 수 있는 경우는 타입을 지정하여 준다. - 실제로 타입을 명시할 수 없는 경우, 명시적으로 any 처리 해주기. Korea
  48. 48. 간단한 자바스크립트 코드 입니다. node.js 의 path 에서 join 을 가져와서 사용하고 있습니다. 아무런 경고가 발생하지 않고, 해당 함수를 잘못 호출하면, 타입 에러와 함께 런타임이 종료됩니다. join 함수의 파라미터가 string 타입만 받는다는 것을 알면, 에러를 피해갈 수 있겠지만, getPath 의 외부 모습에서는 짐작을 하기 어렵습니다.
  49. 49. TS 코드로 전환하면, 에러가 발생하는데 타입 추론을 할 수 없어서 any 라고 알려주는 것입니다. 타입 시스템에서 난 모르겠으니 any 가 맞으면 any 라고 적어! any 가 맞는지를 개발자는 확인하여 정확한 타입을 달아주거나, 정말 any 인 경우는 any 로 지정하면 됩니다. 이 경우는 이 함수를 작성하는 사람이 함수 안에서 join 을 사용했기 때문에 정확한 타입을 적어줄 수 있습니다. 그렇게 하면 getPath 함수를 쓰는 사람은 걱정하지 않고, 함수의 타입만 보고 사용할 수 있습니다.
  50. 50. 최종적으로 strict 하게 다음과 같이 바꾸면 안전합니다.
  51. 51. 1위. TS7006 – 1161건 (26.5%) Parameter ‘어쩌구’ implicitly has an ‘any’ type. - 타입 추론에 실패하여 any 지만, 실제로 타입을 알 수 있는 경우는 타입을 지정하여 준다. - 실제로 타입을 지정할 수 없는 경우, 명시적으로 any 처리 해주기. Korea
  52. 52. 이 함수는 자바스크립트에서 문제가 없을 겁니다. 왜냐면 실제로 모든 타입을 파라미터로 사용할 수 있는 함수이기 때문입니다.
  53. 53. 이런 경우 TS 에서 명시적으로 any 라고 알려주지 않으면, 에러가 납니다. 진짜 any 인 경우입니다. 이렇게 정말 any 인 경우에 any 로 지정해 주면, 이제 any 라고 적혀있지 않는 경우에서 에러가 나면, 타입 추론에 실패한 경우가 됩니다.
  54. 54. Type Guard 런타임에서의 타입을 컴파일러에게 알려주어 안전하게 예외 처리를 할 수 있도록 도와 줍니다.
  55. 55. 2위. TS2322 – 790건 (18%) Type ‘어쩌구’ is not assignable to type ‘저쩌구’. - strictNullChecks 옵션으로 인해, 그동안 subtype 으로 암묵적으로 사용히던 null 과 undefined 를 분기하여 처리. Korea
  56. 56. 보통 이렇게 객체를 생성해내는 함수를 사용할 때, 생성에 문제가 있을 경우 null 을 반환하기도 합니다.
  57. 57. 앞서, null 과 undefined 가 모든 타입의 서브타입으로 사용되지 않도록 했기 때문에, getLicense 함수를 그냥 License 타입으로 간주하면 절대 안됩니다. 그래서 TS 는 이런 경우에 에러를 발생시켜 예방해 줍니다.
  58. 58. 이렇게 리턴 타입에 License 뿐만 아니라 null 을 추가해서, 추가적인 문제가 발생하지 않도록 해야합니다. 그리고 함수 실행하여 얻은 결과를 타입 가드를 활용하여 처리해주면 더욱 안전해 집니다.
  59. 59. 이렇게 어떤 내부 프로퍼티의 초기값을 null 로 지정하고, 특정 상황에서 null 이 아닌 실제 값을 할당하여 사용하는 경우도 종종 있습니다.
  60. 60. 초기값이 null 일 수 있기 때문에 null 타입도 표현해줘야 합니다.
  61. 61. null 타입을 표현 해주면 이제 특정 상황에서 문제가 있을 수 있음을 에러로 표시해 주기 때문에 그에 맞게 null 을 체크하는 로직으로 코드를 안전하게 만들어야 합니다.
  62. 62. 3위. TS2564 – 709건 (16.2%) Property ‘어쩌구’ has no initializer and is not definitely assigned in the constructor. - strictPropertyInitialization 옵션으로 인해, 생성자에서 초기화하지 않는 경우, 처리 - 대신 제대로 초기화 되는지 확인 Korea
  63. 63. 자바스크립트로 클래스를 만들면 다음과 같습니다. 현재 멤버 프로퍼티를 선언과 동시에 값을 할당하거나, 생성자에서 값을 할당해주지 않습니다. 이 상태에서 init 을 호출하지 않고 사용한다면 문제가 발생합니다.
  64. 64. 그래서 TS 로 바꾸면, 이에 대해 문제를 제기합니다.
  65. 65. 이런 경우 명시적으로 이 클래스의 이 멤버 변수는 선언과 동시에 할당하거나, 생성자에서 할당하지 않고, 다른 곳에서 값을 할당할거야 라는 표시로 느낌표를 붙입니다. 물론 이 경우, 완벽하게 안전해라고 말해주기보단 주의하는게 좋을거야 라고 말해주기 때문에 실제로 주의해야 합니다.
  66. 66. 4위. TS2345 – 433건 (9.9%) Argument of type ‘어쩌구’ is not assignable to parameter of type ‘저쩌구’. - TS2322 와 같은 에러 - 클래스와 인터페이스 간의 불일치로 인한 경우. 둘의 타입을 정확하게 맞춰줌. - strictNullChecks Korea
  67. 67. 5위. TS2531 – 272건 (6.2%) Object is possibly 'null’. - 주로 얻어온 결과물에 null 이 있을 수 있는데, 값이 정상적으로 읽어진 경우로만 생각하는 경우. - 값이 확실히 얻어진다는 것을 신뢰할 수 있으면, 타입 어설션을 사용한다. - non-null 어셜션으로 처리할 수도 있다. - 신뢰할 수 없으면, 조건문을 이용하여, 피한다. Korea
  68. 68. 보통 querySelector 와 같이 dom 을 얻어오는 함수를 사용하면, 항상 dom 객체가 얻어지는 것이 아니라, null 이 얻어질 수도 있습니다. 또한 dom 을 얻어, parentElement 를 구해도 항상 얻어지지 않을 수 있습니다.
  69. 69. TS 에서는 이렇게 에러가 발생합니다. null 일 수 있다고 경고해주는 겁니다.
  70. 70. 개발자가 dom 이 얻어지는 조건을 확실히 알고 있다면, 타입 어설션으로 타입을 강제해 줄 수 있습니다. 또한 non-null 어설션을 이용해서 null 값을 제외하고 사용하겠다고 강제해 줄 수도 있습니다. 이런 방법은 런타입에 확실함을 개발자가 확인할 수 있을때만 시도해야합니다.
  71. 71. 5위. TS2531 – 272건 (6.2%) Object is possibly 'null’. - 주로 얻어온 결과물에 null 이 있을 수 있는데, 값이 정상적으로 읽어진 경우로만 생각하는 경우. - 값이 확실히 얻어진다는 것을 신뢰할 수 있으면, 타입 어설션을 사용한다. - non-null 어셜션으로 처리할 수도 있다. - 신뢰할 수 없으면, 조건문을 이용하여, 피한다. Korea
  72. 72. 이번에는 런타임에 어떤 값일지 개발자가 확정할 수 없는 경우입니다. 코드에서 getTest 함수를 통해 얻은 객체는 실제로 런타임에 null 일 수 있습니다.
  73. 73. TS 에서는 이 부분에서 null 일 수 있음을 정확히 경고해 줍니다. 하지만 개발자가 특정할 수 없기 때문에 타입 어설션과 같은 행위를 해서는 안됩니다.
  74. 74. 얻은 test 객체가 null 이 아니고 실제 객체가 있을 때만 실행하도록 표현을 하거나, If 문을 이용해서 처리하는게 좋습니다. 타입 가드 처리도 되므로 null 인 경우를 리턴 처리하고, null 을 제외한 타입으로 컴파일 타임에 인식할 수 있습니다.
  75. 75. 6위. TS7017 – 196건 (4.5%) Index signature of object type implicitly has an 'any' type - Index signature of object type implicitly has an 'any' type 에 걸려 인덱스 시그니처를 가진 타입을 만들어 대체하기. - 범용적으로 활용하기 하기 위해 제네릭 사용. Korea
  76. 76. 자바스크립트에서는 어떤 객체에 자유롭게 인덱스 시그니처를 이용해서 프로퍼티를 추가할 수 있습니다.
  77. 77. 하지만 TS 에서는 타입으로 표현하지 않으면, 인덱스 시그니처에 해당하는 값의 타입을 특정할 수 없기 때문에, 경고를 줍니다.
  78. 78. 그래서 인덱스 시그니처의 타입을 특정해 주는 것이 좋습니다. 물론 타입을 특정할 수 없으면, any 로 표현해도 가능합니다.
  79. 79. 6위. TS7017 – 196건 (4.5%) Index signature of object type implicitly has an 'any' type - Index signature of object type implicitly has an 'any' type 에 걸려 인덱스 시그니처를 가진 타입을 만들어 대체하기. - 범용적으로 활용하기 하기 위해 제네릭 사용. Korea
  80. 80. any 로 대입해 줄 일이 반봅되다 보니, 불편함을 느껴서, 제네릭을 이용해서 범용적인 확장 타입을 표현했습니다.
  81. 81. 하면서 느낀 점 작업 할 때 - 중도 포기할 뻔… - 너무 많고, - 이거 하면 공들인 만큼 좋은지 아직은 모르겠고, - any 에 자꾸 손이 가고, - 힘들고, - 지치고, - 나중에는 생각을 안하고, 기계적으로 하게 됨. - 할거면, 미리 할 걸 (조금이라도 양이 적을 때) Korea
  82. 82. 하고 나서 느낀 점 작업 마친 직후 - 겁나 힘들다. - 과연 이게 무슨 소용이 있겠나 Korea
  83. 83. 하고 나서 느낀 점 작업 마치고 수개월 후 - 하기를 잘했다고 생각함. - 할거면, 미리 할 걸 (조금이라도 양이 적을 때) - 진짜로 컴파일 타임에 타입을 활용하는 것 같은 느낌적인 느낌. - 너무 믿으면, 타입을 값으로 믿어버리는 오류에 빠지기 쉬움. Korea
  84. 84. 이 웅재, Studio XID, Inc. @2woongjae Korea 감사합니다!
  85. 85. Q&A Korea

×