검색엔진 알고리즘을 빈번히 업데이트하지만 모든 변경사항을 공개하지 않습니다.
하지만 웹 사이트의 속도는 SEO 평가지표 중 중요한 요소입니다.
https://developers.google.com/search/docs/fundamentals/get-started#manage-the-user-experience
저는 구글에서 제공하는 PageSpeed Insights를 통해 개별 페이지의 성능테스트 했고, 그 측정항목 중 FCP(First Contentful Paint)가 낮은 점수를 받고 있었습니다
Eliminate render-blocking resources
FCP를 줄이기 위해 가장 중점으로 확인한 요소입니다.
웹 브라우저는 HTML을 parsing하면서 DOM 구조를 만듭니다.
이때 외부에서 CSS, JS를 불러오는 <link>, <script> 태그를 만나면 각각의 파일을 불러오게 됩니다.
Script는 parser blocking 하기때문에 파서가 대기하는 시간이 생기게 됩니다.
( 이전에 <script>태그를 <body> 최하단에 넣는 대안을 사용하는 이유가 그런 이유였습니다 )
심지어 파일을 로드하는데 네트워크 통신이 일어난다면 대기시간은 더 길어지게 됩니다.

위 이미지는 현재 최적화가 필요하다고 생각하는 페이지의 <head>입니다.
React Source가 코드분할되어 적용되고 있는 모습과 kakao-js-sdk, jquery, iamport가 추가되어있습니다.
별 문제가 없어보일 수도 있지만 저의 요구조건과는 조금 다릅니다.
제가 요구하는 조건은 아래와 같습니다.
해당 조건을 만족시키기 위해 특정 페이지 또는 컴포넌트가 실행될 때, 동적으로 <script>태그를 생성해 넣어주는 방법을 대안으로 선택했습니다.
개선할 페이지는 React, TS로 작업되어있습니다. 그래서 해당 기능은 useIamportModule이라는 CustomHook으로 생성했습니다
// useIamportModule.ts
import React, { useEffect, useState, useCallback } from 'react';
// Helper function types
export interface ILoadScriptTag<T> {
type?: keyof React.ReactHTML;
elementAttribute: T;
callbackAfterLoad?: () => void;
}
export const loadScriptTag = async <T extends Partial<HTMLScriptElement>>({
type = 'script',
elementAttribute,
callbackAfterLoad,
}: ILoadScriptTag<T>): Promise<{ status: 'loaded' }> => {
if (!elementAttribute) throw new Error('elementAttribute is required');
const scriptElement = document.createElement(type) as HTMLScriptElement;
Object.entries(elementAttribute).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
scriptElement.setAttribute(key, value as string);
}
});
return new Promise((resolve, reject) => {
scriptElement.onload = () => {
callbackAfterLoad?.();
resolve({ status: 'loaded' });
};
scriptElement.onerror = () => {
reject(new Error('Failed to load script: ' + (elementAttribute.src || 'unknown source')));
};
document.head.appendChild(scriptElement);
});
};
// Extend global window interface
declare global {
interface Window {
jQuery: any;
IMP: {
init: (merchantCode: string) => void;
};
}
}
interface IUseIamportModule {
initLoad?: boolean;
errorCallback?: () => void;
}
export default function useIamportModule({ initLoad = false, errorCallback }: IUseIamportModule = {}) {
const [isLoading, setIsLoading] = useState(false);
const isJQueryLoaded = useCallback(() => !!window.jQuery, []);
const isIamportLoaded = useCallback(() => !!window.IMP, []);
const loadJQuery = useCallback(() => {
if (isJQueryLoaded()) return Promise.resolve();
return loadScriptTag<Partial<HTMLScriptElement>>({
elementAttribute: {
id: 'iamport-jquery',
defer: true,
src: process.env.REACT_APP_JQUERY_CDN as string,
},
});
}, [isJQueryLoaded]);
const loadIamport = useCallback(() => {
if (isIamportLoaded()) return Promise.resolve();
return loadScriptTag<Partial<HTMLScriptElement>>({
elementAttribute: {
id: 'iamport-sdk',
defer: true,
src: process.env.REACT_APP_IAMPORT_CDN as string,
},
callbackAfterLoad: () => {
window.IMP?.init(process.env.REACT_APP_IAMPORT_CODE as string);
},
});
}, [isIamportLoaded]);
const initIamportModule = useCallback(async () => {
setIsLoading(true);
try {
await loadJQuery();
await loadIamport();
} catch (error) {
console.error('Failed to initialize Iamport module:', error);
errorCallback?.();
} finally {
setIsLoading(false);
}
}, [loadJQuery, loadIamport, errorCallback]);
useEffect(() => {
if (initLoad) {
initIamportModule();
}
}, [initLoad, initIamportModule]);
return {
isLoadingIamport: isLoading,
IMP: window.IMP,
load: initIamportModule,
};
}
FCP Time에 대한 문제를 해결하고, SEO에 도움이 되는 성능 개선을 하였습니다
물론 FCP Time만 줄인다고 SEO에 놀라운 향상을 이루어낼 순 없습니다.
하지만 구글의 SEO 랭킹 요소 중 웹사이트 성능이 큰 요소를 차지하기 때문에 유의미한 결과일 것입니다.
그리고 부가적으로 FCP Time을 줄이는 등 성능 개선을 통해 더 나은 UX를 제공할 수도 있다는 장점도 있을 것입니다
다음에도 SEO에 대한 더 나은 개선책이 있다면 포스팅하겠습니다 :)
읽어주셔서 감사합니다.