wireframe

wireframe

와이어 프레임이란?
와이어 프레임은 구조 수준에서 웹 사이트 서비스를 설계하는 방법입니다. 와이어 프레임은 일반적으로 사용자의 요구와 사용자 여정을 고려하여 페이지에 내용과 기능을 배치하는 데 사용됩니다. 와이어 프레임은 시각적 디자인과 내용이 추가되기 전에 페이지의 기본 구조를 설정하기 위해 개발 프로세스 초기에 사용됩니다.

라고 experienceux에서 설명하고 있습니다.

개인적으로는 대강의 레이아웃과 들어가야 할 요소들을 정하고 클릭했을 때 어디로 갈 것인가를 정하는 것을 와이어프레임으로 보고 있습니다. 뼈대 없이 건물이 올라 갈 수 없듯이 와이어 프레임은 디자인에 있어서도 개발에 있어서도 필수 적인 단계라고 생각합니다.

와이어 프레임은 손으로 그려도 되고
손으로 와이어 프레임 그리기

PPT로 그리셔도 됩니다.
PPT와이어 프레임

또한 와이어 프레임을 만들 수 있는 툴을 제공하는 웹 서비스를 이용하셔도 됩니다.

wireframe 서비스

와이어 프레임 서비스는 기본적으로 많은 예시 컴포넌트를 제공합니다. 제공되는 컴포넌트를 조합하여 기획을 하면 시간이 많이 단축되겠죵.

국내 업체들이 제공하는 서비스도 있습니다.

카카오에서 제공하는 oven이나
Oven

네이버에서 제공하는
프로토나우

프로토나우가 있습니다만 프로토 나우의 경우엔 2016년에 마지막 업데이트가 있었습니다. 두 서비스 모두 정체되어 있거나 사실상 사장되었다고 생각이 듭니다.

웹에서 wireframe서비스 제공

저는 가볍게 몇 페이지의 기획만이 필요했기 때문에 무료 사이트 위주로 찾았습니다. 그 중에 추천할 만한 것은

mockflow

mockflow

간단하게 사용하실 수 있습니다.
작업을 다 하고 다양한 형태(pdf, ppt 등)로 export가 가능합니다.
많은 컴포넌트를 제공하고 있습니다.

하지만 좀 작업에 있어서 버벅거림이 느껴집니다. PPT 처럼 오묘하게 간격이 맞지 않는 것 같은 느낌이 듭니다. 무료이용 고객은 3페이지를 제공합니다. 그 이상은 유료로 이용해야 합니다.

moqups

moqups

다양한 컴포넌트를 제공합니다.
목업 프로우보다 훨신 자연스러운 움직임을 제공합니다.

하지만 더 많은 프로젝트를 진행하기 위해서는 유료 서비스를 이용해야 합니다.

Sketch

스케치는 맥OS에서 이용 가능한 어플입니다. 하…..진짜 맥에서만 제공하기 때문에 맥이 없는 디자이너는 진짜 맥맥하고 웁니다.

Sketch
많은 디자이너들이 Adobe Photopshop으로 작업했지만 스케치로 많이 옮겨가는 추세입니다. 덧붙여, 수많은 라이브러리와 강력한 심볼 기능으로 간단하게 화면 제작이 가능하여 기획자들도 많이 사용하고 있습니다.

오픈 소스를 공유하고 많은 플러그인을 사용할 수 있다는 것에서 스케치는 포토샵보다 분명히 앞서가고 있었습니다. 왜 이제야 알게 됐을까 하는 생각도 듭니다.

거기에 스케치에서 제공하는 강력한 심볼기능은 컴포넌트 단위로 개발하는 개발자들과 협업하기에 아주 좋은 시스템이라고 할 수 있습니다.

UXpin

국내의 대형 웹 서비스 업체에서도 많이 쓴다고 봤습니다. 인터넷에 검색하면 유저가 만들어 놓은 반응형 와이어 프레임도 쉽게 찾을 수 있습니다. 유료지만 시험 기간이 있으니 한번 경험해 보시는 것도 나쁘지 않을 것이라 생각됩니다.

UXpin

Adobe XD

어도비에서 Sketch에 대항하여 만든 프로그램입니다. UI가 상당히 유사하고 쉽기 때문에 적응하기 쉽습니다. 하지만 Adobe라는게 약점일 수 있습니다. 라이브러리가 많이 없으며 아직 불안정합니다. 이걸로 기획하다가 그냥 꺼지기를 여러번…. Sketch와는 다르게 자동으로 저장되지도 않아 자료를 여러번 날려먹었습니다.
제 작업용 컴퓨터를 맥으로 바꾸는데 제일 큰 역할을 했습니다. 하하
Adobe XD


검색해 보시면 여러가지 와이어 프레임 툴이 많이 있을 겁니다.
10 Wireframing Tools to Help you Create Great Websites, and Make Great Design Decisions

저도 이런 글들을 참조하며 많이 써봤습니다. 이러니ㅏ 저러니 해도 자기한테 맞는 와이어프레임을 쓰는게 좋을 것 같습니다. 작업이 많이 귀찮은 분들을 라이브러리나 예제가 풍부한 쪽을, 협업을 많이 하시는 분은 expot가 여러가지 형태로 되는 쪽을 권해드립니다.

다들 화이팅!
린생....

공유하기 댓글

vue에서 firebase 데이터 가져오기

사실 제가 다 알아서 진도를 나가는 것은 아닌 것 같습니다. 언제나 그랬듯이 지나고 나면 알게 되겠지 라는 마음으로 꾸준히 하고 있습니다.

firebase에서 데이터를 가져오기 위해 필요했던 작업들입니다.

내용이 많았던 페이지들의 라우터를 분리하고 필요한 라이브러리를 설치하는 등의 추가 작업이 필요했습니다.

1. firebase 커넥션 분
2. 테스트 페이지 만들기
3. api 미들웨어로 등록
4. test 페이지 라우터 만들기
5. http 비동기 통신을 위해 axios라이브러리 설치
6. localhost:3000 의 데이터를 받아오기
7. data()로 firebase 데이터 정리
8. 정리된 데이터를 html 안에 넣기

저도 잘 기억은 나지 않지만 하나하나 차근차근 정리해 보도록 합시다.

1. firebase 커넥션 분리

저희는 우선 app.js에 들어있던 커넥션을 정리했습니다.
app.js 레벨에 새로운 파일 db.config.js를 만들어 firebase관련된 내용을 분리시켰습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
//db.config.js
var admin = require("firebase-admin");
var serviceAccount = require("./ex01-shin-key.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://ex01-shin.firebaseio.com"
});
var db = admin.database();
module.exports = db;

2. 테스트 페이지 만들기

저는 우선 저번에 만들었던 second페이지에서 받아오는 정보를 다시 보여주는 페이지를 만들었습니다. 일단 폼을 만들고 라우터 연결 등등의 연결을 해줄겁니다.
vue > front > src > components에 새로운 members폴더를 생성합니다.

members.html 생성

1
2
3
<div>
<h1>members</h1>
</div>

members.vue파일 생성

1
2
3
4
5
<template src = "./members.html"></template>
<style scoped src = "./members.css"></style>
<script>
</script>

그리고 주소 라우터를 생성해줍니다. 위치는 front > src > router > index.js

