swagger-ui-express 설치하기

1
npm i swagger-ui-express

package.json에 swagger-ui-express 추가하기

1
"swagger-ui-express": "latest" // or desired version

swagger js-doc 설치하기

기존에 있던 js 파일에서 api를 따오기 위해 swagger jsdoc을 설치한다.

1
npm install swagger-jsdoc --save

swagger-jsdoc 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var swaggerJSDoc = require('swagger-jsdoc');

var options = {
swaggerDefinition: {
info: {
title: 'Hello World', // Title (required)
version: '1.0.0', // Version (required)
},
},
apis: ['./routes.js'], // Path to the API docs
};

// Initialize swagger-jsdoc -> returns validated swagger spec in json format
var swaggerSpec = swaggerJSDoc(options);

app.js 세팅하기

1
2
3
4
5
6
const express = require('express');
const app = express();
const swaggerUi = require('swagger-ui-express');
const swaggerSpec = swaggerJSDoc(options)

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));

document the api

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
/**
* @swagger
* /login:
* post:
* description: Login to the application
* produces:
* - application/json
* parameters:
* - name: username
* description: Username to use for login.
* in: formData
* required: true
* type: string
* - name: password
* description: User's password.
* in: formData
* required: true
* type: string
* responses:
* 200:
* description: login
*/
app.post('/login', function(req, res) {
res.json(req.body);
});

Model Definition을 통한 재사용

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
/**
* @swagger
* definitions:
* NewUser:
* type: object
* required:
* - username
* - password
* properties:
* username:
* type: string
* password:
* type: string
* format: password
* User:
* allOf:
* - $ref: '#/definitions/NewUser'
* - required:
* - id
* - properties:
* id:
* type: integer
* format: int64
*/

/**
* @swagger
* /users:
* get:
* description: Returns users
* produces:
* - application/json
* responses:
* 200:
* description: users
* schema:
* type: array
* items:
* $ref: '#/definitions/User'
*/
app.get('/users', function(req, res) {
res.json([ {
id: 1,
username: 'jsmith',
}, {1
id: 2,
username: 'jdoe',
} ]);
});

/**
* @swagger
* /users:
* post:
* description: Returns users
* produces:
* - application/json
* parameters:
* - name: user
* description: User object
* in: body
* required: true
* type: string
* schema:
* $ref: '#/definitions/NewUser'
* responses:
* 200:
* description: users
* schema:
* $ref: '#/definitions/User'
*/
app.post('/users', function(req, res) {
// Generate ID
req.body.id = Math.floor(Math.random() * 100) * 1
res.json(req.body);
});

swagger-ui-express 공식 문서
swagger-jsdoc 공식 문서

정적 파일을 제공할 폴더 설정

1
app.use(express.static('public'));

express.static을 활용하여 public 폴더를 정적 파일을 제공하기 위한 기본 폴더로 지정한다.

정적 파일 요청 양식

1
2
3
4
5
localhost:3000/images/kitten.jpg
localhost:3000/css/style.css
localhost:3000/js/app.js
localhost:3000/images/bg.png
localhost:3000/hello.html

기본 폴더명인 public은 입력하지 않아도 된다.

참조

여행 산업의 모바일화가 우리에게 시사하는 점

여행 상품, 이제는 모바일에서 더 많이 구매한다

인터파크 투어는 최근 모바일 거래 비중이 60%를 넘어서며 오프라인 시장을 역전했다.
뿐만 아니라 여행 전 꼭 거치는 환전 역시 이미 오프라인을 넘어서고 있다.

전통적인 오프라인 비즈니스의 대명사인 여행산업 마저 모바일 시대의 흐름에 변혁이 일어나고 있는 것이다.

또, 여행을 가서도 모든 일정과 맛집, 여행지 정보를 스마트폰을 통해 얻고 있으니 가히 ‘모바일 여행 시대’가 열렸다고 해도 무방할 것이다.

SNS를 통한 여행지 공유, 관광상품 마케팅의 최전선으로 부상

