블로그스팟 모바일 URL ?m=1 간단 제거 방법

블로그스팟 고질병 ?m=1
구글 블로그인 blogspot은 모바일 기기로 접속하면 주소가 달라져요. 정확히는 기본적인 주소가 뒤에 ?m=1이라는 문자가 붙은 주소로 바뀌죠.
그 자체로는 블로그에 해롭지 않지만, 짧은 순간이지만 리디렉션이 발생하고 보기에 거슬린다는 문제가 있어요.
가능하다면 제거하는 게 좋겠는데 일반적인 리디렉션으로는 해결할 수 없다는 문제가 있어요. 해 봤자 모바일 기기에서는 다시 ?m=1이 붙은 주소로 돌아오는 무한반복에 빠져 페이지 접속이 안 될 뿐이고요.
그래서 일반적인 리디렉션이 아니라 약간 다른 방법, 트릭이 필요해요.
간단하게 ?m=1을 제거하는 스크립트
<script>
//<![CDATA[
// 포스트, 페이지에서만 ?m=1 제거
if ( location.pathname.endsWith('.html') ) {
if( location.search.includes("m=1") ) {
history.replaceState('', '', location.pathname);
}
}// maidvsai.com
//]]>
</script>
일부 주소 문제를 제외하고, 간단하게 보자면 위 코드를 HTML 편집에서 헤드(head) 태그 제일 앞에 붙여 넣으면 끝이에요.
location.pathname을 가져와서 브라우저 기록을 교체하는 거죠. location.pathname은 기본 경로(path) 외에 해쉬-프래그먼트(#) 등 잡다한 것 없는 상태가 반환되므로 딱히 신경 쓸 것도 없어요.
흔히 사용하는 meta refresh를 이용한 리디렉션은 새로운 주소로 이동하는 것이라 페이지 새로 고침이 발생한다는 단점이 있지만, history.replaceState()를 사용하면 브라우저의 기록을 바꾸는 것 뿐 실제로 '이동'하는 것은 아니어서 새로고침이 발생하지 않는 장점이 있어요.
하지만 사소한 문제와 치명적 문제가 있어요.
경로 주의점
첫째: 존재하지 않는 파일명(주소)을 사용하면 방문자가 직접 새로고침 시 없는 페이지로 연결되면서 404 not found가 떠요.
둘째: replaceState()사용 시 게시물 주소 추출 및 전달 방법에 따라 2026/06/ 과 같은 중간 경로가 사라져버릴 수 있어요.
세번째 인자에 "/"가 없다면 문제없는데요, "/"로 시작하면 중간 경로가 제거되고 루트 경로로 이동하거든요. 이 경우도 전달하는 경로가 잘못되면 새로고침 시 404가 떠요. 예를 들면 '/1234.html'을 전달하는 경우 'root/1234.html'로 이동하죠.
물론 예시 코드에는 그런 문제가 없어요. replaceState() 처음 써 봤을 때 잘 모르고 그렇게 한 적이 있어서 가능성을 기록해 두는 거예요.
셋째: location.pathname 자체가 쿼리문을 제외하고 반환하니까 만약 주소에 이런저런 쿼리가 붙어있었다면 쿼리문 전체가 사라져요.
보통의 경우는 그다지 문제되지 않을 거예요. 평범한 관련글 링크에 쿼리문이 붙진 않을테니까요. 그렇다고 SERP에 쿼리문이 있는 상태로 인덱싱되지도 않을 것이고요.
하지만 글 목록에서 "더 보기"를 눌렀을 때 문제가 될 수 있어요.
글 더보기를 누르면 "updated-max"로 시작하는 쿼리가 붙거든요. 거기에 m=1이 더해지는 거죠. 예를 들면 바로 아래와 같은 주소가 나와요.
www.maidvsai.com/search/label/Blogidea?updated-max=2026-05-10T22:14:00%2B09:00&max-results=20&start=20&by-date=false&m=1
복잡한 쿼리 끝에 "m=1" 이게 붙었죠? 이러면 위 스크립트의 조건문을 수정하여 글로벌로 썼을 때 쿼리가 통째로 사라져요. 남는 것은 달랑 "www.maidvsai.com/search/label/Blogidea" 이거 뿐이에요.
이 예시 코드는 '.html'이 있는 주소 즉, 'post' 혹은 'page'에서만 스크립트를 실행하니 이런 복잡한 쿼리가 없어서 괜찮은데요, 블로그 전체에서 글로벌 스크립트로 쓸 때 문제가 생기죠. 캠페인 추적 같은 경우에도요.
개선된 스크립트
그래서 글로벌 스크립트나 유입경로 확인 등의 목적으로 쿼리 문자열이 필요하다면, 정확히 'm=1'만 제거하고 다른 쿼리문은 남기는 방식을 써야해요.
<script>
//<![CDATA[
// 모바일 파라메터가 있는가?
const isMblQuery = location.search.includes('m=1');
function repHistory() {
let query = new URLSearchParams(location.search);
query.delete('m'); // 쿼리 중 'm' 파라미터만 삭제.
query = query.toString(); // 일반 문자열로 변경.
query = query ? '?'+query : ''; // 남은 쿼리가 있으면 붙여 줌.
query = query.replace(/%3A/gi, ':').replace(/%2F/gi, '/'); // 일부 인코딩 문자 일반 표시.
let setReplaceUrl = location.pathname + query + location.hash; // 조립.
history.replaceState('', '', setReplaceUrl); // 전달.
}
if( isMblQuery ) { repHistory() }
// maidvsai.com
//]]>
</script>
이전의 post, page 전용 스크립트와 달리 location.pathname을 그대로 밀어넣는 게 아니라, path, query, hash를 전부 가져와서 "m=1"만 빼낸 상태로 햄버거 만들듯 조립하는 거죠.
이 스크립트의 특징은 아래와 같아요.
- URLSearchParams 사용으로 오류 방지
- URLSearchParams().toString()의 퍼센트 인코딩 문제 보완
스크립트를 보면 URLSearchParams를 사용했는데요, 단순하게 replace로 'm=1'을 지우는 방식은 쿼리 중 'utm=1&abc=true' 이런 파라메터가 있을 때 'ut&abc=true' 이런 식으로 오류 발생 위험이 있어서 URLSearchParams를 사용했어요.
Q: 퍼센트(%) 인코딩 된 것은 왜 decodeURIComponent() 안 쓰고 개별 변환함?
A: 만의 하나를 대비해서요.
혹시나, 만에 하나, 쿼리 파라메터에 앰퍼샌드(&)나 물음표(?) 같은 쿼리 구분자 자체가 데이터로 들어가 있을 때, decodeURIComponent를 쓰면 오류가 생길지도 몰라서요. 흔하지는 않겠지만 서드파티 추적 도구나 광고 도구를 쓸 때 뭐가 어떻게 붙을지 몰라서 replace로 몇 개만 변환하도록 했어요.
아무튼, 이제 다른 복잡한 쿼리가 있어도 정확하게 "m=1" 쿼리만 제거해요. 만의 하나를 위한 위험원도 최대한 제거했으니 블로그스팟이면 어디서나 사용할 수 있을 거예요.
문제점1: 스크롤 튐
하지만... history.replaceState()를 사용해서 페이지 새로고침이 없다는 장점이 있지만 예상 외의 단점도 있어요.
자바스크립트를 사용하다보니 즉시 반응이 아니고 실행 시 어쩔 수 없이 딜레이가 약간 생기는데요, 기기 성능이 낮을 수록 더 눈에 띄고 또 페이지 스크롤 값이 변하는 문제가 있었어요. (2022년 에센셜 테마에서 확인)
더 자세하게는, 모바일에서 블로그를 방문한 사람이 페이지가 열린 '즉시' 스크롤을 쭉 내렸을 경우 history.replaceState()로 주소를 바꿈과 동시에 스크롤이 약간 상단으로 이동해 버린다는 의심이 있어요.
특히 브라우저 크기를 바꾼 뒤 새로고침하면 자주 발생했는데, 해쉬 프래그먼트가 붙은 상태에서 새로고침 하면 해당 문제가 잘 보여요. 해당 해쉬가 붙은 제목이 화면 최상단이 아니라 약 250~500px 정도 아래로 내려와 있더라고요.
기기 성능 문제인지 모바일 화면에서만 발생하고 PC화면에서는 발생하지 않았고, 일관되지 않았기에 정확한 원인이나 조건은 확인하지 못했어요.
접속(일반주소) -> 리디렉션(모바일주소) -> 브라우저 기록 교체까지 3단계나 거쳐서 그럴 수도 있고요, 브라우저가 화면을 다시 그리는 과정에서 타이밍 꼬인 것도 의심되지만 역시 확실한 것은 아니에요.
근본적으로는 모바일 접속 시 ?m=1이 붙은 주소로 바꿔버리는 blogspot의 문제긴 하지만, 방문자 입장에서는 누구 잘못이든 간에 '이 블로그는 문제있다'고 느끼게 된다는 점이 중요하죠.
물론 이런 식으로 화면 크기만 바꾼 뒤 새로고침 하는 것은 거의 존재하지 않을 만큼 드문 경우일 거예요.
그리고 페이지 접속 시 처음 0.5초 동안의 초기 스크롤은 대략 100px 내외니까 큰 문제는 아닐지도 모르고요.
스크롤 시점이 빠르다는 것은 화면이 빨리 뜬다는 뜻이니, 그만큼 기기 성능이 좋아서 로딩이 빠르다면 역시 방문자는 아무 문제도 못 느낄 수도 있고요.
문제점2: 리디렉션 오류?
지금은 데이터가 사라져서 확인이 안 되지만, history.replaceState()로 주소의 '?m=1'을 제거하던 기간에 구글 서치콘솔에서 모바일 주소를 리디렉션 오류가 있는 페이지로 잡은 것 같아요. (2022년 확인)
원래는 리디렉션 오류 페이지가 아니라 '적절한 표준 태그가 포함된 대체 페이지' 상태로 분류되어 있었는데 딱 그 시기에 문제가 좀 있었어요.
그 짧은 순간에 브라우저 기록이 바뀌어서 그런걸까요? 아니면 블로그스팟의 고질적인 문제일까요? 모바일 쿼리 없는 원본 주소는 멀쩡하니 큰 문제 없겠지만, 혹시나 하는 마음이 들어 불안한 것도 사실이에요.
공식 문서에서 자바스크립트 리디렉션도 허용하고 있는 만큼 블로그 스팟의 요상한 리디렉션 구조 때문일거라고 강하게 의심되지만, 회사 차원에서 공식적인 인정은 없으니 정확한 것은 알 수 없는 일이죠.
아무래도 실제 운영 중인 블로그에 적용하기 전에 테스트 블로그를 만들어서 장기간 관찰을 해 봐야 할 것 같은데요, 문제는 과연 그만한 가치가 있을지는 회의적이란 거죠.
스크립트에 간단한 조건문을 추가하여 구글 봇이면 패스할 수도 있지만, 역시 '이렇게까지 해야 하느냐?'라는 의문이 생겨요.
그냥 보기싫다는 문제가 제일 큰건데, 이걸 굳이 불안함을 품고 쿼리 스트링을 떼어내야 하느냐는 회의감 섞인 의문이에요.
결론: 구글이 문제다
구글이 ?m=1 이걸 쉽게 제거할 수 있도록 해 주면 좋겠지만, 블로그스팟에서 지원하지 않으니 블로거들이 머리를 싸매도 결론은 나오지 않는 도돌이표예요.
강제 리디렉션 하나 끄는 것이 그렇게 어려운지...
반응형 사이트가 일반적이지 않을 때 만들어진 기능 같은데, 테마 설정에서 휴대기기 접속 시 데스크톱 테마를 표시하도록 지정하거나 반대로 모바일 테마를 보이게 선택해도 없어지지 않아요. 모양만 있고 실제로는 아무 효과도 없는 좀비 코드더라고요.
블로그스팟이라는 서비스 자체는 망할 일 없겠지만, 그래도 돈이 안 되어 방치중인 서비스라 개선 가능성도 별로 없으니... 이래저래 고민만 깊어가네요.