SPA 정의
- 단일 페이지 애플리케이션(Single Page Applications)
- 웹페이지가 마치 하나인 것처럼 처리하는 웹 어플리케이션으로 사용자와의 상호작용을 통해 동적으로 내용을 업데이트 하는 웹 어플리케이션
- 페이지 리로드 없이 사용자와 실시간으로 상호작용
- 부드러운 페이지 전환과 함께 업데이트 시간을 단축시켜 사용자 경험을 높힐 수 있다.
SPA의 작동 원리
- 초기 전체 페이지를 로드하며 HTML, CSS, JavaScript를 포함
- 사용자와 상호작용에 따라 필요한 데이터만 서버에 요청하고 비동기로 받은 데이터로 페이지의 특정 부분을 동적으로 업데이트
- 페이지는 새로고침 되지 않고 빠르고 부드러운 사용자 상호작용을 제공
전통적인 MPA
- 전통적인 웹사이트 구조
- Multi Page Applications
- 각 페이지가 별도의 HTML 파일로 존재
- 페이지 간 이동 시 전체 페이지를 새로 로드하기 때문에 리로드에 따른 사용자 경험이 좋지 않다.
SPA vs MPA
| SPA | MPA |
|---|
| 페이지 전환 없이 동적 콘텐츠 업데이트 | 각 페이지 별도의 HTML 파일 |
| 클라이언트 사이드 라우팅(CSR) | 서버 사이드 라우팅(SSR) |
| 초기 로딩 시간이 길 수 있음 | 각 페이지 로드 시 서버 부하 |
| SEO 최적화에 도전적 | SEO 친화적 |
SEO: Search Engine Optimization / 검색 엔진 최적화
SPA와 MPA의 선택은 개발하려는 어플리케이션의 목적, 기능 타겟 사용자의 요구사항 등을 고려하여 선택해야한다.
SPA의 핵심 구성 요소
- 클라이언트 사이드 라우팅
- 브라우저에서 URL변경에 따른 뷰 전환 처리
- 페이지 리로드 없는 빠른 사용자 경험 제공
- History API나 React Router, Vue Router와 같은 - 라이브러리로 구현
- 뷰 렌더링
- 사용자 인터페이스의 동적 업데이트
- 컴포넌트 기반 아키텍쳐로 각 부분을 독립적으로 관리하고 업데이트하게 해준다.
- 상태 관리
- 어플리케이션 상태의 중앙집중식 관리
- 데이터 흐름의 일관성 유지
- recoil, redux 같은 도구들로 상태관리를 쉽고 체계적으로 할 수 있다.
- 데이터 통신
- 서버와의 비동기 통신
- 필요한 데이터만 로드하여 효율적인 데이터 관리
- ajax, fecth API
SPA의 장점
- 사용자 경험 개선
- 빠른 페이지 전환
- 원활한 사용자 인터페이스
- 사례: 대화형 웹 애플리케이션 - 대시보드
- 대량의 데이터를 처리하고 사용자에게 신속한 피드백을 제공해야하는 비지니스에 매우 중요한 장점
- 개발 효율성
- 프론트엔드와 백엔드의 분리 - 개발과정을 간소화
- 재사용 가능한 컴포넌트 - 개발 시간, 노력 최소화
- 사례: 모듈화된 컴포넌트의 재사용
SPA의 한계
- 초기 로딩시간
- 최적화 전략: 코드 스플리팅(필요한 부분만 불러온다), 레이지 로딩(필요하지 않은 리소스를 초기로딩에서 제외하고, 필요할때만 불러온다)
- SEO 최적화 문제
- 클라이언트 사이드 렌더링으로 인해 검색엔진에 노출되기 어렵다 - 자바스크립트를 실행시키지 않거나 제한적으로 실행시키기 때문
- 해결 전략: 서버 사이드 렌더링(SSR ex-next.js), 프리렌더링
- 브라우저 호환성 문제
- 대응 전략: 폴리필(구형 브라우저에서 지원되지 않는 최신 기능을 지원), Babel과 같은 트랜스파일러 사용
면접 질문 대비
- SPA란 무엇인가요?
- 하나의 페이지로 이루어진 웹애플리케이션이며 사용자와의 상호작용에 따라 필요한 부분만 동적으로 업데이트 한다.
- 전체 페이지를 새로 로드하는 대신에 필요한 데이터만 서버로부터 비동기적으로 받아 현재 페이지를 업데이트한다.
- 빠른사용자 경험을 제공하고 서버 부하를 줄이는데 도움이 된다.
- SPA의 주요 장점은 무엇인가요?
- 빠른 사용자경험
- 개선된 클라이언트 사이드 포퍼먼스
- 개발 과정에서의 효율성
- 사용자는 페이지 전환 없이 애플리케이션 내에서 원활하게 상호작용 할 수 있고, 브라우저에 불필요한 리로드를 감소
프런트/백이 분리되어있어 개발과 유지보수 용이
- SPA에서 초기 로딩 시간이 긴 이유는 무엇인가요?
- 시작될때 필요한 모든 자바스크립트 css를 한번에 로드하기 때문, 전체 로직과 리소스를 브라우저에서 로드해야하기 때문인데 이는 코드 스플리팅을 사용하여 최적화 할 수 있다.
- SPA의 SEO 최적화에 어려움이 있는데 그 이유와 해결법은?
- 클라이언트 사이드 렌더링이기 때문. 검색엔진 크롤러가 spa 컨텐츠를 크롤링하거나 인덱싱하기 어려운데, 검색엔진은 전통적인 정적인 html 컨텐츠를 크롤링하는데 SPA는 자바스크립트가 실행된 후에 컨텐츠가 생성되기 때문
- 해결은 서버사이드렌더링 - 서버에서 초기 페이지 렌더링하고 클라이언트로 보냄, 프리렌더링 - 빌드 시간에 정적HTML파일을 생성하게 됨 을 사용하는 것이고
- SPA에서 라우팅은 어떻게 작동하나요?
- 전통적인 웹 애플리케이션에서는 사용자가 다른 페이지로 이동할 때 마다 서버에 요청을 하고 요청한 페이지가 로드가 되서 클라이언트에 로드 됨.
- SPA에서는 URL변경이 발생해도 실제로 새 페이지를 로드하는 대신에 자바스크립트를 이용해서 현재 페이지를 동적으로 변경
ex)react router, vue router
- 필요한 컴포넌트만 렌더링
CSR과 SSR의 차이
- 클라이언트 사이드 렌더링(CSR)은 브라우저가 서버로부터 데이터를 받아 클라이언트 측에서 HTML을 생성하는 방식. SPA 대부분은 이 방식을 사용.
- 서버 사이드 렌더링(SSR)은 서버에서 모든 HTML을 생성하고 완성된 페이지를 클라이언트에 보내는 방식. 초기 로딩속도가 빠르고 SEO(검색엔진최적화)에 유리하지만, 페이지간 이동 시 전체 페이지를 다시 로드해야함.
- CSR은 사용자 상호작용이 많고 동적인 웹 애플리케이션에 적합, SSR은 콘텐츠 중심의 웹사이트에 적합.
SPA의 라이프 사이클 관리
- 애플리케이션은 초기화, 뷰 렌더링, 이벤트 처리, 데이터 업데이트 등 여러 단계를 거침.
- 상태 관리는 사용자 인터페이스(UI)의 일관성을 유지하고 복잡한 데이터 흐름을 관리하는 데 중요함. FE 프레임워크 (React, Angular, Vue)는 이러한 과정을 용이하게 함.
- 라이프 사이클 관리는 애플리케이션의 성능 최적화와 사용자 경험 향상에 중요한 역할을 함. 예를 들어, 불필요한 데이터 로딩을 피하고, 사용자 상호작용에 빠르게 반응하는 등의 방법이 있음.
초기화 단계 (Initialization)
- 자바스크립트, CSS, 첫번째 뷰가 렌더링됨
- 사용자 인터페이스 구조, 라우팅 설정, 상태관리 시스템 설정되는 단계
뷰 렌더링 단계(View Rendering)
- 사용자의 상호작용이나 라우팅 변경에 따라 새로운 뷰가 생성되고 기존 뷰가 업데이트 된다. 동적인 사용자 인터페이스를 구현하기 위해 가상 DOM을 사용하는 등의 기술이 활용됨
- ex) 사용자가 링크를 클릭할때 새로운 컴포넌트가 렌더링 되면서 페이지의 내용이 변경됨
이벤트 처리 단계(Event Handling)
- 사용자 입력, 클릭, 스크롤등의 입력을 처리. 이벤트핸들러 데이터 바인딩을 통해서 사용자 인터페이스의 상태를 업데이트
- ex) 폼에 데이터를 입력, 버튼을 클릭하는 단계
데이터 업데이트 단계(Data Update)
- 서버에서 새로운 데이터를 요청하거나 애플리케이션 상태를 변경
- 반응성을 보장하는 핵심적인 부분
- 사용자 인터페이스와 데이터의 동기화를 관리
- ex) 사용자가 입력한 정보를 바탕으로 서버에서 데이터를 가져오거나, 어플리케이션의 상태를 업데이트하는 등
종료 단계(Termination)
- 메모리 해제, 이벤트리스너 제거, 진행중인 요청 취소 등의 정리 작업
- 오작동, 불필요한 예외처리를 안하기 위해서 필요
SPA 성능 최적화 전략
- Code splitting과 lazy-loading
- Javascript와 CSS최적화
- 브라우저 캐싱 및 네트워크 최적화 기법
- Code splitting(코드 분할)
- 애플리케이션의 번들을 여러 청크(작은 단위)로 나누어 필요할 때만 로드하는 것을 의미
- SPA에서 모든 코드를 한 번에 로드하면 초기 로딩 시간이 길어질 수 있다.
- CodeSplitting을 사용하면 사용자가 실제로 필요로 하는 기능의 코드만 로드하여 초기 로딩 시간을 줄일 수 있다.

