목차
반응형
객체지향 프로그래밍
객체지향 프로그래밍이란?
- 현실 세계의 사물처럼, 프로그래밍에 필요한 개념을 객체라는 개념으로 추상화하고, 객체들간의 상호작용을 중심으로 프로그래밍하는 패러다임을 의미한다.
객체지향 프로그래밍에서 객체란?
- 좁은 의미로 객체는 클래스의 인스턴스라고 할 수 있고, 넓은 의미에서 객체는 속성과 행위로 이루어진 독립된 단위를 의미한다.
객체지향 프로그래밍의 특징을 설명해주세요.
- 추상화 : 추상화는 객체간에 공통적인 특징이나 기능을 추출하는 것을 의미한다.
(예를 들어, 강아지와 고양이를 동물이라는 공통적인 특징으로 추상화 할 수 있다.) - 상속 : 상속은 기존 객체의 기능을 다른 객체에서 사용할 수 있도록 넘겨주는 것을 의미한다.
- 다형성 : 다형성은 어떤 메소드가 다양한 방식으로 동작할 수 있는 특징을 의미하는데, 오버라이딩이나 오버로딩을 통해서 구현할 수 있다.
- 캡슐화 : 캡슐화는 필요한 데이터와 데이터를 사용하는 행위를 함께 묶어두고, 외부에서는 정해진 메소드를 통해서 객체와 상호작용 할 수 있는 특징을 의미한다.
자바스크립트는 객체지향 프로그래밍 언어인가요?
- 자바스크립트는 여러가지 프로그래밍 패러다임을 따르는 멀티 패러다임 프로그래밍 언어고, 그 중에서도 객체지향 프로그래밍도 가능한 언어다.
- 다만 C++나 Java 같은 클래스 기반 객체지향 프로그래밍 언어라고는 할 수 없다.
프로토타입
프로토타입에 대해 설명해주세요.
- 자바스크립트는 객체들로 구성되어 있는데, 객체는 각각 프로토타입을 가지고 있다.
- 프로토타입을 통해 자바스크립트의 상속이 구현되어 있는데, 프로토타입은 객체의 원형이라는 의미 그대로 상위 객체의 속성이나 메소드를 하위 객체에서 사용할 수 있다.
- 객체에서 어떤 속성이나 메소드에 접근할 때, 현재 객체에 찾는 값이 없으면 해당 객체의 상위 프로토타입에서 접근할 값을 찾는 것을 반복한다. 이걸 프로토타입 체인이라고 한다.
Strict Mode
Strict Mode에 대해 설명해주세요.
- Strict mode는 ES5에 추가된 문법으로, 더 엄격한 환경에서 자바스크립트 코드를 실행하도록 제한하는 문법이다.
- Strict mode는 “use strict” 라는 키워드를 작성해서 적용할 수 있다.
- Strict mode가 적용되면 선언하지 않은 변수를 참조하거나 delete 를 통한 식별자 제거, with 문 사용 시 에러가 발생한다.
- Strict mode가 적용되면 일반 함수 내부에서 this를 사용하면 this에 undefined가 바인딩된다.
클래스와 Strict Mode
- ES6에 추가된 Class 문법을 사용하면 클래스 내부의 body는 자동으로 Strict Mode로 실행된다.
Wrapper Object
래퍼 객체에 대해 설명해주세요.
- 자바스크립트에서는 원시 타입을 제외한 모든 나머지는 객체 타입이다.
- 이런 원시 타입도 객체 타입처럼 사용해야 하는 경우가 있는데, 이때 자바스크립트 엔진이 원시 타입의 값을 일시적으로 연관된 객체로 변경하고 사용하게 된다. 이때 사용하는 객체를 래퍼 객체라고 부른다.
- 그리고 래퍼 객체는 사용이 끝나면 다시 원시 타입의 값으로 되돌아간다.
래퍼 객체 사용을 예를 들어주세요.
- 변수에 문자열을 할당하고, 변수에 .length 라는 프로퍼티를 참조하는 경우가 있다.
- 문자열 타입은 기본적으로 원시 타입이라서 객체처럼 프로퍼티를 가질 수 없지만, 자바스크립트 엔진에서 String이라는 래퍼 객체로 변경해주고, 객체의 프로퍼티에 접근하도록 처리하기 때문에 문제 없이 사용할 수 있다.
this
this에 대해서 설명해주세요.
- 자바스크립트에서 this는 일반적으로 자신이 속한 객체나 생성할 인스턴스를 가리키는 자기 참조 변수다.
this 바인딩이란?
- 바인딩은 식별자와 값을 연결하는 과정을 의미한다.
- 자바스크립트에서 this는 호출되는 방식에 따라서 어떤 값이 연결될지 결정된다. 이걸 this 바인딩이라고 한다.
상황에 따른 this 바인딩에 대해 소개해주세요.
일반적으로, 크게 4가지 방식으로 사용할 수 있다.
- 첫번째는 전역이나 일반 함수 내부에서 사용하는 경우, 이때 this는 전역 객체를 가리키게 된다.
- 두번째는 오브젝트 내부의 메소드에서 사용되는 경우, 이 경우에는 호출한 객체를 가리키게 된다.
- 세번째로 생성자 함수 내부에서 사용되는 경우, 이 경우에는 생성할 인스턴스를 가리키게 된다.
- 마지막으로 이벤트 리스너의 콜백 함수에서 사용하는 경우, 이 경우에는 이벤트의 current target과 같은 곳을 가리키게 된다.
몇가지 예외가 있는데,
- apply, call, bind 메소드를 통해 호출한 경우, 파라미터로 전달된 객체를 바인딩하게 된다.
- ES6에 추가된 화살표 함수를 이용하는 경우, 화살표 함수는 특정 this를 가지고 있지 않기 때문에 바로 상위 환경에서 this를 참조한다.
실행 컨텍스트
실행 컨텍스트에 대해 설명해주세요.
- 실행 컨텍스트는 해당 코드가 평가되고 실행되는 환경을 의미한다.
- 코드를 실행하기 위해 필요한 정보를 모아놓은 컨텍스트들을 콜 스택에 쌓아올리고, 스택에서 pop 하면서 실행하는 것을 의미한다.
실행 컨텍스트 작동에 대해서 아는만큼 설명해주세요.
- 실행 컨텍스트는 크게 3가지 방식으로 생성된다.
- 프로그램이 실행될 때, 함수가 호출될 때, 마지막으로 eval 함수가 호출될 때 생성된다.
- 실행 컨텍스트가 생성되면 자바스크립트 엔진의 콜 스택에 쌓이게 되고, 가장 위에 쌓인 실행 컨텍스트부터 처리하면서 코드를 실행하고 스택에서 제거된다.
- 실행 컨텍스트는 ES6 기준으로 렉시컬 환경(Lexical Environment)과 변수 환경(Variable Environment)로 구성되어 있다.
렉시컬 환경
- 렉시컬 환경은 외부 참조 환경과 환경 레코드로 구성되어 있다.
- 외부 참조 환경은 자바스크립트의 스코프 체인과 연관된 개념으로, 상위 실행 컨텍스트의 렉시컬 환경에 연결되어 있다.
- 환경 레코드는 값들을 저장할 때 필요하며, 식별자 바인딩을 하는 선언적 환경 레코드와 with문 같은 특수한 상황에서 사용되는 객체 환경 레코드로 구성되어 있다.
변수 환경
- 변수 환경은 렉시컬 환경과 같이 외부 참조 환경과 환경 레코드로 구성되어 있다.
실행 컨텍스트에서 렉시컬 환경과 변수 환경이 나눠진 이유를 알고 있나요?
- 기존의 함수 레벨 스코프를 가지는 var 변수들은 변수 환경에 저장된다.
- 하지만 ES6 이후 실행 컨텍스트는 내부에서 일어나는 함수 선언, 블록문, try/catch 문과 같은 곳에서 새로운 렉시컬 환경이 생성된다.
- 그래서 새로운 블록 안에서 블록 레벨 스코프를 가지는 let, const 변수를 사용하면 렉시컬 환경이 여러개 생길 수 있기 때문에 분리했다고 생각한다.
실행 컨텍스트 작동 방식에 대해서 설명해주세요.
- 실행 컨텍스트 작동 방식은 크게 생성 단계와 실행 단계로 구분할 수 있다.
- 생성 단계에서는 this가 바인딩되고 스코프 체인을 위한 외부 환경 참조를 결정한다.
그리고 이 과정에서 렉시컬 환경과 변수 환경의 환경 레코드에 식별자 정보를 수집한다. - 이때 환경 레코드에 변수와 같은 식별자들이 실행 단계 이전에 수집되기 때문에, 실제 선언문 이전에 변수에 접근하거나 함수를 호출할 수 있는 호이스팅이 발생한다.
- 이후 실행 단계에서 자바스크립트 엔진이 실행 컨텍스트를 기반으로 실제 코드를 실행한다.
클로저
클로저에 대해 설명해주세요.
- 클로저는 렉시컬 스코프 밖에서 실행되더라도, 렉시컬 환경을 기억하고 참조할 수 있는 함수다.
- 함수 내부에서 사용하는 변수나 함수들은 함수가 선언된 시점의 스코프에서 결정되는데, 이것을 렉시컬 스코프라고 한다.
- 일반적인 함수는 실행이 종료되면 더이상 참조할 수 없는데, 클로저는 렉시컬 환경을 기억하고 참조할 수 있다.
클로저의 장점을 설명해주세요.
- 클로저를 사용하면 클로저가 어떤 함수 내부의 어떤 값들을 참조하고 있을 경우, 해당 함수의 생명주기가 종료되더라도 렉시컬 환경을 통해 참조할 수 있다. 이를 통해 은닉화와 캡슐화 효과를 얻을 수 있다.
- 함수 내부 변수를 접근하는 클로저를 생성하면, 그 함수는 클로저를 통해서가 아니면 더이상 내부에 접근할 수 없기 때문이다.
클로저는 단점이 없나요?
- 일반적인 함수는 실행이 종료되면 가비지 컬렉션에 의해 메모리에서 해제된다.
- 하지만 클로저를 사용하면 사용이 종료된 함수 내부에 렉시컬 환경에 의해 참조가 가능하므로 메모리에서 해제할 수 없기 때문에 메모리를 사용하게 된다.
클로저를 예를 들어줄 수 있나요? (클로저를 사용해본 경험이 있나요?)
- 커링 기법을 자바스크립트로 구현할 때 클로저를 사용한다.
- 자바스크립트 라이브러리로 리액트를 주로 사용하는데, 리액트의 setState와 같은 훅을 클로저를 통해 구현할 수 있다.
실제 코드를 작성해보세요.
// 변환 전
function sum(x, y) {
return x + y;
}
// 변환 후
function sum(a) {
return (b) => {
return a + b;
};
}
const add2 = sum(2); // add2는 sum(a)에 2를 인수로 넣은 함수가 된다.
const five = add2(3); // 2+3
- sum 함수의 파라미터가 2개인 것을 커링 기법을 적용해서 파라미터를 분리했다.
- 클로저의 특성으로 sum이 반환하는 함수는 a 값을 기억하게 되고, add2 함수와 같이 사용할 수 있다.
const MyReact = (function () {
let _state;
function useState(initialValue) {
const state = _state || initialValue;
const setState = (newState) => {
_state = newState;
};
return [state, setState];
}
return { useState };
})();
- 리액트의 state 는 초기값을 지정한 이후에 다시 렌더링이 되서 함수가 호출되더라도 초기값으로 초기화되지 않는다.
그리고 useState로 반환된 state를 직접 변경해도 실제 상태는 변하지 않는다. - 이걸 _state 값을 두고, useState 를 클로저로 선언해서 구현할 수 있다.
클래스
자바스크립트에서 객체지향 프로그래밍을 어떻게 구현하나요?
- ES5의 생성자 함수를 이용하거나, 프로토타입 메소드를 사용해서 구현할 수 있다.
- ES6에서 추가된 클래스 문법을 이용해도 마찬가지로 객체지향 프로그래밍을 구현할 수 있다.
클래스 문법의 특징이 있나요?
- 클래스로 선언하면 new 키워드 없이 호출할 수 없다.
- 그리고 ES6에서 extends, super 와 같이 클래스 기반 객체지향 언어에 친숙한 키워드가 추가되었다.
- 클래스 내부는 암묵적으로 Strict mode가 적용된다.
- 또한 클래스는 let, const와 같이 블록레벨 스코프를 가지며, 호이스팅이 발생하지 않는 것 처럼 작동한다.
- 마지막으로 클래스 내부에 선언된 프로퍼티들은 Enumerable이 false인 열거되지 않는 값으로 간주한다.
클래스에는 어떤 값들이 포함되어 있나요?
- 클래스 내부에는 크게 생성자인 constructor와 프로토타입 메소드, 정적 메소드가 포함되어 있다.
- ES2022 이후로는 클래스 내부에 변수도 선언할 수 있다.
스프레드 문법
스프레드 문법에 대해 설명해주세요.
- ES6에 추가된 문법으로, 배열이나 문자열같이 나열할 수 있는 여러 값들을 하나 하나의 개별 값으로 분해하는 문법이다.
- 값 앞에 점 세개를 붙여서 사용한다.
스프레드 문법은 어디에 적용할 수 있나요?
- 불변성을 지켜야하는 배열과 객체를 얕은복사할 때 사용할 수 있다.
- 스프레드 문법은 대상을 순회할 수 있는 iterable에 적용할 수 있다.
- 예를 들어 배열, 문자열, Map, Set, DOM 컬렉션 등이 있다.
- 혹은 객체 리터럴 내부에서는 Object에도 적용할 수 있다.
구조 분해 할당
구조 분해 할당을 설명해주세요.
- 구조 분해 할당은 값을 해체해서 해체된 값을 개별 변수에 담을 수 있게 하는 문법이다.
- 주로 배열이나 객체의 값들을 각 변수에 나눠서 할당할 때 사용한다.
구조 분해 할당을 적용한 예시가 있을까요?
- React Query를 사용한 함수를 별도로 생성해서 사용하고자 하는 컴포넌트에서 값을 가져올 때 사용했다.
// react-query를 사용한 함수
export const useGetRecentPosts = () => {
const { data, isLoading, error, refetch } = useQuery(
['recentPosts'],
async () => {
return await axios
.get('/api/posts/recent')
.then((res) => {
return res.data;
})
.catch((error) => {
console.error(error);
});
},
{
staleTime: Infinity,
refetchInterval: 60 * 1000,
},
);
...
return { recentPosts, isLoading, error, refetch };
};
// 사용하고자 하는 컴포넌트에서 구조분해할당
const { recentPosts, isLoading, error, refetch } = useGetRecentPosts();
- 서버로 POST 요청 시 객체 데이터를 보내면, 서버에서 req.body의 값을 구조분해할당해서 사용할 수 있다.
const { postId, postContent, postLike, postUser } = req.body;
Reference
반응형