4. 회원가입 기본

저번 강좌를 통해 기본적인 페이지 디자인과 페이지 이동(routing) 관련 기능은 완성했습니다. 그럼 이제 본격적으로 meteor와 관련된 기능들을 작성해 보도록 하겠습니다. 일단 Account에서 가장 기본이 되는 회원 가입부터 만들어 보도록 하겠습니다.

회원가입은 다음과 같이 몇가지 시나리오를 두고 진행하도록 하겠습니다.

1단계: 가장 기본적인 과정입니다. client페이지에서 가입정보를 폼으로 받고, 서버로 부터 회원가입 요청 후 가입완료 되는 형태의 진행이 되겠습니다.
2단계: 유효성검사(validate) 추가
3단계: 이메일 인증


4.1 기본 회원 가입

우선 register.js 를 열고 아래 소스를 입력해 줍니다.

client/register.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
Template.register.events({
'click button[name=btn-register]' (evt, tmpl)
{
evt.preventDefault(); //새로 고침 방지
var inputUsername = tmpl.find('input[name=user-email]').value;
var inputEmail= tmpl.find('input[name=user-email]').value;
var inputPassword = tmpl.find('input[name=user-password]').value;
var inputPasswordAgain = tmpl.find('input[name=user-passwordAgain]').value;
var inputFirstName = tmpl.find('input[name=user-firstName]').value;
var inputLastName = tmpl.find('input[name=user-lastName]').value;
var inputUser = {
username: inputUsername,
email: inputEmail,
password: inputPassword,
passwordAgain: inputPasswordAgain,
profile: {
firstName: inputFirstName,
lastName: inputLastName
}
}
//서버로의 로그인 호출
Meteor.call('register', inputUser, function (err)
{
if(!err)
{
Bert.alert( '가입에 성공했습니다. 가입한 메일로 인증을 받아주세요' , 'danger', 'growl-top-right' );
FlowRouter.go('/login');
}
else
{
Bert.alert( err.message , 'danger', 'growl-top-right' );
}
});
}
});

기본적으로 템플릿의 요소를 가지고 이벤트(마우스 click, over 등)를 일으키는 방법은 Template.템플릿이름.event({}) 이 같은 문법을 사용합니다. 그리고 click button[name=btn-register]' (evt, tmpl) 이 부분은 name=btn-register라는 선택자에 click 이벤트가 발생 했을 때 어떤 처리를 할 것인가에 대한 스크립트가 되겠습니다. 여기서 evt, tmpl 이라는 2가지 파라미터를 갖게 되는데요. evt는 위의 소스로 예를 들자면 실제 클릭이 일어난 버튼의 액션 되고, tmpl은 해당 이벤트가 일어나는 템플릿 정보로 tmpl을 이용하여 사용한 템플릿의 정보(ex: input 값)를 받아올 수 있습니다.

이를 바탕으로 아래와 같이 input 정보를 변수에 담을 수 있습니다.

1
var inputUsername = tmpl.find('input[name=user-email]').value;

var inputUser의 경우 내용은 아래와 같은 의미를 갖는데, 여기서 중요한 점은 username, email, password 는 meteor account의 기본 저장 형태이므로 꼭 이런 식으로 이름을 정하면 되고 기타 정보들은 profile아래에 사용자가 커스텀하게 원하는 정보를 입력해 둘 수 있습니다.

1
2
3
4
5
6
7
8
9
10
var inputUser = {
username: inputUsername, // 유저이름
email: inputEmail, // 유저 이메일
password: inputPassword, // 비밀번호(db에 저장될 대 암호화 됨)
passwordAgain: inputPasswordAgain, // 비밀번호 확인
profile: { // 위의 정보 이외의 정보는 profile에 저장하면 된다.
firstName: inputFirstName,
lastName: inputLastName
}
}

마지막으로 Meteor.call('register', inputUser, function (err) {}) 는 register라는 메소드를 호출해 실제 회원정보를 데이터베이스에 넣는 작업을 하게 됩니다.

참고로 meteors는 아래와 같이 method를 만들고, 이를 Meteor.call('메소드 이름') 이와 같은 방법으로 호출해서 사용할 수 있습니다.

  • method 생성
