HTML, CSS, JavaScript 등 개발자가 작성한 문서가 브라우저에서 출력되는 과정
DOM 트리 생성, CSSOM 트리 생성
렌더링 트리 생성
레이아웃 단계
페인팅 단계
브라우저에 렌더링
서버 측에서 렌더링을 진행
사용자가 페이지를 요청하면, 서버가 필요한 데이터를 가져와 HTML을 완성한 뒤 클라이언트에 전달, 클라이언트는 전달받은 HTML을 그대로 렌더링
사용자에게 완성된 HTML을 제공하므로 초기 로딩 시간이 짧음
검색 엔진이 완전한 HTML을 인덱싱할 수 있어 SEO에 유리, 수익성 면에서 유리
서버에서 렌더링을 처리하므로 저성능 기기에서도 성능이 좋음
모든 요청에 대해 서버가 HTML을 렌더링해야 하므로 서버에 부하가 큼
클라이언트 측에서 동적 콘텐츠를 처리하는 데 한계가 있을 수 있음
서버와 클라이언트 모두에서 코드를 유지해야 하므로 개발이 복잡해질 수 있음
클라이언트 측에서 렌더링을 진행
초기 HTML 파일은 최소한의 내용만을 포함하며, JavaScript 코드가 클라이언트에 전달된 후 브라우저가 이를 실행하여 필요한 데이터를 서버에서 받아와 페이지를 완성
서버 측의 부하를 줄여줄 수 있음
사용자가 페이지와 상호작용할 때 빠른 반응을 제공
애니메이션, 실시간 업데이트 등 동적 콘텐츠 구현이 쉬워 풍부한 사용자 경험이 가능
컴포넌트 기반 라이브러리(React, Vue 등)를 사용하여 모듈식 개발이 가능해 개발 편의성이 높음
초기 로딩 속도가 느림
JavaScript를 실행하지 않는 검색 엔진에서는 콘텐츠 인덱싱이 어려울 수 있어 SEO에 불리, 수익성 면에서 불리
클라이언트 측에서 모든 렌더링을 처리하므로 저성능 기기에서는 성능이 저하될 수 있음
데이터 패킷이 목적지에 도달하기 위해 어떤 경로를 선택해야 하는지를 결정하는 과정
라우팅은 주로 라우터에서 수행되며, 라우터는 네트워크의 여러 경로 중 최적의 경로를 선택
라우터는 라우팅 테이블을 유지하며, 이 테이블에는 다양한 목적지 네트워크와 그에 도달하는 데 사용할 경로 정보가 포함
다양한 라우팅 프로토콜(OSPF, BGP, RIP 등)이 있으며, 이들은 라우터가 경로를 학습하고 결정하는 데 사용
최적의 경로를 선택하여 네트워크 효율성을 높임
큰 네트워크에서도 효과적으로 작동할 수 있는 확장성이 있음
네트워크 상태에 따라 동적으로 경로를 변경할 수 있음
설정 및 유지 관리가 복잡할 수 있음(특히 큰 네트워크에서)
라우팅 프로토콜은 네트워크 오버헤드를 증가시킬 수 있음
경로 결정 과정에서 약간의 지연이 발생할 수 있음
데이터 패킷을 입력 포트에서 적절한 출력 포트로 전달하는 과정
라우터뿐만 아니라 스위치에서도 수행
포워딩은 라우팅 테이블에 의해 이미 결정된 경로를 따라 패킷을 전달
포워딩 과정은 빠르게 이루어져야 하며, 패킷이 지연 없이 전달되도록 함
패킷을 빠르게 전달하여 네트워크 지연을 최소화함
경로가 이미 결정되어 있으므로 상대적으로 단순한 작업
네트워크의 모든 장치가 패킷 전달을 빠르게 수행하여 전체 네트워크 효율성을 높임
라우팅 테이블에 의존하므로 경로 변경 시 즉각적인 대응이 어려움
네트워크 상태 변화에 대한 적응력이 부족할 수 있음
라우팅 결정에 의존하므로, 잘못된 라우팅 정보가 있을 경우 문제가 발생할 수 있음
라우팅과 포워딩은 함께 작동하여 네트워크가 효율적으로 데이터를 전송할 수 있도록 함
라우팅이 경로를 결정하고, 포워딩이 그 경로를 따라 데이터를 전달하는 역할
하지만 라우팅을 수행할 때마다 항상 포워딩을 해야 하거나 포워딩을 할 때 항상 라우팅 과정이 선행되어야 하는건 아님
두 작업은 서로 독립적으로 발생할 수 있음
app.get("/list", function (req, res) { //router
list(req, res);
});
function list(req, res) { //forward
mydb
.collection("post")
.find()
.toArray()
.then((result) => {
// console.log(result);
res.render("list.ejs", { data: result });
});
}
웹 어플리케이션에서 클라이언트와 서버 간의 상태 정보를 관리하기 위해 클라이언트 측에 저장하는 작은 텍스트 파일
자동 로그인, 장바구니, 최근 검색 리스트와 같은 주로 보안과 직접적으로 연관되지 않는 용도로 사용
const cookieParser = require('cookie-parser'); //쿠키를 다루기 위한 미들웨어 설치
app.use(cookieParser('암호화키'));
app.get('/cookie', (req, res) => { //'/cookie'요청 시 처리할 라우터 생성
let milk = parseInt(req.signedCookies.milk) + 1000; //쿠키(milk) 데이터를 암호화한 상태로 읽어옴, 그냥 읽어올 때는 req.cookies.milk
//'/cookie'로 요청할 때마다 값을 1000씩 증가
if (isNaN(milk)) { //쿠키(milk) 값이 NaN일 시
milk = 0; //쿠키(milk) 값을 0으로 설정
}
res.cookie('milk', milk, { signed: true }); //'milk'라는 키에 'milk'라는 값 형식으로 암호화된 상태로({ signed: true })
//cookie() 함수를 사용해 쿠키 생성 후 res 객체를 통해 브라우저로 전송
res.cookie('name', '홍길동', { signed: true }); //'name'라는 키에 '홍길동'이라는 값 형식으로 암호화된 상태로({ signed: true })
//cookie() 함수를 사용해 쿠키 생성 후 res 객체를 통해 브라우저로 전송
res.send(`product: ` + req.signedCookies.milk + `name: ` + req.signedCookies.name); //브라우저에서 서버로 재요청 시 req 요청 객체를 통해
}); //쿠키 정보를 읽은 후 다시 브라우저로 전송

서버에서 쿠키를 생성해서 브라우저에 처음으로 보낸 상태이므로
res.signedCookies.milk와 res.signedCookies.name에는 아무런 값도 들어있지 않은 상태, undefined 출력

웹 어플리케이션에서 클라이언트와 서버 간의 상태 정보를 관리하기 위해 서버 측에 저장하는 식별자
클라이언트는 서버로부터 고유한 세션 ID를 발급받음
이 세션 ID는 쿠키나 URL 매개변수로 클라이언트에 전달
사용자 로그인 상태 관리, 쇼핑 카트 관리, 사용자 설정 유지와 같은 보안이 중요한 데이터나 대용량 데이터를 관리하는 데 주로 사용
하지만 세션 유지가 많을 수록 서버 부하가 걸릴 수 있음
let session = require('express-session');
app.use(session({
secret: 'secret', //세션 아이디를 암호화하기 위한 재료 값
resave: false, //세션을 접속할 때마다 새로운 세션 식별자(sid)의 발급 여부를 결정, 일반적으로 false
saveUninitialized: true //세션을 사용하기 전까지 세션 식별자를 발급하지 않도록 함, 일반적으로 true
}));
app.get('/session', function (req, res) {
if (isNaN(req.session.milk)) { //req.session.milk 요청 시 값이 NaN이면
req.session.milk = 0; //값을 0으로 설정
}
req.session.milk = req.session.milk + 1000; //req.session.milk의 값에 1000 누적
res.send('session: ' + req.session.milk + '원'); //req.session.milk의 값을 브라우저에 전송
})

요소를 문서에서 완전히 제거하여 공간도 차지하지 않음(화면에 렌더링 되지 않음)
요소와 자식 요소 모두 숨겨짐
레이아웃에 영향을 줌
요소를 시각적으로 숨기지만 공간은 여전히 차지함(화면에 렌더링되지만 보이지 않음)
요소와 자식 요소 모두 숨겨짐
레이아웃에 영향을 주지 않음