저작권 안내: 저작권자표시 Yes 상업적이용 No 컨텐츠변경 No

스프링5 입문

JSP 2.3

JPA 입문

DDD Start

인프런 객체 지향 입문 강의

요즘 유행하는 빅데이터류의 기술을 사용하고 있지는 않지만, 빅이 아닌 나머지 분야에서의 대부분 자바 개발자들은 아마 웹 관련 프로젝트에 주로 참여하고 있을 거라 생각되어 최근에 진행중인 프로젝트에서 사용한 오픈 소스들에 대한 초간단 리뷰를 한번 해 보고자 한다. 이들 목록은 아래와 같다.

  • Spring Data JPA
  • Apache Shiro
  • Sitemesh
  • Bootstrap
  • Solr
  • Easyrec
Spring Data JPA

필자는 ORM 매니아이다. 아니 매니아를 넘어 ORM 신봉자에 가깝고 심지어 SQL은 (물론 필요할 땐 사용하지만) 쳐다보기도 싫을 정도이다. 이런 필자에게 Spring Data JPA는 하이버네이트에서 JPA로 넘어가는 계기를 만들어줬다. Spring Data를 사용하면 다음의 편리함들이 있다.
  • (거의 모든 리포지토리에 대해) 리포지토리 인터페이스만 정의하면 Spring Data가 런타임에 구현객체를 만들어 준다. 그래서 잡다하고 지겨운 코드 작성을 줄일 수 있다.
  • DDD의 Specification을 지원해서 검색 조건을 도메인 용어로 잘 표현할 수 있게 된다.
    • 덤으로 이들 스펙의 조합도 쉽게 할 수 있다.
  • 페이징, 정렬 등의 표준화된 인터페이스 제공

DB 연동과 관련된 지겨운 코드 타이핑을 덜 하게 해 주고 이는 더 중요한 부분에 시간을 더 많이 쏟을 수 있다는 걸 의미한다. 물론, DB 연동 관련 코딩 시간이 주니까 전반적인 개발 시간도 줄어드는 효과가 있다.


Apache Shiro


Apache Shiro는 인증과 권한을 위한 프레임워크로서 웹 URL 기반의 접근 제어나 코드에서 직접 권한 검사를 하기 위한 기능을 제공한다. 단, Shiro를 알맞게 커스터마이징해서 사용하려면 Shiro의 구조와 동작 방식에 대한 이해가 필요하다. 이와 관련해서는 예전에 필자가 정리한 http://javacan.tistory.com/entry/Apache-Shiro-Core-Diagram 글을 참고하기 바란다. 필자의 프로젝트의 경우는 권한 검사 부분을 커스터마이징 해서 사용했다. 예를 들어, DB로부터 역할과 기능 정보를 로딩하도록 커스텀 클래스를 구현했고, 쿠키를 이용해서 인증을 수행하도록 구현했다.


Sitemesh


예전부터 Tiles보다 Sitemesh가 좋았다. Sitemesh가 좋은 이유는 데코레이터를 적용하지 않아도 결과물이 완전한 HTML이 된다는 점이다. 예를 들어, Tiles를 사용하는 경우에는 내가 만드는 JSP가 Tiles 템플릿의 일부 영역을 만드는 것이기 때문에 완전한 HTML이 아니며, 따라서 필요한 자바 스크립트가 <head> 안에 들어가는 것이 아니라 <body> 태그 어딘가에 들어가게 된다. <head>에 넣으려면 별도의 JSP 파일에 넣어야 하는 불편함이 따른다. 반면에 Sitemesh를 사용하면 내가 만드는 코드가 완전한 HTML을 생성하게 된다. 즉, 데코레이터 적용 여부에 상관없이 완전한 하나의 결과물을 만들어내기 때문에, UI 관련 코드가 불필요하게 이 파일 저 파일에 쪼개지는 현상을 줄일 수 있다.


Bootstrap


프로토타입을 만들더라도 UI나 UX나 너무 개발자스러우면(^^;) 뭔가 만든 것 같지 않은 느낌이 들기 마련이다. 필자도 이걸로 고민을 좀 했는데, 아는 지인의 소개로 Bootstrap이란 걸 알게 되었다. Twitter에서 오픈한 CSS 소스인데, Bootstrap의 사용법을 조금만 익히면 최소한 개발자스러운 껍데기를 벗어날 수 있게 된다. 게다가 약간의 이미지만 곁들이면 있어 보이기까지 한다. 필자처럼 UI에 대한 감이 없는 개발자들이 디자인의 도움없이 뭔가 껍데기를 입혀야 한다면 적극 추천한다.