1
2
3
4
5
6
7
8
9
...
import members from '@/components/members/members'
...
{
path: '/members',
name: 'members',
component : members
}
...

3.api 미들웨어로 등록

app.js에 api를 미들웨어로 등록해줍니다.

1
2
app.use('/api', route);
//데이터만 응답하는 서버 api

4. test 페이지 라우터 만들기

이제 테스트 페이지의 라우터를 만들어줍니다.
라우트는 vue(최상위 위치) > routes 에서 셋팅해줍니다.
index.js에서는

1
2
3
let accountRoute = require('./account.route');
module.exports = accountRoute;

account.route.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
var express = require('express');
var db = require('../db.config');
var accountRoute = express.Router();
var ref = db.ref("/");
//firebase에서 들어가는 경로 설정
accountRoute.get('/test',(req, res) => {
var usersRef = ref.child("users")
//child 로 하위 메뉴로 들어감
usersRef.once('value')
.then((data)=>{
console.log(data);
res.send(data);
//then은 성공했을 때 행동양식
})
.catch(err => {
res.statusCode(500)
console.log(err)
//catch는 실패했을 때 행동양식
})
});
module.exports = accountRoute;

5. http 비동기 통신을 위해 axios라이브러리 설치

이 부분은 Vue.js 한국 사용자 모임에 잘 정리되어 있습니다.
HTTP 요청을 위한 axios
axios는 비동기 통신을 위한 플러그인입니다.

비동기 전송 방식은 요청과 그 결과가 동시에 일어나지 않습니다. 송, 수신간 동기를 맞추지 않고(?) 문자단위로 구분하여 전송합니다. 상대방의 상태와 관계없이 일방적으로 다음 동작을 발생시킵니다. 구현이 간단하고 비용이 적게드나 데이터 전송 시 많은 오버헤드(?)를 가진다고 합니다.

1
npm install --save axios

npm으로 axios를 설치해줍니다. 그리고 front > src에 들어있는 main.js에서 import.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import axios from 'axios'
Vue.prototype.$http = axios
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})

6. localhost:3000 의 데이터를 받아오기

이제부터 좀 헷갈리기 시작합니다. 돌아가는 상황을 보면 second 파일에서 자료를 보낼때 그것을 캐치해서 members에 뿌리는 건가 싶기도 합니다.

일단 members에서는 localhost:3000의 데이터를 가져와보기로 합니다.
members.vue에서 다음과 같은 태그를 넣어 줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default {
name: "members",
methods: {
getData: function() {
const baseUrl = "http://localhost:3000/api";
this.$http.get(baseUrl + "/test").then(res => {
console.log("test", res.data);
this.shin = res.data.shin;
});
},
listSet: function(){
this.list = [1,2,3,4,5,6]
}
},
};

7. data()로 firebase 데이터 정리

그리고 쉼표 아랫부분에는 파이어베이스에서 데이터를 가져오는 공간을 마련해줍니다.

1
2
3
4
5
6
7
8
9
10
11
...
data() {
return {
shin:{
id:"",
password: "",
fullName: ""
},
list:[]
};
}

members.html파일에서는 데이터를 저번과 같은 방식으로 넣어주면 끝!

8. 정리된 데이터를 html 안에 넣기

member.html

1
2
3
4
5
6
7
8
9
<div>
<h1>members</h1>
<button v-on:click="listSet">list</button>
<h2 v-for="item in list">{{item}}</h2>
<button v-on:click="getData">dataTest</button>
<h5>{{shin.id}}</h5>
<h5>{{shin.password}}</h5>
<h5>{{shin.fullName}}</h5>
</div>

이렇게 하고 실행시켜줍니다.
vue 레벨에서 app.js를 실행시켜주고

1
node .\app.js

vue > front 에서 vue실행.

1
npm run dev

그럼 다음과 같은 결과를 보실 수 있습니다.
으아아아아아 어려우엉ㅜㅜ

공유하기 댓글

node에 vue프레임워크 연결하기

으아 진짜 엉망진창으로 정리하고 있다는 느낌이 든다.

vue연결하기

Vue(/vjuː/ 로 발음, view 와 발음이 같습니다.)는 사용자 인터페이스를 만들기 위한 진보적인 프레임워크 입니다.

라고 Vue에서 설명하고 있습니다. Vue홈페이지는 한글화가 잘 되어 있어서 영어에 거부감이 있으신 분들도 쉽게 접근 가능하실 겁니다.

그럼 저번에 firebase에 연결해 놓았던 node프로젝트를 꺼내 봅니다. 여기에 Vue를 설치해 보도록 하겠습니다.

vue설치하기 바로가기

링크에 들어가보시면 설치하기 방법이 여러가지 나와있습니다.

1
npm install vue --s

npm으로 설치하고 cli로 프로젝트를 만들어줍니다.

1
2
3
4
5
6
7
8
//vue-cli 설치
npm install --global vue-cli
//"webpack" 템플릿을 이용해서 새 프로젝트 생성
vue init webpack my-project
//의존성을 설치하고 실행하세요!
cd my-project
npm install
npm run dev

이렇게하면 다음과 같은 문서 구조가 될겁니다. front문서 생겼쪙

문서구조

front를 클릭하면

문서구조

이렇게 보여집니다. 여기에서 주로 쓰게 될 폴더는 src > components 파일입니다.

만들었으니 일단 실행해보겠습니다.
명령 프롬프트에

1
npm run dev

을 입력해줍니다. 그럼 http://localhost:8080 이 주소로 접속이 가능하게됩니다. Vue의 로컬 호스트는 보통8080으로 정해집니다. Hexo는 4000…

주소로 들어가면 이런 화면을 볼 수 있습니다.

vue설치

그럼 설치가 다 된 것 같으니 테스트를 하나 해봅시다.

two way binding

v-model 디렉티브를 사용하여 폼 input과 textarea 엘리먼트에 양방향 데이터 바인딩을 생성할 수 있습니다.

components 폴더에 새로운 폴더를 만듭니다. 저는 second파일을 만들었습니다.

거기에 각각 second.css, second.html, second.vue파일 쪼개주기.

1
2
3
4
5
6
7
<div>
<h1>{{msg}}</h1>
<div>
<h3>{{password}}</h3>
<input v-model = "password" type = "text">
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template src = "./second.html"></template>
<style scoped src = "./second.css"></style>
<script>
export default {
name: 'Second',
data () {
return {
msg: 'This is Second page',
password: "chicken"
}
}
}
</script>

사실 이 파일은 HelloWorld.vue 예시 파일을 쪼개 놓은 것입니다. 앵귤러에서는 이렇게 파일을 쪼갠다고 하더라구요. 이렇게 파일을 쪼개 주고 routes 설정을 바꿔줘야 합니다.

라우터 설정은 src > router > index.js에 가시면 바꿀 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
// second route추가
{
path: '/second',
name: 'second',
component: second
},
]
})

이렇게 변경을 하고 http://localhost:8080/#/second 여기에 들어가 폼에 아무거나 입력하시면 위에 그 내용을 보실 수 있으실 겁니다. 실시간(?)으로 바뀌는 것을 양방향 바인딩이라고 하나? ㅎㅎㅎ

공유하기 댓글

Firebase 노드에 연결하기

node에 firebase 연결하기

