:: Google Firebase 기반 웹 애플리케이션 개발
Firebase
Firebase 는 구글에서 만든 클라우드 기반 데이터베이스 서비스로 개발자들이 손쉽게 자신의 어플리케이션을 데이터베이스와 연동 할 수 있는 인프라를 제공해 주고 있다. 이 외에도 애널리틱스, 인증, 저장소, 호스팅 등 다양한 서비스들이 있으며 추가 서비스들이 베타로 개발되고 있음. 보다 자세한 사항은 https://firebase.google.com 을 참고.
Firebase 특징
일반적으로 클라우드 서비스라고 하면 아마존의 AWS 나 마이크로소프트의 Azure 등을 떠올릴 수 있다. 그러나 이들 클라우드 서비스를 통해 데이터베이스 서비스를 이용 하려면 먼저 윈도우나 리눅스 OS 기반으로 클라우드 서버 인스턴스를 생성해야 한다.
그리고 MySQL 이나 Maria DB 등을 설치하고 Tomcat + Java + JAX-RS 혹은 Node.js 기반으로 Rest API 를 구현한 다음 개발 하기를 원하는 안드로이드나 웹 기반의 프로그램에 Rest API 호출을 위한 라이브러리등을 이용해 프로그램을 구현해야 한다.
직접 자신의 서버를 구축하지 않아도 된다는 점을 빼면 단순히 모바일 이나 웹 개발에 데이터베이스가 필요한 개발자의 경우 부가적으로 해야 할 일이 너무 많다는 문제가 있다.
Firebase 의 주요 특징은 다음과 같다.
- 클라우드 기반 으로 별도의 서버 구축 필요 없이 웹으로 로그인해 모든 셋업 및 관리 가능
- NoSQL 데이터베이스로 JSON 구조의 데이터 구조를 손쉽게 정의하고 사용할 수 있음
- 안드로이드, iOS, 웹, C++, Unity 등을 위한 API 제공을 통해 응용 프로그램 개발이 용이.
- 자바의 경우 Rest API 사용 방식으로 개발이 가능.
- 실시간 데이터베이스로 API를 사용시 실시간으로 데이터 업데이트가 지원.
Firebase 시작하기
로그인 및 프로젝트 생성
- https://firebase.google.com 에 접속해 구글 계정으로 로그인 한다.
시작하기
링크 혹은 콘솔 https://console.firebase.google.com 로 접속해 Add project 를 선택해 프로젝트를 생성 한다.- 프로젝트 이름을 입력 한다.
데이터베이스 생성
- 현재 firebase 에서 제공하는 데이터베이스는 초기 제공되던 original realtime database 와 베타버전으로 향후 메인 데이터베이스 서비스가 될 cloud firestore 가 있다. 여기서는 original realtime database 를 사용한다.
- Create database 버튼을 눌러 데이터베이스를 생성.
- lock mode 와 test mode 중에서 test mode 로 시작.
- 생성된 데이터베이스 화면에서 URL을 복사.
- 모든 Firebase 실시간 데이터베이스 URL을 REST 엔드포인트로 사용할 수 있음.
- 만일 twitter 라는 노드의 데이터를 접근하려면 URL/twitter.json 형태가 된다. 관련해서는 뒤에서 자세히 살펴 본다.
Firebase REST API
- Firebase는 안드로이드, iOS, 웹, C++, Unity를 위한 전용 라이브러리를 제공 하고 있다.
- 또한 기본적인 REST API로 제공하고 있으므로 개발 환경에 따라 선택해 사용할 수 있다.
- 순수 자바언어를 위한 라이브러리는 구글에서 제공하고 있지 않으며 오픈소스로 공개된 firebase4j 가 있으나 수년동안 베타상태이며 firebase 가 추후 cloud firestore 로 변경될 예정 이므로 그리 권장되지는 않음.
- 만일 순수 자바언어로 firebase 를 사용하고자 한다면 REST API를 사용하는 것이 좋다.
- 앞에서 생성한 데이터베이스 에서 복사한 URL은 해당 데이터베이스의 엔드포인트가 되며 각각의 데이터는 자동으로 생성되는 키값을 가지게 된다.
-
각각의 데이터는 해당
키값.json
형태로 접근할 수 있으며 특정 키값의 특정 필드는키값/필드명.json
으로 접근이 가능 하다. - 등록된 데이터가 그림과 같을때 각각의 데이터 요청 REST API의 형태는 다음과 같다.
- 전체데이터: https://mytwitter-a466c.firebaseio.com/twitter.json
- 특정 키값의 데이터: https://mytwitter-a466c.firebaseio.com/twitter/-LTAiugbwoOobawJMbm2.json
- 특정 키값의 특정 필드 데이터: https://mytwitter-a466c.firebaseio.com/twitter/-LTAiugbwoOobawJMbm2/msg.json
예제
예제는 twitter 와 비슷하게 간단한 메시지를 공유할 수 있는 웹 프로그램으로 구성되어 있다. 별도의 아이디없이 입력한 아이디로 로그인이 되며 로그인 후에는 등록된 메시지를 보고 자신의 메시지를 남길 수 있는 구조임.
build.gradle
Firebase 를 위해 특별하게 추가할 라이브러리는 없으며 REST API를 사용하기 위해 unirest 만 추가.
....
compile group: 'com.mashape.unirest', name: 'unirest-java', version: '1.4.9'
TwitterController
기본 컨트롤러는 서블릿으로 구현 한다. 특이 사항은 없으며 action 패러미터를 통해 login, tweet 두가지 요청을 처리하도록 되어 있음.
package javaweb.twitter;
import... 생략
@WebServlet("/twitter")
public class TwitterController extends HttpServlet {
private static final long serialVersionUID = 1L;
HttpServletRequest request;
HttpServletResponse response;
HttpSession session;
ServletContext application;
String view;
TwitterService service;
private void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
this.request = request;
this.response = response;
session = request.getSession();
application = request.getServletContext();
// 데이터 서비스 선택 지정.
service = new FirebaseService();
String action = request.getParameter("action");
if(action == null) {
session.invalidate();
response.sendRedirect("/javaweb/twitter/twitter_login.jsp");
return;
}
switch (action) {
case "login":
login();
break;
case "tweet":
tweet();
}
RequestDispatcher dispatcher = request.getRequestDispatcher(view);
dispatcher.forward(request, response);
}
public void login() {
// HTML 폼에서 username으로 전달된 값을 가지고 옴
String username = request.getParameter("username");
// username이 null 이 아닌 경우 세션에 값을 저장
if(username != null) {
session.setAttribute("user",username);
}
list();
}
public void list() {
List<String> tweetlist = new ArrayList<String>();
tweetlist = service.getList();
request.setAttribute("tweetlist", tweetlist);
view = "/twitter/tweet_list.jsp";
}
public void tweet() throws IOException {
// HTML 폼에서 전달된 msg 값을 가지고 옴
String msg = request.getParameter("msg");
// 세션에 저장된 로그인 사용자 이름을 가지고 옴
String username = (String)session.getAttribute("user");
// 사용자 이름, 메시지, 날짜 정보를 포함하여 ArrayList에 추가
LocalDateTime date = LocalDateTime.now();
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
msg = username+" :: "+msg+" , "+ date.format(f);
service.write(msg);
// 톰캣 콘솔을 통한 로깅
application.log(msg+"추가됨");
// 목록 화면 데이터 로딩
list();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
processRequest(request, response);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
processRequest(request, response);
}
}
FirebaseService
- Firebase 를 사용하기 위한 서비스 클래스.
- 컨트롤러 요청에 따라 firebase 의 rest api 를 이용해 메시지를 등록하고 전체 목록을 가지고 오는 기능이 구현되어 있음.
Unrest
라이브러리 를 사용하고 있으며write()
메서드에서는 헤더 설정과 함께 JSONObject 로 등록할 데이터를 JSON 형태로 변경하고 헤더 설정에서 HTTP BODY 에 해당 데이터를 포함시킴.- 전체 데이터를 가지고 오는
getList()
메서드 에서는 URL 요청으로 전달받은 JSON 메시지를 JSONObject 로 변환한 다음 각각의 데이터를 가지고 오기 위해 먼저 키 값들을 Iterator으로 변환. 그리고 데이터 목록을 만들기 위해 while() 문에서 가지고 온 키값을 이용해 메시지 데이터를 불러와서 tweetlist 에 추가한 다음 리턴.
Twitter Service Interface
package javaweb.twitter;
import java.util.List;
public interface TwitterService {
void write(String msg);
List<String> getList();
}
FirebaseService
package javaweb.twitter;
import.. 생략
public class FirebaseService implements TwitterService {
Logger log = Logger.getGlobal();
String baseurl="https://mytwitter-a466c.firebaseio.com/twitter.json";
@Override
public void write(String msg) {
JSONObject bodyMsg = new JSONObject();
bodyMsg.put("msg", msg);
HttpResponse<String> jsonResponse;
try {
jsonResponse = Unirest.post(baseurl)
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.body(bodyMsg)
.asString();
log.info(jsonResponse.getBody().toString());
} catch (UnirestException e) {
e.printStackTrace();
}
}
@Override
public List<String> getList() {
List<String> tweetlist = new ArrayList<String>();
HttpResponse<JsonNode> retmsg;
try {
retmsg = Unirest.get(baseurl).asJson();
JSONObject msglist = retmsg.getBody().getObject();
Iterator<String> keys = msglist.keys();
while(keys.hasNext()) {
JSONObject msg = (JSONObject) msglist.get(keys.next());
tweetlist.add(msg.getString("msg"));
}
} catch (UnirestException e) {
e.printStackTrace();
}
return tweetlist;
}
}
JSP 구현
예제 프로그램은 로그인 화면인 twitter_login.jsp 와 목록인 tweet_list.jsp 로 구성 됨.
inc_header.html
bootstrap 등 공통으로 필요한 외부 css 나 js 를 포함하기 위한 파일. 여기서는 bootstrap.min.css 만 필요하지만 bootstrap 의 모든 기능을 사용하려면 아래의 js 를 포함해야 한다.
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<!-- jQuery library -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- Popper JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<!-- Latest compiled JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
twitter_login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<%@ include file="inc_header.html" %>
<title>twitter_login.jsp</title>
</head>
<body>
<div class="container mx-auto m-5 p-5 w-50 shadow bg-info">
<H2>Twitter::Login</H2>
<form name="form1" method="post" action="/javaweb/twitter">
<input type="hidden" name="action" value="login" />
<div class="input-group">
<input class="form-control" type="text" name="username" placeholder="login id"/>
<input class="btn btn-warning" type="submit" value="Login"/>
</div>
</form>
</div>
</body>
</html>
tweet_list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="java.util.*"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<%@ include file="inc_header.html" %>
<title>Tweet List</title>
</head>
<body>
<div class="container shadow mx-auto mt-5 p-5 w-60">
<H3>My Simple Twitter!!</H3>
<HR>
<form action="/javaweb/twitter" method="post">
<input type="hidden" name="action" value="tweet">
<div class="input-group w-75">
<button type="button" class="btn btn-outline-success">@${user}</button>
<input class="form-control" type="text" name="msg">
<input class="btn btn-warning" type="submit" value="Tweet">
<a class="btn btn-secondary" href="/javaweb/twitter">Sign out</a>
</div>
</form>
<HR>
<div align="left">
<ul class="list-group">
<c:forEach var="msg" items="${tweetlist}">
<li class="list-group-item list-group-item-action">${msg}</li>
</c:forEach>
</ul>
</div>
</div>
</body>
</html>
실행결과
-
TwitterController 를 실행하면 twitter_login.jsp 가 실행 된다.
-
아이디를 입력하고 로그인하면 다음과 같이 기존에 등록된 목록이 나오고 새로운 글을 작성하고 tweet 버튼을 누르면 글이 게시 된다.