├── .DS_Store ├── ch1 ├── .DS_Store ├── a.txt ├── anonymous.js ├── anonymous2.js ├── arrow-test.js ├── b.txt ├── babeltest │ ├── .babelrc │ ├── bmi.js │ ├── index.html │ ├── package.json │ └── src │ │ ├── a.js │ │ └── b.js ├── c.txt ├── calculator_main.js ├── calculator_module.js ├── dice-server.js ├── es2015io │ ├── .DS_Store │ ├── .babelrc │ ├── package.json │ └── src │ │ ├── calctest.js │ │ ├── main.js │ │ ├── main2.js │ │ └── main3.js ├── es2015io2 │ ├── .babelrc │ ├── package.json │ └── src │ │ ├── gobsem.js │ │ └── main.js ├── fib.js ├── generator-test.js ├── hello-server.js ├── project_a │ ├── index.js │ └── package.json ├── readfile-async.js ├── readfile-cb.js ├── readfile-cb2.js ├── readfile-cb3.js ├── readfile-generator.js ├── readfile-promise.js ├── readfile-sync.js ├── readfile.js ├── request-downloadfile.js ├── test.png └── test.txt ├── ch2 ├── .DS_Store ├── cp-arrow.html ├── cp-class.html ├── cp-greeting.html ├── cp-imagetext.html ├── cp-imagetext2.html ├── cp-stopwatch.html ├── greeting.html ├── hello-react.html ├── hello │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg ├── img │ ├── pic1.jpeg │ ├── pic2.jpeg │ └── pic3.jpeg ├── jsx-conv │ ├── a.js │ ├── b.js │ └── package.json ├── jsx-embed.html ├── jsx-embed2-error.html ├── jsx-embed2.html ├── jsx-error.html ├── jsx-escape.html ├── jsx-style.html ├── jsx-test.html ├── list-items.html ├── list-items2.html ├── package.json ├── react-binclock.html ├── react-clock.html ├── react-clock2.html ├── react-stopwatch.html ├── st-checkbox.html ├── st-click.html ├── st-click2.html ├── st-clock.html ├── state-tf.png ├── temp.html ├── temp.js ├── test-webpack │ ├── calc.js │ ├── main.js │ └── webpack.config.js ├── test-webpack2 │ ├── main.html │ ├── package.json │ ├── src │ │ ├── Hello.js │ │ └── main.js │ └── webpack.config.js └── tree.html ├── ch3 ├── .DS_Store ├── cycle │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg ├── form_multi │ ├── .DS_Store │ ├── build.config.js │ ├── build.sh │ ├── index.html │ ├── out │ │ └── bundle.js │ └── src │ │ ├── MultiForm.css │ │ ├── MultiForm.js │ │ └── index.js ├── form_number │ ├── .DS_Store │ ├── build.config.js │ ├── build.sh │ ├── number_form.html │ ├── out │ │ └── bundle.js │ └── src │ │ ├── NumberForm.js │ │ └── index.js ├── form_simple │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── SimpleForm.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg ├── inch_to_cm │ ├── .DS_Store │ ├── build.sh │ ├── index.html │ ├── out │ │ └── bundle.js │ ├── package.json │ ├── src │ │ ├── .DS_Store │ │ ├── InchToCm.js │ │ ├── ValueInput.js │ │ └── index.js │ └── webpack.config.js ├── input_compo │ ├── .DS_Store │ ├── index.html │ ├── out │ │ └── bundle.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── .DS_Store │ │ ├── FormInput.js │ │ └── index.js │ └── webpack.config.js ├── input_zip │ ├── .DS_Store │ ├── build.sh │ ├── index.html │ ├── out │ │ └── bundle.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── .DS_Store │ │ ├── ZipInput.js │ │ └── index.js │ └── webpack.config.js ├── parts │ ├── .DS_Store │ ├── build.config.js │ ├── build.sh │ ├── cbox.html │ ├── out │ │ ├── .DS_Store │ │ ├── cbox.js │ │ ├── radio.js │ │ ├── select.js │ │ ├── text.js │ │ └── textarea.js │ ├── radio.html │ ├── select.html │ ├── src │ │ ├── .DS_Store │ │ ├── cbox.js │ │ ├── radio.js │ │ ├── select.js │ │ ├── text.js │ │ └── textarea.js │ ├── text.html │ └── textarea.html ├── refs-focus.html ├── refs-instance-test.html ├── sagent │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── fruits.json │ │ └── index.html │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ └── test-sagent.js ├── st-color.html └── stopwatch │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ ├── favicon.ico │ └── index.html │ └── src │ ├── Stopwatch.css │ ├── Stopwatch.js │ ├── index.js │ └── logo.svg ├── ch4 ├── .DS_Store ├── TestNative │ ├── .babelrc │ ├── .buckconfig │ ├── .flowconfig │ ├── .gitattributes │ ├── .gitignore │ ├── .watchmanconfig │ ├── app.json │ ├── index.android.js │ ├── index.ios.js │ ├── package-lock.json │ ├── package.json │ └── yarn.lock ├── electron_clipfmt │ ├── .DS_Store │ ├── index.html │ ├── main.js │ ├── out │ │ └── index.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.js │ └── webpack.config.js ├── electron_hello │ ├── .DS_Store │ ├── index.html │ ├── main.js │ ├── out │ │ └── index.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.js │ └── webpack.config.js ├── mstdn_cli │ ├── .DS_Store │ ├── 1_create_app.js │ ├── 2_auth.js │ ├── 3_get_timeline.js │ ├── 4_toot.js │ ├── index.html │ ├── main.js │ ├── out │ │ └── index.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── index.js │ │ └── styles.js │ └── webpack.config.js └── native_mstdn │ ├── .babelrc │ ├── .buckconfig │ ├── .flowconfig │ ├── .gitattributes │ ├── .gitignore │ ├── .watchmanconfig │ ├── MastodonClient.js │ ├── app.json │ ├── index.android.js │ ├── index.ios.js │ ├── package-lock.json │ ├── package.json │ └── yarn.lock ├── ch5 ├── .DS_Store ├── bbs │ ├── .DS_Store │ ├── bbs-server.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── bundle.js │ │ └── index.html │ ├── src │ │ └── index.js │ └── webpack.config.js ├── chat │ ├── .DS_Store │ ├── chat-server.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── bundle.js │ │ └── index.html │ ├── src │ │ ├── index.js │ │ └── styles.js │ └── webpack.config.js ├── express_test │ ├── .DS_Store │ ├── dice-ex.js │ ├── dice-q.js │ ├── dice.js │ ├── hello.js │ ├── html │ │ ├── .DS_Store │ │ ├── fuga │ │ │ └── index.html │ │ ├── hoge.html │ │ └── index.html │ ├── package.json │ ├── post-show.js │ ├── post-test.js │ ├── post-upload.js │ ├── pub │ │ └── .DS_Store │ └── static.js ├── flux_test │ ├── .DS_Store │ ├── index.html │ ├── out │ │ └── bundle.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── actions.js │ │ ├── appDispatcher.js │ │ ├── index.js │ │ └── stores.js │ └── webpack.config.js ├── nedb_test │ ├── .DS_Store │ ├── a.js │ ├── package.json │ └── test.db ├── router_params │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── CustomerApp.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ └── registerServiceWorker.js ├── router_test │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── HelloApp.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ └── registerServiceWorker.js └── router_test2 │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json │ └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── HelloApp2.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ └── registerServiceWorker.js ├── ch6 ├── .DS_Store ├── handwriting │ ├── .DS_Store │ ├── 1-download.js │ ├── 2-conv2csv.js │ ├── 3-split.js │ ├── 4-train.js │ ├── 5-test.js │ ├── 6-server.js │ ├── database │ │ └── .DS_Store │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── bundle.js │ │ └── index.html │ ├── src │ │ ├── index.js │ │ └── styles.js │ └── webpack.config.js ├── sns │ ├── .DS_Store │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── bundle.js │ │ ├── default.css │ │ ├── index.html │ │ └── user.png │ ├── server │ │ ├── .DS_Store │ │ ├── database.js │ │ ├── timeline.db │ │ └── user.db │ ├── sns-server.js │ ├── src │ │ ├── index.js │ │ ├── sns_login.js │ │ ├── sns_timeline.js │ │ ├── sns_users.js │ │ └── styles.js │ └── webpack.config.js └── wiki │ ├── .DS_Store │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── bundle.js │ ├── default.css │ └── index.html │ ├── src │ ├── index.js │ ├── styles.js │ ├── wiki_edit.js │ ├── wiki_parser.js │ ├── wiki_parser.pegjs │ └── wiki_show.js │ ├── test-parser.js │ ├── webpack.config.js │ └── wiki-server.js ├── package-lock.json └── package.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/.DS_Store -------------------------------------------------------------------------------- /ch1/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch1/.DS_Store -------------------------------------------------------------------------------- /ch1/a.txt: -------------------------------------------------------------------------------- 1 | *** a.txt *** -------------------------------------------------------------------------------- /ch1/anonymous.js: -------------------------------------------------------------------------------- 1 | // 익명 함수를 사용해 함수를 정의합니다. 2 | const f1 = function (s) { console.log(s) } 3 | const f2 = (s) => { console.log(s) } 4 | // 익명 함수는 일반적인 함수처럼 사용합니다. 5 | f1('foo') 6 | f2('bar') -------------------------------------------------------------------------------- /ch1/anonymous2.js: -------------------------------------------------------------------------------- 1 | // 소문자를 대문자로 변환하는 예 2 | const s = 'Keep On Asking, and It Will Be Given You.' 3 | const r = s.replace(/[a-z]+/g, function (m) { 4 | return m.toUpperCase() 5 | }) 6 | console.log(r) 7 | 8 | // 배열의 숫자를 정렬하는 예 9 | const ar = [100, 1, 20, 43, 30, 11, 4] 10 | ar.sort((a, b) => { return b - a }) 11 | console.log(ar) -------------------------------------------------------------------------------- /ch1/arrow-test.js: -------------------------------------------------------------------------------- 1 | // 화살표 함수의 예 2 | const x3 = (n) => n * 3 -------------------------------------------------------------------------------- /ch1/b.txt: -------------------------------------------------------------------------------- 1 | *** b.txt *** -------------------------------------------------------------------------------- /ch1/babeltest/.babelrc: -------------------------------------------------------------------------------- 1 | { "presets": ["es2015"] } -------------------------------------------------------------------------------- /ch1/babeltest/bmi.js: -------------------------------------------------------------------------------- 1 | // BMI 클래스를 정의합니다. 2 | class BMI { 3 | constructor (height, weight) { 4 | this.height = height 5 | this.weight = weight 6 | this.bmi = this.calc() 7 | } 8 | calc () { 9 | const heightM = this.height / 100 10 | return this.weight / (heightM ** 2) 11 | } 12 | print () { 13 | let res = '표준' 14 | if (this.bmi >= 25) res = '비만' 15 | else if (this.bmi >= 18.5) res = '표준' 16 | else res = '저체중' 17 | console.log('BMI =', this.bmi, res) 18 | } 19 | } 20 | // 테스트 21 | const bmi = new BMI(160, 60) 22 | bmi.print() -------------------------------------------------------------------------------- /ch1/babeltest/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ch1/babeltest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babeltest", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "build": "babel bmi.js -o bmi.out.js", 7 | "watch": "babel bmi.js -w -o bmi.out.js", 8 | "start": "node bmi.out.js" 9 | }, 10 | "devDependencies": { 11 | "babel-cli": "^6.23.0", 12 | "babel-preset-es2015": "^6.22.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ch1/babeltest/src/a.js: -------------------------------------------------------------------------------- 1 | setTimeout(() => { 2 | console.log('foo') 3 | }, 100) 4 | -------------------------------------------------------------------------------- /ch1/babeltest/src/b.js: -------------------------------------------------------------------------------- 1 | // Foo 클래스를 정의합니다. 2 | class Foo { 3 | constructor () { 4 | this.value = 100 5 | } 6 | bar() { 7 | console.log('Foo.bar') 8 | console.log(this.value) 9 | } 10 | } 11 | // Foo를 사용합니다. 12 | const f = new Foo() 13 | f.bar() -------------------------------------------------------------------------------- /ch1/c.txt: -------------------------------------------------------------------------------- 1 | *** c.txt *** -------------------------------------------------------------------------------- /ch1/calculator_main.js: -------------------------------------------------------------------------------- 1 | // 모듈을 읽어 들입니다. 2 | const calculator = require('./calculator_module.js') 3 | // 모듈의 함수를 사용합니다. 4 | console.log('3+5=', calculator.add(3, 5)) 5 | console.log('4*8=', calculator.mul(4, 8)) -------------------------------------------------------------------------------- /ch1/calculator_module.js: -------------------------------------------------------------------------------- 1 | // 덧셈과 곱셈하는 함수 2 | function add (a, b) { 3 | return a + b 4 | } 5 | function mul (a, b) { 6 | return a * b 7 | } 8 | // 외부에 공개합니다. 9 | module.exports = { 10 | 'add': add, 11 | 'mul': mul 12 | } -------------------------------------------------------------------------------- /ch1/dice-server.js: -------------------------------------------------------------------------------- 1 | // http 모듈을 읽어 들입니다. 2 | const http = require('http') 3 | const ctype = { 'Content-Type': 'text/html;charset=utf-8' } 4 | 5 | // 웹 서버를 실행합니다. --- (※1) 6 | const svr = http.createServer(handler) // 서버를 생성합니다. 7 | svr.listen(8081) // 8081번 포트를 사용합니다. 8 | 9 | // 서버에 접근이 있을 때의 처리 --- (※2) 10 | function handler (req, res) { 11 | // URL을 구분합니다. 12 | const url = req.url 13 | // 최상위 페이지일 때 14 | if (url === '/' || url === '/index.html') { 15 | showIndexPage(req, res) 16 | return 17 | } 18 | // 주사위 페이지일 때 19 | if (url.substr(0, 6) === '/dice/') { 20 | showDicePage(req, res) 21 | return 22 | } 23 | // 그 밖의 경우 24 | res.writeHead(404, ctype) 25 | res.end('404 not found') 26 | } 27 | 28 | // 인덱스 페이지에 접근했을 경우 --- (※3) 29 | function showIndexPage (req, res) { 30 | // HTTP 헤더를 출력합니다. 31 | res.writeHead(200, ctype) 32 | // 응답 본문을 출력합니다. 33 | const html = '

