-
Notifications
You must be signed in to change notification settings - Fork 10
[2주차] 김서연 미션 제출합니다. #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
디자인이 너무 귀여운 것 같고 캘린더 직접 만든거 대단하세요 !!
제가 최적화 쪽으로 정말 문외한이라 이해해주시면 감사하겠습니당 🙇♀️
{["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"].map((day) => ( | ||
<div key={day}>{day}</div> | ||
))} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
map 때문에 배열이 불필요하게 재생성 되는 것 같은데 forEach를 써보는건 어떨까요
onKeyDown={(e) => { | ||
if (e.key === "Enter" && inputValue.trim() !== "") { | ||
addTodo(selectedDate, inputValue); | ||
setInputValue(""); // 입력창 초기화 | ||
} | ||
}} | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 함수에서 리엑트 상태 관리 비동기성 때문에 이거 제대로 작동 안 할 가능성이 큽니당 (strict mode라 그럴 가능성이 더 큼!)
바로 e.currentTarget.value 써주는게 더 안정적일 거에요
그리고 keyDown은 isComposing 써줘서 한글 입력 오류 안 나도록 설정할 수 있습니당!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
헉 확인 못했던 부분이었는데 좋은 피드백 감사합니다!!
.map((todo, index) => ( | ||
<TodoItem | ||
key={`${selectedDate}-${index}`} | ||
todo={todo} | ||
index={index} | ||
selectedDate={selectedDate} | ||
deleteTodo={deleteTodo} | ||
toggleTodo={toggleTodo} | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
마찬 가지로 map을 사용해서 배열을 한번 더 만들어줄 필요는 없을 것 같습니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
몰랏는데 map을 써야해요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
하핫
{index + 1} | ||
</DateNumber> | ||
<TodoPreview> | ||
{uncompletedTodos.slice(0, 2).map((todo, i) => ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
다른 분 코드 리뷰에서 봤는데 key값으로 index를 쓰는 게 안 좋다라고 하시더라 구용
사실 저도 index 썼습니다 하핫
링크 첨부 하고 갑니당~~
key에 index 넣으면 안되는 이유
import TodoList from "./TodoList"; | ||
import { ModalOverlay, ModalContent, CloseButton } from "../styles/StyledModal"; | ||
|
||
const Modal = ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
React.Memo 빠진 것 같습니당
const year = today.getFullYear(); | ||
const month = String(today.getMonth() + 1).padStart(2, "0"); // 월은 0부터 시작하므로 +1 | ||
const day = String(today.getDate()).padStart(2, "0"); // 1자리 수일 경우 0 추가 | ||
return `${year}-${month}-${day}`; // "YYYY-MM-DD" 형식으로 반환 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
구버전 IOS는 2025/08/07 이것만 알아 들었던걸 알고 계시나요...
제가 이거 파싱 문제 때문에 프로젝트에서 고생했었는데...
혹시 다른 곳에서 날짜 적용이 안된다하면 구버전 IOS를 사용하고 있어서 그런겁니다 하핫
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
몰랐던 사실이에요 /로 구분하는 게 더 안전한 방법이겠네요!! 좋은 정보 감사합니당
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
다들 캘린더를 직접 구현하시는 것 같은데 다들 너무 대단한 것 같아요,, 리액트 많이 안다뤄보신걸로 아는데 잘하시네요... key 값을 index로 설정해서 key값 중복으로 로직이 꼬이셨다고 하는데 이런 이유 때문에 key값을 인덱스로 쓰는 것은 리액트 공식 문서에서도 권장되고 있지 않아요! id와 같이 고유한 값을 key값으로 주는게 좋습니다~ 2주차 과제하시느라 수고하셨습니다!
{isModalOpen && ( | ||
<Modal | ||
selectedDate={selectedDate} | ||
closeModal={closeModal} | ||
todos={todos} | ||
addTodo={addTodo} | ||
deleteTodo={deleteTodo} | ||
toggleTodo={toggleTodo} | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
모달은 기술적으로 body 아래의 div로 출력되면 더 직관적이고 접근성이 더 좋기 때문에 Portal을 사용하는 게 좋습니다! 주희님 코드 참고하시면 좋을 것 같아요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안 그래도 주희님 코드리뷰하며 createPortal을 처음 알게 되었습니당... 적용해봐야겠어요!!
const closeModal = () => { | ||
setIsModalOpen(false); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이렇게 한줄짜리 코드는 따로 함수로 빼지 않고 바로 closeModal={() => setIsModalOpen(false)}
처럼 적어주는게 더 직관적일 것 같아요!
export const CalendarDays = styled.div` | ||
display: grid; | ||
grid-template-columns: repeat(7, 1fr); | ||
text-align: center; | ||
font-weight: bold; | ||
color: #4caf50; | ||
padding: 1rem; | ||
`; | ||
|
||
export const CalendarGrid = styled.div` | ||
display: grid; | ||
grid-template-columns: repeat(7, 1fr); | ||
gap: 0.5rem; | ||
padding: 1rem; | ||
`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 그리드 사용해서 캘린더 구현하셨네요👍
onKeyDown={(e) => { | ||
if (e.key === "Enter" && inputValue.trim() !== "") { | ||
addTodo(selectedDate, inputValue); | ||
setInputValue(""); // 입력창 초기화 | ||
} | ||
}} | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
background-color: #8bc34a; | ||
color: white; | ||
font-size: 1rem; | ||
border: none; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
버튼에서 border: none을 반복해서 사용하는 것 같은데 Global Style에서 이 부분을 리셋해줬으면 더 좋았을 것 같아요!
import { | ||
TodoContainer, | ||
TodoHeader, | ||
TodoInput, | ||
AddButton, | ||
TodoBoard, | ||
SectionTitle, | ||
TodoListWrapper, | ||
} from "../styles/StyledTodoList"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분 하나하나 import 하지 말고 import * as S from '../styles/StyledTodoList';
처럼 사용하면 좋을 것 같아요! 철흥님이 이 방식으로 작성하신 것 같습니다~!
onClick={() => { | ||
addTodo(selectedDate, inputValue); | ||
setInputValue(""); // 입력창 초기화 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
개인적으로는 이렇게 두 줄 이상으로 코드가 늘어나면 함수로 따로 빼는게 더 좋다고 생각됩니다!
@font-face { | ||
font-family: "MyFont"; /* 원하는 폰트 이름 지정 */ | ||
src: url("./assets/fonts/MyCustomFont.ttf") format("truetype"); | ||
font-weight: normal; | ||
font-style: normal; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
혹시 GlobalStyles랑 index.css 모두에 font-face를 명시하신 이유가 따로 있을까요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
폰트 적용이 제대로 안 됐던 적이 있어 이리저리 추가하다가 빼는 걸 까먹었나 봅니다...😱
cursor: pointer; | ||
position: relative; | ||
outline: none; | ||
transition: all 0.2s ease-in-out; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 애니메이션 구현 멋져요 👍
배포 링크
React | To-do List
느낀 점
처음 리액트로 코드를 옮길 때 styled component를 사용하지 않고 전부 className으로 복잡하게 코드를 짰다가 전부 다시 옮기는 과정을 거쳤어요... 하하 과정은 너무 힘들었지만 옮겨보고 나니 styled component로 코드를 짜는 게 훨씬 가독성도 좋고 편리하다고 느껴져서 왜 사용하고 있는지 몸소 깨닫게 된 소중한 경험이었습니다 앞으로도 계속 사용하게 될 것 같아요!
또 비동기 때문에 버튼을 눌러도 UI가 한 박자 늦게 바뀌고, key 값을 index로 설정했다가 완료/미완료 리스트 사이에 key값 중복으로 로직이 꼬이는 오류 같은 자잘한 문제들을 해결하느라 힘들었는데 비슷한 문제를 겪은 분들이 계시다면 어떤 과정으로 어떻게 코드를 짜셨을지 궁금합니다! 이번에도 다른 분들 코드와 피드백을 열심히 확인해 많이 배워가야겠습니다! ㅎㅎ
Key Questions
1️⃣ Virtual-DOM이란?
Virtual-DOM
2️⃣ React.memo(), useMemo(), useCallback()
📌 React.memo()
HOC (Higher-Order Components)
사용법
📌 useMemo()
hook
사용법
📌 useCallback()
hook
사용법
정리
React.memo()
useMemo()
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useCallback()
const onSave = useCallback(() => saveToServer(name, age), [name, age]);
3️⃣ React 컴포넌트의 생명주기
📌 생명주기
마운트
업데이트
언마운트
정리
constructor
→getDerivedStateFromProps
→render
→componentDidMount
getDerivedStateFromProps
→shouldComponentUpdate
→render
→getSnapshotBeforeUpdate
→componentDidUpdate
componentWillUnmount