우당탕탕 기술블로그
[React] 카카오 애드핏 에러 (useEffect, componentDidMount, useLayoutEffect) 본문
✏️ 문제 상황
함수형 컴포넌트를 이용해서 MBTI 테스트 웹사이트를 만들고 카카오 애드핏을 달아보기 위해 익숙했던 useEffect를 사용해서 컴포넌트가 마운팅되고 나서 동기적으로 광고 DOM 을 추가해서 띄울 것을 의도했다. 그치만 계속 에러가 나서 리렌더링이 될때마다 광고를 분명 하나만 달았는데 두 개, 세 개씩 늘어나게 되었다. 어떨 때는 className으로 지정한 adfit을 찾을 수 없다는 에러를 뱉어내기도 했다.
...
useEffect(()=>{
let ins = document.createElement('ins');
let scr = document.createElement('script');
ins.className = 'kakao_ad_area';
ins.style = "display:none; width:100%;";
scr.async = 'true';
scr.type = "text/javascript";
scr.src = "//t1.daumcdn.net/kas/static/ba.min.js";
ins.setAttribute('data-ad-width','320');
ins.setAttribute('data-ad-height','100');
ins.setAttribute('data-ad-unit','내 광고 코드');
document.querySelector('.adfit').appendChild(ins);
document.querySelector('.adfit').appendChild(scr);
}, [])
...
return(
...
<div className="adfit"></div>
...
);
🧐 문제 원인
React 공식 문서에 따르면
componentDidMount와 componentDidUpdate와는 다르게, useEffect로 전달된 함수는 지연 이벤트 동안에 레이아웃 배치와 그리기를 완료한 후 발생합니다. 이것은 구독이나 이벤트 핸들러를 설정하는 것과 같은 다수의 공통적인 부작용에 적합합니다. 왜냐면 대부분의 작업이 브라우저에서 화면을 업데이트하는 것을 차단해서는 안 되기 때문입니다.
그렇지만, 모든 effect가 지연될 수는 없습니다. 예를 들어 사용자에게 노출되는 DOM 변경은 사용자가 노출된 내용의 불일치를 경험하지 않도록 다음 화면을 다 그리기 이전에 동기화 되어야 합니다. (그 구분이란 개념적으로는 수동적 이벤트 리스너와 능동적 이벤트 리스너의 차이와 유사합니다)
광고 DOM을 추가해서 변경을 의도했고 그래서 이 상황의 경우 useEffect를 사용하면 안되는 것을 알았다. 정확히는 사용이 안된다기보다는 이것을 제어하기위한 useState를 사용해서 렌더링을 제어해야하는 상황이다.
😊 해결 방안
공식문서를 참고해서 useEffect가 아닌 클래스형 컴포넌트를 만들고 componentDidMount 를 사용해서 카카오 애드핏 광고요소를 달도록 했다.
componentDidMount() 는 컴포넌트가 마운트된 직후, 즉 트리에 삽입된 직후에 호출됩니다. DOM 노드가 있어야 하는 초기화 작업은 이 메서드에서 이루어지면 됩니다. 외부에서 데이터를 불러와야 한다면, 네트워크 요청을 보내기 적절한 위치입니다.
import React from "react";
import styled from "styled-components";
export class AdfitBannerAd extends React.Component {
// 클래스 인스턴스를 초기화하고 초기 상태를 설정
constructor(props) {
super(props)
}
// 렌더링을 다시할지 여부를 결정하는 라이프사이클 메서드
shouldComponentUpdate() {
// 업데이트되지 않고 이전 렌더링 결과를 사용
return false;
}
componentDidMount() {
let ins = document.createElement('ins');
let scr = document.createElement('script');
ins.className = 'kakao_ad_area';
ins.setAttribute('style', 'display: none;');
scr.async = true;
scr.type = 'text/javascript';
scr.src = '//t1.daumcdn.net/kas/static/ba.min.js';
ins.setAttribute('data-ad-width', '320');
ins.setAttribute('data-ad-height', '50');
ins.setAttribute('data-ad-unit', '내 광고 코드');
let parent = document.getElementById('adFit');
parent?.appendChild(ins);
parent?.appendChild(scr);
}
render() {
return (
<BannerAd id="adFit"/>
)
}
}
const BannerAd = styled.div`
width: 100%;
margin-top: 50px;
추가적으로 useLayoutEffect 훅도 있었다! useLayoutEffect 사용을 권장하지는 않지만 내가 의도하는 상황에서는 간편하게 useLayoutEffect를 사용해서 함수형 컴포넌트로 개발한 것을 통일성 있게 가져갈 수는 있을 것 같다.
아래는 공식문서의 useLayoutEffect에 대한 설명이다.
useLayoutEffect는 useEffect 와 동일하긴 한데, 모든 DOM 변경 후에 동기적으로 발생합니다. 이것은 DOM에서 레이아웃을 읽고 동기적으로 리렌더링하는 경우에 사용하세요. useLayoutEffect 의 내부에 예정된 갱신은 브라우저가 화면을 그리기 이전 시점에 동기적으로 수행될 것입니다.
클래스 컴포넌트에서 코드를 변환하는 경우에 useLayoutEffect는 componentDidMount나 componentDidUpdate와 동일한 단계를 실행하게 된다는 것에 주의하기 바랍니다. 그렇기는 하지만, 먼저 useEffect를 사용해 보고 문제가 있다면 그다음으로 useLayoutEffect를 사용해 보기를 권합니다.
서버 렌더링을 사용하는 경우라면 자바스크립트가 모두 다운로드될 때까지는 useLayoutEffect와 useEffect 어느 것도 실행되지 않는다는 것을 명심해야 합니다. 이것이 서버에서 렌더링 되는 컴포넌트에서 useLayoutEffect가 사용되는 경우에 대해 React가 경고하는 이유입니다. 이를 수정하기 위해서는 (최초 렌더링 시에 필요하지 않다면) 로직을 useEffect로 이동한다거나 (useLayoutEffect가 수행될 때까지 HTML이 깨져 보이는 경우는) 클라이언트 렌더링이 완료될 때까지 컴포넌트 노출을 지연하도록 하세요.
서버에서 렌더링된 HTML에서 레이아웃 effect가 필요한 컴포넌트를 배제하고 싶다면, showChild && <Child />를 사용하여 조건적으로 렌더링 하고 useEffect(() => { setShowChild(true); }, [])를 사용하여 노출을 지연시키세요. 이런 방법으로 자바스크립트 코드가 주입되기 전에 깨져 보일 수 있는 UI는 표현되지 않게 됩니다.
Component – React
The library for web and native user interfaces
react.dev
useLayoutEffect – React
The library for web and native user interfaces
react.dev
'Web' 카테고리의 다른 글
[React] 이미지 등록 후 서버 요청 시 413 에러 발생, 이미지 압축하기 (0) | 2023.07.11 |
---|---|
[React] input에 입력할 때 글자 깜빡임 현상, 웹폰트 성능 최적화 (0) | 2023.06.18 |
[React] npm i node-sass 에러 (0) | 2023.06.04 |
[HTML, JS] form을 이용한 데이터 POST 요청 시 Not Found 404 오류 (0) | 2023.05.29 |
[CSS] input password 안 보이는 현상 (0) | 2023.05.26 |