Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 53 additions & 2 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
@import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css");

/* 전체 배경 레이아웃 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Pretendard", sans-serif; /*프로젝트 전체 폰트 적용*/
}

html, body {
Expand Down Expand Up @@ -45,7 +48,7 @@ html, body {
font-size: 24px;
font-weight: 700;
color: #1F2937;
font-family: Pretendard;
font-family: "Pretendard", sans-serif;
text-align: left;
margin: 0 0 24px 0;
line-height: 36px; /* 150% */
Expand Down Expand Up @@ -79,4 +82,52 @@ html, body {
color: #1F2937;
margin: 0;
font-weight: 400;
}
}


/* 체크된 텍스트의 스타일 (취소선과 흐린 색상) */
.done-text {
text-decoration: line-through;
color: #9CA3AF; /* 시안과 비슷한 흐린 회색 */
}

/* 체크박스와 텍스트를 가로로 예쁘게 배치하기 위해 추가 (필요시) */
.todo-card {
display: flex;
align-items: center; /* 세로 중앙 정렬 */
gap: 12px; /* 체크박스와 글자 사이 간격 */
/* 기존 테두리, 배경색 등 스타일 유지 */
}


/* 빈 상태 (Empty State) */
.empty-state {
background-color: #ffffff;
width: 100%;
min-height: 233px;
display: flex;
flex-direction: column;
align-items: center; /* 가로 중앙 정렬 */
justify-content: center; /* 세로 중앙 정렬 */
border-radius: 12px;
border: 1px solid #E5E7EB;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.10);
gap: 16px; /* 아이콘과 글씨 사이 간격 */
}

/* 클립보드 아이콘 */
.empty-icon {
font-size: 48px;
}

/* '아직 할 일이 없어요' 텍스트 스타일 */
.empty-text {
font-size: 14px;
font-style: normal;
color: #6B7280;
margin: 0;
line-height : 21px;
text-align: center;

}

38 changes: 32 additions & 6 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,44 @@
// src/App.tsx
import React from 'react';

import TodoHeader from './components/TodoHeader';
import TodoList from './components/TodoList';
import './App.css';

function App() {
const App = () => {
// 1. 위쪽에 보여줄 데이터 (꽉 찬 배열)
const populatedTodos = [
{ id: 1, content: "리액트 공식문서 읽기", isDone: true },
{ id: 2, content: "알고리즘 문제 풀기", isDone: true },
{ id: 3, content: "운동 30분 하기", isDone: false },
{ id: 4, content: "프로젝트 회의 준비", isDone: false },
];

// 2. 아래쪽에 보여줄 데이터 (텅 빈 배열)
const emptyTodos: any[] = [];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 emptyTodosany[]로 선언해서 빈 배열을 처리하고 있는데, 이런 방식은 TypeScript 타입 체크를 우회하게 됩니다. Todo 타입을 공용으로 빼거나 같은 타입으로 명시해두면 빈 배열도 더 안전하게 다룰 수 있습니다.


return (
<div className="app-layout">
<div className="todo-container">
<TodoHeader />
<TodoList />

{/* ========================================= */}
{/* 첫 번째 화면: 데이터가 있을 때 (체크박스 토글) */}
{/* ========================================= */}
<div className="section">
<TodoHeader />
<TodoList todos={populatedTodos} />
</div>

{/* ========================================= */}
{/* 두 번째 화면: 데이터가 없을 때 (빈 상태) */}
{/* ========================================= */}
<div className="section" style={{ marginTop: '96px' }}>

<TodoHeader />
<TodoList todos={emptyTodos} />
</div>
Comment on lines +22 to +37
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다음주에는 이 부분을 조건부 렌더링으로 처리해보면 좋을 것 같아요


</div>
</div>
);
}
};

export default App;
31 changes: 31 additions & 0 deletions src/assets/App.css
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 파일이 현재 import 되는 곳이 보이지 않아서 한번 점검 해보시면 좋을 것같아요

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.app-container {
padding: 20px;
background-color: #f5f5f5; /* Figma 배경색*/
min-height: 100vh;
}

.todo-header h1 {
font-size: 36px; /* Figma Typography */
font-weight: 700;
margin-bottom: 30px; /* Figma 간격 */
align-self : stretch;
}

.todo-list {
display: flex;
padding: 16px;
align-items: center;
gap: 10px;
align-self: stretch;

}

.todo-card {
background: #ffffff;
border-radius: 12px; /* Figma Radius */
padding: 16px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* Figma Shadow */
}
26 changes: 21 additions & 5 deletions src/components/TodoCard.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
// src/components/TodoCard.tsx
import React from 'react';
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React import가 현재 코드에서는 직접 사용되지 않는 것으로 보여서, 최종 제출 전에는 사용하지 않는 import를 한 번 더 정리해두는 습관을 들이면 좋겠습니다.


// props 타입을 정의합니다 (TypeScript용)
interface TodoCardProps {
content: string;
isDone: boolean;
}

const TodoCard = ({ content }: TodoCardProps) => {
const TodoCard = ({ content, isDone }: TodoCardProps) => {
return (
<div className="todo-card">
<p className="card-text">{content}</p>
<div className={`todo-card ${isDone ? 'done-card' : ''}`}>
<div className="checkbox-icon">
{isDone ? (
// 🔵 완료 상태 (파란색 채워진 원 + 흰색 체크)
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="#A4C5FD"/>
<path d="M7 12.5L10 15.5L17 8.5" stroke="white" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
) : (
// ⚪ 미완료 상태 (회색 테두리 빈 원)
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" stroke="#E5E7EB" strokeWidth="2"/>
</svg>
)}
</div>

<p className={`card-text ${isDone ? 'done-text' : ''}`}>
{content}
</p>
</div>
);
};
Expand Down
3 changes: 1 addition & 2 deletions src/components/TodoHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// src/components/TodoHeader.tsx
import React from 'react';

const TodoHeader = () => {
const mainTitle = "✅ 오늘의 할 일"; // JSX 바인딩을 위한 변수
const mainTitle = "✅ 오늘의 할 일";

return (
<header className="todo-header">
Expand Down
45 changes: 34 additions & 11 deletions src/components/TodoList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,42 @@
import React from 'react';
import TodoCard from './TodoCard';

const TodoList = () => {
const todo1 = "리액트 공식문서 읽기";
const todo2 = "알고리즘 문제 풀기";
const todo3 = "운동 30분 하기";
const todo4 = "프로젝트 회의 준비";<q></q>
// 1. 부모에게서 받을 데이터의 타입을 정해줍니다.
interface Todo {
id: number;
content: string;
isDone: boolean;
}

interface TodoListProps {
todos: Todo[];
}

// 2. 괄호 안에 { todos }: TodoListProps 를 넣어서 외부 데이터를 받아옵니다!
// 🚨 주의: 이 아래에 const todos = [...] 같은 코드가 절대 있으면 안 됩니다!
const TodoList = ({ todos }: TodoListProps) => {
return (
<main className="todo-list">
<TodoCard content={todo1} />
<TodoCard content={todo2} />
<TodoCard content={todo3} />
<TodoCard content={todo4} />
</main>
<div className="todo-list-wrapper">

{/* 3. 받아온 todos 배열의 길이에 따라 빈 화면을 보여줄지 결정합니다 */}
{todos.length === 0 ? (
<div className="empty-state">
<span className="empty-icon">📋</span>
<p className="empty-text">아직 할 일이 없어요</p>
</div>
) : (
<div className="todo-list">
{todos.map((todo) => (
<TodoCard
key={todo.id}
content={todo.content}
isDone={todo.isDone}
/>
))}
</div>
Comment on lines +29 to +37
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 목록 구조가 div 기반이라서 실제 “리스트” 의미는 조금 약하게 전달됩니다. 같은 속성의 아이템을 나열하는 구조라면 ul / li를 쓰는 쪽이 더 자연스럽습니다.

)}

</div>
);
};

Expand Down