Java와 Armeria로 기본적인 웹 서버 만들기

안녕하세요. LINE Developer Relations 팀의 Developer Advocate 윤인성입니다.

LINE은 Armeria, Central Dogma 등의 다양한 오픈소스를 주도적으로 개발하고 있습니다. 오늘은 Armeria에 대해서 알아보고, Armeria를 사용해 기본적인 웹 서버를 구성해보는 방법에 대해서 알아보겠습니다. 이번 글은 Java와 IntelliJ가 기본적으로 설치되어 있다는 것을 전제로 진행합니다.

Java와 IntelliJ는 각각 다음 사이트에서 다운로드할 수 있습니다.

프로젝트 만들기

프로젝트를 생성하는 과정부터 차근차근 설명하겠습니다. 일단 IntelliJ를 실행하고 Create New Project 버튼을 눌러 프로젝트를 생성합니다.

Gradle을 사용해 프로젝트를 설정하겠습니다. New Project 대화 상자의 왼쪽에 있는 요소들 중에서 Gradle을 선택하고 Java 프로젝트를 선택해주세요.

이어서 GroupId, ArtifactId, Version을 입력하는 화면이 나옵니다.

  • GroupId는 따로 입력할 필요가 없으므로 생략하도록 하겠습니다.
  • ArtifactId에는 프로젝트의 적당한 이름을 입력해주세요. 이 글을 진행하면서는 hello-armeria를 입력하겠습니다.
  • Version은 기본적으로 설정되어 있는 값을 사용하겠습니다.

값을 모두 입력했다면 Next를 클릭해주세요. 이 이후에 나오는 모든 대화상자는 기본 설정으로 두고 모두 Next를 누른 뒤 Finish를 클릭합니다.

Gradle 설정 추가

프로젝트가 생성되면 왼쪽 Project 패널에서 build.gradle이라는 파일을 열고, 다음 코드를 참고하여 줄을 추가해주세요. 추가한 후에 저장을 누르면 자동으로 Gradle이 라이브러리를 설정(다운로드와 빌드 등)해줍니다. 설정이 완료될 때까지 시간이 조금 걸릴 수 있으니 잠시 대기해주세요.

plugins {
    id 'java'
}
  
version '1.0-SNAPSHOT'
  
sourceCompatibility = 1.8
  
repositories {
    mavenCentral()
}
  
