✅리액트 맛보기 냠냠
- 리액트란?
->사용자 인터페이스를 만들기 위한 자바스크립트 라이브러리이다.- 리액트 특징은?
-> 컴포넌트 기반, jsx를 사용, 선언적이라는 특징을 가지고 있다.- 3세대 웹이 나타난 이유와 프론트 언어의 대표적인 라이브러리 3가지는?
-> 웹의 복잡성의 증가로 인해 효율적인 개발과 처리를 위해서 등장하였으며
대표적인 라이브러리로는 react, veu angular가 있다.- 라이프 사이클 메소드의 순서는?
~~- constructor, render, componentDidMount, componentDidUpdate, componentwillUnMount 순이다. ~~
- 순차적으로 1. 컴포넌트를 호출하면 mounting 중이며 2,. constructor에는 화면에 아직 아무것도 없는 상태 3. render 메소드를 호출하면 화면에 보이기 시작하고 4. componentDidMount에선 새로운 props, setState 메소드로 state를 변경하여 상태를 업데이트한 뒤 5. 컴포넌트가 더이상 필요하지 않는 unmounting 상태가 되면 componentWillUnmount가 된다.
- 컴포넌트란?
재사용 가능한 독립적인 모듈(하나의 레고 블록 개념), UI의 단위를 의미한다.
-WHY USE❔❕
리액트
는 사용자 정의 태그를 만들어주는 기술 중 하나이며 리액트에선 이를
component
라고 부른다.
component
사용하면 좋은 점- 1. '가독성'을 획기적으로 높인다. - 2. component를 활용해 '재사용성'을 높인다. - 3. 에러 발생시 component만 수정하면 되어 '유지보수에 효과적'이다.
-WHAT IS❓
JSX
란 JavaScript 확장버전으로syntax extension for JavaScript
의 줄임말로 HTML문법을 JavaScript 코드 내부에 써준 것을 의미합니다.
JSX Element
는 .js 파일 어디에서나 필요한 곳에 작성할 수 있고 변수에 저장또는 함수의 인자로도 넘길 수도 있다.
- 태그에
attribute
를 주고 싶을 때는 항상 "" 쌍따옴표로 감싸주고 attribute를 추가하고 싶을 때는 실제 HTML에서 쓰는 attribute name(속성명)과 다를 수 있으니 항상 react 공식문서를 참고해서 써야한다.
- EX> class 부여시 HTML에 쓰는 속성명은 class지만 JSX에서는 className이라고 추가해줘야한다는 사실!
- JSX에서는 어떤 태그라도
self closing tag
가 항상 가능하며<input>
과 같이 하나의 태그가 요소인 경우에는<input />
와 같이 항상 /으로 끝내줘야 한다.
- 1_4_1. (필수)
소괄호로 감싸기
- 중첩된 요소를 만들려면 () 소괄호로 감싸야한다.
<script> const good = ( // 중첩 요소 만들 시엔 무조건 소괄호로 감싸야한다. <div> <p>hi</p> </div> ); </script>
- 1_4_2. (필수)
항상 하나의 태그로 시작
- 처음 요소가 sibling이면 안되고 무조건 하나의 태그로 감싸져야 한다
<script> const introduce = ( // 무조건 하나의 태그로 감싸져야한다. <div> <li>hi</li> <li>my</li> <li>name is</li> <li>jamie</li> </div> ) </script>
렌더링(rendering)
란 html 요소, React 요소 등의 코드가 눈으로 볼 수 있도록 그려지는 것을 의미한다eactDOM.render 함수
를 사용하여 React 요소를 DOM node에 추가해 화면에 렌더한다.- 함수의 첫 번째 인자로 JSX로 React 요소를 넘기고, 두 번째 인자로 해당 요소를 렌더하고 싶은 container(부모요소)를 전달한다.
<script> ReactDOM.render( <h1>Hello, world!</h1>, // 첫 번째 인자, JSX React 요소 document.getElementById('root') // 두 번째 인자, 위의 요소를 렌더하고 싶은 부모 요소 ); </script>
component(컴포넌트)
란 재사용 가능한 UI 단위이다.- 동일 코드가 반복되는 부분을 하나의
component
로 만든다면 다양한 곳에서재사용
할 수 있다.
- 가령, component로 만든 부분의 디자인을 수정 할때도 css를 수정하면 해당 component가 쓰인 모든 곳에서 수정된 디자인이 모두 반영되는 장점을 가진다.
component
를 사용하면 독립적으로, 재사용가능한 코드로 관리할 수 있다.- 하나의
component
에 필요한 html, css, js(validation check)를 모두 합쳐서 만들 수도 있다.component
는 함수와 비슷한데, 함수 역시 함수의 기능이 독립적이며 재사용할 수 있기 때문이다.- 또
component
도 함수처럼 input을 받아서 return이 가능하다.
Reactcomponent
에는 input을props
라 하고 화면에 보여져야 할 `React 요소가 return된다.
- React에서는
component
를 class나 함수로 만들 수 있고 상황에 따라 무엇으로 만들지 달라지게 된다.
- class로
component
를 만드려면 React.Component를 extends해서(리액트의 컴포넌트로부터 클래스를 상속받아) 생성한다.component
생성 시엔 render() 메서드를 필수적으로 정의하고, return도 필수이다.render() 메서드
를 무조건 정의해야한다는 의미는component
를 만들 때 필요한 메서드는 더 있지만 그 중에서 render() 메소드는 필수인 것을 뜻한다.<script> function Welcome(props) { return <h1>Hello, {props.name}</h1>; } class Welcome() extends React.Component{ render() { return <h1> hi, {this.props.name}</h1>; } } </script>
- 정의한 컴포넌트는 함수나 class명으로 사용할 수 있고 작성 방식은 <함수, class명 /> 으로 작성한다.
- 우리가 정의한
component
를 사용할 땐 원하는 attribute를 추가할 수 있는데component
(함수)에서 parameter로 해당 attribute를 받아서 사용할 수 있는데 이를props(property)
라 부른다.- .(dot)으로 속성명에 접근 가능하고, props.속성명으로 속성 값을 가져올 수 있다.
<script> // 1. Introduce 컴포넌트 정의 function Introduce (props) { return <h1>What's Up, My name is {props.name}</h1> } // 2. App 컴포넌트 정의 function App () { return( <div> <Introduce name = "kim" /> <Introduce name = "minjae" /> <Introduce name = "jamie(Eng)" /> </div> ) } // 3. 화면에 React 요소 그려주기 ReactDOM.render( <App />, document.getElementById('root') ); </script>
- Introduce 컴포넌트: props.name의 값을 사용하고 있는 것을 보아 Introduce 컴포넌트를 사용한 측, 부모에서 name이라는 attribute를 부여
- App 컴포넌트는 div로 감싸져있고,
<Introduce />
컴포넌트를 세번 작성하며 각각의 name attribute를 부여- ReactDOM.render 함수를 이용해 첫 번째 인자로 전달한 React 요소를 두 번째 인자로 전달한 root가 id인 요소 안에 집어넣어
<App />
컴포넌트를 그려줌
Comment 컴포넌트
는 재사용 가능한 코드 단위인 컴포넌트 내에서 더 재사용할 수 있는 요소들을 의미합니다.<script> // 분리되기 전의 Comment 컴포넌트 function Commment(props) { return ( <div className="comment"> <div className="user-info"> <img className= "avatar" src = {props.author.avatarUrl} alt = {props.author.name} /> <div className = "user-info-name"> {props.author.name} </div> </div> <div className = "comment-text"> {props.text} </div> <div className = "comment-date"> {formatData(props.date)} </div> </div> ) } </script>
- Comment 컴포넌트에 있는
.avatar
부분을 떼와서Avatar
라는 이름으로 컴포넌트를 만들어주자
- Comment 컴포넌트에선 props.author로 접근한 avatarUrl과 name을 Avatar 컴포넌트에서 직관적으로 사용하기 위해 user라는 이름으로 받아온다.
- props.user에서 avatarUrl, name 값을 가져온다.
<Avatar />
를 사용하는 부모 측에 user attribute를 추가한다.function Commment(props) { return ( <div className="comment"> <div className="user-info"> <Avatar user={props.author}/> <div className = "user-info-name"> {props.author.name} </div> </div> <div className = "comment-text"> {props.text} </div> <div className = "comment-date"> {formatData(props.date)} </div> </div> ) } function Avatar(props) { return ( <img className="avatar" src = {props.user.avatarUrl} alt = {props.user.name} /> ); } </script>
- 마찬가지로
.user-info
부분을 컴포넌트로 만들기 위해 user-info 부분 역시 떼어다가UserInfo
라는 컴포넌트로 만든다.// Commment 컴포넌트 function Commment(props) { return ( <div className="comment"> <UserInfo user = {props.author} /> <div className = "comment-text"> {props.text} </div> <div className = "comment-date"> {formatData(props.date)} </div> </div> ) } // 분리한 Avatar 컴포넌트 function Avatar(props) { return ( <img className="avatar" src = {props.user.avartarUrl} alt = {props.user.name} /> ); } // 분리한 UserInfo 컴포넌트 function UserInfo(props) { return ( <div className="user-info"> <Avartar user={props.user}/> <div className = "user-info-name"> {props.user.name} </div> </div> ) } </script>
<script> // 1. 분리한 formatDate 컴포넌트 function formatDate(date) { return date; } // 2. 분리한 Avatar 컴포넌트 function Avatar(props) { return ( <img className="avatar" src = {props.user.avatarUrl} alt = {props.user.name} /> ); } // 3. 분리한 UserInfo 컴포넌트 function UserInfo(props) { return ( <div className="user-info"> <Avatar user={props.user} /> <div className="user-info-name"> {props.user.name} </div> </div> ); } // 기존의 comment 컴포넌트 function Comment(props) { return ( <div className="comment"> <UserInfo user={props.author} /> <div className="comment-text"> {props.text} </div> <div className="comment-date"> {formatDate(props.date)} </div> </div> ); } function App() { return ( <Comment author="jamie" text="컴포넌트 분리 성공" date="2021-08-29" /> ) } ReactDOM.render( <App />, document.getElementById('root') ); </script>
state
란 컴포넌트의 상태 값으로state
와props
는 둘 다 object이고, 화면에 보여줄 정보(상태)를 가지고 있다는 점에서는 비슷한 역할을 한다.props
는 컴포넌트를 사용하는 부모 쪽에서 전달해야만 사용할 수 있고(parameter처럼)state
는 컴포넌트 내에서 정의하고 사용한다.
- 여러 개의
state
를 추가할 수도state
의 이름은 원하는대로 지을 수도 있다.<script> class Button extends React.Component { constructor() { super(); this.state = { clicked: false } } render() { return ( <div className="btn" onClick={()=>{this.setState({ clicked: !this.state.clicked })}} {this.state.clicked ? '좋아요' : '싫어요'} </div> ); } } ReactDOM.render( <Button />, document.getElementById('root') ); </script>
render 함수
<script> render() { return ( <div className="btn" onClick={()=>{this.setState({ clicked: !this.state.clicked })}} {this.state.clicked ? '좋아요' : '싫어요'} </div> ); } </script>
<div />
클릭시 onClick에 넘긴 함수 render함수가 실행하여 화면에 그려준다.
- click 이벤트가 발생하면 clicked라는
state
를 수정한다.this.setState()
함수를 통해 state를 업데이트 할 수 있다.!this.state.clicked
로 업데이트 한다는 것의 의미는 현재 clicked의 반대의(true => false, false => true)값을 저장함을 의미한다.- 따라서 onClick이 달려있는
<div />
를 클릭할 때마다(이벤트 발생), clicked 상태가 true 혹은 false로 업데이트 된다.
- clicked state가 true면 ‘좋아요’를 false면 ‘싫어요’를 보여준다.
constructor()
의 constructor는 class의 instance가 생성될 때 항상 호출되는 함수(생성자)다.<script> constructor() { super(); this.state = { clicked: false } } </script>
- 초기화 할 값들을
constructor
에서 세팅해준다.- Button 컴포넌트를 그리기 위해선
this.state.clicked
값이 필요하다.- 제일 최초에는 값이 없으므로
constructor(생성자 함수)
에서 값을 지정해줘야고super()
키워드를 사용해 React.Component class에 있는 메서드들(ex> render)을 사용할 수 있다.
<Button />
에 type을 추가하여 Button 컴포넌트에서 props로 받을 수 있다.- class .like-btn는 배경색을 넣은 css로
this.props.type
이 ‘like’면 .like-btn class 속성을 추가한다.<script> class Button extends React.Component { constructor() { super(); this.state = { clicked: false } } render() { return ( <div className={`btn ${this.props.type === 'like' ? 'like-btn' : ''}`} onClick={()=>{this.setState({ clicked: !this.state.clicked })}} {this.state.clicked ? '좋아요' : '싫어요'} </div> ); } } ReactDOM.render( <Button type="like" />, document.getElementById('root') ); </script>
render, componentDidMount, componentDidUpdate, componentWillUnmount
등의 함수는 React.Component class
에서 제공하는 메서드이다.
- 이는 컴포넌트를 만들어
lifecycle
에 따라state
를 관리할 수 있도록 코드 수정이 가능하다는 의미이다.- 1초에 한 번 tictoc함수를 호출해서 현재시간을 초 단위로 업데이트해보자.
<script> function tictoc(){ const element = ( <div> <h1> what's up world! </h1> <h2> It is {new Date().toLocaleTimeString()}.</h2> </div> ); ReactDOM.render( element, document.getElementById('root') ); } setInterval(tictoc, 1000); </script>
- 원래 코드에서 tictoc은 일반적인 함수이며, 내부에서 ReactDom.render를 호출해서 React 요소를 그려주고 있다.
- 화면으로 보여질 부분을 함수를 사용해서 컴포넌트로 만들어보자.
<script> // 화면으로 보여지는 Clock 컴포넌트 function Clock(props){ return ( <div> <h1> what's up world! </h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div> ); } function tictoc() { ReactDOM.render( <Clock date={new Date()} />, document.getElementById('root') ); } setInterval(tictoc, 1000); </script>
- 클래스로 컴포넌트를 만들 땐
-render()
함수가 꼭 필요하며화면에 그려줄 부분을 return한다
.- Clock 함수에 있던 return을 그대로 render()의 return으로 가져오고
props.date를 this.props.date로 바꿔준다.render 메서드
는 초가 바뀔 때마다, 즉 1초마다 호출되어 내용을 변경해줘야 한다.
그런데 Clock 컴포넌트(즉, Clock 인스턴스)는 mounting 동안 본인 컴포넌트 내에서 값이 업데이트 되어야 하므로, state로 변경 값을 관리해야하는데 이때 lifecycle 메서드가 필요하다.<script> class Clock extends React.Component { render() { return ( <div> <h1> what's up world! </h1> <h2>It is {this.props.date.toLocaleTimeString()}.</h2> </div> ); } } </script>
props
로 받던 date 정보를 state
로 바꿔서 관리해보자<script> class Clock extends react.component { render() { return ( <div> <h1> what's up world! </h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ) ; } } </script>
this.state
로 바꾸어줬으니 초기 세팅이 필요하다. constructor에서 세팅을 해야한다.- 원래 tictoc 함수 외부에 있던 setInterval를 Clock 컴포넌트 내부로 들여와서, 매 초마다 새로운 시간을 가져오도록 수정한다.
이와 관련된 메서드는 componentDidMount, componentWillUnmount이다
<script> class Clock extends React.Component { constructor (){ super(); this.state = { date : new Date() }; } componentDidMount(){ } componentWillUnmount(){ } render() { return ( <div> <h1> what's up world! </h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } </script>
- Clock 컴포넌트가 화면에 그려지자마자(render되자마자)
componentDidMount
메서드가 호출되면서 timer를 시작한다.- 1초마다 tictock 함수를 호출하게 되고 tictoc 함수는 새로운 날짜를 가져와서
this.state.date
에 업데이트 해준다.- 생성한 timer는 this.timerId에 저장한하는데 setInterval 함수를 호출해서 timer를 생성하면, 해당 timer의 id를 리턴하기에 이를 변수에 저장할 수 있다.
<script> componentDidMount(){ this.timerID = setInterval( () => this.tictoc{}, 1000 ) } </script>
- unmount 될 때,
componentWillUnmount 메서드
가 호출되고 clearInterval 함수를 사용해 아까 만들었던 timer를 삭제해줍니다.<script> componentWillUnmount(){ clearInterval(this.timerID) } </script>
<script> class Clock extends React.Component { constructor () { super(); this.state = { date : new Date() }; } componentDidMount() { this.timerID = setInterval( () => this.tictoc(), 1000 ); } componentWillUnmount(){ clearInterval(this.timerID) } tictoc() { this.setState({ date : new Date() }); } render() { return ( <div> <h1> what's up world! </h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('root') ); </script>
전체적인 과정 (Clock 컴포넌트 Mount -> Unmount 과정)
- ReactDOM.render() 에서 첫 인자로 를 넘길 때, React는 Clock 컴포넌트의
constructor
를 호출한다.- Clock에서 초기 시간이 필요하므로 this.state에 현재 시간으로 초기화한다. 이후 Clock 컴포넌트의
render() 메서드
가 호출된다.- DOM에 render()의 return된 요소가 추가되면
componentDidMount
함수가 호출된다.- Clock 컴포넌트의 tictoc 메서드가 매 초 호출될 수 있도록 timer를 추가한다.
- 매 초 브라우저가 tictoc 메서드를 호출하면서
this.state.date
값이 변한다.- state가 변경되면 원래
componentDidUpdate
함수가 호출되지만, 우리는 정의하지 않았으므로render()
가 다시 호출되면서 바뀐 부분이 변경된다.- DOM에서 Clock 컴포넌트가 삭제될 땐,
componentWillUnmount
가 호출되고 timer도 같이 멈추게 됩니다.
event handler 함수
를 넘겨준다.<script> class Button extends React.Component { constructor() { super(); this.state = { clicked: false } } render() { return ( <div className="btn" onClick={()=>{this.setState({ clicked: !this.state.clicked })}} {this.state.clicked ? '좋아요' : '싫어요'} </div> ); } } ReactDOM.render( <Button />, document.getElementById('root') ); </script>
- Button 컴포넌트의
<div>
태그에 onClick 이벤트에()=>{this.setState({ clicked: !this.state.clicked })}
인 event handler를 넘겨주었다.- 해당 요소를 click 할 때마다
event handler 함수
가 실행되어, 클릭할 때마다this.state.clicked
반대 boolean 값으로 clicked state를 업데이트 한다.- render 메서드 내부의 React 요소에 함수가 포함되면 코드가 깔끔해 보이지 않으니
event handler
부분을 분리해서 함수를 밖으로 뺄 수 있다.
<script> class Button extends React.Component { constructor() { super(); this.state = { click: false } this.handleClick = this.handleClick.bind(this) } handleClick() { this.setState({ clicked: !this.state.clicked }); } render() { return ( <div className={`btn ${this.props.type === 'like' ? 'like-btn' : ''}`} onClick={this.handleClick} {this.state.click ? '좋아요' : '싫어요'} </div> ); } } ReactDOM.render( <Button />, document.getElementById('root') ); </script>
constructor
에 추가된 코드를 보면
this.handleClick = this.handleClick.bind(this);
는
this.handleClick가 this.handleClick.bind(this)
라는 뜻으로 constructor에서 this.handleClick에 이미 this를 bind시킨 handleClick 메서드를 대입해준다.
-handleClick 메서드 내에서 this 키워드를 사용하고 있는데, 이 this는 Button 컴포넌트의 context여야 하기때문에 handleClick.bind(this)라고 작성해준 위치에서 그 this를 handleClick에 넘겨서 handleClick 메서드 내에서도 같은 this를 사용해야한다.- 그렇게하면 Button 컴포넌트의 this.state에 접근할 수 있고 setState 함수도 사용 가능하다.
- 이벤트에
event handler
함수를 넘길때 bind를 해주지 않으면 event handler 함수내에서 this의 context를 잃어버려서 this가 undefined가 되니 React 요소에 이벤트를 추가할 경우 bind(this)를 해줘야한다.
- 리액트의 기본 개념부터 잘 익히자
위코드 주말 사전 학습가이드에서:{}