주사위 페이지 안내

\n' + 34 | '

6면체 주사위

' + 35 | '

12면체 주사위

' 36 | res.end(html) 37 | } 38 | 39 | // 주사위 페이지에 접근했을 때 --- (※4) 40 | function showDicePage (req, res) { 41 | // HTTP 헤더를 출력합니다. 42 | res.writeHead(200, ctype) 43 | // 몇 면체 주사위가 필요한지 확인합니다. 44 | const a = req.url.split('/') 45 | const num = parseInt(a[2]) 46 | // 임의 숫자를 생성합니다. 47 | const rnd = Math.floor(Math.random() * num) + 1 48 | // 응답 본문을 출력합니다. 49 | res.end('

' + rnd + '

') 50 | } -------------------------------------------------------------------------------- /ch1/es2015io/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch1/es2015io/.DS_Store -------------------------------------------------------------------------------- /ch1/es2015io/.babelrc: -------------------------------------------------------------------------------- 1 | { "presets": ["es2015"] } 2 | 3 | -------------------------------------------------------------------------------- /ch1/es2015io/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "es2015io", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "babel-cli": "^6.23.0", 14 | "babel-preset-es2015": "^6.22.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ch1/es2015io/src/calctest.js: -------------------------------------------------------------------------------- 1 | // 외부에 공개할 함수를 정의합니다. 2 | export function add (a, b) { 3 | return a + b 4 | } 5 | export function mul (a, b) { 6 | return a * b 7 | } -------------------------------------------------------------------------------- /ch1/es2015io/src/main.js: -------------------------------------------------------------------------------- 1 | // "calctest.js" 모듈을 읽어 들입니다. 2 | import {add, mul} from './calctest.js' 3 | // 읽어 들인 함수를 사용합니다. 4 | console.log(add(2, 3)) 5 | console.log(mul(6, 8)) -------------------------------------------------------------------------------- /ch1/es2015io/src/main2.js: -------------------------------------------------------------------------------- 1 | // 모듈을 읽어 들입니다. 2 | import * as ct from './calctest.js' 3 | // 모듈의 함수를 사용합니다. 4 | console.log(ct.add(2, 3)) 5 | console.log(ct.mul(6, 8)) -------------------------------------------------------------------------------- /ch1/es2015io/src/main3.js: -------------------------------------------------------------------------------- 1 | // 모듈을 읽어 들입니다. 2 | import {add as addF, mul as mulF} from './calctest.js' 3 | // 모듈의 함수를 사용합니다. 4 | console.log(addF(2, 3)) 5 | console.log(mulF(6, 8)) -------------------------------------------------------------------------------- /ch1/es2015io2/.babelrc: -------------------------------------------------------------------------------- 1 | { "presets": ["es2015"] } 2 | 3 | -------------------------------------------------------------------------------- /ch1/es2015io2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "es2015io2", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "babel-cli": "^6.24.1", 14 | "babel-preset-es2015": "^6.24.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ch1/es2015io2/src/gobsem.js: -------------------------------------------------------------------------------- 1 | // 함수를 정의합니다. 2 | function gobsem(a, b) { 3 | return a * b; 4 | } 5 | // gobsem을 기본적으로 공개합니다. 6 | export default gobsem; -------------------------------------------------------------------------------- /ch1/es2015io2/src/main.js: -------------------------------------------------------------------------------- 1 | // gobsem 모듈을 읽어 들입니다. 2 | import gobsem from './gobsem'; 3 | // gobsem 함수를 사용합니다. 4 | const v = gobsem(2, 3); 5 | console.log(v); -------------------------------------------------------------------------------- /ch1/fib.js: -------------------------------------------------------------------------------- 1 | // 피보나치 수를 계산하는 제너레이터 함수를 정의합니다. 2 | function * genFibonacci () { 3 | let a = 0 4 | let b = 1 5 | while (true) { 6 | [a, b] = [b, a + b] 7 | yield a 8 | } 9 | } 10 | 11 | // 제너레이터 객체를 추출합니다. 12 | const fib = genFibonacci() 13 | for (const num of fib) { 14 | // 무한 반복하게 되므로 50을 넘으면 탈출하게 합니다. 15 | if (num > 50) break 16 | console.log(num) 17 | } -------------------------------------------------------------------------------- /ch1/generator-test.js: -------------------------------------------------------------------------------- 1 | // 제너레이터 함수를 정의합니다. --- (※1) 2 | function * counter () { 3 | yield 1 4 | yield 2 5 | yield 3 6 | } 7 | // 제너레이터 객체를 생성합니다. --- (※2) 8 | const g = counter() 9 | // next() 메서드를 호출합니다. --- (※3) 10 | console.log(g.next()) 11 | console.log(g.next()) 12 | console.log(g.next()) 13 | console.log(g.next()) -------------------------------------------------------------------------------- /ch1/hello-server.js: -------------------------------------------------------------------------------- 1 | // http 모듈을 읽어 들입니다. 2 | const http = require('http') 3 | 4 | // 웹 서버를 실행합니다. --- (※1) 5 | const svr = http.createServer(handler) // 서버를 생성합니다. 6 | svr.listen(8081) // 8081번 포트를 사용합니다. 7 | 8 | // 서버에 접근이 있을 때의 처리 --- (※2) 9 | function handler (req, res) { 10 | console.log('url:', req.url) 11 | console.log('method:', req.method) 12 | // HTTP 헤더를 출력합니다. 13 | res.writeHead(200, {'Content-Type': 'text/html'}) 14 | // 응답 본문을 출력합니다. 15 | res.end('

Hello, World!

\n') 16 | } -------------------------------------------------------------------------------- /ch1/project_a/index.js: -------------------------------------------------------------------------------- 1 | const colors = require('colors'); 2 | 3 | const s1 = "A time to throw stones away"; 4 | const s2 = "and a time to gather stones together"; 5 | const s3 = "A time to search and a time to give up as lost"; 6 | const s4 = "A time to keep and a time to throw away"; 7 | 8 | console.log(s1.underline.red); 9 | console.log(s2.inverse.blue); 10 | console.log(s3.rainbow); 11 | console.log(s4.inverse.red); 12 | 13 | -------------------------------------------------------------------------------- /ch1/project_a/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project_a", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "start": "node index.js", 6 | "check": "node -v" 7 | }, 8 | "dependencies": { 9 | "colors": "^1.1.2" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ch1/readfile-async.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | // 프로미스로 비동기적으로 파일을 읽어 들이는 함수를 정의합니다. 3 | function readFileEx (fname) { 4 | return new Promise((resolve, reject) => { 5 | fs.readFile(fname, 'utf-8', (err, data) => { 6 | resolve(data) 7 | }) 8 | }) 9 | } 10 | // 모든 파일을 읽어 들이는 async 함수를 정의합니다. 11 | async function readAll () { 12 | const a = await readFileEx('a.txt') 13 | console.log(a) 14 | const b = await readFileEx('b.txt') 15 | console.log(b) 16 | const c = await readFileEx('c.txt') 17 | console.log(c) 18 | } 19 | readAll() -------------------------------------------------------------------------------- /ch1/readfile-cb.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | // 파일을 읽어 들입니다. 3 | fs.readFile('test.txt', 'utf-8', function (err, data) { 4 | // 읽어 들이기를 완료했을 때의 처리 5 | console.log(data) 6 | }) -------------------------------------------------------------------------------- /ch1/readfile-cb2.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | fs.readFile('test.txt', 'utf-8', (err, data) => { 4 | console.log(data) 5 | }) -------------------------------------------------------------------------------- /ch1/readfile-cb3.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | // (1) a.txt를 읽어 들입니다. 3 | fs.readFile('a.txt', 'utf-8', function (err, data) { 4 | console.log('a.txt를 읽어 들였습니다.', data) 5 | // (2) b.txt를 읽어 들입니다. 6 | fs.readFile('b.txt', 'utf-8', function (err, data) { 7 | console.log('b.txt를 읽어 들였습니다.', data) 8 | // (3) c.txt를 읽어 들입니다. 9 | fs.readFile('c.txt', 'utf-8', function (err, data) { 10 | console.log('c.txt를 읽어 들였습니다.', data) 11 | }) 12 | }) 13 | }) -------------------------------------------------------------------------------- /ch1/readfile-generator.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | // 비동기 처리 완료를 기다리고, 다음 함수를 연속해서 호출하는 함수 3 | function read_gfn (g, fname) { 4 | fs.readFile(fname, 'utf-8', (err, data) => { 5 | g.next(data) 6 | }) 7 | } 8 | // 제너레이터 함수를 정의합니다. 9 | const g = (function * () { 10 | const a = yield read_gfn(g, 'a.txt') 11 | console.log(a) 12 | const b = yield read_gfn(g, 'b.txt') 13 | console.log(b) 14 | const c = yield read_gfn(g, 'c.txt') 15 | console.log(c) 16 | })() -------------------------------------------------------------------------------- /ch1/readfile-promise.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | // Promise를 반환하는 함수를 정의합니다. --- (※1) 3 | function readFile_pr (fname) { 4 | return new Promise((resolve) => { 5 | fs.readFile(fname, 'utf-8', (err, s) => { 6 | resolve(s) 7 | }) 8 | }) 9 | } 10 | // 차례대로 텍스트 파일을 읽어 들입니다. --- (※2) 11 | readFile_pr('a.txt') 12 | .then((text) => { 13 | console.log('a.txt를 읽어 들였습니다.', text) 14 | return readFile_pr('b.txt') 15 | }) 16 | .then((text) => { 17 | console.log('b.txt를 읽어 들였습니다.', text) 18 | return readFile_pr('c.txt') 19 | }) 20 | .then((text) => { 21 | console.log('c.txt를 읽어 들였습니다.', text) 22 | }) -------------------------------------------------------------------------------- /ch1/readfile-sync.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | // --- 동기적으로 파일을 읽어 들입니다. --- (※1) 4 | const data = fs.readFileSync('test.txt', 'utf-8') 5 | console.log(data) 6 | 7 | // --- 비동기적으로 파일을 읽어 들입니다. --- (※2) 8 | fs.readFile('test.txt', 'utf-8', readHandler) 9 | // 읽어 들이기를 완료했을 때의 처리 10 | function readHandler (err, data) { 11 | console.log(data) 12 | } -------------------------------------------------------------------------------- /ch1/readfile.js: -------------------------------------------------------------------------------- 1 | // 파일을 비동기로 읽는 프로그램 2 | const fs = require('fs') // fs 모듈을 사용합니다. 3 | 4 | // 파일을 읽어 들입니다. 5 | fs.readFile('test.txt', 'utf-8', testLoaded) 6 | 7 | // 읽기를 완료했을 때 실행할 함수 8 | function testLoaded (err, data) { 9 | if (err) { 10 | console.log('읽기 실패') 11 | return 12 | } 13 | console.log(data) 14 | } -------------------------------------------------------------------------------- /ch1/request-downloadfile.js: -------------------------------------------------------------------------------- 1 | // 모듈을 읽어 들입니다. 2 | const fs = require('fs') 3 | const request = require('request') 4 | 5 | // request 모듈을 사용해 파일을 다운로드합니다. 6 | request('http://uta.pw/shodou/img/28/214.png').pipe(fs.createWriteStream('test.png')) -------------------------------------------------------------------------------- /ch1/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch1/test.png -------------------------------------------------------------------------------- /ch1/test.txt: -------------------------------------------------------------------------------- 1 | 테스트 파일입니다 2 | 파일을 제대로 읽어 들이는지 확인합니다. -------------------------------------------------------------------------------- /ch2/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch2/.DS_Store -------------------------------------------------------------------------------- /ch2/cp-arrow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 36 | -------------------------------------------------------------------------------- /ch2/cp-class.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 24 | -------------------------------------------------------------------------------- /ch2/cp-greeting.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 27 | 28 | -------------------------------------------------------------------------------- /ch2/cp-imagetext.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 33 | -------------------------------------------------------------------------------- /ch2/cp-imagetext2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 40 | -------------------------------------------------------------------------------- /ch2/cp-stopwatch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 44 | 45 | -------------------------------------------------------------------------------- /ch2/greeting.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 26 | -------------------------------------------------------------------------------- /ch2/hello-react.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 17 | -------------------------------------------------------------------------------- /ch2/hello/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /ch2/hello/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^15.5.4", 7 | "react-dom": "^15.5.4" 8 | }, 9 | "devDependencies": { 10 | "react-scripts": "0.9.5", 11 | "standard": "^10.0.2" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test --env=jsdom", 17 | "eject": "react-scripts eject" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ch2/hello/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch2/hello/public/favicon.ico -------------------------------------------------------------------------------- /ch2/hello/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | React App 17 | 18 | 19 |
20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ch2/hello/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-intro { 18 | font-size: large; 19 | } 20 | 21 | @keyframes App-logo-spin { 22 | from { transform: rotate(0deg); } 23 | to { transform: rotate(360deg); } 24 | } 25 | -------------------------------------------------------------------------------- /ch2/hello/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import './App.css' 3 | 4 | class App extends Component { 5 | render () { 6 | return
7 |

달리는 기차 위에 중립은 없다

8 |
9 | } 10 | } 11 | 12 | export default App 13 | -------------------------------------------------------------------------------- /ch2/hello/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div') 7 | ReactDOM.render(, div) 8 | }) 9 | -------------------------------------------------------------------------------- /ch2/hello/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /ch2/hello/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /ch2/img/pic1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch2/img/pic1.jpeg -------------------------------------------------------------------------------- /ch2/img/pic2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch2/img/pic2.jpeg -------------------------------------------------------------------------------- /ch2/img/pic3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch2/img/pic3.jpeg -------------------------------------------------------------------------------- /ch2/jsx-conv/a.js: -------------------------------------------------------------------------------- 1 | const e = ( 2 |

