프로젝트 구축을 위한 환경설정을 하겠습니다
1. 서브라임 텍스트(text editor)가 준비되어야 하며
2. node.js를 설치해야 합니다
node.js 설치를 진행해보겠습니다
https://nodejs.org/ko/download/
node.js를 설치하셨다면
node CLI를 이용하여 리액트 프로젝트를 생성하겠습니다
우선 다음 npm CLI를 통하여 create-react-app를 설치해야합니다
npm install -g create-react-app
그리고 다음 CLI로 리액트 프로젝트를 생성합니다
create-react-app {생성할 프로젝트명}
프로젝트가 준비되었다면
프로젝트를 sublime에 연결하겠습니다
생성한 프로젝트가 이상이 없는지 확인해보겠습니다
cmd에서 다음 CLI를 입력합니다
cd {프로젝트 디렉토리\src} & npm start
그리고 브라우저에서 localhost:3000 로 이동하면
다음과 같이 리액트 프로젝트 초기화면을 확인 할 수 있습니다
프로젝트를 빌드하기 위해 매번 cmd를 사용해야하는 번거로움이 있습니다
편의를 위해 단축키 이용한 빌드를 구성해보겠습니다
우선 cmd창에서 다음 npm을 설치해줍니다
npm install react-scripts@2.1.8
이왕 npm을 설치하는 김에 다음 npm들도 설치해줍니다
npm i npm-cli --save
npm i jquery
이렇게 jquery를 설치하셨다면
package.json에 dependencies에 jquery를 추가하고
jquery를 사용할 프로젝트에 $를 import하시면 되겠습니다
단축키로 빌드하기 위해선
scripts 부분은 다음과 같이 작성되어야 합니다
후 단축키 빌드를 위한 sublime-build 파일을 작성해야합니다
다음과 같이 진행합니다
여기까지 되셨다면
간편하게 Ctrl+B 단축키로 이 처럼 브라우저를 띄울 수 있습니다
여기까지 되셨다면 다음과 같이 프로젝트를 구성해보겠습니다
위와 같이 dependency들을 추가해야합니다
이를 위하여 yarn CLI를 사용하도록 하겠습니다
npm i -g yarn
으로 yarn을 설치합니다
그리고 cmd에서 해당 프로젝트로 cd(change directory) 후
yarn add {추가할 패키지} 로 다음 dependency들을 추가합니다
ex) yarn add express
express
mongoose
express-graphql
graphql
cors
본격적으로 프로젝트에 다음 소스들을 추가하겠습니다
layout.jsx
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
|
import React from 'react';
import $ from 'jquery';
export class Head extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div id="head">
<a href="#"><h2>TEST Site</h2></a>
<button>메뉴6</button>
</div>
);
}
}
export class Nav extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div id="nav">
<ul className="hlist">
<li><a href="#">메뉴2</a></li>
<li><a href="#">메뉴3</a></li>
<li><a href="#">메뉴4</a></li>
<li><a href="#">메뉴5</a></li>
</ul>
</div>
);
}
}
export class Main extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div id="main">
<div id="inner">
</div>
</div>
);
}
}
export class Foot extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div id="foot">
<p>copyright by webman</p>
</div>
);
}
}
export default class Layout extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Head />
<Nav />
<Foot />
</div>
);
}
}
|
main.jsx
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
|
import React from 'react';
import $ from 'jquery';
function Bar() {
const style1 = {
display:'inline-block',
textAlign:'right',
borderTopLeftRadius:5,
paddingTop:3+'px',
paddingLeft:5+'px',
width:100+'%',
backgroundColor:'black'
};
return (
<div style={style1}>
<span>hi</span>
<ul className="hlist">
<li><a href="#" onClick={function(){doAjax('c.do');}}>C</a></li>
<li><a href="#" onClick={function(){doAjax('u.do');}}>U</a></li>
<li><a href="#" onClick={function(){r();}}>R</a></li>
<li><a href="#" onClick={function(){doAjax('d.do');}}>D</a></li>
</ul>
</div>
);
}
function Input() {
return (
<div style={{float:'right',height:50+'%',width:46+'%',margin:1.8+'%'}}>
<ul>
<li>
<label>id</label><input type="" name="id" placeholder="Id는 자동생성 됩니다" readOnly/>
</li>
<li>
<label>firstName</label><input type="" name="firstName"/>
</li>
<li>
<label>lastName</label><input type="" name="lastName"/>
</li>
<li>
<label>ref</label><input type="" name=""/>
</li>
</ul>
</div>
);
}
function ListItems(list) {
const array=[];
$.each(list, function(idx, val) {
for(var i=0; i<list.length; i++) {
array.push(list[i]);
}
});
return (
<tbody>
{array.map(function(value, index){
return (
<tr>
<td className="default" key={index}>{value.firstName}</td>
<td className="default" key={index}>{value.lastName}</td>
<td className="default" key={index}></td>
</tr>
);
})}
</tbody>
);
}
function List({list}) {
return (
<div>
<form name="form" id="form" style={{margin:0+'px'}}>
<div style={{display:'inline-block',height:100+'%',width:100+'%'}}>
<table cellPadding="0" cellSpacing="0">
<tr>
<th>id</th>
<th>firstName</th>
<th>lastName</th>
<th>ref</th>
</tr>
{/*<ListItems data={list}/>*/}
<tbody>
<tr>
<td className="default" ></td>
<td className="default" >{list}</td>
<td className="default" ></td>
<td className="default" ></td>
</tr>
<tr>
<td className="default" ></td>
<td className="default" >{list}</td>
<td className="default" ></td>
<td className="default" ></td>
</tr>
</tbody>
</table>
<Input/>
</div>
</form>
</div>
);
}
export default class Main1 extends React.Component {
constructor(props) {
super(props);
}
Bar() {}
Input() {}
List(props) {}
render() {
return (
<div id="main">
<div id="inner">
<Bar />
{/*{this.props.list}*/}
</div>
</div>
);
}
}
function r() {
$('input[name=id]').val($('.clicked:eq(0)').html());
$('input[name=firstName]').val($('.clicked:eq(1)').html());
$('input[name=lastName]').val($('.clicked:eq(2)').html());
}
function ajaxList() {
$.ajax({
url: "/list.do", // 클라이언트가 HTTP 요청을 보낼 서버의 URL 주소
type: "GET", // HTTP 요청 메소드(GET, POST 등)
dataType: "json", // 서버에서 보내줄 데이터의 타입
success:function(returnMap){
alert(returnMap.dbData);
ListItems(returnMap.dbData);
},
error:function(jqXHR, status, errorThrown){
}
});
}
function doAjax(url){
var params = $("#form").serialize();
$.ajax({
url: "/"+url, // 클라이언트가 HTTP 요청을 보낼 서버의 URL 주소
data: params, // HTTP 요청과 함께 서버로 보낼 데이터
type: "POST", // HTTP 요청 메소드(GET, POST 등)
dataType: "json", // 서버에서 보내줄 데이터의 타입
success:function(returnMap){
ajaxList();
},
error:function(jqXHR, status, errorThrown){
ajaxList();
}
});
}
$(document).ready(function() {
//ajaxList();
$("#main td").mouseover(function(e){
$(e.target).parent().children('td').removeClass('default');
$(e.target).parent().children('td').addClass('hover');
});
$("#main td").mouseout(function(e){
$(e.target).parent().children('td').removeClass('hover');
$(e.target).parent().children('td').addClass('default');
});
$(document).click(function(e) {
if($(e.target).is("#main td")) {
if($(e.target).hasClass('clicked')) {
$('td').removeClass();
$('td').addClass('default');
} else {
$('td').removeClass();
$('td').addClass('default');
$(e.target).parent().children('td').removeClass();
$(e.target).parent().children('td').addClass('clicked');
//$('input[name=id]').val($(e.target).siblings(':nth-child(1)').html());
//$('input[name=firstName]').val($(e.target).siblings(':nth-child(2)').html());
//$('input[name=lastName]').val($(e.target).siblings(':nth-child(3)').html());
}
} else {
}
});
});
|
main.css
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
#body {
height:100%;
margin:0;
}
span, label {
display:inline-block;margin:6px 25px 8px 25px;
font-size:18px;
}
input {
padding:5px;margin-top:-15px
}
#head {
width:100%;height:60px;
background-color:white;
}
#head a {
display: inline-block;
float: left;margin-left:10%;
text-decoration: none;color:black;
}
#head button {
float: right;margin: 8px 10%;
font-size: 20px;padding:8px 15px;
background-color: transparent;
border:0.1em solid #333;border-radius:8px;
}
#head button:hover {;
background-color: #333;
color:white;
}
#nav {
width:100%;
border-top:1px solid lightgrey;
background-color: #333;
text-align:center;
margin-bottom: -5px;
}
ul {
display:inline-block;
list-style-type:none;
margin:0;padding:0;overflow:hidden;
background-color: transparent;
}
li {
float: left;
}
li a {
display: block;
text-align: center;
padding: 14px 26px;
text-decoration: none;
color: black;
}
li a:hover {
}
#nav ul {
}
#nav li {
}
#nav li a {
color: white;
}
#nav li a:hover {
background-color: #111;
border-bottom:0.1em solid blue;
}
#main{
width:80%;height:76%;padding:2% 10% 2% 10%;
background-color:lightgrey;
}
#inner {
min-height: 500px;
width:95%;height:90%;min-width:800px;
padding:2% 3%;border-radius:0.5%;
background-color:white;
}
#inner table {
display:inline-block;
background-color:lightgrey;
border-collapse: collapse;
font-size: 18px;width:50%;height:58%;
}
#inner th {
padding:10px 24px;
background-color: grey
}
#inner td {
padding:10px 24px;
border-bottom:1px solid grey;
}
#inner ul {
}
#inner li {
}
#inner li a {
border:1.5px solid grey;
color:white;
border-radius:5px;
margin:8px 8px 0px 8px;
}
#inner li a:hover {
background-color: grey;
}
#foot {
display: table;
width:96.1%;height:150px;padding:2%;
background-color:grey;
color:rgb(32,32,32);
}
#foot p {
display: table-cell;
line-height: 1.5;
text-align: right;
vertical-align: bottom;
}
|
cs |
style.css
1
2
3
4
5
6
7
8
9
10
|
.default {
background-color: white
}
.hover {
background-color: lightblue
}
.clicked {
background-color: blue;
color:white;
}
|
mongoGraphQl.js
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
const Express = require("express");
const ExpressGraphQL = require("express-graphql");
const Mongoose = require("mongoose");
const {
GraphQLID,
GraphQLString,
GraphQLList,
GraphQLNonNull,
GraphQLObjectType,
GraphQLSchema
} = require("graphql");
let dbConfig = {
user: "test" || "realserver",
password: "test" || "realserver",
server: "url:port" || "realserver",
dbname: "test" || "realserver",
standalone: "localhost:27017/test"
}
async function dbConn() {
var conn = Mongoose.connect("mongodb://"+dbConfig.standalone, {useUnifiedTopology: true, useNewUrlParser: true,})
//.then(()=>{ console.log('Database Connected') })
//.catch(err=>{ console.log('Connection error: '+err.message) })
}
/*
});
});
Mongoose.connection.db.collection("member", function(err, collection){
collection.find({}).toArray(function(err, data){
})
});
});*/
mid: String,
name: String,
description: String
}), "member");
const MemberType = new GraphQLObjectType({
name: "Member",
fields: {
id: { type: GraphQLID },
mid: { type: GraphQLString },
name: { type: GraphQLString },
description: { type: GraphQLString }
}
});
/*
{
members(mid:"2"){
mid,
name,
description
}
member(id: "5e7f47ea70dc8717101826cf") {
name
}
}
mutation createMember {
member(mid: "1", name: "hi", description: "hello") {
mid,
name,
description
}
}
*/
const testData = [{_id:"12323", mid:"1", name:"hi", description:"good day"},
{_id:"23123", mid:"2", name:"hello", description:"good morning"}];
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: "Query",
fields: {
members: {
type: GraphQLList(MemberType),
args: {
mid: { type: GraphQLString }
},
resolve: (root, args, context, info) => {
//return testData;
}
},
member: {
type: MemberType,
args: {
id: { type: GraphQLNonNull(GraphQLID) }
},
resolve: (root, args, context, info) => {
//return testData[0];
return MemberModel.findById(args.id).exec();
}
}
}
}),
mutation: new GraphQLObjectType({
name: "Mutation",
fields: {
member: {
type: MemberType,
args: {
mid: { type: GraphQLNonNull(GraphQLString) },
name: { type: GraphQLNonNull(GraphQLString) },
description: { type: GraphQLNonNull(GraphQLString) }
},
resolve: (root, args, context, info) => {
var member = new MemberModel(args);
return member.save();
}
}
}
})
});
var app = Express();
const cors = require('cors');
const port = 4000;
// Start the webserver
async function WebServerOn() {
app.use("/graphql", ExpressGraphQL({
schema: schema,
graphiql: true
}));
console.log("Listening at : "+port+"...");
});
}
// Do it
async function run() {
await dbConn();
await WebServerOn();
}
run();
|
apolloClient.js
1
2
3
4
5
6
7
|
import ApolloClient from 'apollo-boost';
export default new ApolloClient({
uri: "http://localhost:4000/graphql",
});
|
postViewer.js
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
|
import React from 'react';
import gql from 'graphql-tag';
import { Query } from 'react-apollo';
export const GET_MEMBERS = gql`
{
members{
mid,
name,
description
}
}
`;
export default () => (
<Query query={GET_MEMBERS}>
{({ loading, data, error }) => {
if (loading) return "gql Loading...";
if (error) return `gql Loading failed[ ${error.message} ]`;
return (
<table>
<thead>
<tr>
<th>mid</th>
<th>name</th>
<th>description</th>
</tr>
</thead>
<tbody>
<tr key={member.mid}>
<td>{member.mid}</td>
<td>{member.name}</td>
<td>{member.description}</td>
</tr>
))}
</tbody>
</table>
);
}}
</Query>
);
|
여기까지 완료하셨다면 Ctrl+B 단축키를 사용하여
다음과 같은 화면을 확인 할 수 있습니다
이젠 DB연동을 위한 작업을 진행해보겠습니다
먼저 몽고DB를 설치하겠습니다.
key:value 형태로 document를 관리하는 NoSQL형태의 DB입니다
https://www.mongodb.com/download-center/community
설치를 완료하셨다면
test라는 database를 생성한 후
member라는 컬렉션을 생성하겠습니다
그리고
member 컬렉션에 접근하여
ADD DATA를 통하여 (document형) 데이터를 추가해보겠습니다
데이터는 다음과 같은 JSON 타입 형태로 추가합니다
1
2
3
4
5
6
|
{
"name" : "first",
"description" : "test data"
}
|
여기까지 완료되셨다면
package.json에서 scripts 부분을 다음과 같이 수정합니다
1
2
3
4
5
6
7
|
"scripts": {
"start": "react-scripts start && node src/services/servers/mongoGraphQl",
"watch:server": "nodemon --watch src/services/servers/mongoGraphQl",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "taskkill -F -IM node.exe"
},
|
cs |
비로서 graphQL+MongoDB 서버를 구축하였습니다
허나 서버를 이용하여 화면에 출력하기 위해선 추가적인 작업이 필요합니다
Apollo라는 패키지를 이용하여 작업을 진행해보겠습니다
다음 패키지를 추가적으로
yarn add 해줍니다
graphql-tag
apollo-boost
react-apollo
또 다음 소스들을 수정합니다
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { ApolloProvider } from 'react-apollo';
import client from './services/servers/apolloClient';
<React.StrictMode>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</React.StrictMode>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
serviceWorker.unregister();
|
App.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
import React from 'react';
import './App.css';
import {Head, Nav, Main, Foot} from './layout/layout';
import Main1 from './layout/main';
import './resources/css/style.css';
import './resources/css/main.css';
import PostViewer from './services/postViewer';
function App() {
return (
<div id="body" className="App">
<Head />
<Nav />
<PostViewer />
<Foot />
</div>
);
}
export default App;
|
후 서버를 실행하게되면
다음과 같은 화면을 확인 할 수 있습니다
다음 시간에는 이어서 main의 CURD 기능들을
좀 더 구체화하도록 하겠습니다
'웹개발 > client-side' 카테고리의 다른 글
1탄) 구름IDE에서 개발하고 띄워보기 (0) | 2021.05.09 |
---|---|
1탄) python + django 웹 개발 환경 세팅 (0) | 2020.03.29 |
1탄) .NET Core, WPF GUI 프로그래밍 스터디 (0) | 2020.03.14 |
3탄) PHP, DB 연동 및 배포 (0) | 2020.03.13 |
2탄) PHP, DB 연동 및 배포 (0) | 2020.03.12 |