์น์ฑ ์ฐ๋
React Native ์น์ฑ ์ฐ๋์ ์ํด์ ์๋์ ์์ ์ด ํ์ํฉ๋๋ค.
- React Native SDK์์ WebView๋ฅผ ๋ ๋๋งํ๋ ์์ ์ ๋ธ๋ฆฟ์ง๋ฅผ ์ถ๊ฐํฉ๋๋ค.
- WebView์์ ์ฌ์ฉํ๋ JavaScript SDK๋ฅผ ๋ํํ์ฌ ReactNative ์ ์ฉ Client๋ฅผ ์์ฑํด์ผํฉ๋๋ค.
์น์ฑ์ ๋ํด์๋ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์.
WebView
๋ฅผ ํตํด ์์ฌ ์น์ฌ์ดํธ๋ฅผ ๋๋๋งํ๋ ๊ฒฝ์ฐ, ์๋ ์ค์ ์ ํตํด ์น์ฌ์ดํธ์ ํฌํจ๋ ํตํด JavaScript SDK๋ฅผ ์น์ฌ์ดํธ ์ฝ๋ ๋ณ๊ฒฝ์์ด ํตํด React Native SDK ๊ธฐ๋ฅ๊ณผ ๋์ผํ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
React Native
React Native SDK๊ฐ ์ค์น๋ ํด๋ผ์ด์ธํธ ์ฌ์ด๋์์ ์๋ ์์ ์ ์งํํด์ฃผ์ธ์.
Github: Source
- ์ ์์ค ์ฝ๋์
useHackleWebviewManager
๋ฅผ ํ๋ก์ ํธ์ ์ถ๊ฐํฉ๋๋ค. useHackleWebviewManager
hook์ ํตํด WebView์ prop์ ์ ๋ฌํฉ๋๋ค.
ReactNative WebView JS Injection
๊ตฌ๋ถ | ์ค๋ช |
---|---|
hackleInjectedJavaScript | ์น๋ทฐ์ JavaScript SDK๊ฐ React Native SDK์๊ฒ ๊ธฐ๋ฅ์ ๋์ ์ฒ๋ฆฌํ๋๋ก ๋ฉ์ธ์ง๋ฅผ ์ ๋ฌํฉ๋๋ค. |
onHackleMessage | javaScript SDK๋ก ๋ถํฐ ์ ๋ฌ๋ฐ์ ๋ฉ์ธ์ง๋ฅผ React Native SDK์์ ์ฒ๋ฆฌํฉ๋๋ค. |
const hackleClient = createInstance("YOUR_SDK_KEY");
function App() {
const webviewRef = useRef(null);
const { hackleInjectedJavaScript, onHackleMessage, isHackleMessageEvent } =
useHackleWebviewManager({
postMessage: (message) => webviewRef.current?.postMessage(message),
hackleClient,
});
return (
<HackleProvider hackleClient={hackleClient}>
<SafeAreaView>
<View>
<WebView
ref={webviewRef}
source={{ uri: "web_url" }}
injectedJavaScriptBeforeContentLoaded={hackleInjectedJavaScript}
onMessage={(e) => {
if (isHackleMessageEvent(e)) {
onHackleMessage(e.nativeEvent.data);
return;
}
}}
/>
</View>
</SafeAreaView>
</HackleProvider>
);
}
JavaScript
JavaScript SDK๊ฐ ์ค์น ๋ ์น ์ฌ์ด๋์์ ์๋ ์์ ์ ์งํํด์ฃผ์ธ์.
JavaScript ๋ธ๋ฆฟ์ง ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ ๋์์ ํฉ๋๋ค.
- JavaScript SDK์ hackleClient์ ๋์ผํ ์ธํฐํ์ด์ค๋ฅผ ๊ฐ๋ Wrapper Client๋ฅผ ์์ฑํฉ๋๋ค.
- Web ํ๊ฒฝ์์ ๋ฐ์ํ๋ track, variation, featureFlag ๋ฑ์ ์์ฒญ์ ๋ฐ์์ React Native SDK๋ก ๋น๋๊ธฐ ์ ๋ฌํฉ๋๋ค.
- ์ด๋ฅผ ์ํ ๋ธ๋ฆฟ์ง ํํ์ ์ฝ๋์ด๊ณ , ๋ชจ๋ ๋ฉ์๋๊ฐ Promise๋ฅผ ๋ฐํํ๋ค๋ ์ ์ด ๊ธฐ์กด hackleClient์์ ์ฐจ์ด์ ๋๋ค.
React Native์์ ๋ธ๋ฆฟ์ง ์์ ์ด ์ ํ๋์ด์ผํฉ๋๋ค.
- ReactNative WebView์
useHackleWebviewManager
๋ฅผ ์ ์ฉํด์ผํฉ๋๋ค.- ์ ์์ ์ด ์ ํ๋์ง ์์ ๊ฒฝ์ฐ ์น์ฑ ์ฐ๋์ด ์ ์์ ์ผ๋ก ์ด๋ฃจ์ด์ง์ง ์์ต๋๋ค.
Github: Full Source Code
import{createInstance as e,Decision as t,DecisionReason as r,FeatureFlagDecision as n}from"@hackler/javascript-sdk";import{v4 as o}from"uuid";var s=function(e,t){return s=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},s(e,t)};function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}s(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}function u(e,t,r,n){return new(r||(r=Promise))(function(o,s){function i(e){try{a(n.next(e))}catch(e){s(e)}}function u(e){try{a(n.throw(e))}catch(e){s(e)}}function a(e){var t;e.done?o(e.value):(t=e.value,t instanceof r?t:new r(function(e){e(t)})).then(i,u)}a((n=n.apply(e,t||[])).next())})}function a(e,t){var r,n,o,s={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]},i=Object.create(("function"==typeof Iterator?Iterator:Object).prototype);return i.next=u(0),i.throw=u(1),i.return=u(2),"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function u(u){return function(a){return function(u){if(r)throw new TypeError("Generator is already executing.");for(;i&&(i=0,u[0]&&(s=0)),s;)try{if(r=1,n&&(o=2&u[0]?n.return:u[0]?n.throw||((o=n.return)&&o.call(n),0):n.next)&&!(o=o.call(n,u[1])).done)return o;switch(n=0,o&&(u=[2&u[0],o.value]),u[0]){case 0:case 1:o=u;break;case 4:return s.label++,{value:u[1],done:!1};case 5:s.label++,n=u[1],u=[0];continue;case 7:u=s.ops.pop(),s.trys.pop();continue;default:if(!(o=s.trys,(o=o.length>0&&o[o.length-1])||6!==u[0]&&2!==u[0])){s=0;continue}if(3===u[0]&&(!o||u[1]>o[0]&&u[1]<o[3])){s.label=u[1];break}if(6===u[0]&&s.label<o[1]){s.label=o[1],o=u;break}if(o&&s.label<o[2]){s.label=o[2],s.ops.push(u);break}o[2]&&s.ops.pop(),s.trys.pop();continue}u=t.call(e,s)}catch(e){u=[6,e],n=0}finally{r=o=0}if(5&u[0])throw u[1];return{value:u[0]?u[1]:void 0,done:!0}}([u,a])}}}"function"==typeof SuppressedError&&SuppressedError;var c=function(){function e(){this.listeners={}}return e.prototype.on=function(e,t){this.listeners[e]=(this.listeners[e]||[]).concat(t)},e.prototype.off=function(e,t){this.listeners[e]=(this.listeners[e]||[]).filter(function(e){return e!==t})},e.prototype.emit=function(e,t){(this.listeners[e]||[]).forEach(function(e){e(t)})},e}();function p(e,t){var r=t.timeoutMillis,n=void 0===r?5e3:r,o=t.onTimeout;return new Promise(function(t,r){e(t,r),setTimeout(function(){o(t,r)},n)})}var l=function(){function e(){this.injectFlag="_hackle_injected"}return e.prototype.isInjectedEnvironment=function(){return!("undefined"==typeof window||!window.ReactNativeWebView)&&!0===window[this.injectFlag]},e.prototype.createInstance=function(e,t){if(this.isInjectedEnvironment()){var r=new f(window.ReactNativeWebView);return new d(e,t,r)}return new h(e,t)},e}(),f=function(){function e(e){this.port=e,this.cleanUp=function(){}}return e.prototype.addEventListener=function(e){window.addEventListener("message",e,!0),this.cleanUp=function(){return window.removeEventListener("message",e,!0)}},e}(),d=function(e){function s(t,r,n){var o=e.call(this)||this;return o.sdkKey=t,o.config=r,o.messageTransceiver=n,o.messageFieldName="_hackle_message",o.resolverRecord=new Map,o.messageTransceiver.addEventListener(function(e){var t,r=e;if(r.data&&"undefined"!==r.data)try{var n=JSON.parse(r.data);if(o.messageFieldName in n){var s=n[o.messageFieldName],i=s.id,u=s.payload;i&&(null===(t=o.resolverRecord.get(i))||void 0===t||t(u),o.resolverRecord.delete(i))}}catch(e){console.log("[DEBUG] Hackle: Failed to parse message. If message not sent by hackle, please ignore this. ".concat(e))}}),o}return i(s,e),s.prototype.createMessage=function(e,t,r){var n;return JSON.stringify(((n={})[this.messageFieldName]={id:e,type:t,payload:r},n))},s.prototype.createId=function(){return o()},s.prototype.getSessionId=function(){return u(this,void 0,void 0,function(){var e,t=this;return a(this,function(r){return e=this.createId(),[2,p(function(r){t.messageTransceiver.port.postMessage(t.createMessage(e,"getSessionId",null)),t.resolverRecord.set(e,r)},{onTimeout:function(e){return e("")}})]})})},s.prototype.getUser=function(){return u(this,void 0,void 0,function(){var e,t=this;return a(this,function(r){return e=this.createId(),[2,p(function(r){t.messageTransceiver.port.postMessage(t.createMessage(e,"getUser",null)),t.resolverRecord.set(e,r)},{onTimeout:function(e){return e({})}})]})})},s.prototype.setUser=function(e){var t=this,r=this.createId();return p(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"setUser",{user:e})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},s.prototype.setUserId=function(e){var t=this,r=this.createId();return p(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"setUserId",{userId:e})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},s.prototype.setDeviceId=function(e){var t=this,r=this.createId();return p(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"setDeviceId",{deviceId:e})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},s.prototype.setUserProperty=function(e,t){var r=this,n=this.createId();return p(function(o){r.messageTransceiver.port.postMessage(r.createMessage(n,"setUserProperty",{key:e,value:t})),r.resolverRecord.set(n,o)},{onTimeout:function(e){return e(null)}})},s.prototype.setUserProperties=function(e){var t=this,r=this.createId();return p(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"setUserProperties",{properties:e})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},s.prototype.updateUserProperties=function(e){var t=this,r=this.createId();return p(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"updateUserProperties",{operations:e.toRecord()})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},s.prototype.updatePushSubscriptions=function(e){var t=this,r=this.createId();return p(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"updatePushSubscriptions",{operations:e.toRecord()})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},s.prototype.updateSmsSubscriptions=function(e){var t=this,r=this.createId();return p(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"updateSmsSubscriptions",{operations:e.toRecord()})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},s.prototype.updateKakaoSubscriptions=function(e){var t=this,r=this.createId();return p(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"updateKakaoSubscriptions",{operations:e.toRecord()})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},s.prototype.setPhoneNumber=function(e){var t=this,r=this.createId();return p(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"setPhoneNumber",{phoneNumber:e})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},s.prototype.unsetPhoneNumber=function(){var e=this,t=this.createId();return p(function(r){e.messageTransceiver.port.postMessage(e.createMessage(t,"unsetPhoneNumber",null)),e.resolverRecord.set(t,r)},{onTimeout:function(e){return e(null)}})},s.prototype.resetUser=function(){var e=this,t=this.createId();return p(function(r){e.messageTransceiver.port.postMessage(e.createMessage(t,"resetUser",null)),e.resolverRecord.set(t,r)},{onTimeout:function(e){return e(null)}})},s.prototype.variation=function(e){var t=this,r=this.createId();return p(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"variation",{experimentKey:e})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e("A")}})},s.prototype.variationDetail=function(e){return u(this,void 0,void 0,function(){var n,o,s,i,u,c,l=this;return a(this,function(a){switch(a.label){case 0:n=this.createId(),a.label=1;case 1:return a.trys.push([1,3,,4]),[4,p(function(t){l.messageTransceiver.port.postMessage(l.createMessage(n,"variationDetail",{experimentKey:e})),l.resolverRecord.set(n,t)},{onTimeout:function(e,t){return t()}})];case 2:return o=a.sent(),i=(s=o).variation,u=s.reason,c=s.parameters,[2,t.of(i,u,new v(null!=c?c:{}))];case 3:return a.sent(),[2,t.of("A",r.EXCEPTION)];case 4:return[2]}})})},s.prototype.isFeatureOn=function(e){var t=this,r=this.createId();return p(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"isFeatureOn",{featureKey:e})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(!1)}})},s.prototype.featureFlagDetail=function(e){return u(this,void 0,void 0,function(){var t,o,s,i,u,c,l=this;return a(this,function(a){switch(a.label){case 0:t=this.createId(),a.label=1;case 1:return a.trys.push([1,3,,4]),[4,p(function(r){l.messageTransceiver.port.postMessage(l.createMessage(t,"featureFlagDetail",{featureKey:e})),l.resolverRecord.set(t,r)},{onTimeout:function(e,t){return t()}})];case 2:return o=a.sent(),i=(s=o).isOn,u=s.reason,c=s.parameters,[2,new n(i,u,new v(null!=c?c:{}),void 0)];case 3:return a.sent(),[2,n.off(r.EXCEPTION)];case 4:return[2]}})})},s.prototype.track=function(e,t){var r=this,n=this.createId();return p(function(o){r.messageTransceiver.port.postMessage(r.createMessage(n,"track",{event:e,user:t})),r.resolverRecord.set(n,o)},{onTimeout:function(e){return e(null)}})},s.prototype.trackPageView=function(){return u(this,void 0,void 0,function(){return a(this,function(e){return[2]})})},s.prototype.remoteConfig=function(){var e=this;return new m(function(t,r){var n=e.createId();return p(function(o){e.messageTransceiver.port.postMessage(e.createMessage(n,"remoteConfig",{key:t,defaultValue:r})),e.resolverRecord.set(n,o)},{onTimeout:function(e){return e(r)}})})},s.prototype.showUserExplorer=function(){var e=this,t=this.createId();return p(function(r){e.messageTransceiver.port.postMessage(e.createMessage(t,"showUserExplorer",null)),e.resolverRecord.set(t,r)},{onTimeout:function(e){return e(null)}})},s.prototype.hideUserExplorer=function(){return u(this,void 0,void 0,function(){var e,t=this;return a(this,function(r){return e=this.createId(),[2,p(function(r){t.messageTransceiver.port.postMessage(t.createMessage(e,"hideUserExplorer",null)),t.resolverRecord.set(e,r)},{onTimeout:function(e){return e(null)}})]})})},s.prototype.fetch=function(){var e=this,t=this.createId();return p(function(r){e.messageTransceiver.port.postMessage(e.createMessage(t,"fetch",null)),e.resolverRecord.set(t,r)},{onTimeout:function(e){return e(null)}})},s}(c),h=function(t){function r(r,n){var o=t.call(this)||this;return o.client=e(r,n),o}return i(r,t),r.prototype.emitUserUpdated=function(){this.emit("user-updated",JSON.stringify(this.getUser()))},r.prototype.getSessionId=function(){return Promise.resolve(this.client.getSessionId())},r.prototype.getUser=function(){return Promise.resolve(this.client.getUser())},r.prototype.setUser=function(e){return this.emitUserUpdated(),Promise.resolve(this.client.setUser(e))},r.prototype.setUserId=function(e){return this.emitUserUpdated(),Promise.resolve(this.client.setUserId(e))},r.prototype.setDeviceId=function(e){return this.client.setDeviceId(e),this.emit("user-updated",e),Promise.resolve()},r.prototype.setUserProperty=function(e,t){return this.emitUserUpdated(),Promise.resolve(this.client.setUserProperty(e,t))},r.prototype.setUserProperties=function(e){return this.emitUserUpdated(),Promise.resolve(this.client.setUserProperties(e))},r.prototype.updateUserProperties=function(e){return this.emitUserUpdated(),Promise.resolve(this.client.updateUserProperties(e))},r.prototype.updatePushSubscriptions=function(e){return Promise.resolve(this.client.updatePushSubscriptions(e))},r.prototype.updateSmsSubscriptions=function(e){return Promise.resolve(this.client.updateSmsSubscriptions(e))},r.prototype.updateKakaoSubscriptions=function(e){return Promise.resolve(this.client.updateKakaoSubscriptions(e))},r.prototype.setPhoneNumber=function(e){return Promise.resolve(this.client.setPhoneNumber(e))},r.prototype.unsetPhoneNumber=function(){return Promise.resolve(this.client.unsetPhoneNumber())},r.prototype.resetUser=function(){return this.emitUserUpdated(),Promise.resolve(this.client.resetUser())},r.prototype.variation=function(e){var t=this;return new Promise(function(r){setTimeout(function(){r(t.client.variation(e))},3e3)})},r.prototype.variationDetail=function(e){return Promise.resolve(this.client.variationDetail(e))},r.prototype.isFeatureOn=function(e){var t=this;return new Promise(function(r){setTimeout(function(){r(t.client.isFeatureOn(e))},3e3)})},r.prototype.featureFlagDetail=function(e){return Promise.resolve(this.client.featureFlagDetail(e))},r.prototype.track=function(e){return Promise.resolve(this.client.track(e))},r.prototype.trackPageView=function(e){return Promise.resolve(this.client.trackPageView(e))},r.prototype.remoteConfig=function(){return this.client.remoteConfig()},r.prototype.showUserExplorer=function(){return Promise.resolve(this.client.showUserExplorer())},r.prototype.hideUserExplorer=function(){return Promise.resolve(this.client.hideUserExplorer())},r.prototype.fetch=function(){return Promise.resolve(this.client.fetch())},r}(c),v=function(){function e(e){this.parameters=e,this.parameters=e}return e.prototype.get=function(e,t){var r=this.parameters[e];return null==r?t:null==t||typeof r==typeof t?r:t},e}(),m=function(){function e(e){this.configFetcher=e,this.configFetcher=e}return e.prototype.get=function(e,t){return u(this,void 0,void 0,function(){var r,n;return a(this,function(o){switch(o.label){case 0:return o.trys.push([0,2,,3]),[4,this.configFetcher(e,t)];case 1:if(!(r=o.sent()))throw new Error("invoke result data not exists");switch(typeof t){case"number":return[2,Number(r)];case"boolean":return[2,Boolean(r)];default:return[2,r]}case 2:return n=o.sent(),console.error("Unexpected exception while deciding remote config parameter[".concat(e,"]. Returning default value. : ").concat(n)),[2,t];case 3:return[2]}})})},e}();export{l as default};
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@hackler/javascript-sdk"),require("uuid")):"function"==typeof define&&define.amd?define(["@hackler/javascript-sdk","uuid"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).HackleManager=t(e.Hackle,e.uuid)}(this,function(e,t){"use strict";var r=function(e,t){return r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},r(e,t)};function n(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}function o(e,t,r,n){return new(r||(r=Promise))(function(o,s){function i(e){try{a(n.next(e))}catch(e){s(e)}}function u(e){try{a(n.throw(e))}catch(e){s(e)}}function a(e){var t;e.done?o(e.value):(t=e.value,t instanceof r?t:new r(function(e){e(t)})).then(i,u)}a((n=n.apply(e,t||[])).next())})}function s(e,t){var r,n,o,s={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]},i=Object.create(("function"==typeof Iterator?Iterator:Object).prototype);return i.next=u(0),i.throw=u(1),i.return=u(2),"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function u(u){return function(a){return function(u){if(r)throw new TypeError("Generator is already executing.");for(;i&&(i=0,u[0]&&(s=0)),s;)try{if(r=1,n&&(o=2&u[0]?n.return:u[0]?n.throw||((o=n.return)&&o.call(n),0):n.next)&&!(o=o.call(n,u[1])).done)return o;switch(n=0,o&&(u=[2&u[0],o.value]),u[0]){case 0:case 1:o=u;break;case 4:return s.label++,{value:u[1],done:!1};case 5:s.label++,n=u[1],u=[0];continue;case 7:u=s.ops.pop(),s.trys.pop();continue;default:if(!(o=s.trys,(o=o.length>0&&o[o.length-1])||6!==u[0]&&2!==u[0])){s=0;continue}if(3===u[0]&&(!o||u[1]>o[0]&&u[1]<o[3])){s.label=u[1];break}if(6===u[0]&&s.label<o[1]){s.label=o[1],o=u;break}if(o&&s.label<o[2]){s.label=o[2],s.ops.push(u);break}o[2]&&s.ops.pop(),s.trys.pop();continue}u=t.call(e,s)}catch(e){u=[6,e],n=0}finally{r=o=0}if(5&u[0])throw u[1];return{value:u[0]?u[1]:void 0,done:!0}}([u,a])}}}"function"==typeof SuppressedError&&SuppressedError;var i=function(){function e(){this.listeners={}}return e.prototype.on=function(e,t){this.listeners[e]=(this.listeners[e]||[]).concat(t)},e.prototype.off=function(e,t){this.listeners[e]=(this.listeners[e]||[]).filter(function(e){return e!==t})},e.prototype.emit=function(e,t){(this.listeners[e]||[]).forEach(function(e){e(t)})},e}();function u(e,t){var r=t.timeoutMillis,n=void 0===r?5e3:r,o=t.onTimeout;return new Promise(function(t,r){e(t,r),setTimeout(function(){o(t,r)},n)})}var a=function(){function e(){this.injectFlag="_hackle_injected"}return e.prototype.isInjectedEnvironment=function(){return!("undefined"==typeof window||!window.ReactNativeWebView)&&!0===window[this.injectFlag]},e.prototype.createInstance=function(e,t){if(this.isInjectedEnvironment()){var r=new c(window.ReactNativeWebView);return new p(e,t,r)}return new l(e,t)},e}(),c=function(){function e(e){this.port=e,this.cleanUp=function(){}}return e.prototype.addEventListener=function(e){window.addEventListener("message",e,!0),this.cleanUp=function(){return window.removeEventListener("message",e,!0)}},e}(),p=function(r){function i(e,t,n){var o=r.call(this)||this;return o.sdkKey=e,o.config=t,o.messageTransceiver=n,o.messageFieldName="_hackle_message",o.resolverRecord=new Map,o.messageTransceiver.addEventListener(function(e){var t,r=e;if(r.data&&"undefined"!==r.data)try{var n=JSON.parse(r.data);if(o.messageFieldName in n){var s=n[o.messageFieldName],i=s.id,u=s.payload;i&&(null===(t=o.resolverRecord.get(i))||void 0===t||t(u),o.resolverRecord.delete(i))}}catch(e){console.log("[DEBUG] Hackle: Failed to parse message. If message not sent by hackle, please ignore this. ".concat(e))}}),o}return n(i,r),i.prototype.createMessage=function(e,t,r){var n;return JSON.stringify(((n={})[this.messageFieldName]={id:e,type:t,payload:r},n))},i.prototype.createId=function(){return t.v4()},i.prototype.getSessionId=function(){return o(this,void 0,void 0,function(){var e,t=this;return s(this,function(r){return e=this.createId(),[2,u(function(r){t.messageTransceiver.port.postMessage(t.createMessage(e,"getSessionId",null)),t.resolverRecord.set(e,r)},{onTimeout:function(e){return e("")}})]})})},i.prototype.getUser=function(){return o(this,void 0,void 0,function(){var e,t=this;return s(this,function(r){return e=this.createId(),[2,u(function(r){t.messageTransceiver.port.postMessage(t.createMessage(e,"getUser",null)),t.resolverRecord.set(e,r)},{onTimeout:function(e){return e({})}})]})})},i.prototype.setUser=function(e){var t=this,r=this.createId();return u(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"setUser",{user:e})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},i.prototype.setUserId=function(e){var t=this,r=this.createId();return u(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"setUserId",{userId:e})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},i.prototype.setDeviceId=function(e){var t=this,r=this.createId();return u(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"setDeviceId",{deviceId:e})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},i.prototype.setUserProperty=function(e,t){var r=this,n=this.createId();return u(function(o){r.messageTransceiver.port.postMessage(r.createMessage(n,"setUserProperty",{key:e,value:t})),r.resolverRecord.set(n,o)},{onTimeout:function(e){return e(null)}})},i.prototype.setUserProperties=function(e){var t=this,r=this.createId();return u(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"setUserProperties",{properties:e})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},i.prototype.updateUserProperties=function(e){var t=this,r=this.createId();return u(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"updateUserProperties",{operations:e.toRecord()})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},i.prototype.updatePushSubscriptions=function(e){var t=this,r=this.createId();return u(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"updatePushSubscriptions",{operations:e.toRecord()})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},i.prototype.updateSmsSubscriptions=function(e){var t=this,r=this.createId();return u(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"updateSmsSubscriptions",{operations:e.toRecord()})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},i.prototype.updateKakaoSubscriptions=function(e){var t=this,r=this.createId();return u(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"updateKakaoSubscriptions",{operations:e.toRecord()})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},i.prototype.setPhoneNumber=function(e){var t=this,r=this.createId();return u(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"setPhoneNumber",{phoneNumber:e})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(null)}})},i.prototype.unsetPhoneNumber=function(){var e=this,t=this.createId();return u(function(r){e.messageTransceiver.port.postMessage(e.createMessage(t,"unsetPhoneNumber",null)),e.resolverRecord.set(t,r)},{onTimeout:function(e){return e(null)}})},i.prototype.resetUser=function(){var e=this,t=this.createId();return u(function(r){e.messageTransceiver.port.postMessage(e.createMessage(t,"resetUser",null)),e.resolverRecord.set(t,r)},{onTimeout:function(e){return e(null)}})},i.prototype.variation=function(e){var t=this,r=this.createId();return u(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"variation",{experimentKey:e})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e("A")}})},i.prototype.variationDetail=function(t){return o(this,void 0,void 0,function(){var r,n,o,i,a,c,p=this;return s(this,function(s){switch(s.label){case 0:r=this.createId(),s.label=1;case 1:return s.trys.push([1,3,,4]),[4,u(function(e){p.messageTransceiver.port.postMessage(p.createMessage(r,"variationDetail",{experimentKey:t})),p.resolverRecord.set(r,e)},{onTimeout:function(e,t){return t()}})];case 2:return n=s.sent(),i=(o=n).variation,a=o.reason,c=o.parameters,[2,e.Decision.of(i,a,new f(null!=c?c:{}))];case 3:return s.sent(),[2,e.Decision.of("A",e.DecisionReason.EXCEPTION)];case 4:return[2]}})})},i.prototype.isFeatureOn=function(e){var t=this,r=this.createId();return u(function(n){t.messageTransceiver.port.postMessage(t.createMessage(r,"isFeatureOn",{featureKey:e})),t.resolverRecord.set(r,n)},{onTimeout:function(e){return e(!1)}})},i.prototype.featureFlagDetail=function(t){return o(this,void 0,void 0,function(){var r,n,o,i,a,c,p=this;return s(this,function(s){switch(s.label){case 0:r=this.createId(),s.label=1;case 1:return s.trys.push([1,3,,4]),[4,u(function(e){p.messageTransceiver.port.postMessage(p.createMessage(r,"featureFlagDetail",{featureKey:t})),p.resolverRecord.set(r,e)},{onTimeout:function(e,t){return t()}})];case 2:return n=s.sent(),i=(o=n).isOn,a=o.reason,c=o.parameters,[2,new e.FeatureFlagDecision(i,a,new f(null!=c?c:{}),void 0)];case 3:return s.sent(),[2,e.FeatureFlagDecision.off(e.DecisionReason.EXCEPTION)];case 4:return[2]}})})},i.prototype.track=function(e,t){var r=this,n=this.createId();return u(function(o){r.messageTransceiver.port.postMessage(r.createMessage(n,"track",{event:e,user:t})),r.resolverRecord.set(n,o)},{onTimeout:function(e){return e(null)}})},i.prototype.trackPageView=function(){return o(this,void 0,void 0,function(){return s(this,function(e){return[2]})})},i.prototype.remoteConfig=function(){var e=this;return new d(function(t,r){var n=e.createId();return u(function(o){e.messageTransceiver.port.postMessage(e.createMessage(n,"remoteConfig",{key:t,defaultValue:r})),e.resolverRecord.set(n,o)},{onTimeout:function(e){return e(r)}})})},i.prototype.showUserExplorer=function(){var e=this,t=this.createId();return u(function(r){e.messageTransceiver.port.postMessage(e.createMessage(t,"showUserExplorer",null)),e.resolverRecord.set(t,r)},{onTimeout:function(e){return e(null)}})},i.prototype.hideUserExplorer=function(){return o(this,void 0,void 0,function(){var e,t=this;return s(this,function(r){return e=this.createId(),[2,u(function(r){t.messageTransceiver.port.postMessage(t.createMessage(e,"hideUserExplorer",null)),t.resolverRecord.set(e,r)},{onTimeout:function(e){return e(null)}})]})})},i.prototype.fetch=function(){var e=this,t=this.createId();return u(function(r){e.messageTransceiver.port.postMessage(e.createMessage(t,"fetch",null)),e.resolverRecord.set(t,r)},{onTimeout:function(e){return e(null)}})},i}(i),l=function(t){function r(r,n){var o=t.call(this)||this;return o.client=e.createInstance(r,n),o}return n(r,t),r.prototype.emitUserUpdated=function(){this.emit("user-updated",JSON.stringify(this.getUser()))},r.prototype.getSessionId=function(){return Promise.resolve(this.client.getSessionId())},r.prototype.getUser=function(){return Promise.resolve(this.client.getUser())},r.prototype.setUser=function(e){return this.emitUserUpdated(),Promise.resolve(this.client.setUser(e))},r.prototype.setUserId=function(e){return this.emitUserUpdated(),Promise.resolve(this.client.setUserId(e))},r.prototype.setDeviceId=function(e){return this.client.setDeviceId(e),this.emit("user-updated",e),Promise.resolve()},r.prototype.setUserProperty=function(e,t){return this.emitUserUpdated(),Promise.resolve(this.client.setUserProperty(e,t))},r.prototype.setUserProperties=function(e){return this.emitUserUpdated(),Promise.resolve(this.client.setUserProperties(e))},r.prototype.updateUserProperties=function(e){return this.emitUserUpdated(),Promise.resolve(this.client.updateUserProperties(e))},r.prototype.updatePushSubscriptions=function(e){return Promise.resolve(this.client.updatePushSubscriptions(e))},r.prototype.updateSmsSubscriptions=function(e){return Promise.resolve(this.client.updateSmsSubscriptions(e))},r.prototype.updateKakaoSubscriptions=function(e){return Promise.resolve(this.client.updateKakaoSubscriptions(e))},r.prototype.setPhoneNumber=function(e){return Promise.resolve(this.client.setPhoneNumber(e))},r.prototype.unsetPhoneNumber=function(){return Promise.resolve(this.client.unsetPhoneNumber())},r.prototype.resetUser=function(){return this.emitUserUpdated(),Promise.resolve(this.client.resetUser())},r.prototype.variation=function(e){var t=this;return new Promise(function(r){setTimeout(function(){r(t.client.variation(e))},3e3)})},r.prototype.variationDetail=function(e){return Promise.resolve(this.client.variationDetail(e))},r.prototype.isFeatureOn=function(e){var t=this;return new Promise(function(r){setTimeout(function(){r(t.client.isFeatureOn(e))},3e3)})},r.prototype.featureFlagDetail=function(e){return Promise.resolve(this.client.featureFlagDetail(e))},r.prototype.track=function(e){return Promise.resolve(this.client.track(e))},r.prototype.trackPageView=function(e){return Promise.resolve(this.client.trackPageView(e))},r.prototype.remoteConfig=function(){return this.client.remoteConfig()},r.prototype.showUserExplorer=function(){return Promise.resolve(this.client.showUserExplorer())},r.prototype.hideUserExplorer=function(){return Promise.resolve(this.client.hideUserExplorer())},r.prototype.fetch=function(){return Promise.resolve(this.client.fetch())},r}(i),f=function(){function e(e){this.parameters=e,this.parameters=e}return e.prototype.get=function(e,t){var r=this.parameters[e];return null==r?t:null==t||typeof r==typeof t?r:t},e}(),d=function(){function e(e){this.configFetcher=e,this.configFetcher=e}return e.prototype.get=function(e,t){return o(this,void 0,void 0,function(){var r,n;return s(this,function(o){switch(o.label){case 0:return o.trys.push([0,2,,3]),[4,this.configFetcher(e,t)];case 1:if(!(r=o.sent()))throw new Error("invoke result data not exists");switch(typeof t){case"number":return[2,Number(r)];case"boolean":return[2,Boolean(r)];default:return[2,r]}case 2:return n=o.sent(),console.error("Unexpected exception while deciding remote config parameter[".concat(e,"]. Returning default value. : ").concat(n)),[2,t];case 3:return[2]}})})},e}();return a});
์ ์์ค ์ฝ๋๋ฅผ ํ๋ก์ ํธ์ ์ถ๊ฐํฉ๋๋ค.
- esm ์ฌ์ฉ์ ๊ถ์ฅํฉ๋๋ค.
default export
ํํ๋ก ๋ชจ๋์ด ์ ๊ณต๋ฉ๋๋ค. - ๋ชจ๋์ ์ฌ์ฉํ ์ ์๋ ํ๊ฒฝ์ด๋ผ๋ฉด umd๋ฅผ ์ฌ์ฉํ์ธ์.
- ์ ์ญ ์ค์ฝํ์
HackleManager
๋ผ๋ ์ด๋ฆ์ผ๋ก ๋ชจ๋์ด ์ ๊ณต๋ฉ๋๋ค.
- ์ ์ญ ์ค์ฝํ์
์์ค์ฝ๋ ์ฐ๋
์์ค ์ฝ๋๋ฅผ npm package๋ก ์ ๊ณตํ๊ณ ์์ง ์์ต๋๋ค.
์๋์ ๋จ๊ณ๋ณ ์ฐ๋ ๊ฐ์ด๋๋ณด๋ค ๋ ์์ธํ ์ฐ๋ ๋ฐฉ๋ฒ์ ์๋์ ๋ ํฌ์งํ ๋ฆฌ์์ ์์ ๋ฅผ ํตํด ํ์ธํด๋ณด์ธ์.
์ธ์คํด์ค ์์ฑ
์๋์ ๊ฐ์ด createInstance
๋ฅผ ํธ์ถํ๋ฉด ์ธ์คํด์ค๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
import ๊ฒฝ๋ก๋ ์ค์นํ ํ๊ฒฝ์ ๋ฐ๋ผ ๊ธฐ์ ํด์ฃผ์ธ์.
- ์์ฑํ ์ธ์คํด์ค๋ฅผ export ํ์ฌ ์ฌ์ฉํฉ๋๋ค.
import HackleManager from "./web-view-integration";
const manager = new HackleManager();
const hackleClient = manager.createInstance(process.env.NEXT_PUBLIC_HACKLE_SDK_KEY!);
export default hackleClient;
๋ฉ์๋ ํธ์ถ
๋ชจ๋ ๋ฉ์๋์ ๋ฐํ ํ์ ์ด Promise ์์ ์ธ์งํด์ผ ํฉ๋๋ค.
์ง์ํ๋ ๋ฉ์๋์ ์ข ๋ฅ๋ JavaScript/ReactNative SDK ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์.
import hackleClient from './client';
async function getVariation() {
const variation = await hackleClient.variation(42);
if (variation === 'A') {
// if variation is A do something...
} else {
// else
}
}
React/Vue/Next.js๋ฅผ ์ฌ์ฉํ๊ณ ์๋์?
React Native WebView ์น์ฑ์ฐ๋ ํ๊ฒฝ์์๋ react-sdk ๊ฐ ์ง์๋์ง ์์ต๋๋ค.
React Native์ WebView ํ๊ฒฝ์์๋ react-sdk๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
React Native WebView ์์์ Vue, React, Next.js ๋ฑ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ/ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด ์๋์ ๊ฐ์ด๋๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์.
Examples
React
๊ธฐ์กด์ react-sdk์ Custom hooks์ ์ ์ฌํ ํํ์ hooks๋ฅผ ๋ง๋ค์ด์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
Example์ ํฌํจ๋ hooks ๋ค์ ์ฐธ๊ณ ํ์ฌ ํ์ํ hooks๋ฅผ ์์ฑํด์ ์ฌ์ฉํ์ธ์.
Suspensable hooks
React Native์ JavaScript SDK๊ฐ ๋น๋๊ธฐ ๋ฉ์์ง ํต์ ์ ํ๋ ๋์ Suspesne
๋ฅผ ์ฌ์ฉํด์ ์ ์ธ์ ์ธ Loading ์ฒ๋ฆฌ๋ฅผ ํ ์ ์์ต๋๋ค.
์ Loading ์ฒ๋ฆฌ๋ฅผ ํด์ผํ๋์?
๋ฉ์์ง ์ก์์ ์ ์ง์ฐ์๊ฐ์ด ๊ธธ์ง๋ ์์ผ๋, ๋ก๋ฉ ์ฒ๋ฆฌ๋ฅผ ํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
- ์คํ(variation)์ ๊ฒฐ๊ณผ ๊ฐ์ ๋ฐ์์จ ์ดํ์ ์ฌ์ฉ์๊ฐ ์ต์ด์ ๋ณธ ๊ฒ๊ณผ ๋ค๋ฅธ ํ๋ฉด์ ๋ณด๊ฒ ๋๋ ๊ฒ์ ๋ฐฉ์งํ๊ณ ์ ํฉ๋๋ค.
Github: Source
import { Suspense } from "react";
import VariationTester from "./components/VariationTester";
import HackleProvider from "./context";
import hackleClient from "./modules/client";
import Loader from "./components/Loader";
import FeatureTester from "./components/FeatureTester";
function App() {
return (
<main>
<HackleProvider hackleClient={hackleClient}>
<div>
<h2>Experiment</h2>
<Suspense fallback={<Loader />}>
<VariationTester />
</Suspense>
</div>
<div>
<h2>FeatureFlag</h2>
<Suspense fallback={<Loader />}>
<FeatureTester />
</Suspense>
</div>
</HackleProvider>
</main>
);
}
export default App;
import useVariation from "../hooks/useVariation";
interface VariationTesterProps {}
export default function VariationTester({}: VariationTesterProps) {
const { data: variation } = useVariation(40, "A", {
suspense: true,
});
return <pre style={{ fontSize: 54 }}>{variation}</pre>;
}
import useFeature from "../hooks/useFeature";
interface FeatureTesterProps {}
export default function FeatureTester({}: FeatureTesterProps) {
const { data: isOn } = useFeature(40, false, {
suspense: true,
});
return <pre style={{ fontSize: 54 }}>{isOn ? "On" : "Off"}</pre>;
}
Updated about 24 hours ago