많은 사람들이 오프라인이 아닌 온라인 상에서 여행 상품을 검색하고 항공권, 숙박 시설, 환전까지 모든 부분을 모바일에서 해결하고 있다.
이에따라 많은 항공사와 여행사들은 기존의 TV광고, 지하철 내 광고보다 SNS를 통한 활발한 마케팅에 열을 올리고 있다.

이렇게 SNS 상에서 인기가 높은 여행지들은 폭발적인 매출의 증가로 이루어 지는데
한 예로 인스타그램 해시태그 4만개를 돌파한 인천 네스트 호텔과 2만 5000개를 넘긴 제주 히든 클리프 호텔의 경우 예약률이 평균 17~31%까지 치솟았다.

모바일 시대를 대비한 여행사의 새로운 패키지 상품

대부분의 사람들이 SNS 등의 매체를 통해 여행지를 결정하고 있으므로, 많은 여행사들은 아예 이런 SNS 마니아 층을 겨냥한 새로운 패키지 상품을 출시하고 있다.
실제로 이러한 패키지 상품은 태그, 댓글 등 자발적인 바이럴 마케팅 효과를 거두고 있으며, 매출도 상당히 높은 편이다.

SNS의 핵심은 무엇인가

SNS의 핵심은 무엇인가에 대한 질문에 두가지 답을 내놓는다.
첫째는 허영심, 둘째는 과시욕이다.
원래 SNS의 시초는 나와 내 주변 사람들의 소식을 듣고 소통하는 용도였으나, 이는 오프라인과 마찬가지로 각종 허례허식을 만들어 내었다.
사실 우리가 일상의 살면서 만나는 다양한 사람들과의 관계에는 기본적으로 ‘잘보이고자 하는 욕망’을 내재한다.
오랜만에 보는 동창회에는 가장 예쁘고 멋진 옷을 입고, 잘 빠진 차를 타고 가고 싶어하는 것이 인간의 허영이다.
이러한 인간의 욕망은 ‘온라인 세계’라는 가상의 세계에서 그 빛을 발하고 있다.
온라인 세계에서는 손쉽게 나의 ‘페르소나’를 바꿀 수 있고, 온라인 상에서 만나는 모든 사람들에게 나의 매력적이고 ‘잘 나가는’ 모습을 보여주고자 한다.

이러한 욕망을 기반으로 다양한 서비스가 성행하게 되었는데, ‘아프리카 TV의 별풍선’, ‘잘나가는 헬스 트레이너 에게 트레이닝 받기’ 처럼 남들에게 인정받는 셀럽들에게 관심을 주고, 그 사람과 온라인 상에서 관계를 맺게 도됨으로써 온라인에서의 자신의 사회적 입지를 올리고자 하는 열망을 반영한다.

이러한 SNS의 특성을 이해한다면, 우리는 사용자들의 공감을 살 수 있고, 새로운 비즈니스 기회를 발견할 수 있다.

우리는 어떻게 해야 하는가

이러한 SNS 를 통한 여행사들의 마케팅 방향에 대해서는 생각할 점이 많다.
여행사들이 꼼꼼히 정보를 수집하고 각종 여행 자료를 바탕으로 선정하는 여행지 보다 SNS 상에 유포되는 핫한 여행지들이 더욱 신빙성을 가지고 큰 영향력을 가진다는 사실을 우리는 인정해야 한다.
한켠으로는 정말 제대로된 정보에 기반하지 않고 단지 SNS상의 이슈화를 통해 매출 증대를 노리려는 약은 여행사들이 대거로 출현할 것인가 하는 우려도 있지만, SNS 시대에서 사람들의 마음을 울릴 다양한 컨텐츠를 개발하고 SNS라는 누구나 접근 가능한 매체를 통해 큰 영향력을 발휘할 수 있게 됨으로써 작은 여행사들은 거대 여행사들에 대항할 힘을 가지게 되었다.

