기본 콘텐츠로 건너뛰기

모든 자바스크립트 개발자가 알아야 하는 33가지 개념 - 원시타입

  자 일단 원시타입이 뭔데? 자 간단하게 설명해보자. 원시타입은 프로그래밍 언어에서 기본적으로 제공되는 데이터 유형이며. 이것은 다른 데이터 유형으로 분류되지 않는 가장 기본적인 유형으로, 보통 숫자, 텍스트 또는 논리값과 같은 간단한 값을 나타내게 된다. 원시타입은 일반적으로 메모리에 직접 저장되며, 객체가 아니라는 점이 중요하다.  자 객체가 아니다?? 그것은 무슨 뜻일까?  "객체가 아니다"는 원시타입이 다른 데이터 유형인 객체와는 다르게 메모리에 직접적으로 저장되는 데이터 유형이라는 것을 의미한다. 객체는 데이터와 그 데이터를 조작하기 위한 메서드(method)들을 포함하는 복합적인 개념이며, 일반적으로 참조(reference)로 저장된다. 이에 반해, 원시타입은 데이터 값 자체가 메모리에 저장된다 자 이제 참조라는 단어가 나왔다. 이건 간단하게 코드로 보자 :) let arr = [ '1' , '2' , '3' , '4' ] let str = '1234' arr [ 1 ] = '3' str [ 1 ] = '3' console . log ( arr . join ( '' )) // 1334 console . log ( str ) // 1234 자, 위의 값에서 arr은 변경이 되었지만, str은 변경 되지 않았다. arr [ 1 ] = '3' // 해당 라인의 의미는 arr 의 1 번 인덱스의 값을 '3' 으로 바꾸어라 라는 의미이다 . // 해당 라인의 의미는 str[1] 의 평가가 끝나서 // "2" = '3' 이 되게 된다 . str [ 1 ] = '3' 레퍼런스의 참조는 평가를 진행하는 경우, 메모리 주소의 참조가 되게 된다. 원시타입은 무엇을 해도, 값일 뿐이라서, 값을 가지고만 핸들링하게 된다.  자바스크립트 세상의 원시타입...

모든 자바스크립트 개발자가 알아야 하는 33가지 개념 - 호출 스택

 언젠가 "모든 자바스크립트 개발자가 알아야 하는 33가지 개념" 이라는 내용이 언젠가 유행이었던 적이 있다.  뭐 내용을 보면, 아주 당연하게 알아야 하는 내용이기도 하고, 추가가 되면 되었지 몰라야 할 만한 내용은 딱히 없다.  해당 내용으로 한동안 블로그를 적고자 한다. 1. JavaScript와 같은 고급 코드 라인이 호출 스택 ( 고급 언어 에서 기계 코드 까지 ) 에서 스택 프레임으로 변환되고 실행되는 방식을 이해합니다.  자료구조를 배우면, 링크드리스트를 배우고 나서, 스택을 배웠던 기억이 있다. 간단하게 선입후출 형태의 자료구조이다. 일반적으로 대부분의 언어에서 스택은 최대크기를 갖는다.  자 여기서 이야기 할 것은 자료구조로써의 스택을 공부하고자 하는 것 아니지만, 선입, 후출, 최대크기가 있다는 정도만 생각하자. let g = 0 function first () { let a = 1 let second = () => { let b = 2 third (); console . log ( " 두 번째 " , a ); debugger ; } second (); console . log ( " 첫 번째 " ); debugger ; } function third () { let c = 3 console . log ( " 세 번째 " ); debugger ; } first (); 자 간단하게 보자.  스택에는 위와 같이 쌓이게 되며, fist()가 가장 먼저 stack에 쌓이지만, first()가 가장 마지막까지 남아 있는 것을 볼 수 있다.  하지만 callStack은 함수만 있는 것이 아니다. 간단하게 실행 컨텍스트 까지 포함된다.    실행 컨텍스트라고 하면, 복잡하게 생각될 수 있는데, 크게 세가지로 볼 수 잇다. VariableEnvironme...

