Frontend 09
스타일 미적용, 어디서부터 문제일까? CSS 연결 오류 완전 분석
코드를 따라 쳤는데 스타일이 안 먹을 때 파일 경로, 선택자, 클래스 이름 중 어디를 봐야 할지 모르겠다.
근거 · 교안 p29-p34
1장: 파일 연결: 어디서부터 시작해야 할까?
1장. 파일 연결: 어디서부터 시작해야 할까?
솔라의 손가락이 키보드 위에서 멈췄다. 모니터 양쪽에 나란히 띄워진 두 개의 창, index.html과 style.css 파일의 내용을 몇 번이고 번갈아 훑었다. 분명 온라인 강의에서 보여준 코드와 글자 하나 다르지 않게 입력했다. 깔끔하게 정리된 게시판 목록이 눈앞에 펼쳐질 순간을 기대하며, 솔라는 브라우저 창을 새로고침했다.
하지만 화면에 나타난 것은 1990년대의 유물 같은 밋밋한 텍스트뿐이었다. 검은 글씨, 하얀 배경, 아무런 꾸밈도 없는 날것의 HTML.
“아니, 왜…?”
다시 코드 편집기로 돌아가 오타를 찾았다. <div>, <header>, <ul>, <li>… HTML 구조는 완벽했다. CSS 파일의 background-color, font-family, text-align 속성도 여러 번 확인했다. 잘못된 부분은 어디에도 없어 보였다. 코드를 정확히 입력하면 당연히 스타일이 적용될 거라고 믿었는데, 눈앞의 결과는 그 믿음을 산산조각 내고 있었다. 맥이 탁 풀린 솔라는 노트북을 들고 거실로 나갔다.
“언니, 나 좀 도와줘. 이거 봐. 똑같이 쳤는데 왜 아무것도 안 바뀌지? 원래는 회색 배경에 깔끔한 목록이 나와야 한단 말이야.”
소파에 앉아 책을 읽던 루나가 고개를 들었다. 솔라가 건넨 노트북 화면을 잠시 들여다보던 루나는 코드 편집기와 브라우저 창을 차례로 가리켰다.
“왼쪽은 설계도(index.html)고, 오른쪽은 디자인 시안(style.css)이네. 그런데 브라우저가 이 둘이 서로 짝이라는 걸 어떻게 알 수 있을까?”
“응? 그야 여기… 이렇게 연결하라고 해서 썼는데.”
솔라는 index.html 파일의 <head> 태그 안을 가리켰다. 거기에는 <link rel="stylesheet" href="style.css">라는 코드가 적혀 있었다. 솔라의 목소리에는 ‘시키는 대로 다 했는데 왜 안 되냐’는 억울함이 묻어났다.
루나는 잠자코 고개를 끄덕였다. “맞아. 바로 그 href 속성이 두 파일을 이어주는 열쇠야. style.css라는 디자인 시안이 어디 있는지 알려주는 주소 같은 거지.”
루나는 잠시 생각하더니 솔라에게 제안했다.
“우리, 작은 실험 하나만 해볼까? 그 주소를 일부러 틀리게 바꿔보는 거야. href 안에 있는 파일 이름을 styles.css처럼, s를 하나 붙여서 저장해봐.”
솔라는 고개를 갸웃거리며 시키는 대로 했다. href="styles.css". 파일을 저장하고 브라우저를 새로고침했다. 화면은 여전히 요지부동. 밋밋한 텍스트가 그대로였다.
“똑같아. 역시 아무것도 안 바뀌는데?”
“그럼 이번엔 다시 원래대로, 정확한 이름으로 고쳐볼래? style.css라고.”
솔라는 다시 href="style.css"로 코드를 되돌리고 저장했다. 그리고 무심코 브라우저를 새로고침하는 순간, 솔라의 눈이 동그래졌다.
마법처럼 화면이 바뀌었다. 칙칙했던 배경에 은은한 회색이 깔리고, 제멋대로 뻗어 있던 글자들이 깔끔하게 정렬되었다. 방금 전까지 코드 속에만 갇혀 있던 디자인이 비로소 제 모습을 드러낸 것이다.
“어…!”
솔라는 방금 자신이 고친 코드 한 줄과 눈앞의 결과물을 번갈아 보았다. 그제야 깨달았다. 자신이 처음 작성했던 코드에 미세한 오타가 있었음을. 아마도 stlye.css 나 style.cs 같은, 눈치채기 어려운 실수였을 것이다.
“아! href… 이 주소가 틀렸던 거구나. 나는 코드의 다른 내용만 다 맞게 쓰면 되는 줄 알았어. 이 HTML 파일이 CSS 파일을 ‘찾아가는 길’을 정확하게 알려줘야만 하는 거였네.”
단순히 ‘연결한다’는 추상적인 개념이 아니었다. HTML 문서가 외부의 CSS 파일을 불러오기 위해서는, 정확한 ‘경로’를 알려주어야만 한다는 사실을 몸으로 깨닫는 순간이었다. 설계도에 디자인 시안의 정확한 위치를 명시하지 않으면, 인테리어 업자가 길을 헤매는 것과 같은 이치였다.
뿌듯함에 미소가 번지는 것도 잠시, 솔라의 시선이 화면 한구석에 머물렀다. 전체적인 배경과 글꼴은 바뀌었지만, 유독 게시판의 ‘글쓰기’ 버튼 하나만은 여전히 투박한 기본 모양 그대로였다.
“그런데 언니, 이제 전체적으로 스타일이 적용되긴 하는데… 저 버튼은 왜 아직도 그대로지? 파일은 이제 제대로 연결됐잖아.”
2장: 선택자 불일치: 내가 원하는 곳에 왜 스타일이 안 갈까?
솔라의 노트북 화면은 이전과 사뭇 달랐다. 밋밋했던 흰 배경 대신 은은한 회색이 깔렸고, 제멋대로 흩어져 있던 글자들은 가지런히 정렬되어 있었다. 파일 연결이라는 첫 관문을 통과한 대가였다. 하지만 솔라의 시선은 한 곳에 고정된 채 움직일 줄 몰랐다. 화면 오른쪽 아래, 여전히 투박한 기본 모양을 고수하고 있는 ‘글쓰기’ 버튼이었다.
솔라는 코드 편집기 창을 반으로 나눠, 왼쪽에는 index.html 파일의 버튼 코드를, 오른쪽에는 style.css 파일에서 해당 버튼을 꾸미기 위해 작성한 코드를 띄워 놓았다.
HTML: <button class="write-btn">글쓰기</button>
CSS: .write-button { background-color: #4a90e2; color: white; ... }
몇 번을 봐도 이상한 점을 찾을 수 없었다. ‘write-btn’, ‘write-button’. 둘 다 ‘글쓰기 버튼’이라는 뜻이 명확했다. 이 정도면 컴퓨터가 알아서 이해해 줘야 하는 것 아닌가? 답답한 마음에 솔라는 두 코드 조각을 번갈아 클릭하며 한숨을 내쉬었다.
“파일이라는 큰 집은 잘 찾아갔는데, 그 안에서 어느 방에 들어가야 할지 길을 잃었나 보네.”
어느새 다가온 루나가 솔라의 화면을 보며 말했다. 비유는 이어지고 있었다. 어제는 집 주소를 잘못 찾아갔다면, 오늘은 일단 집 안에는 들어왔지만, 호수를 몰라 헤매는 상황이라는 뜻이었다.
“언니, 이거 봐. CSS에서 .write-button이라고 스타일을 만들었어. 그리고 HTML 버튼에는 write-btn이라는 클래스 이름을 붙여줬고. ‘글쓰기 버튼’이라는 뜻인데 왜 못 알아듣는 걸까?”
솔라의 목소리에는 진심 어린 의문이 담겨 있었다. 그녀는 CSS가 마치 사람처럼 문맥을 파악하고 의미를 이해해 주길 기대하고 있었다. 대충 비슷하면 찰떡같이 알아들을 거라고 믿었던 것이다.
루나는 말없이 고개를 끄덕였다. 그리고는 솔라의 키보드를 가리키며 제안했다.
“그럼, 브라우저가 정말 못 알아듣는지 직접 확인해 볼까? 브라우저에서 F12 키 한번 눌러볼래?”
솔라가 F12 키를 누르자, 화면 한쪽에 복잡해 보이는 개발자 도구 창이 열렸다. 루나는 당황하는 솔라를 대신해 마우스를 잡고, 개발자 도구의 왼쪽 위 아이콘을 클릭한 뒤, 화면의 ‘글쓰기’ 버튼을 클릭했다. 그러자 개발자 도구 창에 해당 버튼의 HTML 코드가 하이라이트되고, 오른쪽에는 그 요소에 적용된 CSS 스타일 목록이 나타났다.
“이쪽이 이 버튼에 실제로 적용된 스타일 목록이야. 우리가 style.css에 적어둔 다른 스타일들은 보이는데, 정작 .write-button은 목록에 없지?”
루나의 말대로였다. body나 다른 요소에 적용된 스타일은 있었지만, 정작 버튼을 위해 만든 스타일은 코빼기도 보이지 않았다. 브라우저는 .write-button이라는 존재 자체를 모르는 것 같았다.
“자, 그럼 실험을 하나 해보자. 어차피 안되고 있으니 더 망가질 것도 없잖아?” 루나가 장난스럽게 말했다. “CSS 파일에 있는 .write-button을, HTML에 있는 이름과 똑같이 .write-btn으로 바꿔봐. 점(.) 바로 뒤에 오는 이름만.”
솔라는 반신반의하며 CSS 파일의 코드를 수정했다.
.write-button -> .write-btn
그리고 파일을 저장하는 순간, 거짓말처럼 기적 같은 일이 일어났다. 솔라의 시선 끝에 머물러 있던 투박한 회색 버튼이 눈 깜짝할 사이에 세련된 파란색 버튼으로 변신했다. 개발자 도구의 스타일 목록에도 방금 전까지 보이지 않던 .write-btn 스타일이 당당하게 자리를 차지하고 있었다.
“우와! 됐어! 이름이… 글자 하나까지 완벽하게 똑같아야 했던 거구나!”
솔라는 짧은 탄성을 내뱉었다. 그녀는 그제야 깨달았다. HTML과 CSS의 관계는 의미를 해석하는 인간 사이의 소통이 아니었다. 수많은 이름표가 붙은 방들 사이에서, CSS라는 우편배달부가 ‘정확히 일치하는 이름표’가 붙은 방을 찾아 디자인 시안을 전달하는, 기계적이고 엄격한 약속이었던 것이다. ‘write-button’과 ‘write-btn’은 사람에게는 비슷한 의미일지 몰라도, 컴퓨터에게는 ‘서울’과 ‘서율’처럼 전혀 다른 주소일 뿐이었다.
“맞아. 파일 경로가 집 주소였다면, 이건 각 방에 붙어있는 이름표인 셈이야. 이름표가 정확히 일치하지 않으면, 배달부는 문 앞을 그냥 지나쳐 버리는 거지.”
스스로 문제를 진단하고 해결했다는 성취감에 솔라의 얼굴이 환해졌다. 이제 어떤 스타일이 적용되지 않을 때, 파일 경로 다음으로 무엇을 확인해야 할지 명확해졌다. 바로 이름표, 즉 선택자와 클래스 이름의 ‘완벽한 일치’ 여부였다.
자신감이 붙은 솔라는 헤더 영역의 제목을 좀 더 꾸며보기로 했다. 이번에는 실수 없이, header 태그를 가리키는 선택자를 정확히 사용해 글자 크기를 키웠다. 성공이었다. 이어서 헤더 안의 특정 링크(<a>) 하나만 색을 바꾸고 싶었다. 배운 대로 링크에 class="special-link"를 추가하고, CSS 파일에 .special-link { color: red; }라고 적었다. 하지만 어찌 된 일인지 링크는 여전히 브라우저의 기본 파란색을 고집하고 있었다.
“어? 이상하다. 이번엔 파일 경로도 맞고, 이름표도 정확히 맞춰줬는데… 왜 이 링크는 내 말을 안 듣는 거지? 혹시 다른 더 힘센 규칙이라도 있는 건가?“
3장: HTML 구조와 CSS 우선순위: 왜 내 마음대로 안 될까?
자신감이 붙은 솔라의 손가락이 경쾌하게 움직였다. 파일 경로 오류를 잡고, 클래스 이름 오타까지 바로잡으니 웹페이지는 제법 그럴듯한 게시판의 모습을 갖춰가고 있었다. 이제 남은 건 세부적인 디자인을 다듬는 일이었다. 솔라는 게시판 상단 헤더 영역에 있는 ‘새소식’ 링크 하나를 강조하고 싶었다.
코드 편집기에서 해당 링크에 class="special-link"라는 특별한 이름표를 달아주었다. 그리고 CSS 파일에는 그 이름표를 가진 요소를 위한 새로운 규칙을 추가했다. 이번에는 오타가 없도록 몇 번이고 확인했다.
HTML (index.html):
<header>
<h1>게시판</h1>
<a href="/news" class="special-link">새소식</a>
</header>
CSS (style.css):
/* 방금 추가한 규칙 */
.special-link {
color: red;
font-weight: bold;
}
파일 경로도 정확하고, 이름표도 완벽하게 일치했다. 솔라는 의기양양하게 브라우저를 새로고침했다. 하지만 어찌 된 일인지 ‘새소식’ 링크는 여전히 다른 링크들과 똑같은 파란색을 띠고 있었다. 굵게 표시되지도 않았다. 마치 솔라가 정성껏 만든 .special-link 규칙을 보란 듯이 무시하는 것 같았다.
“어? 이상하다. 이번엔 진짜 다 맞는데… 파일도 연결했고, 이름표도 정확히 special-link로 맞춰줬는데 왜 안 바뀌지?”
솔라는 지난번처럼 개발자 도구를 열어 ‘새소식’ 링크를 검사했다. 스타일 목록을 보니 기가 막힌 상황이 펼쳐져 있었다. 목록에 분명 .special-link 규칙이 보였다. 하지만 color: red;와 font-weight: bold; 속성 옆에는 가느다란 취소선이 그어져 있었다. 브라우저가 이 규칙을 인지했지만, 어떤 이유로 적용하지 않기로 ‘결정’했다는 뜻이었다.
“이건 또 뭐야… 내 말을 알아듣긴 했는데, 그냥 묵살해버린 거잖아. 혹시 더 힘센 규칙이라도 있는 건가?”
솔라의 중얼거림에, 어느새 다가온 루나가 화면을 들여다보며 말했다. “디자인 시안을 보냈는데, 현장 소장이 ‘이건 다른 지시가 있어서 안 됩니다’하고 거절한 셈이네.”
루나는 복잡한 코드 대신, 솔라의 책상 위에 있던 빈 메모지와 펜을 가져왔다. 그리고 간단한 상자 두 개를 그렸다. 큰 상자 안에 작은 상자가 들어있는 그림이었다.
“이 바깥 상자가 부모(parent), 안쪽 상자가 자식(child)이라고 해보자. 만약 우리가 ‘바깥 상자 안에 있는 모든 글씨는 파란색으로 칠하라’고 지시하면 어떻게 될까?”
루나는 바깥 상자 옆에 color: blue;라고 적었다.
“음… 안에 있는 글씨도 파란색이 되겠지? 부모의 특징을 물려받을 테니까.” 솔라가 답했다.
“맞아. 그걸 ‘상속’이라고 불러. 그런데 만약, 우리가 지시를 하나 더 추가하는 거야. ‘안쪽 상자 안의 글씨는 빨간색으로 칠하라’고.”
루나는 안쪽 상자 옆에 color: red;를 추가로 적었다.
“그럼… 안쪽 상자는 빨간색이 되겠지. 더 구체적인 지시니까.”
“바로 그거야.” 루나가 펜 끝으로 안쪽 상자를 톡톡 두드렸다. “CSS 세계에는 보이지 않는 힘의 서열, 즉 ‘우선순위’가 있어. ‘모든 글씨’보다는 ‘안쪽 상자의 글씨’라는 지시가 더 구체적이고, 그래서 더 힘이 세. 브라우저는 더 힘센 지시를 따르는 거야.”
루나는 다시 솔라의 노트북 화면을 가리켰다. 취소선이 그어진 .special-link 스타일과, 그 위에서 멀쩡하게 적용되고 있는 다른 스타일을 비교해 보여주었다.
“솔라 네가 만든 .special-link라는 규칙보다 더 구체적이거나, 더 중요하다고 약속된 규칙이 이미 존재했던 거야. 예를 들어, 브라우저가 기본적으로 모든 링크(<a> 태그)에 적용하는 스타일은 생각보다 힘이 세. 그냥 상속받은 색상 정도는 가볍게 무시해버리지.”
솔라는 그제야 깨달았다. 자신이 만든 규칙이 무시당한 것은 오타나 연결 실수가 아니었다. 더 강력한 규칙과의 힘겨루기에서 밀렸기 때문이었다. 모든 CSS 규칙이 평등한 힘을 갖는다고 생각했던 것은 순진한 착각이었다. HTML의 구조, 즉 어떤 요소가 다른 요소를 포함하는지와 어떤 종류의 선택자를 사용했는지에 따라 규칙의 ‘계급’이 달라지는 것이었다.
“그럼 내 지시가 이기게 하려면 어떻게 해야 해?”
“더 구체적으로 만들어주면 돼. ‘어디에 있는 어떤 링크’인지 명확하게. 예를 들어, 그냥 .special-link라고 하는 것보다 ‘헤더(header) 안에 있는, special-link 클래스를 가진 링크(a)‘라고 콕 집어서 말해주는 거지.”
루나의 조언에 따라 솔라는 CSS 코드를 수정했다.
header a.special-link { color: red; font-weight: bold; }
.special-link 앞에 header a를 덧붙여, 규칙을 훨씬 더 구체적으로 만들었다. 파일을 저장하고 브라우저를 새로고침하자, 드디어 ‘새소식’ 링크가 선명한 붉은색으로, 그리고 굵은 글씨로 바뀌었다. 개발자 도구에서도 더 이상 취소선은 보이지 않았다. 더 강력해진 솔라의 규칙이 마침내 우선순위 싸움에서 이긴 것이다.
“됐어! 이제 알겠다. HTML 구조랑 CSS 규칙이 그냥 ‘연결’만 되면 끝이 아니었어. 누가 더 센지, 이 구조 안에서 누구 말이 더 중요한지를 따져야 하는 거였구나.”
단순히 코드를 나열하는 것이 아니라, 힘의 균형을 맞추는 섬세한 작업이라는 것을 깨달은 순간이었다. 뿌듯해진 솔라는 방금 수정한 코드를 깔끔하게 정리하고 저장했다. 그리고 마지막으로 확인하기 위해 브라우저 창을 새로고침했다.
그런데 화면은 조금 전의 모습 그대로였다. 분명 방금 전까지 빨갛게 빛나던 ‘새소식’ 링크가 다시 파란색으로 돌아와 있었다. 코드를 잘못 건드렸나 싶어 편집기를 확인했지만, 코드는 완벽했다.
“어? 아니, 왜 다시 돌아왔지? 나 분명히 저장했는데… 왜 브라우저는 방금 내가 고친 코드를 못 본 척하는 것 같지?“
4장: 브라우저 캐시: 내가 바꾼 코드를 브라우저는 왜 모를까?
솔라의 손가락이 F5 키 위에 맴돌았다. 벌써 네 번째, 아니 다섯 번째 시도였다. 화면 왼쪽의 코드 편집기에는 분명히 header a.special-link { color: red; }라는 규칙이 선명하게 살아 있었다. 방금 전 우선순위 싸움에서 극적인 승리를 거둔, 자신이 직접 더 강력하게 만든 코드였다. 하지만 화면 오른쪽의 브라우저는 그 사실을 모르는 척, ‘새소식’ 링크를 여전히 고집스러운 파란색으로 보여주고 있었다.
솔라는 혹시나 하는 마음에 코드를 다시 수정했다. color: red;를 color: lime;으로 바꿨다. 눈에 확 띄는 루나색. 저장(Ctrl+S) 키를 누르고, 다시 브라우저로 돌아와 새로고침(F5) 키를 눌렀다. 화면이 하얗게 깜빡이더니, 원래의 모습으로 돌아왔다. 파란색 링크는 조금도 변하지 않았다. 파일 경로도, 선택자 이름도, 우선순위도 모두 확인했다. 코드 자체에는 이제 아무런 문제가 없다고 확신했다. 그런데 왜 브라우저는 이 완벽한 코드를 반영하지 못하는 걸까? 마치 솔라가 보낸 최신 디자인 시안을 잃어버리고, 어딘가 서랍 속에 처박혀 있던 옛날 시안을 고집스럽게 꺼내보는 현장 작업자 같았다.
“너 지금 나랑 장난해?”
혼잣말과 함께 마우스 커서가 브라우저의 새로고침 버튼 위를 불안하게 맴돌았다. 그때 거실을 지나가던 루나가 솔라의 중얼거림을 듣고 다가왔다.
“시안을 최신 버전으로 보냈는데, 현장에서 자꾸 예전 버전으로 작업하고 있는 건가?”
루나는 이전의 비유를 자연스럽게 이어받으며 솔라의 노트북 화면을 들여다보았다. 솔라는 기다렸다는 듯이 하소연했다.
“언니, 이거 봐. 분명히 코드를 고치고 저장했어. 그런데 아무리 새로고침을 해도 바뀌지가 않아. 아까 빨간색으로 성공했던 것도 다시 파란색으로 돌아왔어. 개발자 도구를 봐도 내 새 코드는 없고, 취소선 그어졌던 옛날 상태만 보여. 브라우저가 내 style.css 파일의 옛날 모습을 기억하고 있는 것 같아.”
솔라의 추측은 정확했다. 그녀는 ‘브라우저가 항상 최신 코드를 보여줄 것’이라는 믿음이 깨지는 순간을 목격하고 있었다. 새로고침 버튼은 당연히 모든 것을 새로 불러오는 만능 열쇠일 거라고 생각했는데, 그게 아니었던 것이다.
“솔라 말이 맞아. 브라우저는 똑똑하면서도 가끔은 게으르거든. 한번 방문했던 웹사이트는 구성 요소들을 컴퓨터에 잠시 저장해 둬. 다음에 또 방문할 때 일일이 새로 다운로드하지 않고, 저장해둔 걸 꺼내 쓰면 훨씬 빠르니까. 그 임시 저장소를 ‘캐시(Cache)‘라고 불러.”
루나는 솔라의 코드 편집기에서 style.css 파일을 가리켰다.
“브라우저는 네가 수정한 style.css 파일도 ‘어? 이 파일 이름, 아까 내가 저장해둔 거랑 같네? 그럼 그거 그냥 써야지!’ 하고 옛날 파일을 꺼내 쓰는 거야. 네가 아무리 새로고침 버튼을 눌러도, 브라우저는 ‘괜찮아, 나한테 이미 있어’라고 대답하는 셈이지.”
“그럼 어떡해? 내가 코드를 바꿨다는 걸 어떻게 알려주지?”
“게으른 직원을 단호하게 다루는 방법이 필요하지.” 루나가 웃으며 말했다. “우리, 브라우저가 무시할 수 없는 명령을 내려보자. CSS 파일에서 배경색을 아주 눈에 띄는 색으로 바꿔볼래? 예를 들면 노란색으로.”
솔라는 지시에 따라 body 태그의 배경색을 background-color: yellow;로 수정하고 저장했다.
“좋아. 이제 평소처럼 새로고침 해봐.”
솔라가 F5 키를 눌렀다. 화면이 깜빡였지만, 배경은 여전히 은은한 회색이었고 링크는 파란색이었다. 역시나였다.
“자, 이제 진짜 명령이야. 키보드의 Ctrl 키랑 Shift 키를 누른 상태에서, R 키를 한번 눌러봐.”
솔라는 어리둥절한 표정으로 세 개의 키를 동시에 눌렀다. Ctrl + Shift + R.
순간, 브라우저 화면이 번쩍이며 완전히 새로운 모습으로 탈바꿈했다. 화면 전체가 눈부신 노란색으로 바뀌었고, 그토록 애태우던 ‘새소식’ 링크는 마침내 선명한 붉은색을 뽐내고 있었다. 방금 전까지 솔라를 무시하던 브라우저가 정신을 차리고 최신 디자인 시안을 허겁지겁 적용한 것 같았다.
“우와! 됐어! 이게 뭐야?”
“그게 바로 ‘강제 새로고침’이야. 브라우저에게 ‘네가 저장해 둔 캐시 따위는 무시하고, 서버에서 모든 파일을 무조건 새로 다 가져와!’라고 강력하게 명령하는 거지.”
솔라는 그제야 모든 조각이 맞춰지는 것을 느꼈다. 자신이 겪었던 문제는 코드의 오류가 아니었다. 속도를 위해 효율성을 추구하는 브라우저의 ‘캐시’라는 영리한 기능이 빚어낸, 흔한 부작용이었던 것이다. 그리고 자신은 그 부작용을 해결할 강력한 무기, 강제 새로고침이라는 도구를 손에 넣었다.
“아, 이제 알겠다. HTML 파일이랑 CSS 파일이 ‘제대로 연결’된다는 건, 파일 경로, 이름표, 우선순위뿐만 아니라, 브라우저가 이 파일의 ‘최신 버전’을 보고 있는지도 확인해야 한다는 뜻이었구나.”
이제 솔라의 머릿속에는 스타일 문제가 발생했을 때 점검할 자신만의 목록이 생겼다.
- 파일 경로가 틀렸나? (
href확인) - 이름표가 다른가? (
class이름 확인) - 힘 싸움에서 졌나? (우선순위 확인)
- 브라우저가 옛날 걸 기억하나? (강제 새로고침)
든든한 무기들을 얻은 것 같아 뿌듯해진 솔라는 문득 궁금해졌다.
“언니, 그럼 이제 문제가 생기면 이 네 가지를 순서대로 확인해보면 되는 거야? 아니면… 이것들을 더 똑똑하게 점검하는 방법 같은 게 있을까?“
5장: 스타일 문제 진단 흐름: 놓친 스타일, 이제 스스로 찾아내자!
5장. 스타일 문제 진단 흐름: 놓친 스타일, 이제 스스로 찾아내자!
솔라의 책상 위, 노트북 옆에는 깔끔하게 정리된 메모 한 장이 놓여 있었다. 지난 며칠간의 고군분투 끝에 얻어낸 값진 전리품이었다.
<스타일 문제 점검 목록>
- 파일 경로 (
href) 확인- 이름표 (
class) 일치 확인- 우선순위 (힘겨루기) 확인
- 캐시 (강제 새로고침) 확인
이 네 가지 무기만 있으면 어떤 스타일 문제든 해결할 수 있을 것 같았다. 하지만 솔라의 마음 한구석에는 풀리지 않은 질문이 남아 있었다. 문제가 생길 때마다 이 목록을 1번부터 4번까지 순서대로 다 점검해야 하는 걸까? 마치 열쇠 꾸러미를 들고 맞는 열쇠가 나올 때까지 하나씩 다 꽂아보는 것처럼, 어딘가 비효율적으로 느껴졌다.
솔라의 고민을 읽기라도 한 듯, 옆에서 지켜보던 루나가 솔라의 메모를 들어 올렸다. 그리고는 빈 종이에 새로운 그림을 그리기 시작했다. 네 개의 점검 항목을 두 개의 그룹으로 나누고, 화살표로 연결한 간단한 순서도였다.
“솔라 네가 만든 이 목록, 아주 훌륭한 무기들이야. 하지만 전쟁터에 나갈 땐 무기를 어느 순서로 꺼내 쓸지 전략을 짜는 게 더 중요하지.”
루나가 그린 순서도는 솔라의 목록과는 시작부터 달랐다. 가장 위에는 질문이 하나 적혀 있었다.
Q. 문제의 범위가 어떻게 되지?
A. 전체적으로? / B. 부분적으로?
“언니, 이건…”
솔라는 루나의 순서도와 자신의 목록을 번갈아 보았다. 자신의 목록이 문제의 원인들을 나열한 것이라면, 루나의 순서도는 원인을 찾아가기 위한 ‘질문’으로 시작하고 있었다.
“우리 가상의 시나리오로 이 순서도를 한번 따라가 볼까? 네가 게시글 상세 페이지를 만들고 있는데, ‘수정하기’ 버튼만 배경색이 파란색으로 바뀌지 않는다고 상상해봐. 어디서부터 시작해야 할까?”
솔라는 잠시 생각에 잠겼다. 예전 같았으면 ‘파일 경로가 또 틀렸나?’ 하고 href부터 확인했을 것이다. 하지만 루나의 순서도는 다른 길을 제시하고 있었다.
“음… 일단 첫 번째 질문에 답해야겠네. ‘수정하기’ 버튼 하나만 문제니까, ‘B. 부분적으로’가 맞아.”
솔라가 답하자, 루나는 순서도의 ‘B’에서 뻗어 나가는 다음 단계를 손가락으로 가리켰다. 거기에는 솔라의 목록에 있던 2번과 3번 항목이 나란히 놓여 있었다.
- 이름표가 다른가? (
.class이름 확인) - 힘 싸움에서 졌나? (우선순위 확인)
“맞아. 페이지의 다른 부분은 스타일이 잘 적용되는데 특정 요소 하나만 말썽이라면, CSS 파일 자체는 잘 연결되었다는 뜻이야. 그러니 1번(파일 경로)이나 4번(캐시) 문제일 가능성은 거의 없지. 집 주소는 제대로 찾아왔는데, 특정 방의 인테리어만 잘못된 상황이니까. 그 방의 ‘이름표’가 잘못 붙었거나, 더 힘센 지시가 내려온 건 아닌지 확인하는 게 가장 빠른 길이야.”
“아…! 그렇구나. 만약에 페이지 전체가 밋밋하게 나온다면?”
“그때는 ‘A. 전체적으로’를 따라가면 돼.”
루나는 순서도의 반대편 가지를 가리켰다.
- 브라우저가 옛날 걸 기억하나? (강제 새로고침)
- 파일 경로가 틀렸나? (
href확인)
“디자인 시안 자체가 도착하지 않은 상황이니까, 배달부(브라우저)가 옛날 시안을 들고 있거나, 아예 집 주소를 잘못 찾아간 경우를 먼저 의심해야지.”
그제야 솔라의 머릿속에 흩어져 있던 네 개의 점검 항목들이 제자리를 찾아갔다. 이것들은 무작위로 시험해보는 시행착오의 목록이 아니었다. 문제의 현상을 보고 원인의 범위를 좁혀나가는 체계적인 진단 과정이었던 것이다. 스타일 문제 해결은 막연한 감이 아니라, 명확한 논리에 따라 움직이는 탐정의 수사와 같았다.
솔라는 루나의 순서도를 바탕으로 자신만의 ‘진단 체크리스트’를 만들기 시작했다. 그냥 베껴 적는 것이 아니었다. 각 단계에서 무엇을 확인해야 하는지, 어떤 도구(개발자 도구)를 써야 하는지를 자신의 언어로 꼼꼼하게 정리했다.
CSS 스타일 문제 진단 체크리스트 (솔라 Ver.)
STEP 1. 문제 현상 파악: 범위는?
- 전체 스타일이 적용되지 않는다. ➡️ STEP 2-A 로 이동
- 일부 요소만 스타일이 적용되지 않는다. ➡️ STEP 2-B 로 이동
STEP 2-A. 파일 단위 문제 진단
- 강제 새로고침 (
Ctrl+Shift+R): 브라우저가 옛날 파일을 기억하는지 확인. - 파일 경로 확인: HTML
<link>태그의href속성이 CSS 파일의 위치를 정확히 가리키는지 확인. (오타 주의!)
STEP 2-B. 요소 단위 문제 진단 (개발자 도구 F12 필수!)
- 선택자 일치 확인: HTML 요소의
class또는id이름과 CSS 선택자 이름이 글자 하나까지 완벽하게 일치하는지 확인. - 우선순위 확인: 개발자 도구의 스타일 탭에서 원하는 속성에 취소선이 그어져 있는지 확인. 그렇다면, 더 구체적인 선택자를 사용해 규칙의 힘을 키워준다. (예:
.my-btn➡️header .my-btn)
자신이 직접 완성한 체크리스트를 바라보는 솔라의 얼굴에 자신감 있는 미소가 번졌다. 이제는 길을 잃어도 헤매지 않을 수 있다는 확신이 들었다. 막연한 두려움 대신, 해결할 수 있는 문제라는 믿음이 생겼다.
“이제 알겠다. HTML 파일과 CSS 파일이 ‘제대로 연결된다’는 건, 단순히 코드 몇 줄을 맞추는 게 아니었어. 파일의 위치, 요소의 이름, 규칙의 힘, 그리고 브라우저의 기억까지, 이 모든 관계가 올바르게 작동해야 비로소 완성되는 거였구나.”
솔라는 자신의 정적 게시판 프로젝트 폴더를 열었다. 그리고 index.html과 style.css 파일 옆에, troubleshooting.md 라는 새 파일을 만들었다. 방금 완성한 체크리스트를 그 파일 안에 고스란히 옮겨 적었다. 이것은 이제 루나의 조언이 아닌, 온전히 자신의 경험과 깨달음으로 얻어낸 문제 해결 지도였다. 앞으로 어떤 스타일이 길을 잃고 헤매더라도, 이 지도를 펼쳐 차근차근 길을 찾아줄 수 있을 터였다.