이러한 풍조는 ‘인터넷’이 추구하는 방향과 그 행로가 일치하는데, 모든 개개인들이 거대 권력에 대항할 큰 힘을 가지게 되었다는 점에서 그러하다.
앞으로의 모바일 사회에서 우리는 SNS 를 기반으로 하는 새로운 시장의 원리를 받아들이고 대중을 공감할 훌륭한 SNS 컨텐츠의 개발을 바탕으로 경쟁력을 갖추어 나간다면, 거대 자본을 기반으로 하는 여행산업에서 새로운 비즈니스의 기회를 포착할 수 있을 것이다.

angular에서 bootstrap 활용을 위한 ng-bootstrap 패키지를 활용하여 모달을 사용한다.

ModalService의 준비

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { Injectable } from '@angular/core';
// 모달을 사용하기 위한 import
import {NgbModal, NgbActiveModal} from '@ng-bootstrap/ng-bootstrap';
import { LoginModalComponent } from '../_components/login-modal/login-modal.component';
import { ApplyCheckModalComponent } from '../_components/apply-check-modal/apply-check-modal.component';
@Injectable()
export class ModalService {
constructor(private modalService: NgbModal) {
}
openLoginModal(){
const modalRef = this.modalService.open(LoginModalComponent);
modalRef.componentInstance.name = 'World';
}

// open apply checking modal
openApplyCheckModal(){
const modalRef = this.modalService.open(ApplyCheckModalComponent);

//필요한 데이터를 전달
modalRef.componentInstance.name = 'World';
}
}

원래는 NgbModal 모듈을 이용하여 바로 특정 모달 컴포넌트를 여는 방식이지만
여러 페이지에서 다양한 모달을 쉽게 열고 닫기 위해서 새로운 ModalService 서비스를 만들었다.

이 서비스에서 열고자 하는 다양한 modal component를 import하여 쉽게 열 수 있다.

또한 MgbModal을 통해 open 한 modal에 componentInstance를 통해 data를 전달할 수 있다.

Modal Component.ts

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 { Component, OnInit } from '@angular/core';
import {NgbModal, NgbActiveModal} from '@ng-bootstrap/ng-bootstrap';
import { Router, ActivatedRoute } from '@angular/router';

@Component({
selector: 'app-login-modal',
templateUrl: './login-modal.component.html',
styleUrls: ['./login-modal.component.css']
})
export class LoginModalComponent implements OnInit {

constructor(public activeModal: NgbActiveModal) {

}

ngOnInit() {
// reset login status
this.authenticationService.logout();

}
close(){
this.activeModal.close();
}
}

모달 내에서 모달을 닫기 위해 NgbActiveModal 모듈을 사용하였다.

Modal Component.html

1
2
3
4
5
6
7
8
9
10
11
12
<div>
<div class="modal-header">
</div>
<div class="modal-body">
<div class="" style="text-align:center;">
<span style="font-weight:bold; margin-right:1rem;">아이디가 없으신가요?</span>
<span><a [routerLink]="['/register/selectTypePage']" style="color:#65b8b4; font-weight:bold;" (click)="close()">회원가입</a></span>
</div>
</div>
<div class="modal-footer">
</div>
</div>

모달을 한번 감싸주고
그 안에서 modal-header, modal-body, modal-footer class 들을 사용하여 안을 꾸며준다.

Module.ts 에 등록

1
2
3
4
5
6
7
8
9
10
@NgModule({
declarations: [
],
imports: [
],
providers: [
],
bootstrap: [],
entryComponents: [EmailCheckModalComponent]
})

app.module의 entryComponents에 등록하여 미리 컴포넌트를 로드시켜 놓는다.

참조하기

알고리즘 pseudo code

  1. 페이지 로딩 시점에 get(9)
  2. 스크롤바가 하단에 가까우면 받아오고, 받아오기 인덱스 true
    if(height<200px)
    get(9)
    getIndex=true

  3. 받아오는 동안에는 요청 안하기
    if(getIndex==true)
    return 0

  4. 받아온 값이 없을 경우에는 return 0하고 searchEndIndex를 true로 하기
    if(res.length==0)
    return 0
    searchEndIndex=true

HostListener Decorator Component 작성

