<Main />
src/components/Main.jsx
function Main({ children }) {
return <main className="container mx-auto px-4 py-16">{children}</main>;
}
export default Main;
설명:
Main
컴포넌트는 애플리케이션의 주요 콘텐츠를 감싸는 레이아웃 컴포넌트입니다.children
프로퍼티를 통해 부모 컴포넌트로부터 전달된 자식 요소들을 렌더링합니다.mx-auto
), 좌우 패딩(px-4
), 상하 패딩(py-16
)을 적용하여 일관된 레이아웃을 유지합니다.Main
컴포넌트만 수정하면 전체 애플리케이션에 반영됩니다.src/App.jsx
import { Outlet } from 'react-router-dom';
import Header from './components/Header';
import Main from './components/Main';
function App() {
return (
<>
<Header />
<Main>
<Outlet />
</Main>
</>
);
}
export default App;
설명:
Header
컴포넌트를 상단에 배치하여 모든 페이지에서 공통으로 표시되는 헤더를 제공합니다.Main
컴포넌트로 주요 콘텐츠 영역을 감싸며, 그 안에 Outlet
을 배치하여 라우트에 따른 자식 컴포넌트를 렌더링합니다.Outlet
을 통해 React Router의 라우팅 기능을 원활하게 통합할 수 있습니다.src/components/CanvasTitle.jsx
import { FaEdit } from 'react-icons/fa';
function CanvasTitle() {
return (
<div className="flex items-center justify-center mb-10">
<h1 className="text-4xl font-bold text-center">Lean Canvas</h1>
<button
className="ml-2 p-2 bg-yellow-500 text-white rounded-full hover:bg-yellow-600 transition duration-300 ease-in-out focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:ring-opacity-50"
aria-label="Edit title"
>
<FaEdit />
</button>
</div>
);
}
export default CanvasTitle;
설명:
CanvasTitle
컴포넌트는 애플리케이션의 타이틀을 표시하고, 타이틀을 편집할 수 있는 버튼을 제공합니다.h1
태그를 사용하여 "Lean Canvas"라는 타이틀을 중앙에 크게 표시합니다.FaEdit
아이콘을 포함한 버튼을 추가하여 사용자가 타이틀을 편집할 수 있는 인터페이스를 제공합니다.flex
, items-center
, justify-center
), 마진(mb-10
), 타이틀 스타일(text-4xl
, font-bold
), 버튼 스타일(bg-yellow-500
, rounded-full
등)을 적용했습니다.aria-label
속성을 통해 버튼의 목적을 명확히 전달하여 스크린 리더 사용자에게 유용합니다.src/components/CanvasTitle.jsx
import { useState } from 'react';
import { FaCheck, FaEdit } from 'react-icons/fa';
function CanvasTitle() {
const [title, setTitle] = useState('Lean Canvas');
const [editedTitle, setEditedTitle] = useState(title);
const [isEditing, setIsEditing] = useState(false);
const handleEditTitle = () => {
setIsEditing(true);
};
const handleTitleChange = e => {
setEditedTitle(e.target.value);
};
const handleTitleSubmit = () => {
setTitle(editedTitle);
setIsEditing(false);
};
return (
<div className="flex items-center justify-center mb-10">
{isEditing ? (
<div className="flex items-center">
<input
type="text"
value={editedTitle}
onChange={handleTitleChange}
className="text-4xl font-bold text-center text-blue-600 bg-transparent border-b-2 border-blue-600 focus:outline-none"
/>
<button
onClick={handleTitleSubmit}
className="ml-2 p-2 bg-green-500 text-white rounded-full hover:bg-green-600 transition duration-300 ease-in-out focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-opacity-50"
aria-label="Save title"
>
<FaCheck />
</button>
</div>
) : (
<h1 className="text-4xl font-bold text-center">{title}</h1>
)}
{!isEditing && (
<button
onClick={handleEditTitle}
className="ml-2 p-2 bg-yellow-500 text-white rounded-full hover:bg-yellow-600 transition duration-300 ease-in-out focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:ring-opacity-50"
aria-label="Edit title"
>
<FaEdit />
</button>
)}
</div>
);
}
export default CanvasTitle;
설명:
CanvasTitle
컴포넌트는 타이틀을 표시할 뿐만 아니라, 사용자가 타이틀을 편집할 수 있는 기능을 제공합니다.title
: 현재 타이틀을 저장하는 상태.editedTitle
: 사용자가 입력한 새로운 타이틀을 임시로 저장하는 상태.isEditing
: 편집 모드 여부를 나타내는 상태.handleEditTitle
: 편집 모드를 활성화합니다.handleTitleChange
: 입력 필드의 값을 업데이트합니다.handleTitleSubmit
: 수정된 타이틀을 title
상태에 반영하고 편집 모드를 종료합니다.isEditing
이 true
일 때: 입력 필드와 저장 버튼을 표시하여 사용자가 타이틀을 수정할 수 있게 합니다.isEditing
이 false
일 때: 타이틀 텍스트와 편집 버튼을 표시합니다.flex
, items-center
, justify-center
), 타이틀 및 버튼의 스타일을 적용했습니다.useState
훅을 활용하여 컴포넌트의 상태를 효과적으로 관리합니다.aria-label
속성을 통해 버튼의 목적을 명확히 전달하여 스크린 리더 사용자에게 유용합니다.컴포넌트 분리:
Main
: 주요 콘텐츠 영역을 감싸는 레이아웃 컴포넌트.CanvasTitle
: 타이틀을 표시하고 편집할 수 있는 기능을 제공.CanvasItem
, CanvasList
, SearchBar
, ViewToggle
)도 각 기능에 따라 분리되어 코드의 재사용성과 유지보수성을 높였습니다.상태 관리:
Home
페이지에서 searchText
, isGridView
, dummyData
등의 상태를 관리하여 자식 컴포넌트에 전달합니다.반응형 디자인:
sm:flex-row
클래스를 사용하여 작은 화면에서는 세로로, 큰 화면에서는 가로로 레이아웃을 변경합니다.접근성:
aria-label
속성을 사용하여 버튼과 입력 필드의 목적을 명확히 전달했습니다.애니메이션과 트랜지션:
transition-transform
, duration-300
, hover:scale-105
등을 활용하여 사용자 인터랙션 시 부드러운 애니메이션 효과를 추가했습니다.아이콘 사용:
react-icons
라이브러리를 사용하여 시각적인 아이콘을 추가함으로써 UI의 직관성과 미적 요소를 강화했습니다.이번 실습에서는 타이틀 컴포넌트를 구현하여 애플리케이션의 핵심 타이틀을 효과적으로 관리할 수 있게 했습니다. Main
컴포넌트를 통해 일관된 레이아웃을 유지하며, CanvasTitle
컴포넌트는 사용자에게 직관적인 편집 기능을 제공했습니다. 컴포넌트 분리를 통해 코드의 재사용성과 유지보수성을 높였으며, Tailwind CSS와 React Icons를 활용하여 깔끔하고 반응형인 사용자 인터페이스를 구현했습니다.
src/components/CanvasCard.jsx
import { FaPlus } from 'react-icons/fa';
function CanvasCard({ title, isSubtitle = false }) {
return (
<div className="row-span-1 bg-white min-h-48 border border-collapse border-gray-300">
<div
className={`${isSubtitle === false && 'bg-gray-100 border-b border-b-gray-300'} flex items-start justify-between px-3 py-2`}
>
<h3 className={`${isSubtitle === false && 'font-bold'} `}>{title}</h3>
<button className="bg-blue-400 text-white p-1.5 text-xs rounded-md">
<FaPlus />
</button>
</div>
<div className="space-y-3 min-h-32 p-3">memo..</div>
</div>
);
}
export default CanvasCard;
설명:
CanvasCard
컴포넌트는 린캔버스의 각 섹션을 카드 형태로 표시합니다.title
: 카드의 제목을 설정합니다.isSubtitle
(기본값 false
): 해당 섹션이 부제목인지 여부를 결정하여 스타일을 다르게 적용합니다.bg-white
)과 회색 경계선(border-gray-300
)으로 스타일링되어 있습니다.flex
레이아웃을 사용하여 제목과 추가 버튼을 양쪽 끝에 배치합니다.isSubtitle
이 false
일 경우, 헤더에 회색 배경과 하단 경계선(bg-gray-100 border-b-gray-300
)이 적용되고, 제목에 굵은 글씨(font-bold
)가 적용됩니다.bg-blue-400
)과 흰색 텍스트(text-white
)로 스타일링되어 있으며, 클릭 시 새로운 항목을 추가할 수 있는 기능을 암시합니다.memo..
텍스트가 있는 공간은 여유 공간(min-h-32
)과 패딩(p-3
)을 통해 내용이 추가될 때 깔끔하게 표시됩니다.CanvasCard
컴포넌트를 재사용할 수 있어 코드의 중복을 줄입니다.isSubtitle
prop을 통해 섹션의 중요도에 따라 스타일을 동적으로 변경할 수 있습니다.src/components/LeanCanvas.jsx
import CanvasCard from './CanvasCard';
function LeanCanvas() {
return (
<div className="border-4 border-black">
<div className="grid grid-cols-5">
<CanvasCard title={'1. 문제'} />
<CanvasCard title={'4. 해결안'} />
<CanvasCard title={'3. 가치제안'} />
<CanvasCard title={'5. 경쟁우위'} />
<CanvasCard title={'2. 목표 고객'} />
<CanvasCard title={'기존 대안'} isSubtitle />
<CanvasCard title={'8. 핵심지표'} />
<CanvasCard title={'상위개념'} isSubtitle />
<CanvasCard title={'9. 고객 경로'} />
<CanvasCard title={'얼리 어답터'} isSubtitle />
</div>
<div className="grid grid-cols-2">
<CanvasCard title={'7. 비용 구조'} />
<CanvasCard title={'6. 수익 흐름'} />
</div>
</div>
);
}
export default LeanCanvas;
설명:
LeanCanvas
컴포넌트는 린캔버스의 전체 레이아웃을 그리드 시스템을 활용하여 구성합니다.grid-cols-5
)로 구성되어 다양한 린캔버스 섹션을 배치합니다.grid-cols-2
)로 구성되어 추가적인 섹션을 배치합니다.CanvasCard
컴포넌트를 사용하여 일관된 스타일로 표시됩니다.isSubtitle
prop을 통해 특정 섹션의 스타일을 조정하여 중요한 섹션과 보조 섹션을 시각적으로 구분합니다.CanvasCard
컴포넌트를 활용하여 섹션 추가 및 변경이 용이합니다.CanvasCard
의 props만 조정하면 되므로 코드의 유지보수가 간편합니다.src/pages/CanvasDetail.jsx
import CanvasTitle from '../components/CanvasTitle';
import LeanCanvas from '../components/LeanCanvas';
function CanvasDetail() {
return (
<div>
<CanvasTitle />
<LeanCanvas />
</div>
);
}
export default CanvasDetail;
설명:
CanvasDetail
페이지는 개별 린캔버스의 상세 정보를 표시하는 페이지로, 타이틀과 린캔버스 레이아웃을 포함합니다.CanvasCard
컴포넌트를 활용하여 각 섹션을 일관된 스타일로 렌더링합니다.CanvasTitle
과 LeanCanvas
컴포넌트를 다른 페이지에서도 재사용할 수 있어 코드의 효율성을 높입니다.컴포넌트 분리:
CanvasCard
: 린캔버스의 개별 섹션을 카드 형태로 표시합니다.LeanCanvas
: 린캔버스의 전체 레이아웃을 그리드 시스템으로 구성합니다.CanvasTitle
: 린캔버스의 타이틀을 표시하고 편집할 수 있는 기능을 제공합니다.CanvasItem
, CanvasList
, SearchBar
, ViewToggle
, Main
, Header
)도 각 기능에 따라 분리되어 코드의 재사용성과 유지보수성을 높였습니다.상태 관리:
Home
페이지에서 searchText
, isGridView
, dummyData
등의 상태를 관리하여 자식 컴포넌트에 전달합니다.handleDeleteItem
함수를 통해 dummyData
상태를 업데이트합니다.반응형 디자인:
grid-cols-5
와 grid-cols-2
클래스를 사용하여 그리드의 열 수를 조정하고, sm:flex-row
클래스를 통해 작은 화면에서는 세로로, 큰 화면에서는 가로로 레이아웃을 변경합니다.접근성:
aria-label
속성을 사용하여 버튼과 입력 필드의 목적을 명확히 전달했습니다. 이는 스크린 리더 사용자에게 유용합니다.애니메이션과 트랜지션:
transition-transform
, duration-300
, hover:scale-105
등을 활용하여 사용자 인터랙션 시 부드러운 애니메이션 효과를 추가했습니다.아이콘 사용:
react-icons
라이브러리를 사용하여 시각적인 아이콘을 추가함으로써 UI의 직관성과 미적 요소를 강화했습니다.FaPlus
아이콘을 사용하여 추가 기능을, FaEdit
아이콘을 사용하여 편집 기능을 직관적으로 표시했습니다.이번 실습에서는 레이아웃 UI를 구현하여 린캔버스의 각 섹션을 체계적으로 관리하고 표시할 수 있는 구조를 만들었습니다. 컴포넌트 분리를 통해 코드의 재사용성과 유지보수성을 높였으며, Tailwind CSS와 React Icons를 활용하여 깔끔하고 반응형인 사용자 인터페이스를 구현했습니다. 또한, 상태 관리를 통해 동적인 UI 업데이트와 사용자 상호작용을 효과적으로 처리했습니다.