안녕하세요! 웹 개발을 하다 보면 데스크탑에서는 멀쩡하던 UI가 모바일에서만 말썽을 부리는 경우가 종종 있죠. 저도 최근 Next.js와 Tailwind CSS로 웹툰 결과 페이지를 만들다가 말풍선 안의 텍스트가 모바일에서만 한쪽으로 쏠리는 현상을 겪었습니다. 브라우저 개발자 도구의 모바일 뷰에서는 괜찮았는데 말이죠!
오늘은 이 문제의 원인을 파악하고 해결하는 과정을 공유해 보려고 합니다.
처음에 내가 작성했던 코드 (문제 발생 코드)
먼저 문제가 발생했던 핵심 코드 일부를 살펴볼게요. 텍스트 위치와 내용을 webtoonTextMeta.ts
에서 관리하고, WebtoonText.tsx
컴포넌트가 이를 렌더링하는 구조였습니다.
constants/bluemoonladysaju/webtoonTextMeta.ts
(일부)
// ... (interface 정의 등) ...
export const webtoonTextMeta: WebtoonTextMeta = {
IMG1_BUBBLE: {
textTemplate: `이제 본격적으로 \n{{name}}님의 사주팔자를 \n분석해볼 차례네요.`,
// top, left, bottom, right 중 일부를 사용해 위치 지정
// 예시: IMG1_BUBBLE의 초기 설정 값
// containerBottom: "6%",
// containerLeft: "14%",
// !! 중요: 이때 containerWidth가 정의되지 않았음 !!
sort: "center", // 텍스트 중앙 정렬 의도
},
// ... 다른 텍스트 메타 정보 ...
};
components/UI/Webtoon/WebtoonText.tsx
(핵심 로직 일부)
// ... (imports, props 정의 등) ...
export const WebtoonText = ({ /* ...props... */ }) => {
// ... (변수 할당 로직) ...
let style: React.CSSProperties = {};
if (textKey) {
const bubble = webtoonTextMeta[textKey];
// ... (bubble 데이터 가져오기) ...
const {
// 메타데이터에서 위치 값들을 가져옴
containerTop, containerLeft, containerBottom, containerRight, // 나중에 추가된 속성들
top, left, bottom, right, // 초기에 사용하던 속성들
// !! containerWidth를 가져오지만, webtoonTextMeta에 정의 안되어 있으면 undefined !!
containerWidth,
className: bubbleCls,
sort: metaSort,
} = bubble;
// 💡 문제의 실마리 1: containerWidth가 없으면 style.width가 설정되지 않음!
if (containerWidth) style.width = containerWidth;
// 위치 설정 (top, left 우선)
style.top = containerTop !== undefined ? containerTop : top;
style.left = containerLeft !== undefined ? containerLeft : left;
// ... (right, bottom 처리 로직) ...
if (bubbleCls) {
bubbleClassName = bubbleCls;
}
}
// ... (텍스트 정렬 클래스 생성 로직) ...
// 예: finalSort가 "center"면 textAlignClass = "text-center"
// 💡 문제의 실마리 2: w-fit 이나 명시적 width 없이 absolute 사용
// 초기 버전에서는 combinedClassName = `absolute w-fit ${...}` 였거나,
// 이후 버전에서는 w-fit이 빠졌지만 style.width가 설정되지 않으면 비슷한 효과
const combinedClassName = `absolute ${className} ${bubbleClassName} ${textAlignClass}`;
return (
<div className={combinedClassName} style={style}>
{/* ... 텍스트 렌더링 ... */}
</div>
);
};
초기 문제 상황 요약:
WebtoonText
를 감싸는 div
는 position: absolute;
입니다.
webtoonTextMeta
에 말풍선 텍스트 컨테이너의 너비(containerWidth
)가 명시적으로 정의되어 있지 않았습니다.
style.width
가 설정되지 않은 absolute
요소는 기본적으로 내부 콘텐츠 크기만큼만 너비를 가집니다 (마치 w-fit
을 사용한 것처럼).
- 텍스트는
sort: "center"
(즉, text-align: center
)로 이 "내용물 크기만큼 변하는" 컨테이너 안에서 중앙 정렬되었습니다.
- 컨테이너의 시작 위치는
containerLeft
(또는 containerBottom
등)로 고정되어 있었습니다.
무엇이 문제였을까? "너비 없는 컨테이너"의 함정!
자, 여기서 핵심 문제가 발생합니다.
- 유동적인 컨테이너 너비:
containerWidth
가 없으니, 말풍선 텍스트를 담는 div
의 너비는 텍스트 길이에 따라 고무줄처럼 늘어났다 줄어들었다 했습니다.
- 고정된 시작점, 늘어나는 방향:
containerLeft: "14%"
처럼 컨테이너의 왼쪽 시작점은 고정되어 있었습니다. 텍스트가 길어져서 컨테이너가 넓어져야 한다면? 컨테이너는 오른쪽으로만 쭉~ 늘어납니다.
- 쏠리는 착시:
text-align: center
는 분명히 텍스트를 "컨테이너 내부에서" 중앙 정렬 시킵니다. 하지만 컨테이너 자체가 오른쪽으로 길쭉하게 늘어나 버리니, 사용자가 보기에는 텍스트가 오른쪽으로 쏠린 것처럼 보이는 것이죠!
비유하자면:
- 문제 상황: 한쪽 끝만 벽에 고정된 고무줄(너비가 유동적인 컨테이너) 위에 구슬(텍스트)을 올려놓고 "가운데 있어!"라고 하는 것과 같아요. 고무줄이 길어지면 구슬도 고정된 끝에서 점점 멀어지겠죠?
데스크탑 개발자 도구의 모바일 뷰에서는 이 미세한 너비 변화나 폰트 렌더링 차이가 실제 모바일 기기와 달라서 문제가 잘 드러나지 않았던 것입니다. (물론 viewport
메타 태그 설정도 중요한 기본이지만, 이 경우는 너비 설정이 더 직접적인 원인이었습니다.)
해결책: 컨테이너에 "확실한 너비"를 주자!
해결책은 간단했습니다. 말풍선 텍스트를 담을 컨테이너에 명시적인 너비(containerWidth
)를 설정해주는 것이었죠.
constants/bluemoonladysaju/webtoonTextMeta.ts
(수정 후)
// ... (interface 정의 등) ...
export const webtoonTextMeta: WebtoonTextMeta = {
IMG1_BUBBLE: {
textTemplate: `이제 본격적으로 \n{{name}}님의 사주팔자를 \n분석해볼 차례네요.`,
containerBottom: "6%",
containerLeft: "14%",
containerWidth: "70%", // ✨ 해결의 열쇠! 명시적인 너비 설정 ✨
sort: "center",
},
IMG3_BUBBLE: {
textTemplate: `제가 {{name}}님의 사주를\n보기 쉽게 표로 정리했어요`,
containerTop: "12%",
containerLeft: "11%",
containerWidth: "75%", // ✨ 여기도 너비 설정! ✨
sort: "center",
},
// ...
};
WebtoonText.tsx
컴포넌트는 이전 코드에서 if (containerWidth) style.width = containerWidth;
부분이 이미 있었기 때문에, webtoonTextMeta.ts
파일만 수정해도 style.width
가 올바르게 적용됩니다.
왜 해결되었을까요?
- 고정된 컨테이너 너비: 이제
containerWidth: "70%"
(예시)처럼 설정해주니, 말풍선 텍스트를 담는 div
는 부모 요소 너비의 70%라는 고정된 너비를 갖게 됩니다.
- 예측 가능한 정렬:
text-align: center
는 이 "너비가 고정된" 컨테이너 안에서 텍스트를 정확하게 중앙으로 정렬합니다. 컨테이너 자체가 한쪽으로 늘어나지 않으니 텍스트가 쏠려 보일 일도 없습니다.
비유하자면:
- 해결된 상황: 길이가 정해진 나무판자(너비가 고정된 컨테이너)를 벽의 특정 위치에 단단히 고정하고, 그 위에 구슬(텍스트)을 놓으니 항상 가운데에 잘 있는 것과 같습니다.
추가적으로 확인했던 사항들 (좋은 습관!)
viewport
메타 태그: HTML <head>
에 <meta name="viewport" content="width=device-width, initial-scale=1.0">
설정은 모바일 웹 개발의 기본 중의 기본이죠. 꼭 확인해야 합니다! (Next.js의 경우 pages/_document.tsx
또는 app/layout.tsx
에서 설정)
- 실제 기기 디버깅: 브라우저 개발자 도구만 믿지 말고, 실제 모바일 기기를 연결해서 디버깅(Chrome DevTools, Safari Web Inspector)하는 것이 가장 확실합니다.
마무리
CSS 레이아웃, 특히 position: absolute
를 사용할 때는 요소의 크기(너비, 높이)를 명확히 지정하는 것이 얼마나 중요한지 다시 한번 깨닫게 된 경험이었습니다. 혹시 비슷한 문제를 겪고 계신 분이 있다면, 가장 먼저 요소의 너비가 제대로 설정되어 있는지 확인해보시길 바랍니다!
오늘의 삽질(?) 기록이 여러분께 조금이나마 도움이 되었으면 좋겠습니다. 즐거운 코딩 하세요!