본 문서는 거창하지만 Typescript 를 통한 프로덕트 개발 A - Z 까지의 함축된 개발 과정을 담고 있다.
글을 작성하게 된 계기는 개발을 막 시작하는 이들이 전체 프로덕트 개발 프로세스를 모르기에 느끼는 고충을 같은 주니어 개발자인 본인이 그나마 일전 운좋게 프론트와 백엔드를 개발한 경험을 통해 조금의 가이드 라인을 제공해줄 수 있지 않을까 하는 작은 바램에서 시작되었다. 다소 많은 내용을 함축하여 담아야하는데, 큰 맥락에 대한 이해를 주 목적으로 두고 있기에 용어와 개념에 대한 좀 더 상세한 내용은 관련 레퍼런스를 참고하길 바란다. 덧붙여 본 문서가 타입스크립트에 맹신론으로 비춰질까 걱정이 앞 서지만 어디까지나 언어를 도구로써 이해해줄꺼라 믿는다. 결국 본인이 만들고자하는, 추구하는 소프트웨어 철학에 적합하는 도구를 고르는 것은 온전히 본인의 선택이다. 이를테면 현 시점에서 대용량 처리를 요구하는 소프트웨어에 싱글 쓰레드 기반인 자바스크립트를 택하진 않을 것이며, 간단한 기능을 요구하는 기능에 대해 다소 무거운 언어를 택하지 않을 것 이다.
타입스크립트는 정적 타입(Static Typing) 언어이며, 동적 타입(Dynamic Typing) 언어인 자바스크립트의 상위 집합이다. 자바스크립트를 하나의 게임
으로 비유하자면, 타입스크립트는 그 게임에대한 확장팩
이다.
MS에서 개발했으며, 공식 홈페이지에서는 타입스크립트를 아래와 같이 소개하고 있다.
JavaScript that scales (확장된 자바스크립트)
이말인 즉슨, 자바스크립트를 알고 있다면 누구나 쉽게 타입스크립트를 학습해서 사용할 수 있다는 말이다.
타입스크립트 코드를 빌드하면 자바스크립트 코드로 transpile 된다, 공식적으로는 compile 된다 표현하지만 이는 우리가 일반적으로 알고 있는 전통적인 compile 에 개념과는 조금 괴리감이 존재한다. 컴파일의 경우 한 언어로 작성된 소스 코드를 다른 언어로 변환하는 것을 뜻하는 반면, 트랜스파일의 경우 한 언어로 작성된 소스 코드를 비슷한 수준의 추상화를 가진 다른 언어로 변환한다는 차이가 있다. 이를테면 Java 를 컴파일하면 bytecode 코드가 출력되지만, C++ 를 트랜스파일하면 C 가 출력되며 Typescript 를 트랜스파일하면 Javascript 가 출력된다. 본 문서에서는 이와 같은 용어에 대한 혼선을 피하기위해 공식적으로 표현하는 compile 이란 용어를 사용하겠다.
앞 서, 타입스크립트를 정적 타입(Static Typing)으로 자바스크립트를 동적 타입(Dynamic Typing) 으로 소개했다. 이러한 "타입" 에 대하여 논 할때 강타입(strong type) 그리고 약타입(weak type)으로 분류하여 얘기하곤 하는데 이둘에 대한 공식 정의가 따로 없기에 자의적인 기준으로 해석하는 경우가 다반사이다. 그래서 본 문서에서는 이러한 모호한 용어를 대체하여 실제 표현하고자 하는 "특성"으로써 명시하고 싶다.
정적 타입 언어는 프로그램의 타입이 올바른지에 대한 검사를 런타임 이전에 시행하며, 동적 타입 언어는 프로그램의 타입이 올바른지에 대한 검사를 런타임에 실행한다. 래퍼런스 오류를 유발하는 코드가 존재한다면 정적 타입은 컴파일 하는 과정에서 오류를 출력하는 반면 동적 타입은 해당 구문이 실행되는 시점에서 오류를 출력한다. 정적 타입은 이와 같이 오류를 사전에 예방할 수 있다는 이점이 있지만 컴파일에 소요되는 절대적인 시간이 존재한다는 단점도 존재한다. 동적 타입 검사만 수행하던 언어들이 정적 타입 검사를 도입하는 경우가 점점 늘어나고 있는데 코드양이 방대한 경우 전부 다 정적 타입을 도입하기에는 불가능에 가깝기에 이와 같은 상황을 피하기 위해 정적 타입 검사 도입 시도를 한 다수가 점진적 타이핑(gradual typing)을 지원한다. 프로그램 전체가 아닌 프로그래머가 명시한 일부만 정적 타입 검사를 거치며 그외 부분들은 그대로 동적 타입 검사가 이루어지도록하여 말 그대로 점진적 개선을 가능케한다.
우리가 만드는 소프트웨어를 자동차에 빗대어 애기해보자면, 정적타입 언어는 컴파일(점검)하는 과정에서 자동차의 결함을 찾을 수 있다. 반면에 동적 타입의 경우 타입에 국한에 이러한 결함을 사전에 찾는 과정이 없다. 즉, 빠른 속도로 도로를 질주하는 중(런타임) 엔진에 결함으로 사고(오류)가 발생할 수 있다는 것 이다. 현재 우리가 마주하는 자동차의 모습은 초기 설계된 자동차의 모습과는 많이 다를 것 이다. 수 많은 인터랙션과 기능들이 내장되어 있다. 이에 자율 주행까지 더 해져 그 복잡도는 이루말할 수 없을만큼 거대하다.
비단 소프트웨어라 다르지 않다. 현대의 소프트웨어는 매우 높은 복잡성을 요구하며 이러한 복잡성은 결함을 야기하기 좋은 생태계이다. 이와 같은 상황에서 정적 타입은 우리에게 좋은 안전성을 제공해줄 것이다. 안전성이란 용어를 논리학에서 빌리자면 안정성이란 어떤 논리 체계에서 증명 가능한 모든 명제는 참일 것이 보장될때에 이 논리 체계를 안전하다고 한다. 따라서 타입 시스템이 보장하는 범위에서 타입 검사를 수행한 모든 코드는 올바르다.
그렇다하여 정적 타입을 검사하는 언어가 동적 타입을 검사하는 언어보다 낫다와 같은 주장은 반례를 맞닥뜨리기 쉬운 취약한 주장이기에 거듭 언급하지만 무엇을 추구하는가, 어떤 리소스 상황에 놓여있는가와 같은 상황적 요소들을 잘 배합하여 선택하길 바란다. 또한 정적 타입 검사와 동적 타입 검사는 상호 배제적인 개념이 아니며, 정적 타입 검사를 수행하는 여러 언어들 또한 런타임에도 어느정도의 타입 검사를 수행한다. 런타임에만 존재하는 정보를 통해서만 그 적법성을 판단할 수 있는 경우가 존재하기 때문이다.
이해를 돕기 위해 일전 말한 런타임 오류가 발생하는 자바스크립트 코드를 아래와 같이 작성했다.
본 예제가 좋은 예제인지에 대한 의문이 있지만, 설명하자면 아래 코드는 배열에 담긴 각 요소들을 순회하며 해당 요소 속성 접근자로 charCodeAt 이라는 메소드를 호출한다. charCodeAt 은 문자열 프로토타입에 존재 메소드이다. 따라서 두번째 요소 값을 문자열이 아니기에 charCodeAt 메소드를 사용할 수 없기에 "num.charCodeAt is not a function" 이라는 오류를 출력한다.
["1", 2, "3"].forEach(num => num.charCodeAt())
타입스크립트는 컴파일 과정에서 위 오류를 야기하는 코드를 잡아낸다. 컴파일을 하면 "Property 'charCodeAt' does not exist on type 'number'" 라는 오류가 출력된다. 컴파일 과정 이전에 IDE 레벨에서 코드 어시스트를 통해 쉽게 찾아낼 수 있다.
소프트웨어가 요구하는 복잡성이 높아지며, 자바스크립트 진영에서 정적 타이핑에 대한 니즈가 있었고 이를 해소하기 위해 여러시도들이 존재했다. 그 중 부각을 보이며 현재 독보적인 자리를 매김한게 타입스크립이다. 타입스크립트는 Turbo Pascal, Delphi, 그리고 C#의 핵심 개발자인 Anders Hejlsberg의 리드 하에 개발되었고, 2년간의 내부 개발을 거쳐 2012년 10월 1일 공개되었다.
타입스크립트는 위와 같이 지속적으로 상승 곡선을 그리고 있으며, 그에 따른 커뮤니티 시장 또한 방대하여 관련한 래퍼런스를 쉽게 찾을 수 있다. 타입스크립트에 미래, 로드맵이 궁금하다면 위키 글을 읽어보길 바라고, 그들이 추구하는 목표에 대해서 알고싶다면 TypeScript Design Goals 를 읽어보길 바란다. 더불어 타입스크립트를 공부하고 싶다면 관련 문서들을 리서치해가며 학습 하는 방법도 있겠지만 타입스크립트 공식 홈페이지 문서를 통해 학습하길 권장한다. 상세하며 정확하게 정보를 잘 전달하고 있기에 공식 문서를 통해 충분히 타입 스크립트라는 도구를 쉽게 학습할 수 있으리라 생각한다.
자바스크립트를 사용하며정적 타입
에 대한 니즈
를 느꼈다면 타입스크트는 이를 해소
시켜줄 수 있는 좋은 도구로써 충실히 역할을 수행해줄 친구이다. 타입스크립트를 프로젝트에 적극 사용하므로써 크게 생산성
, 안전성
이 두가지를 얻을 수 있다. 생산성은 코드량에 비례하여 나타내고 싶으며, 사이즈가 정말 작은 스크립트 정도의 코드라면 오히려 자바스크립트가 생산성이 더 높다.
다만 프로젝트 단위로 넘어온다면 리팩토링, 디버깅등의 요소를 빠트릴 수 없고 타입스크립트는 확실히 이와 같은 요소들에 시간을 상당 부분 절감 시켜준다. 프론트와 백엔드간 요청/응답에 대한 definition 을 서로 공유하여 변경점을 빠르게 인식할 수 있고, IDE 레벨에서 많은 코드 어시스트를 받을 수도 있다. 더불어 하나의 언어로 프론트와 백엔드를 빠르게 스위칭해가며 개발해보니 그들이 추구하는 링구아 프라카(lingua franca)의 역할을 충분히 해줄 수 있음을 느낄 수 있었다. 여기서 정말 비약적인 생산성 향상
을 노리고 싶다면, 프론트와 백엔드가 동일한 디자인 패턴과 아키텍쳐를 사용하여 컨택스트 스위칭 비용
을 낮추는거다. 본인은 프론트를 Angular 로 백엔드를 Nest 로 개발한 경험이 있다. Angular 는 많이 들어봤을법한테 Nest 는 모르는 분들도 있을 것 같다. 요즘들어 주목 받고 있는 것 같은데, Nest 는 앵귤러와 동일한 디자인 패턴에 아키텍쳐를 따르고 있다. 백엔드에 Angular 라 부를 수 있다. 상당 부분 스프링과도 닮아있기에 스프링 개발자들도 쉽고 가볍게 사용할 수 있을거라 생각된다. 본 문서에서도 Nest 로 백엔드를 개발하긴 하나, 프른토는 React 를 들고 왔다. 이유는 React 가 Angular 보다 상대적으로 문서에서 설명할게 적을거라 생각했다. 안전성과 관련해서는 정적 타입을 도입하지 않은 자바스크립트에 비해 사전에 오류를 발견할 확률이 높다. 이와 관련하여 To type or not to: quantifying detectable bugs in JavaScript 논문을 한번 살펴보길 바란다. 본 논문에서는 자바스크립트에 정적 타입 시스템을 도입했더라면 Github 에 공개적으로 올라온 코드의 버그 중 최소 15%는 커밋 조차 못하고 잡혔을 것이라고 한다.
That’s shocking. If you could make a change to the way we do development that would reduce the number of bugs being checked in by 10% or more overnight, that’s a no-brainer. Unless it doubles development time or something, we’d do it.
영화 평점을 매기는 시스템을 만들어보고자한다. 이 개발 과정이 일반적으로 다루는 내용들을 빠르게 훑어 볼 수 있지 않을까 생각했다. 주제를 찾았다면 우리는 다음에 무엇을해야할까, 앞서 프로젝트를 만드는 순서는 만드는이에 따라 다르기에 꼭 본 문서에 순서를 따를 필요는 없다. 본인이 판단했을때 효율적인 방법으로 개발하길 바란다. 본인의 경우 가장 먼저 사용자 관점
에서 구현해야하는 어플리케이션의 기능을 정의
하고 정의된 내용을 바탕으로 데이터 모델링을 한다. 여기서는 어떠한 디자인 패턴이나 설계 원칙에 대해서 내용을 축약해야하는 입장이기에 따로 얘기하지 않겠다.
아래는 우리가 만들고자하는 프로그램의 기본 요구 사항이다.
위 요구 사항을 통해 우리는 크게 4가지("이용자", "영화", "출연진", "평점") 모델을 유추할 수 있다. 그러면 이제 모델들에 상세 속성과 모델들간의 관계 확립이 필요한데, 이 또한 요구사항에 인터랙션에서 유추할 수 있다. 이를테면 "이용자는 영화 평점을 매길 수 있다.", "이용자는 카테고리에 따라 영화를 볼 수 있다" 를 토대로 영화에는 "카테고리" 라는 속성이 필요함을 알 수 있으며 "평점 매김" 을 통해 이용자별 영화 평점을 담아야함을 알 수 있다.
다음 시간에는 해당 정보들을 통해 ERD 를 작성해보고, 영화 정보를 주기적으로 스크래핑하는 코드를 작성해볼 것 이다.
처음 타입스크립트를 적용해볼때는 코딩 시간이 배로 걸려서 너무 힘들었는데 적응되니 정말 편리한 부분이 많다고 느끼고 있어요! 이해하기 쉽게 잘 풀어주셨네요 😮 잘보고갑니다~~!