├── .gitignore
├── .idea
├── .gitignore
├── encodings.xml
├── jsLibraryMappings.xml
├── markdown-navigator-enh.xml
├── markdown-navigator.xml
├── misc.xml
├── modules.xml
├── redux-vs-mobx.iml
└── vcs.xml
├── 1.redux
├── index.js
├── index2.js
├── package-lock.json
└── package.json
├── 2.redux
├── actions
│ ├── post.js
│ └── user.js
├── index2.js
├── package-lock.json
├── package.json
└── reducers
│ ├── index.js
│ ├── post.js
│ └── user.js
├── 3.react-redux
├── App.jsx
├── actions
│ ├── post.js
│ └── user.js
├── client.jsx
├── index.html
├── package-lock.json
├── package.json
├── reducers
│ ├── index.js
│ ├── post.js
│ └── user.js
├── store.js
└── webpack.config.js
├── 4.react-redux-immer
├── App.jsx
├── actions
│ ├── post.js
│ └── user.js
├── client.jsx
├── index.html
├── package-lock.json
├── package.json
├── reducers
│ ├── index.js
│ ├── post.js
│ └── user.js
├── store.js
└── webpack.config.js
├── 5.react-redux-toolkit
├── App.jsx
├── actions
│ ├── post.js
│ └── user.js
├── client.jsx
├── index.html
├── package-lock.json
├── package.json
├── reducers
│ ├── index.js
│ ├── postSlice.js
│ └── userSlice.js
├── store.js
└── webpack.config.js
├── 6.mobx
├── index.js
├── package-lock.json
└── package.json
├── 7.mobx
├── index.js
├── package-lock.json
└── package.json
├── 8.mobx-react
├── App.jsx
├── Context.jsx
├── client.jsx
├── index.html
├── package-lock.json
├── package.json
├── store.js
└── webpack.config.js
├── 9.mobx-react
├── App.jsx
├── Context.jsx
├── client.jsx
├── index.html
├── package-lock.json
├── package.json
├── store.js
├── useStore.js
└── webpack.config.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Node template
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 | lerna-debug.log*
10 |
11 | # Diagnostic reports (https://nodejs.org/api/report.html)
12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
13 |
14 | # Runtime data
15 | pids
16 | *.pid
17 | *.seed
18 | *.pid.lock
19 |
20 | # Directory for instrumented libs generated by jscoverage/JSCover
21 | lib-cov
22 |
23 | # Coverage directory used by tools like istanbul
24 | coverage
25 | *.lcov
26 |
27 | # nyc test coverage
28 | .nyc_output
29 |
30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
31 | .grunt
32 |
33 | # Bower dependency directory (https://bower.io/)
34 | bower_components
35 |
36 | # node-waf configuration
37 | .lock-wscript
38 |
39 | # Compiled binary addons (https://nodejs.org/api/addons.html)
40 | build/Release
41 |
42 | # Dependency directories
43 | node_modules/
44 | jspm_packages/
45 |
46 | # TypeScript v1 declaration files
47 | typings/
48 |
49 | # TypeScript cache
50 | *.tsbuildinfo
51 |
52 | # Optional npm cache directory
53 | .npm
54 |
55 | # Optional eslint cache
56 | .eslintcache
57 |
58 | # Optional REPL history
59 | .node_repl_history
60 |
61 | # Output of 'npm pack'
62 | *.tgz
63 |
64 | # Yarn Integrity file
65 | .yarn-integrity
66 |
67 | # dotenv environment variables file
68 | .env
69 | .env.test
70 |
71 | # parcel-bundler cache (https://parceljs.org/)
72 | .cache
73 |
74 | # next.js build output
75 | .next
76 |
77 | # nuxt.js build output
78 | .nuxt
79 |
80 | # vuepress build output
81 | .vuepress/dist
82 |
83 | # Serverless directories
84 | .serverless/
85 |
86 | # FuseBox cache
87 | .fusebox/
88 |
89 | # DynamoDB Local files
90 | .dynamodb/
91 |
92 | ### JetBrains template
93 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
94 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
95 |
96 | # User-specific stuff
97 | .idea/**/workspace.xml
98 | .idea/**/tasks.xml
99 | .idea/**/usage.statistics.xml
100 | .idea/**/dictionaries
101 | .idea/**/shelf
102 |
103 | # Generated files
104 | .idea/**/contentModel.xml
105 |
106 | # Sensitive or high-churn files
107 | .idea/**/dataSources/
108 | .idea/**/dataSources.ids
109 | .idea/**/dataSources.local.xml
110 | .idea/**/sqlDataSources.xml
111 | .idea/**/dynamic.xml
112 | .idea/**/uiDesigner.xml
113 | .idea/**/dbnavigator.xml
114 |
115 | # Gradle
116 | .idea/**/gradle.xml
117 | .idea/**/libraries
118 |
119 | # Gradle and Maven with auto-import
120 | # When using Gradle or Maven with auto-import, you should exclude module files,
121 | # since they will be recreated, and may cause churn. Uncomment if using
122 | # auto-import.
123 | # .idea/modules.xml
124 | # .idea/*.iml
125 | # .idea/modules
126 | # *.iml
127 | # *.ipr
128 |
129 | # CMake
130 | cmake-build-*/
131 |
132 | # Mongo Explorer plugin
133 | .idea/**/mongoSettings.xml
134 |
135 | # File-based project format
136 | *.iws
137 |
138 | # IntelliJ
139 | out/
140 |
141 | # mpeltonen/sbt-idea plugin
142 | .idea_modules/
143 |
144 | # JIRA plugin
145 | atlassian-ide-plugin.xml
146 |
147 | # Cursive Clojure plugin
148 | .idea/replstate.xml
149 |
150 | # Crashlytics plugin (for Android Studio and IntelliJ)
151 | com_crashlytics_export_strings.xml
152 | crashlytics.properties
153 | crashlytics-build.properties
154 | fabric.properties
155 |
156 | # Editor-based Rest Client
157 | .idea/httpRequests
158 |
159 | # Android studio 3.1+ serialized cache file
160 | .idea/caches/build_file_checksums.ser
161 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/jsLibraryMappings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator-enh.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/redux-vs-mobx.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/1.redux/index.js:
--------------------------------------------------------------------------------
1 | const { createStore } = require('redux');
2 |
3 | const reducer = (prevState, action) => { // 새로운 state 만들어주기
4 | switch (action.type) {
5 | case 'CHANGE_COMP_A':
6 | return {
7 | ...prevState,
8 | compA: action.data,
9 | };
10 | case 'CHANGE_COMP_B':
11 | return {
12 | ...prevState,
13 | compB: action.data,
14 | };
15 | case 'CHANGE_COMP_C':
16 | return {
17 | ...prevState,
18 | compC: action.data,
19 | };
20 | default:
21 | return prevState;
22 | }
23 | };
24 |
25 | const initialState = {
26 | compA: 'a',
27 | compB: 12,
28 | compC: null,
29 | };
30 |
31 | const store = createStore(reducer, initialState);
32 | store.subscribe(() => { // react-redux 안에 들어있어요.
33 | console.log('changed'); // 화면 바꿔주는 코드 여기서
34 | });
35 |
36 | console.log('1st', store.getState());
37 |
38 | const changeCompA = (data) => {
39 | return { // action
40 | type: 'CHANGE_COMP_A',
41 | data,
42 | };
43 | };
44 |
45 | // store.dispatch({
46 | // type: 'CHANGE_COMP_A',
47 | // data: 'b',
48 | // });
49 | store.dispatch(changeCompA('b'));
50 |
51 | console.log('2nd', store.getState());
52 |
--------------------------------------------------------------------------------
/1.redux/index2.js:
--------------------------------------------------------------------------------
1 | const { createStore } = require('redux');
2 |
3 | const reducer = (prevState, action) => { // 새로운 state 만들어주기
4 | switch (action.type) {
5 | case 'LOG_IN':
6 | return {
7 | ...prevState,
8 | user: action.data,
9 | };
10 | case 'LOG_OUT':
11 | return {
12 | ...prevState,
13 | user: null,
14 | };
15 | case 'ADD_POST':
16 | return {
17 | posts: [...prevState.posts, action.data],
18 | };
19 | default:
20 | return prevState;
21 | }
22 | };
23 |
24 | const initialState = {
25 | user: null,
26 | isLoggingIn: true,
27 | posts: [],
28 | comments: [],
29 | favorites: [],
30 | history: [],
31 | likes: [],
32 | followers: [],
33 | };
34 |
35 | const store = createStore(reducer, initialState);
36 | store.subscribe(() => { // react-redux 안에 들어있어요.
37 | console.log('changed'); // 화면 바꿔주는 코드 여기서
38 | });
39 |
40 | console.log('1st', store.getState());
41 |
42 | const logIn = (data) => {
43 | return { // action
44 | type: 'LOG_IN',
45 | data,
46 | };
47 | };
48 |
49 | const logOut = () => {
50 | return { // action
51 | type: 'LOG_OUT',
52 | };
53 | };
54 |
55 | const addPost = (data) => {
56 | return {
57 | type: 'ADD_POST',
58 | data,
59 | }
60 | };
61 |
62 | // --------------------------------------
63 |
64 | store.dispatch({
65 | type: 'LOG_IN_REQUEST',
66 | });
67 |
68 | store.dispatch(logIn({
69 | id: 1,
70 | name: 'zerocho',
71 | admin: true,
72 | }));
73 | console.log('2nd', store.getState());
74 |
75 | store.dispatch(addPost({
76 | userId: 1,
77 | id: 1,
78 | content: '안녕하세요. 리덕스',
79 | }));
80 | console.log('3rd', store.getState());
81 | store.dispatch(addPost({
82 | userId: 1,
83 | id: 2,
84 | content: '두번째 리덕스',
85 | }));
86 | console.log('4th', store.getState());
87 |
88 | store.dispatch(logOut());
89 | console.log('5th', store.getState());
90 |
--------------------------------------------------------------------------------
/1.redux/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "1.redux",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "js-tokens": {
8 | "version": "4.0.0",
9 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
10 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
11 | },
12 | "loose-envify": {
13 | "version": "1.4.0",
14 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
15 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
16 | "requires": {
17 | "js-tokens": "^3.0.0 || ^4.0.0"
18 | }
19 | },
20 | "redux": {
21 | "version": "4.0.4",
22 | "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.4.tgz",
23 | "integrity": "sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q==",
24 | "requires": {
25 | "loose-envify": "^1.4.0",
26 | "symbol-observable": "^1.2.0"
27 | }
28 | },
29 | "symbol-observable": {
30 | "version": "1.2.0",
31 | "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
32 | "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/1.redux/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "1.redux",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "redux": "^4.0.5"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/2.redux/actions/post.js:
--------------------------------------------------------------------------------
1 | const addPost = (data) => {
2 | return {
3 | type: 'ADD_POST',
4 | data,
5 | }
6 | };
7 |
8 | module.exports = {
9 | addPost,
10 | };
11 |
--------------------------------------------------------------------------------
/2.redux/actions/user.js:
--------------------------------------------------------------------------------
1 | const logIn = (data) => { // async action creator
2 | return (dispatch, getState) => { // async action
3 | dispatch(logInRequest(data));
4 | try {
5 | setTimeout(() => {
6 | dispatch(logInSuccess({
7 | userId: 1,
8 | nickname: 'zerocho'
9 | }));
10 | }, 2000);
11 | } catch (e) {
12 | dispatch(logInFailure(e));
13 | }
14 | };
15 | };
16 |
17 | const logInRequest = (data) => {
18 | return {
19 | type: 'LOG_IN_REQUEST',
20 | data,
21 | }
22 | };
23 |
24 | const logInSuccess = (data) => {
25 | return {
26 | type: 'LOG_IN_SUCCESS',
27 | data,
28 | }
29 | };
30 |
31 | const logInFailure = (error) => {
32 | return {
33 | type: 'LOG_IN_FAILURE',
34 | error,
35 | }
36 | };
37 |
38 | const logOut = () => {
39 | return { // action
40 | type: 'LOG_OUT',
41 | };
42 | };
43 |
44 | module.exports = {
45 | logIn,
46 | logOut,
47 | };
48 |
--------------------------------------------------------------------------------
/2.redux/index2.js:
--------------------------------------------------------------------------------
1 | const { createStore, compose, applyMiddleware } = require('redux');
2 | const reducer = require('./reducers');
3 | const { addPost } = require('./actions/post');
4 | const { logIn, logOut } = require('./actions/user');
5 |
6 | const initialState = {
7 | user: {
8 | isLoggingIn: true,
9 | data: null,
10 | },
11 | posts: [],
12 | };
13 |
14 | const firstMiddleware = (store) => (next) => (action) => {
15 | console.log('로깅', action);
16 | next(action);
17 | };
18 |
19 | const thunkMiddleware = (store) => (next) => (action) => {
20 | if (typeof action === 'function') { // 비동기
21 | return action(store.dispatch, store.getState);
22 | }
23 | return next(action); // 동기
24 | };
25 |
26 | const enhancer = applyMiddleware(
27 | firstMiddleware,
28 | thunkMiddleware,
29 | );
30 |
31 | const store = createStore(reducer, initialState, enhancer);
32 |
33 | console.log('1st', store.getState());
34 |
35 | // --------------------------------------
36 |
37 |
38 | store.dispatch(logIn({
39 | id: 1,
40 | name: 'zerocho',
41 | admin: true,
42 | }));
43 | console.log('2nd', store.getState());
44 | //
45 | // store.dispatch(addPost({
46 | // userId: 1,
47 | // id: 1,
48 | // content: '안녕하세요. 리덕스',
49 | // }));
50 | // console.log('3rd', store.getState());
51 | // store.dispatch(addPost({
52 | // userId: 1,
53 | // id: 2,
54 | // content: '두번째 리덕스',
55 | // }));
56 | // console.log('4th', store.getState());
57 | //
58 | // store.dispatch(logOut());
59 | // console.log('5th', store.getState());
60 |
--------------------------------------------------------------------------------
/2.redux/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "1.redux",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "js-tokens": {
8 | "version": "4.0.0",
9 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
10 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
11 | },
12 | "loose-envify": {
13 | "version": "1.4.0",
14 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
15 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
16 | "requires": {
17 | "js-tokens": "^3.0.0 || ^4.0.0"
18 | }
19 | },
20 | "redux": {
21 | "version": "4.0.4",
22 | "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.4.tgz",
23 | "integrity": "sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q==",
24 | "requires": {
25 | "loose-envify": "^1.4.0",
26 | "symbol-observable": "^1.2.0"
27 | }
28 | },
29 | "symbol-observable": {
30 | "version": "1.2.0",
31 | "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
32 | "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/2.redux/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "2.redux",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "redux": "^4.0.5"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/2.redux/reducers/index.js:
--------------------------------------------------------------------------------
1 | const { combineReducers } = require('redux');
2 | const userReducer = require('./user');
3 | const postReducer = require('./post');
4 |
5 | module.exports = combineReducers({
6 | user: userReducer,
7 | posts: postReducer,
8 | });
9 |
--------------------------------------------------------------------------------
/2.redux/reducers/post.js:
--------------------------------------------------------------------------------
1 | const initialState = [];
2 |
3 | const postReducer = (prevState = initialState, action) => { // 새로운 state 만들어주기
4 | switch (action.type) {
5 | case 'ADD_POST':
6 | return [...prevState, action.data];
7 | default:
8 | return prevState;
9 | }
10 | };
11 |
12 | module.exports = postReducer;
13 |
--------------------------------------------------------------------------------
/2.redux/reducers/user.js:
--------------------------------------------------------------------------------
1 | const initialState = {
2 | isLoggingIn: false,
3 | data: null,
4 | };
5 |
6 | const userReducer = (prevState = initialState, action) => { // 새로운 state 만들어주기
7 | switch (action.type) {
8 | case 'LOG_IN':
9 | return {
10 | ...prevState,
11 | data: action.data,
12 | };
13 | case 'LOG_OUT':
14 | return {
15 | ...prevState,
16 | data: null,
17 | };
18 | default:
19 | return prevState;
20 | }
21 | };
22 |
23 | module.exports = userReducer;
24 |
--------------------------------------------------------------------------------
/3.react-redux/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from 'react';
2 | import { useDispatch, useSelector } from 'react-redux';
3 | const { logIn, logOut } = require('./actions/user');
4 |
5 | const App = () => {
6 | const user = useSelector((state) => state.user);
7 | const posts = useSelector((state) => state.posts);
8 | const dispatch = useDispatch();
9 |
10 | const onClick = useCallback(() => {
11 | dispatch(logIn({
12 | id: 'zerocho',
13 | password: '비밀번호',
14 | }));
15 | }, []);
16 |
17 | const onLogout = useCallback(() => {
18 | dispatch(logOut());
19 | }, []);
20 |
21 | return (
22 |
23 | {user.isLoggingIn
24 | ?
로그인 중
25 | : user.data
26 | ?
{user.data.nickname}
27 | : '로그인 해주세요.'}
28 | {!user.data
29 | ?
30 | :
}
31 |
32 | );
33 | };
34 |
35 | export default App;
36 |
--------------------------------------------------------------------------------
/3.react-redux/actions/post.js:
--------------------------------------------------------------------------------
1 | const addPost = (data) => {
2 | return {
3 | type: 'ADD_POST',
4 | data,
5 | }
6 | };
7 |
8 | module.exports = {
9 | addPost,
10 | };
11 |
--------------------------------------------------------------------------------
/3.react-redux/actions/user.js:
--------------------------------------------------------------------------------
1 | const logIn = (data) => { // async action creator
2 | return (dispatch, getState) => { // async action
3 | dispatch(logInRequest(data));
4 | try {
5 | setTimeout(() => {
6 | dispatch(logInSuccess({
7 | userId: 1,
8 | nickname: 'zerocho'
9 | }));
10 | }, 2000);
11 | // axios.post().then().catch()으로 나중에 대체
12 | } catch (e) {
13 | dispatch(logInFailure(e));
14 | }
15 | };
16 | };
17 |
18 | const logInRequest = (data) => {
19 | return {
20 | type: 'LOG_IN_REQUEST',
21 | data,
22 | }
23 | };
24 |
25 | const logInSuccess = (data) => {
26 | return {
27 | type: 'LOG_IN_SUCCESS',
28 | data,
29 | }
30 | };
31 |
32 | const logInFailure = (error) => {
33 | return {
34 | type: 'LOG_IN_FAILURE',
35 | error,
36 | }
37 | };
38 |
39 | const logOut = () => {
40 | return { // action
41 | type: 'LOG_OUT',
42 | };
43 | };
44 |
45 | module.exports = {
46 | logIn,
47 | logOut,
48 | };
49 |
--------------------------------------------------------------------------------
/3.react-redux/client.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { Provider } from 'react-redux';
4 |
5 | import store from './store';
6 | import App from './App';
7 |
8 | ReactDOM.render(
9 |
10 |
11 | ,
12 | document.querySelector('#root'),
13 | );
14 |
--------------------------------------------------------------------------------
/3.react-redux/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 리덕스연결
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/3.react-redux/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "3.react-redux",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "webpack serve --env development --hot"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "react": "^17.0.1",
13 | "react-dom": "^17.0.1",
14 | "react-redux": "^7.2.2",
15 | "redux": "^4.0.5",
16 | "redux-devtools-extension": "^2.13.8"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.12.10",
20 | "@babel/preset-env": "^7.12.11",
21 | "@babel/preset-react": "^7.12.10",
22 | "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
23 | "babel-loader": "^8.2.2",
24 | "react-refresh": "^0.11.0",
25 | "webpack": "^5.11.0",
26 | "webpack-cli": "^4.9.1",
27 | "webpack-dev-server": "^4.7.1"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/3.react-redux/reducers/index.js:
--------------------------------------------------------------------------------
1 | const { combineReducers } = require('redux');
2 | const userReducer = require('./user');
3 | const postReducer = require('./post');
4 |
5 | module.exports = combineReducers({
6 | user: userReducer,
7 | posts: postReducer,
8 | });
9 |
--------------------------------------------------------------------------------
/3.react-redux/reducers/post.js:
--------------------------------------------------------------------------------
1 | const initialState = [];
2 |
3 | const postReducer = (prevState = initialState, action) => { // 새로운 state 만들어주기
4 | switch (action.type) {
5 | case 'ADD_POST':
6 | return [...prevState, action.data];
7 | default:
8 | return prevState;
9 | }
10 | };
11 |
12 | module.exports = postReducer;
13 |
--------------------------------------------------------------------------------
/3.react-redux/reducers/user.js:
--------------------------------------------------------------------------------
1 | const initialState = {
2 | isLoggingIn: false,
3 | data: null,
4 | };
5 |
6 | const userReducer = (prevState = initialState, action) => { // 새로운 state 만들어주기
7 | switch (action.type) {
8 | case 'LOG_IN_REQUEST':
9 | return {
10 | ...prevState,
11 | data: null,
12 | isLoggingIn: true,
13 | };
14 | case 'LOG_IN_SUCCESS':
15 | return {
16 | ...prevState,
17 | data: action.data,
18 | isLoggingIn: false,
19 | };
20 | case 'LOG_IN_FAILURE':
21 | return {
22 | ...prevState,
23 | data: null,
24 | isLoggingIn: false,
25 | };
26 | case 'LOG_OUT':
27 | return {
28 | ...prevState,
29 | data: null,
30 | };
31 | default:
32 | return prevState;
33 | }
34 | };
35 |
36 | module.exports = userReducer;
37 |
--------------------------------------------------------------------------------
/3.react-redux/store.js:
--------------------------------------------------------------------------------
1 | const { createStore, compose, applyMiddleware } = require('redux');
2 | const { composeWithDevTools } = require('redux-devtools-extension');
3 |
4 | const reducer = require('./reducers');
5 | const { addPost } = require('./actions/post');
6 | const { logIn, logOut } = require('./actions/user');
7 |
8 | const initialState = {
9 | user: {
10 | isLoggingIn: false,
11 | data: null,
12 | },
13 | posts: [],
14 | };
15 |
16 | const firstMiddleware = (store) => (next) => (action) => {
17 | console.log('로깅', action);
18 | next(action);
19 | };
20 |
21 | const thunkMiddleware = (store) => (next) => (action) => {
22 | if (typeof action === 'function') { // 비동기
23 | return action(store.dispatch, store.getState);
24 | }
25 | return next(action); // 동기
26 | };
27 |
28 | const enhancer = process.env.NODE_ENV === 'production'
29 | ? compose(
30 | applyMiddleware(
31 | firstMiddleware,
32 | thunkMiddleware,
33 | ),
34 | )
35 | : composeWithDevTools(
36 | applyMiddleware(
37 | firstMiddleware,
38 | thunkMiddleware,
39 | ),
40 | );
41 |
42 | const store = createStore(reducer, initialState, enhancer);
43 |
44 | module.exports = store;
45 |
--------------------------------------------------------------------------------
/3.react-redux/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
3 |
4 | module.exports = {
5 | name: 'react-redux',
6 | mode: 'development',
7 | devtool: 'eval',
8 | resolve: {
9 | extensions: ['.js', '.jsx'],
10 | },
11 | entry: {
12 | app: './client',
13 | },
14 | module: {
15 | rules: [{
16 | test: /\.jsx?$/,
17 | loader: 'babel-loader',
18 | options: {
19 | presets: [
20 | ['@babel/preset-env', {
21 | targets: {browsers: ['last 2 chrome versions']},
22 | debug: true,
23 | }],
24 | '@babel/preset-react',
25 | ],
26 | plugins: ["react-refresh/babel"]
27 | },
28 | exclude: path.join(__dirname, 'node_modules'),
29 | }],
30 | },
31 | plugins: [
32 | new ReactRefreshWebpackPlugin()
33 | ],
34 | output: {
35 | path: path.join(__dirname, 'dist'),
36 | filename: '[name].js',
37 | publicPath: '/dist',
38 | },
39 | devServer: {
40 | devMiddleware: { publicPath: '/dist' },
41 | static: { directory: path.resolve(__dirname) },
42 | hot: true
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/4.react-redux-immer/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | const { logIn, logOut } = require('./actions/user');
4 |
5 | class App extends Component {
6 | onClick = () => {
7 | this.props.dispatchLogIn({
8 | id: 'zerocho',
9 | password: '비밀번호',
10 | });
11 | };
12 |
13 | onLogout = () => {
14 | this.props.dispatchLogOut();
15 | };
16 |
17 | render() {
18 | const { user } = this.props;
19 | return (
20 |
21 | {user.isLoggingIn
22 | ?
로그인 중
23 | : user.data
24 | ?
{user.data.nickname}
25 | : '로그인 해주세요.'}
26 | {!user.data
27 | ?
28 | :
}
29 |
30 | );
31 | }
32 | }
33 |
34 | const mapStateToProps = (state) => ({
35 | user: state.user,
36 | posts: state.posts,
37 | }); // reselect
38 |
39 | const mapDispatchToProps = (dispatch) => ({
40 | dispatchLogIn: (data) => dispatch(logIn(data)),
41 | dispatchLogOut: () => dispatch(logOut()),
42 | });
43 |
44 | export default connect(mapStateToProps, mapDispatchToProps)(App);
45 |
--------------------------------------------------------------------------------
/4.react-redux-immer/actions/post.js:
--------------------------------------------------------------------------------
1 | const addPost = (data) => {
2 | return {
3 | type: 'ADD_POST',
4 | data,
5 | }
6 | };
7 |
8 | module.exports = {
9 | addPost,
10 | };
11 |
--------------------------------------------------------------------------------
/4.react-redux-immer/actions/user.js:
--------------------------------------------------------------------------------
1 | const logIn = (data) => { // async action creator
2 | return (dispatch, getState) => { // async action
3 | dispatch(logInRequest(data));
4 | try {
5 | setTimeout(() => {
6 | dispatch(logInSuccess({
7 | userId: 1,
8 | nickname: 'zerocho'
9 | }));
10 | }, 2000);
11 | // axios.post().then().catch()으로 나중에 대체
12 | } catch (e) {
13 | dispatch(logInFailure(e));
14 | }
15 | };
16 | };
17 |
18 | const logInRequest = (data) => {
19 | return {
20 | type: 'LOG_IN_REQUEST',
21 | data,
22 | }
23 | };
24 |
25 | const logInSuccess = (data) => {
26 | return {
27 | type: 'LOG_IN_SUCCESS',
28 | data,
29 | }
30 | };
31 |
32 | const logInFailure = (error) => {
33 | return {
34 | type: 'LOG_IN_FAILURE',
35 | error,
36 | }
37 | };
38 |
39 | const logOut = () => {
40 | return { // action
41 | type: 'LOG_OUT',
42 | };
43 | };
44 |
45 | module.exports = {
46 | logIn,
47 | logOut,
48 | };
49 |
--------------------------------------------------------------------------------
/4.react-redux-immer/client.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { Provider } from 'react-redux';
4 |
5 | import store from './store';
6 | import App from './App';
7 |
8 | ReactDOM.render(
9 |
10 |
11 | ,
12 | document.querySelector('#root'),
13 | );
14 |
--------------------------------------------------------------------------------
/4.react-redux-immer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 리덕스연결
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/4.react-redux-immer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "4.react-redux-immer",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "webpack serve --env development --hot"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "@babel/plugin-proposal-class-properties": "^7.12.1",
13 | "immer": "^9.0.7",
14 | "react": "^17.0.1",
15 | "react-dom": "^17.0.1",
16 | "react-redux": "^7.2.2",
17 | "redux": "^4.0.5",
18 | "redux-devtools-extension": "^2.13.8"
19 | },
20 | "devDependencies": {
21 | "@babel/core": "^7.12.10",
22 | "@babel/preset-env": "^7.12.11",
23 | "@babel/preset-react": "^7.12.10",
24 | "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
25 | "babel-loader": "^8.2.2",
26 | "react-refresh": "^0.11.0",
27 | "webpack": "^5.11.0",
28 | "webpack-cli": "^4.9.1",
29 | "webpack-dev-server": "^4.7.1"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/4.react-redux-immer/reducers/index.js:
--------------------------------------------------------------------------------
1 | const { combineReducers } = require('redux');
2 | const userReducer = require('./user');
3 | const postReducer = require('./post');
4 |
5 | module.exports = combineReducers({
6 | user: userReducer,
7 | posts: postReducer,
8 | });
9 |
--------------------------------------------------------------------------------
/4.react-redux-immer/reducers/post.js:
--------------------------------------------------------------------------------
1 | const { produce } = require('immer');
2 |
3 | const initialState = [];
4 |
5 | const postReducer = (prevState = initialState, action) => { // 새로운 state 만들어주기
6 | return produce(prevState, (draft) => {
7 | switch (action.type) {
8 | case 'ADD_POST':
9 | draft.push(action.data);
10 | break;
11 | default:
12 | break;
13 | }
14 | });
15 | };
16 |
17 | module.exports = postReducer;
18 |
--------------------------------------------------------------------------------
/4.react-redux-immer/reducers/user.js:
--------------------------------------------------------------------------------
1 | const { produce } = require('immer');
2 |
3 | const initialState = {
4 | isLoggingIn: false,
5 | data: null,
6 | };
7 |
8 | // nextState = produce(prevState, (draft) => {})
9 |
10 | const userReducer = (prevState = initialState, action) => { // 새로운 state 만들어주기
11 | return produce(prevState, (draft) => {
12 | switch (action.type) {
13 | case 'LOG_IN_REQUEST':
14 | draft.data = null;
15 | draft.isLoggingIn = true;
16 | break;
17 | case 'LOG_IN_SUCCESS':
18 | draft.data = action.data;
19 | draft.isLoggingIn = false;
20 | break;
21 | case 'LOG_IN_FAILURE':
22 | draft.data = null;
23 | draft.isLoggingIn = false;
24 | break;
25 | case 'LOG_OUT':
26 | draft.data = null;
27 | break;
28 | default:
29 | break;
30 | }
31 | });
32 | };
33 |
34 | module.exports = userReducer;
35 |
--------------------------------------------------------------------------------
/4.react-redux-immer/store.js:
--------------------------------------------------------------------------------
1 | const { createStore, compose, applyMiddleware } = require('redux');
2 | const { composeWithDevTools } = require('redux-devtools-extension');
3 |
4 | const reducer = require('./reducers');
5 | const { addPost } = require('./actions/post');
6 | const { logIn, logOut } = require('./actions/user');
7 |
8 | const initialState = {
9 | user: {
10 | isLoggingIn: false,
11 | data: null,
12 | },
13 | posts: [],
14 | };
15 |
16 | const firstMiddleware = (store) => (next) => (action) => {
17 | console.log('로깅', action);
18 | next(action);
19 | };
20 |
21 | const thunkMiddleware = (store) => (next) => (action) => {
22 | if (typeof action === 'function') { // 비동기
23 | return action(store.dispatch, store.getState);
24 | }
25 | return next(action); // 동기
26 | };
27 |
28 |
29 | const enhancer = process.env.NODE_ENV === 'production'
30 | ? compose(
31 | applyMiddleware(
32 | firstMiddleware,
33 | thunkMiddleware,
34 | ),
35 | )
36 | : composeWithDevTools(
37 | applyMiddleware(
38 | firstMiddleware,
39 | thunkMiddleware,
40 | ),
41 | );
42 |
43 | const store = createStore(reducer, initialState, enhancer);
44 |
45 | module.exports = store;
46 |
--------------------------------------------------------------------------------
/4.react-redux-immer/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
3 |
4 | module.exports = {
5 | name: 'react-redux-immer',
6 | mode: 'development',
7 | devtool: 'eval',
8 | resolve: {
9 | extensions: ['.js', '.jsx'],
10 | },
11 | entry: {
12 | app: './client',
13 | },
14 | module: {
15 | rules: [{
16 | test: /\.jsx?$/,
17 | loader: 'babel-loader',
18 | options: {
19 | presets: [
20 | ['@babel/preset-env', {
21 | targets: {browsers: ['last 2 chrome versions']},
22 | debug: true,
23 | }],
24 | '@babel/preset-react',
25 | ],
26 | plugins: ["react-refresh/babel", "@babel/plugin-proposal-class-properties"]
27 | },
28 | exclude: path.join(__dirname, 'node_modules'),
29 | }],
30 | },
31 | plugins: [
32 | new ReactRefreshWebpackPlugin()
33 | ],
34 | output: {
35 | path: path.join(__dirname, 'dist'),
36 | filename: '[name].js',
37 | publicPath: '/dist',
38 | },
39 | devServer: {
40 | devMiddleware: { publicPath: '/dist' },
41 | static: { directory: path.resolve(__dirname) },
42 | hot: true
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/5.react-redux-toolkit/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from 'react';
2 | import { useDispatch, useSelector } from 'react-redux';
3 |
4 | const { logIn } = require('./actions/user');
5 | const userSlice = require('./reducers/userSlice');
6 |
7 | const App = () => {
8 | const user = useSelector((state) => state.user);
9 | const { list } = useSelector((state) => state.post);
10 | const dispatch = useDispatch();
11 |
12 | const onClick = useCallback(() => {
13 | dispatch(logIn({
14 | id: 'zerocho',
15 | password: '비밀번호',
16 | }));
17 | }, []);
18 |
19 | const onLogout = useCallback(() => {
20 | dispatch(userSlice.actions.logOut());
21 | }, []);
22 |
23 | return (
24 |
25 | {user.isLoggingIn
26 | ?
로그인 중
27 | : user.data
28 | ?
{user.data.nickname}
29 | : '로그인 해주세요.'}
30 | {!user.data
31 | ?
32 | :
}
33 |
34 | );
35 | };
36 |
37 | export default App;
38 |
--------------------------------------------------------------------------------
/5.react-redux-toolkit/actions/post.js:
--------------------------------------------------------------------------------
1 | const { createAsyncThunk } = require('@reduxjs/toolkit');
2 |
3 | const delay = (time, value) => new Promise((resolve, reject) => {
4 | setTimeout(() => {
5 | resolve(value);
6 | }, time);
7 | });
8 |
9 | exports.addPost = createAsyncThunk('post/add', async (data, thunkAPI) => {
10 | return await delay(500, data);
11 | });
12 |
--------------------------------------------------------------------------------
/5.react-redux-toolkit/actions/user.js:
--------------------------------------------------------------------------------
1 | const { createAsyncThunk } = require('@reduxjs/toolkit');
2 |
3 | const delay = (time, value) => new Promise((resolve, reject) => {
4 | setTimeout(() => {
5 | resolve(value);
6 | }, time);
7 | });
8 |
9 | exports.logIn = createAsyncThunk('user/logIn', async (data, thunkAPI) => {
10 | // throw new Error('비밀번호가 틀렸습니다.');
11 | return await delay(500,{
12 | userId: 1,
13 | nickname: 'zerocho'
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/5.react-redux-toolkit/client.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { Provider } from 'react-redux';
4 |
5 | import store from './store';
6 | import App from './App';
7 |
8 | ReactDOM.render(
9 |
10 |
11 | ,
12 | document.querySelector('#root'),
13 | );
14 |
--------------------------------------------------------------------------------
/5.react-redux-toolkit/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 리덕스연결
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/5.react-redux-toolkit/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "5.react-redux-toolkit",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "webpack serve --env development --hot"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "@reduxjs/toolkit": "^1.5.0",
13 | "react": "^17.0.1",
14 | "react-dom": "^17.0.1",
15 | "react-redux": "^7.2.2"
16 | },
17 | "devDependencies": {
18 | "@babel/core": "^7.12.10",
19 | "@babel/preset-env": "^7.12.11",
20 | "@babel/preset-react": "^7.12.10",
21 | "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
22 | "babel-loader": "^8.2.2",
23 | "react-refresh": "^0.11.0",
24 | "webpack": "^5.11.0",
25 | "webpack-cli": "^4.9.1",
26 | "webpack-dev-server": "^4.7.1"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/5.react-redux-toolkit/reducers/index.js:
--------------------------------------------------------------------------------
1 | const { combineReducers } = require('redux');
2 | const userSlice = require('./userSlice');
3 | const postSlice = require('./postSlice');
4 |
5 | module.exports = combineReducers({
6 | user: userSlice.reducer,
7 | post: postSlice.reducer,
8 | });
9 |
--------------------------------------------------------------------------------
/5.react-redux-toolkit/reducers/postSlice.js:
--------------------------------------------------------------------------------
1 | const { createSlice } = require('@reduxjs/toolkit');
2 | const { addPost } = require('../actions/post');
3 |
4 | const initialState = {
5 | list: [],
6 | };
7 |
8 | const postSlice = createSlice({
9 | name: 'post',
10 | initialState,
11 | reducers: {},
12 | extraReducers: (builder) => builder
13 | .addCase(addPost.pending, (state, action) => {
14 |
15 | })
16 | .addCase(addPost.fulfilled, (state, action) => {
17 | state.list.push(action.payload);
18 | })
19 | .addCase(addPost.rejected, (state, action) => {
20 |
21 | }),
22 | });
23 |
24 | module.exports = postSlice;
25 |
--------------------------------------------------------------------------------
/5.react-redux-toolkit/reducers/userSlice.js:
--------------------------------------------------------------------------------
1 | const { createSlice } = require('@reduxjs/toolkit');
2 | const { logIn } = require('../actions/user');
3 |
4 | const initialState = {
5 | isLoggingIn: false,
6 | data: null,
7 | };
8 |
9 | const userSlice = createSlice({
10 | name: 'user',
11 | initialState,
12 | reducers: {
13 | logOut(state, action) {
14 | state.data = null;
15 | }
16 | },
17 | extraReducers: (builder) => builder
18 | .addCase(logIn.pending, (state, action) => {
19 | state.data = null;
20 | state.isLoggingIn = true;
21 | })
22 | .addCase(logIn.fulfilled, (state, action) => {
23 | state.data = action.payload;
24 | state.isLoggingIn = false;
25 | })
26 | .addCase(logIn.rejected, (state, action) => {
27 | state.error = action.payload;
28 | })
29 | })
30 |
31 | module.exports = userSlice;
32 |
--------------------------------------------------------------------------------
/5.react-redux-toolkit/store.js:
--------------------------------------------------------------------------------
1 | const { configureStore } = require('@reduxjs/toolkit');
2 |
3 | const reducer = require('./reducers');
4 |
5 | const firstMiddleware = () => (next) => (action) => {
6 | console.log('로깅', action);
7 | next(action);
8 | };
9 |
10 | const store = configureStore({
11 | reducer,
12 | middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(firstMiddleware),
13 | });
14 |
15 | module.exports = store;
16 |
--------------------------------------------------------------------------------
/5.react-redux-toolkit/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
3 |
4 | module.exports = {
5 | name: 'react-redux-toolkit',
6 | mode: 'development',
7 | devtool: 'eval',
8 | resolve: {
9 | extensions: ['.js', '.jsx'],
10 | },
11 | entry: {
12 | app: './client',
13 | },
14 | module: {
15 | rules: [{
16 | test: /\.jsx?$/,
17 | loader: 'babel-loader',
18 | options: {
19 | presets: [
20 | ['@babel/preset-env', {
21 | targets: {browsers: ['last 2 chrome versions']},
22 | debug: true,
23 | }],
24 | '@babel/preset-react',
25 | ],
26 | plugins: ["react-refresh/babel"]
27 | },
28 | exclude: path.join(__dirname, 'node_modules'),
29 | }],
30 | },
31 | plugins: [
32 | new ReactRefreshWebpackPlugin()
33 | ],
34 | output: {
35 | path: path.join(__dirname, 'dist'),
36 | filename: '[name].js',
37 | publicPath: '/dist',
38 | },
39 | devServer: {
40 | devMiddleware: { publicPath: '/dist' },
41 | static: { directory: path.resolve(__dirname) },
42 | hot: true
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/6.mobx/index.js:
--------------------------------------------------------------------------------
1 | const { observable, autorun, reaction, action, runInAction, computed } = require('mobx');
2 |
3 | const state = observable({
4 | compA: 'a',
5 | compB: 12,
6 | compC: null,
7 | });
8 |
9 | autorun(() => {
10 | console.log('A changed', state.compA);
11 | console.log('C changed', state.compC);
12 | });
13 |
14 | reaction(() => {
15 | return state.compB;
16 | }, () => {
17 | console.log('B reaction', state.compB);
18 | });
19 |
20 | runInAction(() => {
21 | state.compA = 'c';
22 | state.compB = 25;
23 | state.compC = 'c';
24 | });
25 |
26 | runInAction(() => {
27 | state.compC = 'd';
28 | });
29 |
--------------------------------------------------------------------------------
/6.mobx/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "5.mobx",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "mobx": {
8 | "version": "6.0.4",
9 | "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.0.4.tgz",
10 | "integrity": "sha512-wT2QJT9tW19VSHo9x7RPKU3z/I2Ps6wUS8Kb1OO+kzmg7UY3n4AkcaYG6jq95Lp1R9ohjC/NGYuT2PtuvBjhFg=="
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/6.mobx/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "6.mobx",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "node index"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "mobx": "^6.0.4"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/7.mobx/index.js:
--------------------------------------------------------------------------------
1 | const { observable, autorun, reaction, action, runInAction } = require('mobx');
2 |
3 | const userState = observable({
4 | isLoggingIn: true,
5 | data: null,
6 | });
7 |
8 | const postState = observable([]);
9 |
10 | runInAction(() => {
11 | postState.push({ id: 1, content: '안녕하세요.' });
12 | userState.data = {
13 | id: 1,
14 | nickname: 'zerocho',
15 | };
16 | });
17 |
--------------------------------------------------------------------------------
/7.mobx/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "7.mobx",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "mobx": {
8 | "version": "6.0.4",
9 | "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.0.4.tgz",
10 | "integrity": "sha512-wT2QJT9tW19VSHo9x7RPKU3z/I2Ps6wUS8Kb1OO+kzmg7UY3n4AkcaYG6jq95Lp1R9ohjC/NGYuT2PtuvBjhFg=="
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/7.mobx/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "7.mobx",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {},
7 | "author": "",
8 | "license": "ISC",
9 | "dependencies": {
10 | "mobx": "^6.0.4"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/8.mobx-react/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { observer } from 'mobx-react';
3 | import { observable } from 'mobx';
4 |
5 | import { userStore, postStore } from './store';
6 |
7 | class App extends Component {
8 | state = observable({
9 | name: '',
10 | password: '',
11 | });
12 |
13 | onClick = () => {
14 | userStore.logIn({
15 | nickname: 'zerocho',
16 | password: '비밀번호',
17 | });
18 | };
19 |
20 | onLogout = () => {
21 | userStore.logOut();
22 | };
23 |
24 | onChangeName = (e) => {
25 | this.state.name = e.target.value;
26 | };
27 |
28 | onChangePassword = (e) => {
29 | this.state.password = e.target.value;
30 | };
31 |
32 | render() {
33 | return (
34 |
35 | {userStore.isLoggingIn
36 | ?
로그인 중
37 | : userStore.data
38 | ?
{userStore.data.nickname}
39 | : '로그인 해주세요.'}
40 | {!userStore.data
41 | ?
42 | :
}
43 |
{postStore.data.length}
44 |
45 |
46 |
47 | );
48 | }
49 | }
50 |
51 | export default observer(App);
52 |
--------------------------------------------------------------------------------
/8.mobx-react/Context.jsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { userStore, postStore } from './store';
3 |
4 | export const storeContext = React.createContext({
5 | userStore,
6 | postStore,
7 | });
8 |
9 | export const StoreProvider = ({ children }) => {
10 | return (
11 |
12 | {children}
13 |
14 | );
15 | };
16 |
17 | export default StoreProvider;
18 |
--------------------------------------------------------------------------------
/8.mobx-react/client.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 | ,
7 | document.querySelector('#root'),
8 | );
9 |
--------------------------------------------------------------------------------
/8.mobx-react/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 리덕스연결
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/8.mobx-react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "8.mobx-react",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "webpack serve --env development"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "@babel/plugin-proposal-class-properties": "^7.12.1",
13 | "mobx": "^6.0.4",
14 | "mobx-react": "^7.0.5",
15 | "react": "^17.0.1",
16 | "react-dom": "^17.0.1"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.12.9",
20 | "@babel/preset-env": "^7.12.7",
21 | "@babel/preset-react": "^7.12.7",
22 | "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
23 | "babel-loader": "^8.2.2",
24 | "react-refresh": "^0.11.0",
25 | "webpack": "^5.9.0",
26 | "webpack-cli": "^4.2.0",
27 | "webpack-dev-server": "^4.7.1"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/8.mobx-react/store.js:
--------------------------------------------------------------------------------
1 | const { observable } = require('mobx');
2 |
3 | const userStore = observable({
4 | isLoggingIn: false,
5 | data: null,
6 | logIn(data) {
7 | this.isLoggingIn = true;
8 | setTimeout(() => {
9 | this.data = data;
10 | this.isLoggingIn = false;
11 | postStore.data.push(1);
12 | }, 2000);
13 | },
14 | logOut() {
15 | this.data = null;
16 | },
17 | });
18 |
19 | const postStore = observable({
20 | data: [],
21 | addPost(data) {
22 | this.data.push(data);
23 | },
24 | });
25 |
26 | export { userStore, postStore };
27 |
--------------------------------------------------------------------------------
/8.mobx-react/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
3 |
4 | module.exports = {
5 | name: 'mobx-react',
6 | mode: 'development',
7 | devtool: 'eval',
8 | resolve: {
9 | extensions: ['.js', '.jsx'],
10 | },
11 | entry: {
12 | app: './client',
13 | },
14 | module: {
15 | rules: [{
16 | test: /\.jsx?$/,
17 | loader: 'babel-loader',
18 | options: {
19 | presets: [
20 | ['@babel/preset-env', {
21 | targets: {browsers: ['last 2 chrome versions']},
22 | debug: true,
23 | }],
24 | '@babel/preset-react',
25 | ],
26 | plugins: ["react-refresh/babel", "@babel/plugin-proposal-class-properties"],
27 | },
28 | exclude: path.join(__dirname, 'node_modules'),
29 | }],
30 | },
31 | plugins: [
32 | new ReactRefreshWebpackPlugin()
33 | ],
34 | output: {
35 | path: path.join(__dirname, 'dist'),
36 | filename: '[name].js',
37 | publicPath: '/dist/',
38 | },
39 | devServer: {
40 | devMiddleware: { publicPath: '/dist' },
41 | static: { directory: path.resolve(__dirname) },
42 | hot: true
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/9.mobx-react/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from 'react';
2 | import { observer, useLocalObservable } from 'mobx-react';
3 |
4 | import useStore from './useStore';
5 |
6 | const App = () => {
7 | const { userStore, postStore } = useStore();
8 |
9 | const state = useLocalObservable(() => ({
10 | name: '',
11 | password: '',
12 | onChangeName(e) {
13 | this.name = e.target.value;
14 | },
15 | onChangePassword(e) {
16 | this.password = e.target.value;
17 | }
18 | }));
19 |
20 | const onClick = useCallback(() => {
21 | userStore.logIn({
22 | nickname: 'zerocho',
23 | password: '비밀번호',
24 | });
25 | }, []);
26 |
27 | const onLogout = useCallback(() => {
28 | userStore.logOut();
29 | }, []);
30 |
31 | return (
32 |
33 | {userStore.isLoggingIn
34 | ?
로그인 중
35 | : userStore.data
36 | ?
{userStore.data.nickname}
37 | : '로그인 해주세요.'}
38 | {!userStore.data
39 | ?
40 | :
}
41 |
{postStore.postLength}
42 |
43 |
44 |
45 | );
46 | };
47 |
48 | export default observer(App);
49 |
--------------------------------------------------------------------------------
/9.mobx-react/Context.jsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { userStore, postStore } from './store';
3 |
4 | export const storeContext = React.createContext({
5 | userStore,
6 | postStore,
7 | });
8 |
9 | export const StoreProvider = ({ children }) => {
10 | return (
11 |
12 | {children}
13 |
14 | );
15 | };
16 |
17 | export default StoreProvider;
18 |
--------------------------------------------------------------------------------
/9.mobx-react/client.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | import App from './App';
5 |
6 | ReactDOM.render(
7 | ,
8 | document.querySelector('#root'),
9 | );
10 |
--------------------------------------------------------------------------------
/9.mobx-react/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 리덕스연결
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/9.mobx-react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "9.mobx-react",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "webpack serve --env development --hot"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "@babel/plugin-proposal-class-properties": "^7.12.1",
13 | "mobx": "^6.0.4",
14 | "mobx-react": "^7.0.5",
15 | "react": "^17.0.1",
16 | "react-dom": "^17.0.1"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.12.9",
20 | "@babel/preset-env": "^7.12.7",
21 | "@babel/preset-react": "^7.12.7",
22 | "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
23 | "babel-loader": "^8.2.2",
24 | "react-refresh": "^0.11.0",
25 | "webpack": "^5.9.0",
26 | "webpack-cli": "^4.2.0",
27 | "webpack-dev-server": "^4.7.1"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/9.mobx-react/store.js:
--------------------------------------------------------------------------------
1 | const { observable, action } = require('mobx');
2 |
3 | const postStore = observable({
4 | data: [],
5 | addPost(data) {
6 | this.data.push(data);
7 | },
8 | get postLength() {
9 | return this.data.length;
10 | },
11 | });
12 |
13 | const userStore = observable({
14 | isLoggingIn: false,
15 | data: null,
16 | logIn(data) {
17 | this.isLoggingIn = true;
18 | setTimeout(action(() => {
19 | this.data = data;
20 | this.isLoggingIn = false;
21 | postStore.data.push(1);
22 | }), 2000);
23 | },
24 | logOut() {
25 | this.data = null;
26 | },
27 | });
28 |
29 | export { userStore, postStore };
30 |
--------------------------------------------------------------------------------
/9.mobx-react/useStore.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { storeContext } from './Context';
3 | import { userStore, postStore } from './store';
4 |
5 | function useStore() {
6 | // const store = React.useContext(storeContext);
7 | // if (!store) {
8 | // // this is especially useful in TypeScript so you don't need to be checking for null all the time
9 | // throw new Error('useStore must be used within a StoreProvider.');
10 | // }
11 | return { userStore, postStore };
12 | }
13 |
14 | export default useStore;
15 |
--------------------------------------------------------------------------------
/9.mobx-react/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
3 |
4 | module.exports = {
5 | name: 'lotto-dev',
6 | mode: 'development',
7 | devtool: 'eval',
8 | resolve: {
9 | extensions: ['.js', '.jsx'],
10 | },
11 | entry: {
12 | app: './client',
13 | },
14 | module: {
15 | rules: [{
16 | test: /\.jsx?$/,
17 | loader: 'babel-loader',
18 | options: {
19 | presets: [
20 | ['@babel/preset-env', {
21 | targets: {browsers: ['last 2 chrome versions']},
22 | debug: true,
23 | }],
24 | '@babel/preset-react',
25 | ],
26 | plugins: ["react-refresh/babel"],
27 | },
28 | exclude: path.join(__dirname, 'node_modules'),
29 | }],
30 | },
31 | plugins: [
32 | new ReactRefreshWebpackPlugin()
33 | ],
34 | output: {
35 | path: path.join(__dirname, 'dist'),
36 | filename: '[name].js',
37 | publicPath: '/dist',
38 | },
39 | devServer: {
40 | devMiddleware: { publicPath: '/dist' },
41 | static: { directory: path.resolve(__dirname) },
42 | hot: true
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 2021년 1월 부로 강좌가 리뉴얼되었습니다.
2 |
3 | 2020년까지의 코드를 원하시는 분은 old 브랜치로 가시면 됩니다. 다만, 최신 코드를 실무에 적용하시는 것을 강력 추천드립니다.
4 |
--------------------------------------------------------------------------------