서버는 node를 쓰면서 데이터베이스는 google의 firebase를 쓰기로 합니다.
우선은 firebase에 가입해 줍니다. 가입하고 새로운 프로젝트 만들기.

웹에서 firebase 프로젝트 생성

저는 admin계정으로 만들기 위해 Firebase Admin SDK를 설정했습니다. 이 과정은 서버에 Firebase Admin SDK추가에서 자세히 확인하실 수 있습니다.

새 비공개 키를 생성하기 위해 서비스 콘솔로 이동하여 해당 프로젝트를 클릭. 하단에 있는 버튼을 눌러 키를 생성합니다. 파일 형태로 다운로드가 되는데 이 키를 프로젝트 폴더 안에 넣어줍니다.

그리고 해당 프로젝트의 명령 프롬프트를 열어 firebase-admin을 설치해줍니다.

1
npm install firebase-admin --save

라우터 설정들이 들어있던 app.js에는 require로 파이어베이스 어드민을 불러옵니다.

1
var admin = require('firebase-admin');

그리고 SDK를 초기화 해줍니다.

1
2
3
4
5
6
var serviceAccount = require('path/to/serviceAccountKey.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: 'https://<DATABASE_NAME>.firebaseio.com'
});

여기에서 각자 이름과 경로 등이 달라질 수 있습니다. 예를들어 저는 프로젝트에 바로 key를 넣었고 프로젝트 이름을 blog-ex로 해 놓았기 때문에 다음과 같은 설정을 해 주어야 합니다.

1
2
3
4
5
6
7
8
var serviceAccount = require("./blog-ex.json");
//key이름이 너무 길면 바꿔줘도 됩니다.
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://blog-ex-20239.firebaseio.com"
});
//프로젝트 이름 뒤에 숫자는 임의로 붙이는 것 같습니다.

자 이렇게 하면 파이어 베이스에 연결은 끝이 납니다.
프로젝트의 데이터베이스는 firebase의 콘솔 > DEVELOP > Database에서 확인하실 수 있습니다.


Firebase에 데이터 넣기

파이어베이스에 데이터를 넣기위해 다음과 같은 태그를 넣어 줍니다.

1
2
var db = admin.database();
var ref = db.ref("/");

그리고 ejs로 받아오기 위해 설정했던 부분을 바꿔줍니다.

1
2
3
4
5
6
7
// app.engine('html', hbs({extname: ".html"}));
// app.set('views', path.join(__dirname, './public'));
// app.set('view engine','html');
app.set('views', path.join(__dirname, './public'));
app.set('view engine', 'ejs');
app.engine('html', require('ejs').renderFile);

그리고 구글에서 제공한 set()update()를 이용하여 정보를 저장해 줍니다.
웹에서 데이터 저장에서 참조하였습니다.

저는 output에서 제공받은 정보를 넣기 위해 output 라우터에 설정하였습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
router.route('/output').post((req, res)=>{
console.log('output 처리함')
var user = {id:req.body.id, password:req.body.password};
res.render('out.html', user);
var usersRef = ref.child("users")
usersRef.set({
shin: {
id:req.body.id,
password:req.body.password
}
});
var shinRef = usersRef.child("shin");
shinRef.update({
"fullName": "shinae yu"
});
});

그리고 나서 app.js를 노드로 실행시키고 login.html에서 정보를 입력한 후 파이어 베이스를 살펴보겠습니다.

firebase에 들어있는 정보

데이터 베이스의 구조는 데이터베이스 구조화에서 더 자세히 살펴볼 수 있으니 참고!

공유하기 댓글

로그인 기능, 로그인 정도 다른 페이지로 넘기기

defalt

뭔지는 잘 모르겠지만 일단 기본으로 셋팅을 해야 되는 부분.
이 부분은 필요에 따라 찾아보고 셋팅하면 될듯.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//app.js
var express = require('express');
var http = require('http');
var path = require('path');
var bodyParser = require('body-parser');
var hbs = require('express-handlebars');
var app = express();
app.set('port', process.env.PORT || 3000);
var router = express.Router();
http.createServer(app).listen(app.get('port'), function(){
console.log('익스프레스 서버를 시작했습니다.' + app.get('port'));
});
app.use('/public', express.static(path.join(__dirname, './public')));
app.use(bodyParser.urlencoded({extended:false}));
app.use(bodyParser.json());

ejs를 쓰기 위해 view 셋팅 변경

ejs는 <%= %>태그를 사용해서 여기에서 받은 정보를 다른 페이지에서 볼 수있게 하는 template…이거 말고도 다른 기능이 있겠지만 ㅎㅎ

우선 설치를 해봅시다. 명령 프롬프트에 npm으로 설치

1
npm install ejs --save

그리고 view셋팅을 변경해 줍니다.

1
2
3
4
5
6
7
8
9
10
//app.js
// app.engine('html', hbs({extname: ".html"}));
// app.set('views', path.join(__dirname, './public'));
// app.set('view engine','html');
//기존의 것을 변경
app.set('views', path.join(__dirname, './public'));
app.set('view engine', 'ejs');
app.engine('html', require('ejs').renderFile);

로그인 파일과 결과 페이지 만들기

저번에 만들었던 파일을 재활용합니다. public 폴더 안에 login.html 과 output.html 파일 만들어주기. 타이틀에서도 알 수 있듯이 login에서 ID와 password를 입력하면 output.html에서 보여지게 할겁니다.

1
2
3
4
5
6
7
8
9
10
11
12
//login.html
...
<body>
<form method="post" action = "/output">
<h4>아이디</h4>
<input type="text" name="id">
<h4>패스워드</h4>
<input type="password" name="password">
<button type="submit">전송</button>
</form>
</body>
...

Route 설정하기

라우팅은 애플리케이션 엔드 포인트(URI)의 정의, 그리고 URI가 클라이언트 요청에 응답하는 방식을 말합니다. 쉽게는 그냥 url을 지정하는 것이라고 생각하시면 될 듯 합니다. 이전에는 url에 정보를 담아 던지는 방법밖에 없어서 url자체가 길어지고 지저분 할 수 밖에 없었다고 하는데 라우팅을 통해서 간단한 url을 제공할 수 있습니다.

하지만 너무 제멋대로 실제 파일구조와 너무 다른 url을 생성한다면 같이 일하는 사람에게 욕먹음…

login.html 파일을 /login으로 url지정하기

1
2
3
router.route('/login').get((req, res)=>{
res.render('login.html');
});

위에서 /public 경로를 지정해 주었기 때문에 render에서는 이후 파일 이름만 넣어 줍니다.여기에서 확장자도 같이 넣어 주도록 합니다. 안그럼 오류나.. route에는 실제 url에 들어갈 이름 지정.

oupput.html파일을 /output으로 url지정

1
2
3
router.route('/output').post((req, res)=>{
res.render('output.html');
});

그리고 맨 끝에는 미들웨어에 router객체를 등록하는 태그를 넣어줍니다.

1
2
app.use('/', router);
//미들웨어에 router객체를 등록

login.html에서 넘어온 정보를 output.html에 뿌려주기

생각보다 넘나 간단한 것. 위에서 라우터 등록하는 부분에 user정보를 렌더합니다(?)

