1. 함수형 프로그래밍 패러다임
자바스크립트는 함수형 프로그래밍 패러다임을 추구하며, 추가로 객체지향 프로그래밍 패러다임도 지원한다.
자바스크립트의 핵심은 함수(함수형 프로그래밍 패러다임)와 객체(객체지향 프로그래밍 패러다임)이다.
- 객체의 경우 객체지향 프로그래밍 패러다임의 여러 패턴들을 적용하고 싶다면, 클래스를 활용할 수 있다.
그 중 함수형 프로그래밍 패러다임에 대해 알아볼 것인데 함수형 프로그래밍은 아래의 2개 조건을 만족하는 것을 의미한다.
- 일급함수 : 함수 변수 할당 + 함수 파라미터 + 함수 반환
- 함수 변수 할당 = 함수 표현식(Expression)
- 함수 파라미터
- 함수 반환
- 함수 변수 할당 = 함수 표현식(Expression)
- 순수 함수 : No-Side-Effects (참조 투명성) = Thread-Safe = 함수 정의할 때 완전한 문장을 만들어야한다.
- 기본적으로 함수는 [파라미터(input) - 함수로직 - 반환(output) ] 3개의 구성 요소로 이루어져 있다.
- 순수 함수의 경우 [파라미터 - 로직 - 반환 ] 이외의 것들에 의존하지 않아야 한다.
- 함수는 명확한 명사(주어, 목적어), 동사에 따라 [파라미터 - 로직 - 반환] 에 명시되어야 한다.
- Thread-Safe : 멀티 스레드를 활용한 개발 시 순수함수 특성은 매우 중요하다.
- 병렬적으로 수행되는 모든 함수들은 서로에게 영향을 주어선 안되고, 외부의 값에 접근해서도 안된다.
- 병렬적으로 수행되는 모든 함수들은 서로에게 영향을 주어선 안되고, 외부의 값에 접근해서도 안된다.
2. 자바스크립트 함수 정의 및 사용
2-0-1. 자바스크립트 변수 선언 방법 : var → let, const
함수 정의와 사용에 들어가기에 앞서, 변수에 대해 배워야한다.
변수 선언은 자바스크립트 버전인 ECMAScript6(2015) 이전에는 var 하나의 방법으로만 선언하였다.
그 이후로 변수 선언은 let과 const로 선언할 수 있다.
- let : 가변 변수 선언 시
- const : 불변 변수 선언 시

- 위의 그림을 통해 var, let, const 차이에 대해 정리해보자
- var : 재할당 가능 + 재선언 가능 + 함수 레벨 스코프
- let : 재할당 가능 + 재선언 불가 + 블록레벨 스코프
- const : 재할당 불가 + 재선언 불가 + 블록레벨 스코프
- 그렇다면 함수 레벨 스코프와 블록 레벨 스코프의 차이는 무엇일까?
- 스코프 : 변수에 대한 접근 영역
- 함수 스코프 : 함수가 생성 시 스코프 영역이 생겨난다.
- 블록 스코프 : 블록이 생성 시 스코프 영역이 생겨난다.
- 함수 스코프 : 함수가 생성 시 스코프 영역이 생겨난다.
- 스코프 : 변수에 대한 접근 영역
2-0-2. 자바스크립트 엔진의 개략적 수행 방식 및 호이스팅과 var와 let의 호이스팅 차이점
자바스크립트 엔진이 수행하는 것은 사실상 자바스크립트 파일이고, 이것 또한 하나의 큰 함수라고 볼 수 있다.
따라서 자바스크립트 엔진의 자바스크립트 파일 구동 방식은 자바스크립트의 함수 구동 방식과 동일하다.
이에 자바스크립트 엔진의 자바스크립트 함수 구동 방식에 대해 아래 간단하게 2개의 Phase로 나누어 설명하겠다.
프로그램에 메모리와 CPU 자원을 할당한 뒤 구동한다면 이것은 프로세스라고 한다.
모든 함수의 구동은 그 함수 구동을 위한 메모리 영역을 확보하는 것에서부터 시작한다.

