ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ES6 + 기초 1
    코-드 스피츠/es6 2019. 1. 21. 10:53



    https://youtu.be/kG87PONfqkg 에서 발췌




    현업 개발자와 학생의 차이 - 복잡성을 정복했느냐.


    프로그래밍을 공부하며 어떤 것이 무엇을 의미하느냐 정도가 아닌 그 어떤 것을 이용해서 복잡성을 정복했느냐.


    모든 프로그래밍은 변하기 때문에 향후 변화에 대해 프로그램이 받아줄 수 있게 미리 짜둬야 한다.




    이번 회에서 배울 것 - 언어의 기본 요소 + 흐름제어flow control



    [프로그램은 무엇인가?]


    어디서부터가 프로그램인가 - 컴퓨터 메모리에 적재된 이후에 실행되기 직전 상태부터가 프로그램. 그전까지 아님.



    컴파일 언어의 라이프 사이클


    컴파일러 -> 인간친화적인 프로그래밍 언어를 기계어로 바꿔준다.

    File은 프로그램인 상태가 아닙니다. 메모리에 적재되어야 프로그램이다.


    1. 랭귀지 코드로 짤 때는 linting이라는 걸 받습니다 - 아직 컴파일 전이지만 니가 짜놓은 랭귀지 코드를 보니까 잘못 짜고 있는 것 같아. 그래서 에러를 내뿜는 lint time


    2. 컴파일을 하니까 컴파일타임에서 에러를 뱉기도 한다. compile time


    3. 메모리에 적재되어 실행할 때 에러를 뱉기도 한다. run time


    4. run time error도 안걸리는 경우가 있다(연봉 주는 프로그램인데 연봉이 스무th하게 반액이 깎여서 들어간다던가) context error



    언제 에러를 잡는게 쉬울까? - 되도록 앞 시점에 에러를 잡고 코드를 짠다.




    스크립트 언어의 라이프사이클


    브라우저가 파일을 메모리에 적재load. 

    load를 하면 컴파일이라는 행위를 거치지 않고 지가 기계어로 바꾼다.auto complie 


    compile language와 script language의 compile에는 차이가 있다. 

    compile language는 여러개 파일이 될 수 있는 language code를 compile하여 하나의 file로 바꿔준다. 그렇기 때문에 모든 language code를 빠짐없이 해석해서 compile한다. 


    script language들의 경우는 language code가 여러개의 파일이어도 내가 지금 바라보고 있는 파일만 compile한다. 이것조차 하나의 파일의 부분부분만 머신코드로 바꿔놓고 나머지는 해석을 안한다. 예를 들어 자스 안의 어떤 함수가 문제가 있어도 그 함수가 호출되기 전까지 문제 없이 파일이 실행된다. 부분적인 컴파일 전략이다.

     

    따라서 complie language의 경우 전체검사를 통해 컴퓨터의 혜택을 받는데 비해 script language들은 전체검사의 혜택이 없다. 프로그래머의 책임이 더 커진다. 그 후 기계코드가 바뀌었으므로 기계가 실행한다. 런타임이 시작된다. 끝나면 종료terminate.


    그럼 우리가 script language인 자스를 쓸 때는 lint랑 run time에서만 에러를 뱉는가? run time 에러가 나면 실행되기 전까지 에러가 있는지 모른다. 따라서 에러를 잡기 매우 어렵다. 왜냐면 모든 상황을 테스트하기가 매우 어렵기 때문이다(슈팅게임의 예) 따라서 대부분의 run time 에러는 잡기 어렵다.


    그렇기 때문에 다른 곳에서도 에러를 잡는 곳을 추가하여 이 문제를 보완할 필요가 있다. 

    첫번째로 compile language 스타일로 짜게 강제하는 type script같은 대안이 있다. 

    두번째로 매크로, 스크립트 언어의 개발론을 따르는 대안이 있다.




    두번째인 매크로, 스크립트 언어의 개발론을 따르는 대안을 본다. 

    런타임에 무슨 일이 일어나는지 자세히 보기로 한다.


    1번 레이어. 런타임에 기저가 되는 함수, 클래스를 선언한다. 

    2번 레이어. 이 기저함수, 클래스를 이용하는 함수, 클래스를 선언한다.

    3번 레이어. 그 함수와 클래스를 사용한다.


    각 레이어간에는 상위 레이어가 정의 시점static time이 되고 하위 레이어가 실행 시점run time이 된다

    이런 레이어 별로 런타임을 세세하게 나누는 전략을 사용하면 에러를 각 레이어 별로 분리할 수 있게 된다.


    script 개발론에서는 물리적인 구분층이 없는 것을 의미론적으로 나누어 관리하는 관리론을 도입한다 라고 말할 수 있다. 왜? 복잡성을 정복하기 위해서.


    따라서 복잡성을 정복할 수 있는 첫번째(유일한?) 전략 격리isolation 라고 할 수 있다.


    역할에 따라 격리 시켜야 책임을 물을 수 있다. 


    함수, 클래스, 라이브러리, 디자인워크, 프레임워크 등을 쓰는 이유는 어떤 에러가 났을 때 그 에러가 어느 소속이고 걔만의 책임이라고 확정지을 수 있는 격리 구간을 만드는데에 있다. 

    개발자가 되기 힘든 이유는 격리하는 법을 배우기 어렵기 때문이다.


    세계가 깊어지려면 코드 한 줄을 짜도 어떤의미와 어떤역할을 갖고 어디까지 격리 시킬수 있는건지를 인식하는 훈련이 계속해서 필요하다. 


    여기까지 프로그램에 대한 개념을 배웠다.



    [자바스크립트의 구성 요소]


    자바스크립트의 구성 요소 중에 가장 기저가 되는 것은 lexical grammar이다. 굳이 직역하자면 어휘적인 문법이란 뜻이다. 


    왜 이것부터 배우는가? 


    이것은 자바스크립트를 구성하는 '한글'이나 '알파벳'과도 같은 문자이기 때문이다.

    이것은 자바스크립트가 텍스트 쪼가리를 만났을 때 반응하는 양상들이다. 



    control character 제어문자 - 한국어나 영어에선 쓰지 않지만 아랍어등에서 쓴다.


    white space 공백문자 - 공백. 그러나 공백을 나타내는 코드는 유니코드에 57가지가 있는데 어디까지 공백으로 인정으로 하는가? 어디까지가 공백 문자인지 자바스크립트에서 정의를 해두었다. 


    line terminators 개행문자 - 줄 끊어주는 문자. 유명한 것으로 라인피드, 캐리지리턴이 있다. 그 외에도 정의되어 있다.


    comments 주석 - 자바스크립트 해석기가 이곳을 주석으로 인식하고 무시하게 된다. 


    keyword 예약어 - 미리 약속되어 있어서 엔진이 이곳을 만나면 특정한 기능을 수행한다.


    literals 리터럴 - 언어에서 정의한 더 이상 쪼갤 수 없는 값의 표현. 어렵냐? 쉽게 설명하면 number인 37을 표현할 때 더 쪼개서 표현가능한가? 아니오. 그게 number literal이다. 늘릴 방법은 많음. 객체 리터럴, 배열 리터럴의 매크로 같은 리터럴도 포함한다.




    [language element]


    자스에선 이 3개외에 안나온다. 서로가 서로를 포함관계로 가질 순 있다. 




    statement


    expression 식 - 최종적으로 하나의 값에 수렴되는 것. 즉 값의 확장된 표현이다. 

    value expression 값식 - 3, 7, true, 'bye'

    연산식 -  연산자가 등장하는 식. - 1 + 3 등의 산술 연산자arithmetic operator를 이용하거나 ...

    호출식 - 예를들어 함수 호출.


    값은 저장하지 않으면 즉시 메모리에서 휘발되어 없어진다.

    프로그래밍은 로드 과정을 통해서 파일이 메모리에 적재되어 실행된다. 프로그램을 실행되는 과정에서는 프로그램을 계속 소비하여 없앤다. 그냥 값은 소비되어 없어져 버린다. 그러면 그건 재활용하거나 이용할 수 없는 무의미한 값의 표현이 되어버린다. 


    identifier 식별자


    그걸 막아주는 것이 변수이다. 

    변수란 무엇인가? 

    변수에는 두가지 역할이 있다.

    첫번째는 메모리 주소의 별명alias이다.

    두번째. 자스에서는 type이 있다. 변수는 해당 값에 대한 타입 정보datatype도 갖고 있다. 


    이 두가지 의미를 담는 특정한 이름을 지정하는 행위를 identifier라고 한다.

    보통 변수를 의미하지만 보다 상위개념에서는 식별자라고 부른다.


    식별자에 대한 특성


    안에 담는 값의 형태는 크게 두가지로 나뉜다.



    첫번째는 값(기본형 primitive type)

    두번째는 참조다(참조형 referance type) 참조는 다른 메모리 주소를 가리킨다. 값은 그 외의 것들을 가리킨다. 



    모든 값은 복사를 통해 일어난다.


    let a = 3

    let b = a


    b에 a를 넣었다.

    이 행위는 두가지 의미가 될 수 있다. 변수에는 두가지 역할이 있기 때문에. 

    이 행위는 a의 메모리 주소를 옮긴 것인가, a의 값인 3을 옮긴 것인가? 값 3을 복사한 것이다. 따라서 우리는 메모리 블록 2개를 쓰고 있는 것이 된다. 

    증거.

    let a = 7을 해도

    여전히 b는 3을 가리킨다.


    메모리 블록을 복사하지 않고 하나만 쓰는 것을 참조형이라고 한다. 


    let a = [1,2,3]

    let b = a // [1,2,3]

    a[0]; // 1
    a[0] = 7

    b[0]; // 7


    b가 계속해서 a를 가리키고 있었기 때문에 b는 a를 따라간다.



    그럼 무엇이 primitive type이고 reference type일까?

    그리고 왜인가?



    CS를 배우면서 기준으로 삼아야할 것이 있다. 언어에는 자연어와 인공어가 있으며 프로그래밍 언어는 인공어다. 인공어는 사람이 설계했기 때문에 설계자의 의도와 규칙을 반영한다. 보통 규칙 중의 하나로 무엇이 primitive type인지 정한다. 자스에서는 number string boolean undefined null NaN이 primitive type이다. 그 외엔 모두 reference type이다. 설계자가 정했기 때문이다.


    변수에는 상수와 변수가 있다.

    상수는 변하지 않고

    변수는 여러번 변할 수 있다.


    복잡성을 탈피하고 싶기 때문에 우리에게는 무조건 상수가 기본형이고 변화가능성이 있을 때만 변수를 쓰기로 한다.



    statement 문



    문은 값이 아닌 모든 것이다.

    semi colon의 정체는 무엇일까?

    for? if? return?


    문의 정체는 무엇일까? 

    (값으로 환원되지 않는)자스 엔진이 어떻게 해석할지 알려주는 힌트(실행방법 또는 명령)이다.

    단지 힌트이기 때문에 실행 후 메모리에서 사라진다. 따라서 문은 변수에 할당할 수 없다. 따라서 값이 아니다. 연산으로만 남을 뿐이다. 

    문과 식의 구분 역시 언어 설계자가 정한다. 이러한 기준들은 abc언어 기반이다.


    공식적으로 네 종류의 문이 있다.


    공문 - 니들이 스크립트를 짤 때 실수를 많이하기 때문에 만듦. 그래서 공문을 인정한다.

    식문 - 3 + 5; 3; 5;

    제어문 - 28개의 제어문

    선언문 - identifier를 선언하기 위한 선언문


    문의 다른 분류

    단문 - 문장 하나

    중문(복문) - 문장 여러개를 묶어서 하나처럼 보는 것.

    {}안에 문장 전체를 묶어서 하나의 문장으로 보는 기능이 있다.

    따라서 단문이 올 자리에 언제나 중문이 대체 가능



    연산식을 좀 더 깊이 들어간다. 

    연산식에는 연산자operator, operation의 목적에 따라 구분하는 방법이 있다.

    산술, 논리 연산자 등.

    자스는 열한가지 연산자가 있다.


    연산자가 받아야하는 항의 수로 구분하는 방법. 

    (1항unary operator, 2항binary operator, 3항triple operator, 다항multiple operator)


    +, - 는 2항 연산자, 또는 1항 연산자

    3 + 1 // 2항

    + 1 // 1항


    대표적인 다항연산자는 comma,이다. 

    a, b, c, d // d의 값이 나온다



    노이만 머신


    컴퓨터를 단순하게 정의한다. 반복적인 일을 빨리처리하여 원하는 목표를 달성한다.


    프로그래밍을 실행할 때 메모리에 있는 명령과 값을 꺼내서 CPU에 옮긴 뒤에 연산을 하고 다시 메모리에 넣는 것을 끊임없이 반복하는 것으로 프로그램을 실행한다.

    컴퓨터에 실제로 적재되는 프로그래밍은 오퍼랜드operand(피연산자)와 명령어로 되어 있다.

    1 + 2가 있으면 더하기는 명령어 이고 1과 2는 오퍼랜드이다. 이걸 들고 레지스터라는 cpu안의 메모리에 오퍼랜드를 적재하고 어큐뮬레이터에 ??? 연산을 해서 결과 3이 나오면 그 값을 레지스터에서 빼와서 메모리로 옮긴다(그런 명령어가 있다면). 옮기지 않으면 휘발되어 버린다. 이런 것들을 차근차근 쉬지않고 다 소비해버린다.


    [sync flow]


    flow - 노이만 머신이 메모리에 적재되어 있는 순서대로 프로그램을 소비하는 과정


    동기화sync - 관여할 수 없이 한 번에 적재되어 있는 메모리의 명령을 소비하는 과정. 우리는 cpu 중단도 안되고 프로그램을 멈출 수도 없다.


    비동기화 - 이 과정을 어길 수 있는 다양한 기법들.


    'language code를 어떻게 기계어 코드와 매칭되는 형태로 언어를 짤까' 의 기본 규칙이 LR파서 규칙이다.


    abc 언어 LR파서의 유일한 예외는 할당assignment이다.


    a = 3 -> 오른쪽에서 왼쪽으로 파싱된다. -> 실수가 많아서 버그가 많아진다.




    프로그램에서 외부의 입력을 받지 않으면 효용성이 낮아진다.

    프로그램은 입력을 받는 걸로 외부에서 반응하게 만들고 싶다. 다른 flow를 탈 수도 있다(개입될 수 있다)

    따라서 flow 제어가 가능해져야 한다. 


    flow control statement 제어문 - sync flow를 컨트롤 하는 것이 제어문의 역할이다.


    routine - flow가 진행되는 한 세트를 부른다. 일반적인 루틴은 한 번에 쫙 모두 실행된다.

    sub flow는 여러 번 돌 수 있고 sub routine이라고 부른다. 

    코루틴은 여러번 실행될 수 있다. 여러번 들어가고 여러번 빠져나간다. 


    프로그래밍언어가 인공어이기 때문에 냉정할 것이라 생각하는가? 아니다. 예를들어 반복문 이나 조건문을 표현하는 방식에는 여러가지가 있다. 이를 통해 복잡한 사람의 의도와 생각을 표현할 수 있다. 더 미세하게 행간을 표현하라는 의도이다. 프로그램은 무조건 변한다. 따라서 굴러만 가는 코드에는 의도가 표현되어 있지 않기 때문에 수정이 힘들어진다. 이러한 의도가 변수명으로 표현하는 것으로 존재한다고 많은 사람들이 착각한다. 그 의도는 변수명이 아닌 제어문과 어떤 문을 어떻게 써서 알고리즘을 표현하는가에 존재한다.


    그래서 제어문을 배우는 기초적인 단계부터 해당 제어문이 무엇을 의미하는지 섬세하게 느껴야만 한다. 


    if문 - if문의 형식은 if 다음에 공백 문자(공백문자의 특징은 공백을 몇개를 해도 한개로 본다는 것이다)가 올 수 있다. 엔진에게 주는 힌트이기 때문에 엔진이 이 문들을 명확하게 정의해 두었다. 


    if키워드 + (n개의 공백문자) + (식expression) + (문statement)

    if else 키워드 if (식expression) (문statement) else (문statement)  


    ()괄호 안의 식이 참이면 ()괄호 다음의 문이 실행된다. 이 조건을 optional 이라고 부른다. 

    그에 비해 if else는 if나 else 둘 중 하나가 무조건 실행이 된다. 이 조건을 mandatory 라고 부른다.


    중문은 영어로 block이라고 부른다.


    자스의 소괄호()는 적어도 3가지 의미가 있다.


    1. 산술 연산자의 우선순위 연산자

    2. 제어문의 형식 기호

    3. 함수 호출할 때 호출의 힌트 - 호출식 연산자


    a = true

    if (a) {console.log('ok')} // 2. a가 true면 ok가 나온다. optional. 도와주거나 추가적인 additional한 표현이다.

    console.log('hello') // 1. 무조건 hello가 나올건데 

    이곳에선 if문이 옵셔널, 즉 중요성이 떨어지는 context이다.


    if (a) {console.log('ok')} // 

    else console.log('hello') // 2지선다를 표현한다

    이곳에선 if, else 가 동등하게 받아들여지는 context이다.



    우리의 뇌는 2지선다에 적합하게 되어 있다. 폰노이만이 10진수 컴터에서 2진수로 바꾼것도 비슷한 이유이다.



    인공언어는 프로그래머의 의도를 표현하는 다양한 수단을 강구하고 있다. 제어문을 배울 때 의도와 목적을 섬세하게 써야만 한다.






    '코-드 스피츠 > es6' 카테고리의 다른 글

    ES6 + 기초 3  (0) 2019.01.24
    ES6 + 기초 2  (0) 2019.01.22
Designed by Tistory.