Skip to content

[2주차] 백규민 과제 제출합니다. - #5

Open
baek-32 wants to merge 22 commits into
CEOS-Developers:masterfrom
baek-32:baek-32
Open

baek-32 wants to merge 22 commits into
CEOS-Developers:masterfrom
baek-32:baek-32

Conversation

@baek-32

@baek-32 baek-32 commented Sep 15, 2026

Copy link
Copy Markdown

🔗 배포링크

https://react-memo-24th-three.vercel.app

💭 느낀 점 및 배운 점

1주차에는 HTML, CSS, JavaScript를 사용하면서 querySelector로 요소를 선택하고 DOM을 직접 변경했습니다. 이번 과제에서는 같은 메모 서비스를 React로 다시 구현하면서, State가 변경되면 화면도 함께 변경되는 React의 동작 방식을 배울 수 있었습니다. 처음에는 Props와 State의 차이와 컴포넌트를 어느 정도까지 분리해야 하는지가 가장 헷갈렸습니다. 구현을 진행하면서 공통으로 사용하는 상태는 App에서 관리하고, 자식 컴포넌트에는 필요한 값과 함수를 Props로 전달하는 구조를 이해하게 되었습니다. 또한 검색 결과나 고정 메모 목록처럼 기존 State로 계산할 수 있는 값은 별도의 State로 만들지 않고 filter()를 이용해 계산했습니다. 컴포넌트를 역할별로 분리하면서 파일의 개수는 많아졌지만, 각 파일이 담당하는 역할이 명확해져 코드를 찾고 수정하기 쉬워졌습니다.

💬 Review Questions

1. Virtual DOM은 무엇이고, 이를 사용함으로써 얻는 이점은 무엇인가요?

Virtual DOM은 React가 현재 화면의 모습을 메모리 안에서 표현하기 위해 사용하는 구조입니다. State나 Props가 바뀌면 React는 새로운 화면의 모습을 계산하고, 이전 결과와 비교하여 실제 DOM에 필요한 변경을 반영합니다.

1주차에는 메모를 검색하거나 고정할 때 DOM 요소를 직접 찾아 내용을 바꾸거나 클래스를 추가해야 했습니다. 이번에는 메모 데이터와 검색어를 State로 관리하고, State를 변경하면 그 결과에 맞게 화면이 렌더링되도록 구현했습니다.

이러한 방식은 DOM을 변경하는 순서를 하나씩 작성하지 않아도 되고, 데이터와 화면의 관계를 파악하기 쉽다는 장점이 있습니다. Virtual DOM이 항상 가장 빠른 방법이라는 의미보다는, 복잡한 UI의 변경 과정을 React가 일관된 방식으로 관리해준다는 점이 중요하다고 생각합니다.

2. React에서 컴포넌트를 분리하는 기준은 무엇이며, 컴포넌트 분리를 통해 얻을 수 있는 이점은 무엇인가요?

이번 과제에서는 화면에서 담당하는 역할이 구분되거나, 반복해서 사용하거나, 자체적인 동작을 가진 부분을 컴포넌트로 분리했습니다. 메모 한 개는 MemoItem, 메모 목록은 MemoList, 카테고리 선택은 TagFilter, 상세 화면은 MemoDetail이 담당하도록 구성했습니다. 여러 곳에서 사용하는 아이콘 버튼은 IconButton으로 만들어 재사용했습니다.

이렇게 분리하면 하나의 파일에 모든 코드가 모이는 것을 방지하고, 기능을 수정할 때 관련된 코드를 쉽게 찾을 수 있습니다. 같은 컴포넌트에 서로 다른 Props를 전달하여 재사용할 수 있다는 점도 장점이었습니다.

다만 UI 요소를 무조건 별도 컴포넌트로 나누면 파일을 오가며 코드를 확인해야 해서 오히려 흐름을 이해하기 어려울 수 있습니다. 따라서 단순히 코드의 길이보다는 역할과 재사용 가능성을 기준으로 분리하는 것이 적절하다고 생각합니다.

