2021년 2월 2일 화요일

오픈소스 활용한 Autodesk Forge 기반 BIM 모델 및 속성 뷰어 개발 방법

이 글은 오토데스크에서 제공하는 오픈소스를 활용한 Autodesk Forge 기반 BIM 모델 및 속성 뷰어 개발 방법을 간단히 다룬다. 아울러, 포지의 주요 기능을 확인한다.

이 글을 따라하면 다음과 같은 앱을 개발하고, 이를 커스터마이징할 수 있을 것이다.

Autodesk Forge 기반 BIM 모델 및 속성 뷰어

머리말

Autodesk Forge는 클라우드 기반으로 BIM360 등 솔류션과 연결되어, 디지털트윈, IoT, 데이터 분석 및 시뮬레이션 등 다양한 응용을 위한 플랫폼이 될 수 있다. 

Autodesk는 해당 분야를 선점하기 위해, 다양한 오픈소스 정책을 제공하고 있다. 이 글은 클라우드 기반 BIM 모델 및 속성 뷰어를 개발하기 위해, 오토데스크에서 제공한 오픈소스를 이용하는 방법을 간단히 알아본다. 참고로, 분석된 코드 구조 설명은 다음 링크를 참고한다.

Forge는 사용자가 개발한 앱을 생성, 관리하고, 앱에서 오토데스크 클라우드 플랫폼의 기능을 호출할 수 있는 API(Application Program Interface)를 제공한다. 아울러, API 사용 시 인증 방법을 제공하여 보안을 제공한다. Forge에서 제공되는 뷰어는 RVT, DWG, DXF, IFC, 3DS, DAE, OBJ, FBX, STEP, STL 등 거의 대부분의 2차원 및 3차원 모델 캐드 포맷을 지원한다(지원 형식). Forge App은 인터넷 기반으로 컴퓨터 뿐 아니라 스마트폰, 스마트패드 등 인터넷 웹이 접근되는 모든 곳에서 사용할 수 있다. 

Forge기반 앱 개발 순서는 보통 다음과 같다.

1. Forge 포털에 접속
2. Forge 포털에서 Create App 선택 및 앱 생성
3. App의 인증키에 해당하는 Client ID및 Secret ID 획득
4. NodeJS 기반 Javascript 등 언어를 이용해 Forge API 호출하여 앱 프로그램 코딩
5. NodeJS 기반일 경우, 앱에서 사용되는 패키지들 설치
6. 웹 서버 실행해 서비스 운영

시간 관계상 이 글에서는 앱 개발 시 사용하는 자바스크립트, NodeJS, 인증키에 대한 기본 설명은 하지 않는다. 이 기술에 대한 내용은 본 블로그 검색을 통해 확인할 수 있다. 오토데스크는 포지의 개발 지원을 위해, 수십개 이상의 서비스 유형에 대한 기본 템플릿을 오픈소스 형태로 깃허브(github)에 공개해 놓았다. 우리는 이를 활용할 것이다. 

BIM 모델 및 속성 뷰어 앱 개발

앱 개발 전에 이 글은 NodeJS를 웹 서버로 사용한다. 다음 웹사이트를 방문해 Node.js과 github 프로그램을 먼저 설치한다. 참고로, 다른 개발 환경은 여기를 참고해 해당 프로그램을 설치한다.

이제 다음 순서로 앱 개발해 본다.

1. Forge 포털 (forge.autodesk.com)에 접속 및 로그인

2. Forge 포털에서 Create App 메뉴 선택 및 앱 생성

메뉴를 선택하면, 앱 이름을 입력받는다. BIM viewer 등의 이름으로 입력한다. Callback URL은 "http://localhost:3000/api/forge/oauth/callback" 를 입력한다. 앱 정보 입력 마무리를 한다.

3. App의 인증키에 해당하는 Client ID및 Secret ID 획득

앱이 생성되면 Client ID, Secret ID를 다음과 같이 얻을 수 있다. 이 문자열은 코딩할 때 오토데스크 크라우드 플랫폼 API 호출 시 사용된다.  

입력 후 생성된 포지 앱 정보

4. NodeJS 기반 Javascript 등 언어를 이용해 Forge API 호출하여 앱 프로그램 코딩
이 단계는 오토데스크에서 제공하는 오픈소스를 사용한다. 
명령창(터미널)을 뛰우고 다음과 같이 실행한다.

생성된 폴더안에 들어간다. 