;

  자 제목이 참 뜬근없기도 하다. 저게 뭘까?  empty statement라고 부르는 것이다. for ( let i = 0 ; i < 1000 ; i++) ; 과연 위의 코드는 무엇일까? 옛날 개발자가 보았다면, 특정 시간을 기다리는 코드라고 말할 것이다. 하지만, 현대에서는 절대로 의미 없는 코드라는 것은 알고 있을 것이다. 자 이제 사용할만한 부분을 보자. const condition = 'A' ; if ( condition === 'A' ) { // do Something } else if ( condition === 'B' || condition === 'C' ) ; else { // do Something }  자 일단 위 같은 코드인데, 1. A인 경우 2. A가 아니면서 B,C 인 경우 3. A, B, C 가 아닌경우. 2번의 경우 아무것도 실행 안하고 else문에 도달하는 것을 멈추게 할 수 있다. 실무 입장에서는?  참으로 javascript는 empty를 좋아한다. 희소배열 이라던지, 애로우 평션의 empty body라던지, empty statement라던지,  하지만 모든 empty요소가 오류의 가능성을 내재 하고 있다. 1. 희소 배열의 경우, 일반적인 for문과 map의 순회 횟수가 다른다. 2. 애로운 평션의 엠티 바디의 경우, 원래 원했던 리턴값이 undefined인지, plain object인지 헷갈린다. 3. empty 스테이트먼트의 경우, 진짜로 원해서 그 위치에 ;가 들어간건지 헷갈린다.

세상에 나와선 안되었던 키워드. async/await

 오늘은 한 가지 세상에 있어서 안되었던 키워드를 이야기 하고자 한다. 뭐 물론, 이 키워드를 이용하여, 만들어진 프로그램은 수도 없이 많을 것이다. 특히나, ES6이후에 만들어진 웹사이트 Node 서버들은 무조건 포함이 되는 키워드일 것이다.  이런 생각을 하는 사람들은 그렇게 많지는 않을 것 같다. 아마도 동료들한테 말을 하는 순간 엄청나게 욕을 먹고 까일 것 같다...  async/await은 비동기 코드를 동기코드로 만들어주는 아주 마법 같은 키워드이다. 하지만 그로 인해서 모든 대부분의 개발자들이 동시성이 주는 마법을 잊어버리고 있다. 특히나 싱글스레드인데도 사용하다니, 더 이상 동시성에 대한 고려를 하지 못하도록 원천 차단하고 있다.  그래도 Promise 때 까지만 해도 좋았다. 무슨 말일까? 잠깐만 아래의 코드를 잠깐만 생각해보도록 하자. promise 방식 function initData () { apiCall1(). then ((res) => { this . data1 = res. payload }) apiCall2(). then ((res) => { this . data1 = res. payload }) apiCall3(). then ((res) => { this . data1 = res. payload }) } async/await async function initData () { this . data1 = await apiCall1(). payload this . data2 = await apiCall2(). payload this . data3 = await apiCall3(). payload } 자 이 두개의 코드가 내가 말하고 싶은 것의 축약본이다. apiCall1: 1초, apiCall2: 2초, apiCall3: 3초, 라고 생각해보자. 총: async/await으로...

함수형 프로그래밍의 역사.

  간단하게, 이 내용을 작성하는 것은, 자꾸만 주변에서 함수형프로그래밍이 망했던... 패러다임이라는 내용이 오갔던, 내용이 있어서 그에 대한 내용을 작성을 합니다.  요즘 개발자들이 오해를 하는 부분들이 있는데, 함수형프로그래밍이 아주 최신에 만들어진 패러다임이라는 생각이다.  간단하게 위의 이미지만 보더라도 말이다. 뭐 간단하게 말하면, 라이터와 성냥같은 차이이다. 라이터가 성냥보다 최근에 만들어진 도구라고 생각 할 수 있지만, 성냥이 더 최근에 개발된 것이다.  컴퓨터 공학도라고 하면, 튜링머신에대해서 배웠을 것이다. 영화( 이미테이션 게임 (2014) )에서 본 사람들도 있을 것이고 말이다. 튜링머신이 왜 만들어졌는지 알까? 영화만으로 본 사람들은 독일군의 암호를 해독하는 그 장치가 튜링머신이라고 오해 할 수 있다. 하지만, 컴퓨터 공학도 라면, 간단하게 종이와 볼펜만 가지고 튜링머신을 만들 것이다.  튜링머신은 20대 였던, 앨런튜링이 작성했던, 논문(  1936년   )에서 만들어진 기계 이름이다. 발명하게된 이유는 A라는 수학자가 세상에 우리가 알고 있는 모든 명제를 학습 시킨 머신이 있다면, 확인되지 않은 명제x를 입력을 하게 되었을 때, 모든 명제에 대해서 참과 거짓을 판별을 할 수 있고, 그렇게 하여 자동으로 명제X들을 다 풀어낼 수 있다. 이 내용을 부정하기 위해 만들어진 종이쪼가리 머신 일 뿐이다. 거짓에대한 증명을 홀팅프러블럼을 이용하여 증명 하였다.  일단 대충 내용이 튜링머신에 대해서 길어지니, 왜 이 내용을 이야기하는 것일까? 우리가 알고 있는 절차형 프로그래밍이 튜링머신과 닮았다고 생각하지 않는가?  현재의 상태를 보관 하고 있는 테이프(상태)에 따라 명령이 실행되는 방식이다. 절차지향형 프로그래밍언어 C라이크 언어들에 대한 내용이다.  자 절차형프로그래밍에 대한 시초는 대충 훑어 보았고, 함수형프로그래밍의 시초를 한 번 살펴보자. 함수형프로그...

Iterator Helpers

  오늘날 generator function을 사용하는 사람들은 매우 적다. 현재 상태에 따라, 결과가 달라진다라는 것도 요즘 패러다임에는 맞지 않는 방식이니까 말이다. 하지만, 이번에 Stage 3 Draft / December 12, 2023 스펙에 "Iterator Helpers"가 등장한 것은 꽤나 흥미로운 일이다.  언젠가 iterator에 대해서 다루면서, 가장 간단하게 iterator를 생성하는 방법으로 generator function을 소개하였다. function * test () { yield 1 ; yield 2 ; yield 3 ; } let iter = test () ; const arr = [... iter ] ; console . log ( 'arr' , arr ) ; iter = test () ; for ( const item of iter ) { console . log ( 'item' , item) ; }  iterator는 펼침연산을 수행할 수 있고, for of를 이용하여 순회 할 수 있다. 하지만 무엇인가 부족했다. iterator 자체로는 map을 사용할 수 도 없고, filter를 사용할 수 없었다.  그것이 바로 Iterator helpers인 것이다. function * test () { yield 1 ; yield 2 ; yield 3 ; } [... test (). map (v => v * 2 ). filter (v => v % 3 )]  map과 filter를 사용하여, 새로운 iterator를 받을 수 있게 되었다. 그 뿐만이 아니다. [... test (). flatMap (v => test ()). map (v => v * 2 ). filter (v => v % 3 )]  당연하게도, flatMap이 사용 가능하다!  위에서는 펼짐 연산자를 이용하여, 배열로 형변환을 해주었지만?...

옛날 코드 다시 읽기 4 (비동기 api 호출)

 제너레이터 함수를 다루어 보았을까? 만약 그렇다면, async/await이 없던 시절에 개발을 했던 사람일 수 있다.  자 오늘은 옛날 코드 다시보기가 될 수 있을 수 있다.  callBack  자 하나하나씩 보도록 하자. 일단 xmlHttpRequest나 form방식은 건너띄고, 우리에게 친숙한 callback 방식이다. // callBack 버전 $.ajax({ url : 'https://jsonplaceholder.typicode.com/comments' , dataType : 'json' , success : function (comments) { // 댓글 목록을 처리합니다 . // ... // 댓글 목록을 가지고 POST API 호출 const comment = comments[ 0 ]; // 첫 번째 댓글 선택 const postData = { postId : comment . postId , name : comment . name , email : comment . email , body : comment . body , }; $.ajax({ url : 'https://jsonplaceholder.typicode.com/comments' , method : 'POST' , data : JSON . stringify ( postData ), dataType : 'json' , success : function (data) { console . log ( ' 새로운 댓글이 생성되었습니다 :' , data); ...

프레임워크를 사용하면서 주의점.

  프레임워크/라이브러리가가 발전함에 따라, 대부분의 애니메이션 처리들은 다른 코드에 의존하며 제작하게 되었다. 대략 8년전만 해도.... 애니메이션의 처리를 정밀하게 처리하기 위하여, requestAnimationFrame을 이용하긴 하였다.  requestAnimationFrame에 대해서 잠깐 설명하자면, 브라우저의 경우, 1초에 최대 60프레임을 동작 시킬수 있다. 각 프레임이 렌더링이 되고나서 호출이 되게 하는 부분이라고 생각하면 될 것이다. 즉 모든 requestAnimationFrame을 통하여 60번을 실행하게 되면, 최소 1초이내에 60회가 실행 된다고 보면되지만... 요청하는 내용과 기기의 사양에 따라, 1초보다 훨씬 긴 시간이 될 수 있다.  그 시절 requestAnimationFrame을 이용하여, 구현 할 수 밖에 없었던 것도 있긴하다. 라떼 이야기이긴 하지만, 어쨋든 우리나라에는 IE가 있었기 때문에, css에 의존한 애니메이션동작을 한게가 있었다. 하지만, 지금은 어떤 서비스가 IE를 지원을 할까? 마이크로소프트 공식 홈페이지도 IE를 지원하지 않을 것 같다.  프레임워크/라이브러리 발전에 따라 requestAnimationFrame을 사용할 수도 없는 환경이 되었다. 프레임워크의 경우 내 코드를 실행하는 주체는 프레임워크이고, 그 사이에 너무나 많은 코드들을 수행한다. 즉 프레임워크 외적의 무언가를 사용하여, 값을 변경하는 것은 공유자원에 여러 소비자들이 사용하는 동시성문제 수준의 오류가 발생 할 수도 있다.  가장 간단한 것은 다음과 같은 문제이다. <script setup > import { ref } from 'vue' const title = ref( 'title' ) const foo = function () { const $input = document . querySelector ( '#inputTest' ) $input.value = ...

정규표현식 g 플래그가 무엇일까?

  부끄러운 이야기이지만, 정규표현식을 공부를 한 적이 없다. 무슨말인가 하면, 그냥 인터넷에 전화번호 정규 표현식이라고 검색만 하고, 이메일 정규표현식이라고 검색만 해보고 사용을 해왔다.  뭐 그것이 나쁘다고 하는 소리도 아니고, 앞으로는 정규표현식을 매번 창조 할 것이라고 말하는 것도 아니다. 인류는 바퀴를 한 번만 발명 하였다.  오늘 이야기 할 이야기는 g 플래그이다. const aRegx = / a /g ; console . log ( aRegx . test ( 'a' )) ; // true console . log ( aRegx . test ( 'a' )) ; // false 단적인 예로 위의 코드가 true, false로 값이 나온다라는 걸 알고 있다면, 딱히 해당 포스팅을 굳이 볼 필요는 없을 것 같당... 자 위와 같은 상황이 왜 나오게 되는 걸까? g 플래그와 결과값 g 플 래그는 정규 표현식에서 모든 패턴 일치 를 찾는 데 사용됩니다. 코드 예시에서: aRegx 는  /a/g  정규 표현식을 사용합니다.  이는 문자열 내에서 모든 "a"를 찾도록 설정됩니다. 첫 번째  test호출은 "a" 문자열에서 첫 번째 "a"를 찾고  true 를 반환합니다. 두 번째   test호출은  이전 검색이 끝난 위치 에서 검색을 시작합니다.  즉,  첫 번째 "a" 이후부터 검색합니다. "a" 문자열에는  "a"가 하나만 존재 하기 때문에 두 번째 호출은 더 이상 일치하는 문자를 찾지 못하고, false 를 반환합니다. 결론적으로: g 플래그는 모든 일치를 찾도록 합니다. 이전 검색이 끝난 위치( lastIndex )에서 다음 검색을 시작합니다. 참고: lastIndex 속성을 사용하여 이전 검색의 마지막 위치를 직접 확인하거나 조작할 수 있습니다. g 플래그 없이 사용하면 첫 번째 일치만 찾고 검색을 종료합니다.

else 사용하지 않기(?)

 자 일단 if ~ else를 사용하는 것이 나쁘다고 말하고 싶은 생각은 추호도 없다. 하지만, 뇌에서 그냥 흘러나오는 방식대로만 작성하는 것은 그렇게 좋지 않은 코드라고 생각하기 때문에 작성을 해본다.  else를 사용하지 않아도 되는 상황을 좀 생각해보도록 하자.  재밌는 것을 가져왔다.  혼인 신고가 가능한 나이에 대한 것이다.  남자는 만 18세, 여자는 만 16세에 달해야 하며, 만 20세 미만의 미성년자는 부모의 동의를 얻어야 한다.  자 우리가 프로그래밍 해볼 것은 이거다.  1. 16세 미만이면 결혼 불가능  2. 16세 ~ 18세 미만이면 여자라면 부모 동의를 받은 경우 결혼 가능.  3. 20세 미만이면 부모 동의를 받은 경우 결혼 가능.  4. 20세 이상이면 부모 동의없이 결혼 가능. function canIWedding (person) { if (person?.age) { if (person. age < 16 ) { console. log ( '한국에서 결혼 불가' ) } else if (person. age < 18 ) { console. log ( '여자라면 부모 동의 시 가능' ) } else if (person. age < 20 ) { console. log ( '부모 동의 시 가능' ) } else { console. log ( '결혼가능' ) } } else { console. log ( '출생신고 필요.' ) } }  뭐 일단 동의하고 시작할 것이 있다. 딱히 로직이 어렵지는 않다.  하지만 위의 로직을 그대로 들여다 보도록 하자. 말 그대로 그대로 말이다.  자 21세의 경우라고 생각을 해도록 하...

== 한번 더 보기

  ==을 이야기하려면 글을 몇개를 작성해도 끝이 없을 것 같다. 그 만큼 사용하기 까다로우며, 몇 가지 규칙들은 꽤나 많은 ===을 대신 할 수 있는 장점이 있다.  간단하게 null을 이용한, undefined, null 체크가 있다. if ( null == null && null == undefined ) { console . log ( 'A == null 은 null 과 undefined 를 체크 할 수 있습니다 .' ) }  위의 코드를 ===를 이용하여 체크하려면, if ( null === null || undefined === undefined ) { console . log ( 'A === null 은 null 만 체크가능 하여 , undefined 를 체크 하기 위해서는 길이가 길어집니다 .' ) } 하지만 ==는 아래와 같은 문제점이 있다. if ( [] == false ) { console . log ( '[] 는 false 입니다 .' ) } if ( [] == 0 ) { console . log ( '[] 는 0 입니다 .' ) } if ( [] == '0' ) { console . log ( '[] 는 \' 0 \' 이 아닙니다 .' ) } if ( 0 == '0' ) { console . log ( '0 은 \' 0 \' 입니다 .' ) } [] == 0이고, 0 == '0'이지만, [] == '0'이 아니게 된다. 삼단논법이 박살나는 순간이다. 참고로 []는 ''이다.... if ( [] == '' ) { console . log ( '[] 는 \'\' 입니다 .' ) } 자 그러면, 왜 []는 ''인지 생각을 해볼 필요...

Promise.race

  Promise race는 promise끼리 경주를 시킨다고 보면 될 것 같다. 같은 기능의 api를 중복 호출 하는 경우 const delay = (ms) => new Promise ((resolve) => setTimeout (resolve, ms)); async function searchApiv1 () { await delay ( Math . random () * 5000 ); return 'searchApiv1' } async function searchApiv2 () { await delay ( Math . random () * 5000 ); return 'searchApiv2' } const res = await Promise . race ([ searchApiv1 (), searchApiv2 ()]) console . log ( res )  자 위의 코드를 잠깐만 살펴보자, searchApiv1, searchApiv2은 각각 5초이내에 완료가 되는 api라고 보면 된다. 둘 중 하나의 api의 결과만 필요한 경우, 위처럼 작성하면 된다.  뭐 물론 위와 같은 코드를 짤만한 상황이 그렇게 많지는 않을 것이다. 조금 현실적인 예제를 보도록 하자. 클라이언트 에서의 api타임아웃 구현 const delay = (ms) => new Promise ((resolve) => setTimeout (resolve, ms)); async function searchApi () { await delay ( Math . random () * 5000 ); return 'searchApiv1' } async function requestTimeout (ms) { await delay (ms) throw 'request time out' } try { const res = await Promise . race ([ search...