워드프레스 블록 에디터로 이미지를 삽입할 때 ‘클릭해서 확대하기’ 옵션을 선택하면 사용자가 이미지를 클릭하면 라이트박스 효과로 이미지가 확대되어 표시됩니다.

이미지를 삽입할 때 이 옵션을 선택하지 않더라도 링크가 없는 이미지를 사용자가 클릭하면 모달 이미지로 표시되도록 하고 싶은 경우 아래의 자바스크립트 코드를 활용할 수 있습니다.
워드프레스 블록 에디터 이미지 클릭 확대: 가벼운 모달·내비게이션·유휴 초기화로 속도 영향 최소화
워드프레스닷컴(WordPress.com)의 경우 비즈니스 요금제 이상에서만 플러그인을 설치하여 자바스크립트 코드를 추가할 수 있지만, 한시적으로 개인 요금제와 프리미엄 요금제에서도 플러그인 설치를 허용하는 행사를 진행하고 있어서 개인 요금제로 이 블로그를 개설했습니다.
이미지를 삽입할 때 ‘클릭하여 확대하기’ 옵션을 일일이 선택하는 것이 번거로워서 JS 코드로 링크가 설정되지 않은 포스트 내의 모든 이미지에 대하여 클릭 시 라이트박스 효과로 크게 확대되어 표시되도록 해보았습니다.
다음과 같은 사항을 고려하여 코드를 만들었습니다.
- 가볍고 빠른 풀사이즈 이미지 모달 + 좌/우 내비게이션.
- 링크 없는 블록 에디터 이미지 클릭 시 모달 오픈
- Full 사이즈 우선 표시(data-full-url 등 → srcset 최대폭 → src)
- 뷰포트 거터(좌우 여백) 적용: 이미지 프레이밍 + 탐색 버튼 항상 가시화
- 내비게이션 버튼 + ←/→ 키로 이전/다음 전환, ESC/배경/X로 닫기
- 첫 상호작용 시 지연 초기화(pointerdown + requestIdleCallback) → Lighthouse 친화적. 구글 페이지스피드 인사이트 점수에 영향 최소화.
- 이벤트 위임 1회만 설치, 단일 모달 동적 생성(필요 시 CSS 주입)
- 접근성: role=”dialog”, aria-modal 지원, 포커스 관리(열기/닫기 복귀)
- 스크롤 잠금 + 스크롤바 폭 보정으로 CLS 방지
- 수동 프리로드: 이웃 이미지 미리 로드로 전환 지연 감소
- 반응형 데스크톱 전용(모바일<768px 제외), 링크로 감싼 이미지는 무시
- 모던 UI: 투명 오버레이, 글래스 버튼(반투명+보더+블러), 호버/포커스 상태
- CSS 변수로 여백/색상 톤 간단 커스터마이즈 가능
이미지를 클릭하면 다음 그림과 같이 이미지가 확대되어 표시되며 좌우 내비게이션 버튼을 클릭하여 동일 포스트 내의 이전/다음 이미지를 이동이 가능합니다.

