SCSS와 SASS는 CSS의 Preprocessor(전처리기)입니다. CSS보다 쉬우면서, 추가 기능이 있는 CSS의 확장판 같은 역할을 하기에 크게 4가지의 장점이 있습니다. 첫번째로는 코드의 중복을 줄일 수 있습니다. CSS의 특성상, 셀럭터를 중복해서 사용해야하는 경우가 많습니다. SASS의 Nesting을 이용해 코드의 양을 줄이고, 연관된 코드를 그룹화할 수 있습니다. 두번째로는 변수, 함수, 연산자를 만들어 사용할 수 있습니다. 번수는
$
달러 기호 모양으로 시작하는 변수를 지정해, 크기나 색상과 같은 값으로 일관적으로 변경할 수 있게 해줍니다. 세번째로는mixin
을 사용해 사이트 전체적으로 자주 쓰이는 CSS그룹을 만들고 재사용할 수 있습니다. 마지막으로extend
를 사용해 특정 셀렉터를 상속할 수 있게 하여, 셀렉터에 정의된 값을 한 곳에서 관리할 수 있게 합니다. 이러한 scss와 sass의 장점은 코드를 쉽게 관리할 수 있게 하여 유지보수성을 극대화시킵니다.
CSS와 SASS/SCSS의 차이점
저는 이 세가지 중에서 SCSS를 주로 사용해왔습니다. 왜냐하면 SCSS는 CSS의 문법을 그대로 사용하기에 호환성이 좋기 때문입니다. CSS에서는 셀렉터를 중복해서 사용하는 경우가 많았습니다. 그렇기 때문에 변수, 함수, 연산자를 만들어서 사용할 수 있는 SCSS와 SASS 중에서 고민하였습니다. SCSS와 SASS의 차이점에는
{}
(중괄호)와;
(세미콜론)의 유무라고 말씀드릴 수 있습니다. 이 점에서는 생략되어지는 SASS가 편리할 수 있지만 mixin문법에서는 +와 -로 표시하기 때문에 직관적으로 코드가 보이지 않는 불편함이 있었습니다. 그렇기 때문에 저는 익숙한 CSS 문법을 사용하는 SCSS를 사용할 것입니다.
id는 문서 안의 유일한 요소를 식별하고 싶을 때 사용하며, class는 공통점이 있는 여러 요소들을 그룹화하여 식별하고 싶을 때 사용합니다. CSS에서 id는 #(샾)을 붙이며, class는 .(피리어드)를 붙여서 구별합니다. 또한 id는 class의 속성보다 우선순위가 높으며, id의 속성은 해당 요소에 부여된 class의 속성과 관계없이 작동합니다.
모든 HTML요소는 박스 모양으로 구성되기 때문에, 이를 박스 모델이라고 합니다.박스 모델에서는 각 HTML 요소들에 padding, border, margin, content가 있습니다. content는 박스의 실질적 내용으로, 텍스트나 이미지 등 요소의 실제 내용이 들어갑니다. padding은 content와 border사이에 있는 안쪽의 여백입니다. border는 content와 padding을 감싸는 테두리입니다. 마지막으로 margin은 border를 기준으로 이웃하는 요소 사이의 간격입니다.
절대 길이 단위와 상대 길이 단위로 나누게 됩니다. 절대 길이 단위는 물리적인 측정 거리를 의미합니다.
px
은 절대길이 단위로, 일반적인 모니터 디스플레이의 1픽셀을 의미합니다. 나머지 단위들은 상대길이 단위입니다. 상대 길이 단위는 다시 글꼴 상대 길이와 뷰포트 백분율 길이로 나뉘게 됩니다. 글꼴 상대 길이는 길이값을 특정 문자나 현재 사용하는 폰트를 기준으로 설정하는 반면에 뷰포트 백분율 길이는 길이 값을 뷰포느 기준으로 설정합니다.em
과rem
은 글꼴 상대 길이이며,vh
와vw
는 뷰포트 상대 길이 입니다.em
은 부모 요소의 폰트 사이즈를 기준으로 하는 반면에rem
은 루트요소 즉, 일반적으로<html>
요소의 폰트 사이즈를 기준으로 합니다. 그리고vh
는 뷰포트의 초기 컨테이너 블록의 높이 1%를 기준으로 하는 반면,vw
는 뷰포트의 초기 컨테이너 블록의 너비 1%를 기준으로 합니다.
CSS 레이아웃 기법에는 크게 Flexbox와 grid 두 가지가 있습니다. flexbox는 인터페이스 내 아이템 간 공간 배분과 정렬 기능을 제공하기 위한 1차원 레이아웃 모델로 설계되었습니다. X축 또는 Y축 방향으로 요소를 배치, 정렬할 수 있어서 각 요소의 순서를 변경할 수 있습니다. 그렇기 때문에 기존 CSS 레이아웃 기술에 비교했을 때 손쉽게 멀티스크린에 대응할 수 있습니다. grid는 테이블처럼 세로 열과 가로 행을 기준으로 요소를 정렬합니다. 그러나 테이블에 비해 grid는 더 다양한 레이아웃을 구현할 수 있습니다. 행과 열 격자 구조에 요소를 자유롭게 배치할 수 있고, HTML 문서 위계 구조와 무관하게, grid 인터페이스 내부에 포함된 자식 아이템을 Grid 내부에 자유롭게 위치시킬 수 있습니다.
flexbox는 레이아웃을 다룰 때 한 번에 하나의 차원(행이나 열)만 다루기 때문에 1차원이라 부른다. 반면, grid 레이아웃은 행과 열을 함께 조절하기 때문에 2차원 모델이라 부른다.
css in JS를 사용하면 class 명이 빌드 시 유니크한 해시 값으로 변경되기 때문에 기존의 복잡한 class 명명 규칙을 해결해줍니다. 또한 컴포넌트 단위로 추상화 되기 때문에 css 파일 간에 의존성을 신경쓰지 않아도 되고, 컴포넌트와 CSS가 동일한 구조로 관리되기 때문에 불필요해진 CSS를 관리하기 위해서 별도의 리소스를 투입할 필요가 없습니다. 또한 CSS가 컴포넌트 스코프에서만 적용되기 때문에 우선순위 문제가 발생하지 않습니다. 하지만 번들의 크기가 커진다는 단점이 있습니다. CSS in JS를 사용하기 위해서는 여러 라이브러리를 사용하게 됩니다. 라이브러리의 추가는 곧 번들 사이즈가 증가함을 의미합니다. 번들 사이즈가 커지게 되면, 다운로드 시간도 오래 걸리기 때문에 사용자 경험에 치명적이게 됩니다. 특히나 CSS in JS는 자바스크립트가 모두 로딩 된 후 CSS 코드가 생성되기 때문에 더 느려집니다. 그렇기 때문에 인터렉션이 비교적 늦다는 단점도 있습니다.
이 둘은 먼저 나온 태그를 더 위에 배치시키고 싶다면 사용하는 속성들입니다. z-index는 position이 relative거나 absolute여야 동작합니다. 먼저 postion이 static 태그들이 나오는 순서대로 쌓이고, 그 위에 relative나 absolute인 태그들이 나오는 순서대로 쌓입니다. 흔히 이 둘은 웹이나 앱의 메뉴를 만들 때 가장 최 상단에 쌓여야하므로 그 때 주로 사용되어집니다.
자바스크립트는 단일 스레드 기반의 언어로 한 번에 하나의 작업만 처리할 수 있습니다. 그렇기 때문에 자바스크립트 엔진은 어떤 작업을 하면 중간에 어떤 함수도 실행될 수 없습니다. 하지만 그렇게 작업을 하게 되면 너무 많은 시간을 사용자는 기다려야합니다. 이러한 문제점을 해결하고자 함수 호출을 관리하는 call stack과 비동기 작업 처리를 위해 Web API가 함께 작업을 처리하게 됩니다. Web API는 작업 완료에 시간이 오래 걸리는 작업을 처리하게 되는데 이 결과값을 처리할 수 있는 callback 함수를 task queue에 쌓습니다. 이 때 작업이 다 완료가 되어서 call stack이 비어있게 되면 task queue의 담겨 있는 callback 함수를 다시 call stack으로 담아서 callback을 실행시키게 됩니다. 이 작업이 event loop입니다.
JavaScript event loop는 call stack이 비어있는 경우, task queue에서 대기하던 callback을 call stack으로 옮겨서 callback을 실행시켜주는 역할을 합니다.
일반적으로 다른 함수의 인자로 전달되는 함수를 callback이라고 합니다. 비동기 작업을 해결하기엔 충분했지만 함수에 담아서 사용해야하기 때문에 코드가 직관적이지 않아서 "callback hell"현상이 일어났습니다. 이 문제를 해결하고자 promise가 탄생하게되었습니다. promise는 성공과 실패라는 executor을 전달해주어야합니다. 이때 작업에 성공했을 경우는 resolve, 실패했을 경우는 reject라고 합니다.그래서 성공 시에는 resolve함수에서 전달된 인자가 인스턴스의 결과값으로 나오게 됩니다. 프로미스 인스턴스는 대기상태인 pending, 성공인 fulfilled, 실패한rejected 3가지 중 하나의 상태를 가집니다. promise는 then과 catch메소드로 프로미스를 리턴하기에 promise chaining이 가능합니다. 하지만 연속되는 비동기 작업이 순서대로 작동되어야하는 경우 콜백지옥처럼 프로미스 지옥이 발생하였습니다. 그래서 async/await가 발생하게 되었습니다. async/await는 동기적으로 작동하는 것처럼 Promise를 조금 더 쉽게 다룰 수 있게 만들어진 문법적 설탕이라고 불립니다. async/await 키워드만 적절히 사용하면 기존 함수를 작성하는 문법을 그대로 살릴 수 있습니다. async 함수를 실행하면 함수 내 리턴값 여부에 상관없이 Promise가 리턴되며, 함수 내의 리턴 값은 Promise result 값으로 전달되어집니다. await 키워드는 async 함수 내에서만 사용이 가능하며 응답이 올때까지 코드 읽기를 멈췄다가 요청이 오고나서야 다음코드를 실행하는 문법입니다. 이때 await로 실행된 코드는 Promise가 아닌 Promise의 result 값을 반환합니다.
Blocking은 호출된 함수가 자신이 할 일을 모두 마칠 때까지 제어권을 계속 가지고 호출한 함수가 대기하도록 하는 것입니다. 예를 들어 실행하는 데 10초가 걸리는 A라는 함수가 실행되면 B라는 함수가 실행되기까지 10초간 대기해야합니다. 반면에 Non-Blocking은 호출된 함수가 할일을 마치지 않았더라고 제어권을 내주어 호출한 함수가 다른 일을 진행할 수 있는 것입니다. 예를 들어 A함수가 종료되지 않은 상태여도 바로 B함수를 실행할 수 있습니다.
동기 실행은 한 작업이 끝남과 동시에 다음 작업이 실행되는 실행방식을 의미합니다. 예를 들어 함수 A가 실행이 완료되자마자 함수 B가 연속적으로 실행되었다면 이것은 동기적 실행입니다. 싱글 스레드에서 synchronous execution은 한 번에 하나씩 작업을 하게 됩니다. 멀티 스레드인 경우에도 동기적으로 실행된다면 A작업이 끝날 때까지 B작업은 기다려야합니다. 비동기 실행은 한 작업이 끝나기 전에 다른 작업을 수행할 수 있습니다. 예를 들어 함수 A가 실행 중인데, 함수 B가 실행되었다면 이는 비동기적 실행입니다. 싱글 스레드에서 Asynchronous execution은 한 스레드에서 여러 작업을 수행할 수는 있지만, 결국 한 번에 여러 작업을 수행할 수는 없습니다. 반면에 멀티스레드인 경우는 여러 작업이 비동기로 실행되는 경우 여러 스레드가 동시에 작업할 수 있습니다.
node.js는 싱글 스레드 방식이 아닙니다. 싱글 스레드 방식으로 동작하는 것은 브라우저에 내장된 자바스크립트 엔진입니다. 만약 모든 자바스크립트 코드가 자바스크립트 엔진에서 싱글 스레드 방식으로만 동작한다면 비동기적으로 동작할 수 없을 것입니다. 즉, 자바스크립트 엔진은 싱글 스레드로 동작하지만 브라우저나 Node.js는 멀티 스레드로 동작하는 것입니다.
우선 event-driven architecture에 대해 설명 드리겠습니다. 이벤트 기반 아키텍처는 애플리케이션 설계를 위한 소프트웨어 아키텍처 및 모델입니다. 예를 들어 판매 중인 컴퓨터를 사용자가 구매하여 판매된 상태로 바뀌게 되었을 때 컴퓨터이 상태 변화를 감지한 시스템에서 이벤트가 발생하게 되어서 관련 부서 시스템에 자동으로 상태가 변경됨을 전송합니다. 이후 전송된 이벤트는 각 시스템의 요구에 따라 적절한 처리 과정을 거치게 됩니다. 끊임없이 변화하며 상호작용하는 데이터 간 흐름에서 사용자가 일으긴 동작을 event라고 합니다. 즉 키보드와 마우스의 조작으로 상황에 따라 그에 반응하는 코드로 인해 새로운 현상이 일어나는 것을 예로 들 수 있습니다. Node.js엔ㄴ HTML 요소가 없기 때문에 대부분의 event는 프로세스, 네트워크 파일 과의 상호작용 등에서 비롯됩니다. 따라서 Node.js는 event-driven atchitecture라고 할 수 있습니다.
this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기참조 변수를 뜻합니다. this는 지역변수처럼 사용될 수 있으며, 함수가 호출되는 시점에 의해 동적으로 결정되는 dynamic scope입니다. dynamic scope는 함수를 호출한 위치가 아닌 어디서 호출했는지에 따라 동적으로 상위 스코프를 결정하는 것을 뜻합니다. 그러므로 this는 함수가 호출되는 시점에 의해 동적으로 결정되기 때문에 dynamic scope를 따르게 됩니다.
객체 지향 프로그래밍은 서로 연관된 변수와 함수를 그룹핑해서 소프트웨어를 조금 더 쉽고 명확하게 관리할 수 있도록 해주는 것을 뜻합니다. 예를 들어 옷정리를 할 때 겨울옷과 여름옷으로 구분하여 정리를 할 때처럼 서로 연관되어 있는 것들은 기능별로 그룹화해서 이름을 붙여서 프로그래밍을 할 수 있습니다. 이 객체 지향을 사용한다면 유지보수가 쉽고 프로그램의 생산성을 높일 수 있습니다.
prototype chaining은 기존에 존재하는 객체를 기반으로 새로운 객체를 생성할 때 사용됩니다. 만약 객체의 프로퍼티에 접근하려고 할 때, 프로퍼티가 없다면
_proto__
접근자 프로퍼티를 따라서 본인의 부모 역할을 하는 객체를 순차적으로 돌아다니며 검색합니다. Prototype Chaning의 최상위 객체는 Object.prototype이며, 해당 객체의 프로퍼티, 메소드는 모든 객체에게 상속됩니다.
IIFE는 함수 정의와 동시에 즉시 호출되는 즉시 실행함수를 의미합니다. 즉시실행함수는 단 한번만 호출이 되며 다시 사용할 수 없는 특성이 있습니다. 예를 들어 어떠한 버튼을 클릭할 때마다 카운트를 올려주고, 해당 클릭 수를 alert창으로 띄워준다고 가정해보겠습니다. 'count'라는 전역변수를 선언한 후 해당 변수를 계속해서 참조할 수도 있겠지만, click하는 event handler 자체에 해당 데이터를 넣는 방법이 더 좋을 것 같습니다. 그렇게 되면 추가적인 변수로 global scope를 더럽히지 않고, IIFE를 적용해 closure를 조정하여 'count'변수를 외부에서 참조하지 못하도록 보호 할 수 있게 됩니다. 이러한 상황에서 IIFE가 사용되어지기에 'count'에 담겨있는 데이터의 부작용을 사전에 방지시킬 수 있습니다.
setTimeOut()에 의해 실행된 코드는 별도의 실행 컨텍스트에서 setTimeOut이 호출된 함수로 호출되어집니다. this는 함수의 호출 방식에 의해서 결정되기에 this를 호출된 함수에서 설정 혹은 할당하지 않는 경우, non-strice모드에서는 전역(혹은 window) 객체, strict 모드에서는 undefined를 기본 값으로 합니다. 그렇기에 setTimeOut함수를 이용해서 window객체나 undefined가 출력되는 에러를 볼 수 있습니다. 이 방법을 해결하기 위해서는 3가지 해결책이 있습니다. 첫번째로는 this 설정이 필요한 곳을 함수로 감싸주고, 두번째는 화살표 함수로 작성하는 방법입니다. 마지막인 세번째는 bind를 활용해서 this를 지정해주면 됩니다.
setTimeout()에 의해 실행된 코드는 별도의 실행 컨텍스트에서 setTimeout이 호출된 함수로 호출됩니다. 호출된 함수에 대해서는 this 키워드를 설정하는 일반적인 규칙이 적용되며, this를 설정 혹은 할당하지 않은 경우, non-strict 모드에서 전역(혹은 window) 객체, strict모드에서 undefined를 기본 값으로 합니다. 다음 예제를 봐주세요.
myArray = ["zero", "one", "two"];
myArray.myMethod = function (sProperty) {
alert(arguments.length > 0 ? this[sProperty] : this);
};
myArray.myMethod(); // prints "zero,one,two"
myArray.myMethod(1); // prints "one"
위와 같이 동작하는 이유는 myMethod 호출될 때, this는 myArray로 설정되므로, 함수 내에서의 this[속성]은 myArray[속성]와 같습니다. 하지만, 다음 예제를 보면:
setTimeout(myArray.myMethod, 1000); // 1초 뒤 "[Window 객체]" 출력
setTimeout(myArray.myMethod, 1500, "1"); // 1.5초 뒤 "undefined" 출력
myArray.myMethod 함수는 setTimeout에 전달되고, 호출될 때 this는 설정되어 있지 않아 window 객체를 기본값으로 합니다. forEach, reduce 등 Array 메서드 같이 this를 매개변수로 넘길 수 있는 옵션 또한 없습니다. 그리고 아래에서 보다시피, call을 사용해 this를 설정하는 것도 동작하지 않습니다.
setTimeout.call(myArray, myArray.myMethod, 2000); // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object"
setTimeout.call(myArray, myArray.myMethod, 2500, 2); // same error
setTimeout(function(){myArray.myMethod()}, 2000); // 2초 뒤"zero,one,two" 출력
setTimeout(function(){myArray.myMethod('1')}, 2500); // 2.5초 뒤"one" 출력
setTimeout(() => {myArray.myMethod()}, 2000); // 2초 뒤 "zero,one,two" 출력
setTimeout(() => {myArray.myMethod('1')}, 2500); // 2.5초 뒤 "one" after 2.5 출력
var healthObj = {
name : "달리기",
lastTime : "PM10:12",
showHealth : function() {
setTimeout(function(){
console.log(this.name + "님, 오늘은 " + this.lastTime + "에 운동을 하셨네요");
}.bind(this), 500);
}
}
자바스크립트는 콜 스택과 메모리 힙이라는 메모리 구조를 통해 데이터 및 코드를 실행관리합니다. 여기서 콜 스택은 원시타입 즉, 숫자, 문자와 같은 데이터가 저장되는 공간이고, 메모리 힙은 참조타입인 배열, 객체 함수 같은 데이터가 저장됩니다. 즉, 참조타입 데이터를 선언하게 되면 해당 주소값만 콜 스택에 저장되고 주소값에 따른 value는 힘에 저장되어집니다.
call-by-value는 함수 호출 방식 중 하나입니다. 값에 의한 호출이라고 불러집니다. 기본적으로 자바스크립트는 원시값을 argument로 넘겨주면 call-by-value의 형태로 작동합니다. 더 자세히 설명드리자면 함수를 호출하면 argument로 값이 넘어오게 됩니다. 여기서 argument는 함수의 변수에 집어넣는 값을 의미합니다. 그 값은 복사된 값으로 넘어오게 되고 복사되었기에 해당 인자를 변형시켜도 영향을 받지 않습니다. 그렇기에 기존 정의 된 인자를 출력한다면 값이 바뀌지 않다는 것을 확인할 수 있습니다.
var a = 1;
var func = function(b) { // callee
b = b + 1;
console.log(b) // 2
}
func(a); // caller
console.log(a); // 1
var a = {};
var func = function(b) { // callee
b.a = 1;
}
func(a); // caller
console.log(a.a); // 1