1
2
3
4
5
6
//app.js
router.route('/output').post((req, res)=>{
console.log('output 처리함')
var user = {id:req.body.id, password:req.body.password}
res.render('out.html', user);
});

그리고 헷갈려서 여태 보지 않았던 output.html을 봅시다. body안에 정보를 넣어 줍니다. 이때 태그는 <%= %>이런 형식의 태그를 씁니다.

1
2
3
4
5
6
7
8
9
10
//output.html
<body>
<h1>Express서버에서 응답한 결과입니다.</h1>
<p>
<%= id %>
</p>
<p>
<%= password %>
</p>
</body>

app.js에서 user의 id와 password를 키값으로 등록을 했기 때문에 HTML안에서는 간단하게 키값으로만 쓸 수 있습니다.


이제 node app.js로 노드를 서버를 실행시키고 localhost:3000/login에 들어가 정보를 입력하면 output으로 이동하는 것을 확인할 수 있습니다.

공유하기 댓글

노드로 간단한 웹서버 만들기

간단한 웹서버 만들기

노드에는 기본으로 http모듈이 있어서 서버 객체를 만들 수 있는데 이 서버 객체는 웹서버 기능을 담당한다.

일단 폴더 하나를 npm init 명령어로 노드 프로젝트를 만든다.
그리고 파일을 하나 만든다.
1
2
3
4
5
6
7
8
9
10
var http = require('http');
//웹 서버 객체를 만듦.
var server = http.createServer();
//웹 서버를 시작하여 3000번 포트에서 대기
var port = 3000;
server.listen(port, function(){
console.log('웹 서버가 시작되었습니다.');
});

여기까지 코드를 작성하고 브라우저를 실행시켜 보자. 주소는 localhost:3000.
무엇이 보일까? 안보인다. 아무것도 왜냐하면 서버를 시작만 했지 클리언트에 던져주는 정보가 아무것도 없기 때문.
다만 명령 프롬프트에서

1
2
PS C:\workspace\test_02\node_02> node index.js
웹 서버가 시작되었습니다.

라는 정보를 볼 수 있다. 연결이 되긴 한 모양.

그럼 어떻게 정보를 요청할 수 있을지 이벤트를 살펴보자.

  • connection : 클라이언트가 접속하여 연결이 만들어질 때 발생하는 이벤트
  • request : 클라이언트가 요청할 때 발생하는 이벤트
  • close : 서버를 종료할 때 발생하는 이벤트

위의 코드 아래에 다음 코드를 넣고 실행시켜보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
server.on('connection', function(socket){
var addr = socket.address();
console.log('클라이언트가 접속했습니다. :', addr.address, addr.port);
});
server.on('request', function(req, res){
console.log('클라이언트 요청이 들어왔습니다.');
// console.dir(req);
});
server.on('close', function(){
console.log('서버가 종료됩니다.');
});

이렇게 하면 여전히 브라우저의 화면에서는 아무것도 나오지 않지만 명령프롬프트에서는 다음과 같이 출력되는 것을 확인 할 수 있다.

1
2
3
4
PS C:\workspace\test_02\node_02> node index.js
웹 서버가 시작되었습니다.
클라이언트가 접속했습니다. : ::1 3000
클라이언트 요청이 들어왔습니다.

눈에 보이지는 않지만 접속 함으로서 요청이 들어오긴 한 모양.


웹 서버에 요청을 보내는 방식(method)
  • GET : header에 요청정보를 넣어 보냄
  • POST : 본문에 요청정보를 넣어 보냄
  • PUT
  • DELETE

익스프레스로 웹 서버 만들기

실무에서 웹 서버를 구성할 때에는 express모듈을 주로 사용.
express모듈을 사용하더라도 express모듈은 http모듈을 기초로 만들어진 것.
익스프레스 모듈에서는 미들웨어와 라우터를 제공하는데 이걸 알면 시능을 만드는게 훨씬 편리해 진다고 한다.

그럼 express모듈 설치

npm install express --save로 익스프레스 모듈을 설치 한다.

파일 생성해서 익스프레스 시작

1
2
3
4
5
6
7
8
9
//express 기본 모듈 불러오기
var express = require('express')
, http = require('http');
//express 객체 생성
var app = express();
//기본 포트를 app 객체에 속성으로 설정
app.set('port', process.env.PORT || 3000);

여기에서 축약한 형태가 들어가면서 눈에 잘 안보이는 형태가 있는데 이것은 var data = {}; data.port = 3000; 이렇게 들어가 있는 형태라고 생각하면 됨. ||이 연산자는 or연산자로 지금은 3000으로 들어가 있지만 만약에 process.env.PORT 가 설정되어 있다면 일단 그것으로 셋팅이 됨.

1
2
3
4
//express 서버 시작
http.createServer(app).listen(app.get('port'), function(){
console.log('익스프레스 서버를 시작했습니다.' + app.get('port'));
});

이렇게 작성하고 실행하면

1
익스프레스 서버를 시작했습니다.3000

는 메세지를 볼 수 있당.


express server method
  • set(name, value) : 서버 설정을 위한 속성을 지정, set()메소드로 지정한 속성은 get()메소드로 꺼내 확인할 수 있음
    └ env, views(뷰들이 들어있는 폴더, 폴더배열 설정), view engine(디폴트 뷰엔진 설정)
  • get(name) : 서버 설정을 위해 지정한 속성을 꺼내옴
  • use([path], function[,function…]) : 미들웨어 함수를 사용
  • get([path], function) : 특정 패스로 요청된 정보를 처리

미들웨어 등록

미들웨어는 중간에 요청이 있으면 추가로 서버에서 정보를 던져주는 것 같다.
라우트는 그쪽으로 연결 해 주는 것. 아직 잘 모르겠는 것. 느낌같은 느낌

use()메소드를 이용해서 미들웨어로 등록해보자.

1
2
3
4
5
6
7
8
9
app.use(function(req, res, next){
console.log('첫번째 미들웨어에서 요청을 처리함');
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.end('<h1>Expess 서버에서 응답한 결과입니다.</h1>');
//여러 개의 미들웨어 등록 시
next();
});

이렇게 하면 브라우저에서 h1으로 문장이 나오는 것을 확인 할 수 있다.
명령 프롬프트에서는 ‘첫번째 미들웨어에서 요청을 처리함’ 이라는 문장 확인.

static 미들웨어

[public]폴더 안에 있는 파일을 사이트의 /public 패스로 접근하도록 만들고 싶다면 static()메소드를 호출할 때 아래와 같이 패스를 정해주면 됨.

1
2
3
4
5
//path 활성화
var path = require('path');
//패스 정해주기
app.use('/public', express.static(path.join(__dirname, './public')));

body-parser 미들웨어

body-parser미들웨어는 post로 요청했을 때, 요청 파라미터를 확인 할 수 있게 만들어준다.
클라이언트가 Post방식으로 요청할 경우 본문 영역에 들어있는 요청 파라미터들을 파싱하여 요청 객체의 body속성에 넣어줌.

npm install body-parser --save 로 설치하고
var bodyParser = require('body-parser');를 위에 넣어 활성화

public폴더 안에 login.html 파일을 만들어보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form method="post">
<h4>아이디</h4>
<input type="text" name="id">
<h4>패스워드</h4>
<input type="password" name="password">
<button type="submit">전송</button>
</form>
</body>
</html>