lazy, suspense 함수의 사용이 핵심인데, lazy함수를 사용해서 lazy컴포넌트를 동적으로 불러오게 되고, 이 함수는 다른 컴포넌트를 반환하는 함수를 인자로 받고 컴포넌트를 필요할때 까지 로드하지 않는다.
준비가 되지 않으면 suspense의 fallback으로 로딩중이라는 화면을 출력하고, 준비가 되면 LazyComponent가 출력되는것이다. 초기 로딩시에 필요하지 않은 코드를 로드할 필요가 없어져서 초기 로딩시간을 상당히 줄일 수 있고 네트워크 사용량을 최소화 할 수 있다.
- Lazy Loading
- 사용자에게 필요할 때까지 자원(이미지, 커포넌트 등)을 로드하지 않는 기술.
- 불필요한 자원 로드를 방지하고, 초기 성능을 개선하는 데 도움을 준다.

- 초기상태는 null로 이미지가 로드되기전 까지 아무것도 렌더하지 않는다. useEffect는 컴포넌트가 마운트될때 실행된다.
- 필요할때 까지 기다리는게 핵심인데, 사용자가 이미지를 볼 필요가 있을때까지 실제 이미지를 로드하지 않는다.
다른 방법

- 컴포넌트가 사용자의 뷰포트에 노출될때 이미지를 로드하는 방식
IntersectionObserver를 이용하는 방식인데 useState를 통해 이미지가 로드되었는지 상태를 관리하고 useRef를 통해서 DOM요소에 대한 참조를 생성한다.
- useEffect 내부에서는 IntersectionObserver가 생성되고 특정요소가 뷰포트에 들어오는지 그 여부를 감지하게 된다.
메모리 누수방지를 하기위해 컴포넌트가 언마운트될때는 useEffect의 리턴에서 disconnect해준다.
- 이미지를 많이 다루는 프로젝트에 용이함(ex 무한스크롤)
JavaScript 최적화
- 코드 최소화(Minification)
- 코드 최소화는 Javascript 파일을 간결화하여 성능을 향상시키는 기법.
- 코드 내 불필요한 공백, 주석, 그리고 개발자가 읽기 쉽게 만든 형식을 제거한다.
- 파일 크기를 줄여 네트워크 지연 시간을 감소시키고, 웹사이트 로딩 속도를 개선한다.