3 | Hello, World! 4 |

5 | ) 6 | -------------------------------------------------------------------------------- /ch2/jsx-conv/b.js: -------------------------------------------------------------------------------- 1 | const e = ( 2 |

3 |

Test

4 |

5 | ) 6 | -------------------------------------------------------------------------------- /ch2/jsx-conv/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsx-conv", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "babel": { 13 | "presets": [ 14 | "react", 15 | "es2015" 16 | ] 17 | }, 18 | "devDependencies": { 19 | "babel": "^6.23.0", 20 | "babel-cli": "^6.24.0", 21 | "babel-preset-es2015": "^6.24.0", 22 | "babel-preset-react": "^6.23.0", 23 | "standard": "^10.0.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ch2/jsx-embed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 17 | -------------------------------------------------------------------------------- /ch2/jsx-embed2-error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 21 | 22 | -------------------------------------------------------------------------------- /ch2/jsx-embed2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 21 | -------------------------------------------------------------------------------- /ch2/jsx-error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 22 | 23 | -------------------------------------------------------------------------------- /ch2/jsx-escape.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 14 | -------------------------------------------------------------------------------- /ch2/jsx-style.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 36 | -------------------------------------------------------------------------------- /ch2/jsx-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 24 | -------------------------------------------------------------------------------- /ch2/list-items.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 37 | -------------------------------------------------------------------------------- /ch2/list-items2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 42 | 43 | -------------------------------------------------------------------------------- /ch2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ch2", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "temp.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "standard": "^10.0.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ch2/react-binclock.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 46 | -------------------------------------------------------------------------------- /ch2/react-clock.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 24 | -------------------------------------------------------------------------------- /ch2/react-clock2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 29 | -------------------------------------------------------------------------------- /ch2/react-stopwatch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 |
11 | 40 | 41 | -------------------------------------------------------------------------------- /ch2/st-checkbox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 49 | -------------------------------------------------------------------------------- /ch2/st-click.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 27 | -------------------------------------------------------------------------------- /ch2/st-click2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 32 | -------------------------------------------------------------------------------- /ch2/st-clock.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 39 | -------------------------------------------------------------------------------- /ch2/state-tf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch2/state-tf.png -------------------------------------------------------------------------------- /ch2/temp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /ch2/temp.js: -------------------------------------------------------------------------------- 1 | // 取り込まれるJavaScript 2 | ReactDOM.render( 3 |

Hello, World!

, 4 | document.getElementById("root") 5 | ) 6 | -------------------------------------------------------------------------------- /ch2/test-webpack/calc.js: -------------------------------------------------------------------------------- 1 | // calc.js 2 | export function mul (a, b) { 3 | return a * b 4 | } 5 | -------------------------------------------------------------------------------- /ch2/test-webpack/main.js: -------------------------------------------------------------------------------- 1 | import {mul} from './calc' 2 | const a = mul(3, 5) 3 | console.log(a) 4 | -------------------------------------------------------------------------------- /ch2/test-webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './main.js', 3 | output: { 4 | filename: 'out/test.js' 5 | } 6 | }; 7 | 8 | -------------------------------------------------------------------------------- /ch2/test-webpack2/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ch2/test-webpack2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-webpack2", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "babel-core": "^6.24.1", 14 | "babel-loader": "^6.4.1", 15 | "babel-preset-es2015": "^6.24.1", 16 | "babel-preset-react": "^6.24.1", 17 | "react": "^15.5.4", 18 | "react-dom": "^15.5.4", 19 | "webpack": "^2.3.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ch2/test-webpack2/src/Hello.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | export class Hello extends React.Component { 3 | render () { 4 | return

Hello!

5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ch2/test-webpack2/src/main.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import {Hello} from './Hello' 4 | 5 | ReactDOM.render( 6 | , 7 | document.getElementById('root')) 8 | -------------------------------------------------------------------------------- /ch2/test-webpack2/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './src/main.js', 3 | output: { 4 | filename: './out/bundle.js' 5 | }, 6 | module: { 7 | rules: [ 8 | { 9 | test: /.js$/, 10 | loader: 'babel-loader', 11 | options: { 12 | presets:['es2015', 'react'] 13 | } 14 | } 15 | ] 16 | } 17 | }; 18 | 19 | 20 | -------------------------------------------------------------------------------- /ch2/tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

Hello

5 |
6 |
    7 |
  • xxx
  • 8 |
  • xxx
  • 9 |
  • xxx
  • 10 |
