본 포스팅은 'React.js, 스프링 부트, AWS로 배우는 웹 개발 101 - 김다정'님의 책을 보고 작성되었습니다.
목차
1. 컴포넌트
2. Props와 state
3. material ui를 이용한 디자인
4. 이벤트 핸들러
1. 컴포넌트
Todo 컴포넌트
checkbox와 label을 렌더링하는 컴포넌트
import React from 'react'
class Todo extends React.Component {
render() {
return (
<div className='Todo'>
<input type="checkbox" id="todo0" name="todo0" value= "todo" />
<label for="todo0">Todo 컴포넌트 만들기</label>
</div>
);
}
}
export default Todo;
App컴포넌트에서 Todo 컴포넌트 사용
import React from "react";
class Todo extends React.Component {
render() {
return (
<div className="Todo">
<input
type="checkbox"
id = "todo0"
name="todo0"
value="todo0"/>
<label for = "todo0">Todo 컴포넌트 만들기</label>
</div>
);
}
}
export default Todo;
- JSX도 원하는 컴포넌트를 나열함으로써 원하는 만큼 컴포넌트를 재사용할 수 있다.
2. Props와 state
Props와 state를 사용한 수정 코드
import React from "react";
class Todo extends React.Component {
constructor(props){
super(props);
this.state={item: props.item};
}
render() {
return (
<div className="Todo">
<input
type="checkbox"
id = {this.state.item.id}
name={this.state.item.id}
checked={this.state.item.done}/>
<label id = {this.state.item.id}>{this.state.item.title}</label>
</div>
);
}
}
export default Todo;
- 위의 경우엔
{state.item.id}
를 사용해 HTML 태그 내에서 자바스크립트 변수를 사용한 경우이다.- 자바스크립트로 된 변수를 JSX에서 사용하기 위해선 변수를{}로 묶어주면 된다. {자바스크립트 코드}를 통해 HTML 안에서도 자바스크립트를 사용할 수 있다.
- state는 리액트가 관리하는 오브젝트이다. 리액트에서는 추후 변경이 가능한 변수를 state 오브젝트에서 관리한다.
- 그 이유는 자바스크립트 내에서 변경한 변수의 값을 HTML에 다시 렌더링하기 위함이다.
props에서 item을 넘겨주는 방법
import React from 'react'
class Todo extends React.Component {
constructor(props) {
super(props);
this.state = {
item:{id:0, title:"Hello world 1", done:true},
};
}
render() {
return (
<div className='Todo'>
<Todo item={this.state.item} />
</div>
);
}
}
export default Todo;
props로 매개변수가 넘어가는 과정
Todo 리스트
import React from 'react'
import Todo from './Todo'
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items:[
{id:"0", title:"Hello world 1", done:true},
{id:"1", title:"Hello world 2", done:false},
],
};
}
render() {
var todoItems = this.state.items.map((item, idx)=>(
<Todo item = {item} key={item.id} />
));
return (
<div className='App'>
{todoItems}
</div>
);
}
}
export default App;
- Todo를 하나 더 만들어 item을 하나 더 넘겨본 코드이다.
- Todo 컴포넌트를 연속 2개를 늘어 놓는 대신 배열과 반복문을 이용해 구현한 코드이다.
3. material ui를 이용한 디자인
- material ui는 UI를 조금 더 편리하고 깔끔하게 구성할 수 있게 도와주는 라이브러리이고 리액트 18에는 아직 지원하지 않아 17 버전으로 다운그레이드 하여 yarn을 통해 다운 받아주었다.
- 복잡한 css 없이도 UI 개선이 가능하다. 이 역시도 컴포넌트로 제공되는 것임을 잊지말자.
material ui를 사용한 Todo 컴포넌트 디자인
import React from "react";
import {ListItem, ListItemText, InputBase, Checkbox} from '@material-ui/core';
class Todo extends React.Component {
constructor(props){
super(props);
this.state={item: props.item};
}
render() {
const item = this.state.item;
return (
<ListItem>
<Checkbox checked={item.done}/>
<ListItemText>
<InputBase
inputProps={{"aria-label":"naked"}}
type = "text"
id = {item.id}
name = {item.id}
value = {item.title}
multiline = {true}
fullWidth = {true}
/>
</ListItemText>
</ListItem>
);
}
}
export default Todo;
material ui를 사용한 App 컴포넌트 디자인
import React from 'react';
import Todo from './Todo';
import {Paper, List} from '@material-ui/core';
import "./App.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items:[
{id:"0", title:"Hello world 1", done:true},
{id:"1", title:"Hello world 2", done:false},
],
};
}
render() {
var todoItems = this.state.items.length > 0 && (
<Paper style={{margin:16}}>
<List>
{this.state.items.map((item,idx)=>(
<Todo item ={item} key ={item.id}/>
))}
</List>
</Paper>
);
return (
<div className='App'>
{todoItems}
</div>
);
}
}
export default App;
4. 이벤트 핸들러
Todo 추가
- Todo 추가를 위한 UI
- 백엔드 콜을 대신할 가짜(Mock) 함수를 작성
- 핸들러 함수 구현과 핸들러 함수를 UI에 연결하는 방법
UI 구성
import React from 'react';
import {TextField, Paper, Button, Grid} from '@material-ui/core'
class AddTodo extends React.Component {
constructor(props){
super(props);
this.state = {item: {title: ""}}; /* 사용자 입력을 저장할 오브젝트 */
}
render() {
return (
<Paper style={{margin:16, padding:16}}>
<Grid container>
<Grid xs={11} md={11} item style={{paddingRight:16}}>
<TextField placeholder='Add Todo here' fullWidth />
</Grid>
<Grid xs={1} md={1} item>
<Button fullWidth color='secondary' variant='outline'>
+
</Button>
</Grid>
</Grid>
</Paper>
);
}
}
export default AddTodo;
import React from 'react';
import Todo from './Todo';
import {Paper, List, Container} from '@material-ui/core';
import "./App.css";
import AddTodo from './AddTodo';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items:[
{id:"0", title:"Hello world 1", done:true},
{id:"1", title:"Hello world 2", done:false},
],
};
}
render() {
var todoItems = this.state.items.length > 0 && (
<Paper style={{margin:16}}>
<List>
{this.state.items.map((item,idx)=>(
<Todo item ={item} key ={item.id}/>
))}
</List>
</Paper>
);
return (
<div className='App'>
<Container maxWidth="md">
<AddTodo/>
<div className='TodoList'>{todoItems}</div>
</Container>
</div>
);
}
}
export default App;
Add 핸들러 추가
순서
- 키보드 입력
- onInputChange 함수 실행
- + 버튼 클릭 또는 Enter 키 누름
- onButtonClick 함수 실행 or enterKeyEventHandler 함수 실행
구현해야 할 함수들 목록
- onInputChange
- 사용자가 input 필드에 키를 하나 입력할 때마다 실행되며 input 필드에 담긴 문자열을 자바스크립트 오브젝트에 저장한다.
- onButtonClick
- 사용자가 +버튼을 클릭할 때 실행되며 onInputChange에서 저장하고 있던 문자열을 리스트에 추가해준다.
- enterKeyEventHandle
- 사용자가 input 필드상에서 엔터 또는 리턴키를 눌렀을 때 실행되며 onInputChange에서 저장하고 있던 문자열을 리스트에 추가해준다.
onInputChange 함수 작성
onInputChange = (e) => { //함수 작성
const thisItem = this.state.item;
thisItem.title = e.target.value;
this.setState({item:thisItem});
console.log(thisItem);
}
- Event e를 매개변수로 받는다. event는 어떤 일이 일어난 것을 의미한다.
- 자바스크립트의 event 오브젝트는 어떤 일이 일어났을 때의 상태와 그 일에 대한 정보를 담고 있다.
- onChage()는 이벤트가 발생할 때 실행해야 하는 함수로 이벤트 핸들러라고 한다. 개발자는 우리가 만든 함수와 이벤트 핸들러를 사용필드와 연결해주는 작업을 해야 한다.
onInputChange 함수 사용
<TextField
placeholder='Add Todo here'
fullWidth
onChange={this.onInputChange}
value ={this.state.item.title}
/>
- 사용자가 input 필드에 입력하는 정보를 컴포넌트 내부에서 임시로 저장하려고 변수를 초기화 시켜주었다.
- input 필드에 정보를 입력하기 시작하면 그 정보는 TextField 컴포넌트로 전달된다.
- TextField는 onChange를 props로 받고 이 함수는 사용자가 TextField에서 키보드를 하나 누를 때마다 실행된다.
- onChange에 핸들러 함수인 onInputChange를 연결해 사용자가 입력하는 정보를 item에 저장할 수 있다.
간단 정리쇼
이벤트 함수를 작성 -> 이벤트 핸들러를 통해 사용하고자 하는 필드에 연결 -> 필드 값을 변화 시켜주기(value값을 위에 예시에선 변경해줌.).
add함수 작성
- 추가되는 새로운 Todo 리스트의 경우엔 AddTodo 컴포넌트에서 관리할 수 없다. 그 이유는 App 컴포넌트가 AddTodo의 상위 컴포넌트로 하위 컴포넌트에서 상위 컴포넌트의 items에 접근할 수 없기 때문이다.
- 따라서 리스트에 추가하는 함수는 App 컴포넌트에서 작성해주어야 하고 App 컴포넌트에 add() 함수를 추가해준 뒤 해당 함수를 AddTodo의 프로퍼티로 넘겨 AddTodo에서 사용할 수 있게 해야 한다.
import React from 'react';
import Todo from './Todo';
import {Paper, List, Container} from '@material-ui/core';
import "./App.css";
import AddTodo from './AddTodo';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items:[
{id:"0", title:"Hello world 1", done:true},
{id:"1", title:"Hello world 2", done:false},
],
};
}
// add() 함수 추가
add = (item) => {
const thisItems = this.state;
item.id = "ID" + thisItems.length; // key를 위한 id 추가
item.done = false; // done 초기화
thisItems.push(item); // 리스트에 아이템 추가
this.setState({items:thisItems}); //업데이트의 경우엔 반드시 this.setState로 진행
console.log("items: ", this.state.items);
}
render() {
var todoItems = this.state.items.length > 0 && (
<Paper style={{margin:16}}>
<List>
{this.state.items.map((item,idx)=>(
<Todo item ={item} key ={item.id}/>
))}
</List>
</Paper>
);
//함수 연결 AddTodo에서 사용할 수 있게 props로 넘겨준다.
return (
<div className='App'>
<Container maxWidth="md">
<AddTodo add={this.add} />
<div className='TodoList'>{todoItems}</div>
</Container>
</div>
);
}
}
export default App;
// add() 함수 추가
add = (item) => {
const thisItems = this.state.items;
item.id = "ID" + thisItems.length; // key를 위한 id 추가
item.done = false; // done 초기화
thisItems.push(item); // 리스트에 아이템 추가
this.setState({items:thisItems}); //업데이트의 경우엔 반드시 this.setState로 진행
console.log("items: ", this.state.items);
}
<AddTodo add={this.add} />
AddTodo에서 add 함수 사용 방법
import React from 'react';
import {TextField, Paper, Button, Grid} from '@material-ui/core'
class AddTodo extends React.Component {
constructor(props){
super(props);
this.state = {item: {title: ""}}; /* 사용자 입력을 저장할 오브젝트 */
this.add = props.add; // props의 함수를 this.add에 연결
}
onInputChange = (e) => { //함수 작성
const thisItem = this.state.item;
thisItem.title = e.target.value;
this.setState({item:thisItem});
console.log(thisItem);
}
onButtonClick= (e) => {
this.add(this.state.item);//add함수 사용
this.setState({item:{title: ""}});
}
render() {
//함수 연결
return (
<Paper style={{margin:16, padding:16}}>
<Grid container>
<Grid xs={11} md={11} item style={{paddingRight:16}}>
<TextField
placeholder='Add Todo here'
fullWidth
onChange={this.onInputChange}
value ={this.state.item.title}
/>
</Grid>
<Grid xs={1} md={1} item>
<Button
fullWidth
color='secondary'
variant='outline'
onClick={this.onButtonClick}>
+
</Button>
</Grid>
</Grid>
</Paper>
);
}
}
export default AddTodo;
간단 정리쇼
상위 컴포넌트에서 관리하는 객체를 사용해야 할 경우?
- 상위 컴포넌트 (App)
- 상위 컴포넌트에서 함수를 만든다.
- 만든 함수를 하위 컴포넌트의 props로 넘겨준다.
- 하위 컴포넌트 (AddTodo)
- 사용하고자 하는 함수를 하위컴포넌트에서 props로 연결해준다.
- 하위 컴포넌트에서 사용 해준다. (onButtonClick에서 add() 함수를 props로 넘겨 받아서 사용한다는 의미이다.)
- 이벤트 핸들러를 사용해서 함수를 연결해준다.
Enter키 입력 시 아이템 추가
import React from 'react';
import {TextField, Paper, Button, Grid} from '@material-ui/core'
class AddTodo extends React.Component {
constructor(props){
super(props);
this.state = {item: {title: ""}}; /* 사용자 입력을 저장할 오브젝트 */
this.add = props.add; // props의 함수를 this.add에 연결
}
onInputChange = (e) => { //함수 작성
const thisItem = this.state.item;
thisItem.title = e.target.value;
this.setState({item:thisItem});
console.log(thisItem);
}
onButtonClick= (e) => {
this.add(this.state.item);//add함수 사용
this.setState({item:{title: ""}});
}
enterKeyEventHandler = (e) => {
if(e.key === 'Enter'){
this.onButtonClick();
}
}
render() {
//함수 연결
return (
<Paper style={{margin:16, padding:16}}>
<Grid container>
<Grid xs={11} md={11} item style={{paddingRight:16}}>
<TextField
placeholder='Add Todo here'
fullWidth
onChange={this.onInputChange}
value ={this.state.item.title}
onKeyPress={this.enterKeyEventHandler}
/>
</Grid>
<Grid xs={1} md={1} item>
<Button
fullWidth
color='secondary'
variant='outline'
onClick={this.onButtonClick}>
+
</Button>
</Grid>
</Grid>
</Paper>
);
}
}
export default AddTodo;
전체적인 흐름 정리(개인적으로 학습된 것들)
- 컴포넌트가 무엇인지 리액트는 컴포넌트를 어떻게 사용하는지를 살펴보았다.
- props는 변하지 않는 것 부모 요소에서 정해지는 것이라는 점을 알았다.
- state는 변할 수 있는 것이고 컴포넌트가 스스로 관리하는 것이라는 점을 알았다.
- 조금 더 편리한 UI 사용을 위해 material-ui 사용법을 알아 보았고 이는 부트스트랩과 별반 다르지 않아 어렵지 않게 다가왔다.
- 이벤트 핸들러를 추가하기 위해서는 함수를 추가한 뒤 바인딩한 뒤 사용하고 상위 컴포넌트에서 정의한 함수를 사용하기 위해서는 함수 추가 바인딩 하위 컴포넌트에 연결하는 패턴을 학습했다.