Meteor와 React 환경을 좀 더 Production에 가깝게 만들어 본다.
- Insecure 제거 및 allow/deny 설정
- React의 Refs에 대해 변경
- Meteor methods/call 사용한 목록 조회
- SimpleSchema 적용
- 적용소스
Insecure 패키지 제거 및 Allow/Deny 설정
모든 컬렉션에 대한 자동 subscribe에 대한 autopublish 패키지는 이미 제거를 했고, 다음으로 insecure 패키지를 제거하여 컬렉션의 접근 권한을 제어하는 allow/deny를 설정한다.
$ meteor remove insecure
insecure를 제거하게 되면 read만 가능하고 update, delete, insert가 불가능하다. 따라서 권한부분은 서버에서 실행되기 때문에 allow/deny설정을 Meteor.isServer블럭안에서 해주어야 한다.
- allow: true로 설정된 것만 가능하고 그외는 모두 불가능하다. (가능한 것이 적으면 allow 설정)
- deny: true로 서정된 것만 불가능하고 그외는 모두 가능하다. (불가능한 것이 적으면 deny 설정)
// api/links.ts
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
const Links = new Mongo.Collection('links');
if (Meteor.isServer) {
Meteor.publish('links', () => Links.find());
Links.allow({
insert (userId: string, doc: any) {
console.log('insert doc:', doc);
return (userId && doc.owner === userId);
},
remove(userId: string, doc: any) {
console.log('delete doc:', doc);
return (userId && doc.owner === userId);
}
})
}
export default Links;
allow에서 owner 아이디를 비교하므로 epic에서 owner 값을 설정한다.
// imports/ui/pages/link/link.epic.ts
const addLink: Epic = (
action$,
store
) =>
action$.pipe(
filter(isOfType(actions.ADD_REQUEST)),
switchMap(action => {
const { title, url } = action.payload;
const owner = Meteor.userId();
return insertCollection(Links, { title, url, owner, createdAt: new Date() })
}),
...
);
userId는 로그인을 하게되면 이미 서버와 동기화 되어 서버가 가지고 있는 userId를 첫번째 파라미터로 넘겨준다. 두번째 파라미터 doc은 insert할 때 입력한 파라미터값 이고, remove의 doc은 저장된 몽고디비의 document이다.
// meteor run을 수행한 콘솔창에 찍힌다.
=> Meteor server restarted
I20181123-15:00:36.619(9)? insert doc: { title: 'google2',
I20181123-15:00:36.676(9)? url: 'http://www.google.com',
I20181123-15:00:36.676(9)? owner: 'AmMBJzZ33Nc8Bsqhe',
I20181123-15:00:36.676(9)? createdAt: 2018-11-23T06:00:36.611Z }
I20181123-15:00:39.955(9)? remove doc: { _id: 'Zrb8jGSpt2AyRn4Q3',
I20181123-15:00:39.955(9)? title: 'google2',
I20181123-15:00:39.955(9)? url: 'http://www.google.com',
I20181123-15:00:39.955(9)? owner: 'AmMBJzZ33Nc8Bsqhe',
I20181123-15:00:39.955(9)? createdAt: 2018-11-23T06:00:36.611Z }
Form 입력의 React의 Refs 변경
React에서 DOM객체 접근 방법은 3가지 이고, string과 콜백펑션말고 16.3 버전이후 나온 createRefs()를 사용한다.
- ref="string"
- ref={callback-function}
- createRefs()
DOM 객체를 받을 변수를 선언하고, ref={this.변수}로 할당한다. 값 접근은 this.변수.current 객체를 통한다.
// imports/ui/pages/Login.tsx
.. 중략 ..
export default class Login extends React.Component<LoginProps, LoginState> {
email: any = React.createRef();
password: any = React.createRef();
constructor(props) {
super(props);
this.state = {
error: ''
};
}
onLogin = (e: any) => {
e.preventDefault();
let email = this.email.current.value.trim();
let password = this.password.current.value.trim();
Meteor.loginWithPassword({ email }, password, (err) => {
if (err) {
this.setState({ error: err.reason });
} else {
this.setState({ error: '' });
}
});
}
public render() {
return (
<div>
<h1>Login to short Link</h1>
{this.state.error ? <p>{this.state.error} </p> : undefined}
<form onSubmit={this.onLogin}>
<input type="email" ref={this.email} name="email" placeholder="Email" />
<input type="password" ref={this.password} name="password" placeholder="Password" />
<button>Login</button>
</form>
<Link to="/signup">Have a account?</Link>
</div>
);
}
}
Signup.tsx과 AddLink.tsx도 동일하게 변경한다.
Meteor methods/call을 사용한 저장/삭제
imports/sdk/utils/ddp.util.ts 에 method call에 대한 observable 반환 메소드 추가
export function insertCall(methodName: string, params: any): Observable<RequestModel> {
return from(new Promise((resolve, reject) => {
Meteor.call(methodName, params, (error, result) => {
if (error) {
reject({ error: true, result: { ...error }, params: { ...params } });
}
if (typeof result === 'string' || typeof result === 'number') {
resolve({ success: true, result, params: { ...params } });
} else {
resolve({ success: true, result: { ...result }, params: { ...params } });
}
});
}));
}
export function removeCall(methodName: string, _id: string): Observable<RequestModel> {
return from(new Promise((resolve, reject) => {
Meteor.call(methodName, _id, (error, result) => {
if (error) {
reject({ error: true, result: { ...error }, params: { _id } });
}
if (typeof result === 'string' || typeof result === 'number') {
resolve({ success: true, result, params: { _id } });
} else {
resolve({ success: true, result: { ...result }, params: { _id } });
}
});
}));
}
imports/api/links.ts 에서 Meteor.methods를 추가한다.
if (Meteor.isServer) {
...
Meteor.methods({
insertLink(params: any) {
if (!this.userId) {
throw new Meteor.Error('Please login');
}
return Links.insert(params);
},
removeLink(_id: string) {
if (!this.userId) {
throw new Meteor.Error('Please login');
}
return Links.remove(_id);
}
});
}
imports/ui/pages/link/link.epic.ts 에서 collection 을 호출하지 않고, Meteor.call을 호출 한다. takeUntil은 브라우져 이동등을 할때 호출을 끊는 역할을 한다. 현재는 주석처리로 미구현상태임.
const addLink: Epic = (
action$,
store
) =>
action$.pipe(
filter(isOfType(actions.ADD_REQUEST)),
switchMap(action => {
const { title, url } = action.payload;
const owner = Meteor.userId();
return insertCall('insertLink', { title, url, owner, createdAt: new Date() })
// return insertCollection(Links, { title, url, owner, createdAt: new Date() })
}),
map((response: RequestModel) => {
if (response.error) {
return actions.addLinkFailed({ ...response.result })
}
return actions.addLinkSuccess(response.result)
}),
// takeUntil(action$.pipe(
// filter(isOfType(actions.ADD_REQUEST))
// ))
);
const removeLink: Epic = (
action$,
store
) =>
action$.pipe(
filter(isOfType(actions.DELETE_REQUEST)),
switchMap(action => {
return removeCall('removeLink', action.payload);
// return removeCollection(Links, action.payload);
}),
map((response: RequestModel) => {
if (response.error) {
return actions.removeLinkFailed({ ...response.result, ...response.params })
}
return actions.removeLinkSuccess(response.params._id);
}),
// takeUntil(action$.pipe(
// filter(isOfType(actions.ADD_REQUEST))
// ))
);
SimpleSchema 적용하기
Validation을 위해 simple schema 패키지를 설치한다.
$ meteor npm install --save simpl-schema
/imports/api/links.ts 에 schema를 정의한다.
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';
const Links = new Mongo.Collection('links');
if (Meteor.isServer) {
...
const linkSchema = new SimpleSchema({
title: {
type: String,
min: 3
},
url: {
type: String
},
owner: {
type: String
},
createdAt: {
type: Date
}
});
Meteor.methods({
insertLink(params: any) {
if (!this.userId) {
throw new Meteor.Error('Please login');
}
try {
linkSchema.validate(params);
return Links.insert(params);
} catch(e) {
throw new Meteor.Error('no valid schema');
}
},
...
});
}
export default Links;
account에 대한 validate도 정의해 본다. imports/api/ 폴더아래에 account-validate.ts 파일을 생성하고 server/main.ts에서 import한다.
// account-validate.ts
import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import SimpleSchema from 'simpl-schema';
if (Meteor.isServer) {
Accounts.validateNewUser((user) => {
const email = user.emails[0].address;
try {
new SimpleSchema({
email: {
type: String,
regEx: SimpleSchema.RegEx.Email
}
}).validate({ email });
} catch(e) {
throw new Meteor.Error(400, e.message);
}
return true;
});
}
// server/main.ts
import { Meteor } from 'meteor/meteor';
import '../imports/api/links';
import '../imports/api/account-validate';
Meteor.startup(() => {
});
// Singup.tsx 파일에 테스트를 위해 noValidate를 추가한다.
<form onSubmit={this.onCreateAccount} noValidate>
<참조>
'Meteor > React + Meteor' 카테고리의 다른 글
[React + Meteor] 개발환경 설정 - 6 (0) | 2018.12.03 |
---|---|
[React + Meteor] 개발환경 설정 - 5 (0) | 2018.11.30 |
[React + Meteor] 개발환경 설정 - 3 (0) | 2018.11.22 |
[React + Meteor] 개발환경 설정 - 2 (0) | 2018.11.16 |
[React + Meteor] 개발환경 설정 - 1 (0) | 2018.11.13 |