[React] ๋ค์ ๋ง๋๋ React-Messenger (selectorFamily, FaCC)
์ธ์ค์ค ๋ฆฌ์กํธ ๋ฉ์ ์ ๊ณผ์ ๋ฅผ ์ค๋นํ๋ฉด์ 6๊ฐ์ ์ ์ ์ ์ถํ๋ ๊ณผ์ ๋ฅผ ๋ค์ ๋ณด์๋ค. ์๊ฐ์ ์ซ๊ฒจ๊ฐ๋ฉด์ ํ๋ค๋ณด๋, ๊ต์ฅํ ๋๋ฌ์ด ๋ถ๋ถ์ด ๊ฝค ์๋ค. ์ด๋ฐ์ ๋ฐ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ฉด์ ์ด๋ฐ์ ๋ฐ ์ต์ ๊ณผ props๊ฐ ๋ค์ด๊ฐ๊ฒ ๋๊ณ , ๊ทธ๋ ๊ฒ ๋ชฌ์คํฐ ์ปดํฌ๋ํธ๊ฐ ๋์ด๊ฐ๋ค. ์คํ์ผ๋ง์ ํ ๋๋ ์ํ๋ ๋๋ก ๋ ๋๊น์ง ์์ ์ ์์ ์ ๊ฑฐ๋ญํ๋ค๋ณด๋ ๋งค์ฐ ๋ณต์กํด์ก๋ค. ๊ทธ๋์ ์ด๋ฒ ํฌ์คํ ์ 6๊ฐ์ ์ ๊ณผ์ ๋ฌผ ๋ฆฌํฉํ ๋ง์ ๊ธฐ๋ก!
- Recoil selectorFamily
- ์ปค์คํ ํ ์ ์ด์ฉํด ๋ก์ง ์ถ์ํ
- Funtion as Child Component
- ํ์ ๊ฐ๋
1. ๋ค์ด๋ฐ
์ ์ผ ์๊ธํ๋ค. ๊ณ์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ฉด์ ๊ธ๊ธํ๊ฒ ๋ณ์๋ฅผ ๋ง๋ค์ด๊ฐ๋ค. Room, chats, chatList ๋ฑ์ ์ด๋ฆ์ด ๋ณ๋ค๋ฅธ ๊ธฐ์ค ์์ด ์ฌ์ฉ๋๋๊ฒ ์ค์ค๋ก๋ ๊ฐ๋นํ๊ธฐ ํ๋ค ์ ๋์๋ค. ๊ฐ์ธํ๋ก์ ํธ์์๋ ๋ค์ด๋ฐ์ ๊ฝค ์ค์ํ๋ค..!
์ ์ฌ์ง์ด ๋ฐ๋ก ๊ทธ ๋จ์ ์ธ ์๋ค. atom์ ์ด๋ฆ์ 'chatState'์ธ๋ฐ key๋ 'chats'. ์ฌ์ง์ด ํ์ ์ 'RoomType'์ ๋ฐฐ์ด์ด๋ค. ๋ญ ์ด๋ฐ ์ฐ๋ ๊ธฐ๊ฐ์ ์ฝ๋๊ฐ ์์ ์๊ฐ. ์ต๋ํ ๋ช ํํ ๊ธฐ์ค์ ์ก๊ณ ํ์ ๊ณผ ์ํ๋ค์ ์ด๋ฆ๋ถํฐ ์ง์๋ค.
export interface IChat {
userId: number;
content: string;
date: string;
like: boolean;
chatId: string;
}
export interface IChatting {
chattingId: number;
userIdList: number[];
chatList: IChat[];
}
export interface IUser {
userId: number;
userName: string;
profileImage: string;
statusMessage: string;
}
์ฑ ํ๋ํ๋๋ IChat
, ์ฑํ
๋ฐฉ ๊ด๋ จ ํ์
์ IChatting
.
Chatting์ chatList์๋ IChat
ํํ์ ๋ฐฐ์ด์ด ๋ค์ด๊ฐ๋ค.
import chattingsData from '../mocks/chattingsData.json';
import userData from '../mocks/userData.json';
import friendsData from '../mocks/friendsData.json';
export const chattingsState = atom<IChatting[]>({
key: 'chattings',
default: chattingsData.chattings,
});
export const userState = atom<IUser>({
key: 'user',
default: userData.users[0],
});
export const friendsState = atom<IUser[]>({
key: 'friends',
default: friendsData.users,
});
๊ธฐ๋ณธ atom์๋ key + State
ํ์์ผ๋ก ์ด๋ฆ์ ๋ถ์ธ๋ค. ๋ด ์ ๋ณด(user)์ ์น๊ตฌ์ ๋ณด(friends)๋ฅผ ๋ถ๋ฆฌํด์ฃผ์๋ค.
์ฑํ
๋ชฉ๋ก์ Chattings, ๊ฐ ์ฑํ
๋ฐฉ๋ค์ Room์ด๋ผ๋ ๋ค์ด๋ฐ. Chattings๋ฅผ map
์ผ๋ก ๋๋ ค์ ๋ฆฌ์คํธ๋ฅผ ๋ ๋๋งํด์ฃผ๋๋ฐ, map์ ์ฒซ๋ฒ์งธ ์ธ์๋ก chatting์ด๋ผ๋ ์ด๋ฆ์ ์ฌ์ฉํด์ฃผ์๋ค. Room๊ณผ chatting ๋ ์ด๋ฆ์ ๋ชจ๋ ์ฐ๋ ๊ฒ์ ๋ํด์ ๊ณ ๋ฏผ์ ๊ฝค ํ์๋ค. Room์ ์ปดํฌ๋ํธ ์ด๋ฆ, chatting์ ์ํ์ ์ด๋ฆ์ผ๋ก ์๊ฐํ๊ณ ์ง๊ธ๊ณผ ๊ฐ์ด ์์ฑํ๋ค.
2. ๋ฆฌ์ฝ์ผ ํ์ฉํ๊ธฐ (selectorFamily)
๋จผ์ , ๋ฌธ์์์๋ Selector
๋ฅผ ์ด๋ ๊ฒ ์ค๋ช
ํ๋ค.
Selector๋ ํ์๋ ์ํ(derived state)์ ์ผ๋ถ๋ฅผ ๋ํ๋ธ๋ค. ํ์๋ ์ํ๋ฅผ ์ด๋ค ๋ฐฉ๋ฒ์ผ๋ก๋ ์ฃผ์ด์ง ์ํ๋ฅผ ์์ ํ๋ ์์ ํจ์์ ์ ๋ฌ๋ ์ํ์ ๊ฒฐ๊ณผ๋ฌผ๋ก ์๊ฐํ ์ ์๋ค.
๋ผ๊ณ ํ๋๋ฐ.. ํฌ๊ฒ ์๋ฟ์ง๋ ์๋ค. ํ์๋ ์ํ๋ฅผ ๊ฐ์ ธ์จ๋ค(get)๋๊ฑด, atom์ ๊ณง๋๋ก ๊ฐ์ ธ์ค๋๊ฒ ์๋๋ผ ์ํ๋๋๋ก ๊ฐ๊ณตํด์ ๊ฐ์ ธ์ฌ ์ ์๋ค๋ ๊ฒ - ์ด๋ผ๊ณ ์ดํดํ๋ค. SQL์์ SELECT
๋ฌธ์ ์ด์ฉํด ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ํ๋ ๊ฐ์ ์ถ์ถํ๋ ๊ฒ๊ณผ ๋น์ทํ๋ค. ๊ทธ๋ฟ๋ง ์๋๋ผ SQL์ aggregateํจ์์ฒ๋ผ ๊ณ์ฐ๋ ๊ฐ์ ๋ฐ์์ฌ ์ ์๋ค.
SelectorFamily
๋ Selector
์ ๋น์ทํ ํจํด์ด์ง๋ง, get
๊ณผ set
์ ์ฝ๋ฐฑ์ ์ธ์๋ฅผ ์ ๋ฌํ ์ ์๋ค.
atoms/user.ts
export const userStateByUserId = selectorFamily<IUser, number>({
key: 'userByUserId',
get:
(userId: number) =>
({ get }) => {
const user = get(userState);
const friends = get(friendsState);
if (userId === 0) {
return user;
} else {
return friends.filter((friend) => friend.userId === userId)[0];
}
},
});
export const usersStateByUserIdList = selectorFamily<IUser[], number[]>({
key: 'usersByUserIdList',
get:
(userIdList: number[]) =>
({ get }) => {
return userIdList.map((userId) => get(userStateByUserId(userId)));
},
});
๊ฐ๋จํ๊ฒ ์ด๋ฐ์์ผ๋ก ์ฌ์ฉํ ์ ์๋ค. userId๋ฅผ ์ธ์๋ก ๋ฐ์ ํด๋น ์ ์ ์ ์ ๋ณด๋ง ๊ฐ์ ธ์ค๋ selector์ (๊ทธ selector๋ฅผ ์ฌ์ฉํ๋) userId์ ๋ฐฐ์ด์ ์ธ์๋ก ๋ฐ์ ์ ์ ๋ค์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ selector์ด๋ค.
get ๋ด์์ ๋ค๋ฅธ atom์ ์ํ๋ฅผ ์ฐธ์กฐํ ์ ์๋ค. ๋ด ์ ๋ณด์ ์น๊ตฌ ์ ๋ณด๊ฐ ๋ค๋ฅธ atom์ผ๋ก ๋ถ๋ฆฌ๋์ด ์๋๋ฐ,ํ๋์ selector์์ ๋ชจ๋ ๋ถ๋ฌ์ ๋ฆฌํดํ๋๋ก ํ๋ค.
2.1 selector์์ set ์ฌ์ฉํ๊ธฐ
export const chattingStateByChattingId = selectorFamily<IChatting, number>({
key: 'chattingByChattingId',
get:
(chattingId: number) =>
({ get }) =>
get(chattingsState).filter(
(chatting) => chatting.chattingId === chattingId,
)[0],
set:
(chattingId: number) =>
({ set }, newChatting) => {
set(chattingsState, (prev) =>
prev.map((chatting) =>
chatting.chattingId === chattingId
? (newChatting as IChatting)
: chatting,
),
);
},
});
get
๋ง ์์ ๋ readonlyํ ํจ์๋ง ์ ๊ณตํ์ง๋ง set
์ ํ์ฉํ๋ค๋ฉด selector์์ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ํจ์๋ ์ธ ์ ์๋ค. ๊ทธ๋ฅ useRecoilState
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ ์ฒ๋ผ. ์ฑ์ ๋ณด๋ด๋ ๊ธฐ๋ฅ๋ selectorFamily
์์์ ๊ตฌํํด๋ณด์๋ค. ํด๋นํ๋ ์ฑํ
๋ฐฉ์ id์ newChatting์ ์ธ์๋ก ๋ณด๋ธ๋ค. ์ด๋ค ์์ผ๋ก ๋ณด๋ด์ผ ํ์ง??
set ์์ฑ์๋ ์์ ํ์
์ ํจ์๊ฐ ๋ค์ด๊ฐ ์ ์๋ค. ์ ๋ฆฌํ๋ฉด P => ({set}, newValue) => void;
์ ํํ์ด๋ค. ์ธํฐ์ ์๋ก์ด ๊ฐ์ ์ธ์๋ก ๋ณด๋ธ๋ค. ์์ ์ค์ฒฉ๋ ํจ์์ ์ธ์๋ selectorFamily
์ ์ธ์์ด๋ค.
useChat.tsx
const useChat = (chattingId: number) => {
const currentId = useRecoilValue(currentState);
const [chatting, setChatting] = useRecoilState(
chattingStateByChattingId(chattingId),
);
const sendChat = (value: string) => {
if (value.length !== 0 && value.replace(/ /g, '').length !== 0) {
const newChat: IChat = {
userId: currentId,
content: value,
date: dayjs().format(),
like: false,
chatId: uuid(),
};
setChatting({ ...chatting, chatList: [...chatting.chatList, newChat] });
}
};
return { sendChat };
};
export default useChat;
์ปดํฌ๋ํธ์์๋ ์ด๋ ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
useRecoilState(chattingStateByChattingId(chattingId));
3. ์ปค์คํ ํ ์ผ๋ก ๋ก์ง ๋ถ๋ฆฌ
์ปดํฌ๋ํธ ๋ด์์ UI ๋ ๋๋ง์ ๋ด๋นํ๋ ๋ถ๋ถ๊ณผ ๋น์ฆ๋์ค ๋ก์ง์ ์ต๋ํ ๋ถ๋ฆฌํ๋๋ฐ์ ๊ด์ฌ์ด ์๋ค. ์ด๋ฌํ ๊ด์ฌ์ฌ์ ๋ถ๋ฆฌ์ ์ค์์ฑ์ ๋ค๋ฃจ๋ ์ํฐํด๋ค์ด ๊ฝค ๋ง์๋ค. ์กฐ๊ธ ๋ ์ ์ธ์ ์ธ ์ฝ๋๋ฅผ ํตํด ์ปดํฌ๋ํธ๊ฐ ์ด๋ค ์ผ์ ํ๋์ง ๋ช
ํํ๊ฒ ์ ์ ์๊ณ , ๋ณ๊ฒฝ์ด ์ฆ์ UI ์ปดํฌ๋ํธ์ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋ ๋ก์ง์ ๋ถ๋ฆฌํจ์ผ๋ก์ ๋ณ๊ฒฝ์ ์ฉ์ดํ ์ข์ ์ฝ๋๊ฐ ๋ ์ ์๋ค. ๋์ฒด๋ก ์ด๋ฐ ์ด์ผ๊ธฐ๋ฅผ ์ ์ด๋์๋ค.
๋ฐ๋ก ์์์ ์์ฑํ๋ useChat์ด ๊ทธ ๋ถ๋ฆฌ๋ ๋ก์ง์ด๋ค.
RoomFooter.tsx
const RoomFooter = ({ chattingId }: { chattingId: number }) => {
const inputRef = useRef<HTMLTextAreaElement>(null);
const { value, onChange, resetValue } = useInput('');
const { sendChat } = useChat(chattingId);
const focusInput = () => inputRef.current?.focus();
useEffect(() => {
focusInput();
}, []);
const handleSendChat = () => {
sendChat(value);
resetValue();
focusInput();
};
const onEnter = (e: KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === 'Enter' && !e.shiftKey) {
if (e.nativeEvent.isComposing === false) {
e.preventDefault();
handleSendChat();
}
}
};
return (
<Wrapper>
<div>
<MessageInput
value={value}
onChange={onChange}
ref={inputRef}
onKeyDown={onEnter}
/>
<SendButton
onClick={handleSendChat}
disabled={value.length === 0 ? true : false}
>
์ ์ก
</SendButton>
</div>
</Wrapper>
);
};
export default RoomFooter;
์์ง ์ข์ ์ฝ๋๋ผ๊ณ ๋งํ๊ธด ๋ญํ์ง๋ง, UI๋ฅผ ๋ค๋ฃจ๋ ๋ก์ง๋ง ๋จ์์๊ณ ์ํ๋ฅผ ๋ค๋ฃจ๋ ๋ก์ง์ ์ ๋ถ ์ปค์คํ
ํ
์ผ๋ก ์ถ์ํ๋์ด ์๋๋ก ํ๋ค.
์ด์ธ์๋ ์คํฌ๋กค ์์น๋ฅผ ๋ค๋ฃจ๋ ๋ก์ง ๋ฑ์ ์ปค์คํ
ํ
์ผ๋ก ๋นผ๋ฒ๋ฆฌ๋ฉด์ ์ปดํฌ๋ํธ๋ฅผ ๋ ๊ฐ๊ฒฐํ๊ฒ ๋ง๋ค ์ ์๋ค.
3.1 ๋ฆฌ๋ ๋๋ง ๋ฌธ์ ?
์๋๋ ์ปค์คํ ํ ์ ์ด์ฉํด ๊ฐ ํ๋์ atom์ ์ ์ดํ๋ ํจ์๋ค์ ๋ชจ์๋๊ณ ์ปดํฌ๋ํธ์์ ๋ถ๋ฌ์ ์ฌ์ฉํ์๋ค. ๊ทธ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ์ง ์์ง๋ง ํ ์์ ๊ตฌ๋ ํ๊ณ ์๋ ๋ค๋ฅธ ์ํ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ๋ถํ์ํ ์ํฉ์์๋ ๋ฆฌ๋ ๋๋ง๋๋ ๊ฒฝ์ฐ๊ฐ ์์๋ค.
์๋ฅผ๋ค์ด chatting atom์ ์๋ userIdList๋ก user ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด์๋, chattings์ friends ๋ชจ๋๋ฅผ ๊ตฌ๋
ํ๊ณ ์์ด์ผ ํ๋ค. user ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ ๋ก์ง์ด๊ธฐ ๋๋ฌธ์ useUsers
๋ผ๋ ํ
์ ๋ง๋ค๊ณ ๋ฐํํ์๋ค. ๋ฌธ์ ๋ ๋จ์ํ users ์ํ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด์ useUsers
ํ
์ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ์์ ์๊ฒผ๋ค. ํด๋น ์ปดํฌ๋ํธ์์๋ chattings ์ํ์ ๊ด์ฌ์ด ์์ง๋ง, useUsers ํ
์์ chattings๋ฅผ ๊ตฌ๋
ํ๊ณ ์๊ธฐ ๋๋ฌธ์ ๊ทธ ์ํ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์ธ๋ฐ์๋ ๋ฆฌ๋ ๋๋ง์ด ์ผ์ด๋๋ ๊ฒ์ด๋ค.
์ด๋ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ ๋ฆฌ์ฝ์ผ์ selectorFamily
๋ฅผ ๋ ์ ๊ทน์ ์ผ๋ก ์ฌ์ฉํ๊ฒ ๋์๋ค. ๊ฐ๊ฐ์ ๋ก์ง์ selector
๋ด์์ ์ฒ๋ฆฌํ ์ ์๊ฒ ๋๋ฉด์ ๋ถํ์ํ ๋ ๋๋ง์ ๋ง์ ์ ์์๋ค.
4. Function as Child Component
์ฑํ ๋ชฉ๋ก๊ณผ ์น๊ตฌ๋ชฉ๋ก์ item์ ๊ฐ์ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฌ์ฉํ๋ ค๊ณ ํ๋ค. ์ ๋ฒ ๊ณผ์ ์๋ ListItem ์ปดํฌ๋ํธ ํ๋๋ฅผ ์ฌ์ฉํ๋ RoomListItem, FriendsListItem ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๋ค. ์์ ์ปดํฌ๋ํธ๋ฅผ ํ๋ ๋๊ณ ์ํ๋ฅผ ๊ฐ๊ณตํด์ props์ผ๋ก ๋๊ฒจ์ฃผ์ด์ผ ํ๊ธฐ ๋๋ฌธ์ด์๋ค (๋์ผํ ListItemProps ํ์ ์ props๋ฅผ ๋๊ฒจ์ฃผ์ด์ผ ํ๋๋ฐ, ๊ฐ ํ์ด์ง์์ ๊ฐ๊ณ ์๋ ์ํ๋ users์ chattings๋ก ๋ค๋ฅด๋ค). ๋๋ ค ๋ถํ์ํ๊ฒ ์ฝ๋๊ฐ ๋ถ๋ฆฌ๋์ด์ ์คํ๋ ค ๋ถํธํด์ง๋ ๊ฐ์ด ์์๋ค.
์ด๋ฒ ๊ณผ์ ์์๋ ๊ฐ์ ํ์ผ ๋ด์์ ์์ฑํ๋ ListItem ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฌ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ์ ๊ณ ๋ฏผํด๋ณด์๋ค. Function as Child Component ๋ผ๋ ํจํด์ ์ ์ฉํ๋ค. Headless ์ปดํฌ๋ํธ์ ์ผ์ข
์ผ๋ก, ์ํ๋ ์ ์ดํ์ง๋ง ์คํ์ผ๋ง์ ๋ด๋นํ์ง ์๋ ์ปดํฌ๋ํธ๋ฅผ ๋งํ๋ค.
Friends.tsx
const Friends = () => {
const friends = useRecoilValue(friendsState);
const me = useRecoilValue(userState);
return (
<Wrapper>
{/* ...์๋ต */}
<MyProfile>
<Squircle imageUrl={me.profileImage} selected={false} size={55} />
<div>{me.userName}</div>
</MyProfile>
<Divider />
<SubHeading>์น๊ตฌ {friends.length}</SubHeading>
{friends.map((friend) => (
<FriendListHeadless friend={friend} key={friend.userId}>
{({ friend, handleClickListItem }) => (
<ListItem data={friend} handleClickListItem={handleClickListItem} />
)}
</FriendListHeadless>
))}
</Wrapper>
);
};
export default Friends;
friends ๋ฐฐ์ด์ map
์ผ๋ก ๋๋ ค ํ๋์ฉ ์์ดํ
์ ๋ ๋๋งํ๋ค. ํน์ดํ๊ฑด ๋ฐํํ๊ณ ์๋ ์ปดํฌ๋ํธ์ ๋ชจ์์ด๋ค.
<FriendListHeadless />
๋ผ๋ ์ปดํฌ๋ํธ์ child์๋ ๋๋ค๋ฅธ ์ปดํฌ๋ํธ(ListItem)์ ๋ฐํํ๋ ํจ์๊ฐ ๋ค์ด๊ฐ๋ค. ๊ทธ ํจ์๋ friend ๊ฐ์ฒด์ ํด๋ฆญ์ด๋ฒคํธ ์ ์คํ๋ ํจ์๋ฅผ ์ธ์๋ก ๋ฐ์ ListItem์ props์ผ๋ก ์ง์ด๋ฃ๋ ํํ์ด๋ค.
FriendListHeadless
interface FriendListHeadlessProps {
friend: IUser;
children: (args: any) => JSX.Element;
}
const FriendListHeadless = ({ friend, children }: FriendListHeadlessProps) => {
const navigate = useNavigate();
const chattingId = useRecoilValue(
chattingStateByUserId(friend.userId),
).chattingId;
return children({
friend: friend,
handleClickListItem: () => navigate(`/room/${chattingId}`),
});
};
Headless ์ปดํฌ๋ํธ ๋ด์์ chattingId ์ํ๋ฅผ ๋ฐ์์จ๋ค. ํด๋น ์ฑํ
๋ฐฉ์ผ๋ก navigate
ํ๋ ํจ์๋ฅผ ์์ฑํ๋ค. ๊ทธ๋ฆฌ๊ณ children ์ปดํฌ๋ํธ์ props์ผ๋ก ๋๊ฒจ์ค๋ค. ๊ตณ์ด ํ ์ปดํฌ๋ํธ๊ฐ ๋ ํ์ํ ์ด์ ๋ chattings๊ฐ ์๋ ๊ฐ chatting์ ์ํ๊ฐ ํ์ํ๊ธฐ ๋๋ฌธ. map
์ผ๋ก ๋๋ฆฌ๊ณ ์๋ ๊ทธ ์์์ ์ด๋ฃจ์ด์ ธ์ผํ๋ค. ๋ฒ๊ฑฐ๋ก์ด ์์ ์ปดํฌ๋ํธ๋ก ํ๊ณ ํ๊ณ ๋ด๋ ค๊ฐ ํ์ ์์ด, Headless์ปดํฌ๋ํธ๊ฐ ์ด๋ค ์ปดํฌ๋ํธ๋ฅผ child๋ก ๋๊ณ ์๋์ง๋ ํ๋์ ๋ณผ ์ ์๋ ๊ฒ๋ ์ฅ์ ์ธ ๊ฒ ๊ฐ๋ค.
4.1 ํ์ ๊ฐ๋
๋๋ค์ ๋ง์ฃผ์น ๊ณ ๋ฏผ ํฌ์ธํธ. ๋๊ฐ์ ListItem ์ปดํฌ๋ํธ์ data props๋ก ๋ด๋ ค์ค๋ ๊ฐ์ฒด๊ฐ ๋ฌ๋ผ์ง ์ ์๋ค. ์ฑํ
๋ชฉ๋ก์์๋ IChatting ํ์
, ์น๊ตฌ๋ชฉ๋ก์์๋ IUser ํ์
์ ๊ฐ์ฒด์ด๋ค. ํ๋กํ์ด๋ฏธ์ง์ ์ฌ์ฉ์ ์ด๋ฆ์ ๊ณตํต์ผ๋ก ์ฐ์ง๋ง IChatting
ํ์
์ ๊ฐ์ฒด์๋ userId ๋ฐ์ ์๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก friends ์ํ์์ ๊ฐ์ ธ์ค๋ ๋ฑ์ ์ฒ๋ฆฌ๊ฐ ํ์ํ๋ค. ๋ง์ง๋ง์ผ๋ก ์ฑํ
๋ชฉ๋ก์์๋ ์ํ๋ฉ์์ง ๋์ ๋ง์ง๋ง ์ฑํ
๊ณผ ๊ทธ ๋ ์ง๊ฐ ๋ณด์ฌ์ ธ์ผ ํ๋ค.
const isChattingType = (data: any): data is IChatting => {
return data.chattingId !== undefined;
};
// const isChatting = isChattingType(data);
<InfoSub>
{isChatting
? data.chatList[data.chatList.length - 1].content
: data.statusMessage}
</InfoSub>
ํ์ ๊ฐ๋๋ฅผ ํตํด ์กฐ๊ฑด๋ฌธ์์ ๊ฐ์ฒด์ ํ์ ์ ์ขํ ๋๊ฐ ์ ์๋ค. ๋จ์ํ ์ด๋ค ์ธ์๋ช ์ ์ด๋ ํ ํ์ ์ด๋ค๋ผ๋ ๊ฐ (is ํค์๋)์ ๋ฆฌํดํ๋ ํจ์์ด๋ค. ์ปดํฌ๋ํธ ๋ด์์ ํ์ ๊ฐ๋ ํจ์์ ๋ฐํ๊ฐ์ ์ฌ์ฉํ ์ ์๋ค.
๊ทผ๋ฐ ์ด๋ ๊ฒ ๋ค๋ฅธ ๋๋ฉ์ธ์ ์ํ๋ฅผ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ๋ค์ ํ๋๋ก ๋ง๋๋๊ฒ ์ข์ ์ฝ๋์ผ๊น..?
์ฑํ ๋ชฉ๋ก๊ณผ ์ ์ ๋ชฉ๋ก ๋ชจ๋์์ ์ฌ์ฉํ ์ ์๋๋ก ๋์์ธ๋์ด์๊ธด ํ์ง๋ง, ์ฌ์ ํ ๋ณ๊ฒฝ์ด ์๊ธฐ๋ฉด ์ปดํฌ๋ํธ๋ฅผ ์์ ํด์ผ ํ๋ค. ์ ์ด์ ์ปดํฌ๋ํธ๋ฅผ ๋๋ฉ์ธ๊ณผ ์์ ํ ๋ถ๋ฆฌํด์ { profileImage, mainText, subText, time? } ๋ฑ์ ๋ฒ์ฉ์ ์ธ props๋ฅผ ๋ฐ๋๋ก ํ๋ฉด ๋ ์ข์ ์ปดํฌ๋ํธ๊ฐ ๋๋ ค๋..?
๋ณ๊ฒฝ์ ์ ์ฐํ ์ปดํฌ๋ํธ
5. ๋ ๋นผ๊ณ ์งฐ๋ JSX, Styled-components
JSX๋ถํฐ Styled components ํจ์๊น์ง ๋ญํ๋ ์ ๋๋ก ๋๊ฒ ์๋ ์ฝ๋์๋ค. ๋์ ์๋๋ฐฉ์ ๋งํ์ ์ด ์๋ก ๋ฐ๋๋ฐฉํฅ์ ์์ด์ผํ๊ณ , (ํ์ฌ) - ๋ด์ฉ - ์๊ฐ
์ ์์๋ ๋์ ์๋๋ฐฉ์ด ๋ฐ๋๋ก ๋์ด์๋ค. ๊ทธ๋ฆฌ๊ณ ํ๋ช
์ด ์ฐ์ํด์ ์ฑํ
์ ๋ณด๋ผ๋, ์ฒซ๋ฒ์งธ๋ก ๋ณด๋ธ ๋งํ์ ์๋ง ๊ผฌ๋ฆฌ๊ฐ ๋ฌ๋ ค ์์ด์ผ ํ๋ค. ๊ทธ ๊ผฌ๋ฆฌ์ ์คํ์ผ๋ ๋์ ์๋๋ฐฉ์ด ๋ค๋ฅด๋ค.
๊ณ์ํด์ ๋ณต์กํ ์กฐ๊ฑด์ ํ๋์ฉ ์ถ๊ฐํด๊ฐ๋ฉฐ ๊ฐ๋ฐํ๋ค๋ณด๋ ์ ์ ๋๋ฌ์ด ์ฝ๋๊ฐ ๋์ด๊ฐ๋ค. ์ด๋ฒ ๊ณผ์ ์์๋ ์ฒ์๋ถํฐ ์ด์ฌํ ๊ณ ๋ฏผํ๊ณ ๊ตฌ์ฑ์ ํด๋ณด์๋ค.
1. flex-end
์ flex-start
๋ฅผ ์ด์ฉํด ์ฐ์ธก์ ๋ ฌ ์ข์ธก์ ๋ ฌ์ ๊ฐ๋จํ ํ ์ ์๋ค.
2. ์์์ ์์๋ flexbox์์ order
์์ฑ์ ์ด์ฉํด css ๋ด์์ ๋ฐ๊ฟ ์ ์๋ค.
3. ๋งํ์ ๊ผฌ๋ฆฌ๊ฐ์ ๋ณต์กํ ์คํ์ผ์ ๋ฐ๋ก ํจ์๋ก ๋ถ๋ฆฌํด ๋ฃ์ด์ค๋ค.
์ด๋ ๊ฒ 36์ค์ด์๋ JSX ์ฝ๋๋ฅผ 12์ค๋ก ์ค์ผ ์ ์์๋ค. Styled component ๋ถ๋ถ์ ๋์ฑ ์ค์ด๋ค์๋ค. ๊ฑฐ์ ๋๋ฐฐ ๊ฐ๊น์ด ๊ฐ๊ฒฐํด์ง ์ฝ๋๋ฅผ ๋ณผ ์ ์๋ค.
์ด๋ฒ์ ๋ฆฌํฉํ ๋งํ ๊ณผ์ ์ฝ๋์
์ด์ ์ ํ๋ ๊ณผ์ ์ฝ๋.
๋๊ฐ์ ๊ธฐ๋ฅ์ด๋๋ผ๋ ๊ณ์ ๊ณ ๋ฏผํด๊ฐ๋ฉฐ ๋ฆฌํฉํ ๋งํ๋ ๊ฒฝํ์ด ์ค์ํ๋ค.
์๊ฐ๋ณด๋ค ๋ง์ ๊ฒ์ ์ป์ด๊ฐ๋ ๊ณผ์ ์๋ค.