외부에서 브라우저를 통해 내 코드를 볼 수 없게 할수도 있다.
하지만 개발을 실제로 할때, 이작업은 가독성이 나쁘고 굉장히 비효율적이다. 그래서 빌드도구, 번들러 등을 사용해서 자동화한다. 웹팩으로 최소화 이전 코드를 분석해서 코드 최소화를 자동화 할 수 있다.
- 불필요한 코드 제거(Tree Shaking)
- 실제로 사용되지 않는 코드를 제거하는 기법.
- 최종 번들의 크기를 줄이고, 애플리케이션의 로딩 속도와 성능을 향상시킨다.
- 물론 웹팩으로 이부분도 자동화할 수 있지만 코드를 작성할때 부터 이부분을 유의하는 습관을 들이는것이 중요하다.
- 비동기 로딩(Asynchronous Loading)
- 비동기 로딩은 필요한 시점에 Javascript 파일을 로드하는 방법.
- 애플리케이션의 초기 로딩 시간을 단축시키고, 사용자 경험을 개선.
- 특히 큰 크기의 스크립트 파일이나 라이브러리를 다룰 때 유용.

CSS 최적화
- CSS 최적화는 웹사이트의 로딩 시간을 단축하고, 렌더링 성능을 향상시키는 데 중요하다.
- 특히 대규모 웹사이트에서는 스타일시트의 크기와 복잡성이 성능에 큰 영향을 미친다.

