์์ค์(?) ๋๋ ค์๋ ๋ชจ๋ฐ์ผ ์ฒญ์ฒฉ์ฅ์์ ์ ์ผ ๋ฐ๊พธ๊ณ ์ถ์๋ ๊ธฐ๋ฅ..
๋๋ถ๋ถ ์ด๋ค ๊ธ์ฐ๋ ํ์์ ํผ์ ๋ด์ฉ์ ์
๋ ฅํ๋ฉด
์ ๋/์ ๋ถ์ ์ด๋ฉ์ผ๋ก ์ ์ก๋๊ฒ ํ๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ณ ์์๋ค.
๊ทผ๋ฐ ๋๊ฐ.. ๊ทธ๊ฑธ ๊ตณ์ด.. ์ด๋ฉ์ผ๋ก ์ถํ๋ฉ์ธ์ง๋ฅผ ๋ณด๋ด๊ณ ์์๊น ์ถ์ด์..
์ธ์คํ๊ทธ๋จ์ฒ๋ผ ๋ค๋ฅธ์ฌ๋๋ค๋ ๋๊ตฐ๊ฐ์ ์ถํ๋ฉ์ธ์ง๋ฅผ ํจ๊ป ๊ณต์ ํด๋ ์ข์ง ์์๊น ์ถ์ด์
๋๊ธ์ฒ๋ผ ๋ณด์ด๊ฒ ๋ง๋ค๊ธฐ๋ก ํ๋ค!
์ฐ์ ์ฌ๋ฌ๊ฐ์ input์ ๊ด๋ฆฌํ๋ ์์
์ ๋ง์ด ํด๋ณด์ง ์์๊ธฐ ๋๋ฌธ์
codesandbox์์ ์ฐ์ต์ ๋จผ์ ํด๋ณด์๋ค..
์ธ์คํ์ฒ๋ผ ์ด๋ฆ(bold์ฒด) ๋๊ธ๋ด์ฉ
ํผ์ผ๋ก ๋ง๋ค์ด์ผ ํ๊ธฐ ๋๋ฌธ์
๋ผ๋ ์ด๋ฆ์ผ๋ก input 2๊ฐ ๋ง๋ค๊ธฐ!
๊ทธ๋์ ์์ฑํ ์ฝ๋๋
export default function CommentComponent() {
const [inputs, setInputs] = useState({
username: "",
content: "",
});
const handleChange = () => {
const {name, value} = e.target; //e.target์์ name๊ณผ value๋ฅผ ์ถ์ถ(?)
setInputs({
...inputs, // ๊ธฐ์กด input ๊ฐ์ฒด๋ฅผ ๋ณต์ฌ
[name] : value, // name ํค๋ฅผ ๊ฐ์ง ๊ฐ์ value๊ฐ์ผ๋ก ์ค์
});
};
const handleSubmit = () => {
e.preventDefault();
if(!inputs.username || !inputs.content) return null; // ๋น์นธ์ผ๊ฒฝ์ฐ์ ๋ฐ์ ์์
addTweet(inputs) // ๋๊ธ ์ถ๊ฐ๋๋ ํจ์ - ๋ฐ์์ ์์ธํ ์ ๋ฆฌ
setInputs({
username: "",
content: "",
});
};
...
return (
...
<form>
<NameInput
name="username"
placeholder="์ด๋ฆ์ ๋ฃ์ด์ฃผ์ธ์"
value={inputs.username}
onChange={handleChange}
/>
<ContentInput
name="content"
placeholder="๋๊ธ ๋ฌ๊ธฐ..."
value={inputs.content}
onChange={handleChange}
/>
<button type="submit">๊ฒ์</button>
</form>
์๋ฒ์์ ์ฐ๊ฒฐ, ๋ฐฐ์ด์์ ์ถ๊ฐ๋๋ ์ฝ๋, propsํ์
๋ฑ๋ฑ ์ฌ๋ฌ๊ฐ์ง๋ฅผ ํฉ์ณ์ ์๊ฐํ๋ค๋ณด๋
์กฐ๊ธ ์ด๋ ค์ ๋ ๋ถ๋ถ....ใ
ใ
MainPage
์์ ์๋ฒ์์ ๋ฐ์์จ state๊ฐ์ธ comments
๋ฅผ props๋ก ๋ฐ์์ค๊ธฐcomments
๋ฅผ map
ํจ์๋ก ๋๋ ค ์๋ก์ด comment
๋ฐฐ์ด์ ๋ง๋ค๊ธฐcomment
๋ฐฐ์ด์ CommentOutput
์ด๋ผ๋ ๊ฐ ๋๊ธ์ view๋ฅผ ๋ด๋นํ๋ ์ปดํฌ๋ํธ์ props๋ก ๋๊ฒจ์ฃผ๊ธฐcomment
์์ username
์ ์ด๋ฆ, text
๋ ๋๊ธ ๋ด์ฉ์ผ๋ก,์ด๋, ์ถ๊ฐํด์ฃผ๋ ํจ์์ ์ comment
๋ฐฐ์ด์ ์ธ์๋ก ๋๊ฒจ์ค ๋
์๋ฒ์์ ๋๊ฒจ์ค ๊ฐ๊ณผ ๋์ผํ๊ฒ ๋๊ฒจ์ฃผ์ด์ผ ํ๋ค(๊ณ ethan์ด ๋งํ๋ ๊ฒ ๊ฐ๋ค..)
์ฝ๋๋ฅผ ๋ณด์๋ฉด
type CommentOutputProps = {
comment: CommentType;
};
// ๊ฐ๋ณ ๋๊ธ์ component
function CommentOutput(props: CommentOutputProps) {
const { comment } = props;
return (
<SingleComment>
<div>
<span style={{ fontWeight: "bold", marginRight: "8px" }}>
{comment.username}
</span>
<span>{comment.text}</span>
</div>
</SingleComment>
);
}
// ๋๊ธ ์ถ๊ฐํ๋ ํจ์(addTweet)์ ํ์
type AddComment = (state: InputState) => void;
type CommentStateProps = {
comments: CommentType[];
};
export default function CommentComponent() {
...
const dispatch = useDispatch();
const addTweet: AddComment = (args) => {
const { username, content } = args;
const comment = {
postId: "",
username: username,
text: content,
};
dispatch(addComment(comment));
fetch("์๋ฒ์ฃผ์/addComment", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(comment),
})
.then((response) => response.json())
};
return (
<Container>
{comments.map((comment) => {
return <CommentOutput comment={comment} />;
)}
...
</Container>
);
}
์์ง๋ POST ๋ฉ์๋๋ก ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ ์์
์ด ์ต์ํ์ง ์์ง๋ง,
์ข์์ ๊ธฐ๋ฅ๊ณผ๋ ๋ค๋ฅด๊ฒ body์ comment
๋ผ๋ ๊ฐ์ ๋ณด๋ด์ผ ํ๋ค๋๊ฑธ ์๊ฒ ๋์๋ค.
์ข ๋ ์ฐ์ตํด๋ด์ผ๊ฒ ์ง๋ง, postman์์ ์ด๋ค ์ ๋ณด๋ฅผ ์ด๋ป๊ฒ ๋ด์ผ ํ๋์ง๋ ์กฐ๊ธ์ ์๊ฒ ๋์์ผ๋
๋ค์ ์์
๋๋ ๊ทธ๋๋ ์ข ์ต์ํ๊ฒ ํ ์ ์๋๋ก
๋ ๊ณต๋ถํด์ผ๊ฒ ๋ค.
์์ ์ ์ด๋์ ๋ ํ๋ค๋ณด๋ ๋๊ธ ๊ธธ์ด๋ ์์ด ๋์ด๋๋ฉด ๋ฌธ์ ๊ฐ ์๊ฒผ๋ค.
์ฌ์ฉ์์ด๋ฆ ์๋ ๋ด์ด๋ฆ์---์ด์ผ๋ง๋์๋ฐ๊ฐ์
์ด๋ ๊ฒ ๋๋๋ง๋์ผ๋๋๋ฐ ์ด๋ ต๋ค
์ด๋ฆ ๋ฐ์ผ๋ก๋ ๋ด์ฉ์ด ๋ค์ด๊ฐ์ผ ํ๋๋ฐ ๊ตฌ์ญ์ด ๋๋์ด์ ธ์ ์๊พธ๋ง
์ด๋ ๊ฒ๋ฐ์...
๊ทธ๋์ ์์ฒญ ์ค๋ซ๋์ ์ฝ์ง์ ํ๋๋ฐ, ethan์ด ํ๋ฐฉ์ ํด๊ฒฐํด๋ฒ๋ ค์ ์ด์ด๊ฐ ์์๋ค..
๋ฐฉ๋ฒ์ block์์์ธp
ํ๊ทธ๋ก ์ด๋ฆ/๋๊ธ๋ด์ฉ ์ inline์์์ธspan
ํ๊ทธ๋ฅผ ๊ฐ์ธ๋ ๋ฐ๋ก ๊ฐ๋ฅํด์ก๋ค...
์ด๊ฒ ๋ค css์ง์์ด ์์ ํ์ด๋ผ๊ณ ์๊ฐํ๊ณ
๋ ๊ณต๋ถํ๊ธฐ๋ก..!
ํ
์คํธ๋ฅผ ํ๋ค๊ณ ์๋ฒ์ ์ฐ๊ฒฐํ์ฑ๋ก ๊ณ์ ๋๊ธ์ ์ถ๊ฐํ๋ค๊ฐ
40๊ฐ๊ฐ ๋์ด๋ฒ๋ ธ๋๋ฐ, ๊ทธ๋ ๊ฒ ๋๋๊น ์ฒซ ๋๋๋ง ํ๋ฉด์ธ Mainpage
๊ฐ ๋๋ฌด ์ง์ ธ๋ถํด๋ณด์๋ค.
๊ทธ๋์ ์ธ์คํ๊ทธ๋จ์ฒ๋ผ ๋๊ธ์ ์ ์ด์ ์จ๊ฒจ๋ฒ๋ฆฌ๊ธฐ๋ก ํ๋ค.
๋ฌผ๋ก ๋๊ฐ์ด ํ์ด์ง ์ด๋์ฒ๋ผ ๋ณด์ด๊ฒ๋ ์๋์ง๋ง
๋ฒํผ์ ํด๋ฆญํจ์ผ๋ก์ ๋๊ธ์ด ์ ํ๋ค/ํด์ง๋ ํด๋๊ธฐ๋ฅ์ ๊ตฌํํด๋ณด์๋ค.
isActive
๋ผ๋ ์ํ๊ฐ(boolean)์ ์ฌ์ฉํด์ ํด๋ฆญํ ๋๋ง๋ค ์ํ๊ฐ ๋ฐ๋๋๋ก ์ฝ๋๋ฅผ ์งฐ๋ค.
๋์์ ๋๊ธ๋ถ๋ถ component์ visible
์ด๋ผ๋ props๋ฅผ ์ ๋ฌํด์
visible
๊ฐ์ด false์ผ ๊ฒฝ์ฐ display: none, visibility: hidden
์ผ๋ก ์ค์ ํ๋
css ์์๋ฅผ ์กฐ์ ํ์๋ค.
...
<CommentBox>
<FolderSpan onClick={handleFolder}>
๋๊ธ {count}๊ฐ {visible ? "์ ๊ธฐ" : "๋ชจ๋ ๋ณด๊ธฐ"}
</FolderSpan>
{visible
? comments.map((comment) => {
return <CommentOutput comment={comment} />;
})
: null}
</CommentBox>
<CommentFooter visible={visible}>
...
</CommentFooter>
๊ทธ๋ฆฌ๊ณ ์ถ๊ฐ๋ก ๋๊ธ์ด ์ ํ์์ ๋ comment์ length
๋ฅผ ํตํด ๊ฐฏ์๋ฅผ ํ์ํด์ฃผ์๋ค.
์ด๋ ๊ฒ ์์
์ ํ๊ณ ๋๋ ๋ ์๊ธฐ๋ ์์ฌ..
๋๊ธ์ ํผ์ณค์ ๋, ๋ฐ๋ก ์ด๋ฆ input์ผ๋ก ํฌ์ปค์ค๋ฅผ ๋ง์ถ๊ณ ์ถ์๋๋ฐ
useRef
๋ก ์์
ํ๋ ์ ์ด ์์๋๋ฐ ์ ์๋๋๊ฑฐ๋ค...
๊ทธ๋ฌ๋ค๊ฐ useEffect
์ ํฉ์ณ์ ์คํฌ๋กค์ ๋งจ ์๋๋ก ๋ด๋ฆฐ ๋ค ํฌ์ปค์ค๋ฅผ ์ฃผ๋ ๋ฐฉ๋ฒ์ ์ฐพ์๋ค.
...
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if(visible) {
window.scrollTo(0, document.body.scrollHeight);
if(inputRef.current) inputRef.current?.focus();
}
}, [visible]);
...
<input
ref={inputRef}
name="username"
...
/>
์ข์์ ๊ธฐ๋ฅ์์๋ ๋งํ์ง๋ง, ์๋ฒ์ ์ฐ๋ํด์ view๋ฅผ ๋ณด์ฌ์ฃผ๋ค๋ณด๋
์๋ฒ์ ์
๋ก๋ ๋๋ ์ฌ์ด์ ์ฌ์ฉ์์๊ฒ ์์
์ด ์งํ์ค์ด๋ผ๊ฑฐ๋, ์๋ฃ๋์๋ค๋ ํ์๋ฅผ ํด์ฃผ๋๊ฒ ํ์ํ๋ค.
๊ทธ๋์ ํ ์คํธ๋ฉ์ธ์ง๋ฅผ ๋์์ฃผ๊ธฐ๋ก ํ๊ณ ์ง์ ๊ตฌํํด๋ณด๊ธฐ๋ก ํ๋ค.
isActive
๋ผ๋ ์ํ๊ฐ, ๊ทธ ๊ฐ์ ๋ณ๊ฒฝํ ์ ์๋ setIsActive
ํจ์๋ฅผ ๋ง๋ค์ด์
ToastMessage
์ปดํฌ๋ํธ์ props๋ก ์ ๋ฌํ์๋ค.
๊ทธ๋ฆฌ๊ณ ๊ฒ์๋ฅผ ํด๋ฆญํ๋ฉด ํ ์คํธ ๋ฉ์ธ์ง๊ฐ ๋์์ง๋๋ก button
์ปดํฌ๋ํธ์
onClick={() => setIsActive(true)}
์ฝ๋๋ฅผ ์ถ๊ฐํด์ฃผ์๋ค.
// ToastMessage.tsx
type ToastProps = {
children: string;
isActive: boolean;
setIsActive: (a: boolean) => void;
};
export default function ToastMessage(props: ToastProps) {
const { children, isActive, setIsActive } = props;
// 3์ดํ์ ๋ฉ์ธ์ง ์ฌ๋ผ์ง๊ฒ ํ๊ธฐ
useEffect(() => {
if (isActive === true) {
setTimeout(() => {
setIsActive(false);
}, 3000);
}
});
return (
<Fragment>
{isActive ? (
<Toast show={true}>{children}</Toast>
) : (
<Toast show={false} />
)}
</Fragment>
);
}
๋๋ถ์ ์์ ํ์์ต๋๋ค :)