이제 도메인 주소를 입력하면 S3의 버킷으로 연결되어야 한다.

Route 53을 이용해서 연결시켜 보자.

AWS Console 에서 Route 53 메뉴로 이동한다.

호스트 존이 아무것도 없다면 Create Hosted Zone을 클릭하고 

있다면 해당 호스트 존을 클릭하면 된다.


호스트 존 이름은 버킷 이름과 같게 하면 된다. (www가 없는 버킷 이름)


다음과 같이 4개의 Record Set이 필요하다.

Record Set을 만들었다면 먼저 

NS와 SOA는 생성 되어있을 것이다.


A를 생성해보자 (Type)

CreateRecordSet 버튼을 누르면 다음과 같은 창이 오른쪽에 뜬다.

Name 에 아무것도 입력하지 않고

Type은 A - IPv4 Address로 설정한다.

Alias를 Yes로 변경한다.

그러면 다음과 같이 S3의 버킷이 보인다.

해당 버킷을 클릭해서 연결시켜준다.

Routing Policy는 simple

Evaluate Target Health는 NO로 설정한다.


이제 spiritlink.online 주소를 통해 S3 버킷 접속이 가능해 졌다.

www.spiritlink.online 주소를 통해 S3 버킷에 연결시켜보자.


CreateRecordSet 버튼을 클릭한 다음 Name에 www 를 입력한다.

그다음 똑같이 Alias를 Yes로 선택하고 S3버킷을 선택한다. 

* 주의 : www.spiritlink.online 버킷과 spiritlink.online 버킷은 별도이다.

www.spiritlink.online 주소를 입력하면 S3에서 spiritlink.online 버킷으로 연결되도록 처리가 필요하다.



Routing Policy 를 Simple로 선택하고

Evaluate Target health 를 NO로 선택한뒤 생성을 완료한다.


이제 다른 사이트에서 구매한 도메인에 연결시켜보자.

나는 가비아 에서 도메인을 구매했는데 다른 사이트도 이와 비슷하다.

도메인을 구매한 사이트에 도메인 정보로 들어가게 되면 도메인 네임 서버 주소를 입력하는 창이 있다.

여기에 NS 에 적혀있는 4개의 주소를 입력하면 된다.


1차, 2차, 3차, 4차 도메인 네임서버를 입력해 준뒤

몇분이 지나면 정상적으로 호스팅 되는것을 확인할 수 있다.

AWS 에서 웹사이트를 호스팅 하기 위해서는 다양한 서비스의 조합이 필요하다.

그중에 웹사이트를 호스팅 하기 위해서 제일 먼저 필요한것은

js, html 등 작성한 코드, 리소스 들이다.

먼저 해당 리소스들을 업로드 해보자.


aws console에서 s3로 이동한다.


그뒤 버킷을 생성한다.

버킷 이름은 도메인 이름과 같게 설정하는게 좋다.

리전은 서울로 설정한다.

다음을 전부 넘기고 생성한다.


이렇게 생성한뒤 버킷 이름을 클릭하면 다음과 같은 창이 보인다.

업로드 버튼을 누른뒤 js 파일, html 파일 등을 업로드 한다.

파일 추가 버튼을 누르는것보다

마우스로 드래그 한뒤 드랍하는게 훨씬 편하다. (폴더 내의 파일들까지 알아서 업로드 할 수 있다.)


다음과 같이 파일이 업로드 되었다. 이제 웹사이트 호스팅을 시작해보자 (S3에서)

속성 탭으로 이동한뒤 정적 웹 사이트 호스팅을 클릭한다.

이 버킷을 사용하여 웹 사이트를 호스팅한다를 클릭한다.

그 뒤 위의 엔드포인트 옆 주소를 클릭한다.

그러면 403 Forbidden 이 출력된다.

읽기 권한이 할당되지 않아서 이러한 현상이 발생한다.

인덱스에 자신의 프로젝트 index.html을 입력해주면 된다.

버킷에서 권한 -> 버킷 정책 탭으로 이동한다.


다음과 같이 입력하면 된다.

모자이크 된 부분은 버킷의 이름이다.

적용이 정상적으로 완료되면 다음과 같이 보인다.


이렇게 처리가 완료되면 엔드포인트 주소를 클릭했을때

웹사이트가 호스팅 되기 시작하는것을 확인할 수 있다.


다음 글에서는 도메인과 연결을 시작해보자.

기존에 ECMA6기반으로 코드를 작성, 프로젝트를 압축 (concat) 하고 난독화 (uglify) 등을 진행할때 gulp를 사용했었습니다.