React에서 CSS 모듈은 컴포넌트별로 스타일을 캡슐화하고, 전역 스타일 충돌을 방지하며, CSS 분할 및 코드 스플리팅이 자연스럽게 이루어진다.
- CSS-in-JS의 압축 및 최소화
- CSS-in-JS 라이브러리는 개발 과정에서 편리함을 제공하며, 프로덕션 빌드 시 자동으로 CSS를 압축하고 최소화한다.
- 파일 크기를 줄이고 로딩 속도를 개선하는데 도움이 된다

브라우저 캐싱
- 웹사이트 리소스를 사용자의 로컬 컴퓨터에 저장하여, 재방문 시 빠르게 로드 할 수 있게 하는 기술.
- 네트워크 트래픽을 줄이고, 웹사이트 로딩 속도를 향상시키는 중요한 방법.
- SPA에서 캐싱은 특히 중요. SPA는 자원을 한 번에 로드한 후 필요한 부분만 갱신하므로, 캐싱을 통해 초기 로딩 시간과 네트워크 사용을 크게 줄일 수 있다.
- ”Cache-Control” 헤더는 얼마나 오랫동안 리소스를 캐시할 것인지 브라우저에게 지시 할 수 있다.

리소스를 3600초동안 캐시하고 재방문시 서버에 재검증을 요구
- ETag
- ETag는 서버가 각 리소스에 대해 고유한 식별자를 제공하는 방법이다.
- 브라우저는 ETag를 사용하여 리소스의 변경 여부를 확인하고, 변경되지 않았다면 캐시된 버전을 사용한다.
- 서비스 워커와 캐싱
- 서비스워커는 웹 애플리케이션의 캐싱 전략을 더욱 세밀하게 제어할 수 있게 해주는 자바스크립트 워커.
- 오프라인 경험, 백그라운드 데이터 동기화 등 고급 캐싱 시나리오에 사용됨.

