1. 개요
이 기사에서는 동적 웹 애플리케이션 또는 REST-ful 웹 서비스 개발에 필요한 모든 것을 제공하는 JavaLite의 전체 스택 웹 프레임워크인 Activeweb을 설명합니다 .
2. 기본 개념 및 원리
Activeweb은 "구성보다 관례"를 활용합니다. 즉, 구성 가능하지만 적절한 기본값이 있으며 추가 구성이 필요하지 않습니다. 미리 정의된 특정 형식의 클래스, 메서드 및 필드 이름 지정과 같은 몇 가지 미리 정의된 규칙을 따르기만 하면 됩니다.
또한 실행 중인 컨테이너(기본적으로 Jetty)에 소스를 다시 컴파일하고 다시 로드하여 개발을 간소화합니다.
의존성 관리를 위해 Google Guice를 DI 프레임워크로 사용합니다. Guice에 대한 자세한 내용은 여기에서 사용방법(예제)를 참조하십시오 .
3. 메이븐 설정
시작하려면 먼저 필요한 종속 항목을 추가해 보겠습니다.
<dependency>
<groupId>org.javalite</groupId>
<artifactId>activeweb</artifactId>
<version>1.15</version>
</dependency>
최신 버전은 여기에서 찾을 수 있습니다 .
또한 애플리케이션을 테스트하려면 activeweb-testing 의존성이 필요합니다 .
<dependency>
<groupId>org.javalite</groupId>
<artifactId>activeweb-testing</artifactId>
<version>1.15</version>
<scope>test</scope>
</dependency>
여기에서 최신 버전을 확인하십시오 .
4. 지원 구조
논의한 바와 같이 애플리케이션 구조는 특정 규칙을 따라야 합니다. 다음은 일반적인 MVC 애플리케이션의 모습입니다.
보시다시피 controllers , service , config 및 models 은 앱 패키지 의 자체 하위 패키지에 있어야 합니다 .
뷰는 WEB-INF/views 디렉토리 에 있어야 하며 , 각각은 컨트롤러 이름을 기반으로 하는 자체 하위 디렉토리를 가집니다. 예를 들어 app.controllers.ArticleController에는 해당 컨트롤러에 대한 모든 보기 파일을 포함하는 article/ 하위 디렉터리가 있어야 합니다 .
배포 설명자 또는 web.xml 에는 일반적으로 <filter> 및 해당 <filter-mapping>이 포함되어야 합니다. 프레임워크는 서블릿 필터이므로 <servlet> 구성 대신 필터 구성이 있습니다.
...
<filter>
<filter-name>dispatcher</filter-name>
<filter-class>org.javalite.activeweb.RequestDispatcher</filter-class>
...
</filter>
...
또한 홈 컨트롤러 와 유사한 응용 프로그램의 기본 컨트롤러를 정의하려면 <init-param> root_controller가 필요합니다 .
...
<init-param>
<param-name>root_controller</param-name>
<param-value>home</param-value>
</init-param>
...
5. 컨트롤러
컨트롤러는 ActiveWeb 애플리케이션의 기본 구성 요소입니다. 그리고 앞에서 언급했듯이 모든 컨트롤러는 app.controllers 패키지 안에 있어야 합니다 .
public class ArticleController extends AppController {
// ...
}
컨트롤러가 org.javalite.activeweb.AppController를 확장하고 있음에 유의하십시오.
5.1. 컨트롤러 URL 매핑
컨트롤러는 규칙에 따라 자동으로 URL에 매핑됩니다. 예를 들어 ArticleController는 다음에 매핑됩니다.
http://host:port/contextroot/article
이제 이것은 컨트롤러의 기본 동작에 매핑됩니다. 액션은 컨트롤러 내부의 메서드일 뿐입니다. 기본 메서드 이름을 index()로 지정합니다.
public class ArticleController extends AppController {
// ...
public void index() {
render("articles");
}
// ...
}
다른 메서드나 작업의 경우 메서드 이름을 URL에 추가합니다.
public class ArticleController extends AppController {
// ...
public void search() {
render("search");
}
}
URL:
http://host:port/contextroot/article/search
HTTP 메서드를 기반으로 컨트롤러 작업을 수행할 수도 있습니다. @POST, @PUT, @DELETE, @GET, @HEAD 중 하나로 메서드에 어노테이션을 달기만 하면 됩니다 . 작업에 어노테이션을 달지 않으면 기본적으로 GET으로 간주됩니다.
5.2. 컨트롤러 URL 확인
프레임워크는 컨트롤러 이름과 하위 패키지 이름을 사용하여 컨트롤러 URL을 생성합니다. 예를 들어 app.controllers.ArticleController.java URL은 다음과 같습니다.
http://host:port/contextroot/article
컨트롤러가 하위 패키지 안에 있는 경우 URL은 다음과 같습니다.
http://host:port/contextroot/baeldung/article
한 단어 이상을 포함하는 컨트롤러 이름(예: app.controllers.PublishedArticleController.java )의 경우 URL은 밑줄을 사용하여 구분됩니다.
http://host:port/contextroot/published_article
5.3. 요청 매개변수 검색
컨트롤러 내부에서 AppController 클래스 의 param() 또는 params() 메서드를 사용하여 요청 매개변수에 액세스할 수 있습니다 . 첫 번째 메서드는 검색할 매개변수의 이름인 문자열 인수를 사용합니다.
public void search() {
String keyword = param("key");
view("search",articleService.search(keyword));
}
그리고 다음과 같은 경우 나중에 모든 매개변수를 가져오는 데 사용할 수 있습니다.
public void search() {
Map<String, String[]> criterion = params();
// ...
}
6. 조회수
ActiveWeb 용어에서 보기는 종종 템플릿이라고 합니다. 이는 대부분 JSP 대신 Apache FreeMarker 템플릿 엔진을 사용하기 때문입니다. 여기 사용방법(예제)에서 FreeMarker에 대한 자세한 내용을 읽을 수 있습니다 .
WEB-INF/views 디렉토리 에 템플릿을 배치합니다 . 모든 컨트롤러에는 필요한 모든 템플릿을 포함하는 이름 옆의 하위 디렉터리가 있어야 합니다.
6.1. 컨트롤러 보기 매핑
컨트롤러에 도달하면 기본 작업 index()가 실행되고 프레임워크는 해당 컨트롤러의 뷰 디렉터리에서 WEB-INF/views/article/ index.ftl 템플릿을 선택합니다 . 마찬가지로 다른 작업의 경우 작업 이름을 기준으로 보기가 선택됩니다.
이것이 항상 우리가 원하는 것은 아닙니다. 때때로 우리는 내부 비즈니스 로직을 기반으로 일부 뷰를 반환하기를 원할 수 있습니다. 이 시나리오에서는 부모 org.javalite.activeweb.AppController 클래스 의 render() 메서드를 사용하여 프로세스를 제어할 수 있습니다 .
public void index() {
render("articles");
}
사용자 지정 보기의 위치도 해당 컨트롤러에 대해 동일한 보기 디렉터리에 있어야 합니다. 그렇지 않은 경우 템플릿 이름 앞에 템플릿이 있는 디렉토리 이름을 붙여서 render() 메서드에 전달합니다.
render("/common/error");
6.3. 데이터가 있는 보기
보기에 데이터를 보내기 위해 org.javalite.activeweb.AppController는 view() 메소드를 제공합니다 .
view("articles", articleService.getArticles());
여기에는 두 개의 매개변수가 필요합니다. 첫 번째는 템플릿의 개체에 액세스하는 데 사용되는 개체 이름이고 두 번째는 데이터를 포함하는 개체입니다.
또한 assign() 메서드를 사용하여 데이터를 뷰에 전달할 수 있습니다. view()와 assign() 메소드 사이에는 전혀 차이가 없습니다 . 둘 중 하나를 선택할 수 있습니다.
assign("article", articleService.search(keyword));
템플릿의 데이터를 매핑해 보겠습니다.
<@content for="title">Articles</@content>
...
<#list articles as article>
<tr>
<td>${article.title}</td>
<td>${article.author}</td>
<td>${article.words}</td>
<td>${article.date}</td>
</tr>
</#list>
</table>
7. 의존성 관리
개체 및 인스턴스를 관리하기 위해 ActiveWeb은 Google Guice를 의존성 관리 프레임워크로 사용합니다.
애플리케이션에 서비스 클래스가 필요하다고 가정해 보겠습니다. 이렇게 하면 컨트롤러에서 비즈니스 로직이 분리됩니다.
먼저 서비스 인터페이스를 생성해 보겠습니다.
public interface ArticleService {
List<Article> getArticles();
Article search(String keyword);
}
그리고 구현:
public class ArticleServiceImpl implements ArticleService {
public List<Article> getArticles() {
return fetchArticles();
}
public Article search(String keyword) {
Article ar = new Article();
ar.set("title", "Article with "+keyword);
ar.set("author", "baeldung");
ar.set("words", "1250");
ar.setDate("date", Instant.now());
return ar;
}
}
이제 이 서비스를 Guice 모듈로 바인딩하겠습니다.
public class ArticleServiceModule extends AbstractModule {
@Override
protected void configure() {
bind(ArticleService.class).to(ArticleServiceImpl.class)
.asEagerSingleton();
}
}
마지막으로 이것을 응용 프로그램 컨텍스트에 등록하고 필요에 따라 컨트롤러에 주입합니다.
public class AppBootstrap extends Bootstrap {
public void init(AppContext context) {
}
public Injector getInjector() {
return Guice.createInjector(new ArticleServiceModule());
}
}
이 구성 클래스 이름은 AppBootstrap 이어야 하며 app.config 패키지 에 있어야 합니다 .
마지막으로 컨트롤러에 주입하는 방법은 다음과 같습니다.
@Inject
private ArticleService articleService;
8. 테스트
ActiveWeb 애플리케이션의 단위 테스트는 JavaLite의 JSpec 라이브러리를 사용하여 작성됩니다.
JSpec의 org.javalite.activeweb.ControllerSpec 클래스를 사용하여 컨트롤러를 테스트하고 유사한 규칙에 따라 테스트 클래스의 이름을 지정합니다.
public class ArticleControllerSpec extends ControllerSpec {
// ...
}
이름은 끝에 "Spec"이 있는 테스트 중인 컨트롤러와 유사합니다.
테스트 사례는 다음과 같습니다.
@Test
public void whenReturnedArticlesThenCorrect() {
request().get("index");
a(responseContent())
.shouldContain("<td>Introduction to Mule</td>");
}
request () 메서드는 컨트롤러에 대한 호출을 시뮬레이트하고 해당 HTTP 메서드 get()은 작업 이름을 인수로 사용합니다.
params() 메서드를 사용하여 매개변수를 컨트롤러에 전달할 수도 있습니다 .
@Test
public void givenKeywordWhenFoundArticleThenCorrect() {
request().param("key", "Java").get("search");
a(responseContent())
.shouldContain("<td>Article with Java</td>");
}
여러 매개변수를 전달하기 위해 이 유창한 API를 사용하여 메소드를 연결할 수도 있습니다.
9. 애플리케이션 배포
Tomcat, WildFly 또는 Jetty와 같은 모든 서블릿 컨테이너에 애플리케이션을 배포할 수 있습니다. 물론 가장 간단한 배포 및 테스트 방법은 Maven Jetty 플러그인을 사용하는 것입니다.
...
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.4.8.v20171121</version>
<configuration>
<reload>manual</reload>
<scanIntervalSeconds>10000</scanIntervalSeconds>
</configuration>
</plugin>
...
플러그인의 최신 버전은 여기에 있습니다 .
이제 마지막으로 – 실행할 수 있습니다.
mvn jetty:run
10. 결론
이 기사에서는 ActiveWeb 프레임워크의 기본 개념과 규칙에 대해 배웠습니다. 이 외에도 프레임워크에는 여기에서 논의한 것보다 더 많은 기능이 있습니다.
자세한 내용은 공식 문서를 참조하십시오.
그리고 항상 그렇듯이 기사에 사용된 샘플 코드는 GitHub 에서 사용할 수 있습니다 .