-
Notifications
You must be signed in to change notification settings - Fork 6
React
리액트의 개발환경을 구축하고 싶다.
- nodejs 설치
- npm i create-react-app
- create-react-app myapp
- cd myapp
- npm start
- 전체코드 : https://github.com/egoing/react-tutorial-steps/tree/8a8c4c05a2bee99c7f3b48cb3578d95042ef9cd7
- 변경점 : https://github.com/egoing/react-tutorial-steps/commit/8a8c4c05a2bee99c7f3b48cb3578d95042ef9cd7
- 실습 : https://stackblitz.com/edit/react-step-0
아래의 코드를 리액트화하고 싶다.
<header>
<h1>WEB</h1>
</header>
<nav>
<ul>
<li>
<a href="/1">html</a>
</li>
<li>
<a href="/2">css</a>
</li>
<li>
<a href="/3">javascript</a>
</li>
</ul>
</nav>
<article>
<h2>Welcome</h2>
Hello, WEB!
</article>- 전체코드 : https://github.com/egoing/react-tutorial-steps/tree/51ce11f02e5d1d1694ba4db22e21d96b27ad3fdf
- 변경점 : https://github.com/egoing/react-tutorial-steps/commit/51ce11f02e5d1d1694ba4db22e21d96b27ad3fdf
- 실습 : https://stackblitz.com/edit/react-step-1
header, nav, article 태그가 너무 복잡합니다. 이들을 컴포넌트화하고 싶다.
각 태그를 Header, Nav, Article 라는 이름의 컴포넌트로 만들어서 단순화 해보세요.
function Header() {
//todo
}
function Nav() {
//todo
}
function Article() {
//todo
}
export default function App() {
return (
<div>
<Header />
<Nav />
<Article />
</div>
);
}
- 전체코드 : https://github.com/egoing/react-tutorial-steps/tree/a91065184c004d0c3776f23546538d01e98146ef
- 변경사항 : https://github.com/egoing/react-tutorial-steps/commit/a91065184c004d0c3776f23546538d01e98146ef
- 실습 : https://stackblitz.com/edit/react-step-2
컴포넌트가 너무 경직 되어 있습니다. HTML 태그 처럼 속성을 통해서 컴포넌트를 커스터마이징 하고 싶습니다.
아래와 같은 인터페이스를 가지고 있는 컴포넌트를 만들어보세요.
function Nav(props) {
//todo
}
function Article(props) {
//todo
}
export default function App() {
return (
<div>
<Header />
<Nav
data={[
{ id: 1, title: "html", description: "html is..." },
{ id: 2, title: "css", description: "css is..." },
{ id: 3, title: "js", description: "js is..." }
]}
/>
<Article title="제목" description="본문" />
</div>
);
}
- 전체코드 : https://github.com/egoing/react-tutorial-steps/tree/e4c77e955799bd1ae873690ffcc9d7a728aab68b
- 변경점 : https://github.com/egoing/react-tutorial-steps/commit/e4c77e955799bd1ae873690ffcc9d7a728aab68b
- 실습 : https://stackblitz.com/edit/react-step-3
컴포넌트에 이벤트 인터페이스를 추가해서 핸들러의 상태 변화를 통보 받을 수 있도록 하고 싶다.
function Header(props) {
//todo
}
function Nav(props) {
//todo
}
export default function App() {
var article = null;
if (mode === "WELCOME") {
// todo
} else if (mode === "READ") {
// todo
}
return (
<div>
<Header
onChangeMode={function() {
setMode("WELCOME");
}}
/>
<Nav
data={topics}
onChangeMode={function(topic_id) {
setMode("READ");
setSelectedId(topic_id);
}}
/>
{article}
</div>
);
}
- 전체코드 : https://github.com/egoing/react-tutorial-steps/tree/ae3f98bc9e2e4d2a90058c483530b32f12433768
- 변경사항 : https://github.com/egoing/react-tutorial-steps/commit/ae3f98bc9e2e4d2a90058c483530b32f12433768
- 실습 : https://stackblitz.com/edit/react-step-4
글생성 기능을 추가하고 싶습니다.
- 데이터를 변경할 때는 불변성을 유지하도록 주의 합니다.
- 원시 데이터 타입은 그냥 변경하면 됩니다.
- 배열과 객체는 원본을 복제한 후에 변경하면 됩니다.
- 차이는 다음 영상을 참고해주세요. https://youtu.be/fDJG4CjbB3A?t=215 참고 : https://gist.github.com/aluksidadi/10aa07c6f007efc587f793212548ad51
- immutable 과 관련된 이슈는 다음 영상을 참고하세요. https://opentutorials.org/module/4075
- immutable.js 와 같은 라이브러리를 이용하는 것을 추천합니다.
- 기존 js 문법의 문제점과 immutable.js를 사용하는 이유 : https://docs.google.com/spreadsheets/d/1tn5YI9wDZieZOwJHCaT_cQuixw64tr_iv3Pv6AqdfI8/edit#gid=0
function Create(props) {
return (
<article>
<h1>Create</h1>
<form
action="topics"
method="post"
onSubmit={function(e) {
//todo
}}
>
<p>
<input type="text" name="title" placeholder="title" />
</p>
<p>
<textarea name="description" placeholder="description" />
</p>
<p>
<input type="submit" />
</p>
</form>
</article>
);
}
function Control(props) {
return (
<ul>
<li>
<input
type="button"
value="create"
onClick={function() {
//todo
}}
/>
</li>
</ul>
);
}
export default function App() {
var article = null;
if (mode === "WELCOME") {
} else if (mode === "READ") {
} else if (mode === "CREATE") {
article = (
<Create
onCreate={function(data) {
data.id = nextId;
setTopics([...topics, data]);
setSelectedId(nextId);
setMode("READ");
setNextId(nextId + 1);
}}
/>
);
}
return (
<div>
//...
<Control
onChangeMode={function(mode) {
setMode(mode);
}}
/>
</div>
);
}
- 전체코드 : https://github.com/egoing/react-tutorial-steps/commit/251d88a7643cad924876e4c0c81e26c022ebdb89
- 뱐걍시힝 : https://github.com/egoing/react-tutorial-steps/commit/251d88a7643cad924876e4c0c81e26c022ebdb89
- 실습 : https://stackblitz.com/edit/react-step-5
수정 기능을 추가하고 싶습니다.
- 폼을 수정할 때는 onChange 이벤트를 통해서 state 값을 수정하고, 수정된 state값이 value에 반영될 수 있도록 처리해야 합니다.
function Update(props) {
var [title, setTitle] = useState(props.data.title);
var [description, setDescription] = useState(props.data.description);
return (
<article>
<h1>Update</h1>
<form
action="topics"
method="put"
onSubmit={function(e) {
//todo
}}
>
<p>
<input
type="text"
name="title"
placeholder="title"
value={title}
onChange={function(e) {
//todo
}}
/>
</p>
<p>
<textarea
name="description"
placeholder="description"
value={description}
onChange={function(e) {
//todo
}}
/>
</p>
<p>
<input type="submit" />
</p>
</form>
</article>
);
}
function Control(props) {
//todo
}
export default function App() {
//todo
} else if (mode === "UPDATE") {
var selectedTopic = null;
for (var i = 0; i < topics.length; i++) {
if (topics[i].id === selectedId) {
selectedTopic = topics[i];
break;
}
}
article = (
<Update
data={selectedTopic}
onUpdate={function(data) {
var newTopics = [...topics];
for (var i = 0; i < newTopics.length; i++) {
if (newTopics[i].id === data.id) {
newTopics[i].title = data.title;
newTopics[i].description = data.description;
break;
}
}
setTopics(newTopics);
setMode("READ");
}}
/>
);
}
//...
}
- 전체코드 : https://github.com/egoing/react-tutorial-steps/tree/374950f1f16ed83fa157a69a7884fa2fb33958cb
- 변경사항 : https://github.com/egoing/react-tutorial-steps/commit/374950f1f16ed83fa157a69a7884fa2fb33958cb
- 실습 : https://stackblitz.com/edit/react-step-6
삭제를 구현하고 싶습니다.
<Control
onChangeMode={function(mode) {
if (mode === "DELETE") {
todo
}
}}
/>
- 전체코드 : https://github.com/egoing/react-tutorial-steps/tree/8b31c2b0f5ab8d68e8d9c57ff65d27a5f71849ae
- 변경사항 : https://github.com/egoing/react-tutorial-steps/commit/8b31c2b0f5ab8d68e8d9c57ff65d27a5f71849ae
- 실습 : https://stackblitz.com/edit/react-step-7
코드가 너무 많아졌습니다. 코드를 파일로 분리해서 정리 정돈하고 싶습니다.
export, import
https://stackblitz.com/edit/react-exim?file=src/App.js
MyModule.js
// 외부에 노출을 원하지 않는 함수
function privateFunc() {
return "private";
}
// privateFunc를 이용해서 외부에서 사용되도록 고안된 함수
function publicFunc() {
return privateFunc() + " + " + "public";
}
// 외부에서 사용할 수 있도록 export 한다.
export { publicFunc };
// 외부에서 renamedFunc로 사용하도록 한다.
export { publicFunc as renamedFunc };
// var, const, let도 가능하다.
export var publicVar = "public var";
function defaultFunc() {
return "default";
}
export default defaultFunc;
App.js
import { publicFunc } from "./MyModule";
console.log(publicFunc());
import { renamedFunc } from "./MyModule";
console.log(renamedFunc());
import { publicVar } from "./MyModule";
console.log(publicVar);
import my from "./MyModule";
console.log(my());
- 전체코드 : https://github.com/egoing/react-tutorial-steps/tree/cfeb2c02f8f16350ddc308ab0a7e83d588fdfaa5
- 변경점 : https://github.com/egoing/react-tutorial-steps/commit/cfeb2c02f8f16350ddc308ab0a7e83d588fdfaa5
- 실습 : https://stackblitz.com/edit/react-step-8
ajax를 이용해서 리로드 없이 동적으로 데이터를 변경하고 싶습니다.
useEffect - 사이드 이팩트란 무엇인가? react 에서 ajax 통신하는 방법 .env.development.template => .env.development 로 변경해서 서버와 통신 할 REACT_APP_API_URL 값을 셋팅해줘야 한다.
https://stackblitz.com/edit/web-vanilla?file=index.html
CREATE DATABASE mydb;
USE mydb;
CREATE TABLE `topic` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(45) DEFAULT NULL,
`description` text,
`created` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `topic` VALUES (1,'MySQL','MySQL is ...','2021-01-07 11:37:35'),(2,'Oracle','Oracle is ...','2021-01-07 11:37:35'),(3,'SQL Server','SQL Server is ...','2021-01-07 11:37:35');import random
from flask import Flask, request, redirect, send_from_directory, render_template, make_response, make_response, Response
import json
from flask_cors import CORS
import pymysql
app = Flask(__name__)
CORS(app)
resp = Response()
resp.headers['Access-Control-Allow-Origin'] = '*'
@app.route('/topics', defaults={'topic_id':None}, methods=['GET', 'POST'])
@app.route('/topics/<topic_id>', methods=['PUT', 'GET', 'DELETE'])
def topics(topic_id):
try:
db = pymysql.connect(
user='root',
passwd='devpass',
host='127.0.0.1',
port=3306,
db='mydb',
charset='utf8',
autocommit=True
)
cursor = db.cursor(pymysql.cursors.DictCursor)
if request.method == 'GET':
if topic_id == None:
sql = "SELECT * FROM topic"
cursor.execute(sql)
topics = cursor.fetchall()
output = []
for topic in topics:
output.append({'id':topic['id'], 'title':topic['title'], 'created':topic['created'].strftime('%Y-%m-%d %H:%M:%S')})
resp.set_data(json.dumps(output))
return resp
else:
sql = "SELECT * FROM topic WHERE id = "+str(topic_id)
cursor.execute(sql)
print('sql', sql)
topic = cursor.fetchone()
output = json.dumps({'id':topic['id'], 'title':topic['title'], 'description':topic['description'], 'created':topic['created'].strftime('%Y-%m-%d %H:%M:%S')})
resp.set_data(output)
return resp
elif request.method == 'POST':
form = request.get_json()
print('form', form)
sql = """
INSERT INTO topic (title, description, created)
VALUES(
'%(title)s',
'%(description)s',
NOW()
)
""" % {'title':form['title'], 'description':form['description']}
cursor.execute(sql)
output = json.dumps({'id':cursor.lastrowid, 'title':form['title'], 'description':form['description'] })
resp.set_data(output)
return resp
elif request.method == 'PUT':
form = request.get_json()
sql = """
UPDATE topic SET
title = '%(title)s',
description = '%(description)s'
WHERE id = '%(topic_id)s'
""" % {'topic_id': topic_id, 'title':form['title'], 'description':form['description']}
cursor.execute(sql)
output = json.dumps({'id':topic_id, 'title':form['title'], 'description':form['description'] })
resp.set_data(output)
return resp
elif request.method == 'DELETE':
sql = """
DELETE FROM topic WHERE id = %(id)s
""" % {'id':topic_id}
cursor.execute(sql)
output = json.dumps({'id':topic_id})
resp.set_data(output)
return resp
finally:
cursor.close()
db.close()
@app.route('/static/<path:path>')
def static_files(path):
return send_from_directory('static', path)
@app.route('/', defaults={'path':None})
@app.route('/<path>')
def index(path):
resp.set_data(render_template('index.html'))
return resp
if __name__ == '__main__':
app.run(debug=True)- 전체코드 : https://github.com/egoing/react-tutorial-steps/tree/fc1212d102bc7c6f522fe360ae73d90a10c8023f
- 변경점 : https://github.com/egoing/react-tutorial-steps/commit/fc1212d102bc7c6f522fe360ae73d90a10c8023f
- 실습 : https://stackblitz.com/edit/react-step-9-ajax
UI에 따라서 페이지 리로딩 없이 URL이 변경되고, URL에 따라서 적당한 정보가 표시되도록 개선하고 싶습니다.
- history.pushState를 이용해서 URL을 변경하는 방법
- 전체코드 : https://github.com/egoing/react-tutorial-steps/tree/094140449fc4bee2e0172c13dd64ceed7b8d21f6
- 변경점 : https://github.com/egoing/react-tutorial-steps/commit/094140449fc4bee2e0172c13dd64ceed7b8d21f6
- 실습 : https://stackblitz.com/edit/react-step-9-route
step 9 - route 에서 구현한 내용을 react-route-dom을 이용해서 개선하고 싶습니다.
- react-route-dom을 이용하는 방법
- react-route-dom을 이용해서 if 문을 없애고, 보다 단순하게 코드를 제어할 수 있게 됨을 공감