서비스워커를 사용해 요청이 캐시에 존재하면 캐시된 응답을 반환하고, 그렇지 않으면 네트워크에서 요청을 가져오게 된다.
네트워크 최적화
- HTTP 요청 최소화
- HTTP 요청 최소화는 웹페이지의 로딩 속도를 향상시키는 중요한 방법이다.
- 요청이 많아질수록 웹페이지의 로딩시간이 늘어난다.
- 웹팩 등의 모듈 번들러를 이용하여 여러개의 Javascript 또는 CSS 파일을 하나로 합치는 방법 등을 사용할 수 있다.
- CDN 사용
- CDN(Content Delivery Network)은 전 세계에 분산된 서버 네트워크를 이용하여 사용자에게 컨텐츠를 빠르게 제공하는 기술.
- CDN을 이용하면 사용자와 가까운 서버에서 컨텐츠를 제공받을 수 있어, 웹페이지의 로딩 속도를 향상시킬 수 있다.
- 이미지, 비디오 최적화
- 이미지 및 비디오는 웹페이지의 데이터 대부분을 차지하므로, 이들을 최적화하는 것이 중요하다.
- 적절한 이미지 포맷의 선택, 이미지 및 비디오의 압축, 레이지로딩(lazy-loading) 등의 방법을 통해 이미지 및 비디오를 최적화 할 수 있다.
보안 고려사항
- XSS
-
XXS(Cross-Site Scripting)는 공격자가 웹사이트에 악의적인 스크립트를 삽입하고, 다른 사용자의 브라우저에서 그 스크립트가 실행되게 만드는 공격
-
사용자의 세션 토큰, 쿠키, 개인 정보 등을 탈취할 수 있다.
-
댓글이나 메시지 기능이 있는 웹사이트에서 사용자의 입력을 필터링하지 않고 직접 페이지에 출력할 경우 악의적인 스크립트가 삽입 될 수 있다.
-
XSS 방지를 위해선 사용자 입력을 적절히 이스케이프 처리하는 것이 중요.
예시
-
사용자가 입력한 텍스트에서 HTML 태그를 무력화.
-
콘텐츠 보안 정책(CSP)을 설정하여, 신뢰할 수 있는 스크립트 소스만 실행되도록 한다.

- CSRF
-
CSRF(Cross-Site Request Forgery)는 공격자가 사용자의 브라우저를 이용하여 사용자가 의도하지 않은 요청을 보내게 하는 공격이다.
-
사용자가 로그인한 상태에서 이루어지며, 사용자는 자신도 모르게 중요한 작업을 수행하게 될 수 있다.
-
사용자가 온라인 뱅킹에 로그인한 상태에서, 공격자가 만든 웹사이트에 접속하게 되면, 이 사이트는 사용자를 속여 뱅킹 사이트에 트랜잭션을 요청하게 할 수 있다.
-
CSRF 공격을 방지하기 위해서는 요청에 대한 토큰을 사용하는 것이 효과적이다.
-
서버는 폼을 생성할 때 고유한 토큰을 생성하고, 이 토큰은 폼을 제출할 때 함께 전송되어야 한다. -> 서버는 토큰이 유효한지 검증

- 콘텐츠 보안 정책(CSP) 구현
- CSP(Content Security Policy)는 웹사이트에서 실행될 수 있는 콘텐츠의 출처를 제한하는 보안 정책이다.
- 이 정책은 웹사이트를 XSS와 같은 Cross Site Scripting 공격으로부터 보호하는 데 중요한 역할을 한다.
- 안전한 인증 방식 및 토큰 기반 인증(JWT)
- JWT(JSON Web Token)는 클라이언트와 서버 간에 정보를 안전하게 전송하기 위한 컴팩트하고 JWT 자체적으로 필요한 모든 정보(self-contained)를 포함된 방식입니다.
- JWT는 인증 및 정보 교환에 널리 사용된다.
- JWT는 세 부분으로 구성된다.
- Header(헤더), Payload(페이로드), Signature(서명)
- 상태를 유지하지 않는 stateless 인증방식으로 서버의 부하를 줄일수 있고 자체적으로 필요한 모든 정보를 포함할 수 있어서 인증과정을 간소화 할 수 있다.