Apache Solr


Solr는 그 유명한 Lucene을 이용한 검색 서비스이다. 웹 서비스로 제공되기 때문에 플랫폼에 상관없이 쉽게 연동할 수 있다. 설치도 쉽고, 검색을 위한 스키마 설계만 간단하게 해주면 거의 바로 사용할 수 있다. 게다가 (필자처럼) 검색에 대한 지식이 약해도 빠르게 적용해 볼 수 있다는 장점이 있다. 한글 검색을 제대로 하려면 별도의 분석기가 필요하고 사전도 필요하겠지만, 단순 키워드 매칭 수준의 검색 용도르는 충분하다. 물론, 유사단어, 검색어 오류 수정 등의 기능을 제공하고 싶지만 많은 노력이 필요할 것이다.


Easyrec


이번에 PoC 성격의 프로젝트를 진행하면서 뭔가 개인화 추천 기능을 넣고 싶었다. CI(Collective Intelligence) 관련 내용은 이전부터 틈틈히 봤지만 그렇다고 이걸 직접 구현하고 싶진 않았다. 게다가 Mahout 같은 걸 삽질해 가면서 사용하고 싶진 않았다. 그런 와중에 지인(좋은 지인 열 개발자 안 부럽다인가요..)의 소개로 Easyrec라는 걸 알게 됐다. 정말이지 딱 필요한 기능만 제공하고 있어 이거다 싶을 정도였다. 내부 DB로는 MySQL을 사용하고 있고 자바 기반의 웹 어플리케이션으로 만들어졌기 때문에, 어지간한 환경에서 다 사용할 수 있다. 웹기반으로 동작하기 때문에 자바가 아닌 다른 언어에서도 쉽게 연동할 수 있다. 이쪽 분야의 전문가가 아니기에 품질이 어느 정도인지 아직 확인은 안 되지만, '빅'이 아닌 사이트에서 작게 사용하기에는 충분할 거라 생각된다.



Posted by 최범균 madvirus

댓글을 달아 주세요

개인적으로 SiteMesh의 동장 방식이 Tiles보다 좋기 때문에 SiteMesh를 선호하는데, SiteMesh를 사용하면서 언제나 아쉬운 점은 <servlet-mapping>의 <url-pattern>의 값을 /catalog/*와 같은 경로 기반으로 설정한 경우 SiteMesh의 데코레이터 설정 파일에서 경로 패턴에 기반한 매칭을 사용할 수 없다는 점이다. (관련글: SiteMesh를 이용한 웹 페이지 데코레이션, http://javacan.tistory.com/entry/131) 특히 요즘처럼 (REST 방식의 유행으로) 확장자 없는 URL을 제공하는 게 멋처럼 느껴질 때에는 더더욱 SiteMesh의 지원이 아쉬웠다.

이런 아쉬움을 해소하기 위해 <servlet-mapping>의 <url-pattern>의 값에 상관없이 URL 경로를 이용해서 패턴 매칭을 하는 DecoratorMapper 클래스를 작성해보았다.

WemadeConfigDecoratorMapper 클래스 구현 코드

아래 코드는 실제로 구현해서 사용하고 있는 코드이다.

package com.madvirus.sitemesh;

import java.util.Properties;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

import com.opensymphony.module.sitemesh.Config;
import com.opensymphony.module.sitemesh.Decorator;
import com.opensymphony.module.sitemesh.DecoratorMapper;
import com.opensymphony.module.sitemesh.Page;
import com.opensymphony.module.sitemesh.mapper.AbstractDecoratorMapper;
import com.opensymphony.module.sitemesh.mapper.ConfigLoader;

/**
 * 서블릿 경로가 아닌 컨텍스트 경로를 제외한 요청 URI를 이용해서 패턴 매칭을 수행한다.
 * 
 * Sitemesh 2.4.1 버전의 ConfigDecoratorMapper로부터 코드를 가져와서 작성하였음.
 * 
 * @author 최범균
 * @version 2010. 3. 5.
 */
public class WemadeConfigDecoratorMapper extends AbstractDecoratorMapper {

    private ConfigLoader configLoader = null;

