기본 콘텐츠로 건너뛰기

Document PIP(Picture in Picture) 기능

  PIP기능의 경우, 안드로이드 사용자라면, 웬만하면 사용해보았을 것이다. 별건 아니고...  유튜브같이 영상 매체를 실행중에, 홈화면으로 진입하게 되면, 위와 같이 플로팅UI를 사용하여, 영상을 계속적으로 볼 수 있게 지원해주는 기능이다.  지금까지는 위처럼 영상에서만 가능하였지만, 현재 실험적기능으로 문서를 플로팅 처리하는 것이 생겼다.  하지만, 아쉽게도... PC기준으로, 크롬, 엣지, 오페라 정도만 지원을 하고 있다. 하지만, 이정도로만 해도, 의미는 있다. 어드민 페이지를 구현을 하다보면, 스크롤이 길게 생기는 경우가 있고, 값을 확인하여, 결과값을 입력해야 하는 경우같은 경우도 있기 때문에, 부분적으로 플로팅 UI를 지원하는 것은 앞으로 꽤나 큰의미를 가질 수 있다.  간단하게 코드로 작성해보자. <!doctype html > <html class ="no-js" lang ="" > <head> <meta charset ="utf-8" > </head> <body> <table id ="contents" > <tr> <th> 이름 </th> <th> 나이 </th> <th> 성별 </th> </tr> <tr> <td> 테스트 </td> <td> 20 </td> <td> 남자 </td> </tr> <tr> <td> 테스트2 </td> <td> 30 </td> <td> 여자 </td> </tr> </table> <button onclick =...

undefined 사용해도 될까?

 자, 일단 undefined에 대한 사적적의미를 살펴보자. "한정되지 않은, 확실하지 않은"이라는 뜻이다. 일단 defined의 반대의 의미니 defined뜻도 한번 알아보자. "정의된"이라는 뜻이란다.  단 이번 포스팅에서는 undefined는 "정의되지 않은"으로 해석을 진행 하려고 한다.  (사실 정의하다. 라는 말은 함수에서나 사용 하는 단어이니, 다른 개발자와 의사소통할 땐, 주의해야한다.)  기본적으로 우리 자바스크립트 개발자가 설명하는, null과 undefined의 사용방식의 차이를 생각해보도록 하자. let foo = null ; let bar ;  위와 같이 선언을 한 경우, foo의 경우에는 의도적으로 null값을 넣어, 아직 할당 되지 않은 상태를 표현한다. bar의 경우에도 모든 변수의 초기값은 undefined로, 정의 되지 않은 상태를 표현한다. 자 그럼 다음 코드를 보자. const obj = { a : null } console . log ( obj . a ) console . log ( obj . b )  우리는 obj.a의 값은 null이고, obj.b의 값은 undefined라고 생각 하게 된다. 자 여기서 이상한 말이 생겼다. obj.b의 값이 undefined라고 생각하게 된다.  자 이 말이 왜 어색 할까? undefined는 정의가 되지 않았다라는 뜻 아닌가? const obj = { a : null , b : undefined }  실제로 위와 같이 작성해야 우리는 obj.b의 값이 undefined라고 말을 해야 하지 않은가? 여기서 undefined를 사용해서는 안되는 이유를 하나 이야기 해보자.  obj.b의 값이 undefined에요.  위와 같은 말을 하였을 때, obj.b의 값이 undefind라는 말일까? obj.b가 정의되지 않은 상태라는 것을 말하는 것일까?  키의 순회 const o...

단일 진실 공급원(Single Source Of Truth)

 Single Source Of Truth 백엔드 개발자라면, 자주 들어보았을 것이고, 아쉽게도 프런트엔드만 개발했다면 들어 기회가 별로 없다.  하지만, 대부분의 개발자들이 지키고 있을 원칙이다. 정보 시스템 설계 및 이론에서, 단일 진실 공급원(영어: single source of truth, SSOT)은 정보 모형과 관련된 데이터 스키마를 모든 데이터 요소를 한 곳에서만 제어 또는 편집하도록 조직하는 관례를 이른다.  위의 말은 되게 어려우니, 간단하게 알아보도록 하자. A B SUM 1 2 3 2 3 5 3 4 6  자 위와 같은 데이터가 있다고 하자. 빨간 라인이 일단 잘 못 되었다는 것을 느낄 것이다. [{a:1, b:2, sum:5}, {a:2, b:3, sum:5}, {a:3, b:4, sum:6}]  이런 문제가 왜 생기는 것일까? SUM은 A, B의 파생 데이터지만, 위의 상황에서는 A, B의 값을 참고해서, 직접 계산한 값을 SUM의 필드로 관리하고 있는 것이 문제다. => 즉 A, B의 값이 복사 되어 SUM 필드에서 또 관리가 되고 있는 샘이다. ROW A B SUM 1 1 2 =SUM(A:1, B:1) 2 2 3 =SUM(A:2, B:2) 3 3 4 =SUM(A:3, B:3)  자 이 형태는 어떤가? SUM은 이제 A와 B의 참조만 이루어 지고, 결과 값으로 자연스럽게 두 개의 필드의 합이 계산될 것이다.  => A, B의 값이 변경 되는 경우 SUM을 수정할 필요가 없어지기 때문에, SSOT를 잘 지키고 있는 셈이다.  자 실전의 예제를 한번 보도록 하자. <template> <div v-for =" user in userList " :key =" user . userId " > <label> {{ user . name }} <input type ="checkbox" v-model ="...