아래와 같이 호스트의 이벤트를 감지한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { HostListener, Inject} from "@angular/core";
import { DOCUMENT } from "@angular/common";

export class LayoutNavComponent implements OnInit {
constructor(@Inject(DOCUMENT) private document: Document) { }
}
@HostListener("window:scroll", ['$event.target'])
onWindowScroll() {
//we'll do some stuff here when the window is scrolled
console.log(this.document.body.scrollHeight);
console.log("scrollTop:",this.document.documentElement.scrollTop);
let scrollHeight=this.document.documentElement.scrollHeight;
let readHeight=this.document.documentElement.scrollTop+this.document.documentElement.clientHeight;

if((-10<=(readHeight-scrollHeight))&&((readHeight-scrollHeight)<=10)){
this.groupService.getGroupsBySearchKey(this.locationService.locationDepth1, this.locationService.locationDepth2, this.groupService.searchKey);
}
}

scroll event의 성능향상을 위한 Tip

모든 이벤트 마다 onScroll 함수를 발생시키면 CPU의 성능을 많이 차지하므로
다음과 같이 함수에 delay를 주어 이를 해결할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
function () {
if (waiting) {
return;
}
waiting = true;

scroll();

setTimeout(function () {
waiting = false;
}, 100);
}

하드웨어 가속시키기

하드웨어 가속이란 그래픽 처리 장치를 이용하여 중앙처리 장치의 처리량을 줄이고, 브라우저의 렌더링을 효율화하는 것을 말한다.
css 작업에 하드웨어 가속을 활성화하면, 작업처리가 빨라져서 웹페이지의 렌더링을 보다 빠르게 할 수 있다.

특정 엘레멘트에 어떤 지시를 내리면 엘리먼트는 자신의 레이어에 분류되고 페이지에 있는 다른 엘리멘트와 독립되어 렌더링 된다.
결론만 말하면 css에 다음과 같은 속성을 붙이면 하드웨어를 가속화 하여 빨리 렌더링이 가능하다.

1
transform: 3dtransforms(0,0,0)

위 명령어는 3d가 아닌 엘레먼트에 가짜로 3d속성을 주므로써 해당 element의 렌더링을 cpu가 아닌 gpu가 담당하게 하여 렌더링 속도를 높이며, will-change 명령어는 엘레먼트에 어떤 변경을 할 것인지 브라우저에게 미리 알려주므로써 최적화 하는 것이다. 페이지는 순식간에 갱신돼 부드러운 화면처리가 가능하게 된다.

참조


html 상에서 input (type=file) 만들기

1
<input type="file" (change)="onChange($event)"></input>

onChange로 file 추출 및 저장하기

1
2
3
onChange(event){
let file = event.srcElement.files[0];
}

formData에 key, value 매핑하기

1
2
3
4
5
6
7
8
9
10
11
12

console.log(file);

폼데이터를 선언한다.
let formData:FormData = new FormData();

formData에 key와 value를 메겨준다.
formData.append('fileName',this.fileName+'.'+file.name.split('.')[1]);

여기서 file.name.split('.')[1]는 확장자를 찾아주는 것이다.

formData.append('imageFile', file, file.name);

참조자료

라우터 및 라우팅 파라미터 설정

Route를 활용하여 라우팅을 할 경로와 매칭하는 컴포넌트를 선언한다.

Example

1
2
3
4
5
export const routes: Routes = [
{ path: '', redirectTo: 'product-list', pathMatch: 'full' },
{ path: 'product-list', component: ProductList },
{ path: 'product-details/:id', component: ProductDetails }
];

여기서 :id는 해당 경로의 라우팅 할때의 파라미터 값이 들어간다.

특정 라우팅을 수행하는 링크 걸기

html태그에서 해당 라우팅으로 이동할 수 있도록 링크를 걸 수 있다.

Example - html 코드 내에서 라우팅 시키는 경우
undefined

example - 컴포넌트 상에서 라우팅 시키는 경우

1
2
3
goToProductDetails(id) {
this.router.navigate(['/product-details', id]);
}