11 |
12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /ch3/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch3/.DS_Store -------------------------------------------------------------------------------- /ch3/cycle/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /ch3/cycle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cycle", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^15.5.4", 7 | "react-dom": "^15.5.4" 8 | }, 9 | "devDependencies": { 10 | "react-scripts": "0.9.5", 11 | "standard": "^10.0.2" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test --env=jsdom", 17 | "eject": "react-scripts eject" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ch3/cycle/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch3/cycle/public/favicon.ico -------------------------------------------------------------------------------- /ch3/cycle/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | React App 17 | 18 | 19 |
20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ch3/cycle/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-intro { 18 | font-size: large; 19 | } 20 | 21 | @keyframes App-logo-spin { 22 | from { transform: rotate(0deg); } 23 | to { transform: rotate(360deg); } 24 | } 25 | -------------------------------------------------------------------------------- /ch3/cycle/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | class App extends Component { 3 | // 마운트 4 | constructor (props) { 5 | super(props) 6 | console.log('constructor') 7 | } 8 | componentWillMount () { 9 | console.log('componentWillMount') 10 | } 11 | componentDidMount () { 12 | console.log('componentDidMount') 13 | } 14 | // 변경 15 | componentWillReceiveProps (nextProps) { 16 | console.log('componentWillReceiveProps') 17 | } 18 | shouldComponentUpdate (nextProps, nextState) { 19 | console.log('shouldComponentUpdate') 20 | return true 21 | } 22 | componentWillUpdate () { 23 | console.log('componentWillUpdate') 24 | } 25 | componentDidUpdate () { 26 | console.log('componentDidUpdate') 27 | } 28 | // 언마운트 29 | componentWillUnmount () { 30 | console.log('componentWillUnmount') 31 | } 32 | render () { 33 | console.log('render') 34 | const setStateHandler = (e) => { 35 | console.log('* call setState()') 36 | this.setState({r: Math.random()}) 37 | } 38 | return ( 39 |
40 | 42 |
43 | ) 44 | } 45 | } 46 | export default App -------------------------------------------------------------------------------- /ch3/cycle/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div') 7 | ReactDOM.render(, div) 8 | }) 9 | -------------------------------------------------------------------------------- /ch3/cycle/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /ch3/cycle/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /ch3/form_multi/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch3/form_multi/.DS_Store -------------------------------------------------------------------------------- /ch3/form_multi/build.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const basedir = __dirname 3 | 4 | module.exports = { 5 | entry: path.join(basedir, 'src/index.js'), 6 | output: { 7 | path: path.join(basedir, 'out'), 8 | filename: 'bundle.js' 9 | }, 10 | devtool: 'inline-source-map', 11 | module: { 12 | rules: [ 13 | { 14 | test: /.js$/, 15 | loader: 'babel-loader', 16 | options: { 17 | presets: ['es2015', 'react'] 18 | } 19 | }, 20 | { 21 | test: /.css$/, 22 | use: [ 23 | { 24 | loader: 'style-loader' 25 | }, 26 | { 27 | loader:'css-loader', 28 | options: { 29 | modules: true 30 | } 31 | } 32 | ] 33 | } 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ch3/form_multi/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | config=$(cd $(dirname $0) &&pwd)/build.config.js 3 | cd ../.. && webpack --config $config $* 4 | 5 | -------------------------------------------------------------------------------- /ch3/form_multi/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ch3/form_multi/src/MultiForm.css: -------------------------------------------------------------------------------- 1 | .multiForm { 2 | width: 400px; 3 | text-align: left; 4 | margin-left: auto; 5 | } 6 | 7 | .multiForm p { 8 | margin: 4px; 9 | padding: 4px; 10 | } 11 | -------------------------------------------------------------------------------- /ch3/form_multi/src/MultiForm.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | // 여러 개의 입력 항목을 가진 컴포넌트 3 | export default class MultiForm extends Component { 4 | constructor (props) { 5 | super(props) 6 | // 입력 양식의 초깃값을 설정합니다. --- (※1) 7 | this.state = { 8 | name: '윤인성', 9 | age: 25, 10 | hobby: '독서' 11 | } 12 | } 13 | // 값이 변경됐을 때 --- (※2) 14 | doChange (e) { 15 | const userValue = e.target.value 16 | const key = e.target.name 17 | this.setState({[key]: userValue}) 18 | } 19 | // 전송 버튼을 눌렀을 때 20 | doSubmit (e) { 21 | e.preventDefault() 22 | const j = JSON.stringify(this.state) 23 | window.alert(j) 24 | } 25 | // 화면 렌더링 --- (※3) 26 | render () { 27 | // 이벤트를 메서드에 바인딩합니다. 28 | const doSubmit = (e) => this.doSubmit(e) 29 | const doChange = (e) => this.doChange(e) 30 | return ( 31 |
32 |
39 |
46 |
53 | 54 |
55 | ) 56 | } 57 | } -------------------------------------------------------------------------------- /ch3/form_multi/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import MultiForm from './MultiForm' 4 | const st = { 5 | textAlign: 'left', 6 | padding: '10px' 7 | } 8 | ReactDOM.render( 9 |
10 | 11 |
, 12 | document.getElementById('root') 13 | ) 14 | -------------------------------------------------------------------------------- /ch3/form_number/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch3/form_number/.DS_Store -------------------------------------------------------------------------------- /ch3/form_number/build.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const basedir = __dirname 3 | 4 | module.exports = { 5 | entry: path.join(basedir, 'src/index.js'), 6 | output: { 7 | path: path.join(basedir, 'out'), 8 | filename: 'bundle.js' 9 | }, 10 | devtool: 'inline-source-map', 11 | module: { 12 | rules: [ 13 | { 14 | test: /.js$/, 15 | loader: 'babel-loader', 16 | options: { 17 | presets: ['es2015', 'react'] 18 | } 19 | } 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ch3/form_number/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | config=$(cd $(dirname $0) &&pwd)/build.config.js 3 | cd ../.. && webpack --config $config $* 4 | 5 | -------------------------------------------------------------------------------- /ch3/form_number/number_form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ch3/form_number/src/NumberForm.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | // 숫자 입력 컴포넌트 3 | export default class NumberForm extends Component { 4 | constructor (props) { 5 | super(props) 6 | this.state = { value: '' } 7 | } 8 | // 값이 변경됐을 때 --- (※1) 9 | doChange (e) { 10 | const curValue = e.target.value 11 | // 숫자 이외의 값을 제거합니다. 12 | const newValue = curValue.replace(/[^0-9]/g, '') 13 | this.setState({value: newValue}) 14 | } 15 | // 전송 버튼을 눌렀을 때 16 | doSubmit (e) { 17 | window.alert('전송: ' + this.state.value) 18 | e.preventDefault() 19 | } 20 | // 화면 렌더링 --- (※4) 21 | render () { 22 | // 이벤트를 메서드에 바인딩합니다. 23 | const doSubmit = (e) => this.doSubmit(e) 24 | const doChange = (e) => this.doChange(e) 25 | return ( 26 |
27 | 30 | 31 |
32 | ) 33 | } 34 | } -------------------------------------------------------------------------------- /ch3/form_number/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import NumberForm from './NumberForm' 4 | 5 | const st = {textAlign: 'center'} 6 | ReactDOM.render( 7 |
8 | 9 |
, 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /ch3/form_simple/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /ch3/form_simple/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "form1", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^15.5.4", 7 | "react-dom": "^15.5.4" 8 | }, 9 | "devDependencies": { 10 | "react-scripts": "0.9.5", 11 | "standard": "^10.0.2" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test --env=jsdom", 17 | "eject": "react-scripts eject" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ch3/form_simple/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch3/form_simple/public/favicon.ico -------------------------------------------------------------------------------- /ch3/form_simple/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | React App 17 | 18 | 19 |
20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ch3/form_simple/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | font-size: 2em; 4 | padding: 4px; 5 | margin: 4px; 6 | } 7 | 8 | .App-logo { 9 | animation: App-logo-spin infinite 20s linear; 10 | height: 80px; 11 | } 12 | 13 | .App-header { 14 | background-color: #222; 15 | height: 150px; 16 | padding: 20px; 17 | color: white; 18 | } 19 | 20 | .App-intro { 21 | font-size: large; 22 | } 23 | 24 | @keyframes App-logo-spin { 25 | from { transform: rotate(0deg); } 26 | to { transform: rotate(360deg); } 27 | } 28 | -------------------------------------------------------------------------------- /ch3/form_simple/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { SimpleForm } from './SimpleForm' 3 | import './App.css' 4 | // 메인 화면 컴포넌트입니다. 5 | export default class App extends Component { 6 | render () { 7 | return ( 8 |
9 | 10 |
11 | ) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ch3/form_simple/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /ch3/form_simple/src/SimpleForm.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | // 입력 양식 컴포넌트입니다. 3 | export class SimpleForm extends React.Component { 4 | constructor (props) { 5 | super(props) 6 | // 상태를 초기화합니다. --- (※1) 7 | this.state = { value: '' } 8 | } 9 | // 값이 변경됐을 때 --- (※2) 10 | doChange (e) { 11 | const newValue = e.target.value 12 | this.setState({value: newValue}) 13 | } 14 | // 전송 버튼을 눌렀을 때 --- (※3) 15 | doSubmit (e) { 16 | window.alert('전송: ' + this.state.value) 17 | e.preventDefault() 18 | } 19 | // 화면 렌더링 --- (※4) 20 | render () { 21 | // 이벤트를 메서드에 바인딩합니다. 22 | const doSubmit = (e) => this.doSubmit(e) 23 | const doChange = (e) => this.doChange(e) 24 | return ( 25 |
26 | 29 | 30 |
31 | ) 32 | } 33 | } -------------------------------------------------------------------------------- /ch3/form_simple/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /ch3/form_simple/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /ch3/inch_to_cm/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch3/inch_to_cm/.DS_Store -------------------------------------------------------------------------------- /ch3/inch_to_cm/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | config=$(cd $(dirname $0) &&pwd)/webpack.config.js 3 | cd ../.. && webpack --config $config $* 4 | 5 | -------------------------------------------------------------------------------- /ch3/inch_to_cm/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ch3/inch_to_cm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "src", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "dependencies": { 7 | "react-scripts": "^0.9.5", 8 | "standard": "^10.0.2", 9 | "superagent": "^3.5.2" 10 | }, 11 | "devDependencies": { 12 | "babel-core": "^6.24.1", 13 | "babel-loader": "^6.4.1", 14 | "babel-preset-es2015": "^6.24.1", 15 | "babel-preset-react": "^6.24.1", 16 | "css-loader": "^0.28.0", 17 | "react": "^15.5.4", 18 | "react-dom": "^15.5.4", 19 | "standard": "^10.0.2", 20 | "style-loader": "^0.16.1", 21 | "webpack": "^2.3.3" 22 | }, 23 | "scripts": { 24 | "test": "echo \"Error: no test specified\" && exit 1" 25 | }, 26 | "keywords": [], 27 | "author": "", 28 | "license": "ISC" 29 | } 30 | 31 | -------------------------------------------------------------------------------- /ch3/inch_to_cm/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch3/inch_to_cm/src/.DS_Store -------------------------------------------------------------------------------- /ch3/inch_to_cm/src/InchToCm.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import ValueInput from './ValueInput' 3 | // inch와 cm 변환 컴포넌트 4 | export default class InchToCm extends Component { 5 | constructor (props) { 6 | super(props) 7 | // ValueInput에 출력할 값을 상태로 저장합니다. --- (※1) 8 | this.state = { 9 | inch: 0, cm: 0 10 | } 11 | } 12 | // inch가 변경됐을 때 --- (※2) 13 | inchChanged (e) { 14 | const inchValue = e.value 15 | const cmValue = inchValue * 2.54 16 | this.setState({ 17 | inch: inchValue, 18 | cm: cmValue 19 | }) 20 | } 21 | // cm가 변경됐을 때 --- (※3) 22 | cmChanged (e) { 23 | const cmValue = e.value 24 | const inchValue = cmValue / 2.54 25 | this.setState({ 26 | inch: inchValue, 27 | cm: cmValue 28 | }) 29 | } 30 | // 화면 렌더링 --- (※4) 31 | render () { 32 | return ( 33 |
34 | this.inchChanged(e)} 36 | value={this.state.inch} /> 37 | this.cmChanged(e)} 39 | value={this.state.cm} /> 40 |
41 | ) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ch3/inch_to_cm/src/ValueInput.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | // 숫자 입력 컴포넌트 3 | export default class ValueInput extends Component { 4 | constructor (props) { 5 | super(props) 6 | // 프로퍼티로 초깃값을 설정합니다. --- (※5) 7 | this.state = { 8 | value: this.props.value 9 | } 10 | } 11 | // 값이 사용자에 의해 변경됐을 때 --- (※6) 12 | handleChange (e) { 13 | const v = e.target.value 14 | // 숫자 이외의 값을 제거합니다. 15 | const newValue = v.replace(/[^0-9.]+/g, '') 16 | // 상태에 설정합니다. --- (※7) 17 | this.setState({value: newValue}) 18 | // 이벤트를 실행합니다. --- (※8) 19 | if (this.props.onChange) { 20 | this.props.onChange({ 21 | target: this, 22 | value: newValue 23 | }) 24 | } 25 | } 26 | // 프로퍼티가 변경됐을 때 --- (※9) 27 | componentWillReceiveProps (nextProps) { 28 | this.setState({value: nextProps.value}) 29 | } 30 | // 렌더링 --- (※10) 31 | render () { 32 | return (
33 | 38 |
) 39 | } 40 | } -------------------------------------------------------------------------------- /ch3/inch_to_cm/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import InchToCm from './InchToCm' 4 | 5 | ReactDOM.render( 6 |
, 7 | document.getElementById('root') 8 | ) 9 | -------------------------------------------------------------------------------- /ch3/inch_to_cm/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const basedir = __dirname 3 | 4 | module.exports = { 5 | entry: path.join(basedir, 'src/index.js'), 6 | output: { 7 | path: path.join(basedir, 'out'), 8 | filename: 'bundle.js' 9 | }, 10 | devtool: 'inline-source-map', 11 | module: { 12 | rules: [ 13 | { 14 | test: /.js$/, 15 | loader: 'babel-loader', 16 | options: { 17 | presets: ['es2015', 'react'] 18 | } 19 | } 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ch3/input_compo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch3/input_compo/.DS_Store -------------------------------------------------------------------------------- /ch3/input_compo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ch3/input_compo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "src", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.html", 6 | "scripts": { 7 | "build": "webpack", 8 | "start": "serve -p 3000" 9 | }, 10 | "dependencies": { 11 | "react-scripts": "^0.9.5", 12 | "serve": "^5.2.2", 13 | "standard": "^10.0.2", 14 | "superagent": "^3.5.2" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^6.24.1", 18 | "babel-loader": "^6.4.1", 19 | "babel-preset-es2015": "^6.24.1", 20 | "babel-preset-react": "^6.24.1", 21 | "css-loader": "^0.28.0", 22 | "react": "^15.5.4", 23 | "react-dom": "^15.5.4", 24 | "standard": "^10.0.2", 25 | "style-loader": "^0.16.1", 26 | "webpack": "^2.3.3" 27 | }, 28 | "keywords": [], 29 | "author": "", 30 | "license": "ISC" 31 | } 32 | -------------------------------------------------------------------------------- /ch3/input_compo/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch3/input_compo/src/.DS_Store -------------------------------------------------------------------------------- /ch3/input_compo/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import FormInput from './FormInput' 4 | class CustomForm extends React.Component { 5 | constructor (props) { 6 | super(props) 7 | this.state = { 8 | email: '', 9 | tel: '', 10 | allok: false 11 | } 12 | this.oks = {} 13 | } 14 | handleChange (e) { 15 | // 모든 항목이 OK인지 확인합니다. 16 | this.oks[e.name] = e.isOK 17 | this.setState({ 18 | [e.name]: e.value, 19 | allok: (this.oks['email'] && this.oks['tel']) 20 | }) 21 | } 22 | handleSubmit (e) { 23 | window.alert(JSON.stringify(this.state)) 24 | e.preventDefault() 25 | } 26 | render () { 27 | const doChange = e => this.handleChange(e) 28 | const doSubmit = e => this.handleSubmit(e) 29 | // 이메일 패턴 30 | const emailPat = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/ 31 | // ASCII 문자 이외 전부 32 | const asciiFilter = /[^\u0020-\u007e]+/g 33 | return ( 34 |
35 | 40 | 45 | 47 | 48 | ) 49 | } 50 | } 51 | // DOM을 변경합니다. 52 | ReactDOM.render( 53 | , 54 | document.getElementById('root') 55 | ) -------------------------------------------------------------------------------- /ch3/input_compo/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const basedir = __dirname 3 | 4 | module.exports = { 5 | entry: path.join(basedir, 'src/index.js'), 6 | output: { 7 | path: path.join(basedir, 'out'), 8 | filename: 'bundle.js' 9 | }, 10 | devtool: 'inline-source-map', 11 | module: { 12 | rules: [ 13 | { 14 | test: /.js$/, 15 | loader: 'babel-loader', 16 | options: { 17 | presets: ['es2015', 'react'] 18 | } 19 | } 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ch3/input_zip/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch3/input_zip/.DS_Store -------------------------------------------------------------------------------- /ch3/input_zip/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | config=$(cd $(dirname $0) &&pwd)/webpack.config.js 3 | cd ../.. && webpack --config $config $* 4 | 5 | -------------------------------------------------------------------------------- /ch3/input_zip/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ch3/input_zip/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "src", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "dependencies": { 7 | "react-scripts": "^0.9.5", 8 | "standard": "^10.0.2", 9 | "superagent": "^3.5.2" 10 | }, 11 | "devDependencies": { 12 | "babel-core": "^6.24.1", 13 | "babel-loader": "^6.4.1", 14 | "babel-preset-es2015": "^6.24.1", 15 | "babel-preset-react": "^6.24.1", 16 | "css-loader": "^0.28.0", 17 | "react": "^15.5.4", 18 | "react-dom": "^15.5.4", 19 | "standard": "^10.0.2", 20 | "style-loader": "^0.16.1", 21 | "webpack": "^2.3.3" 22 | }, 23 | "scripts": { 24 | "test": "echo \"Error: no test specified\" && exit 1" 25 | }, 26 | "keywords": [], 27 | "author": "", 28 | "license": "ISC" 29 | } 30 | -------------------------------------------------------------------------------- /ch3/input_zip/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch3/input_zip/src/.DS_Store -------------------------------------------------------------------------------- /ch3/input_zip/src/ZipInput.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | // 우편 번호 입력 컴포넌트 3 | export default class ZipInput extends Component { 4 | constructor (props) { 5 | super(props) 6 | const v = (this.props.value) 7 | ? this.props.value : '' 8 | // 상태를 초기화합니다. --- (※1) 9 | this.state = { 10 | value: v, 11 | isOK: this.checkValue(v) 12 | } 13 | } 14 | // 패턴에 맞는지 확인하기--- (※2) 15 | checkValue (s) { 16 | const zipPattern = /^\d{5}$/ 17 | return zipPattern.test(s) 18 | } 19 | // 값이 사용자에 의해 변경됐을 때 --- (※3) 20 | handleChange (e) { 21 | const v = e.target.value 22 | // 숫자 이외의 값을 제거합니다. 23 | const newValue = v.replace(/[^0-9]+/g, '') 24 | const newIsOK = this.checkValue(newValue) 25 | // 상태에 설정합니다. 26 | this.setState({ 27 | value: newValue, 28 | isOK: newIsOK 29 | }) 30 | // 이벤트를 실행합니다. --- (※4) 31 | if (this.props.onChange) { 32 | this.props.onChange({ 33 | target: this, 34 | value: newValue, 35 | isOK: newIsOK 36 | }) 37 | } 38 | } 39 | // 프로퍼티가 변경됐을 때 --- (※5) 40 | componentWillReceiveProps (nextProps) { 41 | this.setState({ 42 | value: nextProps.value, 43 | isOK: this.checkValue(nextProps.value) 44 | }) 45 | } 46 | // 렌더링 --- (※6) 47 | render () { 48 | const msg = this.renderStatusMessage() 49 | return (
50 | 57 |
) 58 | } 59 | // 입력이 제대로 됐는지 출력하는 메시지 --- (※7) 60 | renderStatusMessage () { 61 | // 메시지에 적용할 스타일 62 | const so = { 63 | margin: '8px', 64 | padding: '8px', 65 | color: 'white' 66 | } 67 | let msg = null 68 | if (this.state.isOK) { // OK 때 69 | so.backgroundColor = 'green' 70 | msg = OK 71 | } else { // NG 때(빈 문자열이라면 출력하지 않습니다) 72 | if (this.state.value !== '') { 73 | so.backgroundColor = 'red' 74 | msg = NG 75 | } 76 | } 77 | return msg 78 | } 79 | } -------------------------------------------------------------------------------- /ch3/input_zip/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import ZipInput from './ZipInput' 4 | ReactDOM.render( 5 |
, 6 | document.getElementById('root') 7 | ) -------------------------------------------------------------------------------- /ch3/input_zip/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const basedir = __dirname 3 | 4 | module.exports = { 5 | entry: path.join(basedir, 'src/index.js'), 6 | output: { 7 | path: path.join(basedir, 'out'), 8 | filename: 'bundle.js' 9 | }, 10 | devtool: 'inline-source-map', 11 | module: { 12 | rules: [ 13 | { 14 | test: /.js$/, 15 | loader: 'babel-loader', 16 | options: { 17 | presets: ['es2015', 'react'] 18 | } 19 | } 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ch3/parts/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch3/parts/.DS_Store -------------------------------------------------------------------------------- /ch3/parts/build.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const basedir = __dirname 3 | 4 | module.exports = { 5 | entry: { 6 | 'text': path.join(basedir, 'src/text.js'), 7 | 'cbox': path.join(basedir, 'src/cbox.js'), 8 | 'textarea': path.join(basedir, 'src/textarea.js'), 9 | 'radio': path.join(basedir, 'src/radio.js'), 10 | 'select': path.join(basedir, 'src/select.js') 11 | }, 12 | output: { 13 | path: path.join(basedir, 'out'), 14 | filename: '[name].js' 15 | }, 16 | devtool: 'inline-source-map', 17 | module: { 18 | rules: [ 19 | { 20 | test: /.js$/, 21 | loader: 'babel-loader', 22 | options: { 23 | presets: ['es2015', 'react'] 24 | } 25 | }, 26 | { 27 | test: /.css$/, 28 | use: [ 29 | { 30 | loader: 'style-loader' 31 | }, 32 | { 33 | loader:'css-loader', 34 | options: { 35 | modules: true 36 | } 37 | } 38 | ] 39 | } 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ch3/parts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | config=$(cd $(dirname $0) &&pwd)/build.config.js 3 | cd ../.. && webpack --config $config $* 4 | 5 | -------------------------------------------------------------------------------- /ch3/parts/cbox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ch3/parts/out/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch3/parts/out/.DS_Store -------------------------------------------------------------------------------- /ch3/parts/radio.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ch3/parts/select.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ch3/parts/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch3/parts/src/.DS_Store -------------------------------------------------------------------------------- /ch3/parts/src/cbox.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | class CBoxForm extends React.Component { 4 | constructor (props) { 5 | super(props) 6 | this.state = { check: true } 7 | } 8 | render () { 9 | // 입력 양식에 체크 상태를 지정합니다. 10 | return (
11 |
this.doSubmit(e)}> 12 |
18 | 19 |
20 |
) 21 | } 22 | // 체크박스를 클릭했을 때 23 | doChange (e) { 24 | this.setState({ check: !this.state.check }) 25 | } 26 | // 입력 양식을 전송했을 때 27 | doSubmit (e) { 28 | e.preventDefault() 29 | window.alert(this.state.check ? '먹기' : '먹지 않기') 30 | } 31 | } 32 | ReactDOM.render( 33 | , 34 | document.getElementById('root') 35 | ) -------------------------------------------------------------------------------- /ch3/parts/src/radio.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | class RadioForm extends React.Component { 4 | constructor (props) { 5 | super(props) 6 | this.state = { 7 | items: props.items, 8 | value: '' 9 | } 10 | } 11 | render () { 12 | // 라디오버튼을 생성합니다. 13 | const radiolist = this.state.items.map(i => { 14 | return (
15 | 21 |
) 22 | }) 23 | // 입력 양식에 라디오버튼 목록을 지정합니다. 24 | return (
25 |
this.doSubmit(e)}> 26 | {radiolist} 27 | 28 |
29 |
) 30 | } 31 | // 라디오버튼을 변경했을 때 32 | doChange (e) { 33 | this.setState({ value: e.target.value }) 34 | } 35 | // 입력 양식을 전송했을 때 36 | doSubmit (e) { 37 | e.preventDefault() 38 | window.alert(this.state.value) 39 | } 40 | } 41 | ReactDOM.render( 42 | , 43 | document.getElementById('root')) -------------------------------------------------------------------------------- /ch3/parts/src/select.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | class SelectForm extends React.Component { 4 | constructor (props) { 5 | super(props) 6 | this.state = { 7 | items: props.items, 8 | value: props.value 9 | } 10 | } 11 | render () { 12 | // 선택 박스를 생성합니다. 13 | const options = this.state.items.map(i => { 14 | return () 17 | }) 18 | // 입력 양식으로 선택 박스를 지정합니다. 19 | return (
20 |
this.doSubmit(e)}> 21 |
26 | 27 |
28 |
) 29 | } 30 | // 선택 박스를 변경했을 때 31 | doChange (e) { 32 | this.setState({ value: e.target.value }) 33 | } 34 | // 입력 양식을 전송했을 때 35 | doSubmit (e) { 36 | e.preventDefault() 37 | window.alert(this.state.value) 38 | } 39 | } 40 | ReactDOM.render( 41 | , 44 | document.getElementById('root') 45 | ) -------------------------------------------------------------------------------- /ch3/parts/src/text.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | 4 | class TextForm extends React.Component { 5 | constructor (props) { 6 | super(props) 7 | this.state = { value: '' } 8 | } 9 | render () { 10 | // フォームにテキストボックスを指定 11 | return (
12 |
this.doSubmit(e)}> 13 | this.doChange(e)} 15 | value={this.state.value} /> 16 | 17 |
18 |
) 19 | } 20 | // テキストボックスを変更したとき 21 | doChange (e) { 22 | this.setState({ value: e.target.value }) 23 | } 24 | // フォームを送信したとき 25 | doSubmit (e) { 26 | e.preventDefault() 27 | window.alert(this.state.value) 28 | } 29 | } 30 | 31 | ReactDOM.render( 32 | , 33 | document.getElementById('root') 34 | ) 35 | -------------------------------------------------------------------------------- /ch3/parts/src/textarea.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | class TextAreaForm extends React.Component { 4 | constructor (props) { 5 | super(props) 6 | this.state = { value: 'Hello' } 7 | } 8 | render () { 9 | // 입력 양식으로 텍스트에리어를 지정합니다. 10 | return (
11 |
this.doSubmit(e)}> 12 | ' + 14 | '
' + 15 | '
') 16 | }) 17 | // POST 메서드를 받습니다. 18 | app.post('/', (req, res) => { 19 | const s = JSON.stringify(req.body) 20 | res.send('POST: ' + s) 21 | }) -------------------------------------------------------------------------------- /ch5/express_test/post-test.js: -------------------------------------------------------------------------------- 1 | // 익스프레스를 실행합니다. 2 | const express = require('express') 3 | const app = express() 4 | app.listen(3000, () => { 5 | console.log('서버 실행 완료 - http://localhost:3000') 6 | }) 7 | // GET 메서드라면 입력 양식을 응답합니다. 8 | app.get('/', (req, res) => { 9 | res.send('
' + 10 | '' + 11 | '
' + 12 | '
') 13 | }) 14 | // POST 메서드를 받습니다. 15 | app.post('/', (req, res) => { 16 | res.send('POST되었습니다.') 17 | }) -------------------------------------------------------------------------------- /ch5/express_test/post-upload.js: -------------------------------------------------------------------------------- 1 | // 익스프레스를 실행합니다. 2 | const express = require('express') 3 | const app = express() 4 | // multer 사용 준비 --- (※1) 5 | const multer = require('multer') 6 | const path = require('path') 7 | // 업로드 대상 디렉터리를 지정합니다. --- (※2) 8 | const tmpDir = path.join(__dirname, 'tmp') 9 | const pubDir = path.join(__dirname, 'pub') 10 | const uploader = multer({dest: tmpDir}) 11 | // 서버를 실행합니다. 12 | app.listen(3000, () => { 13 | console.log('서버 실행 완료 - http://localhost:3000') 14 | }) 15 | // 업로드 입력 양식을 출력합니다. --- (※3) 16 | app.get('/', (req, res) => { 17 | res.send( 18 | '
' + 19 | '
' + 20 | '' + 21 | '
') 22 | }) 23 | // 정적 파일을 제공합니다. --- (※4) 24 | app.use('/pub', express.static(pubDir)) 25 | // 업로드를 받습니다. --- (※5) 26 | app.post('/', uploader.single('aFile'), (req, res) => { 27 | console.log('파일을 받았습니다.') 28 | console.log('원본 파일 이름:', req.file.originalname) 29 | console.log('저장된 경로:', req.file.path) 30 | // MIME으로 파일의 형식을 확인합니다. --- (※6) 31 | if (req.file.mimetype !== 'image/png') { 32 | res.send('PNG 이미지만 업로드할 수 있습니다.') 33 | return 34 | } 35 | // TODO: 진짜 PNG인지 확인하게 해보세요. --- (※7) 36 | // 파일을 이동합니다. --- (※8) 37 | const fname = req.file.filename + '.png' 38 | const des = pubDir + '/' + fname 39 | const fs = require('fs') 40 | fs.rename(req.file.path, des) 41 | // HTML을 출력합니다. 42 | res.send('다음과 같은 파일이 업로드 됐습니다.
' + 43 | ``) 44 | }) -------------------------------------------------------------------------------- /ch5/express_test/pub/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch5/express_test/pub/.DS_Store -------------------------------------------------------------------------------- /ch5/express_test/static.js: -------------------------------------------------------------------------------- 1 | // 익스프레스를 실행합니다. 2 | const express = require('express') 3 | const app = express() 4 | // 서버를 실행합니다. 5 | app.listen(3000, () => { 6 | console.log('서버 실행 완료 - http://localhost:3000') 7 | }) 8 | // 정적 파일을 제공합니다. 9 | app.use('/', express.static('./html')) -------------------------------------------------------------------------------- /ch5/flux_test/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch5/flux_test/.DS_Store -------------------------------------------------------------------------------- /ch5/flux_test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ch5/flux_test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flux_test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "start": "serve .", 7 | "watch": "webpack -w", 8 | "build": "webpack" 9 | }, 10 | "dependencies": { 11 | "flux": "^3.1.2", 12 | "react": "^15.5.4", 13 | "react-dom": "^15.5.4" 14 | }, 15 | "devDependencies": { 16 | "babel-core": "^6.24.1", 17 | "babel-loader": "^6.4.1", 18 | "babel-preset-es2015": "^6.24.1", 19 | "babel-preset-react": "^6.24.1", 20 | "css-loader": "^0.28.0", 21 | "serve": "^5.1.5", 22 | "style-loader": "^0.16.1", 23 | "webpack": "^2.4.1" 24 | }, 25 | "keywords": [], 26 | "author": "", 27 | "license": "ISC" 28 | } 29 | -------------------------------------------------------------------------------- /ch5/flux_test/src/actions.js: -------------------------------------------------------------------------------- 1 | import {appDispatcher} from './appDispatcher.js' 2 | // 이번에 사용할 Action 3 | export const ActionType = { 4 | CHANGE_NAME: 'CHANGE_NAME', 5 | SUBMIT_NAME: 'SUBMIT_NAME' 6 | } 7 | // Action을 생성하고 ... Dispatcher에 정보를 전달합니다. 8 | export const Actions = { 9 | changeName: (name) => { 10 | if (name === null) return 11 | appDispatcher.dispatch({ 12 | actionType: ActionType.CHANGE_NAME, 13 | value: name}) 14 | }, 15 | submitName: () => { 16 | appDispatcher.dispatch({ 17 | actionType: ActionType.SUBMIT_NAME}) 18 | } 19 | } -------------------------------------------------------------------------------- /ch5/flux_test/src/appDispatcher.js: -------------------------------------------------------------------------------- 1 | import {Dispatcher} from 'flux' 2 | // Dispatcher를 생성합니다. 3 | export const appDispatcher = new Dispatcher() -------------------------------------------------------------------------------- /ch5/flux_test/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import {Actions} from './actions.js' 4 | import {nameStore, messageStore} from './stores.js' 5 | // View를 정의합니다. 6 | class AppView extends React.Component { 7 | constructor (props) { 8 | super(props) 9 | this.state = {name: '', message: ''} 10 | // View와 Store를 연결합니다. 11 | nameStore.onChange = () => { 12 | this.setState({name: nameStore.name}) 13 | } 14 | messageStore.onChange = () => { 15 | this.setState({message: messageStore.message}) 16 | } 17 | } 18 | // View에서는 Action을 던집니다. 19 | render () { 20 | console.log('View.render') 21 | return (
22 |
23 | Actions.changeName(e.target.value)} /> 26 | 27 |
28 |
{this.state.message}
29 |
) 30 | } 31 | } 32 | // DOM의 내용을 변경합니다. 33 | ReactDOM.render( 34 | , 35 | document.getElementById('root') 36 | ) -------------------------------------------------------------------------------- /ch5/flux_test/src/stores.js: -------------------------------------------------------------------------------- 1 | import {appDispatcher} from './appDispatcher.js' 2 | import {ActionType} from './actions.js' 3 | // 이번에 사용할 Store 4 | export const nameStore = {name: '', onChange: null} 5 | export const messageStore = {message: '', onChange: null} 6 | // Action와 Store를 연결합니다. 7 | appDispatcher.register(payload => { 8 | if (payload.actionType === ActionType.CHANGE_NAME) { 9 | nameStore.name = payload.value 10 | nameStore.onChange() 11 | } 12 | }) 13 | appDispatcher.register(payload => { 14 | if (payload.actionType === ActionType.SUBMIT_NAME) { 15 | messageStore.message = nameStore.name + '님 안녕하세요.' 16 | messageStore.onChange() 17 | } 18 | }) -------------------------------------------------------------------------------- /ch5/flux_test/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const basedir = __dirname 3 | 4 | module.exports = { 5 | entry: path.join(basedir, 'src/index.js'), 6 | output: { 7 | path: path.join(basedir, 'out'), 8 | filename: 'bundle.js' 9 | }, 10 | devtool: 'inline-source-map', 11 | module: { 12 | rules: [ 13 | { 14 | test: /.js$/, 15 | loader: 'babel-loader', 16 | options: { 17 | presets: ['es2015', 'react'] 18 | } 19 | } 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ch5/nedb_test/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch5/nedb_test/.DS_Store -------------------------------------------------------------------------------- /ch5/nedb_test/a.js: -------------------------------------------------------------------------------- 1 | const Datastore = require('nedb') 2 | const db = new Datastore({ 3 | filename: 'test.db', 4 | autoload: true 5 | }) 6 | db.loadDatabase((err) => { 7 | // const doc = { name: 'Taro', age: 30 } 8 | // db.insert(doc) 9 | //db.update({name: 'Taro'}, {age: 25}, {}, (err, num) => { 10 | // console.log(err, num) 11 | //}) 12 | db.find({name: 'Taro'}, (err, docs) => { 13 | console.log(docs) 14 | }) 15 | }) 16 | 17 | -------------------------------------------------------------------------------- /ch5/nedb_test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nedb_test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "nedb": "^1.8.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ch5/nedb_test/test.db: -------------------------------------------------------------------------------- 1 | {"age":25,"_id":"ZnSK8lyvULvsprHr"} 2 | {"age":25,"_id":"j4jPW1GlQ4CCL5tD"} 3 | -------------------------------------------------------------------------------- /ch5/router_params/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /ch5/router_params/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "router_params", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^15.5.4", 7 | "react-dom": "^15.5.4", 8 | "react-router-dom": "^4.1.1" 9 | }, 10 | "devDependencies": { 11 | "react-scripts": "1.0.7" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test --env=jsdom", 17 | "eject": "react-scripts eject" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ch5/router_params/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch5/router_params/public/favicon.ico -------------------------------------------------------------------------------- /ch5/router_params/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /ch5/router_params/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /ch5/router_params/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-intro { 18 | font-size: large; 19 | } 20 | 21 | @keyframes App-logo-spin { 22 | from { transform: rotate(0deg); } 23 | to { transform: rotate(360deg); } 24 | } 25 | -------------------------------------------------------------------------------- /ch5/router_params/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | 5 | class App extends Component { 6 | render() { 7 | return ( 8 |
9 |
10 | logo 11 |