클래스의 확장, 라이브러리간 의존성이 생겨 task를 나누어서 관리했었는데

다른 클래스를 참조할때 전역객체를 통해 클래스를 참조하다 보니 window.console에 노출되는등 좋지 않은점이 많이 발견되었습니다.

프로젝트가 커질수록 이렇게 관리하는건 시간이 오래 걸린다 판단되어 Webpack으로 전환했습니다.


하지만 인터넷등을 보니 WebPack 개념 자체에 대해서는 설명이 잘 되어있지만

import.. export를 실질적으로 어떻게 하는지 보여주는 부분은 없어서 난감했었는데 해당 부분에 대해 정리해 놨습니다.


webpack의 개념과 모듈, import, export에 관한 개념은 다른 글에 많이 나와있기 때문에

여기서는 실질적인 구성, 실행을 하는것이 목적입니다.


- 목표 - 

1. main 진입점에서 다른 js파일을 import한뒤 import한 클래스의 함수를 이용해서 로그를 찍어보자. 

2. js파일을 한개로 압축한뒤 minify 시킨다.


현재 사용하는 개발도구는 IntelliJ 입니다. Webstorm은 같은 Jetbrain에서 만든 도구라 크게 다른점은 없을거라 판단되며

기타 도구들도 Node.js 와 npm이 있다면 충분히 가능할것으로 판단됩니다.


[1] terminal 에서 webpack을 global로 설치한다.


정상적으로 설치가 완료되면 위 화면과 같이 나옵니다. (현재 사진은 재설치한 상태)

설치가 되지 않는다면 Node.js설치가 제대로 되었는지 확인해 본다.


[2] terminal 에서 webpack-cli를 global로 설치한다.


의존성과 관련된 문제는 스스로 설치해서 해결해야 된다고 나와있지만

현재 프로젝트 자체가 깨끗하게 비어있기 때문에 문제되는 부분은 없습니다.


[3]webpack.config.js 파일을 생성



-> webpack을 통해 빌드할때 환경설정을 하는 스크립트라고 생각하면 된다.


module.exports = {

mode: 'development',

context: __dirname + '/app', // 모듈 파일 폴더
entry: { // 엔트리 파일 목록
app: './main.js'
},
output: {
path: __dirname + '/dist', // 번들 파일 폴더
filename: '[name].bundle.js' // 번들 파일 이름 규칙
}
}

환경설정과 관련되서 현재 프로젝트의 디렉토리에 관한 설명



현재 프로젝트는 app폴더, dist폴더로 나뉘어져 있다.


app폴더는 기존처럼 우리가 작성한 스크립트들이 들어있는 폴더이고

dist폴더는 webpack을 통해서 하나로 합쳐진 파일이 저장되는 폴더이다.


스크립트를 저장하는 폴더명 (모듈 파일 폴더 이름)을 바꾸고 싶다면 webpack.config.js에서

'/app' 부분을 변경하면 된다 (작성한 코드를 저장하는 폴더)


하나로 합쳐져서 나오는 폴더명 (번들 파일 폴더 이름)을 바꾸고 싶다면

'/dist' 부분을 변경하면 된다 (합쳐진 코드를 저장하는 폴더)



app 폴더 안에 진입점이 될 main.js 와 모듈이 될 Utils.js를 생성합니다.


main.js

"use strict";

import Utils from './Utils'
Utils.log(' Hello Webpack ');

console.log(" Yes !");

Utils.js

"use strict";

export default class Utils{
static log(msg){ console.log('[LOG]' + msg ); }
}


진입점 에서는 Utils 클래스의 static함수를 이용해서 로그를 찍을 예정입니다.

이제 터미널에 webpack 커맨드를 사용해서 빌드합니다.



빌드가 정상적으로 완료되면 이렇게 뜹니다.

dist폴더에 빌드된 파일 (app.bundle.js) 을 확인해보면


/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./main.js");
/******/ })
/************************************************************************/
/******/ ({

/***/ "./Utils.js":
/*!******************!*\
!*** ./Utils.js ***!
\******************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return Utils; });\n\r\n\r\nclass Utils{\r\n static log(msg){ console.log('[LOG]' + msg ); }\r\n}\n\n//# sourceURL=webpack:///./Utils.js?");

/***/ }),

