 |  |
|
난이도 : 중급 Michael Galpin, Software architect, eBay
옮긴이: 장동수 dwkorea@kr.ibm.com
2008 년 11 월 11 일 사람들은 Ajax 애플리케이션을 좋아해서, 기꺼이 데스크톱용 애플리케이션 대신 사용하고 싶어합니다. 유일한 문제점은 네트워크 연결이 끊어지면 사용할 수 없다는 거죠. 그래서 오프라인 지원이 필요합니다. Ajax 기반 애플리케이션에 오프라인을 지원할 계획이라면, 아파치 더비가 멋진 선택입니다. 아파치 더비를 Ajax 애플리케이션이 오프라인일 때 로컬 데이터 저장소로 사용하는 방법을 알아보겠습니다.
사전 지식과 시스템 요구사항
이 글에서는 아파치 더비(Apache Derby)를 클라이언트측 데이터베이스로 사용한다. 더비는 따로 다운로드할 수도 있지만, 자바(Java™) 6부터 자바 DB라는 이름으로 포함되어 있다. 이 글에서는 자바 5나 6 버전에서 더비 10.4.1.3 버전을 사용한다. 브라우저에서 더비를 사용하기 위해 자바 애플릿을 사용하고, 또 자바스크립트를 사용하여 그 애플릿에 접근한다. 그러므로 자바 애플릿과 자바스크립에 대한 사전 지식이 필요하다. 더비를 통해 일반적인 JDBC와 SQL을 사용하므로 관련 기술에 대한 지식도 필요하다(참고자료).
애플릿에서 더비 사용하기
아파치 더비는 어떤 자바 애플리케이션이라도 사용할 수 있는 내장형 데이터베이스다. 자바 SE(Java Platform, Standard Edition) V6에 기본으로 포함될 만큼 유용한 도구다. 내장형 데이터베이스의 용도는 무궁무진하지만, 더비를 클라이언트측에 사용할 때 얻을 수 있는 이점에 대해 망각한 사람이 많다. 간단한 주소록 애플리케이션을 통해 그 가능성을 알아보자. 아파치 더비를 사용하는 애플릿부터 시작해서, 최종적으로 더비를 캐시로 사용하는 Ajax 기반 애플리케이션까지 만들어 보겠다.
데이터 접근
이 글은 데이터베이스 기술에 대한 글이므로, 데이터베이스 코드부터 시작하자. 먼저, 간단한 주소록을 저장할 테이블 스키마를 정의하자(그림 1).
그림 1. Contact 테이블
여러 개의 전화번호와 주소를 포함하는 더 복잡한 주소록 스키마도 생각할 수 있겠지만, 우리가 만들 애플리케이션에선 이 정도면 충분하다. 물론, contact 테이블에 대응하는 자바 클래스도 만들어야 한다. 이 예에서는, 활성 레코드(Active Record) 패턴에 따라 데이터베이스의 한 행(row)을 클래스로 감싸서 모든 데이터베이스 작업을 수행할 수 있도록 만들었다(Listing 1).
Listing 1. Contact 클래스
public class Contact {
private Integer id;
private String firstName;
private String lastName;
private String email;
public static List<Contact> getContacts(String clause){
if (clause == null)
clause = "";
String sql = SELECT_SQL + clause;
Connection conn = DbManager.getConnection();
List<Contact> contacts = new ArrayList<Contact>();
try {
ResultSet cursor = conn.createStatement().executeQuery(sql);
while (cursor.next()){
Contact c = new Contact();
c.setId(cursor.getInt(1));
c.setFirstName(cursor.getString(2));
c.setLastName(cursor.getString(3));
c.setEmail(cursor.getString(4));
contacts.add(c);
}
} catch (SQLException e) {
e.printStackTrace();
}
return contacts;
}
public static List<Contact> getAllContacts(){
return Contact.getContacts(null);
}
public static Contact getContact(String clause){
List<Contact> results = Contact.getContacts(clause);
if (results == null || results.size() != 1){
return null;
}
else return results.get(0);
}
public void save(){
if (id == null)
insert();
else
update();
}
public void delete(){
Connection conn = DbManager.getConnection();
String sql = "delete from Contact where id=?";
try{
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, id);
ps.executeUpdate();
System.out.println("Deleted contact id="+id);
} catch (SQLException e){
e.printStackTrace();
}
}
private void insert() {
Connection conn = DbManager.getConnection();
try {
PreparedStatement ps = conn.prepareStatement(INSERT_SQL,
PreparedStatement.RETURN_GENERATED_KEYS);
ps.setString(1, firstName);
ps.setString(2, lastName);
ps.setString(3, email);
ps.executeUpdate();
ResultSet autoRs = ps.getGeneratedKeys();
if (autoRs.next()){
id = autoRs.getInt(1);
}
System.out.println("Contact saved new id = " + id);
} catch (SQLException e) {
e.printStackTrace();
}
}
private void update(){
Connection conn = DbManager.getConnection();
try{
PreparedStatement ps = conn.prepareStatement(UPDATE_SQL);
ps.setString(1, firstName);
ps.setString(2, lastName);
ps.setString(3, email);
ps.setInt(4, id);
ps.executeUpdate();
System.out.println("Contact updated with id="+id);
} catch (SQLException e){
e.printStackTrace();
}
}
|
이 클래스는 많은 일을 수행하지만, 상당히 직관적이므로 별로 설명이 필요없다. 데이터베이스의 칼럼(column)에 대응하는 필드와, 이 필드들을 위한 전형적인 접근자(getter와 setter들)들을 포함하고 있다. 생성, 갱신, 삭제(CRUD) 작업을 수행하는 메서드와 이를 위한 SQL 쿼리들도 포함하고 있다. 주소록을 조회하는 메서드는 정적(static) 메서드이므로, Contact.getAllContacts()처럼 호출해야 한다. 저장하고 삭제하는 작업은 인스턴스(instance) 메서드이므로, 개별적인 주소록에 대해 호출해야 한다. 여기에서 표시하지 않았지만, 쿼리들은 표준 SQL이다. 전형적인 getter와 setter들도 포함하고 있지만, 분량을 줄이기 위해 표시하지 않았다(다운로드에서 전체 코드 리스트를 받을 수 있다). 이 클래스는 더비를 사용한 클라이언트측 저장소의 기본적인 형태다. 처음에는 애플릿 UI의 일부로 사용하겠지만, 나중에는 자바스크립트에서 애플릿에 접근할 것이다. 각 메서드에서 연결을 얻기 위해 유틸리티 클래스인 DbManager를 호출하고 있음을 주목하자.
Listing 2. DbManager 클래스
package org.developerworks.addressbook;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DbManager {
static{
try {
Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
try {
Connection conn = DriverManager.getConnection("jdbc:derby:contacts;
create=true");
return conn;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
|
더비에 의존하는 코드는 이게 전부다. 사실, 이 코드도 그다지 더비에 의존적이지 않다. 예제에서는 내장된 데이터베이스를 사용하지만, JDBC를 통해 접근하는 다른 데이터베이스들과 다를 바 없다. EmbeddedDriver를 드라이버 클래스로 사용하도록, 클래스의 정적 초기화 메서드(static initializer)에서 사용했다. getConnection 메서드는 create=true 매개변수를 추가해서, 지정한 데이터베이스가 존재하지 않으면 새로 만들도록 지시했다. 내장된 데이터베이스로 사용하기 때문에 JDBC 코드에서 흔히 볼 수 있는 사용자 이름이나 비밀번호가 필요없다. 데이터 접근 코드를 모두 살펴보았는데, 데이터베이스가 내장형 더비 데이터베이스라는 점만 빼면 다른 애플리케이션에서 볼 수 있는 데이터베이스 코드들과 거의 같음을 알 수 있다. 클라이언트측에서 애플리케이션에 특화된 데이터를 저장하기 위한 다른 모델을 만들어 볼 수도 있을 것이다. Listing 2의 데이터 접근 코드를 활용하여 간단한 애플리케이션을 만들어보자.
애플릿 UI
앞에서 만든 데이터 접근 코드를 사용하여 매우 간단한 애플릿을 만들어 보자.
Listing 3. 애플릿 UI 코드
public class AddressBookApplet extends JApplet {
private static final long serialVersionUID = 1L;
private static final String[] columns = { "First Name", "Last Name", "Email", "Id"};
public AddressBookApplet() {
this.setLayout(new GridLayout(1,0));
JPanel panel = buildUi();
this.add(panel);
}
public Contact addContact(String firstName, String lastName, String email) {
Contact c = new Contact();
c.setFirstName(firstName);
c.setLastName(lastName);
c.setEmail(email);
c.save();
return c;
}
public void deleteContact(Integer id){
Contact c= new Contact();
c.setId(id);
c.delete();
}
public Object[][] loadContacts() {
List<Contact> book = Contact.getAllContacts();
Object[][] contacts = new Object[book.size()][4];
int cnt = 0;
for (Contact contact : book){
contacts[cnt++] = contact.toArray();
}
return contacts;
}
private JPanel buildUi() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
final DefaultTableModel dataModel = createDataModel();
final JTable table = createTable(dataModel);
//Lots of Swing/UI Code omitted for brevity
}
private JTable createTable(final DefaultTableModel dataModel) {
final JTable table = new JTable(dataModel);
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
table.setFillsViewportHeight(true);
return table;
}
private DefaultTableModel createDataModel() {
Object[][] contacts = loadContacts();
final DefaultTableModel dataModel = new DefaultTableModel(contacts, columns);
return dataModel;
}
}
|
대부분의 코드는 UI를 구축하는 전형적인 스윙(Swing) 코드다. 모든 UI 코드는 클래스 아래에 모여있는 전용(private) 메서드들이다. buildUi 메서드가 스윙 컴포넌트들을 생성하고 배치하는데, 분량을 줄이기 위해 표시하지 않았다. 세 개의 공용(public) 메서드들(생성자는 빼고) addContact, deleteContact, loadContacts가 더 흥미롭다. 각각은 우리가 앞에서 만들었던 데이터 접근 코드를 감싼다. 완성된 애플리케이션에서는 애플릿 UI를 사용하지 않겠지만, 이렇게 해두면 코드를 테스트하기가 쉽다. 이클립스를 사용한다면, Applet 클래스에서 오른쪽 버튼을 클릭하고 Run As > Java Applet을 선택하자.
그림 2. 이클립스에서 자바 애플릿으로 실행하기
이클립스가 무슨 마법을 부리는 것은 아니고, JDK의 애플릿 뷰어(Applet Viewer)를 실행할 뿐이다. 이클립스를 사용하지 않고 명령행에서 실행해도 된다. 어느 쪽이든 그림 3과 같은 화면을 볼 수 있다.
그림 3. 애플릿 뷰어로 애플릿 실행하기
주소록을 추가하고 삭제하면서 애플리케이션을 시험해보자. 이렇게 하면 나중에 자바스크립트에서 호출할 클라이언트측 데이터베이스 코드를 개발하고 테스트하기가 쉽다. 애플릿 UI는 깔끔한 단위 테스트용이다. 나중에 이와 비슷한 웹 페이지를 만들겠지만 완전히 똑같지는 않다. 그 전에, 보안에 대한 고려가 필요하다.
보안
지금부터 애플릿이 사용하는 JAR를 서명할 것이다. 서명을 해야 더비를 사용하면 클라이언트에 영구적인 데이터베이스(모든 것이 클라이언트에 저장되는)를 만들 수 있다. HTTP 쿠키도 비슷하지만, 지긋지긋한 도메인당 4K 제약이 있다. 클라이언트에 더비 데이터베이스를 만들려면 어떤 제약이 있을까? 대답은 많을 수도 있고 적을 수도 있다.
기본적으로, 애플릿은 로컬 파일 시스템에 접근할 수 없으므로, 더비는 클라이언트에 데이터를 저장할 수 없다. 그렇다면 더비를 사용하는 것은 일장춘몽에 불과할까? 다행히 그렇진 않다. 핵심은 애플릿에 디지털 서명을 하는 것이다. 서명된 애플릿은 로컬 파일 시스템에 접근할 수 있으므로, 서명된 애플릿에서 더비를 사용하면 데이터를 영구히 저장할 수 있다. 필요한 것은 애플릿을 서명하는 일뿐이다.
Listing 4. 애플릿 서명하기
$ keytool -genkey -alias sigs -keystore sigstore -keypass password -storepass password
What is your first and last name?
[Unknown]: Michael
What is the name of your organizational unit?
[Unknown]: developerWorks
What is the name of your organization?
[Unknown]: IBM
What is the name of your City or Locality?
[Unknown]: San Jose
What is the name of your State or Province?
[Unknown]: CA
What is the two-letter country code for this unit?
[Unknown]: US
Is CN=Michael, OU=developerWorks, O=IBM, L=San Jose, ST=CA, C=US correct?
[no]: yes
$ jarsigner -keystore sigstore -storepass password -keypass password -signedjar
addrbook.jar derby.jar sigs
Warning: The signer certificate will expire within six months.
|
위에서 볼 수 있는 것처럼 애플릿(기술적으로는 애플릿을 포함한 JAR 파일)을 서명하기 위해 두 가지 JDK 도구를 사용한다. 먼저 keytool을 사용하여 생성된 암호화 키를 저장하는 키 저장소를 만든다. 이 도구는 SSL 인증을 만들 때도 사용된다. 키를 생성했으면 jarsigner를 사용하여 JAR를 서명한다. addrbook JAR 파일뿐 아니라 더비 JAR 파일도 포함되어 있음을 주목하자. 자체 서명된(self-signed) 애플릿을 만들었는데, 개발하는 동안은 이렇게 해도 무방하지만, 실제로 사용자들이 사용할 애플리케이션이라면 VeriSign 같은 신뢰할 수 있는 인증 기관에서 받은 키/인증서를 사용하는 게 좋다. 핵심은 클라이언트에 데이터를 저장하려면 자바 언어의 클라이언트 보안 모델을 만족하는 추가적인 절차를 따라야 한다는 것이다. 이러한 보안에 대한 고려사항들을 염두에 두고, 계속해서 자바스크립트에서 이 애플릿을 사용해 보자.
자바스크립트와 애플릿
웹 애플리케이션 어디에나 애플릿을 사용할 수 있지만, 웹 애플리케이션의 UI는 표준 HTML과 자바스크립트를 사용하는 것이 더 일반적이다. 여기에서도 HTML과 자바스크립트를 사용할 것이고, 애플릿은 내장된 더비 데이터베이스에 접근하기 위한 용도로만 사용할 것이다. 그래서 자바스크립트와 자바 간의 연동이 필요하다.
애플릿과 연동하기
자바스크립트와 자바 애플릿 간의 통신을 위한 코드를 만든다고 하면 꼼수나 새로운 기술처럼 들리겠지만, 전혀 그렇지 않다. 이 기술은 넷스케이프 내비게이터(Netscape Navigator) 시절부터 있었고, 그 때 쓰던 기술을 그대로 쓸 수 있다. 우선 웹 페이지에 애플릿을 내장하는 HTML 코드부터 살펴보자.
Listing 5. 애플릿 내장 코드
<applet alt="Address Book Applet" name="addrBookApplet"
code="org.developerworks.addressbook.AddressBookApplet"
width="400" height="200" archive="addrbook.jar, derby.jar">
</applet>
|
일반적인 애플릿 내장 코드다. 여기서는 <applet> 태그를 사용한다. 이 태그는 추천하진 않지만(deprecated), 여전히 대부분의 브라우저에서 지원되며, 마이크로소프트(Microsoft®)와 모질라 계열의 브라우저 모두에서 동작하는 유일한 태그다. 이 태그를 사용하고 싶지 않다면 브라우저를 감지하기 위해 자바스크립트를 사용해야 한다. 인터넷 익스플로러라면 <object> 태그를 사용해야 하고 width, height, archive 같은 속성 대신 내포된 <param> 태그를 사용해야 한다. 모질라 계열의 브라우저라면 <embed> 태그를 사용해야 한다. 또한, <object> 태그를 사용하면, 자바스크립트에서 애플릿의 자바 메서드를 호출하려면 scriptable 매개변수가 필요하다. <applet> 태그를 사용하면 필요없다.
알아둬야 할 매개변수가 하나 더 있다. 애플릿에서 웹 페이지의 자바스크립트를 실행하려면 MAYSCRIPT 매개변수가 필요하다. 이 기능은 매우 유용하지만, 이 글의 예제에는 필요없다. 이 예제에서는 자바스크립트에서 애플릿에 접근하지만(즉, 자바스크립트에서 애플릿의 자바 메서드를 호출한다), 애플릿에서 자바스크립트를 호출하지 않으므로 MAYSCRIPT 매개변수가 필요없다. 아무튼 자바에서 자바스크립트를 호출하는 방법을 알아보자.
Listing 6. 자바스크립트에서 자바 호출하기
function saveContact(firstName, lastName, email){
var applet = document.addrBookApplet;
var newContact = applet.addContact(firstName, lastName, email);
addContactToUi(newContact);
}
|
saveContact 함수에서 맨 처음으로 하는 일은 애플릿 객체를 얻는 일이다. 이때 애플릿의 이름을 사용한다(Listing 5에서 name 속성). 이 객체를 통해 addContact 메서드를 직접 호출 할 수 있고, 그 메서드는 새로운 Contact 객체(Listing 6)를 반환한다. 새로운 주소록을 UI에 반영하기 위해 반환된 객체를 다른 자바스크립트 함수에 전달한다. 이게 전부다. 간단하다. 지속적인 데이터 저장은 애플릿이 하고, 나머지는 자바스크립트가 한다.
헤드리스(Headless) 애플릿 사용하기
이제 애플릿을 헤드리스(UI가 없는 순수한 코드 라이브러리)로 만들어 보자. Listing 3에서 위에 있는 공용 메서드 세 개만 남겨두고 모든 UI를 제거하자. UI가 포함된 코드를 조금 고쳐야 한다.
Listing 7. 헤드리스 UI HTML
<applet alt="Headless Applet" name="headlessApplet"
code="org.developerworks.addressbook.HeadlesApplet"
width="1" height="1" archive="addrbook.jar, derby.jar">
</applet>
|
너비와 높이를 모두 1로 지정하는 부분이 흥미롭다. 이렇게 하면 애플릿은 웹 페이지에서 보이지 않는다. 결과적으로 최종 사용자는 페이지에 애플릿이 있는지도 모른다. 이 애플릿은 보이지 않는 도우미다. 물론, 애플릿의 이름을 바꾸려면 자바스크립트도 조금 고쳐야 하지만, 그게 전부다. 이제 클라이언트측에 데이터를 영구적으로 저장하기 위한 도구가 갖춰졌으니, 이를 이용해 웹 애플리케이션을 개선해 보자.
Ajax 캐시 제작
애플릿을 사용하면 Ajax로 할 수 있는 일들을 대부분 할 수 있고, 더 많은 일을 할 수 있다. 이 글에서는 "더 많은 일"에만 관심이 있다. 애플릿은 서버와 통신할 수 있지만, 이 작업은 그냥 Ajax로 하면 된다. 더비를 내장된 클라이언트측 데이터베이스로 사용하는 것은 Ajax만으로는 불가능한 일이다. 여기에 덧붙여, 더비를 서버에서 가져온 데이터를 위한 강력한 클라이언트측 캐시로 사용할 수 있다.
더비를 캐시로 사용하기
아이디어는 이렇다. 주소록은 서버에 저장되며, 추가와 삭제는 Ajax 호출을 통해 수행된다. 그러나 같은 정보를 클라이언트 측 더비에 저장해 두고 캐시로 사용하기로 하자. 그렇게 하면, 전체 주소록을 더비에서 불러 올 수 있다.
Listing 8. 더비 캐시에서 주소록 불러오기
function init(){
var book = document.headlessApplet.loadContacts();
var i = 0;
var contact = {};
for (i=0;i<book.length;i++){
contact = {firstName:book[i][0], lastName:book[i][1],
email:book[i][2], id:book[i][3]};
addContactToUi(contact);
}
}
|
이 함수는 애플릿을 통해 주소록을 불러오고, 각 주소록 항목을 UI에 추가한다. 이 코드는 최초로 페이지를 불러올 때 호출된다. 일반적으로는 별 다른 지연이 없겠지만, 서버와 더비 캐시 사이에 동기를 유지할 필요가 있다.
동기(sync) 유지하기
이 예제에서는 캐시가 정확해야 하므로, 서버와 동기를 유지해야 한다. 가장 간단한 방법은 비동기로 서버에 갱신 요청을 보내고, 그 요청이 처리되는 동안 캐시와 애플리케이션의 UI를 갱신하는 것이다.
Listing 9. 주소록 추가하기
function addContact(){
var contact = {};
var firstName = document.getElementById("firstName").value;
var lastName = document.getElementById("lastName").value;
var email = document.getElementById("email").value;
var req = getXhr(); // get the browser specific XMLHttpRequest object
var params = "firstName="+username + ",lastName="+lastName+",email="+email;
req.open("POST", "/contact/create", true);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.setRequestHeader("Content-length", params.length);
req.setRequestHeader("Connection", "close");
req.onreadystatechange = handleResponse;
req.send(params);
// update cache
saveContact(firstName, lastName, email);
}
|
대부분은 XMLHttpRequest 객체를 사용하는 전형적인 Ajax 코드다. 평소에 하는 대로, XMLHttpRequest로 요청을 보낸다. 이 요청은 비동기이므로 req.send() 호출은 바로 리턴되고, saveContact() 함수(Listing 6)를 호출할 수 있다. req.onreadystatechange 속성을 통해 핸들러를 설정했다. 이렇게 해 두면, 요청이 실패했을 때 적절한 조치를 취할 수 있다. 이러한 상황은 서버가 일시적으로 다운됐거나 사용자 네트워크에 문제가 있을 때 발생하는데, 이 때 좀 더 우아하게 대처할 수 있다. 갱신 내용을 모아두었다가 서버나 네트워크가 복구되면 다시 처리할 수 있다. 다른 방법으로, saveContact 호출을 핸들러로 옮겨도 된다. 이렇게 하면 UI의 응답성은 조금 떨어지겠지만, 서버에서 갱신이 성공했을 때만 캐시를 갱신할 수 있다.
요약
더비를 내장된 데이터베이스로 사용하는 것이 얼마나 쉬운지 알아보았다. 이것을 자바 애플릿과 결합해 클라이언트에 데이터를 영구적으로 저장하는 방법도 알아보았다. 반드시 고려해야 할 보안 측면 — 서명된 애플릿만이 클라이언트의 파일 시스템에 접근할 수 있다 — 이 있지만, 이를 위해 애플릿을 서명하는 방법도 알아보았다. 이러한 내용을 염두에 두고, 자바스크립트에서 자바 애플릿에 접근하여, 더비를 Ajax 기반 웹 애플리케이션을 위한 캐시로 사용했다. 이를 통해 서버가 일시적으로 다운되더라도 데이터에 접근할 수 있었다. 더비는 모든 종류의 "오프라인" 전략(서버와 연결 여부에 관계없이 데이터에 접근할 수 있도록 하는)에서 핵심 요소가 될 수 있다.
다운로드 하십시오 | 설명 | 이름 | 크기 | 다운로드 방식 |
|---|
| 예제 코드 | os-ad-offline-ajax-AddressBook.zip | 8KB | HTTP |
|---|
참고자료 교육
- 더비에 대한 개괄적인 소개를 "아파치 더비 소개" 기사에서 볼 수 있다.
- 이 글에서는 더비를 클라이언트에서 사용했지만 서버에서도 인기있다. 구글 웹 툴킷과 함께 사용하는 방법을 "구글 웹 툴킷, 아파치 더비, 이클립스를 사용하여 Ajax 애플리케이션 구현하기, Part 2: 신뢰성 있는 백엔드"에서 볼 수 있다.
- 더비와 자바 서버 페이스를 함께 사용하는 방법을 "아파치 더비, 아파치 MyFaces, Facelet으로 애플리케이션 개발하기"에서 볼 수 있다.
- "자바 애플릿으로 원격 웹 서비스에 접근하기"는 서비스 지향 아키텍처에서 자바 애플릿을 활용하는 방법을 설명한다.
- developerWorks의 자바 기술 존에서 자바 프로그래밍의 모든 분야에 대한 기사들을 찾을 수 있다.
- 자바 기술 존 튜토리얼에서 자바 기술 위주의 무료 튜토리얼들의 완전한 목록을 볼 수 있다.
- 썬이 지원하는 아파치 더비 배포본인 자바 DB에 대해 더 자세히 알아보자.
- developerWorks 포드캐스트에서 소프트웨어 개발자들을 위한 흥미로운 인터뷰와 토론을 들을 수 있다.
- developerWorks의 기술 행사와 웹 캐스트를 통해 최신 흐름을 알아보자.
- 전 세계에 걸쳐 IBM 오픈 소스 개발자들이 관심있어 할 컨퍼런스, 전시회, 웹 캐스트, 기타 행사 소식을 볼 수 있다.
- 오픈 소스 기술과 IBM의 제품을 이용한 개발을 도와줄 how-to 정보, 도구, 프로젝트 업데이트 소식을 developerWorks 오픈 소스 존에서 볼 수 있다.
- developerWorks 주문형 데모에서 IBM과 오픈 소스 기술과 제품의 기능을 보고 배울 수 있다.
제품 및 기술 얻기
토론
필자소개  | 
|  | Michael Galpin은 1998년부터 전문적으로 자바 소프트웨어를 개발해왔다. 현재 이베이에서 근무하고 있다. 캘리포니아 공과대학(California Institute of Technology)에서 수학을 전공했다. |
기사에 대한 평가
 |
| 이 문서 북마킹 하기
|
|  |