-
Notifications
You must be signed in to change notification settings - Fork 9
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
[2단계 미션] 배강현 미션 제출합니다 #25
base: bae-kh
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.
@bae-kh 👋🏻
2단계 미션도 수고하셨어요! props와 state를 본격적으로 사용하고, React의 여러 항목들을 효율적으로 렌더링하기 위해 map
을 활용할 때 key
값을 붙이는 연습도 하셨을 거에요.
1️⃣ 리뷰
직접 개발 서버를 열고 이것저것 조작해 보았는데 잘 작동하더라고요, props와 state도 용도에 맞게 잘 사용해주셨다고 생각합니다 👍🏻
1단계 미션과 달리 2단계 미션부터는 props와 state를 활용할 수 있게 되었어요. 복잡한 함수가 매개변수로 주어지는 값에 따라 여러 복잡한 일을 잘 해주는 것처럼, 컴포넌트도 props와 state를 잘 활용하면 훨씬 재사용하기에 좋은 컴포넌트들을 많이 만들어보실 수 있을 거라고 생각해요.
이 단계부터는 React에 익숙해지시면서, 컴포넌트를 어떻게 구성해 볼 지를 슬슬 고민해 보시면 좋을 것 같아요.
- 컴포넌트를 각각의 제품으로 보고, 이 제품들을 용도에 따라 재사용에 좋은 컴포넌트로 만들 지, 재사용은 하지 않되 메인 로직을 책임지는 컴포넌트 등으로 만들어 볼 지 등등... 컴포넌트의 용도 에 대해서 고민해 보시면 좋을 것 같아요.
- 컴포넌트를 기능과 용도에 따라 다른 컴포넌트로 분리해 보는 등, 복잡한 기능과 책임을 분산시키는 구조 에 대해서 고민해 보시면 좋을 것 같아요.
물론 한 번에 완벽한 컴포넌트를 짜라는 이야기는 절대 아닙니다 😄 앞으로의 미션들에도, 혹은 실제 프로젝트를 구현하실 때도, 계속해서 좋은 구조를 고민해 보시고, 조금씩 나아가보시라는 의미로 말씀드린 것입니다.
코멘트를 통해서도 고민해 보실만한 주제들을 적어 보았어요. 그럼, 강현님의 의견을 기다리겠습니다!
|
||
const handleCategoryChange = (newCategory) => { | ||
console.log("Selected category:", newCategory); // 카테고리가 변경될 때 출력 | ||
setCategory(newCategory); // 실제 카테고리 상태 변경 |
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.
category = newCategory;
위와 같은 형태의 코드를 사용하지 않으시고, setter 함수를 통해 상태 값을 변경하신 이유는 무엇인가요? 위 방법은 작동할까요?
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.
setter함수를 사용할 경우 상태가 변경될 때마다 컴포넌트를 렌더링하도록 해주기 때문에 사용했습니다.
위 코드 같이 작성할 경우는 상태가 변경될 때 감지하지 못해서 다시 렌더링하지 못할 수 있어서 작동하지 못할 것 같습니다.
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.
상태가 변경될 때 감지하지 못해서
정확합니다. 이렇게 명시된 setter
함수를 사용하는 이유는 React에게 상태가 변화했음을 알리고 이를 기반으로 상태가 변화했을 때 해야하는 적절한 조치들(리렌더링 등)을 수행하기 위해 사용합니다.
JavaScript에서는 category = newCategory
가 용인되겠지만 이렇게 라이브러리나 프레임워크를 사용하는 경우에는 해당 기술에서 제공하는 규칙들이 여럿 있으므로, 항상 공식 문서를 정독하는 습관이 중요합니다 📖
onChange={(event)=> onChangeCategory(event.target.value)} | ||
> | ||
{options.map((option, index) => ( | ||
<option key={index} value={option}> |
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
prop을 잊지 않고 사용해 주셨군요. 👍🏻
여기에 조언을 더 드리자면, key
를 인덱스 값으로 사용하는 것은 React에서의 전형적인 안티 패턴으로 잘 알려져 있습니다. 어쩔 수 없는 상황이 아닌 한 사용을 지양해 주시는 것이 좋다는 의미에요.
물론 미션의 요구사항에 의하면 지금은 key
를 깊게 팔 시기는 아니라는 점은 저도 알고 있습니다. 그렇지만 그냥 안티 패턴이라고만 말씀드리면 제가 일방적으로 "그냥 쓰지 마!" 라고 명령하는 것과 비슷한 상황이 되겠죠? 그래선 안 되잖아요. 그래서 짚어보되, 복잡한 원리 말고 필요성 을 위주로 설명해 볼게요.
React에서 리스트 형태의 컴포넌트 내에서 각 항목에 key
값을 부여하는 이유는 간단히 말해 key
값을 기반으로 더 쉽게 항목들을 식별하고, 변경이 일어났는 지를 알기 위함 입니다. 그렇다면 key
값과 대응되는 항목은 당연히 이후 어떤 변화가 생기든 일치해야겠죠? key 값이 3
인 요소를 React가 찾으려고 할 때, 이후 리스트가 뒤섞이든 3
을 가리키는 요소는 같아야 한다는 의미에요.
인덱스를 사용할 경우에는, 아래의 치명적인 문제점이 있어요. 검은 숫자를 key
값이라고 하고, 직사각형을 각각 컴포넌트라 해 볼 게요. 그리고, 제가 컴포넌트를 이루고 있는 리스트의 첫 번째 위치에 컴포넌트를 하나 끼워넣었어요.
이 경우 어떻게 될까요? key
값이 1
인 항목은 원래 파란색 컴포넌트였는데, 이제는 분홍색 컴포넌트가 되었죠? React가 key
값을 기반으로 컴포넌트를 찾았을 때 잘못된 컴포넌트를 찾게 되는 거에요. 인덱스를 사용하는 경우에는 이런 문제가 생길 수 있습니다. 이 경우 어떤 항목을 지웠는데 전혀 다른 항목이 지워진다든가, 항목이 추가되지 않거나 등등 여러 버그가 발생할 수 있어요.
다행히도 카테고리 필터 컴포넌트에 들어가는 옵션 값들은 실행 도중 바뀌지는 않기 때문에 이러한 불상사는 발생하지 않아요. 그렇지만, 절대 변경이 일어나지 않아! 라고 확신하는 부분이더라도, 가급적이면 유니크한 값을 넣는 습관을 기르시면 좋겠다는 의견 입니다.
지금 이 상황에서는 index
말고도 사용하기 좋은 유니크한 값이 분명 있어요. 어쩌다가 렌더링 순서가 바뀌어도 키와 항목과의 대응 관계가 유지될 수 있는 값이요. 한 번 찾아보시겠어요?
힌트: 리스트의 각 항목이 가지는 값 중에, 절대 겹칠 일이 없는 값이 있다면 그 값은 키로 사용하기 좋은 값이 될 수 있어요.
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 값의 사용에 대해 확신이 없었는데 덕분에 왜 key값을 사용하는지 정확하게 알게되었습니다!
index 말고 사용하기 좋은 유니크한 값으로는 value값이 적절할 것 같습니다.
만약 value값을 사용해도 된다면
<option key={option} value={option}>
이런식으로 작성해도 될까요?
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.
option
은 키 값으로 사용하기 좋은 값이네요 👍🏻
- (이 컴포넌트에서는 그럴 일은 없지만) 리스트 내의 요소들의 순서가 뒤바뀌는 상황에서도 각
<option>
은 그에 대응하는 키들을 가지게 되고 <CategoryFilter>
컴포넌트 특성상 이 컴포넌트에 들어가는 옵션 값들이 겹치도록 의도할 일도 없으니, 각 키 값들이 유니크하다고 할 수 있어요.
이후에 어떤 요구사항이 추가로 주어지거나, 혹은 주어지지 않더라도 이 코드의 일부를 그대로 복사해 다른 컴포넌트에 재사용하는 일이 있더라도 안심할 수 있는 안정적인 방법이라고 생각해요
앞으로도 key
prop이 있다면 유니크함을 보장해줄 수 있는 값이 무엇이 있는지 찾아보시길 바랍니다!
src/App.jsx
Outdated
return <h1>Self-Paced React</h1>; | ||
const [category,setCategory] = useState("전체"); | ||
|
||
let filteredRestaurants= category==="전체" ? restaurants : restaurants.filter((restaurant)=>restaurant.category===category); |
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.
filter
함수 사용 좋아요 👍🏻 코드에서 무엇을 하려는지가 한눈에 보이는 것 같아요~ 삼항 연산자와의 조합도 좋네요filteredRestaurants
변수는 재할당이 한 번도 되지 않는 변수인가요? 그렇다면const
의 사용은 어떨까요?
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.
filteredRestaurants 변수는 재할당은 되지 않지만 재계산이 되는 변수네요. const의 사용이 적절한 것 같습니다. 코드를 작성할 때 재계산과 재할당의 차이가 좀 헷갈려서 그냥 let으로 작성하자 하고 넘겼었는데 다시 짚어주셔서 감사합니다. 다음부터 작성할 때는 좀 더 생각해볼수 있겠네요!
function AddRestaurantModal() { | ||
return ( | ||
<div className={`${styles.modal} ${styles.modalOpen}`}> | ||
<div className={styles.modalBackdrop}></div> | ||
<div className={styles.modalContainer}> | ||
<h2 className={`${styles.modalTitle} text-title`}>새로운 음식점</h2> | ||
<form> | ||
{/* <!-- 카테고리 --> */} | ||
<div className={`${styles.formItem} ${styles.formItemRequired}`}> | ||
<label htmlFor="category" className="text-caption">카테고리</label> | ||
<select name="category" id="category" required> | ||
{options.map((option, index) => ( | ||
<option key={index} value={option}> | ||
{option} | ||
</option> | ||
))} | ||
</select> | ||
</div> | ||
|
||
{/* <!-- 음식점 이름 --> */} | ||
<div className={`${styles.formItem} ${styles.formItemRequired}`}> | ||
<label htmlFor="name" className="text-caption">이름</label> | ||
<input type="text" name="name" id="name" required /> | ||
</div> | ||
|
||
{/* <!-- 설명 --> */} | ||
<div className={styles.formItem}> | ||
<label htmlFor="description" className="text-caption">설명</label> | ||
<textarea name="description" id="description" cols="30" rows="5"></textarea> | ||
<span className="help-text text-caption">메뉴 등 추가 정보를 입력해 주세요.</span> | ||
</div> | ||
|
||
{/* <!-- 추가 버튼 --> */} | ||
<div className={styles.buttonContainer}> | ||
<button className={`${styles.button} ${styles.buttonPrimary} text-caption`}> | ||
추가하기 | ||
</button> | ||
</div> | ||
</form> | ||
</div> | ||
</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.
컴포넌트의 구조가 점점 커져가는 것이 느껴지시나요?
강현님께서 한창 숫자야구의 여러 함수를 구현하실 때, 하나의 함수가 하는 일이 커지면 이를 여러 개의 작은 함수로 나눠 각 함수가 자신이 맡은 하나의 일만 잘 수행하도록 구현해주신 적이 있으실 거에요. 컴포넌트도 마찬가지랍니다.
이제 강현님께서는 state와 props를 사용하실 수 있고, 비슷하게 생긴 UI가 보인다면 이들의 장점을 이용해 비슷한 UI를 두 개의 다른 컴포넌트로 구현하는 대신, state와 props만 바꿔서 이 비슷한 UI를 만들도록 구현하실 수 있게 되었어요. 슬슬 컴포넌트의 장점을 활용해 보시면 좋을 것 같습니다.
물론 때로는 이미 구조가 깔끔한데 과도하게 나누려는 접근 방법이 더 복잡할 수도 있어요. 그러니 "지금 구조는 깔끔한데? 나누지 않을래." 라는 결론을 내리시는 것도 좋아요. 대신 구조에 대해 충분히 고민해 보신 후 이유를 동반한 결정을 내리신다면 좋을 것 같습니다!
아직 미션은 더 남았으니, 지금 이 피드백은 반영하지 않으셔도 좋고 이후 단계에서 컴포넌트를 나눠볼 지, 아니면 유지해 볼 지를 고민해 보세요. 그리고 저 (또는 메인테이너님) 에게 그 생각을 공유해 보시는 것도 좋은 경험이 될 것 같아요.
나누고는 싶은데 뭘 나눠야 할 지 도저히 모르겠어! 라는 생각이라면, 반복해서 사용되는 UI, 굉장히 비슷하게 생긴 UI 를 유심히 살펴 보세요. 재사용을 하기 좋은 청신호일 수 있을 테니까요...!
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.
💡 인터뷰
- React에서
props
와state
는 무엇인가요? - 둘의 차이점은 무엇인가요?
- 어떻게 활용할 수 있을까요?
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.
- props는 부모 컴포넌트가 자식 컴포넌트에게 전달하는 데이터입니다.
부모 컴포넌트가 자식 컴포넌트에 값을 전달하기 때문에 단방향 데이터 흐름을 갖습니다.
props는 읽기 전용으로 자식컴포넌트에서 직접 수정할 수 없습니다다.
state는 컴포넌트 내부에서 관리하는 데이터로 변경 가능한 값입니다.
다른 컴포넌트와 공유되지 않고 컴포넌트의 동적인 부분을 관리하며, 값이 변경될 때마다 해당 컴포넌트는 자동으로 다시 렌더링됩니다.
- 둘의 차이로는 props는 부모 컴포넌트가 관리하고 state는 컴포넌트 자신이 관리합니다.
props는 읽기 전용으로 자식 컴포넌트에서 변경이 불가하지만 state는 변경 가능합니다.
3.props는 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달하거나 상호작용을 위해 함수를 전달할 때 사용합니다. 예를 들어 부모가 자식에게 클릭 핸들러를 전달하여 자식이 클릭 이벤트를 부모에 알릴 수 있습니다.
state는 컴포넌트 내부에서 동적인 상태를 관리합니다. 예를 들어 사용자의 입력 값, 카운트 상태와 같은 내부 변경 사항을 관리하여 즉시 화면에 반영되도록 합니다.
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.
작성하신 질문에 모두 답변드렸습니다 ✔️ 이후 반영하고 싶으신 사항이 있다면 1단계에서의 반영사항을 포함해 모두 이 PR에서 해 주시길 부탁드려요 😊
작업을 완료하시면, 저를 다시 초록 스터디에서 멘션해 주시면 됩니다.
onChange={(event)=> onChangeCategory(event.target.value)} | ||
> | ||
{options.map((option, index) => ( | ||
<option key={index} value={option}> |
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.
option
은 키 값으로 사용하기 좋은 값이네요 👍🏻
- (이 컴포넌트에서는 그럴 일은 없지만) 리스트 내의 요소들의 순서가 뒤바뀌는 상황에서도 각
<option>
은 그에 대응하는 키들을 가지게 되고 <CategoryFilter>
컴포넌트 특성상 이 컴포넌트에 들어가는 옵션 값들이 겹치도록 의도할 일도 없으니, 각 키 값들이 유니크하다고 할 수 있어요.
이후에 어떤 요구사항이 추가로 주어지거나, 혹은 주어지지 않더라도 이 코드의 일부를 그대로 복사해 다른 컴포넌트에 재사용하는 일이 있더라도 안심할 수 있는 안정적인 방법이라고 생각해요
앞으로도 key
prop이 있다면 유니크함을 보장해줄 수 있는 값이 무엇이 있는지 찾아보시길 바랍니다!
|
||
const handleCategoryChange = (newCategory) => { | ||
console.log("Selected category:", newCategory); // 카테고리가 변경될 때 출력 | ||
setCategory(newCategory); // 실제 카테고리 상태 변경 |
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.
상태가 변경될 때 감지하지 못해서
정확합니다. 이렇게 명시된 setter
함수를 사용하는 이유는 React에게 상태가 변화했음을 알리고 이를 기반으로 상태가 변화했을 때 해야하는 적절한 조치들(리렌더링 등)을 수행하기 위해 사용합니다.
JavaScript에서는 category = newCategory
가 용인되겠지만 이렇게 라이브러리나 프레임워크를 사용하는 경우에는 해당 기술에서 제공하는 규칙들이 여럿 있으므로, 항상 공식 문서를 정독하는 습관이 중요합니다 📖
안녕하세요 리뷰어님
이번 미션은 저번 미션을 통해 리액트에 적응하게 되어서 저번 미션보다 수월하게 해결할 수 있었습니다.
공식문서에 자세히 나와있어서 filter나 state, props 공부하고 나니 수월하게 해결했던거 같습니다.
작업 내용
Image Video
Self-Paced.React.-.Chrome.2024-10-22.22-29-36.mp4
Directory Structure