Projects
코드 블록의 Try it Yourself 버튼으로 직접 실행할 수 있다.
구문
06. Projects.jsx — 프로젝트 섹션
이번 단계에서 할 일
프로젝트 목록을 데이터 배열로 관리하고,
map으로 화면에 출력하는 방법을 배웁니다.
피그마 디자인 분석
│ ─── Projects ──────────────────────────────────── │
│ │
│ ┌──────────────────────┬───────────────────────┐ │
│ │ ● Project 01 │ │ │
│ │ │ │ │
│ │ 동대문구청 │ 이미지 영역 │ │
│ │ │ │ │
│ │ 기술스택 : html,css… │ │ │
│ │ 배포매체 : desktop… │ │ │
│ │ 작업기간 : 2주 │ │ │
│ │ 기여도 : 100% │ │ │
│ │ │ │ │
│ │ [기획서] [깃허브] [페이지] │ │ │
│ └──────────────────────┴───────────────────────┘ │
| 요소 | 값 |
|---|---|
| 주황 점 색상 | #f26440 |
| Project 번호 색 | #c7c7c7 |
| 프로젝트 타이틀 | 32px |
| 설명 텍스트 | 18px, #555555 |
| 버튼 배경 | #000000 |
새로운 개념: 데이터와 화면 분리
데이터(배열)와 화면(JSX)을 분리하면:
- 프로젝트가 늘어나도 배열에 항목만 추가하면 됩니다
- 코드가 훨씬 깔끔해집니다
// 데이터만 따로 (내용)
const PROJECTS = [
{ id: 1, title: '동대문구청', ... },
{ id: 2, title: '두 번째 프로젝트', ... }, // 추가도 쉬움
]
// 화면만 따로 (형태)
{PROJECTS.map((project) => (
<ProjectItem project={project} />
))}
STEP 1 — Projects.jsx 작성
// src/components/Projects.jsx
import SectionTitle from './SectionTitle'
/* ─── 프로젝트 데이터 (컴포넌트 밖에 위치) ─── */
// 나중에 프로젝트를 추가할 때 이 배열에만 항목을 추가하면 됩니다
const PROJECTS = [
{
id: 1,
number: 'Project 01',
title: '동대문구청',
stack: 'html, css, photoshop, figma',
deploy: 'desktop, mobile',
period: '2주',
contribution: '100%',
browsers: 'Chrome / Edge / Opera / Safari',
pages: '메인페이지 / 접근성 준수',
links: {
plan: '#',
github: '#',
live: '#',
},
},
// 프로젝트 추가 시 여기에 객체를 하나 더 작성하세요
]
/* ─── 프로젝트 한 항목을 보여주는 컴포넌트 ─── */
const ProjectItem = ({ project }) => {
return (
<div className="project-item">
{/* 왼쪽: 설명 */}
<div className="project-desc">
{/* 번호 (● Project 01) */}
<div className="project-number">
<span className="project-dot" />
<span className="project-number__text">{project.number}</span>
</div>
{/* 프로젝트 이름 */}
<h3 className="project-title">{project.title}</h3>
{/* 상세 정보 */}
<div className="project-info">
<div className="project-info__row">
<span className="project-info__label">기술스택 :</span>
<span>{project.stack}</span>
</div>
<div className="project-info__row">
<span className="project-info__label">배포매체 :</span>
<span>{project.deploy}</span>
</div>
<div className="project-info__row">
<span className="project-info__label">작업기간 :</span>
<span>{project.period}</span>
</div>
<div className="project-info__row">
<span className="project-info__label">본인기여도 :</span>
<span>{project.contribution}</span>
</div>
<div className="project-info__row">
<span className="project-info__label">브라우저 호환성 :</span>
<span>{project.browsers}</span>
</div>
<div className="project-info__row">
<span className="project-info__label">페이지수 / 특징 :</span>
<span>{project.pages}</span>
</div>
</div>
{/* 버튼 3개 */}
<div className="project-buttons">
<a href={project.links.plan} className="project-btn">기획서 보기</a>
<a href={project.links.github} className="project-btn">깃허브 보기</a>
<a href={project.links.live} className="project-btn">페이지 보기</a>
</div>
</div>
{/* 오른쪽: 이미지 자리 */}
<div className="project-image">
{/* 실제 이미지가 있으면: <img src="..." alt="..." /> */}
</div>
</div>
)
}
/* ─── Projects 메인 컴포넌트 ─── */
const Projects = () => {
return (
<section className="projects" id="projects">
<SectionTitle
title="Projects"
subtitle="끈기와 열정으로 성장하는 오성의입니다."
/>
{/* PROJECTS 배열을 반복해서 ProjectItem을 출력 */}
{PROJECTS.map((project) => (
<ProjectItem key={project.id} project={project} />
))}
</section>
)
}
export default Projects
코드 설명
객체 배열에서 map 사용
const PROJECTS = [
{ id: 1, title: '동대문구청', ... },
{ id: 2, title: '두 번째 프로젝트', ... },
]
{PROJECTS.map((project) => (
<ProjectItem key={project.id} project={project} />
))}
project는 배열의 각 객체 (예:{ id: 1, title: '동대문구청' })project.id,project.title처럼.으로 값에 접근key={project.id}— 각 항목의 고유 번호를 key로 사용
props로 객체 전달
// 통째로 넘기기
<ProjectItem project={project} />
// 컴포넌트 안에서 꺼내 쓰기
const ProjectItem = ({ project }) => {
return <h3>{project.title}</h3>
}
객체 전체를 project 라는 이름으로 전달하면
컴포넌트 안에서 project.title, project.stack 등으로 씁니다.
왜 PROJECTS 를 대문자로?
관례상 변하지 않는 상수 데이터는 대문자로 씁니다.
const PROJECTS = [...] // 대문자: 변하지 않는 데이터
const [active, setActive] = useState('Home') // 소문자: 변하는 상태
STEP 2 — CSS 추가
/* src/index.css 에 추가 */
/* ── Projects ────────────────────────────── */
.projects {
padding: 80px 24px;
}
/* 프로젝트 한 항목: 2단 레이아웃 */
.project-item {
display: grid;
grid-template-columns: 1fr 1fr; /* 설명 50% : 이미지 50% */
margin-bottom: 40px;
border: 1px solid #f0f0f0;
}
/* 왼쪽 설명 영역 */
.project-desc {
padding: 40px;
background-color: #ffffff;
}
/* ● 번호 부분 */
.project-number {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 16px;
}
/* 주황 점 */
.project-dot {
display: inline-block;
width: 7px;
height: 7px;
border-radius: 50%; /* 원 모양 */
background-color: #f26440;
}
/* "Project 01" 텍스트 */
.project-number__text {
font-size: 12px;
color: #c7c7c7;
}
/* 프로젝트 이름 */
.project-title {
font-size: 32px;
font-weight: 700;
color: #000;
margin-bottom: 24px;
}
/* 상세 정보 목록 */
.project-info {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 32px;
}
/* 정보 한 줄 */
.project-info__row {
display: flex;
gap: 8px;
font-size: 18px;
color: #555555;
}
/* 라벨 (기술스택 :) */
.project-info__label {
min-width: 140px;
flex-shrink: 0; /* 라벨이 줄어들지 않게 */
}
/* 버튼 3개 묶음 */
.project-buttons {
display: flex;
gap: 16px;
}
/* 검정 버튼 */
.project-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 160px;
height: 52px;
background-color: #000;
color: #fff;
font-size: 18px;
transition: background-color 0.2s;
}
.project-btn:hover {
background-color: #333;
}
/* 오른쪽 이미지 영역 */
.project-image {
background-color: #e8e8e8;
min-height: 447px;
}
브라우저 확인
저장 후 브라우저에서:
- "Projects" 큰 제목이 보입니다
- 왼쪽에 프로젝트 설명, 오른쪽에 회색 이미지 자리가 있습니다
- 3개의 검정 버튼이 보입니다
프로젝트 추가하는 방법
PROJECTS 배열에 객체 하나를 추가하면 자동으로 새 항목이 생깁니다.
const PROJECTS = [
{
id: 1,
title: '동대문구청',
// ...
},
// ↓ 이렇게 추가
{
id: 2,
number: 'Project 02',
title: '두 번째 프로젝트',
stack: 'React, CSS',
deploy: 'web',
period: '1개월',
contribution: '80%',
browsers: 'Chrome / Edge',
pages: '5페이지',
links: { plan: '#', github: '#', live: '#' },
},
]