다음과 같은 자바스크립트를 활용할 수 있습니다.
<script>
/**
* WordPress 블록 이미지 모달 (full size + 좌/우 내비 + 가터 + modern overlay/buttons)
* - 오버레이: 살짝 투명
* - 버튼: 밝은 반투명 surface + 보더 + 블러 + hover/focus 대비
* - 지연 초기화/위임/CLS-safe/idle 설치는 동일
*/
(function () {
"use strict";
const SEL_IMG = ".wp-block-image img, .wp-block-image figure img";
const SEL_CONTAINER_CANDIDATES = [
"article", ".entry-content", ".post-content", ".single-post",
".hentry", ".site-main", "main", ".wp-block-post-content"
];
let installed = false;
let idleId = 0;
let bootstrapBound = false;
// modal/galleria state
let root = null, imgEl = null, closeBtn = null, prevBtn = null, nextBtn = null, lastFocused = null;
let gallery = [];
let index = -1;
// ---------- bootstrap (최소 오버헤드)
function onPointerDownBootstrap(e) {
const img = matchTargetImage(e.target);
if (!img) return;
initDelegation();
openFrom(img);
teardownBootstrap();
}
function scheduleIdleInstall() {
const onIdle = () => { initDelegation(); teardownBootstrap(); };
if ("requestIdleCallback" in window) {
idleId = requestIdleCallback(onIdle, { timeout: 3000 });
} else {
idleId = setTimeout(onIdle, 1200);
}
}
function attachBootstrap() {
if (bootstrapBound) return;
bootstrapBound = true;
window.addEventListener("pointerdown", onPointerDownBootstrap, { capture: true, passive: true });
scheduleIdleInstall();
}
function teardownBootstrap() {
if (!bootstrapBound) return;
bootstrapBound = false;
window.removeEventListener("pointerdown", onPointerDownBootstrap, { capture: true });
if ("cancelIdleCallback" in window && typeof idleId === "number") {
try { cancelIdleCallback(idleId); } catch (_) {}
} else {
clearTimeout(idleId);
}
}
// ---------- delegation install
function initDelegation() {
if (installed) return;
installed = true;
document.addEventListener("click", onClickDelegated, { passive: true });
document.addEventListener("keydown", onKeydown, { passive: true });
}
// ---------- handlers
function onClickDelegated(e) {
const img = matchTargetImage(e.target);
if (!img) return;
openFrom(img);
}
function onKeydown(e) {
if (e.key === "Escape") hide();
if (!root || root.style.display !== "block") return;
if (e.key === "ArrowLeft") { e.preventDefault?.(); prev(); }
else if (e.key === "ArrowRight") { e.preventDefault?.(); next(); }
}
// ---------- target match
function isDesktop() { return window.matchMedia("(min-width: 768px)").matches; }
function matchTargetImage(target) {
const img = target?.closest?.(SEL_IMG);
if (!img) return null;
if (!isDesktop()) return null;
if (img.closest("a")) return null;
return img;
}
// ---------- container & gallery
function findPostContainer(fromEl) {
let node = fromEl.parentElement;
while (node && node !== document.body) {
if (SEL_CONTAINER_CANDIDATES.some(sel => node.matches?.(sel))) return node;
node = node.parentElement;
}
return document;
}
function collectGallery(container) {
return Array.from(container.querySelectorAll(SEL_IMG))
.filter(img => !img.closest("a") && isDesktop());
}
function findIndexInGallery(img, list) {
const idx = list.indexOf(img);
if (idx >= 0) return idx;
const target = resolveFullSrc(img);
return Math.max(0, list.findIndex(i => resolveFullSrc(i) === target));
}
// ---------- full-size resolver
function resolveFullSrc(imgEl) {
const figure = imgEl.closest("figure, .wp-block-image");
const candAttrs = [
"data-full-url", "data-orig-file", "data-large-file",
"data-fullsize-url", "data-original", "data-src"
];
for (const el of [imgEl, figure]) {
if (!el) continue;
for (const name of candAttrs) {
const v = el.getAttribute?.(name);
if (v) return v;
}
}
const fromSrcset = pickLargestFromSrcset(imgEl);
if (fromSrcset) return fromSrcset;
return imgEl.currentSrc || imgEl.src || "";
}
function pickLargestFromSrcset(imgEl) {
const ss = imgEl.getAttribute("srcset");
if (!ss) return "";
const candidates = ss.split(",").map(s => s.trim()).map(s => {
const [url, d] = s.split(/\s+/);
const w = d && d.endsWith("w") ? parseInt(d, 10) : 0;
return { url, w };
}).filter(c => c.url);
if (!candidates.length) return "";
candidates.sort((a, b) => b.w - a.w);
return candidates[0].url;
}
// ---------- modal lifecycle
function ensureModal() {
if (root) return;
if (!document.querySelector('style[data-cc="image-modal-styles"]')) {
const style = document.createElement("style");
style.dataset.cc = "image-modal-styles";
style.textContent = `
.cc-image-modal{
display:none;position:fixed;inset:0;z-index:9999;
/* 더 투명하고 깊이감 있는 오버레이 */
background:rgba(10,10,12,.72);
--cc-gutter:56px; /* 좌우 여백(가터) */
--cc-vpad:24px; /* 상하 패딩 */
--cc-surface: rgba(255,255,255,.18); /* 밝은 반투명 버튼 배경 */
--cc-surface-hov: rgba(255,255,255,.28);
--cc-border: rgba(255,255,255,.30);
--cc-icon: #fff;
--cc-shadow: 0 4px 14px rgba(0,0,0,.35);
}
@media (min-width: 1200px){
.cc-image-modal{ --cc-gutter:72px; --cc-vpad:28px; }
}
@media (max-width: 1024px){
.cc-image-modal{ --cc-gutter:44px; --cc-vpad:20px; }
}
@media (max-width: 840px){
.cc-image-modal{ --cc-gutter:36px; --cc-vpad:16px; }
}
.cc-image-modal[open]{display:block}
.cc-image-modal__backdrop{position:absolute;inset:0}
/* 이미지가 가터를 침범하지 않도록 프레임 안에서 최대화 */
.cc-image-modal__img{
position:absolute;
top:var(--cc-vpad); bottom:var(--cc-vpad);
left:var(--cc-gutter); right:var(--cc-gutter);
margin:auto; display:block;
max-width:calc(100vw - (var(--cc-gutter) * 2));
max-height:calc(100vh - (var(--cc-vpad) * 2));
width:auto; height:auto;
}
/* 닫기 버튼: 밝은 유리 표면 + 보더 + 블러 */
.cc-image-modal__close{
position:absolute; top:var(--cc-vpad); right:calc(var(--cc-gutter) - 8px);
color:var(--cc-icon); cursor:pointer; border:1px solid var(--cc-border);
background:var(--cc-surface); border-radius:12px; width:40px; height:40px;
font-size:26px; line-height:36px; text-align:center; z-index:3;
box-shadow: var(--cc-shadow);
-webkit-backdrop-filter:saturate(140%) blur(6px);
backdrop-filter:saturate(140%) blur(6px);
}
.cc-image-modal__close:hover{ background:var(--cc-surface-hov); }
.cc-image-modal__close:focus{ outline:2px solid rgba(255,255,255,.7); outline-offset:2px; }
/* 내비 버튼: 동일 톤, 항상 이미지 위 */
.cc-image-modal__nav{
position:absolute; top:50%; transform:translateY(-50%);
border:1px solid var(--cc-border); background:var(--cc-surface); color:var(--cc-icon);
font-size:24px; width:48px; height:64px; border-radius:14px; cursor:pointer;
z-index:2; box-shadow: var(--cc-shadow);
-webkit-backdrop-filter:saturate(140%) blur(6px);
backdrop-filter:saturate(140%) blur(6px);
}
.cc-image-modal__nav:hover{ background:var(--cc-surface-hov); }
.cc-image-modal__nav:focus{ outline:2px solid rgba(255,255,255,.7); outline-offset:2px; }
.cc-image-modal__nav--prev{ left:16px; }
.cc-image-modal__nav--next{ right:16px; }
@media (prefers-reduced-motion:no-preference){
.cc-image-modal[open] .cc-image-modal__img{ transition:opacity .18s ease; }
.cc-image-modal__nav, .cc-image-modal__close{ transition:background-color .15s ease, transform .15s ease, box-shadow .15s ease; }
.cc-image-modal__nav:hover, .cc-image-modal__close:hover{ transform:translateY(-1px); }
}
`;
document.head.appendChild(style);
}
root = document.createElement("div");
root.className = "cc-image-modal";
root.setAttribute("role", "dialog");
root.setAttribute("aria-modal", "true");
root.setAttribute("aria-label", "Image preview");
const backdrop = document.createElement("div");
backdrop.className = "cc-image-modal__backdrop";
imgEl = document.createElement("img");
imgEl.className = "cc-image-modal__img";
imgEl.alt = "";
closeBtn = document.createElement("button");
closeBtn.className = "cc-image-modal__close";
closeBtn.setAttribute("aria-label", "Close");
closeBtn.innerHTML = "×";
prevBtn = document.createElement("button");
prevBtn.className = "cc-image-modal__nav cc-image-modal__nav--prev";
prevBtn.setAttribute("aria-label", "Previous image");
prevBtn.textContent = "‹";
nextBtn = document.createElement("button");
nextBtn.className = "cc-image-modal__nav cc-image-modal__nav--next";
nextBtn.setAttribute("aria-label", "Next image");
nextBtn.textContent = "›";
backdrop.addEventListener("click", hide, { passive: true });
closeBtn.addEventListener("click", hide);
prevBtn.addEventListener("click", () => prev());
nextBtn.addEventListener("click", () => next());
root.appendChild(backdrop);
root.appendChild(imgEl);
root.appendChild(closeBtn);
root.appendChild(prevBtn);
root.appendChild(nextBtn);
document.body.appendChild(root);
}
// ---------- CLS-safe scroll lock
function lockScroll(lock) {
const html = document.documentElement;
const body = document.body;
if (lock) {
const sw = window.innerWidth - html.clientWidth;
if (sw > 0) {
html.style.paddingRight = sw + "px";
body.style.paddingRight = sw + "px";
}
html.style.overflow = "hidden";
body.style.overflow = "hidden";
} else {
html.style.overflow = "";
body.style.overflow = "";
html.style.paddingRight = "";
body.style.paddingRight = "";
}
}
// ---------- open/close & navigation
function openFrom(clickedImg) {
const container = findPostContainer(clickedImg);
gallery = collectGallery(container);
if (gallery.length === 0) return;
index = findIndexInGallery(clickedImg, gallery);
ensureModal();
updateNavVisibility();
showIndex(index);
lastFocused = document.activeElement;
root.setAttribute("open", "");
root.style.display = "block";
lockScroll(true);
closeBtn.focus({ preventScroll: true });
}
function hide() {
if (!root) return;
root.removeAttribute("open");
root.style.display = "none";
lockScroll(false);
try { lastFocused?.focus?.({ preventScroll: true }); } catch (_) {}
}
function showIndex(i) {
if (!gallery[i]) return;
const src = resolveFullSrc(gallery[i]);
if (!src) return;
const run = () => {
imgEl.src = src;
preloadAround(i);
};
if (gallery[i].decode) {
gallery[i].decode().catch(() => {}).finally(() => requestAnimationFrame(run));
} else {
requestAnimationFrame(run);
}
}
function prev() {
if (gallery.length <= 1) return;
index = (index - 1 + gallery.length) % gallery.length;
showIndex(index);
}
function next() {
if (gallery.length <= 1) return;
index = (index + 1) % gallery.length;
showIndex(index);
}
function updateNavVisibility() {
const visible = gallery.length > 1;
prevBtn.style.display = visible ? "block" : "none";
nextBtn.style.display = visible ? "block" : "none";
}
function preloadAround(i) {
const idxs = [(i + 1) % gallery.length, (i - 1 + gallery.length) % gallery.length];
idxs.forEach(k => {
const g = gallery[k];
if (!g) return;
const url = resolveFullSrc(g);
if (!url) return;
const im = new Image();
im.decoding = "async";
im.src = url;
});
}
// ---------- boot
attachBootstrap();
})();
</script>
가입형 플러그인을 사용하는 경우 WPCode 플러그인을 설치하여 푸터 영역에 상기 자스 코드를 추가하면 됩니다.😄
상기 코드는 워드프레스 정보꾸러미 블로그에 소개된 코드를 참고하여 개선한 것입니다.
사이트 속도에 대한 영향을 최소화하도록 코드를 만들었기 때문에 사이트 속도에는 별 영향을 안 미칠 것이라 생각됩니다.