-
Notifications
You must be signed in to change notification settings - Fork 50
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
경북대 FE_정서현_2주차 과제 Step 1/2/3 #42
base: 1oveydev
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.
안녕하세요~
1주차 과제때도 동일한 문제가 있었는데, 따로 언급하지는 않았어요.
vscode 익스텐션 중에서 eslint와 prettier를 설치하시고 프로젝트가 정상적으로 동작하는지 확인해주시겠어요~?
현재 에러가 많아 프로젝트를 확인하기 어려운 상황이에요.
에러가 해결되고 나면 추가로 리뷰하겠습니다
const handleNavigate = (path: string) => { | ||
navigate(path); | ||
}; |
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.
강사님이 올려주신 카카오 선물하기 예제에서는 '카카오 선물하기' 라는 버튼을 그냥 텍스트로 만들어 두셨습니다.
저는 실제 카카오 선물하기 사이트를 참고해 로고 이미지를 따와서 Button 처럼 사용하고 싶었습니다.
그래서 해당 로고를 div 태그 기반의 GiftText
라는 스타일 컴포넌트를 클릭 시 이동할 경로를 지정하기 위해 해당 함수를 작성하였습니다.
또, 헤더의 상단 우측에 존재하는 로그인 (내 계정) 버튼은 text를 버튼 처럼 사용하고 있어서, 클릭 시 이동할 경로를 지정해줄 함수가 필요하다고 생각했습니다!
<GiftText onClick={() => handleNavigate('/')}>
<HeaderLogo src="https://gift-s.kakaocdn.net/dn/gift/images/m640/pc_gift_logo.png" alt="카카오 선물하기 로고" />
</GiftText>
{isAuth ? (
<MyAccount onClick={() => handleNavigate('/my-account')}>내 계정</MyAccount>
) : (
<Login onClick={() => handleNavigate('/login')}>로그인</Login>
)}
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.
앗 제 말은 onClick={() => navigate('/')}
처럼 navigate
를 그대로 사용하는 것과 어떤 차이가 있는지 확인하는 것이었어요 ㅎㅎ
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.
아! 그런 뜻이었구나.. 동일한 기능을 함수화 해야겠다는 생각만 잔뜩 가지고 있었던 것 같습니다 ㅠㅠ 오히려 제가 남용하고 있었네요.
handleNavigate
함수는 한줄로 이루어진 코드라 따로 함수화 할 필요가 없었네요!
const MenuButton = ({ icon, text, active, onClick }: MenuButtonProps) => { | ||
return ( | ||
<MenuButtonWrapper onClick={onClick}> | ||
<MenuButtonIcon active={active}>{icon}</MenuButtonIcon> | ||
<MenuButtonText active={active}>{text}</MenuButtonText> | ||
</MenuButtonWrapper> | ||
); | ||
} |
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.
MenuButton은 button이어야 하지 않을까요~?
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.
공통 컴포넌트에 선언해놓은 Button 컴포넌트를 사용하라는 말씀이실까요?
이해를 잘 못했습니다 죄송합니다 ㅠㅠ
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.
만들어두신 MenuButton
컴포넌트를 가져가서 어디선가 사용한다고 생각해 볼게요.
<div>
<span>버튼을 눌러주세요</span>
<MenuButton onClick={ ... } />
</div>
대충 이런 코드를 작성했다고 생각하면, 이제 유저가 MenuButton
을 눌러서 onClick이 호출되겠죠?
그런데 이때 유저가 누른 MenuButton
은 정말 button
이 맞을까요?
버튼처럼 동작하게 만든 것일 뿐, 버튼이라고 말할 수 있을까요?
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.
만들어두신
MenuButton
컴포넌트를 가져가서 어디선가 사용한다고 생각해 볼게요.<div> <span>버튼을 눌러주세요</span> <MenuButton onClick={ ... } /> </div>대충 이런 코드를 작성했다고 생각하면, 이제 유저가
MenuButton
을 눌러서 onClick이 호출되겠죠? 그런데 이때 유저가 누른MenuButton
은 정말button
이 맞을까요? 버튼처럼 동작하게 만든 것일 뿐, 버튼이라고 말할 수 있을까요?
아하! 그런 뜻이었군요
다음 처럼 div 엘리먼트에서 button 엘리먼트로 수정하였습니다!
그런데 수정하면서 한가지 궁금한 점이 생겼습니다.
"버튼처럼 동작하게 만든 것일 뿐, 버튼이라고 말할 수 있나요?" 라고 말씀하셨는데 버튼과 동일하게 동작하나 실제로 버튼이 아니었을 때 생기는 문제점이 있을까요?
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.
이 컴포넌트가 다소 복잡도가 높고 각각의 역할이 명확하지 않다고 생각하는데요 불필요한 상태가 남용되고 있지는 않을까요? 그리고 여러 항목 중에서 하나의 항목을 선택하는 패턴이 반복된다면 추상화가 가능하지 않을까요?
코드를 작성할 때에는 필터와 버튼을 따로 state 로 두어야 한다고 생각했는데 막상 시간이 지나고 다시보니 불필요한 state 였다는 것을 깨달았습니다..
왜 다 지나고 나면 보이는 걸까요? ㅠㅠ
activeButton과 wishButton 상태는 genderFilter와 wishFilter에 의해 이미 관리되고 있으므로 제거해도 좋을 것 같습니다!
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.
const [genderFilter, setGenderFilter] = useState<GenderFilterType>('ALL');
const [wishFilter, setWishFilter] = useState<WishFilterType>('받고 싶어한');
const handleGenderFilterChange = (filter: GenderFilterType) => {
setGenderFilter(filter);
};
const handleWishFilterChange = (filter: WishFilterType) => {
setWishFilter(filter);
};
const isActiveButton = (value: GenderFilterType | WishFilterType, activeValue: GenderFilterType | WishFilterType) => {
return value === activeValue;
};
위는 불필요한 state 를 없앤 수정된 코드이며,
아래는 반복되는 패턴을 추상화한 수정된 코드입니다!
const wishFilterItems = [
'받고 싶어한',
'많이 선물한',
'위시로 받은'
] as const
type WishFilterType = (typeof wishFilterItems)[number]
<GiftRankingMenuBottom>
{wishFilterItems.map((text) => (
<GiftRankingMenuBottomText
key={text}
active={isActiveButton(text, wishFilter)}
onClick={() => handleWishFilterChange(text)}
>
{text}
</GiftRankingMenuBottomText>
)
)}
</GiftRankingMenuBottom>
const [genderFilter, setGenderFilter] = useState('ALL'); | ||
const [wishFilter, setWishFilter] = useState('받고 싶어한'); | ||
|
||
const handleGenderFilterChange = (filter: string) => { | ||
setGenderFilter(filter); | ||
}; | ||
|
||
const handleWishFilterChange = (filter: string) => { | ||
setWishFilter(filter); | ||
}; |
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.
string 타입보다 조금 더 안전하게 관리할 수 있지 않을까요?
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.
string 타입보다 조금 더 안전하게 관리할 수 있지 않을까요?
type GenderFilterType = 'ALL' | '남성이' | '여성이' | '청소년이';
type WishFilterType = '받고 싶어한' | '많이 선물한' | '위시로 받은';
const [genderFilter, setGenderFilter] = useState<GenderFilterType>('ALL');
const [wishFilter, setWishFilter] = useState<WishFilterType>('받고 싶어한');
const handleGenderFilterChange = (filter: GenderFilterType) => {
setGenderFilter(filter);
};
const handleWishFilterChange = (filter: WishFilterType) => {
setWishFilter(filter);
};
이렇게 코드를 작성하면 좀 더 안전하게 관리가 가능할 것 같습니다!
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.
좋습니다 ㅎㅎ 그렇다면 이건 어떨까요?
여러개의 필터 아이템들을 일일히 작성하지 않고 프로그래매틱하게 코드로 그려주는 방법은 없을까요?
Array.prototype.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.
좋습니다 ㅎㅎ 그렇다면 이건 어떨까요?
여러개의 필터 아이템들을 일일히 작성하지 않고 프로그래매틱하게 코드로 그려주는 방법은 없을까요?
Array.prototype.map
으로 그려주면 좋을 것 같은데요!다만 현재 방식으로는 조금 어려울 것 같은데 어떻게 수정하면 좋을까요?
다음처럼 작성할 수 있을 것 같습니다!
더 좋은 방법이 있을까요?
const filterItems: { icon: string; text: string, value: GenderFilterType }[] = [
{ icon: 'ALL', text: '전체', value: 'ALL' },
{ icon: '👩🦰', text: '여성이', value: '여성이' },
{ icon: '👱♂️', text: '남성이', value: '남성이' },
{ icon: '👦🏻', text: '청소년이', value: '청소년이' },
];
<GiftRankingMenuTop>
{filterItems.map((item) => (
<MenuButton
key={item.value}
icon={item.icon}
text={item.text}
active={activeButton === item.value}
onClick={() => handleButtonClick(item.value)}
/>
))}
</GiftRankingMenuTop>
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.
좋습니다~! 그럼 조금 더 고민해볼까요?
만약 '개발자가' 라는 항목이 추가되어야 한다면 지금은 GenderFilterType도 수정해야 하고, filterItems도 수정해야하는데요, 혹시 한 군데만 수정하면 되도록 해볼수는 없을까요??
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.
만약 '개발자가' 라는 항목이 추가되어야 한다면 지금은 GenderFilterType도 수정해야 하고, filterItems도 수정해야하는데요, 혹시 한 군데만 수정하면 되도록 해볼수는 없을까요??
꽤 오래 고민해보았는데, 저는 마땅한 방법이 생각나지 않아서 지피티의 도움을 받아보았습니다 ㅠ
const filterItems = [
{ icon: 'ALL', text: '전체', value: 'ALL' },
{ icon: '👩🦰', text: '여성이', value: '여성이' },
{ icon: '👱♂️', text: '남성이', value: '남성이' },
{ icon: '👦🏻', text: '청소년이', value: '청소년이' },
]
type GenderFilterType = (typeof filterItems)[number]['value']
지피티는 이런 방식을 추천하던데, 저는 위와 같은 방법이 생소하여 맞는지 잘 모르겠습니다..
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.
타입스크립트는 사용하시다보면 익숙해지실거라 이런게 있구나~ 정도만 알아두시면 좋을 것 같아요.
지금 작성된 방식대로면 아마 GenderFilterType은 string으로 추론될 것 같은데요, filterItems를 as const로 선언하여 리터럴 타입으로 추론하게 만들고 value를 추출하면 유니온 타입이 될 것 같아요
무슨 말인지 몰라도 괜찮습니다.. ㅎㅎ 개발하시다 보면 자연스럽게 알게 되실거에요.
const filterItems = [
{ icon: 'ALL', text: '전체', value: 'ALL' },
{ icon: '👩🦰', text: '여성이', value: '여성이' },
{ icon: '👱♂️', text: '남성이', value: '남성이' },
{ icon: '👦🏻', text: '청소년이', value: '청소년이' },
] as const;
type GenderFilterType = (typeof filterItems)[number]['value']
// 'ALL' | '여성이' | '남성이' | '청소년이'
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.
타입스크립트는 사용하시다보면 익숙해지실거라 이런게 있구나~ 정도만 알아두시면 좋을 것 같아요.
지금 작성된 방식대로면 아마 GenderFilterType은 string으로 추론될 것 같은데요, filterItems를 as const로 선언하여 리터럴 타입으로 추론하게 만들고 value를 추출하면 유니온 타입이 될 것 같아요
const filterItems = [ { icon: 'ALL', text: '전체', value: 'ALL' }, { icon: '👩🦰', text: '여성이', value: '여성이' }, { icon: '👱♂️', text: '남성이', value: '남성이' }, { icon: '👦🏻', text: '청소년이', value: '청소년이' }, ] as const; type GenderFilterType = (typeof filterItems)[number]['value'] // 'ALL' | '여성이' | '남성이' | '청소년이'
as const
를 작성해줌으로써 GenderFilterType 의 type을 리터럴 상수로 추론하게 하는 것이군요!
TS는 알면 알수록 신기한 것 같아요..
const baseItem = { | ||
imageSrc: | ||
'https://st.kakaocdn.net/product/gift/product/20231030175450_53e90ee9708f45ffa45b3f7b4bc01c7c.jpg', | ||
subtitle: 'BBQ', | ||
title: 'BBQ 양념치킨 + 크림치즈볼 + 콜라1.25L', | ||
amount: 29000, | ||
}; | ||
|
||
const items = Array(21) | ||
.fill(baseItem) | ||
.map((item, index) => ({ ...item, rankingIndex: index + 1 })); | ||
|
||
const initialItems = items.slice(0, 6); | ||
const moreItems = items.slice(6, 21); |
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.
메모리 관점에서 어느 스코프에 존재하는게 조금이나마 이득일까요?
메모리 관점에서, 해당 변수들은 함수형 컴포넌트 내부에 존재하는 것이 이득이 아닐까요?
전역 스코프에 존재하게되면 어플리케이션 전체에서 계속 존재하게 되는데 메모리 사용량이 증가하는 것으로 알고 있습니다.
조금 관련 내용을 찾아보니, 위 코드처럼 작성하게 되면 컴포넌트가 재 렌더링 될 때마다 해당 변수들이 계속 새로 생성된다고 합니다.
이 부분을 개선하는 방법으로 useMemo() 훅을 사용하면 컴포넌트가 재 렌더링 되더라도 동일한 데이터를 반복해서 생성하지 않게 된다는 것을 알게 되었습니다.
따라서, 코드를 다음과 같이 수정하였습니다.
const baseItem = useMemo(() => ({
imageSrc:
'https://st.kakaocdn.net/product/gift/product/20231030175450_53e90ee9708f45ffa45b3f7b4bc01c7c.jpg',
subtitle: 'BBQ',
title: 'BBQ 양념치킨 + 크림치즈볼 + 콜라1.25L',
amount: 29000,
}), []);
const items = useMemo(() => Array(21)
.fill(baseItem)
.map((item, index) => ({ ...item, rankingIndex: index + 1 })), [baseItem]);
const initialItems = useMemo(() => items.slice(0, 6), [items]);
const moreItems = useMemo(() => items.slice(6, 21), [items]);
rankingIndex={item.rankingIndex} | ||
imageSrc={item.imageSrc} | ||
subtitle={item.subtitle} | ||
title={item.title} | ||
amount={item.amount} |
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.
전부 나열하지 않고 더욱 간결하게 표현할 수 있지 않을까요?
전부 나열하지 않는 표현법이 있는지 잘 모르겠습니다.. 어떤 방법이 있을까요?
{initialItems.map(({ imageSrc, subtitle, title, amount, rankingIndex }, index) => (
<RankingGoodsItems
key={index}
rankingIndex={rankingIndex}
imageSrc={imageSrc}
subtitle={subtitle}
title={title}
amount={amount}
/>
))}
const themes = [ | ||
{ key: '1', name: '생일', src: 'https://img1.kakaocdn.net/thumb/[email protected]/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240417111629_616eccb9d4cd464fa06d3430947dce15.jpg' }, | ||
{ key: '2', name: '졸업 선물', src: 'https://img1.kakaocdn.net/thumb/[email protected]/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20230822215558_21704445865c4dd2aa62d1d08c638778.jpg' }, | ||
{ key: '3', name: '스몰 럭셔리', src: 'https://img1.kakaocdn.net/thumb/[email protected]/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240620131349_b18e29d16a554ca3b8600dbefce6b75c.jpg' }, | ||
{ key: '4', name: '명품 선물', src: 'https://img1.kakaocdn.net/thumb/[email protected]/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20201113110103_c6f6cddc6bf04f06be7cbd50329201e1.jpg' }, | ||
{ key: '5', name: '결혼/집들이', src: 'https://img1.kakaocdn.net/thumb/[email protected]/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240530084619_cbd8d48dfa244611821cefad235f5977.jpg' }, | ||
{ key: '6', name: '따뜻한 선물', src: 'https://img1.kakaocdn.net/thumb/[email protected]/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240528154243_ce80237207fc44e19a40c024d0229bc8.jpg' }, | ||
{ key: '7', name: '가벼운 선물', src: 'https://img1.kakaocdn.net/thumb/[email protected]/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240614093600_c91127a557824d44bc559a4586db0bd3.jpg' }, | ||
{ key: '8', name: '팬심 저격', src: 'https://img1.kakaocdn.net/thumb/[email protected]/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240626144435_f33aa632e99c4faebb39413d59e0e2eb.jpg' }, | ||
{ key: '9', name: '교환권', src: 'https://img1.kakaocdn.net/thumb/[email protected]/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240613144304_821f8c2b12be405ea7648d0039beb431.jpg' }, | ||
{ key: '10', name: '건강/비타민', src: 'https://img1.kakaocdn.net/thumb/[email protected]/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240513132515_5d1e6b4d541b4e0b8ee0930efa70131b.jpg' }, | ||
{ key: '11', name: '과일/한우', src: 'https://img1.kakaocdn.net/thumb/[email protected]/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240516094905_425d02258d7d4fffaccc7219285a951d.jpg' }, | ||
{ key: '12', name: '출산/키즈', src: 'https://img1.kakaocdn.net/thumb/[email protected]/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20220822225448_9aae6412d6f846a8ad681c7a3bf05306.jpg' }, | ||
] |
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.
여기에 선언하는 것보다 더 적절한 곳이 있지 않을까요?
별도의 Theme.tsx
파일을 만들어서 따로 저장하였습니다.
themes 배열이 크고 복잡하므로 import 해서 사용하는 것이 더 적절해 보입니다.
src/pages/LoginPage.tsx
Outdated
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
const { name, value } = e.target | ||
if (name === 'username') { | ||
setUsername(value) | ||
} else if (name === 'password') { | ||
setPassword(value) | ||
} | ||
}; |
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.
이 함수는 적절한 추상화였을까요?
기존의 username, password state 를 form state 하나로 합쳤습니다!
const [formState, setFormState] = useState({ username: '', password: '' })
그리고 이전 객체에서 특정 속성만 업데이트 하는 방식으로 추상화 하였습니다.
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target
setFormState(prevState => ({
...prevState,
[name]: value,
}))
}
엇.. 저도 방금 달아주신 코멘트를 보고 폴더구조를 확인해봤는데 원격에 올라간 빠른 답변 감사합니다! 수정 후 다시 코멘트 달겠습니다 |
관련 사항 수정 후 다시 커밋 올렸습니다! 이런 상황을 처음 겪어봐서, 구글링을 통해 해결방법을 찾아봤는데
이런 수순으로 해결하는것이 맞을까요? |
네네 이제 정상적으로 보이네요! |
빠른 답변 감사합니다! 최대한 이런 상황이 생기지 않도록 노력하는것이 최우선이겠으나 만약 이런 상황이 또 생긴다면 알려주신 방법대로 해보겠습니다! :) |
카카오 테크 캠퍼스 - 프론트엔드 카카오 선물하기 편
📌 2주차 질문
🤔 1. CRA 기반의 SPA 프로젝트에서 React Router 를 사용하지 않는다면 어떤 문제가 발생하는가?
리액트 라우터를 사용하지 않으면, 페이지 간의 전환 및 라우팅 관리를 직접 구현해야하는 문제가 발생한다. 개발자가 직접 해당 기능을 구현하는 것은 매우 복잡하고 오류 발생 가능성이 커서 유지보수가 어려울 수 있다.
따라서 서버 사이드 렌더링 (SSR) 구현이 복잡해지고, Search Engine Optimization 문제가 발생할 수 있다.
🤔 2. React Context 나 Redux 같은 상태관리 라이브러리는 언제 사용하면 좋은가?
React Context 나 Redux 같은 상태관리 라이브러리는
여러 컴포넌트에서 공유해야하는 State
가 있을 때 유용하다.React 는 단방향 데이터 흐름을 따르기에 하위 에서 상위 컴포넌트로 데이터를 전달하기가 어렵다. 이런 경우 상태관리 라이브러리를 활용하여 State 를 전역으로 관리하고, 컴포넌트 간에 직접적인 통신을 간소화할 수 있다.
🤔 3. Local Storage, Session Storage 와 Cookies 의 차이가 무엇이며 각각 어떨 때 사용하면 좋은가?
Local Storage
클라이언트 측 웹 스토리지이며, 도메인 별로 데이터를 저장한다.
데이터는 브라우저를 종료해도 지속된다.
JavaScript 의
localStorage
객체를 통해 접근하며 key-value 쌍으로 접근한다.→ 지속적으로 필요한 데이터를 저장할 때, 브라우저를 닫았다가 다시 열어도 유지해야할 경우에 유용하다.
Session Storage
클라이언트 측 웹 스토리지이며, 세션 동안 데이터를 저장한다.
브라우저 탭이나 창이 열려있는 동안에만 데이터가 유지된다. 브라우저가 닫히면 데이터가 삭제된다.
JavaScript 의
sessionStorage
객체를 통해 접근하며 key-value 쌍으로 접근한다.→ 세션동안 지속될 데이터가 필요한 경우, 예를 들어 사용자가 웹 어플리케이션에 로그인 한 상태 등에 적합하다.
Cookie
서버 및 클라이언트 간의 데이터 교환을 위해 사용된다.
데이터는 클라이언트의 파일 시스템에 저장된다.
JavaScript 의
document.cookie
API 를 통해 접근하며, 문자열 형태로 데이터를 저장한다.→ HTTP 요청 시 서버에 자동으로 전송되므로 인증 토큰이나 세션 ID 와 같은 인증정보를 저장할 때 유용하다.
✓ 2주차 체크 리스트
Header, Footer 와 같은 공통 컴포넌트 작성
각 Url Path 별로 페이지 만들기
Main page (
/
)Theme 카테고리 섹션 추가
Theme 카테고리 Item 클릭 시 Theme page(
/theme/:themekey
) 로 이동Theme page (
/theme/:themekey
)재사용성을 고려하여 Header 섹션 추가
상품 목록 섹션 추가
Login page (
/login
)ID와 PW를 입력하면 로그인이 되도록 구현
로그인 페이지에서 ID와 PW를 입력하면 ID를 Session Storage의
authToken
key 에 저장/my-account
페이지는 로그인 한 사람만 접근 가능하게 구현모든 페이지 진입 시
authToken
을 토대로 로그인 여부를 판단하는 로직 추가Header 에서 로그인 한 경우 내 계정을 로그인 하지 않은 경우 로그인 버튼을 따로 추가
My page (
/my-account
)