5. NodeJS 기반일 경우, 앱에서 사용되는 패키지들 설치
명령창에서 다음 명령을 입력해 앱에서 사용하는 패키지들을 설치한다.
npm install
npm init -y
npm install --save axios body-parser express
set FORGE_CLIENT_ID=<<앱의 CLIENT ID 입력>>
set FORGE_CLIENT_SECRET=<<앱의 CLIENT SECRET 입력>>

설치되는 패키지 모듈

6. 웹 서버 실행해 서비스 운영
다음 명령을 입력해, 앱 서버를 실행한다. 
npm start
웹서버 실행된 모습

만약, 인증키에서 에러가나면, 다음과 같이 앱 폴더 내의 start.js 소스코드를 편집해, 직접 해당 ID를 문자열로 직접 입력한다(아래 그림의 검정색 부분).

제대로 웹 서버가 실행되면, http://localhost:3000/ 에 접속해 본다. 
그럼, 다음과 같이 앱에서 생성된 웹 페이지가 렌더링된다. 파일 선택 버튼을 클릭하고, 래빗(revit)파일을 오토데스크 포지 클라우드에 업로드한다. 그럼, 버켓(bucket)에 해당 파일이 업로드된다.

업로드 끝난후 page 재갱신 버튼을 클릭하면, 다음과 같이 BIM 모델 및 속성 뷰어를 확인할 수 있다.

다음과 같이 BIM 모델 객체 트리, 속성창과 설정을 확인할 수 있다.

모델 단면 등을 검토할 수 있는 기능이 자동 제공된다. 

핵심 코드 설명
방금전 앱 생성으로 컴퓨터에 자동 생성된 폴더 및 소스 파일은 다음과 같다. 

동작순서는 다음과 같다. 
1. index.html 렌더링
2. Auth 인증 링크 클릭
3. Axios 이용해 인증 정보를 https://developer.api.autodesk.com/authentication/v1/authenticate 에 전달
4. 인증 성공하면, /api/forge/datamanagement/bucket/create 링크 열기. 모델 업로드할 버킷 키 POST하고 /api/forge/datamanagement/bucket/detail로 전송
5. /api/forge/datamanagement/bucket/detail 에서 버킷 키 전송에 성공하면, upload.html 폼 전송
6. 모델 업로드 폼에서 파일 선택 후 업로드하면, /api/forge/datamanagement/bucket/upload 전송
7. 모델 전송 성공하면, URN을 얻게된다. 업로드된 모델 URN을 /api/forge/modelderivative/ 에 전달
8. /api/forge/modelderivative/:urn 링크에 BIM 모델 URN, Format 등 전송하고, 성공하면, viewer.html 전송함. viewer에서는 업로드된 URN을 ForgeViewer에 전달해 모델을 렌더링함.

참고로, 1에서 7단계까지는 포지 앱 인증 및 모델 업로드에 해당하며, 실제 앱에서 비지니스 로직이 실행되는 부분은 8단계 이후가 된다. 

앱의 핵심 코드는 start.js와 viewer.html 파일안에 있다. 주요 부분만 설명하기로 한다.
var express = require('express');         // 웹서버로 express 모듈 패키지를 사용
var Axios = require('axios');               // http client 처리 axios 패키지 사용
var bodyParser = require('body-parser');    // JSON format 파싱 패키지 사용

// 웹 서버 기본 설정
var app = express();
app.use(bodyParser.json());
app.use(express.static(__dirname + '/www'));

// port 3000 에서 앱 서버 실행
app.set('port', 3000);
var server = app.listen(app.get('port'), function () {
    console.log('Server listening on port ' + server.address().port);
});

// Forge 앱 계정 인증키 설정. OAuth2 기술 사용
var FORGE_CLIENT_ID = 'Client ID';
var FORGE_CLIENT_SECRET = 'Secure ID';
var access_token = '';

// 인증키 처리 함수
app.get('/api/forge/oauth', function (req, res) {
    Axios({
        method: 'POST',
        url: 'https://developer.api.autodesk.com/authentication/v1/authenticate',
        headers: {
            'content-type': 'application/x-www-form-urlencoded',
        },
        data: querystring.stringify({
            client_id: FORGE_CLIENT_ID,
            client_secret: FORGE_CLIENT_SECRET,
            grant_type: 'client_credentials',
            scope: scopes
        })
    })
        .then(function (response) {
            // 인증 성공시 인증 토큰 획득. 토큰이 있어야 API 호출 가능
            access_token = response.data.access_token;
            console.log(response);
            res.redirect('/api/forge/datamanagement/bucket/create');
        })
});