dependencies {
    // 다음 줄을 추가해주세요.
    // <Group Name>:<Artifact Id>:<Version>
    compile "com.linecorp.armeria:armeria:0.68.2"
      
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

Hello Armeria…!

이어서 프로젝트 아래에 있는 src > main > java에 마우스 오른쪽 버튼을 클릭하고, New > Java Class를 눌러서 Java 클래스를 생성합니다.

이 글을 진행하면서는 이름을 ServerMain.java로 짓겠습니다.

이어서 모든 예제의 시작이라고 할 수 있는 “Hello World…!”를 "Hello Armeria...!"로 작성하겠습니다.

일단 ServerBuilder 클래스의 인스턴스를 생성한 뒤 다음과 같이 서버를 실행합니다.

  • http() 메서드로 포트를 지정
  • service() 메서드를 사용해서 서버를 구성
  • 이를 활용해서 Server 인스턴스를 생성
  • start() 메서드를 호출
import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.common.HttpStatus;
import com.linecorp.armeria.common.MediaType;
import com.linecorp.armeria.server.Server;
import com.linecorp.armeria.server.ServerBuilder;
import java.util.concurrent.CompletableFuture;
  
public class ServerMain {
    public static void main(String[] args) {
        ServerBuilder sb = new ServerBuilder();
        sb.http(8080);
  
        sb.service("/hello", (ctx, res) ->
            HttpResponse.of(
                HttpStatus.OK,
                MediaType.HTML_UTF_8,
                "<h1>Hello Armeria...!</h1>"));
  
        Server server = sb.build();
        CompletableFuture<Void> future = server.start();
        future.join();
    }
}

참고로 IntelliJ에 익숙하지 않을 분들을 위해서 편리하게 사용할 수 있는 단축키를 소개하겠습니다. ServerBuilder, HttpResponse처럼 import 구문으로 가져와야 사용할 수 있는 클래스를 입력할 때는 입력 완료 후 Alt + Enter를 눌러서 컨텍스트 메뉴를 띄워 주세요.

컨텍스트 메뉴에서 Import class를 선택하면, 클래스를 import하는 구문이 자동적으로 추가됩니다.

다만 MediaTypeImport class를 선택하면, 추가적인 선택지가 다음과 같이 올 수 있습니다.

  • MediaType(com.google.common.net)
  • MediaType(com.linecorp.armeria.common)

현재 우리가 살펴보고 있는 내용은 Armeria이므로 com.linecorp.armeria.common으로 선택해서 import하면 됩니다.

실행

IntelliJ의 메뉴에서 Run > Run ‘<메인 파일 이름>’을 클릭해주시면, 서버가 실행됩니다. SLF4J를 읽어들이지 못했다는 경고가 나올 수 있습니다. 이는 로그를 출력할 때 사용하는 라이브러리입니다. Armeria에 대해서만 간단하게 살펴보는 중이므로 이와 관련된 설명은 생략하겠습니다.

웹 브라우저를 열고 http://127.0.0.1:8080/hello에 접속하면, 다음과 같이 "Hello Armeria...!"라는 글자를 볼 수 있습니다.

간단하게 Java로 웹 서버를 만들어 보았습니다. Armeria는 이 이외에도 굉장히 다양한 기능을 제공합니다. 그 중에서 웹 서버의 핵심이자 기본이라고 할 수 있는 라우팅을 간단하게 살펴봅시다.

라우팅

일단 이전과 같은 방법으로 CustomService라는 이름의 Java 클래스를 생성해주세요. 이름은 원하는 것으로 지어도 상관 없습니다.

이어서 다음과 같이 입력합니다. 이전에 입력했던 HttpResponse.of() 이외에 어노테이션(annotation)으로 라우팅과 관련된 설정을 추가했습니다.

import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.common.HttpStatus;
import com.linecorp.armeria.common.MediaType;
import com.linecorp.armeria.server.annotation.Get;
import com.linecorp.armeria.server.annotation.Param;
  
public class CustomService {
    @Get("/")
    public HttpResponse methodA() {
        return HttpResponse.of(
            HttpStatus.OK,
            MediaType.HTML_UTF_8,
            "<h1>Hello Custom Service...!</h1>");
    }
  
    @Get("/page/:number")
    public HttpResponse methodB(@Param("number") int number) {
        return HttpResponse.of(
            HttpStatus.OK,
            MediaType.HTML_UTF_8,
            "<h1>Hello " + number + "...!</h1>");
    }
}

참고로 현재 예제는 build.gradle에 설정을 추가하지 않고 간단하게 설명하기 위해서 매개 변수에서 @Param("number") int number 형태로 입력했습니다. build.gradle의 컴파일 옵션에 -parameters를 추가하면, @Param int number 형태로 입력할 수 있습니다.

위 코드처럼 어노테이션을 적용하면, methodA()GET "/"로 들어왔을 때 호출되고, methodB()GET "/page/<숫자>"로 들어왔을 때 호출됩니다. 이렇게 어노테이션을 사용해서 라우팅을 적용할 수 있는 클래스를 만들었다면, annotatedService() 메서드 매개 변수를 통해 클래스의 인스턴스가 전달되도록 아래처럼 코드를 수정합니다.

import com.linecorp.armeria.server.Server;
import com.linecorp.armeria.server.ServerBuilder;
import java.util.concurrent.CompletableFuture;
  
public class ServerMain {
    public static void main(String[] args) {
        ServerBuilder sb = new ServerBuilder();
        sb.http(8080);
  
        sb.annotatedService(new CustomService());
  
        Server server = sb.build();
        CompletableFuture<Void> future = server.start();
        future.join();
    }
}

실행하기

코드를 실행하고 http://127.0.0.1:8080/로 접속하면, 다음과 같이 "Hello Custom Service"라는 글을 출력합니다.

이번에는 http://127.0.0.1:8080/page/10로 들어가봅시다. 들어가면 다음과 같이 "Hello 10...!"이라는 글자를 출력합니다.

Armeria를 사용하면 Java로도 이렇게 간단하게 서버 라우팅을 구현할 수 있습니다.

지금까지 Armeria의 가장 기본적인 사용 방법에 대해 알아보았습니다. Armeria는 이 이외에도 기본적인 정적 파일 제공부터, Thrift 조합, Kafka를 활용한 로그 기록 등의 수 많은 기능을 제공합니다. Armeria와 관련된 자세한 내용은 공식 홈페이지를 참고해주세요.

Related Post