📌 브라우저 내부에서 동작하는 앱: 사용하는 동안 페이지를 전환하지 않음
📌 웹 사이트의 페이지를 동적으로 바꿔가며 표현하는 것
→ 이벤트가 발생하거나 url이 변경되는 경우 브라우저 내부의 일부 리소스만 변경됨
📌 e.g. Gmail, Google Maps etc.
CSR
방식을 사용하는 경우 클라이언트 측 쿠키 외에 사용자에 대한 정보를 저장할 공간이 마땅치 않다.스크립트 코드
를 삽입하여 개발자가 고려하지 않은 기능이 작동하게 할 수 있음(XSS: Cross-Site Scripting)vs MPA(Multiple Page Application)
- 사용자가 새로운 url 요청하는 경우 그 url에 해당하는 리소스를 서버에 요청, 서버의 응답을 통해 페이지를 렌더링하는 방식(전통적인 방식)
- 페이지의 전체 리소스를 요청하므로 새로고침이 발생
+
검색 엔진 최적화를 하기 적절하고 쉽다.
+
최초 페이지 로딩이 빠르다(서버에서 렌더링하여 가져오기 때문)
-
매 페이지 요청마다 새로고침이 발생한다.
-
페이지 이동 시 중복된(불필요한) 리소스도 로딩된다.
-
모바일 앱 개발 시 추가적인 백엔드 작업이 필요하여 개발이 복잡해질 수 있다.
📌 SSR(Server Side Rendering)
: 모든 템플릿은 서버 연산을 통해 렌더링하고 완성된 페이지 형태로 응답한다.
: MPA에서 주로 사용한다.
📌 CSR(Client Side Rendering)
: 최초에 한 번 서버에서 전체 페이지를 로딩, 이후 사용자의 요청이 올 때마다 리소스를 서버에서 제공하면 클라이언트가 해석하고 렌더링하는 방식이다.
Reflected XXS
: url 파라미터에 ㅅ크립트 코드를 넣어 서버를 거치지 않고 스크립트를 생성하는 방식
: 일반적으로 브라우저 자체에서 차단되는 경우가 많다.
Stored XXS
: 사이트 내 게시글의 댓글, 닉네임 등에 스크립트 코드를 작성하여 서버에 저장함으로써 해당 게시글을 열람한 사용자를 공격하는 방식
📌 쿠키 정보, 세션 ID 등을 획득할 수 있는 스크립트 코드를 작동시킴으로서 공격자는 특정 사용자의 쿠키, 세션 ID를 획득할 수 있다. 이를 통해 공격자는 정상 사용자처럼 사이트에서 활동할 수도 있다.
📌 시스템을 통제할 수 있는 악성 데이터를 스크립트 코드에 포함시킴으로써 사용자의 시스템을 통제할 수 있다.
📌 악성코드를 다운로드하는 url로 사용자를 리다이렉트 시킬 수 있다.
📌 거짓 페이지를 노출시킬 수 있다. <img>
태그를 사용해 원래 페이지와 전혀 관련 없는 페이지를 표시할 수 있으며 이를 통한 개인정보 유출의 위험이 있다.
스크립트 코드에 대한 문자열을 필터링하는 방식으로 방지 가능
필터 제작
: XXS를 유발하는 스크립트 코드 input을 문자열로 받아들일 수 있도록 모든 특수문자를 이용해 HTML 엔티티로 필터링한다(replace()). HTML Entities
BBCode 사용
: 만들기 편하고 안정성도 높은 편이다. BBCode
쿠키 보안 옵션
: 쿠키 생성 시 보안쿠키 파라미터를 지정하면 TLS(Transport Layer Security, 전승 계층 보안) 상에서만 사용 가능하다.
콘텐츠 보안 정책(CSP) 사용
: 스크립트 실행 정책을 설정하여 예방하는 방법으로 출처가 자기 서버인 스크립트만 실행될 수 있도록 한다.
📌 History API 사용
📌 pushState()
메소드로 url을 History 객체에 넣고 url에 해당하는 리소스로 화면 렌더링
📌 PATH로 바로 접근하려면 배열보다는 객체
const routes = {
"/": Home,
"/about": About,
};
// 객체 안에 객체 넣어도 될 듯?
// App.js
...
const routes = [
{ path: '/', view: homePage, title: 'Home' },
{ path: '/settings', view: settings, title: 'Settings' },
];
this.render = () => {
const results = routes.map((route) => {
return {
route,
result: location.pathname.match(pathToRegex(route.path))
};
});
let match = results.find((route) => route.result !== null);
};
...
📌 우리 팀은 class로 구현
// 기본적인 componet 틀
export default class Component {
$target;
props;
state;
constructor($target, props) {
this.$target = $target;
this.props = props;
this.setup();
}
setup() {
this.setEvent();
this.render();
}
mounted() {}
template() {
return ``;
}
render() {
this.$target.innerHTML = this.template();
this.mounted();
}
setEvent() {}
addEvent(eventType, selector, callback) {
const children = [...this.$target.querySelectorAll(selector)];
this.$target.addEventListener(eventType, (event) => {
if (!event.target.closest(selector)) return false;
event.stopImmediatePropagation();
callback(event);
});
}
}
🌱 setup
: 필요한 변수 설정, 소켓 연결 등
🌱 setEvent
: 이벤트 리스너 관련 설정
🌱 render
: 페이지 렌더링
🌱 template
: 페이지 기본 틀 생성
🌱 mounted
: template
를 기준으로 각종 컴포넌트 생성
🌱 addEvent
: 이벤트 리스너 추가
⚠️ 컴포넌트 생성 시 생성자만 호출하면 된다 ⚠️
변수에 할당 시 [object Object]가 화면에 출력되는 이슈가 있음! router 구현 다 했는데 이거 때문에 며칠동안 고생함..
XXS 1
XXS 2
VanillaJS로 SPA 구현 1
VanillaJS로 SPA 구현 2
VanillaJS로 SPA 구현 (1+2)
VanillaJS로 SPA 구현 3