var multer = require('multer');         // 모델 파일 업로드 위한 모듈 획득
var upload = multer({ dest: 'tmp/' }); // 모델 변환된 파일 임시 저장 폴더 설정

// 포지에 모델 파일 업로드 위한 버켓 설정 및 업로드
app.post('/api/forge/datamanagement/bucket/upload', upload.single('fileToUpload'), function (req, res) {
    var fs = require('fs'); // Node.js File system for reading files
    fs.readFile(req.file.path, function (err, filecontent) {
        Axios({
            method: 'PUT',
            url: 'https://developer.api.autodesk.com/oss/v2/buckets/' + encodeURIComponent(bucketKey) + '/objects/' + encodeURIComponent(req.file.originalname),
            headers: {
                Authorization: 'Bearer ' + access_token,
                'Content-Disposition': req.file.originalname,
                'Content-Length': filecontent.length
            },
            data: filecontent
        })
            .then(function (response) {
                // 업로드 성공 시 ModelDerivative 렌더링 페이지로 redirect 시킴.
                console.log(response);
                var urn = response.data.objectId.toBase64();
                res.redirect('/api/forge/modelderivative/' + urn);
            })
    });
});

// ModelDerivative 에서 모델 뷰어 처리함
app.get('/api/forge/modelderivative/:urn', function (req, res) {
    var urn = req.params.urn;
    var format_type = 'svf';
    var format_views = ['2d', '3d'];
    Axios({
        method: 'POST',
        url: 'https://developer.api.autodesk.com/modelderivative/v2/designdata/job',
        headers: {
            'content-type': 'application/json',
            Authorization: 'Bearer ' + access_token
        },
        data: JSON.stringify({
            'input': {
                'urn': urn   // 포지 버켓에 업로드된 모델 URN(Uniform Resource Name)
            },
        })
    })
        .then(function (response) {
            // 성공 시 뷰어 동작 시킴
            console.log(response);
            res.redirect('/viewer.html?urn=' + urn);
        })
});

<!DOCTYPE html>
<html>

<head>
    <title>Autodesk Forge: 3D Viewer App Sample</title>

    <meta http-equiv="cache-control" content="max-age=0" />
    <meta http-equiv="cache-control" content="no-cache" />
    <meta http-equiv="expires" content="0" />
    <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
    <meta http-equiv="pragma" content="no-cache" />
    <!-- Third Party package -->
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <!-- Autodesk Forge Viewer files (IMPORTANT) -->
    <link rel="stylesheet" href="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/style.min.css" type="text/css">
    <script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/viewer3D.min.js"></script>
    <style>
        /** Just simple CSS styling to make this page a little nicer **/
        body {
            margin: 0;
            padding: 0;
        }
    </style>
</head>