1
2
3
4
5
6
7
Meteor.methods({
method이름: function(data)
{
}
});

만들어진 method는 아래와 같은 방법으로 불러와 사용할 수 있습니다.

  • method 호출
1
2
3
4
5
6
7
8
9
10
Meteor.call('method이름', 넘겨줄객체묶음, function (err) {
if(!err)
{
// 성공
}
else
{
// 실패
}
});

이제 위에 register.js 에서 호출하는 메서드 register를 아래와 같이 만들어 작성해 주세요. 참고로 해당 메소드는 server폴더 아래의 accounts.js에서 작성이 되며, 실제로도 서버에서 실행이 되는 코드가 되겠습니다.

server/accounts.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
Meteor.methods({
register: function(inputUser) // 서버측에서 회원가입 실행시 사용됨.
{
var userEmail = inputUser.email;
var userPassword = inputUser.password;
var userPasswordAgain = inputUser.passwordAgain;
var userFirstName = inputUser.profile.firstName;
var userLastName = inputUser.profile.lastName;
// 회원가입 시작
try
{
Accounts.createUser({
username: userEmail,
email: userEmail,
password: userPassword,
profile: {
serviceType: 'site',
firstName: userFirstName,
lastName: userLastName,
gender: ''
}
});
}
catch (err)
{
throw err;
}
});

그리고 이제 회원가입을 위해 아래의 패키지를 설치해 주시면 되겠습니다.

설치 패키지

  • meteor add accounts-password

이제 http://localhost:3000/register 페이지로 가서 오류 없이 회원 가입이 완료되면, FlowRouter.go('/'); 이 명령어를 타고, 처음 페이지로 이동하게 됩니다.

여기서 일반 웹개발자라면 당황스러운 부분이 발생할 수 있는데요. 현재까지 우리는 딱히 데이터베이스에 스키마 정의도 하지 않았고, 입력을할 프로시저를 만들거나, 아니면 직접 쿼리를 수동으로 입력하지도 않았지만, 가입이 완료되고 심지어 로그인이 되었다는 것에 믿기 힘들 수도 있습니다. meteor가 생산성 생산성 하는데는 바로 이런 부분에서 알 수 있습니다. ^^

몽고 디비에 접속해 실제 테이블이 생성되었고, 객체가 들어가 있는 것을 보는 방법을 간단히 설명드리겠습니다. meteor를 실행시킨 상태에서 Robomongo라는 몽고디비 클라이언트 툴을 이용해 localhost:3001 로 접속하면 아래와 같이 meteor라는 데이터베이스에 user라는 collection(RDBMS의 테이블에 해당)이 있는 것을 볼 수 있습니다.

[몽고 디비 로그인 부분 증명]

미티어는 기본적으로 몽고 디비를 사용하고 있는데 이 몽고디비는 nosql 데이터 베이스로 일종의 텍스트 기반의 데이터 베이스가 되겠습니다. 몽고디비는 딱히 스키마 정의를 하지 않고, json형태의 구조를 읽고, 쓰고, 수정하는 방법으로 사용하는 데이터베이스 입니다. 여기서 이부분까지 자세히 설명은 좀 힘들겠지만, Accounts.createUser Meteor에서 이 api를 호출하는 것으로 유저가 저장된다고 일단 이해하시면 되겠습니다. ^^


4.2 로그인

회원가입을 구현했으니 이어서 로그인 및 로그인 정보를 출력하는 부분을 만들어 보도록 하겠습니다.

client/login.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
Template.login.events({
'click button[name=btn-singIn]' (evt, tmpl)
{
evt.preventDefault();
var inputEmail = tmpl.find('input[name=user-email]').value;
var inputPwd = tmpl.find('input[name=user-pwasword]').value;
Meteor.loginWithPassword(inputEmail, inputPwd, function(err)
{
if(!err)
{
FlowRouter.go('/');
}
else
{
console.log(err);
}
});
return false;
}
});

소스에서 사용된 Meteor.loginWithPassword()는 meteor에서 제공하는 기본 로그인 api 입니다.

다음으로 로그인이 되면 홈화면에서 로그인 정보가 나오도록 첫화면을 바꿔보도록 하겠습니다.

client/home.html

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
<template name="home">
{{#unless loggingIn}}
{{#if currentUser}}
{{> userInfo}}
{{else}}
{{> defaultHome}}
{{/if}}
{{/unless}}
</template>
<template name="defaultHome">
<div class="col-md-8 col-md-offset-2">
<div class="col-md-12">
<div class="col-md-12">
<h2>환영합니다.</h2>
<hr/>
</div>
<div class="col-md-6">
<a href="{{pathFor 'login'}}" class="btn btn-primary btn-lg btn-block">Go Login</a>
</div>
<div class="col-md-6">
<a href="{{pathFor 'register'}}" class="btn btn-success btn-lg btn-block">Go Register</a>
</div>
</div>
</div>
</template>
<template name="userInfo">
<div class="panel panel-default col-md-6 col-md-offset-3 panel-custom">
<div class="panel-heading text_center">
<h2>METEOR ACCOUNTS</h2>
<p>가입유저 정보</p>
</div>
<div class="panel-body panel-body-set">
<div class="col-xs-12 panel-body-inner-box-3">
<p>Email: {{currentUser.username}} </p>
<hr/>
<p>First Name: {{currentUser.profile.firstName}} </p>
<hr/>
<p>Last Name: {{currentUser.profile.lastName}} </p>
<hr/>
</div>
<div class="form-group panel-body-inner bottom-padding">
<button name="btn-logout" class="btn btn-danger btn-lg btn-block button">로그아웃</button>
<a href="{{pathFor 'changePassword'}}" class="a-right" >비밀번호 변경</a>
</div>
</div>
</div>
</template>

변경된 home.html 소스를 설명드리도록 하겠습니다.
일단 단일 템플릿을 home, defalutHome, userInfo 의 세가지 템플릿으로 분리했습니다.
#if currentUser 이 부분은 meteor에서 로그인 상태를 확인할 때 사용하는 API입니다. 로그인이 되어있으면 이 조건절을 타고 > userInfo 템플릿을 호출하여 보여주고, 로그 아웃 상태 일 때는 else 조건절을 따라 > defaultHome 을 보여주도록 설정한 소스입니다.

그리고 userInfo 템플릿을 보면 아시겠지만, 로그인 정보는 currentUser객체로 각종 정보를 받아서 표시 할 수 있습니다.

정상적으로 로그인이 되면 첫화면이 다음과 같이 나오게 됩니다.

[로그인 후 첫화면]

참고로 여기서 #if currentUser 만 사용해도 되지만 이 소스 앞에 #unless loggingIn 로 감싸준 이유는 페이지가 새로 시작될 때 1초 정도 currentUser가 작동하지 않는 문제가 발생하기 때문입니다. 이 현상 때문에 defaultHome 탬플릿이 아주 잠깐 보이고 userInfo로 가는 잔성 현상이 발생하게 됩니다. 이 잔상 현상을 방지 하기위한 일종의 핵? 이 #unless loggingIn 로 감싸주는 방법입니다.


4.3 유효성검사(validate) 추가

회원 가입이 되었지만, 아직 부족한 부분이 많습니다. 이제 하나씩 기능을 더해보도록 하겠습니다. 우선은 유효성 검사를 추가해 보도록 하겠습니다.

일단 아래 패키지를 설치해주세요. 해당 패키지는 유효성검사와 직접적인 관계는 없지만, 경고문의 단순히 자바스크립티 alert 문을 쓰면 좀 허접해(?) 보이는 것을 개선하기 위해 사용한 고급진? alert 효과 패키지 라고 보시면 되겠습니다.

설치 패키지

  • meteor add themeteorchef:bert

bert 패키지의 사용법은 아래와 같이 사용하시면 됩니다. 보시면 상당히 심플한 구조를 가지고 있는 것을 알 수 있는데요. 타입과 출력형태만 메지지의 성격이나 디자인에 맞게 설정해서 바로 사용하시면 됩니다.

1
Bert.alert( '메시지', '타입(ex: success)', '출력형태(ex: growl-top-right)' );

Bert에 대해 좀 더 자세히 알고 싶은 분은 아래 링크를 참고하세요.
https://themeteorchef.com/tutorials/client-side-alerts-with-bert

다음으로 checkPattern.js파일을 만들고, 아래 소스를 입력하세요.
checkPattern.js의 경우 클라이 언트나 서버 모두에서 사용할 경우가 있으므로 lib 폴더에 작성하도록 하겠습니다. 한가지 주의 할 점은 여기의 소스는 꼭 함수명 앞에 var를 써서는 안됩니다. 이부분과 정규식 부분은 잘 비교해서 사용하시면 되겠습니다.

lib/checkPattern.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
// 필수 입력사항 체크
NotEmptyString = function (value)
{
if(value.length > 0)
{
return true
}
throw new Meteor.Error(403, '필수 사항을 입력해주세요.');
}
// 이메일 형식 체크
isEmail = function (value)
{
var filter = /^([0-9a-zA-Z_\.-]+)@([0-9a-zA-Z_-]+)(\.[0-9a-zA-Z_-]+){1,2}$/;
if(filter.test(value))
{
return true;
}
throw new Meteor.Error(403, '이메일 형식이 바르지 않습니다 ');
}
// 패스워드 6자리 이상 체크
isValidPassword = function (password)
{
if(password.length >= 6)
{
return true;
}
throw new Meteor.Error(403, '비밀번호는 6자리 이상이여야 합니다.');
}
// 패스워드, 패스워드 확인 일치 체크
isMatchPassword = function (password, passwordAgain)
{
if(password === passwordAgain)
{
return true;
}
throw new Meteor.Error(403, '비밀번호와 비밀번호 확인이 다릅니다.');
}
// 전화번호 : "000-0000-0000"
isPhone = function(value)
{
var filter = /^\d{3}-\d{3,4}-\d{4}$/;
if(filter.test(value))
{
return true;
}
throw new Meteor.Error(403, '전화번호 형식이 바르지 않습니다 ');
}
// 특정 날짜 형식 : "yyyy/mm/dd"
isBirthDay = function (value)
{
var filter = /^(19[0-9][0-9]|20\d{2})\/(0[0-9]|1[0-2])\/(0[1-9]|[1-2][0-9]|3[0-1])$/;
if(filter.test(value))
{
return true;
}
throw new Meteor.Error(403, '날짜 형식이 바르지 않습니다 ');
}

위 소스들의 형식은 각각 비슷하다는 것을 금방 알 수 있을 것입니다. 연산자로 넘어온 값을 정규식으로 비교하고 이 값에 오류가 있으면 throw를 통해 오류를 던져주는 구조입니다.

그리고 accounts.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
[위 생략]
.
.
.
var userFirstName = inputUser.profile.firstName;
var userLastName = inputUser.profile.lastName;
// 회원가입 입력값 검증 시작
try
{
NotEmptyString(userEmail);
NotEmptyString(userPassword);
NotEmptyString(userPasswordAgain);
NotEmptyString(userFirstName);
NotEmptyString(userLastName);
isEmail(userEmail);
isValidPassword(userPassword);
isMatchPassword(userPassword, userPasswordAgain);
}
catch (err)
{
throw err;
}
// 회원가입 입력값 검증 종료
// 회원가입 시작
try
{
Accounts.createUser({
username: userEmail,
email: userEmail,
.
.
.
[아래 생략]

소스에 대해 설명을 조금 하면, 기본적으로 try,catch문을 만들고 try 안에 미리 작성해둔 검증문과 이 검증문을 어떤 input값에 대응하게 할지를 정하는 과정입니다.

1
2
3
4
5
6
7
8
try
{
검증구문
}
catch(err)
{
오류 발생시 메시지 발생
}

이제 위의 값들을 다 메칭해 주셨으면, 테스트를 해보도록 하겠습니다.
일단 모든 값은 무조건 입력해야 하므로, NotEmptyString를 사용해고, 이메일은 이메일 형식이 있으면, isEamil을 사용해고, 패워드는 기본 길이와, 패스워드&패스워드확인이 같은지를 체크하는 부분을 추가해습니다.

이제 localhost:3000/register 를 실행시켜 보고, 검증이 잘 진행되는 확인해 보도록 하겠습니다. 만약 검증부분이 정상적으로 입력되었다면, 아무것도 입력하지 않거나,이메일 형식이 틀리는 문제 등이 발생시, 아래와 같이 경고창이 뜨고 더이상 진행이 안되는 상태가 됩니다.

이 내용을 바탕으로 기존 로그인 화면도 검증 및 경고창을 추가해 보도록 하겠습니다. 아래 소스를 바탕으로 검증추가 부분을 추가해주세요.

client/login.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
[위 생략]
.
.
.
var inputEmail = tmpl.find('input[name=user-email]').value;
var inputPwd = tmpl.find('input[name=user-pwasword]').value;
// 검증추가 시작
try
{
NotEmptyString(inputEmail);
NotEmptyString(inputPwd);
}
catch(err)
{
console.log(err);
//Bert.alert( err.message , 'danger', 'growl-top-right' );
return;
}
// 검증추가 종료
Meteor.loginWithPassword(inputEmail, inputPwd, function(err)
{
if(!err)
{
FlowRouter.go('/');
}
.
.
.
[아래 생략]

회원가입과 로그인에서의 유효성검사에서 다른 점이 있습니다. 바로 회원가입은 서버쪽에서 검사을 했고, 로그인 페이지는 클라이언트에서 실행되도록 한것입니다. 즉 유효성검사는 서버나 클라이언트 어디서든 사용할 수 있다는 얘기가 됩니다. 상황에 따라 사용할 수 있고, 아주 중요한 내용이라면, 서버&클라이언트 모두에서 유효성검사를 할 수 있습니다. 그리고 이 두 경우 모두 lib/checkPattren.js 를 참조해서 이용할 수 있다는 점이 Meteor의 매력중 하나가 아닐까 생각합니다. ^^


4.4 이메일 인증

가입시 자동가입 등을 막는 방법으로 많이 사용되는 방법으로 이메일 인증이 있을 것입니다. Meteor는 이에 필요한 API를 나름 잘 제공하고 있습니다.

우선 첫번째로 할 일은 인증시에 보낼 메일에 대한 설정부터 해보겠습니다. 다음 소스를 해당경로에 파일을 만들고 입력해주세요.

server/main.js

1
2
3
4
5
6
7
8
9
// Set up login services
Meteor.startup(function() {
Accounts.config({
sendVerificationEmail: true
});
process.env.MAIL_URL="smtps://이메일주소:패스워드@smtp.gmail.com:465/";
});

위 소스에서
Meteor.startup 의 경우 meteor가 실행될 때 초기에 필요한 세팅등을 실행시키는 부분이 되겠습니다. 여기서는 보내는 메일에 대한 정보(smtp 에 대한 정보)를 입력해 주시면 되는데 smtp의 경우 메일 서비스 마다 조금 다른 부분이 이을 수 있으니 꼭 엽력하기 전에 해당 smtp 서비스의 입력방법을 확인해주세요.
참고로 현재 버전(1.5)에서 gmail의 경우 smtp가 아닌 위와 같이 smtps로 해야 메일이 정상적으로 발송됩니다.
Accounts.config 부분의 sendVerificationEmail: true 만 주면 메일로 확인을 해야만 가입승인이 떨어지는 구조가 되고, 자동으로 메일을 발송합니다. 하지만 메일의 경우 영어로 된 기본 메일이 발송되므로 아래 소스를 참고로 친절한 한글 메일을 만들 수 있습니다. ^^

server/accountsMails.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 공통사항
Accounts.emailTemplates.siteName = "account sample";
Accounts.emailTemplates.from = "admin <admin@gmail.com>"; // 보내는 주소 정보
// 가입확인 메일
Accounts.emailTemplates.verifyEmail = {
subject() {
return "Meteor accounts 가입확인 메일";
},
text( user, url ) {
let emailAddress = user.username,
urlWithoutHash = url.replace( '#/', '' ),
supportEmail = "메일아이디@gmail.com",
emailBody = `이메일 주소를 확인하려면 (${emailAddress}) 다음 링크를 방문하십시오. \n\n link: ${urlWithoutHash} \n\n 만약 확인을 요청하지 않은 경우, 이 이메일을 무시하십시오. 문제가 있다고 생각되면 다음 서비스 지원팀에 문의하십시오. \n\n 문의주소 : ${supportEmail}.`;
console.log('mailUser');
console.log(user);
return emailBody;
}
};

위 소스에서 공통부분은 가입인증 말고도 메일 보내는 다른 메일 서비스에서 계속해서 사용하게 될 부분으로, 사이트명과 보내는 사람의 이메일 주소를 설정하는 부분입니다.

다음으로 실제 가입확인 메일에 관한 부분을 조금 설명드리겠습니다.
supportEmail=user.username으로 세팅된 이유는 기본적으로 username을 email로 세팅하기 때문이다
emailBody는 메일 내용에 관한 부분입니다. 제일 중요한 부분은 ${urlWithoutHash} 입니다. 이 링크는 일종의 일회성 키값으로 해당 링크로 방문을 해야 가입승인이 활성화 됩니다.

다음은 이런 실제 메일을 보내는 method를 추가해보겠습니다.
아래 소스를 참고로 sendVerificationLink(가입 확인 메일 발송) 메소드를 추가해주세요.

server/accounts.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
Meteor.methods({
register: function(data) // 서버측에서 회원가입 실행시 사용됨.
{
// 서버에서 한번 더 입력값 검증을 해도 된다. 아니면 모든 입력값 검증을 서버로 옮겨 와도 상관은 없다.
try
{
NotEmptyString(data.username);
NotEmptyString(data.password);
isEmail(data.username);
.
.
.
[중략]
.
.
.
}
catch (err)
{
throw err;
}
},
// 가입 확인 메일 발송
sendVerificationLink(user)
{
var userId = user;
if ( userId ) {
return Accounts.sendVerificationEmail( userId );
}
}
});

이제 가입이 이루어질 타이밍에 sendVerificationLink 메소드를 호출하는 작업을 해보겠습니다. 같은 server/accounts.jsAccounts.onCreateUser()라는 일종의 라이프 사이클 API를 이용하면 되겠는데요. 즉 createUser api로 유저가 생성될 때 어떤 일들을 함께 처리할지를 정하는 함수라고 보면 되겠습니다.
onCreateUser로 해야하는 일은 user.profile에 넣어줄 값 싱크, 메일 발송, 그리고 이후에 할 외부 api(facebook, google 등)를 이용한 로그인 시 profile 싱크 맞추는 작업 등이 있겠습니다. 아래 소스를 참고로 accounts.js 파일을 작성해주세요. 다시 한번 말씀드리자면 회원의 다양한 정보(성별, 주소 등등)를 user.profile에 넣어두고 사용할 수 있습니다.

server/accounts.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
Meteor.methods({
register: function(data) // 서버측에서 회원가입 실행시 사용됨.
{
.
.
.
[중략]
.
.
.
});
// 가입시 메일 발송
Accounts.onCreateUser(function (options, user) {
user.profile = options.profile; // 이렇게 안해주면 profile이 없는 상태로 가입됨...
Meteor.setTimeout(function() { // setTimeout을 걸어주지 않으면 메일 발송이 안됨
Meteor.call('sendVerificationLink', user._id, function (err)
{
if (err)
{
throw error.reason;
}
});
}, 2 * 1000);
return user; // 꼭 새 사용자 객체를 반환해야 함.(가이드에 나와있는 내용임)
});

다음으로 라우터에 해당 이메일로 보냈던 확인 주소로 접근할 경우 키 값이 맞는지 확인 하는 부분을 추가해야 한다. 메일로 보낸 링크가 정상적이라면, 링크를 클릭한 주소로 접속하는 순간 회원가입이 완료되는 방식이 되겠습니다. 참고로 위의 소스에서 sendVerificationLink 호출시 Meteor.setTime()로 감싸주지 않으면 오류가 발생합니다.

lib/routes.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
[위 중략]
.
.
.
content: 'changePassword'
});
}
else
{
FlowRouter.go('/');
}
}
});
//이메일 인증 관련
FlowRouter.route( '/verify-email/:token', {
name: 'verify-email',
action( params )
{
Accounts.verifyEmail( params.token, ( error ) => {
if ( error )
{
Bert.alert( error.reason, 'danger' );
}
else
{
FlowRouter.go( '/' );
Bert.alert( '이메일 인증에 성공했습니다.', 'success' );
}
});
}
});

Accounts.verifyEmail API를 이용해서 메일을 통한 링크 접근 값이 정상적일 경우 회원가입이 정상적으로 처리되도록 합니다.

다음으로 로그인 후 첫 화면에서 회원 가입이 진행중일 경우(이메일 인증을 아직 받지 않은 상태)에는 유저 정보를 보여주는 것이 아닌 아직 메일에서 가입승인을 하지 않았다는 메시지를 보여주는 부분을 추가하도록 하겠습니다.

client/home.html

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
<template name="userInfo">
{{#unless currentUser.emails.[0].verified}}
<div class="panel panel-default col-md-6 col-md-offset-3 panel-custom">
<div class="panel-heading text_center">
<h2>METEOR ACCOUNTS</h2>
</div>
<div class="panel-body panel-body-set">
<div class="col-xs-12 panel-body-inner-box-4">
<h3>확인 메시지</h3>
<p>가입하신 이메일로 가입승인을 해주셔야 사용이 가능합니다.</p>
<p>메일이 오지 않으셨다면 메일을 재발송 해주세요.</p>
</div>
<form class="col-md-12 panel-body-inner-1">
<div class="form-group">
<input type="text" class="form-control account-text-form-1" placeholder="가입 이메일">
</div>
</form>
<div class="form-group panel-body-inner bottom-padding">
<button name="resend-verification-link" class="btn btn-danger btn-lg btn-block button">메일 재발송</button>
</div>
</div>
</div>
{{else}}
<div class="panel panel-default col-md-6 col-md-offset-3 panel-custom">
<div class="panel-heading text_center">
<h2>METEOR ACCOUNTS</h2>
<p>가입유저 정보</p>
</div>
<div class="panel-body panel-body-set">
<div class="col-xs-12 panel-body-inner-box-3">
<p>Email: {{currentUser.username}} </p>
<hr/>
<p>First Name: {{currentUser.profile.firstName}} </p>
<hr/>
<p>Last Name: {{currentUser.profile.lastName}} </p>
<hr/>
</div>
<div class="form-group panel-body-inner bottom-padding">
<button name="btn-logout" class="btn btn-danger btn-lg btn-block button">로그아웃</button>
<a href="{{pathFor 'changePassword'}}" class="a-right" >비밀번호 변경</a>
</div>
</div>
</div>
{{/unless}}
</template>

위의 내용중에 확인 메시지 페이지의 경우 회원가입만 되어 있고, 메일로 확인을 하지 않았을 때 보여지는 화면인데요. 이 경우 메일 발송이 잘 못 이루어 졌을 수도 있어, 이럴 경우 다시한번 메일을 발송하는 input 박스를 만들어 두는 두는 장치를 추가한 부분이 되겠습니다.

[home.html 페이지 - 회원가입 확인 페이지]

home.html 은 총 3가지의 상황에 따른 화면을 나타냅니다. 위의 회원가입 확인 페이지와 함께 다음 2페이지가 상황에 따라서 나타나가 됩니다.

[home.html 페이지 - 로그 아웃 상태일 때의 페이지]

[home.html 페이지 - 회원가입 확인 후 로그인 페이지]

여기서 잠깐 meteor에서의 회원가입 과정을 조금 설명드리겠습니다. meteor로 Accounts.createUser()로 회원가입이 진행되면, 데이터베이스 user collection(meteor는 기본적으로 mongodb를 사용합니다.)에 다음과 같은 json 필드가 추가됩니다. 각 컬럼은 다음과 같은 성격을 가집니다. 기본정보 이외의 사이트마다 등록이 필요한 값들은 profile 필드 아래에 넣어두고 사용할 수 있습니다.

  • _id : 고유값
  • createdAt : 생성날짜
  • services/password : 비밀번호(자동으로 암호화)
  • username: 가입아이디(기본적으로 가입 이메일을 사용)
  • emails/address : 가입한 이메일 주소
  • emails/verified : 가입확인 유무(기본 false로 이메일 가입확인에 사용)
  • profile: 기타 가입 정보(사용자가 임의로 추가하는 필드의 경우 이 아래에 두고 사용하면 됨.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"_id" : "gKdDPzkLRjcbAwnG5",
"createdAt" : ISODate("2017-05-27T15:13:55.357Z"),
"services" : {
"password" : {
"bcrypt" : "$2a$10$og01yOey9To/ugGr8uxBbe1nCqKZ30qCzikh88OJi4KY9Vj3Qm7xO"
}
},
"username" : "freeseamew@gmail.com",
"emails" : [
{
"address" : "freeseamew@gmail.com",
"verified" : false // 이 부분을 통해 이메일 인증 확인 가능
}
],
"profile" : {
"serviceType" : "site",
"firstName" : "가입자 이름",
"lastName" : "가입자 성",
"gender" : ""
}
}

이를 참고로 위의 home.html 페이지가 상황에 따라서 화면을 보여주는 원리는 다음과 같습니다.

#unless loggingIn 로 먼저 로그인 상태를 확인 한 다음에 로그인 되어 있으면, #unless currentUser.emails.[0].verified를 참고하여 verified가 true이면 로그인 이용자의 정보를 보여주고, false일 경우에는 회원가입 확인 페이지를 보여주는 구성을 가지게 됩니다.


5. 회원가입시 사용자 정의 가입정보 추가 방법

위에서 설명 했지만 기본적인 가입 시스템을 만들면, _id, createAt, username, emails와 같은 최소한의 정보는 기본적으로 저장합니다. 하지만 그외의 정보(성별, 사용자이름, 레벨, 주소 등)는 profile이라는 필드 아래에 넣어서 사용 할 수 있습니다. 사실 위에서도 이미 그 부분에 대해서 조금 설명을 드렸지만, 조금 더 이 부분에 대해서만 다시한번 설명 드리도록 하겠습니다.

register.html에서 폼 값 등으로 정의된 값은 register.js의 registerInfo라는 객체에 저장된 후 register 메소드로 전달됩니다. 이 때 필요한 값들을 profile아래에 정의해서 넘기면 됩니다.

client/register.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
var registerInfo = {
username: inputUsername,
email: inputEmail,
password: inputPassword,
profile: { // 사용자 정의 가입정보
firstName: inputFirstName,
lastName: inputLastName
}
}
//서버로의 로그인 호출
Meteor.call('register', registerInfo, function (err)
{
if(!err)
{
Bert.alert( '가입에 성공했습니다. 가입한 메일로 인증을 받아주세요' , 'danger', 'growl-top-right' );
FlowRouter.go('/login');
}
else
{
Bert.alert( err.message , 'danger', 'growl-top-right' );
}
});
}

이를 server/accounts.js'의register` 메소드에서 다시 받게 됩니다. 그리고 ‘Accounts.createUser()’에 받은 값들을 넘깁니다.

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
Meteor.methods({
register: function(data) // 서버측에서 회원가입 실행시 사용됨.
{
'
'
'
[중략]
'
'
'
// 회원가입 시작
try
{
user = Accounts.createUser({
username: data.username,
email: data.email,
password: data.password,
profile:{
serviceType: 'site',
firstName: data.profile.firstName,
lastName: data.profile.lastName,
gender: ''
}
});
return {
"userId": user
};
}
catch (err)
{
throw err;
}
},

마지막으로 Accounts.onCreate() api를 사용하여 위의 값이 저장될 때 다시한번, 사용자 정의 값이 어디에 저장될 지 지정해 주면 됩니다. 참고로, profile값들은 options.profile에 들어가 있고, 아래와 같이 설정하면 정상적으로 몽고디비의 user collection의 가입자의 profile에 저장됩니다.

1
2
3
4
5
6
7
8
9
10
11
Accounts.onCreateUser(function (options, user)
{
user.profile = options.profile;// 이렇게 안해주면 profile이 없는 상태로 가입됨...
[아래 생략]
.
.
.

머 회원가입 과정은 대략 이렇습니다. 제 설명으로 조금이나마 이 과정이 이해가 되었으면 좋겠지만, 그렇지 않다면, 그냥 이렇게 가입이 되고 로그인이 된다로 이해하고 넘어가 주셔도 될 것 같습니다. 제가 강좌를 처음 쓰는 것이라, 여러 설명이 조금 매끄럽지 못한점은 양해 부탁드립니다. ㅡㅜ

그럼 다음 강좌도 계속 봐주세요 ^^