오늘의 목표: 코드가 화면으로 바뀌는 순간 보기
지금까지는 웹의 원리, React 컴포넌트, Next.js의 페이지 구조를 머릿속으로 이해했습니다. 이제는 직접 확인할 차례입니다.
이번 장의 목표는 거창하지 않습니다.
내 컴퓨터에 Next.js 프로젝트를 만들고, 홈 화면 문구를 바꾸고, 프로필 이미지와 프로젝트 썸네일을 넣어 포트폴리오 첫 화면을 띄워 봅니다.
중요한 것은 완성도 높은 디자인이 아닙니다. 코드 한 줄이 브라우저의 실제 화면과 연결되어 있다는 감각입니다. 이 감각을 한 번 얻으면 코드는 더 이상 검은 상자가 아닙니다.
디자이너에게 이번 장은 이런 경험에 가깝습니다.
Figma 파일 만들기
→ 프레임 하나 만들기
→ 텍스트 수정하기
→ 이미지 넣기
→ 프로토타입 미리 보기에서 확인하기Next.js에서는 같은 흐름이 이렇게 바뀝니다.
Next.js 프로젝트 만들기
→ app/page.tsx 열기
→ 텍스트 수정하기
→ public 폴더에 이미지 넣기
→ localhost에서 확인하기여기서 localhost는 내 컴퓨터 안에서만 열리는 임시 웹 주소입니다. 작업 중인 Figma 파일을 브라우저에서 미리 보는 창이라고 생각하면 됩니다.

