리엑트 Touch Event 사용방법

React에서 Touch Event 사용방법

이벤트 등록과 제거를 componentDidMount()과 componentWillUnmount()에서 해줘야 합니다.
SPA이기 때문에 이벤트 제거를 잘하지 않으면 어플리케이션 전체에 영향을 미치기 때문에 주의

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
import React, { Component } from 'react';
class ReactEvent extends Component {
constructor(props) {
super(props);
this.setEventTouch = this.setEventTouch.bind(this);
}

componentDidMount() {
document.addEventListener('touchstart', this.setEventTouch);
document.addEventListener('touchmove', this.setEventTouch);
document.addEventListener('touchend', this.setEventTouch);
}

componentWillUnmount(){
document.removeEventListener('touchstart', this.setEventTouch);
document.removeEventListener('touchmove', this.setEventTouch);
document.removeEventListener('touchend', this.setEventTouch);
}

setEventTouch(e = {}) {
switch (e.type){
case 'touchstart':
this.isDrag = false;
this.touchStartPositionX = e.changedTouches[0].clientX;
this.touchStartPositionY = e.changedTouches[0].clientY;

break;
case 'touchmove':
this.isDrag = true;

break;
case 'touchend':
this.touchEndPositionX = e.changedTouches[0].clientX;
this.touchEndPositionY = e.changedTouches[0].clientY;

break;
default:

}
}

render() {
return (<div>React Event Test</div>);
}

}

export default ReactEvent;

Touch event 종류

touchstart

터치 표면에 터치 포인터가 놓여지면 이벤트 전송

touchend

터치 포인터가 제거 되었을 때 이벤트 전송

touchmove

표면을 따라 터치 포인트를 이동할 때 이벤트 전송

Touchmove 이벤트 전송 속도는 브라우저에 따라 다르며 사용자 하드웨어 성능에 따라 다를 수도 있음

touchcancel

터치 포인트가 어떤 식으로든 중단되면 전송

Passive event

Passive event 는 특별히 모바일에서 scroll 성능 향상을 위한 새로운 웹표준 입니다.

기존 스크롤 이벤트는 canceled 를 할 수 없습니다. 그래서 passive 설정을 하지 못합니다.

그러나 handler 안에서 expensive work 를 방지해야 합니다.

scroll jank

페이지가 스크롤링 되기 전에 항상 리스너가 끝나기를 기다리는 지연이을 가리키는 말

passive event listener 는 addEventListener 의 options 파라미터에서 리스너가 스크롤을 절대 취소하지 않는다는 것을 나타내는 플래그를 설정할 수 있도록 하여 이 문제를 해결합니다.

addEventListener

Syntax

1
2
target.addEventListener(type, listener[, options]);
target.addEventListener(type, listener[, useCapture]);

Parameters

  • type

    Event type 지정

  • listener

    지정된 Event type이 발생했을 때 알림을 받을 callback 함수

  • options

    • capture: 이벤트 캡쳐링 적용 여부. 크롬 49부터 지원

    • once: 이벤트를 한번만 호출하고 해제되는 옵션. 크롬 55부터 지원

    • passive: 스크롤 성능 향상을 위한 옵션으로 true일 경우, 스크롤을 위해 블록되는 것을 방지한다. 이 경우, preventDefault를 사용할 수 없다. 크롬 51부터 지원
  • useCapture

Usage notes

1
2
3
4
5
6
7
8
9
document.addEventListener('touchstart', handler, false);


// Chrome 49 부터 EventListenerOptions 지원
document.addEventListener("touchstart", handler, {
capture: false, // Chrome 49
once: false, // Chrome 55
passive: false // Chrome 51
});

addEventListener 하위 브라우저 지원 방법

Chrome 54+ 부터 EventListenerOptions의 passive 속성이 특별한 상황일 경우에는 기본값이 true로 설정된다.

document또는 body에 이벤트 리스너를 추가할때, touchstart, touchmove와 같이 스크롤이 블록되는 이벤트인 경우, passive의 기본 속성값은 true가 된다.

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
document.addEventListener(
"touchmove",
function(e) {
e.preventDefault();
},
isPassive()
? {
capture: false,
passive: false
}
: false
);

function isPassive() {
var supportsPassiveOption = false;
try {
addEventListener(
"test",
null,
Object.defineProperty({}, "passive", {
get: function() {
supportsPassiveOption = true;
}
})
);
} catch (e) {}
return supportsPassiveOption;
}

References

이니시스 모바일 결제 연동

PG사를 통한 결제 연동

자사 서비스에 결제를 붙이기 위해서는 PG사와 계약 후 진행해야 합니다.