그리고 app.js엔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//body-parser를 사용해 application/x-www-form-urlencoded 파싱
app.use(bodyParser.urlencoded({extended:false}));
//body-parser를 사용해 application/json 파싱
app.use(bodyParser.json());
app.use(function(req, res, next){
console.log('첫번째 미들웨어에서 요청을 처리함');
console.log(req.body.id);
console.log(req.body.password);
//json형식으로 받은 id와 패스워드를 body로 보내줌
res.send({id:req.body.id, password:req.body.password});
});

을 넣는다. localhost:3000/public/login.html 에서 form에 정보를 입력하고 전송하면 입력된 정보를 다시 볼 수 있다.

요청 라우팅하기

클라이언트에서 요청한 요청 패스에 따라 실행될 함수는 라우터(router) 객체를 사용해 등록함. route() 메소드가 있어서 요청 패스를 지정할 수 있으며 get()이나 post()메소드를 호출하면 실행될 함수를 등록할 수 있음

1
2
3
4
5
6
7
8
9
10
//라우터 객체 참조
var router = express.Router();
//라우팅 함수 등록
router.route('/process/login').get(...);
router.route('/process/login').post(...);
...
//라우터 객체를 app객체에 등록
app.use('/', router);
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
var express = require('express');
var http = require('http');
var path = require('path');
var bodyParser = require('body-parser');
var hbs = require('express-handlebars');
var app = express();
app.set('port', process.env.PORT || 3000);
// var data = {};
// data.port = 3000;
// console.log(data);
var router = express.Router();
http.createServer(app).listen(app.get('port'), function(){
console.log('익스프레스 서버를 시작했습니다.' + app.get('port'));
})
//엔진등록
app.engine('html', hbs({extname: ".html"}));
//view 폴더랑 public폴더랑 같이 잡아놓음
app.set('views', path.join(__dirname, './public'));
//view 템플릿 엔진을 html로 등록, html, ejs, pug로 등록할 수 있다.
app.set('view engine','html');
//router 객체에 route를 등록
router.route('/test').get((req, res)=> {
console.log('test work!!');
// res.send({name:'shin', age:32});
res.render('index3.html');
});
//미들웨어에 router객체를 등록
app.use('/', router);

이렇게 작성하고 localhost:3000/test에 들어갈 경우 명령 프롬프트엔 test work!!가 출력되고 public폴더 아래에 있는 index3.html 파일 화면이 보여지게 된다.

공유하기 댓글

노드 시작하기

사건의 발단

발단… 이라고 까지는 뭐하지만 내가 요즘 회사 내 프로젝트를 하느라고 정신이 없음…맨날 포토샵하고 제플린, 필요하면 인비전까지 손대고 있기는 한데 시간이 없어서 개발까지 살펴보지 못함….

그래서 회사에서 개발하는 것 손가락만 빨며 보기 일쑤인데 그것마저도 회사 전체가 바빠서 개발자들한테 물어보기도 뭣함…그런데 나도 개발이란 것을 해보고 싶음. 그래서 간단하게 노드부터 시작해보기로 함.

앞으로 배울 것들

하지만 배울 것은 아직 엄청엄청 많음.
인비전, 피그마, BEM, OOCSS 등등. 뭐 이렇게 보니 다 다 방법에 관한 것들이라 프로젝트 들어갈 때 하나씩 스터디 해가면서 적용해보면 될듯.

참고 링크 [CSS방법론] SMACSS, BEM, OOCSS

일단 node

node가 뭔가요

간단히 들은 설명으로는 인터넷 서버(브라우저)에서 결과를 볼 수 있던 자바 스크립트를 로컬서버(내 컴터)에서도 간단히 볼 수 있도록 하기 위해 노드를 쓰는 것이라고 한다. 서버에 올리지 않고도 볼 수 있는 것.
노드는 또한 모듈로 각 부분을 쪼개 놓는 것을 가능하게 한다. 모듈로 쪼개면 보기도 편해지고, 가벼워지고, 관리도 쉬워지는 장점이 있다.

Hello node

노드를 실행하는 방법은 생각보다 간단했다.
일단 뭔가 볼 수 있는 자바 스크립트 파일을 만든다.

1
console.log('Hello Node');

노드를 깔고 명령 프롬프트에 node + 해당 파일 이름을 입력한다.

1
node index.js

그럼 명령 프롬프트에서 결과를 볼 수 있음.


node 기초

내부 모듈 만들기

내부 모듈을 함 만들어 보겠음. calc.js 라는 이름으로 파일을 하나 만듦니다.

1
2
3
4
5
6
7
var calc = {};
clac.add = function(a,b){
return a+b;
}
module.exports = calc;

얘가 모듈, 더하기 기능을 밖으로 뺐다고 생각하자…
그리고 이것을 다른 파일에서 불러다 써보도록 하자

1
2
3
4
var calc = require('./calc.js');
//내부 모듈은 모듈의 위치를 적어준다.
console.log(calc.add(20, 45));
//65

외부 모듈 가져다 써보기

외부 모듈을 만들기 전에 일단 어떻게 외부 모듈을 가져와서 쓸 수 있는지 알아보자.

  1. 폴더를 만들어서 노드 프로젝트로 등록하자.
    mymoment라는 폴더를 생성. 이 위치에 npm init라는 명령어로 package.json 파일을 생성. 패키지 제이슨 파일에서 이 프로젝트에 쓰이는 외부 패키지들을 관리(?)할 수 있다.(“dependencies”) 이 패키지 파일을 프로젝트 별로 생성하게 되는데 그 이유는 프로젝트별로 사용하는 패키지와 버전이 각기 다르기 때문
    script에서는 특정 동작에 대한 명령어를 만들 수 있다. ex)hexo s

  2. moment.js 를 설치해보자.
    mymoment 폴더 위치에 npm install moment --save 를 입력. 그럼 자동으로 모멘트 js가 깔린다. 참고로 모멘트는 날짜 계산을 쉽게 도와주는 모듈. 모듈을 설치하면 package.json 파일의 dependencies에 "moment": "^2.21.0" 가 추가된 것을 확인할 수 있다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "name": "mymoment",
    "version": "1.0.0",
    "description": "",
    "main": "01.js",
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
    "moment": "^2.21.0"
    }
    }
  3. moment.js불러오기

    1
    2
    3
    4
    5
    6
    7
    var moment = require('moment');
    function nextMonth(){
    return moment().add(1, 'M');
    }
    module.exports = {nextMonth};

모멘트를 불러온 후, 다음달을 계산하는 함수를 생성해서 내보내는 모듈
이렇게 하면 이제 외부 모듈을 가져와서 사용할 수 있다.

재미지당!!!

모듈을 만들어보자

  1. .gitignore 파일 만들기
    git에 올려야 하는데 node_modules은 제외하고 올려야 하니 .gitignore파일 만들어서 /node_modules적어주기

  2. git에 새로운 repository 만들어서 올리기
    그리고 주소 복사.

  3. 설치하는 방법은 아까와 마찬가지로 해당 파일 위치로 가서 npm install git주소(복사한 것)을 입력하는 것.

  4. 그러면 package.json 에서도 mymoment 라는 외부 모듈이 깔린 것을 확인 할 수 있다.

