이제는 퍼즐 조각을 맞출 시간입니다. 선택자, 박스 모델, Flexbox, 반응형, 디버깅을 따로따로 배울 때는 각각이 작은 기술처럼 보였지만, 프로젝트 하나를 끝까지 만들면 이 조각들이 한 화면 안에서 어떻게 연결되는지 분명하게 보입니다. 프로필 페이지는 사진, 소개, 버튼, 통계, 링크처럼 자주 쓰는 요소가 한 번에 들어가서 초보자가 연습하기에 아주 좋은 주제입니다.12
이번 챕터에서는 깔끔한 프로필 페이지를 직접 만들면서, 지금까지 배운 CSS를 실제 결과물로 묶어 보겠습니다. 중요한 것은 화려함보다 완성입니다. 작은 프로젝트 하나를 끝내는 경험이 실력을 가장 빨리 단단하게 만듭니다.

이번 프로젝트에서 만들 페이지
이번에 만들 페이지는 한 사람의 정보를 보기 좋게 보여 주는 단일 프로필 페이지입니다.
상단 커버 영역과 프로필 사진
이름, 한 줄 소개, 버튼, 통계 정보
소개 카드, 기술 태그, 링크, 최근 활동 영역
모바일 화면에서는 세로로 자연스럽게 정리되는 반응형 레이아웃
완성하고 나면 단순히 예쁜 카드 하나를 만든 것이 아니라, 실제로 자주 쓰이는 UI 조합을 스스로 만들 수 있게 됩니다.
먼저 결과를 머릿속에 그려보자
레이아웃을 만들기 전에 화면을 말로 먼저 그려 보면 CSS가 훨씬 쉬워집니다. 페이지 전체 가운데에 큰 카드가 하나 있고, 카드 맨 위에는 그라데이션 커버가 있습니다. 그 아래에는 둥근 프로필 사진이 살짝 겹쳐 올라오고, 옆에는 이름과 소개가 놓입니다. 그 다음 줄에는 버튼 두 개가 있고, 그 아래에는 숫자 통계가 가로로 정렬됩니다. 마지막으로 본문 영역에서는 소개, 기술, 링크, 활동 카드가 배치됩니다.
핵심은 두 가지입니다. 첫째, 큰 덩어리는 Flexbox로 나눕니다. 둘째, 각 카드의 안쪽 간격과 바깥 간격은 박스 모델로 다룹니다. 이 두 가지만 잘 잡아도 페이지가 갑자기 그럴듯해집니다.
HTML 구조 만들기
먼저 index.html 파일을 만들고 아래 코드를 넣어 보세요. 구조를 읽기 쉽게 나누는 것이 중요합니다. 클래스 이름도 역할이 보이도록 지었습니다.
<!DOCTYPE html>
<html lang='ko'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>프로필 페이지</title>
<link rel='stylesheet' href='style.css'>
</head>
<body>
<div class='page'>
<div class='profile-card'>
<div class='cover'></div>
<header class='profile-header'>
<img
class='avatar'
src='https://via.placeholder.com/160'
alt='김초보 프로필 사진'
>
<div class='profile-meta'>
<p class='badge'>Front-end Beginner</p>
<h1>김초보</h1>
<p class='intro'>
HTML과 CSS로 보기 좋은 화면을 만드는 연습을 하고 있습니다.
</p>
<p class='location'>📍 서울 · 매일 조금씩 성장 중</p>
</div>
</header>
<div class='actions'>
<a class='btn btn-fill' href='#'>연락하기</a>
<a class='btn btn-line' href='#'>포트폴리오 보기</a>
</div>
<ul class='stats'>
<li><strong>12</strong><span>프로젝트</span></li>
<li><strong>248</strong><span>팔로워</span></li>
<li><strong>36</strong><span>글</span></li>
</ul>
<main class='profile-main'>
<section class='card about'>
<h2>소개</h2>
<p>
안녕하세요. 저는 작은 화면도 편하게 읽히는 웹 페이지를 만드는 데 관심이 있습니다.
요즘은 Flexbox와 반응형 레이아웃을 집중해서 연습하고 있습니다.
</p>
</section>
<section class='card skills'>
<h2>기술</h2>
<ul class='skill-list'>
<li>HTML</li>
<li>CSS</li>
<li>Flexbox</li>
<li>Responsive Web</li>
<li>Git</li>
<li>Figma</li>
</ul>
</section>
<section class='card links'>
<h2>링크</h2>
<div class='link-list'>
<a href='#'>GitHub</a>
<a href='#'>Blog</a>
<a href='#'>Email</a>
</div>
</section>
<section class='card activity'>
<h2>최근 활동</h2>
<div class='activity-item'>
<strong>프로필 카드 실습</strong>
<p>박스 모델과 그림자, 버튼 스타일을 정리했습니다.</p>
</div>
<div class='activity-item'>
<strong>Flexbox 복습</strong>
<p>가로 배치와 세로 배치를 화면 크기에 따라 전환했습니다.</p>
</div>
<div class='activity-item'>
<strong>디버깅 연습</strong>
<p>DevTools로 특이성과 여백 문제를 직접 확인했습니다.</p>
</div>
</section>
</main>
</div>
</div>
</body>
</html>이 HTML에서 눈여겨볼 점은 card, btn처럼 재사용되는 클래스가 있다는 점입니다. 이런 식으로 이름을 붙이면 나중에 스타일을 한 번에 적용하기 쉽습니다. 바로 이런 순간에 선택자가 힘을 발휘합니다.
CSS로 하나씩 꾸미기
이제 style.css 파일을 만들고 아래 코드를 넣어 보세요. 한 번에 붙여 넣은 뒤 브라우저에서 확인해도 좋고, 위에서부터 조금씩 추가하면서 변화 과정을 보는 것도 좋습니다.
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: system-ui, sans-serif;
background: linear-gradient(180deg, #e0f2fe 0%, #f8fafc 100%);
color: #0f172a;
}
img {
display: block;
max-width: 100%;
}
a {
color: inherit;
}
.page {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 40px 20px;
}
.profile-card {
width: min(960px, 100%);
background: #ffffff;
border-radius: 24px;
overflow: hidden;
box-shadow: 0 18px 60px rgba(15, 23, 42, 0.12);
}
.cover {
height: 180px;
background: linear-gradient(135deg, #7c3aed, #06b6d4);
}
.profile-header {
display: flex;
gap: 24px;
align-items: flex-end;
padding: 0 32px;
margin-top: -60px;
}
.avatar {
width: 120px;
height: 120px;
border-radius: 50%;
object-fit: cover;
border: 6px solid #ffffff;
background: #e2e8f0;
flex-shrink: 0;
}
.profile-meta {
padding-bottom: 8px;
}
.badge {
display: inline-block;
margin: 0 0 12px;
padding: 6px 12px;
border-radius: 999px;
background: #ede9fe;
color: #6d28d9;
font-size: 14px;
font-weight: 700;
}
.profile-meta h1 {
margin: 0 0 8px;
font-size: 32px;
}
.intro,
.location {
margin: 0;
color: #475569;
line-height: 1.6;
}
.location {
margin-top: 8px;
}
.actions {
display: flex;
gap: 12px;
padding: 24px 32px 0;
}
.btn {
display: inline-block;
text-align: center;
text-decoration: none;
padding: 12px 18px;
border-radius: 12px;
font-weight: 700;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.btn-fill {
background: #111827;
color: #ffffff;
}
.btn-line {
border: 1px solid #cbd5e1;
background: #ffffff;
}
.stats {
list-style: none;
display: flex;
gap: 16px;
margin: 24px 32px 0;
padding: 20px 0;
border-top: 1px solid #e2e8f0;
border-bottom: 1px solid #e2e8f0;
}
.stats li {
flex: 1;
text-align: center;
}
.stats strong {
display: block;
font-size: 28px;
margin-bottom: 6px;
}
.stats span {
color: #64748b;
}
.profile-main {
display: flex;
flex-wrap: wrap;
gap: 20px;
padding: 32px;
}
.card {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 18px;
padding: 24px;
}
.card h2 {
margin-top: 0;
margin-bottom: 14px;
font-size: 20px;
}
.card p {
margin: 0;
line-height: 1.7;
color: #334155;
}
.about {
flex: 1 1 380px;
}
.skills {
flex: 1 1 240px;
}
.links {
flex: 1 1 240px;
}
.activity {
flex: 1 1 380px;
}
.skill-list {
list-style: none;
display: flex;
flex-wrap: wrap;
gap: 10px;
padding: 0;
margin: 0;
}
.skill-list li {
padding: 8px 12px;
border: 1px solid #cbd5e1;
border-radius: 999px;
background: #ffffff;
}
.link-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.link-list a {
padding: 12px 14px;
text-decoration: none;
border: 1px solid #e2e8f0;
border-radius: 12px;
background: #ffffff;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.activity-item + .activity-item {
margin-top: 16px;
}
.activity-item strong {
display: block;
margin-bottom: 6px;
}
.btn:hover,
.link-list a:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.08);
}
@media (max-width: 768px) {
.page {
padding: 0;
}
.profile-card {
border-radius: 0;
}
.profile-header {
flex-direction: column;
align-items: center;
text-align: center;
padding: 0 20px;
}
.actions {
flex-direction: column;
padding: 24px 20px 0;
}
.stats {
flex-wrap: wrap;
margin-left: 20px;
margin-right: 20px;
}
.stats li {
flex: 1 1 30%;
}
.profile-main {
padding: 20px;
}
}
@media (max-width: 480px) {
.cover {
height: 150px;
}
.avatar {
width: 100px;
height: 100px;
}
.profile-meta h1 {
font-size: 28px;
}
}코드에서 꼭 봐야 할 포인트
이 프로젝트의 진짜 공부 포인트는 예쁘게 보이는 결과보다, 왜 이런 CSS를 썼는지 이해하는 데 있습니다.
먼저 * { box-sizing: border-box; }는 박스 모델을 다루기 쉽게 만들어 줍니다. 너비를 정했는데 패딩과 테두리 때문에 갑자기 더 넓어지는 상황을 크게 줄여 줍니다. 초보자가 레이아웃에서 가장 자주 헷갈리는 부분을 미리 정리해 두는 셈입니다.
display: flex;는 여러 곳에서 등장합니다. .page에서는 카드를 화면 가운데로 보내고, .profile-header에서는 사진과 텍스트를 나란히 배치하며, .actions는 버튼을 가로로 놓고, .profile-main은 여러 카드를 유연하게 감쌉니다. 같은 Flexbox라도 부모가 누구냐에 따라 역할이 달라지는 점을 기억해 두세요.
.stats li { flex: 1; }는 통계 항목이 똑같은 너비를 갖게 만듭니다. 숫자가 길든 짧든 균형 있게 보이기 때문에 실무에서도 정말 자주 쓰는 패턴입니다.
.profile-main에 flex-wrap: wrap;를 준 것도 중요합니다. 이 한 줄 덕분에 공간이 부족하면 카드가 다음 줄로 내려갑니다. 즉, 반응형의 절반은 이미 여기서 시작된 셈입니다.
그리고 .profile-header의 margin-top: -60px;는 프로필 사진이 커버 영역 위로 살짝 올라와 보이게 하는 장치입니다. 이런 작은 겹침 하나만 있어도 화면이 훨씬 완성도 있어 보입니다. CSS는 종종 이런 작은 연출에서 재미가 터집니다.
반응형이 어떻게 작동하나
좁은 화면에서는 가로로 배치된 것들을 세로로 바꾸는 것이 가장 안전합니다. 그래서 @media (max-width: 768px) 안에서 .profile-header와 .actions의 방향을 바꾸었습니다. 이런 식으로 큰 화면에서는 가로, 작은 화면에서는 세로로 전환하는 방식은 Flexbox 반응형의 대표적인 패턴입니다.3
여기서 중요한 생각은 하나입니다. 모바일용 CSS를 따로 새로 만드는 것이 아니라, 기존 배치에서 꼭 필요한 부분만 바꾼다는 것입니다. 너무 많이 뒤집으면 오히려 관리가 어려워집니다.
직접 브라우저 너비를 줄여 보면서 아래 변화를 확인해 보세요.
프로필 사진과 소개가 좌우 배치에서 위아래 배치로 바뀌는지
버튼 두 개가 세로로 정렬되어 누르기 쉬워지는지
카드들이 화면 너비에 맞춰 자연스럽게 줄바꿈되는지
이 과정을 눈으로 확인하면 미디어 쿼리가 더 이상 마법 주문처럼 느껴지지 않습니다.
스타일이 안 먹을 때 디버깅하는 법
프로젝트를 만들다 보면 꼭 한 번쯤은 이런 생각이 듭니다. 분명 CSS를 썼는데 왜 안 바뀌지? 이럴 때는 감으로 고치지 말고 순서대로 확인해야 합니다. DevTools의 Styles, Computed, Layout 패널은 적용된 규칙, 상속된 값, 박스 모델, 덮어쓰기 상황을 확인하는 데 아주 유용합니다.45
다음 순서로 확인해 보세요.
선택자가 맞는지 먼저 본다. HTML에
class='profile-main'이라고 썼는데 CSS에서.profile-mian처럼 오타가 나면 아무 일도 일어나지 않습니다. 가장 단순한 실수가 가장 자주 나옵니다.부모와 자식을 헷갈리지 않았는지 본다. Flexbox가 안 먹는다면 거의 항상 부모에
display: flex;를 안 준 경우입니다. 자식에게 아무리flex: 1;을 써도 부모가 flex 컨테이너가 아니면 소용이 없습니다.박스 모델을 확인한다. 간격이 이상하게 넓거나 좁다면
margin,padding,border를 각각 확인하세요. DevTools의 박스 모델 그림을 보면 어느 쪽에서 공간이 생기는지 바로 알 수 있습니다.더 강한 규칙이 덮어쓰는지 본다. 같은 요소에 여러 규칙이 걸리면 특이성이 더 높은 선택자가 이깁니다. DevTools에서는 적용되지 않은 규칙이 취소선처럼 보이기 때문에, 무엇이 이기고 있는지 바로 찾을 수 있습니다.
초보자에게 특히 추천하는 임시 디버깅 방법도 있습니다. 문제가 있는 요소에 잠깐 outline: 2px solid red;를 주면 요소의 실제 범위를 눈으로 확인하기 쉬워집니다. border와 달리 레이아웃을 밀어내지 않아서 확인용으로 쓰기 좋습니다.
예를 들어 카드 위치가 이상하다면 이렇게 잠깐 써 보세요.
.card {
outline: 2px solid red;
}문제를 찾았으면 반드시 지워 주세요. 디버깅 도구가 갑자기 디자인 콘셉트가 되어 버리면 곤란합니다.
직접 바꿔보는 연습
지금부터가 진짜 연습입니다. 아래 항목을 직접 바꾸면서 결과를 확인해 보세요.
커버 배경을 단색이 아니라 다른 그라데이션으로 바꿔 보기
통계 항목을 3개에서 4개로 늘리고 균형이 어떻게 바뀌는지 보기
기술 태그를 더 추가해서
flex-wrap이 실제로 어떻게 동작하는지 보기
여유가 있다면 버튼 색상도 바꾸고, 링크 카드에 작은 아이콘을 넣어 보세요. 프로젝트는 정답을 복사하는 순간보다, 한 줄을 바꾸고 화면이 어떻게 달라지는지 볼 때 훨씬 많이 남습니다.
마무리
이 챕터의 목표는 완벽한 포트폴리오 사이트를 만드는 것이 아니었습니다. 하나의 프로필 페이지를 처음부터 끝까지 완성하면서, 흩어져 있던 CSS 개념을 실제 화면으로 연결하는 것이 목표였습니다.
여기까지 따라왔다면 이미 중요한 경험을 한 것입니다. 선택자로 요소를 정확히 집었고, 박스 모델로 간격을 다듬었고, Flexbox로 배치했으며, 미디어 쿼리로 반응형을 만들었고, DevTools로 문제를 추적할 준비도 마쳤습니다.
이제 남은 일은 하나입니다. 이 페이지를 당신만의 정보로 바꾸는 것입니다. 이름을 바꾸고, 소개를 고치고, 링크를 채우고, 색을 바꾸세요. 그 순간 이 예제는 연습용 코드에서 당신의 첫 결과물로 바뀝니다.
참고
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.