각 카드사별로 계약을 한 후 연동이 어려울 뿐만 아니라 일정 볼륨이 되지 않으면 카드사에서 개별 계약이 되지 않기 때문에 PG를 통해 결제를 연동하는 방법이 좋습니다.

우리나라 PG사는 이니시스, LG U+, KCP 등이 있는데 그중에서 이니시스 모바일 결제 모듈 INIpay(Mobile) 연동 구성을 정리하겠습니다.

결제 연동 시 사용되는 인프라 구성

  • WEB Server: Node 6
  • API Server: Spring 4
  • DB: MariaDB
  • Infra: AWS

결제 프로세스 정리

자사 결제 페이지 부터 결제 프로세스의 시작으로 보면 됩니다.

자사 결제 관련 정보(상품명, 가격, 수량 등)을 DB에 저장 후 이니시스 결제 페이지로 redirect가 되는 구조입니다.

자세한 내용은 아래에서 정리합니다.

1. 자사 서비스 결제 화면

  1. 자사 서비스의 주문페이지 요청
  2. 주문 페이지를 사용자 화면에 노출
  3. 주문 정보(상품명, 가격, 수량 등)을 DB에 저장
  4. 이니시스 결제 페이지로 redirect

2. 이니시스 결제 화면

여기서 부터는 이니시스 페이지가 보여집니다.

이니시스 페이지에서 약관동의 후 결제할 카드를 선택합니다.

그러면 선택한 카드사 결제페이지에서 결제완료 후 자사 결제 완료 화면으로 redirect 되며 결제가 완료됩니다.

시스템 구성도에서는 위와 같은 플로우로 움직입니다.

이니시스 결제 시 일어나는 network 패킷

위의 그림은 이니시스 서버에서 사용자 결제가 끝난후 WebServer로 Callback을 주는데 까지의 내용입니다.

이니시스에서 마지막으로 응답을 주는 check.php에서 WebServer로 결제 관련 내용을 전달합니다.

P_TID, P_REG_URL 등의 정보를 자사 callback 으로 전달합니다.

파라미터의 상세 내용은 이니시스 문서를 참고하시면 됩니다.

check.php 응답 내용

1
2
3
4
5
function returnAuth() {
var form = document.getElementById("return_form");
form.action = "http://localhost:8080/order/callback";
form.submit();
}

결론

처음 결제 연동을 할 때 자사 서비스와 PG사와 통신 부분이 잘 들어오지 않습니다.

그러나 해당 프로세스를 이해하고 나면 HTTP 통신으로 다 끝나기 때문에 크게 어렵지 않습니다.

  • PC 결제는 INIpay(WebStandard) 모듈을 연동되며 크게 다르지 않습니다.

크로스도메인(Crossdomain)에서 브라우저 CORS 정책과 Simple Request, Preflighted Request 정리

브라우저는 보안상의 이유로 script안에서 crossdomain에 대한 HTTP 요청을 제한한다
XMLHttpRequest를 사용하면 crossdomain 요청을 하지 못했지만 개발자들의 요청에 의해 W3C에서 Cross-Origin Resource Sharing(CORS)를 권고한다.

CORS에서 요청은 Simple Request, Preflight request 2가지 방법있으며 아래에 자세한 설명이 되어있다.

CORS에서 사용가능한 Header정보

사용 가능한 methods

  • GET
  • HEAD
  • POST

사용가능 headers

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type

Content-Type에서 사용가능한 값

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

Simple requests


사용가능한 methods, header, content-type요청은 바로 통신가능함

Crossdomain 요청 예제

서버 응답이 아래와 같으면 모든 cross-domain 접근 허용
Access-Control-Allow-Origin: *

서버 응답이 아래와 같으면 모든 http://foo.example에서만 접근 허용
Access-Control-Allow-Origin: http://foo.example

Preflighted requests

사용가능한 methods, header, content-type이외의 요청은 Preflighted requests로 진행.
Preflighted requests 요청은 OPTIONS method로 사용할 methods, headers 요청 후 서버에서 허락하면 실제 통신 가능.

Preflighted requests 예제


Resources

IE9 에서 304 Not Modified가 응답되는 이유(Conditional Request, Unconditional Request)

