๐ฑ๐ค ์๋ฌธ : https://bhavaniravi.medium.com/learn-reactjs-by-building-a-chat-frontend-2d8fe664276e
๋ฆฌ์กํธ๋ฅผ ์ฌ์ฉํ์ฌ ์ฑํ ์์ฉํ๋ก๊ทธ๋จ์ ์ฒ์๋ถํฐ ์์ฑํด๋ณด์.
๐ฅ ๋ฆฌ์กํธ๋ ๋ฌด์์ธ๊ฐ?
React๋ ์ฌ์ฉ์ ์ธํฐํ์ด์ค๋ฅผ ๋น๋ํ๊ธฐ ์ํ ์๋ฐ์คํฌ๋ฆฝํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. ํ์ด์ค๋ถ๊ณผ ๊ฐ์ธ ๊ฐ๋ฐ์ ๋ฐ ๊ธฐ์ ์ปค๋ฎค๋ํฐ์ ์ํด ์ ์ง ๋ณด์๋๋ค. — Wikipedia
๊ฐ๋จํ ๋งํ๋ฉด HTML + JS + CSS = React์ด๋ค.
๐ฅ ์ ๋ฆฌ์กํธ๋ฅผ ์ฌ์ฉํ๋๊ฐ?
- ๋๋ ์ต๊ทผ์ ํ์ด์ฌ์ ์ฌ์ฉํ๋ ๋ฐฑ์๋๋ก ํฌ์ง์ ์ ์ฎ๊ฒผ๊ณ ์ด ์ผ์ ๋ ์ข์ํ๋ค. ํ์ง๋ง ๋ด ์๋ฐ์คํฌ๋ฆฝํธ ์ค๋ ฅ์ด ํ๋์ด ๋๋ ๊ฒ์ ์ซ๋ค.
- ๋ด ์น๊ตฌ๋ค ๋๋ถ๋ถ์ด ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ค์ด๊ธฐ ๋๋ฌธ์ ๊ทธ๋ค์ ์ธ์ด(๋ฆฌ์กํธ ์ฉ์ด์ ๊ทธ๋ค์ด ๋ฌด์์ ๋งํ๊ณ ์๋์ง ๋ ๋ค)๋ฅผ ์์์ผ ํ๋ค๋ ์๋ฐ๊ฐ์ด ์๋ค.
- ๋ฆฌ์กํธ๋ก ๊ตฌ์ถํ ๊ฐ์ธ ๋น๋ฅผ ์ด์ฉํด ๋๋ง์ ํฌํธํด๋ฆฌ์ค ์น์ฌ์ดํธ๋ฅผ ๋ง๋ค๊ณ ์ถ๋ค.
- ๋ณด๋์ค ํฌ์ธํธ: ํจ๊ป ์ผํ๊ณ ์๋ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๊ฐ "๋ฆฌ์กํธ๋ก๋ ํ ์ ์๋ ์ผ์ ์๋ค"๋ผ๊ณ ๋งํ๊ณ ๊ฐ๋ค.
๐ฅ ํ์ ์กฐ๊ฑด
HTML, CSS ๋ฐ ์๋ฐ์คํฌ๋ฆฝํธ์ ๊ฐ์ ํ๋ก ํธ์๋ ๊ธฐ์ ์ ๋ํ ์ดํด๊ฐ ํ์ํ๋ค.(๋ง์คํฐํ ํ์๋ ์๋ค.) ์ด ๊ธฐ์ ๋ค์ ํ์ฉํด ์น ํ์ด์ง๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์๊ณ ์๋ค๋ฉด ์ถฉ๋ถํ๋ค.
๐ฅ ๋ฆฌ์กํธ์ ๋ํ ๊ธฐ๋ณธ์ ์ธ ์ดํด
React = Component + props + state
์ด๋ณด์๋ก์ ์ ํ๊ฒ ๋ ์ธ ๊ฐ์ง ๊ธฐ๋ณธ ์ฉ์ด๋ Component, state, props์ด๋ค. ์ด ์ธ์๋, "Lazy React", "React Hooks" ๋ฑ๊ณผ ๊ฐ์ ํ๋ คํ ์ด๋ฆ๋ค์ ๋ณผ ์ ์๋๋ฐ, ๊ทธ๊ฒ๋ค์ ๋ด ๋ชฉ๋ก์๋ ์์ง๋ง ์ด ๋ธ๋ก๊ทธ์๋ ์๋ค.
๐ฅ ์ด๋ป๊ฒ ์์ํ๋๊ฐ?
๋๋ ๋ฌธ์ํ ์์
๊ด์ด๋ค. ๋ฌธ์ํ์ ๊ทธ ๋ฌธ์๊ฐ ์ ๋๋ก ์๋ํ์ง ์์ ๋ ๋ค๋ฅธ ๋ฆฌ์์ค๋ ํํ ๋ฆฌ์ผ์ ์ฐพ๋ ๋ฐ ์์ฒญ๋ ์๊ฐ์ ํ ์ ํ๋ค.
๋ฆฌ์กํธ ๊ณต์ ๋ฌธ์์ ํํ ๋ฆฌ์ผ์ tic tac toe ๊ฒ์์ ํตํด Component๋ฅผ ๊ตฌ์ฑํ๊ณ state ๋ฐ props๋ฅผ ๊ฐ์ง๊ณ ๋
ธ๋ ๋ฐฉ๋ฒ์ ์๋ ค ์ค๋ค. ๋ ๊ทธ ํํ ๋ฆฌ์ผ์ ์๋ฃํ์ง๋ง ํ๋ก์ ํธ์ ์ด๋ป๊ฒ ์ ์ฉํด์ผ ํ ์ง ์ ํ ์ดํดํ์ง ๋ชปํ๋ค.
๋๋ด ๋ด๊ฒ ํจ๊ณผ๊ฐ ์์๋ ๊ฒ์ "๋ฆฌ์กํธ๋ก ์ฌ๊ณ ํ๊ธฐ" ๋ถ๋ถ์ด์๋ค. ์ด ๋ถ๋ถ์์ ์น ํ์ด์ง๋ฅผ Component๋ก ๋ถํ ํ๋ ๋ฐฉ๋ฒ๊ณผ ์ด๋ฌํ Component์ state, props๋ฅผ ๋์
ํ๋ ๋ฐฉ๋ฒ์ ์๋ ค์ค๋ค.
์ด ํํ ๋ฆฌ์ผ์ ์ข์ ์ ์ ์ค์ ๋ก ์ฌ์ฉ๋๊ณ ์๋ ๋ฐฉ์๊ณผ ๋งค์ฐ ๋น์ทํ๋ค๋ ๊ฒ์ด๋ค. ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ ํญ์ ๋์์ธ์ ๊ฐ์ง๊ณ ์ผํ๋ค. ๋ฐ๋ผ์ tic tac toe ๊ฒ์์ฒ๋ผ ๋น ์ฌ๋ ์ดํธ๋ก ๋ฆฌ์กํธ๋ฅผ ์์ํ๋ ๊ฒ์ ํฐ ๋์์ด ๋์ง ์๋๋ค.
๐ฅ ๋ฌด์์ ๋ง๋ค ๊ฒ์ธ๊ฐ?
๋๋ HTML, CSS ๋ฐ ์๋ฐ์คํฌ๋ฆฝํธ๋ง์ ์ฌ์ฉํด ์ด ๊ณตํต ํ๋ก ํธ์๋ ์ฑํ
๋ชจ๋์ ๊ตฌ์ถํ๋ค. ๋ด ํ
ํ๋ฆฟ์์ ์ฑ๋ด์ ๋ง๋ค๊ณ ๊ฐ์ง๊ณ ๋ ์ ์๋ค. ๋ด ๋๋ถ๋ถ์ ์ทจ๋ฏธ์ฉ ์ฑ๋ด ํ๋ก์ ํธ๋ ์ด ํ
ํ๋ฆฟ์ ๊ฐ์ง๊ณ ์๋ค.
๋์ ์ต์ข
๋ชฉํ๋ ๋ฐฑ์๋ API๋ก ๋ฉ์ธ์ง๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ์ฑํ
ํ๋ก ํธ์๋๋ฅผ ๋ง๋๋ ๊ฒ์ด์๋ค. ๋ด GitHub์ ์๋ ์ฝ๋๋ก ์ง์ ์คํํ๊ณ ์ฌ์ฉํด๋ณผ ์ ์๋ค.
๐ฅ ์ปดํฌ๋ํธ ์์ค์์ ์๊ฐํ๊ธฐ
์ฒซ ๋ฒ์งธ ๋จ๊ณ๋ ์น ํ์ด์ง๋ฅผ ๊ตฌ์ฑํ๋ ๊ฐ๋ณ UI ์์๋ฅผ ์ ์ํ๋ ๊ฒ์ด๋ค.
์ ์ฌ์ง์ ๋ดค์ ๋, ์ฝ 4~5๊ฐ์ UI ์์๋ฅผ ์ฐพ์ ์ ์๋ค. ์ด๋ฌํ ์์๋ค์ ๊ฐ๊ฐ ์ปดํฌ๋ํธ๋ผ๊ณ ํ๋ ๋
๋ฆฝ์ฒด๋ก ๊ฐ์ธ๋ ๊ฒ์ด๋ค.
Send ๋ฒํผ (SendButton)
class SendButton extends Component{
render(){
return (
<div className="send_message"
<div className="text">send</div>
</div>);
}
}
ํ ์คํธ ๋ฐ์ค (MessageTextboxContainer)
class MessageTextBoxContainer extends Component{
render(){
return(
<div className="message_input_wrapper">
<input id="msg_input"
className="message_input"
placeholder="Type your messages here..."/>
</div>
);
}
}
์๋ฐํ (Avatar)
์๋ฐํ๋ ํ๋กํ ์ฌ์ง์ ๋ํ๋ธ๋ค. ์ง๊ธ์ ์ฌ๋๋ง๋ค ์๋ก ๋ค๋ฅธ ๋ฐฐ๊ฒฝ์ ์๋ฐํ๋ฅผ ๋ณด์ฌ์ฃผ๋ ค ํ๋ค.
class Avatar extends Component {
render(){
return(
<div className="avatar"/>
); }
}
๋ฉ์ธ์ง (MessageBox)
MessageBox ์ปดํฌ๋ํธ์๋ Avatar ์ปดํฌ๋ํธ๊ฐ ์๋ค. ๊ฐ ๋ฉ์ธ์ง์ ๋ํด ์ด ์ปดํฌ๋ํธ๋ฅผ ๋ฐ๋ณต์ ์ผ๋ก ๋ง๋ค ๊ฒ์ด๋ค.
class MessageBox extends Component{
render(){
return(
<li className={`message ${this.props.appearance} appeared`}>
<Avatar></Avatar>
<div className="text_wrapper">
<div className="text">{this.props.message}</div>
</div>
</li>
);
}
}
์ ์ฒด ์ฑ (ChatApp)
๋ง์ฐฌ๊ฐ์ง๋ก, ์์์ ๋ง๋ ์ปดํฌ๋ํธ๋ค์ ํจ๊ป ๋ฐ์ธ๋ฉํ๋ ์ ์ฒด UI๋ฅผ ๋ง๋ ๋ค. ์ฌ๊ธฐ์๋ ๋ฉ์ธ์ง๊ฐ ๋์ด๋๋ ๋ฉ์ธ์ง ์ปจํ ์ด๋, ํ ์คํธ ์์ ๋ฐ ๋ณด๋ด๊ธฐ ๋ฒํผ์ด ํฌํจ๋๋ค. ์ผ๋จ ์ง๊ธ์ this.state์ this.handleClick์ ๋ฌด์ํ๋๋ก ํ์!
class ChatApp extends Component {
render() {
return (
<div className="chat_window">
<MessagesContainer messages={this.state.messages}/>
<div className="bottom_wrapper clearfix">
<MessageTextBoxContainer/>
<SendButton handleClick={this.handleClick}/>
</div>
</div>
);
}
}
๐ฅ ์ด๋ฒคํธ ์บก์ฒ๋ง
์ปดํฌ๋ํธ์ states์ props์ ๋ํด ์์๋ณด๊ธฐ ์ ์ ์ด๋ค ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํด์ผ ํ๋์ง ์์๋ณด์.
- ๋ณด๋ด๊ธฐ ๋ฒํผ์ ๋๋ฌ ๋ฉ์ธ์ง ๋ณด๋ด๊ธฐ
- ์ํฐ ํค๋ฅผ ๋๋ฌ ๋ฉ์ธ์ง ๋ณด๋ด๊ธฐ
์ด ๋ธ๋ก์์๋ press_enter ๋ฐ send_button_click ์ด๋ฒคํธ๋ฅผ ์บก์ฒํ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณด๊ฒ ๋ค.
๋ณด๋ด๊ธฐ ๋ฒํผ ์ปดํฌ๋ํธ๋ก ๋์๊ฐ ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฉ์๋๋ฅผ ๋ถ์ฌ๋ณด์. ๋น์ทํ ๋ฐฉ์์ผ๋ก ํ ์คํธ ๋ฐ์ค์ onKeyPress ์ด๋ฒคํธ๋ฅผ ์ถ๊ฐํด ์ด๋ฒคํธ๋ฅผ ์บก์ฒํ ์ ์๋ค.
class SendButton extends Component{
handleClick(){
console.log("I am captured");
}
render(){
return (
<div className="send_message"
onClick={this.props.handleClick}>
<div className="text">send</div>
</div>);
}
}
๐ฅ ์ด๋ฒคํธ ํธ๋ค๋ง
click ๋ฐ keypress ์ด๋ฒคํธ๋ฅผ ์บก์ฒํ์ผ๋ ์ด๋ป๊ฒ ์ด ์ด๋ฒคํธ๋ค์ ์ฒ๋ฆฌํ ์ง ์ดํด๋ณด์. ์ฌ๊ธฐ๊ฐ ๋ด๊ฐ ์ ์ผ ํค๋งจ ๋ถ๋ถ์ด๋ค.
๋ณด๋ด๊ธฐ ๋ฒํผ์ ํด๋ฆญํ๊ฑฐ๋ Enter ํค๋ฅผ ๋๋ฅด๋ฉด ํ
์คํธ ๋ฐ์ค์ ์ ํ ์๋ ๋ฉ์ธ์ง๊ฐ MessageContainer์ ๋ฉ์ธ์ง ์ปดํฌ๋ํธ๋ก ์ถ๊ฐ๋๋ค.
์ด์ ๊ฐ ์ปดํฌ๋ํธ๋ฅผ ์ดํด๋ณด๊ณ ํ์ํ ์ ๋ณด๋ฅผ ์ฑ์ฐ๋ ๋ฐ ํ์ํ ๋ฐ์ดํฐ๊ฐ ๋ฌด์์ธ์ง ์์๋ณด์. ์ด ๋ฐ์ดํฐ๋ ์ปดํฌ๋ํธ์ state์ props์ ์ํด ์ ์๋๋ค.
States์ Props
๊ฐ ์ปดํฌ๋ํธ๋ฅผ ๋ณด๊ณ ๊ทธ ์ปดํฌ๋ํธ๊ฐ ์ ๊ธฐ๋ฅ์ ํ๊ธฐ ์ํด ํ์ํ ๋ฐ์ดํฐ๊ฐ ๋ฌด์์ธ์ง ์ดํด๋ณด์.
- ๋ณด๋ด๊ธฐ ๋ฒํผ(Send Button) : ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ๋ current_message๋ฅผ ์ฑ๋ด์ผ๋ก ์ ์กํ๊ธฐ ์ํ ์ ๊ทผ ๊ถํ์ด ํ์ํ๋ค.
- ๋ฉ์ธ์ง ํ ์คํธ ๋ฐ์ค(Message Text Box) : ์ฌ์ฉ์๊ฐ current_message๋ฅผ ์ ๋ ฅํ ๋ Enter ํค๋ฅผ ๋๋ฅด๋ฉด ๋ฉ์ธ์ง๋ฅผ ์ฑ๋ด์ผ๋ก ์ ์กํ๋๋ก ์ ์ง ๋ฐ ์ ๋ฐ์ดํธํด์ผ ํ๋ค.
- ๋ฉ์ธ์ง ์ปจํ ์ด๋(Message Container) : ๋ด๊ณผ ์ฌ์ฉ์๊ฐ ์์ฑํ ๋ชจ๋ ๋ฉ์์ง์ ๋ชฉ๋ก์ ์ ์ง ๊ด๋ฆฌํด์ผ ํ๋ค.
๋ฉ์์ง ์ปจํ ์ด๋(Message Container)์ ์ง๊ธ ์์ฑํ๊ณ ์๋ ๋ฉ์ธ์ง๋ฅผ ๋์ฐ๋ ค๋ฉด ์ฌ์ฉ์๊ฐ ํ ์คํธ ๋ฐ์ค(Message Text Box)์ ์์ฑํ ๋ฉ์ธ์ง(Current Message)๋ฅผ ์์์ผ ํ๋ค.
๋ํ ๋ฉ์์ง ์ปจํ
์ด๋(Message Container)๋ ์ง๊ธ๊น์ง ์
๋ ฅ ๋ฐ ์์ ๋ ๋ชจ๋ ๋ฉ์์ง๋ฅผ ์ถ์ ํด์ผ ํ๋ค. ๋ฐ๋ผ์ ๋ฉ์ธ์ง๋ฅผ ์์ฑ์ ๋ฐ๋ผ ๋ฐฐ์ด๋ก ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ค.
class TextBox extends Component{
constructor(props){
this.state.current_message = ""
}
onChange(e) {
this.setState({
current_message: e.target.value;
});
if(e.key === "Enter"){
// We need to add a new message to MessageContainer component
}
}
render(){
return(
<div className="message_input_wrapper">
<input ...
value={this.props.current_message}
onChange={this.props.onChange}/>
</div>
);
}
}
ํ ์คํธ ๋ฐ์ค(TextBox) ์ปดํฌ๋ํธ์ current_message๋ ์ฒ์์ ๋น์ด์์ง๋ง ํ ์คํธ๋ฅผ ์ ๋ ฅํ๋ฉด ๊ฐ์ด ๋ณํ๊ฒ ๋๋ค. ๊ทธ๋ฌ๋ ํ ์คํธ ๋ฐ์ค(TextBox) ์ปดํฌ๋ํธ์์ ๋ณ๊ฒฝ๋ ๋ด์ฉ์ ๋ฉ์ธ์ง ์ปจํ ์ด๋(Message Container)์ ํ์๋์ง ์๋๋ค.
"๋ฆฌ์กํธ์์ ์ํ(state) ๊ณต์ ๋ ํด๋น ์ํ๊ฐ ํ์ํ ๊ฐ์ฅ ๊ฐ๊น์ด ๋ถ๋ชจ ์ปดํฌ๋ํธ์๊ฒ ๊ทธ๊ฒ์ ์ ๋ฌํ๋ ๊ฒ์ด๋ค. ์ด๊ฒ์ ์ํ(state)๋ฅผ ๋์ด์ฌ๋ฆฐ๋ค(lifting up)๊ณ ๋งํ๋ค."
์ํ ๋์ด์ฌ๋ฆฌ๊ธฐ(Lifting State Up)
์ด์ ์ปจํธ๋กค์ ์ด๋ค ์ปดํฌ๋ํธ๋ก ์ด๋์์ผ์ผ ํ๋์ง๊ฐ ๋ฌธ์ ๋ค. ์ปดํฌ๋ํธ์ ๊ฐ๋จํ ํธ๋ฆฌ๋ฅผ ๊ทธ๋ ค๋ณด๊ณ ๋ฌด์์ด ์ ํฉํ์ง ์์๋ณด์.
ํธ๋ฆฌ๋ฅผ ๊ทธ๋ ค๋ณด๋ฉด, ๋ชจ๋ ์์ ์ปดํฌ๋ํธ๊ฐ current_message์ ์ง๊ธ๊น์ง ์ ๋ฐ์ ๋ ๋ชจ๋ ๋ฉ์ธ์ง ๋ชฉ๋ก์ ์ ๊ทผํด์ผ ํ๋ค๋ ๊ฒ์ ์ ์ ์๋ค. ๋คํํ๋, ๋ชจ๋ ์ํ(state) ๋ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ(event handling) ์๊ตฌ ์ฌํญ์ ๊ฐ ์์ ์ปดํฌ๋ํธ์ ์์ํ๋ ์์ ์ปดํฌ๋ํธ๊ฐ ChatApp ํ๋๋ฟ์ด๋ค.
์ฐ๋ฆฌ์ ChatApp ์ปดํฌ๋ํธ๋ ์ด์ ์ฝ๊ฐ ๋ณต์กํด์ง ๊ฒ์ด๋ค. ๋จผ์ ํ์ํ ๊ธฐ๋ฅ์ ๋ชจ๋ ์ ์ํ๊ณ ๋์ค์ ํ์์ ๋ฐ๋ผ ์ด๋ฒคํธ์ ์ถ๊ฐํ์.
addMessageBox(enter=true){
let messages = this.state.messages;
let current_message = this.state.current_message;
if(current_message && enter){
messages = [...messages, {"message":current_message}];
}
handleClick(){
this.addMessageBox();
}
_handleKeyPress(e) {
let enter_pressed = false;
if(e.key === "Enter"){
enter_pressed = true;
}
this.addMessageBox(enter_pressed)
}
render() {
return (
<div className="chat_window">
<MessagesContainer messages={this.state.messages}/>
<div className="bottom_wrapper clearfix"> .
<MessageTextBoxContainer
_handleKeyPress={this._handleKeyPress}
onChange={this.onChange}
message={this.state.current_message}> .
</MessageTextBoxContainer>
<SendButton handleClick={this.handleClick}/>
</div>
</div>
);}
์ฝ๋์์ ์ ์ ์๋ฏ์ด ์ด๋ฒคํธ ํธ๋ค๋ฌ(event handler)์ state & props๋ฅผ ์์ ์ปดํฌ๋ํธ ์์ค์์ ์ ์ํ๊ณ ์ด๋ฅผ ํ์ ์ปดํฌ๋ํธ์ ์์ํ๋ค.
ํ๋ก ํธ์๋์ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์ซ์ดํ๋ ์ฌ๋์์๋ถํฐ ๊ทธ๊ฒ์ ์ด์ฉํด ์ฑํ ์ฑ์ ๋ง๋ ์ฌ๋์ด ๋๊ธฐ๊น์ง, ์ด์ ๋๋ ๋ฆฌ์กํธ ๋๋ถ์ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์ข์ํ๋ค. ๋ด๊ฐ ์ด๋ค ํ๋ก ํธ์๋๋ฅผ ๊ตฌ์ถํ ๋๋ ๋ ๋ฆฌ์กํธ์ ํจ๊ปํ ๊ฒ์ด๋ค!
'๋ฒ์ญ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
ํ๋ก ํธ์๋ ์ฒดํฌ๋ฆฌ์คํธ [๋ฒ์ญ] (0) | 2024.03.22 |
---|---|
๋ฆฌ๋์ค๋ก ๋ชจ๋ฌ ์ํ ์ ์ดํ๊ธฐ [๋ฒ์ญ] (0) | 2022.06.10 |