- 함수의 실행 = 실행 컨텍스트는 아래 절차대로 수행된다.
- Creation / Compile Phase = Pre-Parsing (Lexer ~= Paser)
- 변수 선언 + 함수 선언 (Declaration) = 렉시컬 환경 내 적재 + var 변수 초기화
- Excution Phase
- 변수 할당 + 함수 실행 + let, const 변수 초기화
- 만약 변수를 할당 할 때 선언이 없다면 찾아나서야 한다 (Scpoe Chain)
- 함수 실행 시 함수 선언부로 가서 박제된 함수를 실행한다.
- Creation / Compile Phase = Pre-Parsing (Lexer ~= Paser)

var, let, const의 변수 호이스팅에 대한 차이점은 해당포스팅에서 다루었으니 넘어가도록 하자
[ASAC_04/JavaScript] 호이스팅(Hoisting)
1. 호이스팅(Hoisting) 호이스팅(Hoisting)이란 함수내의 변수 및 함수의 선언들을 모두 유효 범위의 최상단으로 끌어올려 주는 JavaScript의 기능 중 하나이다. 실제 코드가 끌어올려 코드의 변화가 있
rnclf1005.tistory.com
2-1. 함수 작성 방법
자바스크립트 기본 문법에 앞서 가장 기본이 되는 함수 정의 방법에 3가지에 대해 알아보자
- 함수 선언문 (Declartion) : 함수 선언 + 정의
- 함수의 선언과 정의가 한꺼번에 되었으므로 함수 호이스팅이 발생한다.
hoisting() // 함수 호이스팅 = function is hoisted function hoisting() { console.log('function is hoisted') }
- 함수의 선언과 정의가 한꺼번에 되었으므로 함수 호이스팅이 발생한다.
- 함수 표현식 (Expression) : 표현식 = 식별자(변수) + 연산자(=) + 리터럴(함수)로 이루어짐
- 함수 표현식의 경우 변수에 함수를 할당했다고 생각하면 되기 때문에 변수 호이스팅이 발생한다.
hoisting() // 변수 호이스팅 = ReferenceError: hoisting is not defined let hoisting = function () { console.log('variable is hoisted') }
- 함수 표현식의 경우 변수에 함수를 할당했다고 생각하면 되기 때문에 변수 호이스팅이 발생한다.
- 화살표 함수 : 단순히 표현만 간략해진게 아니라 내부 동작 또한 간략화하기 위해 사용
- 함수 리터럴은 2가지 방식으로 표현 : function() {} 과 () => {}
2-2. 함수 정의 방식
함수는 아주 단순하게 인풋(파라미터) → 로직 → 아웃풋(반환) 으로 구성되어 있다.
그렇다면 이 세가지를 어떻게 구성할지가 함수의 전부라고 할 수 있다.
- 함수에게 전달인자를 전달하는 방식
- 인자 형태로 전달(ex. function(a, b, c, d) ) : 순서 - 수가 적고 명확하게 지정할 때(반드시 전달해야 한다)
- 객체 형태로 전달(ex. function({c, a, b, d} ) : 비순서 - 앞으로 개수가 늘어날 가능성이 높을 때
- 함수의 값을 반환하는 방식
- 객체를 통한 반환 : 비순서 - 2개 이상 반환시, 사용할 프로퍼티만 사용
- 배열을 통한 반환 : 보편성 - 이름을 다양하게 활용하고 싶을 때
3. 자바스크립트 객체 정의 및 사용
- 객체 명칭 : 객체 = 프로퍼티 집합 = 필드 + 메서드
- 메서드 표현법은 Default ( a: function() {} ) 과 Shorten ( a: () => {} ) 로 나뉜다.
- JavaScript는 Java와 같이 객체 사용에 꼭 클래스가 필요한 것은 아니다.
- 하지만 조금이라도 SOLID나 디자인 패턴같은 객체지향적 요인으로 코드 재사용성을 증대시키기 위해서는 클래스를 사용해야 한다.
- 클래스를 사용해야하는 이유 → Encapsulation : 단순히 감추는 것이 아닌 독립된 시스템을 구축할 수 있어야 한다.
- 이처럼 객체 지향 프로그래밍을 얼마나 잘하는 가는 얼마나 클래스(독립된 시스템 구축)를 잘 만드는가로 이어진다.
3-1. 객체 혹은 클래스 내 Getter / Setter
- Private 변수 : 클래스 필드 앞에 #을 붙인다.
class User {
#firstname = "Aaron"
#lastname = "Ryu"
get fullname() {
return `${this.#firstname} ${this.#lastname}`
}
set fullname(value) {
[this.#firstname, this.#lastname] = value.split(" ")
}
}
var user = new User()
user.fullname = "Baron Kim"
console.log(user.firstname)
console.log(user.lastname)
console.log(user.fullname)
Object.getOwnPropertySymbols(user)
4. 모듈 시스템 : ES Modules(ESM)의 import / export 표현법
자바스크립트 모듈에는 두개의 방식이 존재하나, 프론트엔드에서 ES6 이후 표준화되어 있는 ESM에 대해서만 다뤄보자.
4-1. ESM = ES(ECMAScript) Modules : import / export
웹 브라우저(프론트엔드)에서 자바스크립트는 하나의 전역 Scpoe에서 모든 자바스크립트 모듈들이 Scope 공유한다.
- Named Export 예시 : 고정된 명칭
export const sum = (x, y) => x + y import { sum } from './util.mjs' console.log(sum(2, 4))
- Default Export 예시 : 명칭 변경 가능
export default (x, y) => x + y
import ssam from './util.mjs'
console.log(ssam(2, 4))
const sum = (x, y) => x + y
export default sum
import ssam from './util.mjs'
console.log(ssam(2, 4));
4-2. CJS = CommonJS : require / module.exports
웹 브라우저(프론트엔드)가 아닌 웹 서버(백엔드)에서 자바스크립트 모듈을 쓰려면 파일 단위 모듈화 절실하다.
jQuery 등 다양한 라이브러리 등장 및 웹 서버 자바스크립트 코드량이 많아지면서 변수, 함수의 모듈화 필요
- Node.js 12 부터 새로운 모듈 시스템으로 ESM 을 채택하긴했지만, 여전히 Node 에선 CJS 가 지배적
모듈 시스템에 대한 요구 등장 ← 과거에는 IIFE, 클로저, 스코프 개념들을 조합하여 모듈을 힘들게 흉내
4-3. 모듈 시스템을 어떤 상황에서 어떤 방법을 사용해야 할까
CJS 는 Node.js(서버) 에 ESM 은 브라우저에 좋다.
- CJS 를 지원하는것이 중요한 이유 = Node.js 활용한 SSR 사용 서비스를 위해 CJS 지원 필요
- ESM 을 지원하는것이 중요한 이유 = Tree-shaking 을 지원하는 ESM 이 브라우저 성능에 중요
- CJS - 모듈 동기 로드 : 빌드타임에 정적 분석 불가 → 런타임에 모듈 관계 파악
- ESM - 모듈 비동기 로드 : 정적 모듈 의존 강제 = 빌드타임에 Tree-shaking 가능
5. 자바스크립트 내 비동기 처리 : Promise 및 Async / Await
5-1. Callback (이때 Callback은 Asynchronous가 아니다.)
함수(콜백)를 파라미터로 넘겨(일급함수) 파라미터를 받은 함수에게 실행권을 넘기는 것
// ex.1
function callback(param1, param2) {
console.log(param1 + " and " + param2);
}
function caller(callback) {
callback(arguments[1], arguments[2]);
}
caller(callback, "hello", "goodbye");
// ex.2
function callback(param1, param2) {
console.log(param1 + " and " + param2);
}
function caller(callback) {
setTimeout(() => {
callback(arguments[1], arguments[2])
}, 2000);
}
caller(callback, "hello", "goodbye");
- Callback 자체로는 동기 혹은 비동기 함수와 관계는 없으니 Callback이 많이 사용되는 곳은 비동기일 뿐
- 비동기 함수에게 실행권을 넘기기 위해 Callback을 많이 사용할 뿐이지 Callback만으로 비동기는 아니다.
5-2. Callback Hell
Callback 결과값을 순차적으로 연결할 때 발생한다.
- Callback의 결과가 그 다음 Callback의 실행에 필요한 경우
// ex.1
step1(function (resultfromstep1) {
step2(resultfromstep1, function (resultfromstep2) {
step3(resultfromstep2, function (resultfromstep3) {
step4(resultfromstep3, function (resultfromstep4) {
console.log(resultfromstep4)
});
});
});
});
// ex.2
step1(function (resultfromstep1) {
step2(function (resultfromstep2) {
console.log(resultfromstep1 + resultfromstep2)
step3(function (resultfromstep3) {
console.log(resultfromstep2 + resultfromstep3)
step4(function (resultfromstep4) {
console.log(resultfromstep3 + resultfromstep4)
});
});
});
});
5-3. Promise (Callback과 Asynchronous의 합으로 이해하자)
- Callback 함수(자신의 제어권 넘김) : Callee = Consumer (파라미터를 받길 기다림)
- Asynchronous 함수 (Executor) : Caller = Producer (파라미터를 주입)
그래서 Promise를 Producer-Consumer Pattern On Asynchronous 라고 표현
function caller(callee) {
var produced = producing()
callee(produced)
}
caller(function callee(produced) {
consuming(produced)
})
Promise의 상태와 그에 따른 콜백
- Promise는 비동기를 위해 탄생한 개념(Callback + Asynchronous)이기에 상태를 가진다.
- Promise의 Producing은 성공 / 실패 2개의 상태로 나뉘며, 성공 / 실패에 따른 콜백 설정
- Promise의 Producing은 성공 / 실패 2개의 상태로 나뉘며, 성공 / 실패에 따른 콜백 설정
- [ Pending → Fulfilled(성공, Resolve) / Rejected(실패, Reject) ]
- 코드 설명 : new Caller((succeded_callback, failed_callback) => {...})
- Callback 형태 (Pseudo)
function caller(resolve, reject) { const produced = producing() // API 호출해줘, 이미지 가져와줘 if (succeeded) resolve(produced) if (failed) reject(produced) } caller( function resolve(produced) { consuming(produced) }, function reject(produced) { consuming(produced) }, )
- 1. Promise 형태 (Pseudo)
new Promise( function caller(resolve, reject) { const produced = producing() if (succeeded) resolve(produced) if (failed) reject(produced) } ) .then(function resolve(produced) { consuming(produced) }) .catch(function reject(produced) { consuming(produced) })
- 2. Promise 실무에서 사용하는 형태
new Promise((resolve, reject) => { const result = getUserInformationAPI() if (result.success) { resolve(result.user) } if (result.failed) { reject({ type: 'No User', message: 'Error Occured' }) } }) .then((user) => { console.log(user) }) .catch((error) => { console.log(error.message) })
- 성공은 .then 로 정의된 성공 콜백으로
- 실패는 .catch 로 정의된 실패 콜백으로
- Callback 형태 (Pseudo)
- Resolve 와 Reject
- 하나의 Promise 객체는 Caller 로 인스턴스화되고
- Resolve 성공 / Reject 실패 Callback 은 .then 과 .catch 로 주입된다.
- .then()내부에 Resolve Callback 함수
- **.catch()** 내부에 Reject Callback 함수
5.4. Promise Hell = Nested Promise
- Promise 의 결과가 그 다음 Promise 실행에 필요한 경우
step1(value1)
.then((value2) => {
step2(value2)
.then((value3) => {
step3(value3)
.then((value4) => {
console.log(value4)
})
})
})
5.5. Promise Chain
- 코드 : Promise Hell 해결 방법 1 = Promise Chain 으로 아래와 같이 변환 → 장점 : 한번에 한 값 사용
step1(value1)
.then((value2) => {
return step2(value2)
})
.then((value3) => {
return step3(value3)
})
.then((value4) => {
console.log(value4)
})
5.6.1. Async / Await
- 코드 : Promise Hell 해결 방법 2 = Async / Await 으로 아래와 같이 변환 → 장점 : 모든 값 사용 가능
- 복습 : Promise 는 Caller (Executor) 와 Callee (Callback) 가 하나의 Promise 객체로 뭉쳐진것
- Async/Await 은 이 둘을 분리한것 = Produce-Consumer 둘을 똑 뗀것
- Async 은 Caller 를 정의하는곳에서 사용하고 **async** function caller() {}
- Await 은 Callee 를 정의하는곳에서 사용된다 const result = **await** caller()
- Async/Await 은 이 둘을 분리한것 = Produce-Consumer 둘을 똑 뗀것
5.6.2. Async / Await 쉽게 이해하기
- async = Promise 상자반환 (Promise.resolve 로 감싸져있으면 바로 반환, 아니면 상자 포장 반환)
- await = Promise 상자열기 (Promise 객체를 기다렸다가 상자를 열어 내부 값을 반환)
- async 가 붙은 함수는 반드시 Promise 를 반환하고, Promise 가 아닌 것은 Promise 로 감싸 반환
- async 가 붙은 함수는 반드시 Promise 를 반환
- 반환값이 Promise 가 아닌 것은 Promise 로 감싸 반환 (위와 동일)
- await 키워드를 만나면 Promise 가 처리될 때까지 기다린다.
5.6.3. Promise 내 Caller (Executor) 실행 시점
new Promise 가 생성되는 즉시 Caller (Executor) 함수가 실행 = 즉시 실행
Promise 객체의 3가지 상태
- 대기 Pending : 약속된 결과 값이 반환(다음 상태로 전이)되지 않은 상태, 초기 상태
- Non-Pending ⇒ .finally (Non-Pending 콜백)
- 이행 Fulfilled : 연산이 성공적으로 완료된 상태 ⇒ .then (성공 콜백)
- 거부 Rejected : 연산이 실패한 상태 ⇒ .catch (실패 콜백)
- Non-Pending ⇒ .finally (Non-Pending 콜백)
5.7. Promise 사용 시 주의할 점
Promise 안에서는 무조건 반환을 위해서는 Resolve 사용, Return 절대 아님
5.8. Promise 에러 처리 방법 & Async / Await 에러 처리 방법
- Promise 에러 처리 = .catch (실패 콜백)
- .catch 에서 처리하기 위해서는 Promise 내 Caller 에서 Reject 로 반환해야함
6. 자바스크립트 실무에서 많이 활용되는 ES6+ 문법
자바 버전 : Java 11, Java 17 등 지원하는 문법들이 점차 추가되고 발전
자바스크립트 버전 : 자바스크립트도 자바나 다른 여느 언어와 같이 ECMAScript 으로 매년 새 표준이 등장
- Async / Await 에러 처리 = Try-Catch
- Await 에서 에러 처리하려면 어쩔 수 없이 Try-Catch 를 사용해야함
- Reject 로 단순히 우리가 원하는 값을 반환했을때
- Await 에서 에러 처리하려면 어쩔 수 없이 Try-Catch 를 사용해야함
6.1. 객체 관련
- 객체 비구조화 Destructure : const address → const { country, city } = address
- 객체 프로퍼티 초기화 단축 Property Initializer Shorthand (콜론 제거)
- 계산된 프로퍼티명 혹은 메서드명 Computed Named Property / Method
-
- 깊은 복사를 지원하기 위해 어떻게 해야할까?
- 깊이가 깊지않다면 그냥 조금만 고생해서 복사하고, 깊다면 깊은 복사 함수를 구현하여 복제하라
- 재귀함수를 활용했다. 재귀함수는 코딩테스트에 많이 나오는 개념이야 이해하는법을 익히길
- 추가 : Array 도 따로 처리 필요
- 재귀함수를 활용했다. 재귀함수는 코딩테스트에 많이 나오는 개념이야 이해하는법을 익히길
- 깊이가 깊지않다면 그냥 조금만 고생해서 복사하고, 깊다면 깊은 복사 함수를 구현하여 복제하라
6.2. 배열 관련
- 객체 프로퍼티 / 메서드 혹은 배열 요소 Trailing Comma
- 특정 요소 포함 여부 확인 Array.prototype.includes()
6.3. 함수 관련
- Spread Syntax 및 Rest Parameter
- Spread Syntax : 함수 인자(Argument 전달인자 = 밖) 펼치기 + 배열 및 객체 조작
- 함수 인자 펼치기 : Argument 전달인자 = 밖
- 배열 펼치기 : 배열 복사 + 배열 연결 + 배열 요소(Element) 추가
const arr = ["a", "b"] const arr1 = ["c", "d"] const arr2 = [...arr, ...arr1] // 두 배열의 요소를 합친 새로운 배열 생성
const arr = ["b", "c"] const arr1 = ["a", ...arr, "d"] // 중간에 다른 배열의 요소를 추가하여 새로운 배열 생성
- const arr = ["a", "b", "c", "d"] const arr1 = arr // 이것을 복사라고 말하는 사람은 경계 대상 1호. const arr2 = [...arr] // 배열 복사. 새로운 배열의 생성하되 그 요소들을 arr을 펼쳐서 새로운 배열의 요소로써 채우는 것.
- 객체 펼치기 : 객체 복사 + 객체 연결 + 객체 프로퍼티(Property) 추가
- Rest Parameter : 함수 파라미터(Parameter 매개변수 = 안) 펼치기
- 함수 파라미터 펼치기 : Parameter 매개변수 = 안
- Spread Syntax : 함수 인자(Argument 전달인자 = 밖) 펼치기 + 배열 및 객체 조작
- 함수 기본 파라미터 값 Default Parameters
- 커링을 통한 파라미터 쪼개기 Currying
- Exception Handling : Try / Catch / Finally / Throw얕은 복사 : Object.assign()
6.4. 문자열 및 자료구조 관련
- 문자열 포맷팅 String Literal : 백틱을 통한 문자열 직관적 결합
- Map : 유일 Key 및 Key-Value 집합
- Set : 유일 Key 집합
6.5. 반복문 및 조건문 관련
- for .. of 와 for .. in 의 차이 + forEach
- Null Guarding (null or undefined, Null 로 통칭)
- ?? : Default = 앞엣값이 Null 인 경우 표시할 값 (Nullish coalescing)
- ?. : Null 이라면 넘겨버린다 Null Cascading 이라고도 부른다
- ! + !. : Non-null Assertion - 절대 Null 이면 안된다는것을 개발자가 보장하는것
- 선택적 노출 / 호출 : && (if 간단히 대체) + ? (Ternary Operator, if-else 간단히 대체)
- && 의 단점 : 앞의 구문이 false 혹은 undefined 일때 그대로 false 혹은 undefined 를 반환
- 예시) className 안에 undefined 혹은 false 클래스명이 들어갈 수 있음
- && 의 단점 : 앞의 구문이 false 혹은 undefined 일때 그대로 false 혹은 undefined 를 반환
- Value Comparison Operator == and ===
- Type Conversion (명시적 타입 변환) vs Coercion (암묵적 타입 변환)
- 깊은 복사를 지원하기 위해 어떻게 해야할까?
'프론트엔드 > JavaScript' 카테고리의 다른 글
[JavaScript] 우테코 로또 게임 만들기 (0) | 2024.01.31 |
---|---|
[JavaScript] map / set / trim / includes / filter / reduce (1) | 2024.01.30 |
[JavaScript] 호이스팅(Hoisting) (0) | 2024.01.24 |
[JavaScript] 깊은 복사 / 얕은 복사 (1) | 2024.01.24 |
[JavaScript/CSS] CSS Selector 정리 (1) | 2024.01.16 |