웹브라우저는 2가지 타입의 요청을 한다. ( Conditional Request , Unconditional Request )
Unconditional Request는 요청하는 자원이 로컬에 캐시가 되어있지 않을 때 생기는 요청이다.
이경우 서버는 200 OK 로 응답한다.
웹브라우저가 Unconditional Request 후에 또 요청을 하게 된다면 캐시 파일이 최신인지를 판단하여 실제 네트워크에서 요청이 가지 않고 캐시된 자원을 사용한다.
캐시된 파일이 만료가 되었으면( max-age, Expires 판단) 웹브라우저는 conditional request를 한다.
클라이언트는 이전에 캐싱 된 응답이 여전히 유효하고 재사용되어야하는지 여부를 판별하기 위해 서버에 conditional request를 한다.
Conditional Request는 If-Modified-Since 및 / 또는 If-None-Match 헤더가 포함되어 있고 이 헤더는 브라우저에 캐시버전을 서버에게 알려준다.
서버는 본문이없는 HTTP / 304 Not Modified 헤더를 반환하여 클라이언트의 복사본이 여전히 최신 상태임을 나타내거나 새 버전의 리소스로 HTTP / 200 OK 응답을 반환하여 갱신한다.
IE가 캐시된 항목을 conditional request 하는 이유
Cache-Control, Expires를 보고 캐시된 자원이 더이상 최신이 아닐 때

References

http://www.dashbay.com/2011/05/internet-explorer-caches-ajax/
https://blogs.msdn.microsoft.com/ieinternals/2010/07/08/understanding-conditional-requests-and-refresh/

아이폰 사파리 버그/이슈 정리

아이폰 사파리에서 scroll 이벤트 버그

1
2
3
window.addEventListener('scroll', function(e) {
// do something
});
  • 위의 코드는 사용자가 스크롤을 할때마다 발생되어야 하지만 iOS UIWebViews에서는 스크롤이 끝난다음 호출이 된다.
  • WKWebViews 에서는 발생하지 않기 때문에 WKWebViews로 변경하면 해결할 수 있다.

References

아이폰 사파리에서 클릭 이벤트가 되지 않을 경우

아이폰 사파리에서는 a link, input을 제외하고 클릭 이벤트에 대해 event delegation을 지원하지 않는다.

아래 글을 읽어 보면 메모리 관리 문제가 있어 일부러 막은것으로 추정된다.

그래서 클릭 이벤트 동작을 시키기 위해서 <a href=‘#’></a> 감싸 우회하면 동작을 한다.

References

touch시 화면 깜빡임 없애는 방법

css에 아래 내용을 추가해 주면 됩니다.

1
-webkit-tap-highlight-color: transparent;

References

iPhone XS 에서 키보드가 올라온 후 스크롤 이상 현상 해결 방법

input에 터치 후 키보드가 올라오고 난 후 사라지면 스크롤이 키보드가 올라오기전 상태로 돌아가지 않는 버그가 있습니다.

해당 버그는 아래와 같이 회피해야 할거 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...

handleInputFocus = (e) => {
if(this.isIOS){
this.scrollY = window.pageYOffset || document.documentElement.scrollTop;
}
}

handleInputBlur = (e) => {
if(this.isIOS){
window.scroll(0, this.scrollY)
}
}

...

render() {
return (
<textarea onFocus={this.handleInputFocus} onBlur={this.handleInputBlur}></textarea>
)
}

References

안드로이드 브라우저와 웹뷰는 같은가?

구글 I/O 2012 fireside chat

Q: With Chrome on Android what happens to the original Android browser and WebView?

A: ICS->JB upgrade will not pre-install Chrome. WebView will be converted to Chromium based code. Android and Chrome engineers are working together.

Q: Will WebView get updated?

A: WebView and Chrome browser will be the same when the OS is upgraded. Every 6 weeks Chrome will be updated but WebView will not. Just to be careful not to break anything.

Android 4.4+ KitKat ships without browser app. OEMs have to license Chrome or build their own

구글 I/O 2012 fireside chat 에서 위와 같은 내용이 있었습니다.

웹뷰는 Chrominum 기반으로 변환된다는 내용입니다.

결론적으로 안드로이드 4.4는 브라우저와 웹뷰가 동일하게 렌더링 된다고 보면 될거 같습니다.

그러나 삼성 핸드폰은 자체 sbrower를 탑재하기 때문에 sbrowser 특성을 고려하여 개발해야 합니다.

제조사들이 4.4 부터 크롬 라이센스를 사야하거나 자체 개발한 브라우저를 탑재해야 할거 같습니다.

삼성은 SBrowser가 있는거고 제조사별 알 수 없는 버그들이 양산되는 상황입니다…

결론

안드로이드 stock 브라우저(크롬 아님)와 웹뷰 렌더링 결과는 같은걸로 생각하고 개발하면 됩니다.

(제조사 브라우저에 따라 예외 사항이 생길 수 있음)

References

삼성 sbrowser 버그 정리

삼성 핸드폰에서만 글자간격이 맞지 않는 이유

삼성은 자체 브라우저인 sbrower를 사용한다.

그래서 크롬에서 개발한 웹페이지가 삼성 단말에서 깨져 보이는 경우가 있는데

그건 line-height를 각 요소마다 지정해주면 막을 수 있다.

엘지는 크롬에서 개발한 것과 동일하게 보여준다.

