본문 바로가기

Development Note

간단한 SOFEA(Service-Oriented Front-End Architecture) 따라해보기~

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

지난 실습의 연장선이라고 봐주시면 좋겠습니다.


Jersey 와 DynamoDB 를 이용한 간단 예제 만들기 : http://mrtint.tistory.com/751


위를 통해서 Rest API 를 구성하고 Rest Client 를 통해서 테스트를 해보는 과정까지는 진행이 완료되었고.. 그리고 이런 데이터들을 잘 핸들링하여 화면을 구성해보는 일이 남았는데.. 그 일환으로 HTML 과 jQuery 를 사용해 볼까 했습니다만... 지난 포스팅에서 살펴본.. SOFEA 가 문득 떠올라서 대안으로 불리우는 Backbone.js 나 Angular.js 같은 친구들이 먼저 눈길이 가더라구요. 기본적으로 해본적 있는 것들보다는 잘 모르는 걸 해봐야.. 될 것같아서 시작해 보았습니다. 하지만 그게 이렇게까지 저를 괴롭힐 줄은 꿈에도 몰랐죠 -_-;



일단 Bootstrap 으로 간단하게 UI 를 구성해보았고.. 거기에 AngularJS 를 추가하였습니다. 이는 기본적으로 w3gschool 에서 제공하는 튜토리얼 화면을 기반으로 진행을 했습니다. (링크 : http://www.w3schools.com/angular/angular_bootstrap.asp) 




위와 같은 화면 구성입니다. 기능을 명세 하자면 다음과 같습니다.


1. 연락처 목록 조회

2. 새로운 연락처 생성

3. 기존의 연락처 수정


일단은 Rest API 는 이미 기존 소스에서 다 제공하도록 되어있습니다. 다음의 URL을 통해서 작업을 처리하도록 해 두었습니다.


1. 연락처 목록 조회 - GET / http://localhost:9998/ws/api/contacts/list

2. 새로운 연락처 생성 - PUT / http://localhost:9998/ws/api/contacts/put

3. 기존의 연락처 수정 - PUT / http://localhost:9998/ws/api/contacts/put


특징이라면 생성과 수정은 하나로 다 처리한다는 겁니다. HASH KEY 가 되는 ID 값이 동일하면 나머지 정보에 대해서는 Overwrite 를 하기 때문에 사실상 수정 효과와 같다고 보면 되겠네요. 그리고 생성, 수정 부분의 Rest API 의 로직을 다소 변경을 하였습니다. 지난번에 언급한 BeanParam 의 경우에는 Form 을 통해서 유입되는 파라미터에 대해서는 처리가 가능하나, JSON 형태로 유입되는 파라미터에 대한 처리는 불가하기 때문입니다.


따라서 아래와 같이 수정을 해보았습니다.


    @Path("/put")
    @PUT
    @Produces("application/json")
    public SGResponse put(String contactsJson) {
        SGResponse response = null;
        try {
            User user = objectMapper.readValue(contactsJson, User.class);
            Map userObj = new HashMap<>();
            userObj.put("Id", new AttributeValue().withS(user.getId()));
            userObj.put("Name", new AttributeValue().withS(user.getName()));
            userObj.put("Email", new AttributeValue().withS(user.getEmail()));
            userObj.put("Mobile", new AttributeValue().withS(user.getMobile()));
            userObj.put("LoginDate", new AttributeValue().withS(dateFormatter.format(new Date())));

            PutItemRequest forumRequest = new PutItemRequest().withTableName(tableName).withItem(userObj);
            client.putItem(forumRequest);
            response = SGResponseFactory.createOK();
        } catch (ConditionalCheckFailedException cse) {
            System.err.println("Conditional check failed in " + tableName);
            return SGResponseFactory.createUnKnownError();
        } catch (AmazonServiceException ase) {
            System.err.println("Error updating item in " + tableName);
            return SGResponseFactory.createNoRecordError();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (JsonParseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return response;
    }


파라미터에 있는 BeanParam을 제거하고 contactsJson 이라 불리우는 String 을 추가하였지요. 이후 Object 매핑을 통해서 기존과 같게 처리해줄 뿐 그 이상은 큰 차이는 없슴미다. 다음으로 화면 구성을 Angular JS 로 해보았습니다.



var app = angular.module("Contacts", []).config(function ($httpProvider) {

});

app.controller("contactsController", function ($scope, $http, transformRequestAsFormPost) {
    $scope.id = '';
    $scope.name = '';
    $scope.email = '';
    $scope.mobile = '';
    $scope.loginDate = '';

    $scope.contacts = null;
    $scope.edit = true;
    $scope.error = false;
    $scope.incomplete = false;

    $http.get("http://127.0.0.1:9998/ws/api/contacts/list").success(
        function (reponse) {
            $scope.contacts = reponse.value;
        });

    $scope.editContacts = function (contacts) {
        if (contacts == "new") {
            $scope.edit = true;
            $scope.incomplete = true;

            $scope.name = '';
            $scope.id = '';
            $scope.email = '';
            $scope.mobile = '';
        } else {
            $scope.edit = false;

            $scope.name = contacts.name;
            $scope.id = contacts.id;
            $scope.email = contacts.email;
            $scope.mobile = contacts.mobile;
        }
    }

    $scope.$watch('id', function () {
        $scope.valid();
    });
    $scope.$watch('name', function () {
        $scope.valid();
    });
    $scope.$watch('email', function () {
        $scope.valid();
    });
    $scope.$watch('mobile', function () {
        $scope.valid();
    });

    $scope.valid = function () {
        $scope.incomplete = false;
        if ($scope.edit && (!$scope.name.length || !$scope.id.length || !$scope.email.length || !$scope.mobile.length)) {
            $scope.incomplete = true;
        }
    };

    $scope.saveContacts = function () {
        var contactsData = {
            id: $scope.id,
            name: $scope.name,
            email: $scope.email,
            mobile: $scope.mobile
        };
        $http.put('http://localhost:9998/ws/api/contacts/put', contactsData, {}).success(function () {
            alert("OK");
        });
    };

    $scope.listContacts = function () {
        $http.get("http://127.0.0.1:9998/ws/api/contacts/list").success(
            function (reponse) {
                $scope.contacts = reponse.value;
            });
    }
});


가장 이해가 안가고 어려웠던 부분은 이부분입니다. 따라하고 완성하는데 포커스를 맞추다 보니 AngulaJS 의 대략적인 컨셉이나 추구하는 목적에 대해서 익히지 않다보니 소스를 이해하기에 어려움이 많은 것 같네요. 그래도 지속적인 반복은 이해를 동반하더군요. 주로 타인이 짜 놓은 소스를 보고 이해하려고 많이 노력했습니다. 먼저, 컨트롤러의 개념들을 스크립트 코드를 통해서 이해할 수 있었습니다. 여기서는 contactsController 라는 이름으로 컨트롤러를 생성했습니다. $scope 라는 파라미터는 거의 필수인 듯 하고 그외에는 선택적으로 고를 수 있는 것으로 보이네요.


좀 비유를 하자면 javascript 를 통해서 클래스를 만드는 것과 같이 구성을 해보았습니다.


1. Attributes : $scope.xxx 변수들

2. Methods : $scope.xxxx = function(...) 으로 선언한 함수들

3. Static resources or API : $http, $scope, $httpProvider 등


아직 Client-side 의 컨트롤러라는 것이 생소해서 시도하기가 여간 잔망스럽지 않은데.. 이는 스크립트를 짜는것 뿐만 아니라 HTML 에서도 그 영향을 미칠 수 있도록 설정을 해주어야 합니다. 이는 대부분 HTML 태그에 ng-* 처럼 ng라는 접두어를 포함한 attribute 들을 선언해 줌으로서 가능합니다. 또한 freemarker 나 jstl 또는 jsp 에서 사용하는 것과 같이 데이터를 표현해 주기 위한 약간의 Syntax 도 있습니다. {{ }} 라고 표시되어 있는 부분이 그렇습니다. HTML 태그는 좀 게시하기에 불편한 부분이 있어서 생략합니다. 자세한 것은 Git 에 소스를 공유할 것이니 그쪽을 통해서 확인 하시면 좋겠습니다.


다 완성한 데모 영상을 올려 보았습니다.



대략적으로 마치고 난 뒤의 소감과 더 알아야할 내용들이 있는데.. 이 부분은 다른 포스팅을 통해서 추가적으로 더 해야겠습니다. 너무 글의 길이가 길어져서 말이죠.

많은 삽질들이 있었지만 결과적으로는 많은 도움이 되는 시간이었던 것 같습니다. 그러합니다.


끝!