우리가 만들 화면
이번 장에서 만들 화면은 포트폴리오 랜딩 페이지입니다. 랜딩 페이지는 한 페이지 안에 중요한 정보를 위에서 아래로 배치한 화면입니다.
구조는 단순합니다.
Home 섹션
- 이름
- 한 줄 소개
- 프로필 이미지
About 섹션
- 내가 어떤 디자이너인지
- 어떤 문제를 다루는지
Projects 섹션
- 프로젝트 카드 2개
- 썸네일
- 프로젝트 설명Figma로 보면 하나의 세로 프레임 안에 섹션 프레임이 쌓인 형태입니다.
Portfolio Page Frame
├─ Home Section
├─ About Section
└─ Projects Section
├─ Project Card
└─ Project Card웹에서는 이것을 HTML 구조로 번역합니다.
main
├─ section
├─ section
└─ sectionsection은 화면의 의미 있는 덩어리입니다. Figma에서 프레임으로 영역을 나누는 것과 비슷합니다.
1단계: Next.js 프로젝트 만들기
먼저 터미널을 엽니다.
터미널은 낯설 수 있지만, 지금은 복잡하게 생각하지 않아도 됩니다. Figma에서 메뉴를 클릭하는 대신, 텍스트 명령으로 프로그램에게 일을 시키는 창입니다.
원하는 작업 폴더로 이동한 뒤 아래 명령을 입력합니다.
npx create-next-app@latest designer-portfoliocreate-next-app은 Next.js 프로젝트의 기본 파일과 폴더를 자동으로 만들어 주는 도구입니다. Next.js 공식 학습 코스에서도 새 프로젝트를 만들 때 create-next-app을 사용합니다.1
설치 중에 몇 가지 질문이 나올 수 있습니다. 이 책에서는 처음 배우는 디자이너를 기준으로 아래처럼 선택해도 충분합니다.
TypeScript를 사용할까요? → Yes
ESLint를 사용할까요? → Yes
Tailwind CSS를 사용할까요? → No
src/ 폴더를 사용할까요? → No
App Router를 사용할까요? → Yes
import alias를 설정할까요? → No버전에 따라 질문 문구가 조금 다를 수 있습니다. 핵심은 App Router를 사용한다는 점입니다. 우리는 app 폴더 안에서 페이지를 만들 것입니다.
프로젝트가 만들어지면 폴더 안으로 들어갑니다.
cd designer-portfolio그리고 개발 서버를 실행합니다.
npm run dev공식 학습 코스에서는 pnpm dev를 예시로 들지만, 이 책에서는 별도 설치가 적은 npm run dev를 사용하겠습니다. 둘 다 개발 서버를 켜는 역할은 같습니다. 개발 서버가 켜지면 보통 http://localhost:3000 주소에서 화면을 확인할 수 있습니다.1
브라우저를 열고 아래 주소로 들어갑니다.
http://localhost:3000화면이 보인다면 성공입니다.
지금부터 이 브라우저 창은 여러분의 작업 중인 웹 미리보기 창입니다.
2단계: 폴더 구조를 Figma처럼 이해하기
코드 에디터로 프로젝트 폴더를 열어 봅니다. Visual Studio Code를 사용한다면 아래 명령으로 열 수 있습니다.
code .처음 보면 파일이 많아 보입니다. 하지만 지금 볼 것은 세 곳뿐입니다.
designer-portfolio
├─ app
│ ├─ page.tsx
│ ├─ layout.tsx
│ └─ globals.css
├─ public
└─ package.json디자이너 관점으로 바꾸면 이렇게 이해할 수 있습니다.
| 폴더/파일 | Figma에 비유하면 | 지금 하는 역할 |
|---|---|---|
app/page.tsx | 첫 번째 메인 프레임 | 홈 화면 내용 |
app/layout.tsx | 모든 프레임에 공통으로 깔리는 기본 설정 | 전체 문서 뼈대 |
app/globals.css | 전체 스타일 가이드 | 기본 색, 여백, 글꼴 |
public | 이미지 에셋 폴더 | 프로필, 썸네일 저장 |
package.json | 프로젝트 설정 문서 | 실행 명령과 설치 정보 |
Next.js 공식 학습 코스에서도 app 폴더는 애플리케이션의 라우트, 컴포넌트, 로직을 담는 곳이고, public 폴더는 이미지 같은 정적 에셋을 담는 곳이라고 설명합니다.1
여기서 가장 중요한 파일은 app/page.tsx입니다.
앞 장에서 배운 파일 기반 라우팅을 떠올려 봅시다.
app/page.tsx → /즉, app/page.tsx는 사이트의 첫 화면입니다.
Figma식으로 말하면 Home이라는 이름의 최상위 프레임 하나가 웹 주소 /에 연결된 셈입니다.
3단계: 첫 문구 바꿔 보기
app/page.tsx 파일을 엽니다.
처음 들어 있는 코드를 모두 지우고, 아래 코드로 바꿔 봅니다.
export default function Home() {
return (
<main>
<h1>안녕하세요, 저는 프로덕트 디자이너 민지입니다.</h1>
<p>사용자의 문제를 구조화하고, 화면으로 해결하는 일을 합니다.</p>
</main>
);
}저장한 뒤 브라우저를 확인합니다.
방금 쓴 문장이 화면에 보일 것입니다.
이것이 가장 중요한 첫 경험입니다.
app/page.tsx의 텍스트
→ 저장
→ localhost 화면의 텍스트 변경Figma에서 텍스트 레이어를 수정하면 캔버스가 바뀌듯, page.tsx 안의 텍스트를 수정하면 브라우저 화면이 바뀝니다.
여기서 tsx라는 확장자가 낯설 수 있습니다. 너무 깊게 들어갈 필요는 없습니다.
일단 이렇게만 기억하세요.
.tsx = HTML처럼 생긴 화면 구조를 React 컴포넌트 안에 쓰는 파일<main>, <h1>, <p>는 HTML에서 본 적 있는 태그입니다. 다만 이 태그들이 React 컴포넌트의 return 안에 들어가 있습니다.
4단계: public 폴더에 이미지 넣기
이번에는 이미지 에셋을 넣어 보겠습니다.
Figma에서 프로필 이미지와 프로젝트 썸네일을 export한다고 생각해 봅시다.
준비할 이미지는 세 개입니다.
profile.jpg
project-mobile-app.jpg
project-dashboard.jpg이 이미지들을 프로젝트의 public 폴더 안에 넣습니다.
designer-portfolio
├─ public
│ ├─ profile.jpg
│ ├─ project-mobile-app.jpg
│ └─ project-dashboard.jpgpublic 폴더는 웹사이트가 바로 꺼내 쓸 수 있는 에셋 보관함입니다. 이 폴더 안에 profile.jpg를 넣으면, 코드에서는 이렇게 부를 수 있습니다.
/profile.jpg주의할 점은 하나입니다.
public/profile.jpg → 코드에서는 /profile.jpg코드에 /public/profile.jpg라고 쓰지 않습니다. public 폴더는 이미 웹사이트의 가장 바깥 입구처럼 취급되기 때문입니다.
Figma로 비유하면 이렇습니다.
Figma Assets 패널에 등록한 이미지
→ 프레임에서 바로 끌어다 쓰기Next.js에서는 이렇게 바뀝니다.
public 폴더에 넣은 이미지
→ 코드에서 /파일명으로 불러오기5단계: Home, About, Projects 섹션 만들기
이제 app/page.tsx를 포트폴리오 첫 화면으로 바꿔 보겠습니다.
아래 코드를 그대로 붙여 넣습니다.
import Image from "next/image";
const projects = [
{
title: "모바일 온보딩 개선",
description: "처음 방문한 사용자가 핵심 기능을 더 빨리 이해하도록 가입 흐름을 정리했습니다.",
image: "/project-mobile-app.jpg",
},
{
title: "대시보드 정보 구조 개선",
description: "복잡한 데이터를 카드와 필터 구조로 나누어 의사결정 속도를 높였습니다.",
image: "/project-dashboard.jpg",
},
];
export default function Home() {
return (
<main className="page">
<section className="heroSection">
<div>
<p className="eyebrow">Product Designer Portfolio</p>
<h1>사용자의 문제를 화면의 구조로 바꾸는 디자이너, 민지입니다.</h1>
<p className="heroText">
저는 리서치에서 발견한 문제를 정보 구조, 사용자 흐름, 인터페이스로 연결하는 일을 합니다.
</p>
</div>
<Image
src="/profile.jpg"
alt="프로덕트 디자이너 민지의 프로필 사진"
width={160}
height={160}
className="profileImage"
/>
</section>
<section className="section">
<p className="eyebrow">About</p>
<h2>디자인은 예쁜 화면을 넘어서, 사용자가 길을 잃지 않게 돕는 구조라고 생각합니다.</h2>
<p>
저는 Figma로 화면을 그리기 전에 사용자의 목표, 필요한 정보, 다음 행동을 먼저 정리합니다.
그다음 컴포넌트와 오토레이아웃을 활용해 일관된 화면 시스템으로 만듭니다.
</p>
</section>
<section className="section">
<p className="eyebrow">Projects</p>
<h2>대표 프로젝트</h2>
<div className="projectGrid">
{projects.map((project) => (
<article className="projectCard" key={project.title}>
<Image
src={project.image}
alt={`${project.title} 썸네일`}
width={640}
height={400}
className="projectImage"
/>
<div className="projectContent">
<h3>{project.title}</h3>
<p>{project.description}</p>
</div>
</article>
))}
</div>
</section>
</main>
);
}코드가 길어졌지만 구조는 단순합니다.
main.page
├─ section.heroSection
│ ├─ 소개 문구
│ └─ 프로필 이미지
├─ section.section
│ └─ About 내용
└─ section.section
└─ 프로젝트 카드 목록Figma에서 보면 이런 구조입니다.
Page Frame
├─ Hero Frame
├─ About Frame
└─ Projects Frame
├─ Project Card Instance
└─ Project Card Instance중간에 보이는 projects 배열은 프로젝트 카드에 들어갈 내용을 모아 둔 데이터입니다.
const projects = [
{
title: "모바일 온보딩 개선",
description: "...",
image: "/project-mobile-app.jpg",
},
];이것은 Figma의 컴포넌트 인스턴스 속성과 비슷합니다.
Project Card 컴포넌트
- title
- description
- image같은 카드 구조를 두 번 만들되, 안에 들어가는 내용만 다르게 넣는 방식입니다.
앞 장에서 배운 React 컴포넌트 감각이 여기서 다시 등장합니다. 같은 구조를 반복할 때, 직접 두 번 복사해 붙이는 대신 데이터를 돌면서 카드들을 만듭니다.
{projects.map((project) => (
<article className="projectCard" key={project.title}>
...
</article>
))}처음에는 이 부분이 낯설어도 괜찮습니다. 지금은 이렇게만 이해해도 충분합니다.
projects에 들어 있는 항목 수만큼
projectCard를 반복해서 그린다Figma의 반복 작업과 비교하면 이렇습니다.
Figma
Project Card 인스턴스를 복제하고 텍스트와 이미지를 바꾼다.
React
projects 데이터에 항목을 추가하면 Project Card가 하나 더 그려진다.6단계: 최소 스타일 입히기
아직 화면이 밋밋할 수 있습니다. 이번 장에서는 스타일을 깊게 배우지 않습니다. 다음 장에서 CSS Modules와 Tailwind를 디자인 토큰 관점으로 자세히 다룹니다.
다만 첫 화면을 보기 좋게 만들기 위해 최소한의 CSS만 넣겠습니다.
app/globals.css 파일을 열고 내용을 아래처럼 바꿉니다.
* {
box-sizing: border-box;
}
body {
margin: 0;
background: #f7f3ee;
color: #1f1f1f;
font-family: Arial, sans-serif;
}
.page {
width: min(1080px, calc(100% - 40px));
margin: 0 auto;
padding: 64px 0;
}
.heroSection {
display: grid;
grid-template-columns: 1fr auto;
gap: 40px;
align-items: center;
padding: 56px;
border-radius: 32px;
background: #ffffff;
}
.eyebrow {
margin: 0 0 12px;
color: #7a5cff;
font-size: 14px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
}
h1,
h2,
h3,
p {
margin-top: 0;
}
h1 {
max-width: 720px;
font-size: 48px;
line-height: 1.08;
letter-spacing: -0.04em;
}
h2 {
max-width: 760px;
font-size: 32px;
line-height: 1.2;
letter-spacing: -0.03em;
}
p {
color: #4b4b4b;
font-size: 18px;
line-height: 1.7;
}
.heroText {
max-width: 640px;
}
.profileImage {
border-radius: 50%;
object-fit: cover;
}
.section {
margin-top: 32px;
padding: 48px;
border-radius: 32px;
background: #ffffff;
}
.projectGrid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 24px;
margin-top: 24px;
}
.projectCard {
overflow: hidden;
border: 1px solid #e7e0d8;
border-radius: 24px;
background: #fffdf9;
}
.projectImage {
width: 100%;
height: auto;
display: block;
}
.projectContent {
padding: 24px;
}
.projectContent h3 {
margin-bottom: 8px;
font-size: 22px;
}
.projectContent p {
margin-bottom: 0;
font-size: 16px;
}
@media (max-width: 760px) {
.heroSection {
grid-template-columns: 1fr;
padding: 32px;
}
h1 {
font-size: 36px;
}
h2 {
font-size: 26px;
}
.section {
padding: 32px;
}
.projectGrid {
grid-template-columns: 1fr;
}
}저장한 뒤 브라우저를 다시 확인합니다.
이제 포트폴리오 첫 화면이 보일 것입니다.
이 CSS를 완벽히 이해하지 않아도 됩니다. 오늘은 역할만 파악하면 됩니다.
.page → 전체 페이지의 최대 너비와 바깥 여백
.heroSection → 첫 소개 영역의 레이아웃
.section → About, Projects 영역의 카드 형태
.projectGrid → 프로젝트 카드를 가로로 배치
.projectCard → 프로젝트 카드 하나의 모양Figma의 오토레이아웃과 비교하면 display: grid, gap, padding은 이런 느낌입니다.
| CSS | Figma에 비유하면 |
|---|---|
padding | 프레임 안쪽 여백 |
gap | 오토레이아웃 아이템 간격 |
grid-template-columns | 가로 칼럼 구조 |
border-radius | 모서리 둥글기 |
background | Fill 색상 |
스타일의 세부 원리는 다음 장에서 더 천천히 다룹니다. 지금은 코드와 화면이 연결되는 것을 보는 데 집중합니다.
화면이 바뀌지 않을 때 확인할 것
처음 실습할 때 가장 흔한 문제는 코드 자체보다 위치와 저장입니다.
아래 순서로 확인해 보세요.
1. 파일을 저장했는가
코드를 수정한 뒤 저장하지 않으면 브라우저가 바뀌지 않습니다.
수정 → 저장 → 브라우저 확인Figma는 대부분 자동 저장이지만, 코드 에디터에서는 저장 단축키가 필요할 때가 많습니다.
2. 개발 서버가 켜져 있는가
터미널에 npm run dev가 계속 실행 중이어야 합니다.
터미널을 닫았다면 다시 실행합니다.
npm run dev3. 주소가 맞는가
브라우저 주소가 아래와 같은지 확인합니다.
http://localhost:3000다른 숫자가 안내되었다면 터미널에 표시된 주소를 따라가면 됩니다.
4. 이미지 이름이 정확한가
코드에 이렇게 적었다면,
src="/profile.jpg"public 폴더 안에도 정확히 같은 이름의 파일이 있어야 합니다.
public/profile.jpgProfile.jpg, profile.png, profile 1.jpg는 다른 이름입니다. 웹 개발에서는 파일 이름의 작은 차이도 중요합니다.
5. 괄호가 빠지지 않았는가
React 코드는 괄호가 짝을 이룹니다.
( )
{ }
< >에러가 나면 당황하지 말고, 터미널이나 브라우저에 표시된 메시지에서 파일 이름과 줄 번호를 먼저 봅니다. 개발자는 보통 이 정보를 보고 문제 위치를 찾습니다.
오늘 배운 멘탈 모델
이번 장의 핵심은 문법 암기가 아닙니다.
디자이너가 가져가야 할 멘탈 모델은 네 가지입니다.
첫째, app/page.tsx는 첫 화면 파일입니다.
app/page.tsx → 홈페이지둘째, localhost는 작업 중인 웹 미리보기 창입니다.
npm run dev → http://localhost:3000에서 미리 보기셋째, public 폴더는 이미지 에셋 보관함입니다.
public/profile.jpg → /profile.jpg넷째, Figma 프레임 구조는 웹의 섹션 구조로 번역됩니다.
Figma Frame → section
Component Instance → 반복되는 카드 구조
Image Asset → public 폴더의 파일
Text Layer → JSX 안의 텍스트이제 여러분은 개발자가 만든 화면을 볼 때 이렇게 질문할 수 있습니다.
"이 텍스트는 어느 파일에 있나요?"
"이 이미지는 어디에서 관리되나요?"
"이 섹션은 공통 컴포넌트인가요, 이 페이지에만 있는 구조인가요?"
이 질문들은 단순해 보이지만, 협업의 질을 크게 바꿉니다.
작은 실습: 내 정보로 바꿔 보기
마지막으로 지금 만든 화면을 자신의 포트폴리오로 바꿔 봅니다.
아래 네 가지만 수정해도 충분합니다.
1. 이름과 소개 문장
2. About 섹션의 설명
3. 프로젝트 제목 2개
4. 프로젝트 썸네일 이미지 2개예를 들어 이 부분을 바꿉니다.
<h1>사용자의 문제를 화면의 구조로 바꾸는 디자이너, 민지입니다.</h1>자신의 문장으로 바꿔 보세요.
<h1>B2B SaaS의 복잡한 흐름을 단순하게 만드는 디자이너, 지훈입니다.</h1>프로젝트도 바꿔 봅니다.
const projects = [
{
title: "결제 실패율 개선",
description: "결제 과정에서 사용자가 이탈하는 지점을 찾아 입력 흐름과 오류 메시지를 개선했습니다.",
image: "/project-payment.jpg",
},
{
title: "관리자 페이지 리디자인",
description: "운영자가 자주 확인하는 정보를 우선순위에 따라 재배치했습니다.",
image: "/project-admin.jpg",
},
];이렇게 바꿨다면 public 폴더 안의 이미지 이름도 맞춰야 합니다.
public/project-payment.jpg
public/project-admin.jpg코드와 에셋은 서로 약속한 이름으로 연결됩니다.
이것은 디자인 시스템에서도 익숙한 개념입니다. 컴포넌트 이름, variant 이름, 컬러 토큰 이름이 맞아야 팀이 같은 대상을 가리킬 수 있습니다. 웹 프로젝트에서도 이름은 중요한 협업 언어입니다.
디자이너가 개발자에게 이제 할 수 있는 질문
이번 장을 끝냈다면, 이제 개발자에게 이런 질문을 할 수 있습니다.
"이 화면의 실제 페이지 파일은 어디에 있나요?
app/page.tsx같은 파일인가요?""이 페이지에서만 쓰는 섹션인가요, 아니면 다른 페이지에서도 쓰는 공통 컴포넌트인가요?"
"프로필 이미지나 썸네일은
public폴더에서 관리하나요, 아니면 CMS나 외부 저장소에서 가져오나요?""로컬에서 이 화면을 확인하려면 어떤 명령어를 실행하면 되나요?"
"제가 Figma에서 export한 이미지 이름과 개발 코드에서 참조하는 이름을 어떻게 맞추면 좋을까요?"
"이 랜딩 페이지의 Home, About, Projects 섹션은 나중에 각각 컴포넌트로 분리할 계획이 있나요?"
"반응형에서 프로젝트 카드는 언제 2열에서 1열로 바뀌나요?"
이 질문들은 개발자처럼 보이기 위한 질문이 아닙니다. 디자인이 실제 제품 안에서 어떻게 구현되는지 확인하기 위한 질문입니다.
다음 장에서는 지금 만든 화면에 스타일을 더 체계적으로 입혀 보겠습니다. CSS Modules와 Tailwind를 Figma의 디자인 토큰, 컴포넌트 스타일, 오토레이아웃 관점으로 연결해 이해할 것입니다.
참고
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.