엘지는 자체 브라우저가 아닌 크롬을 사용하기 때문인거 같다.

갤럭시 노트4 셀렉트박스 버그

노트4 sbrower 네이티브 셀렉트 박스 UI는 아래와 같다.
유독 노트4 sbrowser에서 셀렉트박스가 정상 작동하지 않는다.
사용자가 셀렉트박스 항목을 선택하면 셀렉트 박스가 사라져야하는데 사라지지 않는다.

크롬 디버그로 잡아서 강제로 reload, history.back(), focus, click등의 이벤트를 줘도 사라지지 않았다.
네이버도 에러 처리를 따로 하지 않은듯 하다.

노트4 유저가 많다면 셀렉트박스 UI를 사용하지 않던지, DIV로 만들어서 사용해야 한다.

자바스크립트 이벤트 바인딩

이벤트 바인딩(Event Binding)

1. HTML Event Handler

1
<button onclick="onClickFunction">Click!</button>

2. Dom Event Handler

1
document.getElementById('btn').onclick = onClickBtn;

3. EventListener

  • IE8이하에서는 addEventListener대신 attachEvent 메서드를 사용해야 한다.
    1
    2
    document.getElementById('btn').addEventListener('click', onClickBtn, false);
    ~

이벤트 흐름(Event Flow)

이벤트 버블링(event bubbling)

  • 이벤트가 안쪽 노드에서 바깥쪽으로 전파
  • HTML, DOM Event Handler를 사용할 경우 이벤트 버블링만 사용한다.

이벤트 캡처링(event capturing)

  • 이벤트가 바깥쪽 노드에서 안쪽으로 전파
  • EventListener를 사용할 경우 마지막 매개변수를 사용하여 이벤트 흐름을 정할 수 있다.
    true: 이벤트 캡처 , false: 이벤트 버블링(디폴트값)

이벤트 위임(Event Delegation)

  • 이벤트 리스너를 많이 추가하면 메모리를 많이 소모하게 되어 전체 성능이 저하된다.
  • 이벤트는 부모 요소에 영향을 미치기 때문에 부모 element에 이벤트를 바인딩 후 event.taraget을 통해 어떤 element에서 발생했는지 판단할 수 있다.
  • 동적으로 element가 생성되더라도 부모 element에 이벤트가 위임되어 있으면 동일한 기능이 수행된다.
  • 코드가 간결해지고 유지보수에 좋다.

기본 동작 변경

preventDefault()

stopPropagation()

  • 부모 요소에 이벤트 리스너가 있을경우 버블링 중단을 통해 상위 이벤트 실행을 막을 수 있다.

return false

  • element의 기본동작과 이벤트 버블링, 캡처링 모두 중단된다.

파이어베이스 Cloud Functions 설정방법

파이어베이스

파이어베이스는 구글에서 만든 클라우드 인프라 입니다.
그 중에서 Serverless 서비스인 Cloud Functions를 사용하는 방법을 알아 보겠습니다.

파이어베이스 firebase-tools 설치

1
$ npm install -g firebase-tools

파이어베이스 로그인

설치 후 firebase login을 실행하면 브라우저가 실행됩니다.
파이어페이스를 사용할 계정으로 로그인 하시면 됩니다.

1
$ firebase login

파이어베이스 Cloud Functions 프로젝트 생성

firebase init을 실행 후 안내에 따라 필요한 서비스들을 설치합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ firebase init

🔥🔥🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥 🔥🔥🔥 🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥
🔥🔥 🔥🔥 🔥🔥 🔥🔥 🔥🔥 🔥🔥 🔥🔥 🔥🔥 🔥🔥 🔥🔥 🔥🔥
🔥🔥🔥🔥🔥🔥 🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥
🔥🔥 🔥🔥 🔥🔥 🔥🔥 🔥🔥 🔥🔥 🔥🔥 🔥🔥 🔥🔥 🔥🔥 🔥🔥
🔥🔥 🔥🔥🔥🔥 🔥🔥 🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥 🔥🔥 🔥🔥 🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥

You're about to initialize a Firebase project in this directory:

.../test

? Which Firebase CLI features do you want to setup for this folder? Press Space to select features, then Enter to confirm your choices. (Press <space> to select)
❯◯ Database: Deploy Firebase Realtime Database Rules
◯ Firestore: Deploy rules and create indexes for Firestore
◯ Functions: Configure and deploy Cloud Functions
◯ Hosting: Configure and deploy Firebase Hosting sites
◯ Storage: Deploy Cloud Storage security rules

파이어베이스 배포

1
2
$ firebase deploy --only functions # functions만 배포 
$ firebase deploy --only functions:addMessage # functions중 개별 함수 배포

Resource