<body>
    <script>
        var viewer;
        var options = {
            env: 'AutodeskProduction',
            api: 'derivativeV2', // 뷰어에 사용하는 API
            getAccessToken: getForgeToken
        };
        var documentId = 'urn:' + getUrlParameter('urn');

        // 페이지 로딩 시 뷰어 정보 초기화됨
        Autodesk.Viewing.Initializer(options, function onInitialized(){
            // HTML 요소 중 MyViewerDiv 검색    
            var htmlElement = document.getElementById('MyViewerDiv');
            if (htmlElement) {
                // 검색된 요소에 BIM viewer attach함   
                viewer = new Autodesk.Viewing.GuiViewer3D(htmlElement);
                viewer.start();
                // 이 문서에 뷰어 로딩 처리함
                Autodesk.Viewing.Document.load(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
            }
        });        
  </script>
</body>

</html>

이제, 이 코드를 이용해, IoT 데이터와 BIM 객체를 연결하거나, 데쉬보드를 추가해 데이터를 가시화할 수 있을 것이다.

마무리
지금까지 간단히 오토데스크 포지를 이용한 BIM 모델 및 속성 뷰어 개발 방법을 살펴보았다. 관련 깃허브를 확인해 보면, 거의 대부분의 응용 영역에 대한 소스코드를 개발해 공개해 놓았는 데, 코드 품질이 매우 좋다. 
오토데스크 포지 깃허브

적절히 활용하면, IoT, 데이터 분석 및 시뮬레이션 등 다양한 응용 앱을 개발해 인터넷 웹 기반 서비스를 제공할 수 있을 것이다. 예를 들어, 이런 기술을 이용해 디지털트윈 서비스 개념을 구현하기 유용하다. 참고로, 디지털 트윈 개념적 이야기는 아래 글을 참고한다.

레퍼런스
부록: 포지 주요 기능 분석
포지 주요 기능은 포지 커뮤니티 블로그 코드 예제를 통해 확인할 수 있다. 주요 예제는 다음과 같다. 

부록: 포지 가격정책
물론 오토데스크 포지 사용비는 무료는 아니다. 이 링크에 가격정책이 표시되어 있고, 처음 사용시 90일 무료, 100크래딧을 준다. 

크래딧 의미는 다음 그림을 참고하라.
오토데스크 크래딧 정책

부록: 포지와 오토데스크 드라이브 연동
포지는 오토데스크 드라이브와 연동된다. 클라우드 드라이브이므로 인터넷 접속만 하면 언제든 관련 데이터를 포지로 가져올 수 있다. 깃허브 예제에 구글 드라이브에서 데이터 가져오는 방법도 있으니 참고 한다. 아울러, BIM 360과도 유기적으로 연동된다. 다만, 계정이 있어야 한다.
드라이브 폴더 모습
오토데스크 드라이브 기능 중 모델 체커 일부

추신. UNF에 온지 한달 지난 시점에서, 연구 행정관리로 밀려있었던 개발 내용들을 이곳 연구자들과 함께 정리하는 중. 미국은 연구자가 직접 개발하고 논문 쓰는 문화가 당연하고 일반적임^^ 한국 연구풍토는 관리해야 할 정책성 과제가 많고, 일이 몰리다보니 연구보다는 행정에 더 많은 시간을 빼앗기는 것 악순환이 문제. 돈대로 쓰고 결과는 없는 안타까운 일. 누구는 연구과제 관리하는 것도 연구라 말할지 모르겠지만, 본인은 어떤 기술 개발 과제에 본인이 직접 개발해 본게 없는 상태에서 하는 일들은 모두 행정이라 생각. 사실, 이런 일은 전공, 전문성이 1%도 필요 없이 엑셀, 워드만 잘하면 누구나 할 수 있다고 생각. 심하게는 기초적 일인 연구과제 발표자료도 외주화하는 이런 환경을 만들어 놓은 것은 사실 정부 기관 뿐 아니라 연구자 책임도 크다. 적당히 쉽게 주제 선택해 과제 수행하고 간접비나 이익 가져가면 된다는 편한 생각이 관행적으로 자리잡고 있음. 어느새 이런 일이 연구자가 해야 할 일처럼 착각하게 되었다. 여기에 해당 기술 전문성도 없어 겉포장 번지르한 과제 발표 결과에 면죄부 주는 평가 시스템 큰 역할. 이런 이유로, 결과는 얼마나 잘 포장하고 어떤 평가위원들이 오느냐에 따라 복걸복으로 결정되는 경우가 많음. 고질적 문제들. 이런 상황은 힘든 석박사 공부가 본인 브랜드로 계속 발전되기 정말 어려운 것이다. - 우리가 미국 실리콘밸리, 선진 유럽, 일본 심지어 중국 연구 결과에 못미치는 이유

댓글 4개:

  1. 추신에 대해서 심각하게 동의합니다. 좋은 글 감사합니다. ^^

    답글삭제
    답글
    1. 심각하죠. 연구하는 사람들에게는 일만 더 쏟아부어지고, 연구자로 포장된 영업맨은 관리만 적당히 하며 이익을 얻는 상황인데, 누가 힘든 연구 스스로 하려 하겠습니까^^ 그래서, 다들 적당히 쉬엄쉬엄 일하는 거겠지요.

      삭제
  2. 안녕하세요. 막 배우려고 찾아보는 중인데 개발 관련은 아니지만ㅠㅠ궁금한 점이 있어 질문 드립니다.
    뷰어를 사용할 때는 ModelDerivative API이 필요한 것 같은데
    과금정책에서 말하는 job은 렌더링 기준인가요?
    업로드 후 urn이 생성될 때 부과되는 건지, 아니면 urn으로 렌더링을 할 때마다 부과되는 건지 아니면 아예 다른 기준인지 궁금합니다...

    답글삭제
    답글
    1. 블로그 과금 설명한 부분 링크 보시면 내용이 있습니다.
      Model Derivative API
      1.5
      Cloud Credits / complex jobs
      0.2
      Cloud Credits / simple job

      파일 변환 시 과금되는 것 같습니다.

      삭제