3. React 컴포넌트의 생명주기에 대해서 설명해주세요.

React 컴포넌트는 화면에 처음 나타나는 마운트, State나 Props의 변화로 다시 렌더링되는 업데이트, 화면에서 사라지는 언마운트 과정을 거칩니다.

함수 컴포넌트에서는 useEffect를 이용하여 렌더링 이후 필요한 작업을 수행할 수 있습니다. Effect가 의존하는 값이 변경되면 기존 작업을 정리한 후 다시 실행하며, 컴포넌트가 화면에서 제거될 때도 정리 함수가 실행됩니다.

이번 과제에서는 카테고리 드롭다운이 열렸을 때 바깥 클릭과 ESC 키를 감지하는 이벤트를 등록했습니다. 드롭다운이 닫힐 때는 해당 이벤트를 제거했습니다. 메모 상세 모달에서도 ESC 키 이벤트를 등록하고, 모달이 사라질 때 이벤트 리스너를 정리하도록 구현했습니다. 이를 통해 컴포넌트가 사라진 뒤에도 이벤트가 계속 남아 있는 문제를 방지할 수 있었습니다.

@sumin0423 sumin0423 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

규민님 2주차 과제 고생하셨습니다! 작성·수정 화면을 재사용하신 부분이 좋았어요. 다음 주차도 화이팅입니다!!! 😊

const [category, setCategory] = useState(initialMemo?.category ?? "");
const [exitModalType, setExitModalType] = useState(null);

const isEditing = initialMemo !== null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

initialMemo 유무로 작성과 수정 화면을 같이 사용하는 부분 좋은 것 같습니다 ! 입력 UI를 따로 두지 않아서 나중에 수정할 때도 한 곳만 확인하면 될 것 같아요~

document.removeEventListener("keydown", handleEscapeKey);
};
}, [isOpen]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

드롭다운이 열렸을 때만 이벤트를 등록하고 닫히면 정리해주신 것과 바깥 클릭이랑 Esc로 닫는 동작까지 아주 좋은 것 같습니다 !


<p className="text-body-large font-medium text-white-00">
{memo.content}
</p>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

본문에 여러 줄을 입력해도 상세 화면에서는 줄바꿈이 합쳐져 보일 수도 있어요. 여기에 whitespace-pre-wrap을 추가하면 작성할 때 넣은 줄바꿈을 그대로 보여줄 수 있어서 이부분 추천드립니다 !

Comment thread src/App.jsx
);

setSelectedMemo(null);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MemoDetail.jsx 에서의 삭제 안내에는 휴지통에서 확인할 수 있다고 되어 있는데, 현재 App.jsx 코드로는 목록에서 바로 제거되는 방식으로 보입니다. 아직 휴지통 기능이 없다면 “삭제하면 복구할 수 없습니다”처럼 실제 동작에 맞춰 안내해주면 좋을 것 같습니다!

@minnngo minnngo left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

이번 과제도 너무 수고 많으셨습니당~~

Comment thread src/index.css
--text-action-medium--line-height: 28px;

--text-action-small: 16px;
--text-action-small--line-height: 24px;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

색상, 폰트 weight, 타이포그래피 스케일을 전부 @theme 토큰으로 정리하신 게 좋았습니다!
덕분에 컴포넌트 쪽 클래스가 의미 단위로 읽혀서 디자인 시스템이 눈에 잘 들어왔네요!

