2014. 11. 12. 16:35ㆍDevelopment Note
지난 실습의 연장선이라고 봐주시면 좋겠습니다.
위를 통해서 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 에 소스를 공유할 것이니 그쪽을 통해서 확인 하시면 좋겠습니다.
다 완성한 데모 영상을 올려 보았습니다.
대략적으로 마치고 난 뒤의 소감과 더 알아야할 내용들이 있는데.. 이 부분은 다른 포스팅을 통해서 추가적으로 더 해야겠습니다. 너무 글의 길이가 길어져서 말이죠.
많은 삽질들이 있었지만 결과적으로는 많은 도움이 되는 시간이었던 것 같습니다. 그러합니다.
끝!