Skip to Content
WebCSSCSS 기본기12. 카드 내부 배치 다듬기

이번 편의 결과물: 프로젝트 카드 내부의 이미지·제목·설명·태그·버튼이 시안대로 세로로 정돈되어 배치됩니다. · 다루는 개념: 카드 내부 요소 정렬, 중첩 Flexbox, 이미지 비율 맞추기

이 편에서 만드는 파일

web-practice/portfolio/ └ css/ └ styles.css ~ (.project-card 내부 요소 규칙 추가)

개념 정리

11편에서 .project-grid로 카드 6장을 3열로 배열했지만, 카드 하나(.project-card) 안의 이미지·제목·설명·태그·버튼은 아직 기본 스타일 그대로입니다. 이번 편은 카드 하나 내부의 정렬을 다듬습니다.

카드 내부처럼 위에서 아래로 쌓이는 배치는 grid-template-areas(2차원 배치)보다 중첩 Flexbox(flex-direction: column)가 더 간단합니다. 이미지 → 제목 → 설명 → 태그 → 버튼 순서가 마크업 순서와 그대로 일치해, 영역 이름을 따로 정의할 필요가 없기 때문입니다.

상황알맞은 선택
자식 순서와 화면 순서가 같은 세로 나열중첩 Flexbox(flex-direction: column)
화면 순서를 마크업 순서와 다르게 재배치해야 함grid-template-areas

카드를 display: flex; flex-direction: column으로 만들면, 버튼을 카드 맨 아래에 고정하는 것도 쉬워집니다. 버튼 앞의 여백을 margin-top: auto로 주면, 카드 높이가 서로 달라도(설명 줄 수 차이 등) 버튼이 항상 카드 맨 아래에 붙습니다. 09편에서 헤더의 로고와 내비를 벌릴 때 쓴 것과 같은 원리(자동 마진)를 세로 방향에 적용한 것입니다.

이미지 비율은 width: 100%height를 함께 지정하고 object-fit: cover를 더해 맞춥니다. object-fit: cover는 원본 비율을 유지하면서 지정한 박스를 꽉 채우고, 넘치는 부분은 잘라냅니다. aspect-ratio 속성으로 더 정확하게 비율을 고정하는 방법도 있지만, 이 과목의 범위 밖이라 여기서는 고정 height 값으로 충분히 갈음합니다.

자주 하는 오해: 태그 목록(<ul>)에 display: flex만 주면 끝이 아닙니다. 목록 기본 스타일(list-style, padding)이 남아 있으면 태그 앞에 불릿과 들여쓰기가 그대로 보입니다.

실습

1. 기존 마크업 확인하기

카드 내부 요소는 modern_html이 만든 그대로이고, 별도의 BEM 클래스는 붙어 있지 않습니다. 마크업은 고치지 않고 태그와 .tags 하나만으로 선택자를 만듭니다.

<!-- projects.html 카드 1개 (수정하지 않음, 구조만 참고) --> <article class="project-card" data-tags="JavaScript,PWA"> <img src="images/project-01.svg" alt="오늘의 날씨 프로젝트 화면" width="1200" height="800" loading="lazy" decoding="async" /> <h2>오늘의 날씨</h2> <p>위치 기반 날씨를 카드로 보여주는 웹앱입니다.</p> <p hidden="until-found" data-more>오프라인에서도 마지막으로 받은 데이터를 보여줍니다.</p> <ul class="tags"> <li>JavaScript</li> <li>PWA</li> </ul> <a href="https://example.com/weather" target="_blank" rel="noopener noreferrer">사이트 열기</a> <button type="button" data-open-project="weather">자세히</button> </article>

2. css/styles.css에 카드 내부 규칙 작성하기

/* css/styles.css (일부) */ .project-card { display: flex; flex-direction: column; gap: var(--space-2); background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius); padding: var(--space-4); } .project-card img { width: 100%; height: 12rem; object-fit: cover; border-radius: var(--radius); } .project-card h2 { font-size: var(--text-lg); margin: 0; } .project-card p { color: var(--color-text-muted); margin: 0; } .project-card .tags { display: flex; flex-wrap: wrap; gap: var(--space-2); list-style: none; padding: 0; margin: 0; } .project-card .tags li { font-size: var(--text-xs); border: 1px solid var(--color-border); border-radius: var(--radius); padding: var(--space-1) var(--space-2); } .project-card button[data-open-project] { align-self: flex-start; margin-top: auto; padding: var(--space-2) var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius); background: var(--color-bg); color: var(--color-text); cursor: pointer; }

[data-more](오프라인 안내 문단)와 카드의 사이트 링크(<a>)는 이 편에서 별도 스타일을 주지 않고 상속된 기본값을 그대로 씁니다.

카드 hover 시 그림자와 살짝 떠오르는 효과는 시안에 정의되어 있지만, 부드러운 전환(transition)은 아직 배우지 않았습니다. 17편에서 .project-card:hoverbox-shadow·transform·transition을 함께 추가합니다.

3. 실행

projects.html을 새로고침합니다.

4. 확인

  • 카드마다 이미지 → 제목 → 설명 → 태그 → 버튼 순서로 세로 정렬되어 있습니다.
  • 이미지가 카드 폭에 꽉 차면서 비율이 깨지지 않고 잘려 채워집니다.
  • 태그가 여러 개일 때 한 줄에 다 안 들어가면 다음 줄로 자연스럽게 넘어갑니다.
  • 설명 길이가 카드마다 달라도 “자세히” 버튼은 항상 카드 맨 아래 같은 높이에 붙어 있습니다.

직접 해보기

  1. .project-card button[data-open-project]margin-top: auto를 지우고 어떤 문제가 생기는지 확인한 뒤 되돌립니다(설명 줄 수가 다른 카드끼리 버튼 위치가 어긋납니다).
  2. .project-card imgobject-fitcontain으로 바꿔 cover와 어떻게 다르게 보이는지 비교합니다.

답 보기

margin-top: auto를 지우면 버튼이 설명 바로 아래에 붙어, 설명이 짧은 카드와 긴 카드의 버튼 높이가 서로 달라집니다.

object-fit: contain은 이미지 비율을 유지하되 박스 안에 전체가 다 보이도록 줄이므로, 박스와 이미지 비율이 다르면 여백(레터박스)이 생깁니다. cover는 여백 없이 박스를 꽉 채우는 대신 이미지 일부가 잘립니다.

자주 하는 실수

증상원인고치는 법
태그 앞에 불릿(점)이 보인다ul 기본 스타일이 남아 있음.project-card .tagslist-style: none 확인
버튼이 카드마다 다른 높이에 있다margin-top: auto를 빠뜨림.project-cardflex-direction: column인지, 버튼에 margin-top: auto가 있는지 확인
이미지가 찌그러져 보인다object-fit 없이 width·height만 지정object-fit: cover 추가
카드마다 안쪽 여백이 다르다.project-cardpadding을 안 줌padding: var(--space-4) 확인

확인 문제

문제 14지선다
카드 내부처럼 이미지-제목-설명-태그-버튼이 마크업 순서 그대로 세로로 쌓일 때 더 간단한 방법은
문제 24지선다
버튼을 카드 맨 아래에 항상 고정하기 위해 쓴 속성은
문제 34지선다
object-fit: cover의 동작으로 옳은 것은
문제 44지선다
태그 목록(ul)에서 불릿과 들여쓰기를 없애기 위해 필요한 스타일은

참고 자료

Last updated on