내가 만든 모듈을 사용해 볼까.

아까 다음달을 구하는 모듈을 만들었었다.
그러면 그것을 불러와서 사용해 보자.

1
2
3
var my = require('mymoment');
console.log(my.nextMonth());
//moment("2018-04-12T00:18:43.070")

이렇게 파일을 작성하고 노드를 실행하면 결과를 볼 수 있다.

공유하기 댓글

vega-lite로 그래프를 그려보자.

데이터 시각화 라이브러리

웹에서 데이터를 그래프로 그리기 위해(시각화) 라이브러리를 많이 이용한다.
그 중 많이 몇가지를 살펴보자면 구글 차트 라이브러리, 하이차트(Highchart), 베가 라이트(Vega-Lite), c3, EChart가 있음.

google 차트 라이브러리

구글 차트 라이브러리는 총 29종의 기본적인 차트를 제공하는데, 멀티터치를 지원하고 호환성이 좋아서 모바일에서도 많이 사용하는 것 같다. 하지만 인터넷이 잘 되지 않는 환경이라면 보이지 않는나는 듯
써 놓고 보니 좀 이상하네. 웹에 띄우는 거면 인터넷이 되어야 뭐든 보일텐데…

무료로 사용 가능하다.

하이차트(Highchart)

하이차트는 광고목적이 아닐 시에만 무료로 사용 가능.
아이폰과 아이패드를 포함한 모바일 브라우저와 멀티 터치 줌을 지원하는 것이 특징이라고… 이와 같은 이유로 페이스북, 트위터 등 많은 기업에서 사용하고 있다고 한다.

베가 라이트(vega-lite)

내가 써야하는 베가 라이트. 베가는 D3라이브러리를 기반으로 하고 있는데 다른 차트들이 “내가 차트들을 준비했으니 맘에 드는 것을 골라서 써보렴” 하는 느낌이라면 베가는 “니가 다양하게 커스터마이징 할 수 있게 우리가 보조할게” 하는 느낌. 다양한 커스터 마이징이 가능하다. 관련 커뮤니티가 꾸준히 성장하고 있는 것도 장점 중의 하나.

무료로 사용가능. 더 많은 기능을 지원하는 vega와 간단하고 심플하게 사용 할 수 있는 vega-lite가 있다. 많은 장점이 있으나 ie8버전 이하는 지원하고 있지 않아 좀 고민이 있다.

C3

전에 포스팅헸던 C3. 커스터 마이징이 많이 필요하지 않다면 이것도 간단하게 사용하기 좋다. C3는 애니메이션도 훌륭하고 디자인도 좋고 반응형이라서 내가 좋아함. 게다가 D3라이브러리를 기본으로 한 것중에는 좀 쉬운편. D3어려움…

EChart

이번에 차트 라이브러리 찾아보면서 알게된 중국에서 만든 차트. 바이두에서 만든 오픈소스 라이브러리이며 ie8이하까지 지원된다는 막강한 장점이 있다.
차트의 종류가 진짜 어마어마하게 많다. 니가 뭘 좋아할지 몰라서 다 준비해 봤어

개발자분께 이것저것 물어보니 자바스크립트 차트 라이브러리는 오조 오억개 정도 되는 느낌이다. 각자 다 장점과 단점이 있으니 살펴보고 프로젝트에 맞는 차트 라이브러리를 고르면 되겠다. 다만 한번 만든뒤로 업데이트가 되고 있지 않은 라이브러리들이 많은데 관련 커뮤니티가 꾸준히 성장하고 있는 라이브러리를 고르는 것이 좋을 것 같다.


vega-lite 기본 셋팅

본격적으로 베가 라이트를 살펴봅시당.
베가 라이트 예제에 들어가면 여러가지 예제를 살펴볼 수 있다. 이중에 나는 여러개의 라인으로 만들어진 라인차트가 필요.
Multi Series Line Chart
베가라이트가 커스터 마이징으로 다양한 차트를 그릴 수 있지만 나는 일단 초심자이니 비슷한 것을 수정해 가면서 하나하나 배우기로 한다.

음 한번 예제를 구경 했으니 실제로 써보도록 하자.
사용하기에 가보면 어떻게 기본 셋팅을 해야 하는지 나와있다. 실시간으로 가져와도 되고 라이브러리 다운받아도 되고, 노드로 설치해도 되고…

나는 링크로 가져오는(CDN) 방식을 선택.

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
<!DOCTYPE html>
<html>
<head>
<title>Embedding Vega-Lite</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vega/3.0.8/vega.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vega-lite/2.0.3/vega-lite.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vega-embed/3.0.0-rc7/vega-embed.js"></script>
</head>
<body>
<div id="vis"></div>
<script type="text/javascript">
var yourVlSpec = {
"$schema": "https://vega.github.io/schema/vega-lite/v2.0.json",
"description": "A simple bar chart with embedded data.",
"data": {
"values": [
{"a": "A","b": 28}, {"a": "B","b": 55}, {"a": "C","b": 43},
{"a": "D","b": 91}, {"a": "E","b": 81}, {"a": "F","b": 53},
{"a": "G","b": 19}, {"a": "H","b": 87}, {"a": "I","b": 52}
]
},
"mark": "bar",
"encoding": {
"x": {"field": "a", "type": "ordinal"},
"y": {"field": "b", "type": "quantitative"}
}
}
vegaEmbed("#vis", yourVlSpec);
</script>
</body>
</html>

이렇게 하면 기본적인 차트를 볼 수 있습니다.
기본차트

여기에서 우리가 아까 보았던 샘플로 돌아가 그 코드를 yourVlSpec에 넣어 봅시다.
앞으로는 yourVlSpec안에서만 코드를 변경할 겁니다.

1
2
3
4
5
6
7
8
9
10
11
var yourVlSpec = {
"$schema": "https://vega.github.io/schema/vega-lite/v2.json",
"description": "Stock prices of 5 Tech Companies over Time.",
"data": {"url": "data/stocks.csv"},
"mark": "line",
"encoding": {
"x": {"field": "date", "type": "temporal", "axis": {"format": "%Y"}},
"y": {"field": "price", "type": "quantitative"},
"color": {"field": "symbol", "type": "nominal"}
}
}

이렇게 하면 멋진 라인 그래프가 짠! 하고 안 나올 겁니다.
안나와요. 왜냐면 이건 csv데이터를 가져와서 그리는 거라서요. 다음 단계는 데이터 사이언스팀에 자료를 요청하는 겁니다. 왜….회사에 데이터 사이언스팀 하나씩 다 있잖아요. ㅎㅎㅎ

혹시나 데이터 때문에 안나오는 그래프가 안나오면 그때부터는 어떻게 해야 하는지 모를테니 일단 예제의 데이터 형식과 같게 데이터를 뽑습니다.
데이터는 베가 웹에서 개발자모드로 들어가 네트워크 탭을 누르시면 확인하실 수 있습니다.

네트워크 확인
stocks.csv를 보면 항목(symbol), 날짜(date, x축), 데이터 수치(price, y축)으로 정렬이 되어있습니다. 이런 형식으로 뽑아달라고 요청을 했습니다.
자료를 같은 폴더에 넣고 데이터 URL을 수정해 줍니다.

"data": {"url": "index_scaled_price.csv"},