    /** Create new ConfigLoader using '/WEB-INF/decorators.xml' file. */
    public void init(Config config, Properties properties,
            DecoratorMapper parent) throws InstantiationException {
        super.init(config, properties, parent);
        try {
            String fileName = properties.getProperty("config",
                    "/WEB-INF/decorators.xml");
            configLoader = new ConfigLoader(fileName, config);
        } catch (Exception e) {
            throw new InstantiationException(e.toString());
        }
    }

    /**
     * Retrieve {@link com.opensymphony.module.sitemesh.Decorator} based on
     * 'pattern' tag.
     */
    public Decorator getDecorator(HttpServletRequest request, Page page) {
        String thisPath = request.getRequestURI();
        String contextPath = request.getContextPath();
        if (thisPath.startsWith(contextPath)) {
            thisPath = thisPath.substring(contextPath.length());
        }
        String name = null;
        try {
            name = configLoader.getMappedName(thisPath);
        } catch (ServletException e) {
            e.printStackTrace();
        }
        Decorator result = getNamedDecorator(request, name);
        return result == null ? super.getDecorator(request, page) : result;
    }

    /**
     * Retrieve Decorator named in 'name' attribute. Checks the role if
     * specified.
     */
    public Decorator getNamedDecorator(HttpServletRequest request, String name) {
        Decorator result = null;
        try {
            result = configLoader.getDecoratorByName(name);
        } catch (ServletException e) {
            e.printStackTrace();
        }

        if (result == null
                || (result.getRole() != null && !request.isUserInRole(result
                        .getRole()))) {
            // if the result is null or the user is not in the role
            return super.getNamedDecorator(request, name);
        } else {
            return result;
        }
    }

}

sitemesh.xml 파일 설정에서 커스텀 DecoratorMapper 사용

sitemesh.xml 파일에서는 아래 코드와 같이 앞서 구현한 DecoratorMapper를 사용하도록 설정한다.

<sitemesh>
    <property name="decorators-file" value="/WEB-INF/decorators.xml"/>
    <excludes file="${decorators-file}"/>

    <page-parsers>
        <parser content-type="text/html" class="com.opensymphony.module.sitemesh.parser.HTMLPageParser" />
    </page-parsers>
    <decorator-mappers>
        <mapper class="com.opensymphony.module.sitemesh.mapper.PageDecoratorMapper">
            <param name="property.1" value="meta.decorator" />
            <param name="property.2" value="decorator" />
        </mapper>
        <mapper class="com.opensymphony.module.sitemesh.mapper.FrameSetDecoratorMapper"/>
        <mapper class="com.opensymphony.module.sitemesh.mapper.PrintableDecoratorMapper">
            <param name="decorator" value="printable" />
            <param name="parameter.name" value="printable" />
            <param name="parameter.value" value="true" />
        </mapper>
        <mapper class="com.opensymphony.module.sitemesh.mapper.FileDecoratorMapper"/>
        <mapper class="com.madvirus.sitemesh.WemadeConfigDecoratorMapper">
            <param name="config" value="${decorators-file}" />
        </mapper>
    </decorator-mappers>
</sitemesh>

web.xml 파일 및 decorator.xml 파일 설정

web.xml 파일에서 다음과 같이 경로 기반의 패턴을 사용하고 있다고 해 보자.

<web-app ...>
    ...
    <servlet-mapping>
        <servlet-name>ControllerServlet</servlet-name>
        <url-pattern>/my/*</url-pattern>
        <url-pattern>/data/*</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>sitemesh</filter-name>
        <filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>sitemesh</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

위 코드에서 ControllerServlet은 경로 기반의 <url-pattern>을 사용하고 있는데, 앞서 구현한 커스텀 DecoratorMapper를 사용함으로써 다음과 같이 데코레이터 파일에서 URL 패턴을 이용해서 매칭을 할 수 있게 된다.

<decorators defaultdir="/decorators">

    <decorator name="my" page="/WEB-INF/view/common/decorator/my.jsp">
        <url-pattern>/my/*</url-pattern>
    </decorator>

    <decorator name="data" page="/WEB-INF/view/common/decorator/data.jsp">
        <url-pattern>/data/*</url-pattern>
    </decorator>

</decorators>

그 동안 경로 기반의 서블릿 매핑을 사용할 때 SiteMesh 때문에 섭섭함이 있었던 개발자에게 본 글이 조금이나마 도움이 되길 바란다.


Posted by 최범균 madvirus

댓글을 달아 주세요

  1. 삽지리 2010.07.13 10:45 신고  댓글주소  수정/삭제  댓글쓰기

    도움이 되었습니다. 감사합니다.