/***/ "./main.js":
/*!*****************!*\
!*** ./main.js ***!
\*****************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Utils */ \"./Utils.js\");\n\r\n\r\n\r\n_Utils__WEBPACK_IMPORTED_MODULE_0__[\"default\"].log(' Hello Webpack ');\r\n\r\nconsole.log(\" Yes !\");\n\n//# sourceURL=webpack:///./main.js?");

/***/ })

/******/ });

다음과 같이 합쳐진 코드를 확인할 수 있습니다.

추후에 config에서 development 로 되어있는 옵션을 Production 으로 변경한뒤

concat, babel, minify 옵션을 적용하면 코드를 한줄로 압축, 난독화해서 처리할 수 있습니다.


이제 index.html 파일을 다음과 같이 수정합니다.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="dist/app.bundle.js"></script>
</body>
</html>

빌드된 파일 1개만 로드하게 하면 됩니다.

그후 index.html 파일을 우클릭 한뒤 Run, 또는 Debug로 실행하면


다음과 같이 Utils.js의 함수를 이용해서 정상적으로 Log가 생성되는걸 확인할 수 있습니다.


1. webpack설정에서 진입점만 정의한뒤

2. 다른 파일에 있는 클래스를 호출할때는 import를 통해서 호출하면

3. Webpack이 빌드할때 의존성 관련된 부분을 알아서 처리한뒤

4. 한개의 파일로 합쳐준다.

5. index.html 에서는 합쳐준 파일 1개만 로드하면 된다.


이렇게 요약할수 있습니다.

Three js 에서 애니메이션 재생시 애니메이션 한개마다 FBX 파일을 로드, 처리하는건 불필요하다고 판단되어


3D Max에서 JD 파일로 모델을 애니메이션 정보와 함께 출력했다.


JD 파일을 이용해서 씬에 출력 및 애니메이션을 재생해보자.


JDLoader.min.js


JDLoader 파일을 먼저 index에 추가, 로드 시킨다. (검색해도 찾을수 있음)


jd파일을 json로드한뒤 parse 하면 다양한 정보들이 담겨서 나오는데 해당 데이터를 담아보자.



static jdModel( data )
{
let meshes = [];
let mixers = [];
let actions = [];

for( let i = 0; i < data.objects.length; ++i )
{
if( data.objects[i].type == "Mesh" || data.objects[i].type == "SkinnedMesh" )
{
let mesh = null;
let matArray = this.createMaterials( data );
if ( data.objects[i].type == "SkinnedMesh")
{
mesh = new THREE.SkinnedMesh( data.objects[i].geometry, matArray );
}
else
{
mesh = new THREE.Mesh( data.objects[i].geometry, matArray );
}
meshes.push( mesh );

if( mesh && mesh.geometry.animations )
{
for(let j = 0, length = mesh.geometry.animations.length; j < length; j++)
{
let mixer = new THREE.AnimationMixer( mesh );
mixers.push( mixer );
let action = mixer.clipAction( mesh.geometry.animations[j] );
action.play();
actions.push( action );
}
}
}
else if ( data.objects[i].type == "Line" )
{
let jd_color = data.objects[i].jd_object.color;
let color1 = new THREE.Color( jd_color[0] / 255, jd_color[1] / 255, jd_color[2] / 255 );
let material = new THREE.LineBasicMaterial({ color: color1 }); //{ color: new THREE.Color( 0xff0000 ) }
let line = new THREE.Line( data.objects[i].geometry, material );
// scene.add( line );

if( line.geometry.animations )
{
for( let j = 0, length = mesh.geometry.animations.length; j < length; j++ )
{
let mixer = new THREE.AnimationMixer( line );
mixers.push( mixer );
let action = mixer.clipAction( line.geometry.animations[j] );
action.play();
actions.push( actions );
}
}
}
}

let object = {};
object.meshes = meshes;
object.mixers = mixers;
object.actions = actions;
return object;
}

static createMaterials( data )
{
let matArray = [];
for( let i = 0; i < data.materials.length; ++i )
{
let mat = new THREE.MeshPhongMaterial({});
mat.copy( data.materials[i] );
matArray.push( mat );
}

return matArray;
}

Static 함수로 처리했다.


기존의 JDModel 로드 방법에 일부 스크립트가 추가되었는데

반환되는 object내부에

meshes , mixers, actions가 담겨서 나오게 된다.


이 함수를 통해 오브젝트를 받은뒤

meshes에 들어있는 모든 오브젝트를 Scene에 등록해준다.


this.object = new THREE.Group();

let model = StaticMethodCall.jdModel( data );

for(let i = 0, length = model.meshes.length; i < length; i++)
this.object.add( model.meshes[i] );