명명 vs 익명

 익명함수와 명명함수의 성능차이. const test = () => {} console . time ( 'a' ); for ( let i = 0 ; i < 1000000 ; i ++) [ 1 ]. some ( test ) console . timeEnd ( 'a' ); console . time ( 'b' ); for ( let i = 0 ; i < 1000000 ; i ++)[ 1 ]. some (()=>{}) console . timeEnd ( 'b' ); 자 일단 위의 코드를 보자. 어떤게 빠를까? 사실 이 코드를 작성하면서 a가 더 빠르겟지 라는 별생각 없이 작성하기는 하였다. 실제로는 b가 더 빠른 것이 확인 되었다. a: 10.313232421875 ms b: 2.833251953125 ms 위와 같은 결과가 나오자, 명명 함수를 참조하는 데에 오버헤드가 발생할 것이라 생각했다. 하지만, b의 경우 매번 새로운 함수를 생성하고 있지 않은가?  일단 이것이 착시이긴 하다. b의 경우 "() => {}"를 매번 생성하는 것이 아닌, 메모리에 생성 후 참조 하게 된다. 즉 생성은 한번만 하게 된다. 물론 명명함수도 메모리에 저장될 것이다. 하지만, 명명함수의 경우 "이름 => 참조 메모리" 단계가 포함이 되어있다. const test = function () {} const foo = () => {} console . time ( 'a' ); for ( let i = 0 ; i < 10000000 ; i ++) test ( foo ) console . timeEnd ( 'a' ); console . time ( 'b' ); for ( let i = 0 ; i < 10000000 ; i ++) test (() => {}) console . timeEnd ( 'b...

코드에 의미 담기 - 1

   지난번 "무색의 초록 개념들이 격렬하게 잔다."라는 문장을 빗대어, 코드에 의미를 담는 것이 중요하다.라는 이야기를 하였다.  그럼, 코드에 의미를 어떻게 담아볼 수 있을까? 간단하게 다섯가지를 한 번 소개 해보겠다. 1. 변수명 잘 짓기 const boardList = { count : 5 , list : [{ boardId : 1 , title : 'test' , contents : '안녕하세요.' } , { boardId : 2 , title : 'test' , contents : '안녕하세요.' } , { boardId : 3 , title : 'test' , contents : '안녕하세요.' } , { boardId : 4 , title : 'test' , contents : '안녕하세요.' } , { boardId : 5 , title : 'test' , contents : '안녕하세요.' }] }  자 위의 코드가 무엇이 잘못 되었을까? boardList  list라는 단어를 사용 하였지만, boardList는 Object이다. 우리가 list라고 말할 수 있는 데이터 타입은 순서가 있는 자료구조들을 뜻하기 때문에, key, value 형태의 자료구조인 object에 list라고 명명하는 것은 좋지 않다. 2. 고차 함수 사용 시 주의하기  const nameList = [ { firstName : '길동' , lastName : '홍' } , { firstName : '길은' , lastName : '홍' } , { firstName : '길금' , lastName : '홍' } , { firstName : '길동' ,...

무색의 초록 개념들이 격렬하게 잔다.

  문학을 좋아하는 분들이라면,   Colorless green ideas sleep furiously           - Avram Noam Chomsky   위의 문장을 언젠간 한 번 보았을 것이다. 무슨 말일까?  무색의 초록 개념들이 격렬하게 잔다.  => 무색의 초록? 이미 초록인데 무색이라, 전문가들은 색을 바라보는게 좀 특이하다.       ("The Expert"라는 단편영화에서 보면, 빨간선을 투명잉크와 녹색잉크로 그려달라는 내용이 있었다.)  뭐 일단 잡설은 넘어가고, 위의 문장이 뜻하는 것은 문법에는 맞아도 의미상으로는 통하지 않는 말이 있다는 것을 보여주기 위해 예로 든 문장이다.  개발자들은 작업을 어떻게 하는가?  문제 -> 해결 방안 -> 코드 : 아마도 간단하게 설명하면 이런 순으로 진행을 하지 않을까 생각한다.  커먼한 프로그램을 만드는 경우, 개발자들이 마주할 문제, 해결 방안들은 어느정도 유사한 경우가 많다.  => 만약에 게시판을 만든다고 하면, 필연적으로 글쓰기, 댓글 개발, 추가적으로 카테고리화 파일 업로드 기능 등등.... 예상 가능한 기능들이 있지 않은가?  하지만 코드의 영역으로 가면 어떤가? 서버 사양은 어떻고, 사용자들이 사용할 클라이언트는 어떻고, 이는 크게 서버 버전이나 프로그래밍 언어를 제약사항까지 추가 될 수 있다.  하나의 기능을 만들더라도, 누가 언제 어디서 만드는 지에 따라 코드가 달라질 수 있다. 이런 특성 때문에, 대학교수님은 언젠가 프로그래머는 작가와 같이, 문장 하나하나에 의미를 담을 수 있기 때문에, 예술가 같기도 한다고 하였다.  자 일단 잘못 작성한 코드를 보자. function isNumber () { const number = 'number' ...

빈 배열로 호출하면 안되요. reduce

 예전에 some, every를 빈배열로 실행하면 어떻게 되는지 확인하였다. 추가적으로 reduce를 빈배열로 호출하면 어떻게 되는지 이야기해보고자 한다. [ 1 , 2 , 3 , 4 ]. reduce ((a, b) => a + b) reduce는 위와 같이, 반복적인 작업을 누적 시키고 싶을 때, 사용하곤 한다. const list = [] list . reduce ((a, b) => a + b) // Uncaught TypeError: Reduce of empty array with no initial value 하지만, 빈 배열로 실행을 하게 되면, 위처럼 에러가 발생 하게 된다. 물론 기본값을 설정하게 되면 에러가 발생하지 않는다. const list = [] list . reduce ((a, b) => a + b, 0 ) 왜 이렇게 구현 되어있을까? 23.1.3.24  Array.prototype.reduce (  callbackfn  [ ,  initialValue  ] )  4번 조건을 보게 되면 알 수 있다. 일단 4번 조건 까지만, 구현 해보자. Object . defineProperty ( Array . prototype , "reduceImpl" , { value : function (callback) { // 1. Let O be ? ToObject(this value). const o = Object ( this ) // 2. Let len be ? LengthOfArrayLike(O). const len = o . length || 0 // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. if ( typeof callback !== 'function' ) { throw new...

에러 다시보기 1

 혹시 함수를 만들면서 return null을 해본 사람이 있을까? 다시 정확히 질문을 바꾸어 보자, 어떤 함수를 실행 했을 때, null이 나오기를 바라면서 함수를 실행해본 사람이 있을까? 나는 타입언어를 배우는 동안 리턴타입의 지정을 Null이라는 클래스(?)로 해본적이 없다. Nullable은 있어도, Null이라는 클래스가 있는 언어가 있는지도 모르겠다.  언젠가, 널 포인터라는 개념을 만든 토니 호어는 "내 10억 달러 짜리 실수"라고 이야기 하였다. 그런 이야기가 나올 당시가, 함수형프로그래밍이 한참 인기가 있을 때, 나한테 까지도 이야기가 들어왔다. nullalbe객체가 있는데, 그걸 사용 하면 코드에서 null체크를 할 필요가 없어서 코드가 짧아지고, 우아한 코드를 만들 수 있어, null을 왜 만든거야? 뭐 상황에 따라 맞는 말이 될수도 있고, 상황에 따라 불편한 상황을 만들 수 있다.  오늘 내가 해볼 말은 조금 null을 사용해도 되지 않을까? return null을 해도 되는 상황이 있지 않을까? 라는 말을 하고 싶다.  만약, 당신이 앞으로 많은 사람들이 사용하게 될 언어의 readFile함수를 만든다고 생각을 해보자.  일단 첫째로, File 클래스를 리턴을 받아야 하고 path를 파라미터로 받게 되는 아래의 형태의 함수가 될 것이다. function readFile (path: String): File {}  문자열 타입으로 파일경로를 받고, File타입을 리턴을 하게 될 것이다. 더, 고민을 해볼꺼리가 남아있겠지만, 가장 간단하게, 따져볼만한 것이, 해당 경로에 파일이 없는 경우는 어떻게 처리 할 것인가?  여기서, 생각해볼만한 것이다. 세가지가 있을 것이다. 1. throw new PathError(path); 그 상황을 에러로 처분하는 방법. 2. return new File(); 그 상황을 빈 파일의 객체를 리턴하는 방법. 3. return null;  각각...

Void 다시 돌아보기.

  옛날 코드에서는 void라는 키워드를 자주 보았을 것이고, 리턴 타입을 정의해야하는 언어를 사용하는 개발자라면, 수도 없이 보았을 만한 키워드이다.  하지만, 자바스크립트 기준으로 생각을 해보자. void function test () { console . log ( 'called test' ) } test()  위와 같은 코드르 보자, 자바스크립트를 배우지 않았지만, c라이크 언어를 배운 사람이라면, 아 test함수에는 반환값이 없구나 하고, 넘어갈 코드이다. 하지만....  Uncaught ReferenceError: test is not defined  저 코드에는 test자체가 존재 하지 않는다. 옛날의 자바스크립트에서는 undefined가 읽기 전용 이 아니라, 쓰기가 가능한 변수였다. 그래서 undefined를 재정의 할 수 있었기에, undefined = void 0 ; function foo (undefined) { var a ; console . log ( a === undefined ) } foo ()  위 처럼 void가 평가가 항상 undefined인 것을 고려하여, 위와 같이 undefined를 안전하게 관리하거나, 함수의 가장 마지막 인자를 항상 undefined로 이름으로 정의하여, undefined를 안전하게 관리 하였다.  이젠, undefined를 재정의 할 수 없으니, 절~~대 사용하면 안되는 방식이다.  그렇다면, 이제 void 키워드는 더 이상 쓰지 않을만한 내용일까? https://sejiwork.blogspot.com/2021/10/javascript-pseudo-protocol.html  a 태그 사용 시 click 이벤트만 사용 할 때에 사용 할 수 있긴 하지만.... 이걸 써야 할 까? 이 생각은 머리에서 떠나지 않는다. button태그가 있는데 굳이?  우리는 자바스크립트 사용자라면, function ...

async / await 사용 해도 될까?

  제목이 어그로성이긴 하지만... 이번 포스팅의 내용은 async / await을 사용하는 경우 놓칠 만한 오류가 있으니, 주의하자라는 내용을 담고자 하는 내용이니, 참고하자.  Array.protoype.*   // server api const checkValidation = async (v) => v % 2 const filteredList = [ 1 , 2 , 3 , 4 ]. filter ( async (v) => checkValidation (v) ) console . log ( 'filteredList' , filteredList ) // [1, 2, 3, 4]  위의 결과를 생각을 한 번 해보도록 하자, checkValidation이라는 서버 api가 있을 때, 이 처럼 개발되어있다는 것은 filteredList에 [1, 3]과 같이 홀수만 남아 있기를 원하는 것일 것이다.  하지만, filter입장에서는 async 키워드가 달려있는 순간, Promise를 리턴 받았으니, trucy값으로 판단 하여, [1, 2, 3, 4] 값을 리턴 하게 된다. Promise . resolve ([ 1 , 2 , 3 , 4 ]) . then ( list => list. reduce ( async (acc, v) => { acc = await acc if ( await checkValidation (v) ) acc. push ( v ) return acc }, Promise . resolve ( [] ) ) ) . then ( console . log ) // [1, 3]  정상적으로 홀수만 반환 받기를 원한다면, 위 처럼 Promise객체 기반으로 작동 할 수 있도록 작성하는 것이 맞다.  filter함수는 promise를 리턴 받는 순간, trucy로 인식하기 때문에 사용을 하는 순간 재기능을 할 수가 없...

Object.groupBy (배열 그륩핑)

 배열을 다루다 보면, 그륩핑을 해야하는 경우가 있다.  뭐 그러다 보면 아래처럼 불편한 코드가 생성 될 것이다. const list = [ { class : 'a' , score : 1 }, { class : 'a' , score : 2 }, { class : 'a' , score : 3 }, { class : 'a' , score : 4 }, { class : 'b' , score : 1 }, { class : 'b' , score : 1 }, ] const group = {}; for ( const item of list ) { group [ item . class ] = group [ item . class ] || [] group [ item . class ]. push ( item ) } console . log ( 'group' , group )  뭐 reduce를 사용 하게 되면 아래 코드 처럼 되겟지... const list = [ { class : 'a' , score : 1 }, { class : 'a' , score : 2 }, { class : 'a' , score : 3 }, { class : 'a' , score : 4 }, { class : 'b' , score : 1 }, { class : 'b' , score : 1 }, ] const group = list . reduce ( (groupRes, item) => { groupRes[item. class ] = groupRes[item. class ] || [] groupRes[item. class ]. push (item) return groupRes }, {} ) co...