라이브러리와 프레임워크의 차이
- 라이브러리는 특정 목적을 수행하기 위해 개발됨. 제어권이 개발자에게 있음
- 프레임워크는 건축 설계도를 그릴 수 있는 도구와 같은 목적으로 개발됨. 제어권이 개발자에게 있지 않으며, 프레임워크의 규칙을 따라야 함.
리액트를 선언형 프로그래밍이라 하는 이유는?
선언형
어떤 로직으로 어떻게 코드를 짜야 페이지가 그려질 수 있는지에 대하여 생각하기 보다, 컴포넌트나 데이터의 배치를 통해 무엇이 렌더링될지에 대해 고려하는 것이 선언형 ui의 특징이다.
React 공식 홈페이지
React는 상호작용이 많은 UI를 만들 때 생기는 어려움을 줄여줍니다. 애플리케이션의 각 상태에 대한 간단한 뷰만 설계하세요. 그럼 React는 데이터가 변경됨에 따라 적절한 컴포넌트만 효율적으로 갱신하고 렌더링합니다.
예를 들어, React에서는 UI의 특정 부분이 어떻게 보여질지를 선언적으로 정의하고, 데이터의 변화에 따라 UI가 어떻게 업데이트될지를 프레임워크가 알아서 처리합니다. 이는 개발자가 복잡한 DOM 조작이나 상태 관리 로직에 대해 직접적으로 신경 쓰지 않아도 되게 만들어, 개발의 효율성을 크게 향상시킵니다.
왜냐하면 React와 같은 선언형 프로그래밍 모델을 사용함으로써, 개발자는 애플리케이션의 상태 관리와 UI 렌더링 사이의 복잡성을 크게 줄일 수 있기 때문입니다. 이는 코드의 가독성과 유지보수성을 높이는 데 크게 기여하며, 개발 과정에서 발생할 수 있는 오류의 가능성을 줄여줍니다.
또한, 선언형 프로그래밍은 코드의 재사용성을 높여주는 장점도 가지고 있습니다. 고차 함수와 같은 선언형 프로그래밍의 기법들을 활용하면, 일반적인 로직을 추상화하여 여러 곳에서 재사용할 수 있는 컴포넌트나 함수를 쉽게 만들 수 있습니다.
이처럼 선언형 프로그래밍은 자바스크립트와 같은 동적 언어에서 뿐만 아니라, 전반적인 소프트웨어 개발 과정에서 코드의 품질과 개발자의 생산성을 향상시키는 데 중요한 역할을 합니다. 따라서 현대 웹 개발에서 선언형 프로그래밍의 원리와 기법을 이해하고 적용하는 것은 매우 중요한 과제입니다.
명령형
선언형 프로그래밍과 대비되는 개념으로 코드가 어떻게 동작해야하는지를 작성한다.
어떤 방법으로 개발할 것인지를 결정하는 것이 아니라, 무엇이 나타나야 하는지를 묘사해야 하는 차이를 가진다.
HOW와 WHAT의 차이다
리액트 라이프사이클에 대하여
- 생성될 때 (마운팅) : DOM이 생성되고 웹 브라우저 상에서 나타나는 것. 초기 class형에서는 state를 초기에 지정할 때 생성자 함수를 사용해야 했지만 hook에서는 useState를 사용하여 추기상태 설정 가능
- 업데이트 될 때 (업데이팅)React 컴포넌트에 변화가 있을 때를 업데이트(update) 라
- 제거할 때 (언마운팅) 언마운트(unmount) 는 마운트의 반대 과정으로, 컴포넌트를 DOM에서 제거하는 과정
이러한 생명주기 함수는 클래스형 컴포넌트에서만 사용할 수 있으며, 함수형 컴포넌트에서는 사용할 수 없다. 대신 함수형 컴포넌트에서는 Hooks를 통해 유사한 기능을 활용할 수 있다.
마운팅 이벤트
- 리액트 엘리먼트를 DOM 노드에 추가할 떄 발생하며, 한 번만 실행된다.
- 컴포넌트가 시작될 때 가장 먼저 context, defaultProps, state를 저장
- componentWillMount() 메소드 호출 : DOM 삽입 전 실행, 마운팅 중이기에 props나 state 접근 가능하나 변경 불가. DOM 접근 불가
- render
- componentDidMount() 호출 : Ajax, setTimeout, setInterval 등 행동
업데이팅 이벤트
- 속성 혹은 상태가 변경될 때 발생 → 여러번
- componentWillReceiveProps : props를 컴포넌트가 받기 직전 실행. state 업데이트 과정에서는 호출되지 않는다.(props 업데이트떄만 동작)
- shouldComponentUpdate : 업데이트 직전에 호출. 컴포넌트가갱신되는 조건을 정의하여 재렌더링 최소화 가능
- componentWillUpdate : 컴포넌트 갱신 직전에 호출. state를 변경해서는 안된다. (무한루프 발생)
- render()
- componentDidUpdate: 컴포넌트가 갱신된 후 실행된다. render가 완료되어 dom에 접근 가능하다.
언마운팅
- React 엘리먼트를 DOM에서 제거할 때 발생. 한번만 실행
- componentWillUnmount():DOM에서 제거하기 전에 실행되며, 주로 연결했던 이벤트 리스너를 제거하는 등의 여러가지 정리 활동을 한다.
추가
Error : 에러 발생 시 한번만 실행되는 이벤트
getDerivedStateFromProps :props가 바뀌면 그에 따라 state도 바꿔주는 이벤트
state와 props의 차이
- 둘 다 렌더링 결과물에 영향을 주는 정보를 가지고 있다.
state
컴포넌트의 상태를 뜻한다. props와 달리 변할 수 있으며 컴포넌트 내부에서 선언되고 관리되는 특징을 가진다. 일반적으로 컴포넌트의 상태값을 나타내기 위한 것들에서 사용된다.(컴포넌트 안에서 관리)
- 상태에 따라 변화
- 변경이 가능하다.
- state가 변경되면 컴포넌트를 다시 렌더링해야 한다.
- 외부에는 비공개되어 있다.
props
변할 수 없는 값이다. 부모 컴포넌트로부터 props를 받고, 상속받는 컴포넌트 내에서 수정이 불가능하다.(컴포넌트에 전달되는 역할)
- 읽기 전용의 속성을 가진다.
- 부모요소에서 설정된다.
- 초기값과 자료형의 유효성 검사 기능을 가진다.
왜 state는 useState를 통해 변경하는가
- 기본적으로 리액트에서 화면이 업데이트 되게 하기 위해서는 State 혹은 props가 변경되어야 한다. 혹은 부모 컴포넌트가 렌더링되거나 forceUpdate 상황에서 업데이트된다.
- 변수는 변경되어도 자동으로 화면이 바뀌지 않지만, state는 변경되면 자동으로 화면이 바뀌기 때문에 state를 사용한다.
결론 : state를 직접적으로 변경하면 리액트 엔진은 render 함수를 호출하지 않기 때문에 state 변경이 반영되지 않는다. 하지만 useState를 사용하면 render 함수를 통해 화면에 반영할 수 있다. 또한 useState는 비동기적으로 동작하여 여러번의 state 변경을 한번에 처리하여 렌더링 횟수를 줄이고 성능을 향상시키는데 도움이 된다.
왜 리액트는 불변성을 채택하였는가
- state의 변화에 따라 컴포넌트가 리랜더링되는 만큼 불필요한 랜더링이 발생하는 것을 사전에 막고 최적화하기 위해 불변성을 채택
불변성을 유지함으로써 리액트는 애플리케이션의 성능을 크게 향상시킬 수 있습니다. 리액트는 상태가 변경될 때마다 리렌더링을 수행하지만, 불변성을 통해 실제로 변경된 부분만을 파악하고 업데이트할 수 있습니다. 이는 리액트의 가상 돔 알고리즘과 밀접하게 연관되어 있으며, 가상 돔은 이전 상태와 현재 상태를 비교하여 실제 돔에 반영해야 할 최소한의 변경 사항만을 계산합니다. 불변성을 유지하지 않는 경우, 리액트는 상태의 모든 부분을 비교하여 변경 사항을 파악해야 하며, 이는 리렌더링 과정에서의 성능 저하로 이어질 수 있습니다. 따라서, 불변성을 유지함으로써 리액트는 더 효율적인 리렌더링 과정을 수행할 수 있으며, 이는 애플리케이션의 전반적인 성능 향상으로 이어집니다. 왜냐하면 불변성을 통해 리액트는 변경 사항을 더 빠르고 정확하게 파악할 수 있으며, 필요한 부분만을 업데이트할 수 있기 때문입니다.
lazy initializing이란?
- useState에 즉시실행 화살표 함수를 넣은 형태를 말한다.
- useState에 직접 값 대신 함수를 넘기는 방법으로, 초기 값이 복잡한 연산을 포함할 때 사용하는 초기화 방법이다. 이러한 방식을 통해 state가 처음 만들어질 때만 실행될 수 있게 할 수 있다.
JSX란? (내부 동작 방식 위주)
HTML과 JS 로직을 하나의 JS 파일에서 모두 처리할 수 있는 특징을 가진 확장자이며 브라우저에서 동작하기 전에 babel의 트랜스컴파일러를 통해 js 코드 형태로 변환되어 동작한다.
- JSX(Javascript Syntax eXtension)는 Javascript 확장한 문법이다.
- JSX는 리액트로 프로젝트를 개발할 때 사용되므로 공식적인 자바스크립트 문법은 아니다.
- 브라우저에서 실행하기 전에 바벨을 사용하여 일반 자바스크립트 형태의 코드로 변환된다.
1. 부모 요소 하나가 감ㅏ는 형태
- if 구문과 for 루프는 JavaScript 표현식이 아니기 때문에 JSX 내부 자바스크립트 표현식에서는 사용할 수 없다.
- 그렇기 때문에 조건부에 따라 다른 렌더링 시 JSX 주변 코드에서 if문을 사용하거나, {}안에서 삼항 연산자(조건부 연산자)를 사용 한다.
출처: <https://goddaehee.tistory.com/296> [갓대희의 작은공간:티스토리]
- class 대신 className 사용
장점
- 가독성 : JSX는 HTML과 유사한 구문을 사용하기 때문에 UI 코드가 더 읽기 쉽고 이해하기 쉽다.
- 컴포넌트 구조 : JSX는 컴포넌트의 구조를 더 명확하게 표현할 수 있다.
- 변경된 부분만 업데이트 : 리액트에서는 가상 돔을 사용하기 때문에 상태가 변화한 부분에 대해서만 업데이트를 진행하여 불필요한 랜더링을 줄이고 성능을 향상시킬 수 있다.
- 에러 발견 : 컴파일 시점에 HTML 구문 오류를 찾아낼 수 있어 런타임 에러를 줄일 수 있다.
Virtual DOM이란?(재조정 위주)
- 리액트에서는 가상 돔을 사용한다. 가상 돔이란, 실제 DOM(Document Object Model)을 조작하는 방식이 아닌, 실제 DOM을 모방한 가상의 DOM을 구성해 원래 DOM과 비교하여 달라진 부분을 리렌더링 하는 방식으로 작동한다
- 재조정 방식
- UI가 변경을 감지하면 UI를 Virtual DOM으로 렌더링한다. (실제 화면상 렌더링 되는 것이 아닌 비교를 위한 가상 렌더링)
- 현재 Virtual DOM과 이전 Virtual DOM을 비교해 차이를 계산한다.
- 변경된 부분을 실제 DOM에 반영한다.
Context API란?
- 앱에서 컴포넌트에게 props를 사용하지 않고 필요한 데이터(state)를 쉽게 공유할 수 있게 해준다. 따라서 앱의 모든 컴포넌트에서 사용할 수 있는 데이터(state)를 전달할 때 유용하다
- 중간에 여러 컴포넌트를 거쳐야 하거나 앱의 여러 컴포넌트에서 동일한 데이터를 필요로 하는 경우에는 props로만 해결하기에는 불편하다. 계속 props로 넘겨줘야 하는 prop drilling을 해야만 하기 때문이다. 깊이 여부와 무관하게 데이터가 필요한 컴포넌트에서만 불러다가 사용할 수 있는 것이다.
- Context API를 통해 전달하는 데이터의 종류
- 테마 데이터 (다크 모드, 라이트 모드)
- 사용자 데이터 (현재 인증된 사용자)
- 언어 혹은 지역 데이터
- Context API는 자주 업데이트할 필요가 없는 데이터에 사용한다.
- Context API에서 State값을 변경하면, Provider로 감싼 모든 자식 컴포넌트들이 리렌더링되므로 전역 상태 관리를 위한 도구가 아닌, 데이터를 쉽게 전달하고 공유하기 위한 목적으로 사용하는 것이 적합하다.
- 따라서 Context는 리액트에서 컴포넌트를 위한 전역 변수의 개념으로 생각하면 된다.
props 드릴링을 해결하기 위해 사용할 수 있는 방법으로는 어떤 것이 있을까?
- context API 사용 : 데이터를 전역적으로 공유 가능
- redux, zustand와 같은 상태 관리 라이브러리 사용
- custom Hooks로 재사용 가능한 함수로 추상화 가능
- render props 패턴과 children props 패턴
context API나 전역 스테이트 툴을 사용하지 않을 경우 어떻게 props 드릴링을 해결할 수 있을까
- 컴포넌트 구조 재조정
- render props 패턴, Higher Order Components
render props 패턴
컴포넌트의 재사용성을 높이는데 사용된다. 이 패턴은 자식 컴포넌트에게 함수를 전달하고, 이 함수를 통해 부모 컴포넌트의 상태를 사용할 수 있게 한다.
import React, { useState, useEffect } from 'react';
function DataProvider() {
const [data, setData] = useState(null);
useEffect(() => {
// 데이터를 가져오는 로직
setData('Hello, world!');
}, []);
return data;
}
function ChildComponent() {
const data = DataProvider();
return <div>{data}</div>;
}
function ParentComponent() {
return <ChildComponent />;
}
HOC - 컴포넌트를 인자로 받아 새로운 컴포넌트를 반환하는 함수이다. 상태와 로직을 공유할 수 있다.
import React, { useState, useEffect } from 'react';
function withData(WrappedComponent) {
return function EnhancedComponent(props) {
const [data, setData] = useState(null);
useEffect(() => {
// 데이터를 가져오는 로직
setData('Hello, world!');
}, []);
return <WrappedComponent data={data} {...props} />;
}
}
function ChildComponent({ data }) {
return <div>{data}</div>;
}
const EnhancedChildComponent = withData(ChildComponent);
function ParentComponent() {
return <EnhancedChildComponent />;
}
아토믹 디자인 패턴은 나쁜 것일까?
장점:
컴포넌트의 재사용성을 높여 코드의 중복을 줄일 수 있습니다1.
컴포넌트 간의 일관성을 유지하고, UI/UX의 통일성을 높일 수 있습니다1.
디자이너와 개발자 간의 의사소통을 향상시킬 수 있습니다1.
단점:
- 컴포넌트 간의 의존성과 복잡도가 높아질 수 있습니다2.
- 변화가 누적되면서 각각을 구성하는 컴포넌트가 많아질 때는 관리가 어려울 수 있습니다2.
- 충분한 고민 없이 도입한다면 러닝커브가 높아 오히려 의사소통에 장애가 되기 쉽습니다3.
SPA에서 로딩이 느린 단점을 해결할 수 있는 방법.
- 가장 일반적인 방법은 웹팩을 사용하여 코드를 여러 조각으로 나눠 초기 로딩 시 필요한 코드만 로드하고 나머지 코드는 필요 시에 로드하는 방법을 사용하는 것이다. 초기 로딩 속도를 줄여준다.
- lazy loading을 사용하여 특정 모듈이 실ㅈ로 필요할 때 까지 로드를 연기하여 초기 랜더링 속도를 향상시킬 수 있다.
SPA에서 검색 엔진 최적화를 하기 위해서는 무엇을 해야 할까?
- Prerendering
- react-helmet과 같은 프리 랜더링 라이브러리를 사용하여 서버에서 실제 사람인지 봇인지 판단 후 검색 봇에게는 읽을 수 있는 랜더링된 페이지를 전달하는 방법을 통해 검색엔진 최적화를 할 수 있다.
- History API
- pushState메소드는 데이터의 상태, 빈 문자열, 표시할 URL을 인자로 받는다. SPA에서 구분이 필요한 뷰로 진입할 때 마다 pushState 메서드를 통해 주소를 바꿔주면 해당 뷰를 새 콘텐츠가 있는 페이지로 인식한 검색엔진 봇이 페이지를 크롤링하고 색인을 생성한다.
라이브러리와 프레임워크의 차이는?
- 라이브러리는 특정 기능만 수행하도록 제작된 코드의 집합. 호출 시점을 개발자가 제어할 수 있다.
- 프레임워크는 개발을 단순화하기 위한 뼈대나 구성을 제공해주는 코드의 집합. 개발 시작과 기능 구현에 대한 것들은 프레임워크에서 정해놓은대로 따라야 한다.
라이브러리는 개발 주도권이 개발자에게 있고, 프레임워크는 개발 주도권이 프레임워크에 있다.
SPA, MPA, CSR, SSR
SPA vs MPA와 SSR vs CSR 장단점 뜻정리 - 하나몬
MPA
- 여러 페이지로 구성된 웹 애플리케이션
- 전통적인 방식(JSP, PHP)
- 페이지 이동 시 서버로부터 HTML 파일을 전달받아 다시 랜더링하며 새로고침 시 깜빡임 발생
- 페이지 로드에 오래 걸림
- SPA에 비해 SEO 유리 (각각의 페이지별로 키워드에 맞는 meta tag 추가 가능)
SPA
- 단일 페이지 웹 애플리케이션.
- React, vue, Angular에서 사용
- 최초 한번 HTML 파일 전달 후 모든 동작은 Ajax를 통해 업데이트
- 새로고침이 일어나지 않고, 부드러운 이동 가능
- 적은 서버 리소스
- 서버 요청을 최초에 한 번만 하고 이후에는 데이터 업데이트 위주로 하기 때문에 오프라인 상황에서도웹 페이지 이용 가능
- SEO에 취약.
- HTML 단일 파일이기 때문에 meta tag 추가가 어려움
CSR
- HTML 랜더링이 클라이언트에서 실행되는 방식
- 웹사이트 방문 → HTML,CSS,JS 요청 → 서버에서 빈 html과 js 전송 → 브라우저에서 app.js 파일 파싱
- 랜더링이 완료될 때 까지 빈 화면이 보이기 때문에 TTV가 길다. (TIme To View)
- 후속 로딩이 빠르다.
- 서버의 부하를 분산시킬 수 있다.
SSR
- HTML 랜다링이 서버에서 실행된다.
- 서버는 랜더링된 HTML을 브라우저로 전달한다. 하지만 js는 랜더링 전이라 사용자가 이벤트를 동작할 수 없다.
- TTV는 빠르지만 TTI(Time To Interact)의 간격이 크다
- SEO에 유리하다.(완성된 HTML 파일 전달)
SSG(Static Site Generation)
- 정적 사이트 생성, 사전 랜더링
- SSR과 다르게 빌드 시점에 랜더링. → 만들어놓은 HTML 문서를 응답한다.
- 응답 속도가 빠르고 서버 부하가 적다.
- CDN에 캐싱될 수 있어 요청에 응답 속도는 빠르다.
- 빌드 시점에 랜더링하기 때문에 번들 규모가 커질수록 빌드 시간이 오래 걸린다.
- 자주 업데이트 되는 페이지라면 매번 빌드를 다시 해줘야 하기 때문에 정적 페이지에 주로 사용된다.
- SEO 에 유리하다.
여기서, SPA와 CSR은 같을까?
SPA, MPA는 페이지를 하나만 쓰는지, 여러개 쓰는지의 차이이고 CSR, SSR은 렌더링을 어디서 하냐의 차이로 비교 대상이 아니다.
SPA에서는 첫 페이지만 받아오고 이후에 데이터의 수정,조회를 하고 싶기 때문에 CSR이란 방식을 채택한 것이다.
반대로 MPA는 동적이지 않은 페이지를 상황에 맞게 클라이언트에 뿌려주기 때문에 SSR이란 방식을 채택한 것이다.
CSR, SSR
- 동적 컨텐트가 많다면 CSR이 더 유리
- 정적 컨텐츠가 많다면 서버에서 연산해서 보내주는 SSR이 속도면에서 유리
- CSR을 쓴다고 검색에 노출이 안되는건 아니다. 구글은 자바스크립트를 지원하기 때문에 괜찮지만, 네이버와 같은 경우 지원이 안된다. 또 CSR을 색적하여 페이지 우선순위를 매기는 것은 비용이 많이 들어 SSR을 권장한다.
리액트에서 인라인 스타일을 사용하면 안되는 이유
- 재사용 불가
- 퍼포먼스 적으로 인라인 스타일은 html 파일을 무겁게 하여 페이지 랜더링 시에 느린 속도를 보일 수 있으며, 브라우저에서 스타일시트를 캐싱할 수 없다. 때문에 매번 페이지를 로드할 때 마다 해당 스타일을 요청해야 한다.
- 접근성 이슈 : html 시멘틱 구조에 영향을 미쳐 검색 엔진 최적화나 sreen reader 등 해당 요소와 연관된 것들에 대힌 성능에 영향을 미칠 수 있다.
번들러란?
- 파일 병합 - 자바스크립트 파일을 하나 또는 소수의 파일로 합친다. 이로 인해 브라우저에서는 각 파일 별로 요청을 보내지 않고 한 번의 요청으로 모든 파일을 받을 수 있다.
- 의존성 관리: 각 모듈 간의 의존성 관계를 파악하여 올바른 순서로 모듈이 불러와지고 실행하는 것을 보장
- 트랜스파일링 - es6 기반의 코드를 하위 버전 또는 CJS 기반으로 변환하여 호환성을 지원
'개발 > 스터디' 카테고리의 다른 글
Docker와 Standalone (0) | 2024.08.10 |
---|---|
AWS EC2와 ECS 그리고 Docker (0) | 2024.07.29 |
기술 면접 준비 - Typescript 기본 (4) (0) | 2024.06.24 |
기술 면접 준비 - HTML, CSS 기본 (3) (0) | 2024.06.23 |
기술 면접 준비 - HTTP 기본 (2) (0) | 2024.06.17 |