Group을 하나 만든뒤 씬에 등록하고 그 자식으로 넣어주는 방법이 편하다.


mixer는 애니메이션을 담당하므로 update함수에서 지속적으로 호출한다.

this.mixers[0].update( delta );

애니메이션을 재생, 정지 하고 싶을때는 Actions를 이용한다.

this.actions[1].stop();
this.actions[0].play();


acos(x) 로 구했다가 난감한 경험이 있었다.


제대로된 Angle을 구하고 싶다면


let angle = Math.atan2( y, x ) * RAD2DEG; 하면 정상적인 각도가 나옴


디스플레이 화면 특성상 Y값이 밑으로 갈수록 증가하기 때문에


실제로는 다음과 같은 식으로 구현했다.


var RAD2DEG = 180 / Math.PI;
let angle = Math.atan2( -y, x ) * RAD2DEG;


Javascript 에서 모바일 브라우저인지 감지하는 코드


static isMobile(){
// if we want a more complete list use this: http://detectmobilebrowsers.com/
// str.test() is more efficent than str.match()
// remember str.test is case sensitive
let isMobile = (/iphone|ipod|android|ie|blackberry|fennec/).test
(navigator.userAgent.toLowerCase());
return isMobile;
}


'프로그래밍 > JavaScript' 카테고리의 다른 글

WebPack 빌드 및 사용하기  (2) 2018.08.21
Three.js 에서 jd 모델 사용하기.  (0) 2018.08.08
스파인 애니메이션 재생 문제 (Spine Animation)  (0) 2018.07.13
Threejs repeat  (0) 2018.07.04
Spine Attachment Off  (0) 2018.06.20

빛이 벽을 통해서 반사 되거나


공튕기기 게임에서 벽에 부딪힐때 반사각을 구해보자


1. 진행방향 벡터 (p) 와 충돌한 부분의 법선벡터 (n) 가 필요하다.

let n = this.result.normal; // 법선 벡터
let p = []; // 원래 벡터
p2.vec2.sub( p, this.rayClosest.from, this.rayClosest.to );

2. 진행방향 벡터의 역벡터 (-p) 를 구한다.

let minusP = [];         // 역 벡터
p2.vec2.sub( minusP, this.rayClosest.to, this.rayClosest.from);

3. 역벡터 (-p) 와 법선 벡터의 (n) 내적 (dot)을 구한다.

// 내적 계산
let dot = p2.vec2.dot( minusP, n );

4. 법선 벡터를 2배로 (2 * n) 늘린다.

// 법선 벡터 2배
let mulNormal = [];
p2.vec2.scale( mulNormal, this.result.normal, 2 );

5. 2배 길이의 법선벡터를 내적이 계산된 값만큼 스케일을 조절한다. ( 2 * n * dot(-p, n) )

let finalNormal = [];
p2.vec2.scale( finalNormal, mulNormal, dot );

6. 원래 진행방향 벡터 (p)에 5번에서 계산된 벡터 (스케일 조절된 법선 벡터) 를 더한다. ( p + 5번 결과 벡터 )

p2.vec2.add( reflect, p, finalNormal ); // 반사벡터 산출

7. 구해진 반사벡터를 정규화시킨다. ( normalize reflect )

p2.vec2.normalize( reflect, reflect );  // 정규화

8. 2d Screen의 특성에 따라 (y값이 밑으로 갈수록 증가) 역벡터를 구하면 완성( negate reflect )

p2.vec2.negate( reflect, reflect );     // 스크린 특성


마지막으로 계산된 reflect를 물체의 방향 (direction) 으로 사용하면 잘 반사된다.


- 참고 하면 좋은 수학적 개념 -

http://toymaker.tistory.com/entry/%EB%B0%98%EC%82%AC-%EB%B2%A1%ED%84%B0-Reflection-Vector

https://www.slideshare.net/cancan21st/ss-17299826

http://ifyouwanna.tistory.com/entry/%EB%B0%98%EC%82%AC%EB%B2%A1%ED%84%B0

Spine Attachment Off 하는법 (어태치먼트 비활성화)


목적 : 스파인 내부의 일부 장비를 비활성화, 다양항 장비를 교체하는 효과를 낼 수 있다.



this.sprite.skeleton.findSlot('helmet0').setAttachment( null );

이런 방식으로 슬롯을 찾은뒤 어태치먼트를 null 로 설정한다.


setSkinByName을 사용하면 attachment가 다시 설정된다.

+ Recent posts