참고자료

라우팅 이벤트를 감지하여 함수 실행시키기

라우팅이 일어날 때마다 해당 이벤트를 감지하여 함수를 실행시킨다.

Example

1
2
3
4
5
6
7
8
9
10
constructor(router:Router) {
router.events.subscribe(event:Event => {
if(event instanceof NavigationStart) {
}
// NavigationEnd
// NavigationCancel
// NavigationError
// RoutesRecognized
});
}

현 페이지의 라우터 전달값을 받기

1
2
3
4
5
this.route.params.subscribe(
params=>{
this.sectionIndex=params['pageIndex'];
}
)

structural Directive

ngFor

어레이를 돌면서 반복적으로 나타내기 위한 요소이다.
흔히 리스트를 출력할 때 사용한다.

ngFor 내에서 인덱스를 붙이는 경우의 활용

1
<li *ngFor="let item of items; index as i; trackBy: trackByFn">...</li>

ngFor 안에서 ngFor를 사용하는 경우의 활용

1
*ngFor="let subMenuName of menuName['submenu']; let idx = index;"

ngIf

Template And Data Binding in Angular를

Angular에서는 다양한 종류의 Binding을 제공한다.
binding의 종류에 따라

  1. (element / component / directive) property
  2. (element / component / directive) event
  3. (rarely) an attribute name.

의 세가지 경우가 있다.

property binding

html 태그의 다양한 속성을 바인딩 해준다.

대상
Element property
Component property
Directive property

example1
예제에서 보여지듯이 다양한 종류의 속성을 바인딩 할 수 있다.
이미지를 불러오는 경로인 (src) 컴포넌트의 입력값을 주는 [hero] 바인딩, class 속성을 가르쳐주는 [ngClass] 등의
바인딩 사례가 있다.

1
2
3
<img [src]="heroImageUrl">
<app-hero-detail [hero]="currentHero"></app-hero-detail>
<div [ngClass]="{'special': isSpecial}"></div>

example2
[ngStyle]을 활용하여 동적으로 background-image 할당.

1
<div class="profile-image" [ngStyle]="{ 'background-image': 'url(' + image + ')'}">

Event Binding

html 태그에서 발생하는 다양한 종류의 event에 대한 binding이다.

대상
Element event
Component event
Directive event

example

1
2
3
<button (click)="onSave()">Save</button>
<app-hero-detail (deleteRequest)="deleteHero()"></app-hero-detail>
<div (myClick)="clicked=$event" clickable>click me</div>

(click)을 통해 클릭을 하는 시점에 함수를 발동하는 등의 역할을 수행한다.

(focus) and (focusout)

input 등의 경우 입력모드가 발동된 시점이나 특정 element가 focus 된 경우 동작을 나타낸다.

example

1
<input name="date" type="text" (focus)="focusFunction()" (focusout)="focusOutFunction()">

(ngModelChange)

해당 모델의 데이터 값이 바뀔 때마다 특정 함수를 실행할 수 있다.

Example
undefined

참고자료

Two Way Binding을

이벤트와 속성등을 양방향으로 연결하는 바인딩이다.

대상
Event and property

example

1
<input [(ngModel)]="name">

Attribute Binding

대상
Attribute

example

1
<button [attr.aria-label]="help">help</button>

다음과 같이 value, selected를 바인딩 하는 데에도 사용 가능하다.
참조
undefined

class binding

특정 조건에서 class를 바인딩 해준다.

example

1
<div [class.special]="isSpecial">Special</div>

style binding

스타일 속성을 바인딩한다.

example

1
<button [style.color]="isSpecial ? 'red' : 'green'">

너비, 높이 등을 유동적으로 세팅하기 위해 다음과 같은 코드를 흔히 사용한다.

1
2
3
<div class="home-component"
[style.width.px]="width"
[style.height.px]="height">Some stuff in this div</div>

사이즈 조절을 위한 style binding

Structural Directive

HTML Layout에 영향을 주는 요소들이다.
대표적으로
ngIf ngFor 등이 있다.

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×