이렇게 말이죠. 이렇게 하면 아마 전체 면적이 파란 그래프를 보실 수 있으실 겁니다.

파란색 그래프

color에서 "field": "symbol""field": "index"로 수정해 봅니다. 항목이 symbol에서 index로 바뀌었으니까요. 그러면 진짜 알흠다운 그래프를 보실 수 있습니다.

알흠다워!

기본적으로 제공되는 차트도 예쁩니다. 색도 분명하게 구분이 되고 선도 얇습니다. (제거는 데이터 양이 좀 많아서 두꺼워보입니다.) 하지만 조금만 더 신경을 써보기로 합니다.


vega-lite 커스터마이징

거창하게 커스터마이징이라고는 하지만 색깔바꾸고, x축 grid없애고, 테두리 없애고, 크기 조정하고 X축 항목들 좀 조정하는 정도.

크기조정

웹에 넣으려고 작업을 한게 아니고 인쇄용으로 쓰려고 작업을 한 것이기 때문에 나는 큰 사이즈의 그래프가 필요했다. 사이즈를 조절하는 것은 쉽다.

1
2
3
4
5
6
7
var yourVlSpec = {
"$schema": "https://vega.github.io/schema/vega-lite/v2.json",
"description": "Stock prices of 5 Tech Companies over Time.",
"width": 1000, //가로값 1000px
"height": 800, //세로값 800px
...
}

vega는 1000을 적으면 자동적으로 1000px으로 인식합니다.

테두리 없애기

그래프를 그리다 보니 오른쪽 세로선이 거슬림. 아무리 axisRight를 조절해 보아도 안 없어지는 선…. 주변에 자문을 구하니 저게 축이 아니라 테두리일 가능성이 있다는 것.

테두리가 defalt

그럼 테두리를 없애보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var yourVlSpec ={
"$schema": "https://vega.github.io/schema/vega-lite/v2.json",
"description": "Stock prices of 5 Tech Companies over Time.",
"data": {"url": "index_scaled_price.csv"},
"width": 1000,
"height": 800,
"mark": "line",
"encoding": {
...
},
"config": {
"view": {
"stroke": "transparent"
}
}
}

하단에 config를 추가하여 view속성에서 선을 투명으로 바꾸는 코드를 추가해 준다. 그러면 다음과 같이 선이 없어진다.

테두리 없애기

x축의 그리드가 있기 때문에 명확하게 보이지 않는다. 그럼 다음으로 x축 그리드를 없애보기로 한다.

x축 그리드 없애기, 포맷 변경, 라벨 각도 조절