Comment on lines +35 to +43
{onCancel && (
<button
type="button"
className="h-14 flex-1 rounded-xl bg-gray-01 px-6 text-action-medium font-extrabold text-gray-03"
onClick={onCancel}
>
{cancelText}
</button>
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

onCancel로 1버튼/2버튼 모달을 자동 전환하는 구조가 좋네요!
삭제 확인, 작성 취소, 작성 완료까지 3가지 상태를 컴포넌트 하나로 다 커버하고 있어서 효율적인 것 같아요 :)

Comment on lines +14 to +22
const handleCardKeyDown = (event) => {
if (event.target !== event.currentTarget) {
return;
}

if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
onSelectMemo(memo);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

안쪽 핀 버튼에 포커스를 두고 Enter를 눌렀을 때 카드 상세까지 같이 열리는 걸
event.target !== event.currentTarget으로 막으신 점 좋습니다~~!

Comment thread src/App.jsx
Comment on lines +66 to +72
setMemos((currentMemos) => [
{ ...memoDraft, id: nextId, isPinned: false },
...currentMemos,
]);

setIsAddCompleteModalOpen(true);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

isAddCompleteModalOpen만 켜고 isAddingMemo는 끄지 않아서, 완료 모달 뒤에 MemoEditor가 그대로 살아있어요! 시각적으로는 모달에 가려지지만 setIsAddingMemo(false)도 같이 호출해주면 완성도가 더 높아질 것 같습니당!

Comment on lines +31 to +49
<article
className={`flex h-71.25 w-71.25 cursor-pointer flex-col gap-2.5 rounded-[20px] p-5 py-8 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-05 ${categoryStyle.card}`}
role="button"
tabIndex={0}
aria-label={`${title} 상세 보기`}
onClick={handleCardClick}
onKeyDown={handleCardKeyDown}
>
<div className="flex items-center justify-between gap-3">
<h2 className="min-w-0 flex-1 truncate text-heading-small font-bold text-white-00">
{title}
</h2>

<IconButton
icon={isPinned ? pinActiveIcon : pinIcon}
label={isPinned ? "메모 고정 해제" : "메모 고정"}
className="h-7 w-7 shrink-0 focus-visible:outline-white-00 [&_img]:h-5.5 [&_img]:w-5.75"
onClick={handlePinClick}
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

카드 전체를 role="button" tabIndex={0}으로 만들었는데, 그 안에 진짜 <button>이 또 들어있어요! 사소한 부분이기는 하지만 버튼 안의 버튼 구조라 카드는 <article> 시맨틱 그대로 두고 제목만 버튼으로 만들거나, 카드 안에 투명한 "상세 보기" 버튼을 깔아주는 방식이 더 좋을 것 같아요!

Comment thread src/App.jsx
import NoResultsState from "./components/NoResultsState.jsx";

function App() {
const [memos, setMemos] = useState([]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

useState([])라서 새로고침하면 작성한 메모가 전부 사라져요!
lazy initializerlocalStorage를 읽고, useEffectmemos 변경 시 저장하는 흐름을 붙이면 더 좋을 것 같습니당~~ 아랴 링크 내용 확인해보세요!

https://ko.react.dev/reference/react/useState#avoiding-recreating-the-initial-state

Comment on lines +10 to +17
<div className="fixed inset-0 z-60 flex items-center justify-center bg-blue-07/50 px-6">
<section
className="flex h-60 w-full max-w-120 flex-col items-center justify-between rounded-3xl bg-white-00 px-8 pt-12 pb-6 shadow-md"
role="dialog"
aria-modal="true"
aria-labelledby="common-modal-title"
aria-describedby="common-modal-message"
>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

aria-modal, aria-labelledby, aria-describedby까지 꼼꼼히 챙겨주셨네요!👍

다만 지금 fixed inset-0 div라서 모달이 떠 있어도 뒷배경이 휠로 스크롤되고, Tab을 누르면 포커스가 모달 밖으로 새나가요. 네이티브 <dialog> + showModal()로 바꾸면 스크롤 잠금 · 포커스 트랩 · ESC 닫기 · backdrop이 전부 공짜로 따라오니 참고해보세용!

https://developer.mozilla.org/ko/docs/Web/HTML/Reference/Elements/dialog
https://blog.logrocket.com/creating-reusable-pop-up-modal-react/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants