블로그 본문
[하이브리드 렌더링] 메인 쇼핑몰 화면(Main.js) SSR 구현 및 상품 목록 CSR(AJAX/Fetch) 연동 기술
니꼴라 클라우드 테크 9월 14, 2026📌 목차 바로가기
지난 포스팅에서는 사용자 평문 비밀번호 저장의 위험성을 파괴하기 위해 bcrypt 단방향 해시 함수와 솔트(Salt) 기법을 대입하고, 회원가입, 로그인, 로그아웃의 3대 자격 증명 흐름을 처리하는 auth.js 인증 라우터 모듈을 구현했습니다.
이번 포스팅에서는 검색엔진 최적화(SEO)와 빠른 첫 화면 로딩을 보장하는 서버 사이드 렌더링(SSR)과, 화면 깜빡임 없는 앱 형태의 동적 UX를 제공하는 클라이언트 사이드 렌더링(CSR)을 융합한 하이브리드 렌더링(Hybrid Rendering) 아키텍처의 설계 사상과 구현 기술을 설명하겠습니다.
1. 초기 로딩과 반응성의 조화: SSR과 CSR의 기회비용 및 하이브리드 렌더링 사상
웹 애플리케이션 화면을 그려내는 렌더링 방식에는 크게 두 가지 대립되는 기술적 노선이 존재합니다.
- 서버 사이드 렌더링 (Server-Side Rendering, SSR):
- 작동 원리: 서버가 요청을 받아 데이터베이스 쿼리를 완료한 후, 완성된 HTML 전체 마크업을 빌드하여 클라이언트 브라우저로 전송합니다.
- 장점: 검색엔진 크롤러가 완성된 HTML을 즉시 수집할 수 있어 검색엔진 최적화(SEO)에 매우 유리하며, 첫 화면의 시각적 요소가 빠르게 뜨는 FCP(First Contentful Paint) 속도가 뛰어납니다.
- 단점: 화면의 일부 데이터만 변경되더라도 전체 페이지를 다시 요청해 받아오므로 화면 깜빡임(Refresh)이 발생하고 서버 CPU 부하가 증가합니다.
- 클라이언트 사이드 렌더링 (Client-Side Rendering, CSR):
- 작동 원리: 서버는 뼈대만 있는 껍데기 HTML과 자바스크립트 파일만 내려주고, 브라우저가 비동기 API 통신(Fetch/AJAX)으로 데이터를 수령하여 동적으로 DOM을 생성합니다.
- 장점: 페이지 이동 시 화면 전체의 깜빡임이 없고, 필요한 데이터만 부분 업데이트하므로 앱(App)처럼 미려한 사용자 경험(UX)을 선사하며 서버의 렌더링 부하를 클라이언트로 분산시킵니다.
- 단점: 초기 자바스크립트 로딩 전까지 빈 화면이 노출될 수 있으며, 초기 렌더링 마크업이 비어있어 SEO에 불리할 수 있습니다.
- 하이브리드 렌더링(Hybrid Rendering)의 탄생:
- 우리의 의류 쇼핑몰 백엔드는 이 두 방식의 장점만을 취합니다. 메인 페이지의 상단 네비게이션, 헤더, 푸터, 전체 레이아웃 뼈대 및 외부 스타일시트(
style.css)는 SSR 방식으로 서버가 즉각 렌더링하여 반환합니다. - 이어서 화면 내부의 가변적인 대용량 상품 카드 목록은 브라우저가 백그라운드에서 비동기
fetch()통신으로 CSR 방식을 기용해 받아와 동적 주입합니다. 이로써 SEO, 초기 로딩 속도, 그리고 미려한 인터랙션을 동시에 수호하는 하이브리드 아키텍처가 완성됩니다.
- 우리의 의류 쇼핑몰 백엔드는 이 두 방식의 장점만을 취합니다. 메인 페이지의 상단 네비게이션, 헤더, 푸터, 전체 레이아웃 뼈대 및 외부 스타일시트(
2. [서버 측 설계] Main.js의 SSR 메인 라우트 및 RESTful API 구현
엔트리 포인트인 Main.js 내부에서는 SSR 페이지 반환과 CSR용 JSON 데이터 반환이라는 이중 책임을 수행하는 엔드포인트를 수립합니다.
① SSR 메인페이지 라우트 (GET /main)
사용자가 메인 페이지 진입을 시도할 때 작동하는 라우트입니다.
- 세션 자격 검증:
authCheck.isOwner(req, res)를 호출하여 로그인된 사용자만 접근하도록 검문합니다. 비인증 사용자는/auth/login으로 즉시 리다이렉트합니다. - SSR 레이아웃 조립: 세션 저장소에 보관된 사용자 식별 정보(
req.session.nickname)와 외부 정적 파일인style.css링크, 그리고 상품 카드가 들어설 빈 컨테이너(<div id="product-grid" class="product-grid"></div>)를 품은 HTML 뼈대를 구성해 반환합니다.
② CSR 데이터 전송 REST API (GET /api/products)
클라이언트 자바스크립트가 동적으로 상품 데이터를 요청할 때 작동하는 API 엔드포인트입니다.
- RDBMS 데이터 조회:
db.query('SELECT * FROM Products')구문을 실행하여 RDS MySQLProducts테이블에 적재된 6대 의류 상품 마스터 정보를 스캔합니다. - JSON 데이터 응답: 조회된 레코드셋(상품코드, 상품명, 가격, 소재, S3 HTTPS 이미지 URL 주소 등)을
res.json(products)구문을 통해 가벼운 JSON 배열 규격으로 즉시 반환합니다.
3. [클라이언트 측 연동] Fetch API 기반 비동기 데이터 수송과 DOM 동적 주입
SSR로 수득한 메인 뼈대 HTML 내부에는 클라이언트 자바스크립트 비동기 엔진 구동 스크립트가 수록되어 있습니다.
// 클라이언트 측 비동기 상품 로딩 및 DOM 동적 생성 스크립트
document.addEventListener('DOMContentLoaded', () => {
fetchProducts();
});
// 1. 비동기 Fetch API 통신 함수
async function fetchProducts() {
try {
const response = await fetch('/api/products');
if (!response.ok) throw new Error('상품 데이터를 불러오는데 실패했습니다.');
const products = await response.json();
// 2. DOM 동적 주입 함수 호출
displayProducts(products);
} catch (error) {
console.error('Fetch 에러:', error);
document.getElementById('product-grid').innerHTML = '<p class="error">상품 목록을 불러올 수 없습니다.</p>';
}
}
// 3. 상품 카드 DOM 생성 및 주입 헬퍼 함수
function displayProducts(products) {
const container = document.getElementById('product-grid');
container.innerHTML = ''; // 초기화
products.forEach(product => {
const card = document.createElement('div');
card.className = 'product-card';
card.innerHTML = `
<a href="/prod/product/${product.prod_cd}">
<img src="${product.prod_img}" alt="${product.prod_name}" class="product-image" onerror="this.src='/images/no-image.png'" />
<div class="product-info">
<span class="product-category">${product.prod_type || '의류'}</span>
<h3 class="product-title">${product.prod_name}</h3>
<p class="product-price">${Number(product.price).toLocaleString()}원</p>
</div>
</a>
`;
container.appendChild(card);
});
}
■ 클라이언트 처리의 핵심 기술
- 비동기 패치(Asynchronous Fetch):
fetch('/api/products')구문을 통해 브라우저 화면 전체가 재시작되는 단절 없이, 백그라운드 스레드에서 REST API와 고속 통신합니다. - 천단위 콤마 표기 및 널 안전성:
Number(product.price).toLocaleString()을 가동해 화폐 단위를 정돈하고,product.prod_type || '의류'널 병합 패턴으로 데이터 누락 시의 UI 붕괴를 사전에 방지합니다. - S3 이미지 연결 및 에러 대체: S3 스토리지의 고유 HTTPS 주소(
prod_img)를<img>태그에 직접 매핑하되, 네트워크 불안정으로 이미지 로딩 실패 시onerror이벤트를 가동해 미리 준비된 대체 이미지로 안전하게 치환합니다.
4. 하이브리드 렌더링 통신 아키텍처 다이어그램
서버 사이드 렌더링(SSR)으로 메인 레이아웃 뼈대 HTML을 완성한 직후, 클라이언트 브라우저가 비동기 Fetch API를 가동해 RDS MySQL의 상품 데이터와 S3 미디어를 CSR 방식으로 합성해 내는 전체 하이브리드 통신 아키텍처 다이어그램입니다.
5. [코드 분석] Main.js 메인 엔트리 포인트 핵심 구문
실제 백엔드 서버에서 SSR 메인 화면과 CSR 전용 REST API를 동시에 제공하는 Main.js 라우터 핵심 구현 코드입니다.
/**
* Main.js - 쇼핑몰 메인페이지 SSR 및 상품 API 컨트롤러
*/
const express = require('express');
const router = express.Router();
const db = require('./db');
const authCheck = require('./authCheck');
// 1. SSR 메인 쇼핑몰 화면 라우트 (GET /main)
router.get('/main', async (req, res) => {
// 인증 검문소 가동
if (!authCheck.isOwner(req, res)) {
return res.redirect('/auth/login');
}
const authUI = authCheck.statusUI(req, res);
const html = `
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>FASHION STORE - 메인 쇼핑몰</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<header class="main-header">
<div class="logo"><h1>FASHION STORE</h1></div>
<div class="user-status">${authUI}</div>
</header>
<main class="main-container">
<h2>이달의 추천 패션 아이템</h2>
<!-- CSR 방식으로 상품 카드가 동적 주입될 빈 컨테이너 -->
<div id="product-grid" class="product-grid">
<p class="loading">상품 목록을 동적으로 불러오는 중입니다...</p>
</div>
</main>
<script src="/js/main-csr.js"></script>
</body>
</html>
`;
res.send(html);
});
// 2. CSR 전용 상품 목록 REST API (GET /api/products)
router.get('/api/products', async (req, res) => {
try {
const [products] = await db.query('SELECT prod_cd, prod_name, price, prod_type, material, prod_img FROM Products');
res.json(products);
} catch (err) {
console.error('상품 목록 API 조회 에러:', err);
res.status(500).json({ error: '데이터베이스 조회 중 오류가 발생했습니다.' });
}
});
module.exports = router;
마치며: SEO와 미려한 UX를 모두 장악했습니다!
오늘 우리는 백엔드 애플리케이션 화면 설계의 최고 지향점이라 불리는 서버 사이드 렌더링(SSR)과 클라이언트 사이드 렌더링(CSR)을 유기적으로 합성한 하이브리드 렌더링 아키텍처를 완벽하게 설계하고 구현했습니다!
"검색엔진 노출과 첫 화면 로딩 반응성을 수호하기 위해 뼈대 레이아웃은 서버에서 깔끔하게 SSR로 구워내고, 대용량 상품 카드 목록은 비동기 Fetch 통신을 통해 화면 깜빡임 없이 CSR 방식으로 돔에 주입하는 가장 현대적이고 미려한 이커머스 웹 애플리케이션을 완성해 낸 것입니다."
다음 포스팅이자 데이터베이스 복합 연산을 다루는 [복합 조인 연산] 상품 상세페이지(prodDetail.js) 조립을 위한 Products-Customers-Prod_Evals 3중 테이블 INNER JOIN 및 평점 통계 쿼리 편에서는 특정 상품의 상세 속성 조회, 관련 리뷰 목록 획득, 산술 평균 평점 계산 및 별점 HTML 변환 헬퍼 함수 구현법을 설명하겠습니다.
오늘 Fetch API 비동기 통신 중 CORS 오류나 404 경로 예외가 발생했거나, DOM 동적 생성이 정상 작동하지 않으신다면 주저하지 마시고 운영자 메일로 질문해주세요. 오늘도 안전하고 똑똑한 클라우딩 라이프 하세요. 감사합니다! 😉
출처
- 이현호. 실무 프로젝트로 완성하는 클라우드 환경에서 DB 구축과 웹 개발. 길벗캠퍼스. 2026.04. (8.1장 '엔트리 포인트 모듈(Main.js)', 200-204페이지 및 8.2장 '상품 목록 API 및 비동기 연동', 205-209페이지 참조)
- 이현호. "08장. 애플리케이션 개발 - 메인페이지_상품상세정보_설명오디오.m4a" 설명 오디오 가이드 스크립트 기반 SSR과 CSR의 기회비용 비교, Fetch API 기반 비동기 데이터 로딩, toLocaleString 화폐 단위 표기 및 onerror 대체 이미지 처리 기술 비하인드 대화록 완벽 반영.
- MDN Web Docs - Fetch API Guide (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) 및 Express.js Response Methods Documentations.
자주 묻는 질문 (FAQ)
Q. 메인 쇼핑몰 화면 구축 시 SSR(서버 사이드 렌더링)과 CSR(클라이언트 사이드 렌더링)을 결합한 하이브리드 렌더링을 적용하는 핵심 이유는 무엇인가요?
A. SEO(검색엔진 최적화)와 사용자 경험(UX)을 동시에 수호하기 위해서입니다. 상단 네비게이션과 메인 뼈대 HTML은 SSR로 빠르게 전송하여 초기 로딩 속도와 SEO를 잡고, 가변적인 상품 목록 데이터는 CSR(fetch API)로 수록하여 화면 전체 깜빡임 없는 앱 형태의 미려한 UX를 제공합니다.
Q. 클라이언트 측에서 fetch('/api/products') 비동기 통신으로 수신한 JSON 상품 데이터를 동적으로 화면에 주입하는 원리는 무엇인가요?
A. 자바스크립트 DOM 조작과 배열 순회 연산의 합성입니다. 수신한 JSON 배열을 forEach()로 순회하며 상품 이미지(S3 URL), 상품명, 가격(toLocaleString 적용)을 포함한 HTML 카드 템플릿 요소를 생성한 뒤, target 컨테이너 요소(product-grid)에 appendChild()로 주입합니다.
Q. S3 스토리지에 보관된 상품 이미지 주소(prod_img)를 태그에 매핑할 때 이미지 로딩 실패에 대비하는 방어적 코딩 기법은 무엇인가요?
A. onerror 이벤트 핸들러를 가동하는 것입니다. 네트워크 지연이나 S3 이미지 경로 이상으로 로딩이 실패할 때 'onerror="this.src='/images/no-image.png'"' 속성을 트리거하여 준비된 대체 이미지로 자동 교체함으로써 UI 붕괴를 원천 방어합니다.