이것 모두 쉽게 조절이 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
...
"encoding": {
"x": {
"field": "date",
"type": "temporal",
"axis": {
"format": "%Y-%m", //라벨의 포맷을 변경, 달까지 나오게
"grid": false, //그리드 없애기
"labelAngle": "320" //라벨의 각도를 조절
}
},
...

x축 커스터마이징
조절한 모습, 위의 그림과 비교해보면 라벨이 돌아갔고, 세로선이 없어졌으며, 년도까지 나오던 라벨이 달까지 나오는 것을 알 수 있다.

선 색깔 바꾸기

마지막으로 라인의 색을 수정하며 마치기로 합니다.
색은 기존에 있던 컬러에서 약간의 코드를 추가하여 바꿀 수 있습니다.

1
2
3
4
5
6
7
"color": {
"field": "index",
"type": "nominal",
"scale": {
"range": ["#ff775e", "#f985a6", "#97d93f", "#febd3a", "#916ed6", "#6489ae", "#44b9e2"]
} //추가된 부분
}

데이터 컬러 바꾸기


이외에도 vega로 바꿀 수 있는 부분이 많이 있습니다. 베가 홈페이지의 Documentation에 보면 뭔가 많이 있습니다. 수정하고 싶으신 부분을 찾으시거나 비슷한 그래프를 인터넷에 찾아보시면 도움이 되실 거라고 생각합니다. 배울 건 언제나 넘치네요. 하지만 나는 귀찮을 뿐이고

열심히 합시다!

공유하기 댓글

js 함수

서브루틴은 반복되는 작업의 일부를 떼어내어 이름을 붙이고, 언제든 그 이름을 부르기만 하면 실행. 함수의 가장 간단한 사용형태

함수생성

우선 다짜고짜 함수를 만들어보자. 오늘이 윤년인지 아닌지 알아보는 함수 생성.

1
2
3
4
5
6
7
8
function printLeapYearStatus(){
const year = new Date().getFullYear();
if(year %4 !==0) console.log(`${year} is not a leap year.`)
else if(year%100 !=0)console.log(`${year} is a leap year.`)
else if(year % 400 !=0)console.log(`${year} is not a leap year.`)
else console.log(`{$year} is a leap year`);
}
printLeapYearStatus(); //2017 is not a leap year.

값을 반환하는 함수로

위의 printLeapYearStatus 함수는 재사용하기 편리하지만 프로그램이 커지면 콘솔에 기록하는 것은 의미가 없어짐. HTML에 결과를 출력하거나, 파일에 저장하거나, 다른 계산에 사용해야 할 수도 있는데 지금 함수로는 그렇게 사용할 수가 없음

그래서 함수가 값을 반환하는 서브루틴이 되도록 수정

1
2
3
4
5
6
7
8
function isCurrenrYearLeapYear(){
const year = new Date().getFullYear();
if(year%4!==0)return false;
else if(year%100!=0)return true;
else if(year%400!=0)return false;
else return true;
}
isCurrenrYearLeapYear(); //false

함수로서의 함수

순수한 함수에서는 입력이 같으면 결과도 같아야 함
isCurrenrYearLeapYear함수는 언제 호출하느냐에 따라 true를 반환하기도, false를 반환하기도 한다. 순수한 함수는 아니라는 것.

1
2
3
4
5
6
7
function isLeapYear(year){
if(year%4!==0)return false;
else if(year%100!=0)return true;
else if(year%400!=0)return false;
else return true;
}
isLeapYear(2014); //false

year지역변수에 고정된 값이 들어가게 되니 같은 값이 들어가게 되고, 결과도 같아진다. 다른 효과도 전혀 없으니 순수한 함수라고 할 수 있음

This code here could be more DRY

DRY(don't repeat yourself) 함수는 반복을 없애는 것이 좋다.
순수한 함수를 쓰면 코드를 테스트하기 쉽고, 이해하기 쉽고, 재사용하기도 쉽다. 순수한 함수를 사용하도록 습관을 들이는 것이 좋음

함수에 별명 붙이기

변수가 있는 곳에는 함수도 있을 수 있음

  • 함수를 가리키는 변수를 만들어 별명을 정할 수 있음
  • 배열에 함수를 넣을 수 있음
  • 함수를 객체 프로퍼티로 사용할 수 있음
  • 함수를 함수에 전달할 수 있음
  • 함수가 함수를 반환할 수 있음
  • 함수를 매개변수로 받는 함수를 반환하는 것도 가능
    다 가능?
1
2
3
4
5
6
7
function addThreeSquareAddFiveTakeSquareRoot(x){
return Math.sqrt(Math.pow(x+3, 2)+5);
}
//막 이런 함수 별명을 써서 간단하게 바꾸면...
const f = addThreeSquareAddFiveTakeSquareRoot;
//여기서는 함수명 다음에 괄호를 안쓴다!
const answer = (f(5) + f(2)) /f(7);
공유하기 댓글

ES6에서 새로 도입한 데이터 구조 map, set

맵은 키와 값을 연결한다는 점에서 객체와 비슷하고, 셋은 중복을 허용하지 않는다는 점을 제외하면 배열과 비슷

MAP

ES6이전에는 키와 값을 연결하려면 객체를 사용해야 했음. 하지만 객체를 이런 목적으로 사용하면 여러가지 단점이 생김

  • 프로토타입 체인 때문에 의도하지 않은 연결이 생길 수 있음
  • 객체 안 연결된 키와 값이 몇 개나 되는지 쉽게 알아낼 방법이 없음
  • 키는 반드시 문자열이나 심볼이어야 하므로 객체를 키로 써서 값과 연결할 수 없음
  • 객체는 프로퍼티 순서를 전혀 보장하지 않음

map은 키와 값을 연결할 목적이라면 객체보다 나은 선택

1
2
3
4
const n1 = {name:'shin'};
const n2 = {name:'jane'};
const n3 = {name:'jin'};
const n4 = {name:'tt'};

map

먼저 맵을 만듭니다.

1
const userRoles = new Map();

set()

다음에는 맵의 set() 메서드를 써서 사용자의 역할을 할당

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
userRoles.set(u1, 'user');
userRoles.set(u2, 'user');
userRoles.set(u3, 'admin');
//tt는 역할 없음
//set() 메서드는 체인으로 연결할 수 있어서 타이핑의 수고를 덜어줌
userRoles
.set(u1, 'user')
.set(u2, 'user')
.set(u3, 'admin');
//생성자에 배열의 배열을 넘기는 형태로 써도 됨
const userRoles = new Map([
[u1, 'user'],
[u2, 'user'],
[u3, 'admin'],
]);

get()

이제 u2의 역할을 알아볼 때는 get()메서드를 쓰면 됨.

1
userRoles.get(u2); //"user"

맵에 존재하지 않는 키에 get을 호출하면 undefined를 반환.


has()

맵에 키가 존재하는지 확인하는 has()메서드

1
2
3
4
userRoles.has(u1); //true
userRoles.get(u1); //"user"
userRoles.has(u4); //false
userRoles.get(u4); //undefined

맵에 이미 존재하는 키에 set()을 호출하면 값이 교체됨

1
2
3
userRoles.get(u1); //user
userRoles.set(u1, 'admin');
userRoles.get(u1); //admin


size()

size프로퍼티는 맵의 요소 갯수를 반환

1
userRoles.size; //3


keys(),values(), entries()

key메서드는 맵의 키,
values 메서드는 값,
entries 메서드는 첫번째 요소가 키값이고 두번째 요소가 값인 배열을 반환
이 메서드가 반환하는 것은 모두 이터러블 객체여서 for...of루프를 쓸 수 있음

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for(let u of userRoles.keys())
console.log(u.name);
for(let r of userRoles.values())
console.log(r);
for(let ur of userRoles.entries())
console.log(`${ur[0].name}: ${ur[1]}`);
//맵도 분해할 수 있음
for(let [u, r] of userRoles.entries())
console.log(`${u.name}: ${r}`);
//entries()메서드는 맵의 기본 이터레이터.
//위 코드는 다음과 같이 단축해서 쓸 수 있음
for(let [u, r] of userRoles)
console.log(`${u.name}: ${r}`);

이터러블 객체보다 배열이 필요하다면 확산 연산자를 쓰면 됨

1
[...userRoles.values()]; //["user", "user", "admin"]


delete()

맵의 요소를 지울 떄 사용

1
2
userRoles.delete(u2);
userRoles.size; //2


clear()

맵의 요소를 모두 지울 때 사용

1
2
userRoles.clear();
userRoles.size; //0

위크맵

WeakMap은 다음 차이점을 제외하면 Map과 완전히 같음

  • 키는 반드시 객체여야 한다.
  • WeakMap의 키는 가비지 콜렉션에 포함될 수 있다.
  • WeakMap은 이터러블이 아니며 clear()메서드도 없음

일반적으로 자바스크립트 코드는 코드 어딘가에서 객체를 참고하는 한 객체의 메모리를 계속 유지하려고 하는데 WeakMap은 그렇지 않음. 가비지 콜렉션 중인 객체를 노출할 위험이 큼

WeakMap의 이런 특징은 객체 인스턴스의 전용 키를 저장하기에 알맞음

1
2
3
4
5
6
7
8
9
10
11
const secretHolder = (function() {
const secret = new WeakMap();
return class{
setSecret(secret){
secret.set(this, secret);
}
getSecret(){
return secret.get(this);
}
}
})();

비밀을 저장할 때는 setSecret, 보려고 할 때에는 getSecret

1
2
3
4
5
6
7
8
const a = new secretHolder();
const b = new secretHolder();
a.setSecret('secret A');
b.setSecret('secret B');
a.getSecret(); //"secret A"
b.getSecret(); //"secret B"

일반적인 Map을 써도 되지만 그렇게 하면 secretHolder 인스턴스에 저장한 내용은 가비지 콜렉션에 포함되지 않음. 가지비 콜렉션에 추가하고 싶으면 WeakMap(?)

Set

Set은 중복을 허용하지 않는 데이터 집합.
같은 사용자에게 같은 역할을 여러번 부여한다는 것은 상식적이지 않음. 셋은 이런 경웨 이상적인 데이터 구조

1
2
3
4
5
6
7
8
9
10
const roles = new Set();
//사용자 역할을 추가할 때에는 add()메서드를 사용
roles.ass("user"); //Set ["user"]
//사용자에게 관리자 역할을 추가하려면 add()
roles.add("admin"); //Set ["user", "admin"]
//map과 마찬가지로 Set에도 size프로퍼티가 있음
roles.size; //2

추가하려는 것이 이미 셋에 있는지 확인할 필요 없음. 이미 있다면 아무 일도 일어나지 않음

1
2
roles.add("user"); //Set ["user", "admin"]
roles.size; //2

역할을 제거하고 싶을 떄에는 delete()
제거에 성공했다면(그런 역할이 셋에 존재했다면) true반환, 아니면 false

1
2
3
roles.delete("admin"); //true
roles; //Set ["user"]
roles.delete("admin"); //false


WeakSet

위크셋은 객체만 포함 할 수 있으며, 이 객체들은 가비지 콜렉터의 대상이 됨.
WeakMap과 마찮가지로 WeakSet도 이터러블이 아님

위크셋의 용도는 매우 적음.
실제 용도는 주어진 객체가 셋 안에 존재하는지 아닌지를 알아보는 것 뿐 이라고 해도 과언이 아님

EX) 산타클로스가 jingjing라는 WeakSet을 가지고 어떤 아이가 우는 아이인지 확인해서 선물대신 석탄(도 가격이 올랐다던데)을 놓고 온다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const jingjing = new WeakSet();
const children = []
{name: "suzy"},
{name: "shin"}
];
jingjing.add(children[1]);
for(let child of children){
if(jingjing.has(child))
console.log(`Coal for ${child.name}!`);
else
console.log(`Presents for ${child.name}!`);
}
공유하기 댓글