- 예시 : JWT는 사용자 로그인 후 서버가 생성하며, 클라이언트는 이후의 요청에 이 토큰을 포함시켜 인증을 유지한다.
HTTPS 사용성의 중요성
- HTTPS란?
- HTTPS(HyperText Transfer Protocol Secure)는 웹 통신 프로토콜인 HTTP에 데이터 암호화를 추가한 버전이다.
- SSL(Secure Sockets Layer)또는 TLS(Transport Layer Security)프로토콜을 사용하여 데이터를 암호화 한다.
- 데이터 보호: HTTPS는 사용자 데이터를 암호화하여 중간자 공격으로부터 보호한다.
- 신뢰성: SSL/TLS 인증서는 웹사이트의 신뢰성을 보장하며, 사용자가 안전한 사이트를 이용하고 있음을 확신시킨다.
- SEO 개선: 검색 엔진은 HTTPS를 사용하는 사이트를 더 높게 평가한다.
접근성 및 사용자 경험(UX)
- 웹 접근성 준수
- 웹 접근성은 모든 사용자가 웹 콘텐츠와 기능에 쉽게 접근할 수 있도록 보장하는 것을 의미한다.
- 이는 웹사이트 사용에 있어 다양한 제약을 가진 사용자들이 쉽게 접근할 수 있도록 보장하는 것을 포함하며, 법적 요구사항을 충족하는 데도 중요하다.
- ARIA의 사용 예시
- Accessible Rich Internet Applications의 약자로 웹 페이지, 특히 동적 콘텐츠, 그리고 Ajax, HTML, JS 및 관련 기술로 개발된 사용자 인터페이스 구성요소의 접근성을 증가시키는 방법에 대해 규정한 W3C가 출판한 기술사양이다
- ARIA는 웹 콘텐츠와 애플리케이션을 더 접근성 높게 만드는 데 도움을 준다.
- ‘aria-label’, ‘role’ 속성을 사용하여 스크린 리더(화면 낭독기) 사용자를 위한 추가 정보 제공한다.

- HTML 구조 최적화
- 적절한 HTML 태그와 구조는 웹 접근성을 향상시키는데 중요하다.
- ‘header’, ‘nav’, ‘main’, ‘footer’와 같은 시맨틱 태그를 사용한다.

User Interaction 및 Interface 설계
- User Interaction은 웹사이트나 애플리케이션의 성공에 중요한 역할을 한다.
- 좋은 인터랙션 디자인은 사용자의 만족도를 높이고, 목표 달성에 기여한다.
- 동적 폼, 대화형 메뉴, 애니메이션 요소등이 있다.
- 인터랙티브 요소는 사용자 경험의 핵심이다.
- 사용자의 행동에 반응하여 변화하는 인터렉티브 그래픽, 사용자 입력에 따라 실시간으로 정보를 제공하는 동적 검색 바 등

반응형 및 모바일 최적화 디자인 – 반응형 웹 디자인의 중요성
- 반응형 웹 디자인은 다양한 디바이스와 화면 크기에 맞추어 콘텐츠를 적절히 조정하는 디자인 접근법.
- 모바일 사용자 증가와 다양한 디바이스 사용에 따른 필수적인 요소.
- CSS 미디어 쿼리는 다양한 화면 크기와 조건에 따라 스타일을 적용할 수 있게 한다.

마무리
고민해보자!
- 개발을 할 때 단순히 러닝코드만을 작성하지 않는가?
- 내가 사용하는 도구와 방식이 왜 사용되는지 이해하고 있는가?
- 내가 사용하는 도구와 방식이 어떤 장단점을 가지고 있는가?
- 나는 웹 표준과 보안에 대해서 잘 준수하고 있는가?
- 사용자 경험을 향상시키기 위한 고민을 하고있는가?