Welcome to React

12 |
13 |

14 | To get started, edit src/App.js and save to reload. 15 |

16 |
17 | ); 18 | } 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /ch5/router_params/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /ch5/router_params/src/CustomerApp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | BrowserRouter as Router, 4 | Route, 5 | Link, 6 | Switch 7 | } from 'react-router-dom' 8 | // 사용자 정보 9 | const users = [ 10 | {id: 1, name: '윤인성', info: '웹 개발 부서 개발자'}, 11 | {id: 2, name: '연하진', info: '웹 개발 부서 기획자'}, 12 | {id: 3, name: '윤명월', info: '웹 개발 부서 디자이너'} 13 | ] 14 | // 메인 컴포넌트 정의 --- (※1) 15 | const CustomerApp = () => ( 16 | 17 |
18 |
19 | 20 | 21 | 22 | 23 |
24 |
25 |
26 | ) 27 | // 사용자 목록을 출력하는 컴포넌트 --- (※2) 28 | class UserList extends React.Component { 29 | render () { 30 | const ulist = users.map(e => ( 31 |
  • 32 | {e.name} 33 |
  • 34 | )) 35 | return (
      {ulist}
    ) 36 | } 37 | } 38 | // 사용자 상세 정보를 출력하는 컴포넌트 --- (※3) 39 | class UserCard extends React.Component { 40 | render () { 41 | const {params} = this.props.match 42 | const id = parseInt(params.id, 10) 43 | const user = users.filter(e => e.id === id)[0] 44 | return ( 45 |
    46 |
    {id}: {user.name} - {user.info}
    47 |
    →뒤로가기
    48 |
    49 | ) 50 | } 51 | } 52 | export default CustomerApp -------------------------------------------------------------------------------- /ch5/router_params/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /ch5/router_params/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import CustomerApp from './CustomerApp'; 4 | import registerServiceWorker from './registerServiceWorker'; 5 | import './index.css'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /ch5/router_test/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /ch5/router_test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "router_test", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^15.5.4", 7 | "react-dom": "^15.5.4" 8 | }, 9 | "devDependencies": { 10 | "react-scripts": "1.0.7" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test --env=jsdom", 16 | "eject": "react-scripts eject" 17 | } 18 | } -------------------------------------------------------------------------------- /ch5/router_test/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch5/router_test/public/favicon.ico -------------------------------------------------------------------------------- /ch5/router_test/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
    29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /ch5/router_test/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /ch5/router_test/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-intro { 18 | font-size: large; 19 | } 20 | 21 | @keyframes App-logo-spin { 22 | from { transform: rotate(0deg); } 23 | to { transform: rotate(360deg); } 24 | } 25 | -------------------------------------------------------------------------------- /ch5/router_test/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | 5 | class App extends Component { 6 | render() { 7 | return ( 8 |
    9 |
    10 | logo 11 |

    Welcome to React

    12 |
    13 |

    14 | To get started, edit src/App.js and save to reload. 15 |

    16 |
    17 | ); 18 | } 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /ch5/router_test/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /ch5/router_test/src/HelloApp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | BrowserRouter as Router, 4 | Route, 5 | Link 6 | } from 'react-router-dom' 7 | // 리액트 라우터를 사용해 메인 컴포넌트를 정의합니다. --- (※1) 8 | const HelloApp = () => ( 9 | 10 |
    11 | 12 | 13 | 14 | 15 |
    16 |
    17 | ) 18 | // 홈 화면에 출력할 컴포넌트를 정의합니다. --- (※2) 19 | const Home = () => ( 20 |
    21 |

    Hello App

    22 |

    언어를 선택해주세요.

    23 | 28 |
    29 | ) 30 | // 한국어 출력 컴포넌트를 정의합니다. --- (※3) 31 | const HelloKorean = () => ( 32 |
    33 |

    안녕하세요

    34 |

    뒤로가기

    35 |
    36 | ) 37 | // 일본어 출력 컴포넌트를 정의합니다. --- (※4) 38 | const HelloJapanese = () => ( 39 |
    40 |

    こんにちは

    41 |

    戻る

    42 |
    43 | ) 44 | // 영어 출력 컴포넌트를 정의합니다. --- (※5) 45 | const HelloEnglish = () => ( 46 |
    47 |

    Hello

    48 |

    Back

    49 |
    50 | ) 51 | export default HelloApp -------------------------------------------------------------------------------- /ch5/router_test/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /ch5/router_test/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import HelloApp from './HelloApp'; 4 | import registerServiceWorker from './registerServiceWorker'; 5 | import './index.css'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /ch5/router_test2/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /ch5/router_test2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "router_test2", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^15.5.4", 7 | "react-dom": "^15.5.4", 8 | "react-router-dom": "^4.1.1" 9 | }, 10 | "devDependencies": { 11 | "react-scripts": "1.0.7" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test --env=jsdom", 17 | "eject": "react-scripts eject" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ch5/router_test2/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch5/router_test2/public/favicon.ico -------------------------------------------------------------------------------- /ch5/router_test2/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
    29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /ch5/router_test2/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /ch5/router_test2/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-intro { 18 | font-size: large; 19 | } 20 | 21 | @keyframes App-logo-spin { 22 | from { transform: rotate(0deg); } 23 | to { transform: rotate(360deg); } 24 | } 25 | -------------------------------------------------------------------------------- /ch5/router_test2/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | 5 | class App extends Component { 6 | render() { 7 | return ( 8 |
    9 |
    10 | logo 11 |

    Welcome to React

    12 |
    13 |

    14 | To get started, edit src/App.js and save to reload. 15 |

    16 |
    17 | ); 18 | } 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /ch5/router_test2/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /ch5/router_test2/src/HelloApp2.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | BrowserRouter as Router, 4 | Route, 5 | Link 6 | } from 'react-router-dom' 7 | // 리액트 라우터를 사용하는 메인 컴포넌트를 정의합니다. --- (※1) 8 | const HelloApp2 = () => ( 9 | 10 |
    11 | 12 |
    13 | 14 | 15 | 16 | 17 |
    18 | 19 |
    20 |
    21 | ) 22 | // 고정 헤더를 정의합니다. --- (※2) 23 | const HelloHeader = () => ( 24 |
    25 |

    HelloApp v2

    26 |

    27 | [한국어] 28 | [일본어] 29 | [영어] 30 |

    31 |
    32 | ) 33 | // 고정 푸터를 정의합니다. 34 | const HelloFooter = () => ( 35 |
    36 | 인사를 다양한 언어로 출력하는 애플리케이션입니다. 37 |
    38 | ) 39 | // 한국어 출력 컴포넌트를 정의합니다. --- (※3) 40 | const HelloKorean = () => ( 41 |

    안녕하세요

    42 | ) 43 | // 일본어 출력 컴포넌트를 정의합니다. --- (※4) 44 | const HelloJapanese = () => ( 45 |

    こんにちは

    46 | ) 47 | // 영어 출력 컴포넌트를 정의합니다. --- (※5) 48 | const HelloEnglish = () => ( 49 |

    Hello

    50 | ) 51 | // 스타일을 정의합니다. 52 | const styleHeader = { 53 | backgroundColor: 'orange', 54 | color: 'white', 55 | padding: 8 56 | } 57 | export default HelloApp2 -------------------------------------------------------------------------------- /ch5/router_test2/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /ch5/router_test2/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import HelloApp2 from './HelloApp2'; 4 | import registerServiceWorker from './registerServiceWorker'; 5 | import './index.css'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /ch6/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch6/.DS_Store -------------------------------------------------------------------------------- /ch6/handwriting/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch6/handwriting/.DS_Store -------------------------------------------------------------------------------- /ch6/handwriting/1-download.js: -------------------------------------------------------------------------------- 1 | // 메인 처리(다운로드) --- (※1) 2 | (async () => { 3 | const path = require('path') 4 | const base = 'http://yann.lecun.com/exdb/mnist' 5 | await download( 6 | base + '/t10k-images-idx3-ubyte.gz', 7 | path.join(__dirname, 'database', 'images-idx3')) 8 | await download( 9 | base + '/t10k-labels-idx1-ubyte.gz', 10 | path.join(__dirname, 'database', 'labels-idx1')) 11 | })() 12 | // 다운로드하고 압축 풀기 --- (※2) 13 | async function download (url, savepath) { 14 | console.log('다운로드 시작:', url) 15 | const tmp = savepath + '.gz' 16 | await downloadPromise(url, tmp) 17 | await gunzip(tmp, savepath) 18 | console.log('ok:', savepath) 19 | } 20 | // 파일 다운로드하기 --- (※3) 21 | function downloadPromise (url, savepath) { 22 | return new Promise((resolve, reject) => { 23 | const http = require('http') 24 | const fs = require('fs') 25 | if (fs.existsSync(savepath)) return resolve() 26 | const outfile = fs.createWriteStream(savepath) 27 | http.get(url, (res) => { 28 | res.pipe(outfile) 29 | res.on('end', () => { 30 | outfile.close() 31 | resolve() 32 | }) 33 | }) 34 | .on('error', (err) => reject(err)) 35 | }) 36 | } 37 | // 파일 압축 해제하기 --- (※4) 38 | function gunzip (infile, outfile) { 39 | return new Promise((resolve, reject) => { 40 | const zlib = require('zlib') 41 | const fs = require('fs') 42 | const rd = fs.readFileSync(infile) 43 | zlib.gunzip(rd, (err, bin) => { 44 | if (err) reject(err) 45 | fs.writeFileSync(outfile, bin) 46 | resolve() 47 | }) 48 | }) 49 | } -------------------------------------------------------------------------------- /ch6/handwriting/2-conv2csv.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | // 변환 4 | convertToCSV(path.join(__dirname, 'database')) 5 | function convertToCSV (dbdir) { 6 | // 파일 이름 지정 7 | const imgFile = path.join(dbdir, 'images-idx3') 8 | const lblFile = path.join(dbdir, 'labels-idx1') 9 | const csvFile = path.join(dbdir, 'images.csv') 10 | // 파일 열기 --- (※1) 11 | const imgF = fs.openSync(imgFile, 'r') 12 | const lblF = fs.openSync(lblFile, 'r') 13 | const outF = fs.openSync(csvFile, 'w+') 14 | // 이미지 데이터베이스의 헤더 읽기 --- (※2) 15 | const ibuf = Buffer.alloc(16) 16 | fs.readSync(imgF, ibuf, 0, ibuf.length) 17 | const magic = ibuf.readUInt32BE(0) 18 | const numImages = ibuf.readUInt32BE(4) 19 | const numRows = ibuf.readUInt32BE(8) 20 | const numCols = ibuf.readUInt32BE(12) 21 | const numPixels = numRows * numCols 22 | // 헤더 검증 23 | if (magic !== 2051) throw new Error('파일에 문제가 있습니다.') 24 | console.log('이미지 수 =', numImages, numRows, 'x', numCols) 25 | // 레이블 데이터의 헤더 검증 --- (※3) 26 | const lbuf = Buffer.alloc(8) 27 | fs.readSync(lblF, lbuf, 0, lbuf.length) 28 | const magicl = lbuf.readUInt32BE(0) 29 | if (magicl !== 2049) throw new Error('파일에 문제가 있습니다.') 30 | // 이미지 추출하기 --- (※4) 31 | const pixels = Buffer.alloc(numPixels) 32 | const labelb = Buffer.alloc(1) 33 | for (let i = 0; i < numImages; i++) { 34 | // 진행률 출력하기 35 | if (i % 1000 === 0) console.log(i, '/', numImages) 36 | // 이미지 읽기 --- (※5) 37 | fs.readSync(imgF, pixels, 0, numPixels) 38 | fs.readSync(lblF, labelb, 0, 1) 39 | const line = new Uint8Array(pixels) 40 | const label = labelb.readInt8(0) 41 | // PGM 형식으로 저장 --- (※6) 42 | if (i < 20) { 43 | let s = 'P2 28 28 255\n' 44 | for (let j = 0; j < numPixels; j++) { 45 | s += line[j].toString() 46 | s += (j % 28 === 27) ? '\n' : ' ' 47 | } 48 | const savefile = path.join(dbdir, label + '_test_' + i + '.pgm') 49 | fs.writeFileSync(savefile, s, 'utf-8') 50 | } 51 | // CSV 한 줄 만들기 --- (※7) 52 | const csvline = label + ',' + line.join(',') + '\n' 53 | fs.writeSync(outF, csvline, 'utf-8') 54 | } 55 | console.log('ok') 56 | } -------------------------------------------------------------------------------- /ch6/handwriting/3-split.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | // CSV 파일 열기 4 | const csv = fs.readFileSync( 5 | path.join(__dirname, 'database', 'images.csv'), 6 | 'utf-8') 7 | // 줄바꿈으로 자르고 임의 숫자로 셔플 --- (※1) 8 | const a = csv.split('\n') 9 | const shuffle = () => Math.random() - 0.5 10 | const b = a.sort(shuffle) 11 | // 2000개, 500개로 분류하기 12 | const c1 = b.slice(0, 2000) 13 | const c2 = b.slice(2000, 2500) 14 | // 파일로 저장 15 | fs.writeFileSync('image-train.csv', c1.join('\n')) 16 | fs.writeFileSync('image-test.csv', c2.join('\n')) 17 | console.log('ok') -------------------------------------------------------------------------------- /ch6/handwriting/4-train.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const svm = require('node-svm') 4 | // CSV 파일 읽어 들이기 --- (※1) 5 | const data = loadCSV('image-train.csv') 6 | // node-svm으로 데이터 학습시키기 --- (※2) 7 | const clf = new svm.CSVC() 8 | clf 9 | .train(data) 10 | .progress(progress => { 11 | console.log('학습: %d%', Math.round(progress * 100)) 12 | }) 13 | .spread((model, report) => { 14 | // 학습 데이터 저장 --- (※3) 15 | const json = JSON.stringify(model) 16 | fs.writeFileSync( 17 | path.join(__dirname, 'database', 'image-model.svm'), 18 | json) 19 | console.log('완료') 20 | }) 21 | // CSV 파일을 읽어 들이고 node-svm 형식으로 변환하기 --- (※4) 22 | function loadCSV (fname) { 23 | const csv = fs.readFileSync(fname, 'utf-8') 24 | const lines = csv.split('\n') 25 | const data = lines.map(line => { 26 | const cells = line.split(',') 27 | const x = cells.map(v => parseInt(v)) 28 | const label = x.shift() 29 | return [x, label] 30 | }) 31 | return data 32 | } -------------------------------------------------------------------------------- /ch6/handwriting/5-test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const svm = require('node-svm') 4 | // 학습된 데이터 읽어 들이기 --- (※1) 5 | const json = fs.readFileSync( 6 | path.join(__dirname, 'database', 'image-model.svm'), 7 | 'utf-8') 8 | const model = JSON.parse(json) 9 | const clf = svm.restore(model) 10 | // 테스트 데이터 읽어 들이기 --- (※2) 11 | const testData = loadCSV('image-test.csv') 12 | // 테스트해서 오류 비율 구하기 --- (※3) 13 | let count = 0 14 | let ng = 0 15 | testData.forEach(ex => { 16 | const x = ex[0] 17 | const label = ex[1] 18 | const pre = clf.predictSync(x) 19 | if (label !== pre) { 20 | ng++ 21 | console.log('ng=', label, pre) 22 | } 23 | count++ 24 | }) 25 | console.log('오류 비율 =', (ng / count) * 100) 26 | // CSV 파일 읽어 들이기 --- (※4) 27 | function loadCSV (fname) { 28 | const csv = fs.readFileSync(fname, 'utf-8') 29 | const lines = csv.split('\n') 30 | const data = lines.map(line => { 31 | const cells = line.split(',') 32 | const x = cells.map(v => parseInt(v)) 33 | const label = x.shift() 34 | return [x, label] 35 | }) 36 | return data 37 | } -------------------------------------------------------------------------------- /ch6/handwriting/6-server.js: -------------------------------------------------------------------------------- 1 | // 문자 인식 서버 2 | const path = require('path') 3 | const fs = require('fs') 4 | // 상수 정의 5 | const SVM_MODEL = path.join(__dirname, 'database', 'image-model.svm') 6 | const portNo = 3001 // 서버의 포트 7 | 8 | // 웹 서버 실행 9 | const express = require('express') 10 | const app = express() 11 | app.listen(portNo, () => { 12 | console.log('서버 실행 완료:', `http://localhost:${portNo}`) 13 | }) 14 | // 학습 모델 읽어 들이기 --- (※1) 15 | const svm = require('node-svm') 16 | const modelJSON = fs.readFileSync(SVM_MODEL, 'utf-8') 17 | const model = JSON.parse(modelJSON) 18 | const clf = svm.restore(model) 19 | // API를 정의합니다. --- (※2) 20 | app.get('/api/predict', (req, res) => { 21 | const px = req.query.px 22 | if (!px) { 23 | res.json({status: false}) 24 | return 25 | } 26 | const pxa = px.split('').map(v => parseInt('0x' + v) * 16) 27 | console.log('데이터:', pxa.join(':')) 28 | clf.predict(pxa).then((label) => { 29 | res.json({status: true, label}) 30 | console.log('분류:', label) 31 | }) 32 | }) 33 | // 정적 파일 제공 34 | app.use('/', express.static('./public')) -------------------------------------------------------------------------------- /ch6/handwriting/database/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch6/handwriting/database/.DS_Store -------------------------------------------------------------------------------- /ch6/handwriting/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tegaki", 3 | "version": "1.0.0", 4 | "main": "6-server.js", 5 | "scripts": { 6 | "start": "node 6-server.js", 7 | "watch": "webpack -w", 8 | "build": "webpack" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "express": "^4.15.3", 15 | "node-svm": "^2.1.8", 16 | "react": "^15.5.4", 17 | "react-dom": "^15.5.4", 18 | "superagent": "^3.5.2" 19 | }, 20 | "devDependencies": { 21 | "babel-core": "^6.25.0", 22 | "babel-loader": "^7.0.0", 23 | "babel-preset-es2015": "^6.24.1", 24 | "babel-preset-react": "^6.24.1", 25 | "webpack": "^2.6.1" 26 | }, 27 | "description": "" 28 | } 29 | -------------------------------------------------------------------------------- /ch6/handwriting/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 文字認識 6 | 7 | 8 |
    9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /ch6/handwriting/src/styles.js: -------------------------------------------------------------------------------- 1 | // styles.js 2 | const styles = { 3 | app: { 4 | textAlign: 'center' 5 | }, 6 | canvas: { 7 | width: 280, 8 | height: 280, 9 | border: '1px solid silver' 10 | }, 11 | predict: { 12 | fontSize: 24, 13 | backgroundColor: 'yellow' 14 | } 15 | } 16 | export default styles 17 | 18 | -------------------------------------------------------------------------------- /ch6/handwriting/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | module.exports = { 3 | entry: path.join(__dirname, 'src/index.js'), 4 | output: { 5 | path: path.join(__dirname, 'public'), 6 | filename: 'bundle.js' 7 | }, 8 | devtool: 'inline-source-map', 9 | module: { 10 | rules: [ 11 | { 12 | test: /.js$/, 13 | loader: 'babel-loader', 14 | options: { 15 | presets: ['es2015', 'react'] 16 | } 17 | } 18 | ] 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /ch6/sns/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch6/sns/.DS_Store -------------------------------------------------------------------------------- /ch6/sns/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sns", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node sns-server.js", 8 | "watch": "webpack -w", 9 | "build": "webpack" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "express": "^4.15.3", 16 | "nedb": "^1.8.0", 17 | "react": "^15.5.4", 18 | "react-dom": "^15.5.4", 19 | "react-router-dom": "^4.1.1", 20 | "superagent": "^3.5.2" 21 | }, 22 | "devDependencies": { 23 | "babel-core": "^6.24.1", 24 | "babel-loader": "^7.0.0", 25 | "babel-preset-es2015": "^6.24.1", 26 | "babel-preset-react": "^6.24.1", 27 | "webpack": "^2.6.1" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ch6/sns/public/default.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | border-bottom: 2px solid #c0c0ff; 3 | padding: 8px; 4 | background-color: #f0f0ff; 5 | } 6 | 7 | h2 { 8 | border-bottom: 2px solid #c0c0ff; 9 | padding: 8px; 10 | background-color: #f0f0ff; 11 | } 12 | 13 | input { 14 | width: 280px; 15 | background-color: #fff0f0; 16 | } 17 | 18 | button { 19 | } 20 | 21 | blockquote { 22 | background-color: #f0f0ff; 23 | border-left: 2px dotted #c0c0ff; 24 | margin-left: 2em; 25 | padding: 8px; 26 | } 27 | 28 | textarea { 29 | font-size: 1em; 30 | padding: 8px; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /ch6/sns/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WIKI 6 | 7 | 8 | 9 |
    10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /ch6/sns/public/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch6/sns/public/user.png -------------------------------------------------------------------------------- /ch6/sns/server/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch6/sns/server/.DS_Store -------------------------------------------------------------------------------- /ch6/sns/server/timeline.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch6/sns/server/timeline.db -------------------------------------------------------------------------------- /ch6/sns/server/user.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch6/sns/server/user.db -------------------------------------------------------------------------------- /ch6/sns/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { 4 | BrowserRouter as Router, 5 | Route, Switch 6 | } from 'react-router-dom' 7 | import SNSUsers from './sns_users' 8 | import SNSTimeline from './sns_timeline' 9 | import SNSLogin from './sns_login' 10 | const SNSApp = () => ( 11 | 12 |
    13 | 14 | 15 | 16 | 17 | 18 | 19 |
    20 |
    21 | ) 22 | // DOM의 내용을 메인 컴포넌트로 변경합니다. 23 | ReactDOM.render( 24 | , 25 | document.getElementById('root')) -------------------------------------------------------------------------------- /ch6/sns/src/sns_login.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import request from 'superagent' 3 | import {Redirect} from 'react-router-dom' 4 | import styles from './styles' 5 | // 로그인 화면 컴포넌트 6 | export default class SNSLogin extends Component { 7 | constructor (props) { 8 | super(props) 9 | this.state = { userid: '', passwd: '', jump: '', msg: '' } 10 | } 11 | // API를 호출하고 응답받은 토큰을 localStorage에 저장하기 --- (※1) 12 | api (command) { 13 | request 14 | .get('/api/' + command) 15 | .query({ 16 | userid: this.state.userid, 17 | passwd: this.state.passwd 18 | }) 19 | .end((err, res) => { 20 | if (err) return 21 | const r = res.body 22 | console.log(r) 23 | if (r.status && r.token) { 24 | // 인증 토큰을 localStorage에 저장하기 25 | window.localStorage['sns_id'] = this.state.userid 26 | window.localStorage['sns_auth_token'] = r.token 27 | this.setState({jump: '/timeline'}) 28 | return 29 | } 30 | this.setState({msg: r.msg}) 31 | }) 32 | } 33 | render () { 34 | if (this.state.jump) { 35 | return 36 | } 37 | const changed = (name, e) => this.setState({[name]: e.target.value}) 38 | return ( 39 |
    40 |

    로그인

    41 |
    42 | 사용자 ID:
    43 | changed('userid', e)} />
    45 | 비밀번호:
    46 | changed('passwd', e)} />
    48 |
    49 |

    {this.state.msg}

    50 |

    52 |
    53 |
    54 | ) 55 | } 56 | } -------------------------------------------------------------------------------- /ch6/sns/src/sns_timeline.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import request from 'superagent' 3 | import styles from './styles' 4 | // 타임라인 화면을 정의하는 컴포넌트 5 | export default class SNSTimeline extends Component { 6 | constructor (props) { 7 | super(props) 8 | this.state = { timelines: [], comment: '' } 9 | } 10 | componentWillMount () { 11 | this.loadTimelines() 12 | } 13 | loadTimelines () { // 타임라인 추출 --- (※1) 14 | request 15 | .get('/api/get_friends_timeline') 16 | .query({ 17 | userid: window.localStorage.sns_id, 18 | token: window.localStorage.sns_auth_token 19 | }) 20 | .end((err, res) => { 21 | if (err) return 22 | this.setState({timelines: res.body.timelines}) 23 | }) 24 | } 25 | post () { // 자신의 타임라인에 글쓰기 --- (※2) 26 | request 27 | .get('/api/add_timeline') 28 | .query({ 29 | userid: window.localStorage.sns_id, 30 | token: window.localStorage.sns_auth_token, 31 | comment: this.state.comment 32 | }) 33 | .end((err, res) => { 34 | if (err) return 35 | this.setState({comment: ''}) 36 | this.loadTimelines() 37 | }) 38 | } 39 | render () { 40 | // 타임라인 데이터를 하나씩 생성 --- (※3) 41 | const timelines = this.state.timelines.map(e => { 42 | return ( 43 |
    44 | 45 |
    {e.userid}:
    46 |
    {e.comment}
    47 |

    48 |

    49 | ) 50 | }) 51 | return ( 52 |
    53 |

    타임라인

    54 |
    55 | this.setState({comment: e.target.value})} /> 57 | 58 |
    59 |
    {timelines}
    60 |
    61 |

    →친구 추가하기

    62 |

    →다른 사용자로 로그인

    63 |
    64 | ) 65 | } 66 | } -------------------------------------------------------------------------------- /ch6/sns/src/styles.js: -------------------------------------------------------------------------------- 1 | const styles = { 2 | error: { 3 | color: 'red' 4 | }, 5 | login: { 6 | width: 300, 7 | textAlign: 'left', 8 | marginLeft: 30, 9 | padding: 15 10 | }, 11 | timeline: { 12 | borderBottom: '1px solid silver', 13 | padding: 10, 14 | margin: 10 15 | }, 16 | tl_img: { 17 | float: 'left', 18 | width: 32 19 | }, 20 | userid: { 21 | float: 'left', 22 | color: 'gray', 23 | padding: 5, 24 | margin: 5, 25 | width: 70, 26 | textAlign: 'right' 27 | }, 28 | comment: { 29 | float: 'left', 30 | padding: 5, 31 | margin: 5, 32 | width: 300 33 | }, 34 | friend: { 35 | padding: 10, 36 | borderBottom: '1px solid silver' 37 | } 38 | } 39 | export default styles 40 | -------------------------------------------------------------------------------- /ch6/sns/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | module.exports = { 3 | entry: path.join(__dirname, 'src/index.js'), 4 | output: { 5 | path: path.join(__dirname, 'public'), 6 | filename: 'bundle.js' 7 | }, 8 | devtool: 'inline-source-map', 9 | module: { 10 | rules: [ 11 | { 12 | test: /.js$/, 13 | loader: 'babel-loader', 14 | options: { 15 | presets: ['es2015', 'react'] 16 | } 17 | } 18 | ] 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /ch6/wiki/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/nodejs-react/3359e8b4ea8256db2f6fe0df6d994eb4413a9351/ch6/wiki/.DS_Store -------------------------------------------------------------------------------- /ch6/wiki/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wiki", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node wiki-server.js", 8 | "watch": "webpack -w", 9 | "build": "webpack", 10 | "build:parser": "pegjs -o src/wiki_parser.js src/wiki_parser.pegjs" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "body-parser": "^1.17.2", 17 | "express": "^4.15.3", 18 | "nedb": "^1.8.0", 19 | "react": "^15.5.4", 20 | "react-dom": "^15.5.4", 21 | "react-router-dom": "^4.1.1", 22 | "superagent": "^3.5.2" 23 | }, 24 | "devDependencies": { 25 | "babel-core": "^6.24.1", 26 | "babel-loader": "^7.0.0", 27 | "babel-preset-es2015": "^6.24.1", 28 | "babel-preset-react": "^6.24.1", 29 | "pegjs": "^0.10.0", 30 | "webpack": "^2.6.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ch6/wiki/public/default.css: -------------------------------------------------------------------------------- 1 | h2 { 2 | border-bottom: 2px solid #c0c0ff; 3 | padding: 8px; 4 | background-color: #f0f0ff; 5 | } 6 | 7 | blockquote { 8 | background-color: #f0f0ff; 9 | border-left: 2px dotted #c0c0ff; 10 | margin-left: 2em; 11 | padding: 8px; 12 | } 13 | 14 | textarea { 15 | font-size: 1em; 16 | padding: 8px; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /ch6/wiki/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WIKI 6 | 7 | 8 | 9 |
    10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /ch6/wiki/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { 4 | BrowserRouter as Router, 5 | Route, Switch 6 | } from 'react-router-dom' 7 | import WikiEdit from './wiki_edit' 8 | import WikiShow from './wiki_show' 9 | const WikiApp = () => ( 10 | 11 |
    12 | 13 | 14 |
    15 |
    16 | ) 17 | // DOM의 내용을 메인 컴포넌트로 변경합니다. 18 | ReactDOM.render( 19 | , 20 | document.getElementById('root')) -------------------------------------------------------------------------------- /ch6/wiki/src/styles.js: -------------------------------------------------------------------------------- 1 | const styles = { 2 | show: { 3 | border: '1px solid gray', 4 | padding: 12 5 | }, 6 | edit: { 7 | padding: 12, 8 | backgroundColor: 'silver' 9 | }, 10 | right: { 11 | textAlign: 'right' 12 | } 13 | } 14 | export default styles 15 | -------------------------------------------------------------------------------- /ch6/wiki/src/wiki_edit.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import request from 'superagent' 3 | import {Redirect} from 'react-router-dom' 4 | import styles from './styles' 5 | // 편집 화면 컴포넌트 --- (※1) 6 | export default class WikiEdit extends Component { 7 | // 컴포넌트 초기화 --- (※2) 8 | constructor (props) { 9 | super(props) 10 | const {params} = this.props.match 11 | const name = params.name // --- (※3) 12 | this.state = { 13 | name, body: '', loaded: false, jump: '' 14 | } 15 | } 16 | // 위키 내용 읽어 들이기 --- (※4) 17 | componentWillMount () { 18 | request 19 | .get(`/api/get/${this.state.name}`) 20 | .end((err, res) => { 21 | if (err) return 22 | this.setState({ 23 | body: res.body.data.body, 24 | loaded: true 25 | }) 26 | }) 27 | } 28 | // 본문을 서버에 전송 --- (※5) 29 | save () { 30 | const wikiname = this.state.name 31 | request 32 | .post('/api/put/' + wikiname) 33 | .type('form') 34 | .send({ 35 | name: wikiname, 36 | body: this.state.body 37 | }) 38 | .end((err, data) => { 39 | if (err) { 40 | console.log(err) 41 | return 42 | } 43 | this.setState({jump: '/wiki/' + wikiname}) 44 | }) 45 | } 46 | bodyChanged (e) { 47 | this.setState({body: e.target.value}) 48 | } 49 | // 편집 화면 출력 --- (※6) 50 | render () { 51 | if (!this.state.loaded) { // --- (※7) 52 | return (

    읽어 들이는 중

    ) 53 | } 54 | if (this.state.jump !== '') { 55 | // 메인 화면으로 리다이렉트 --- (※8) 56 | return 57 | } 58 | const name = this.state.name 59 | return ( 60 |
    61 |

    {name}

    62 |