오늘 만들 것
지난 글에서 이미 Hello, World!를 출력해보았습니다.
하지만 이번 글에서도 Hello, World!를 출력하려고 합니다.
다른 점이 있다면 이번엔 API의 형태로 데이터를 주고 받겠습니다.
GET http://localhost:8080/greeting와 같이 API를 요청하면,
{ “id”: 1, ”content”: ”Hello, World!” }의 형태로 반환해보겠습니다.
이번 글의 상당 부분은 Pivotal Software에서 제공하는 Spring 가이드(Getting Started · Building a RESTful Web Service) 를 번역하여 작성했습니다.
시작하기 전에: DAO? DTO? VO?
Spring에 대해 조사해 본 적이 있다면 DAO나 DTO, VO 등의 용어를 접해봤을 겁니다.
이 세 용어는 객체의 유형을 뜻합니다.
앞으로도 꾸준히 사용되는 용어이기 때문에 한 차례 정리합니다.
DTO: Data Transfer Object
DTO는 데이터 교환을 위한 객체입니다.
로그인 과정을 예로 들면 Client와 API Server 사이, API Server와 DB Server 사이에 회원 정보를 전달할 때 사용됩니다.
DTO는 보통 멤버 변수와 Getter, Setter로 이루어져 있습니다.
DTO를 사용함으로서 데이터 캡슐화가 가능하고, 각 계층에서 데이터를 안전하게 다룰 수 있습니다.
VO: Value Object
어떠한 값을 정의하기 위해 쓰입니다.
DTO에 읽기 전용 속성이 적용되었다고 이해하시면 됩니다.
읽기 전용이기 때문에 Getter 기능만 존재합니다.
DAO: Data Access Object
DB의 데이터에 접근할 때 사용하는 객체입니다.
DAO는 미리 DB와 Connection을 생성한 후 필요할 때 사용합니다.
때문에 내부에 DB와 데이터를 주고 받는 내용이 정의되어 있습니다.
예를 들어 회원가입을 한다면 아래와 같이 호출할 수 있습니다.
private final userDAO = new UserDAO(); // 사용자 DAO 생성 (DB연결)
private final newUser = new UserDTO(); // 새 사용자 생성
userDAO.insertUser(newUser); // DAO에게 새 사용자 INSERT 요청
DTO, VO, DAO의 사용법은 앞으로 게시판 서버를 구현하며 파악할 수 있습니다.
지금은 ‘이런 것이 있구나’하고 넘어가도 됩니다.
프로젝트 생성
지난 글을 참고해서 프로젝트를 생성하시면 됩니다.
단, 이번 글에서는 API 서버를 생성할 예정이기 때문에 View 페이지가 필요하지 않습니다.
Thymeleaf는 빼고 아래 세 패키지에 대한 의존성만 추가합니다.
DTO 생성
서버에서 클라이언트로 데이터를 전달하기 위해 DTO를 정의합니다.
다음 경로에 아래 코드를 작성하여 저장합니다: /src/main/java/YOUR/DOMAIN/ARTIFACT/dto/GreetingDTO.java
package net.jetalab.spreinglab02.dto; // 자신의 프로젝트에 맞게 수정합니다.
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@AllArgsConstructor // this.var = var; 와 같은 생성자를 자동으로 생성한다.
@Getter // getId(), getContent() 등의 Getter를 자동으로 생성한다.
@Setter // setId(id), setContent(content) 등의 Setter를 자동으로 생성한다.
public class GreetingDTO {
private long id; // 방문 ID
private String content; // 인삿말
}
이 클래스는 방문자의 ID와 방문자에게 전송할 인삿말을 정의합니다.
앞서 DTO에는 Getter와 Setter가 필요하다고 말씀드렸습니다.
하지만 위 코드에는 관련 메소드가 정의되지 않은 것처럼 보여집니다.
사실 7번째 줄부터 9번째 줄의 Annotation이 Getter 메소드와 Setter 메소드, 그리고 Constructor를 정의해줍니다.
우리가 프로젝트를 생성할 때 의존성으로 추가했던 Lombok이 선언을 대신 해준겁니다.
Lombok에 대한 자세한 내용은 아래 링크에서 확인할 수 있습니다.
Lombok을 이용해 Constructor/Getter/Setter/Builder에서 벗어나는법 :: 삐멜 소프트웨어 엔지니어
Controller 생성
사용자 요청을 처리하고 응답을 전송하는 컨트롤러를 정의합니다.
다음 경로에 아래 코드를 작성하여 저장합니다: /src/main/java/YOUR/DOMAIN/ARTIFACT/controller/GreetingController.java
package net.jetalab.spreinglab02.controller; // 자신의 프로젝트에 맞게 수정합니다.
import net.jetalab.spreinglab02.dto.GreetingDTO; // 자신의 프로젝트에 맞게 수정합니다.
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.atomic.AtomicLong;
@RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@RequestMapping("/greeting")
public GreetingDTO greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
return new GreetingDTO(counter.incrementAndGet(), String.format(template, name));
}
}
꽤 간단해보이는 예제이지만 알찬 코드입니다.
차근차근 설명하겠습니다.
- 11번째 줄
@RestController Annotation을 사용하면 간단하게 RESTful 웹서비스를 위한 컨트롤러를 정의할 수 있습니다.
Spring 4에서 추가된 @RestController Annotation은 기존의 @Controller와 @ResponseBody를 생략할 수 있게 해줍니다.
RESTful 웹 서비스를 간편하게 구축할 수 있도록 해준다고 이해하면 될 것 같습니다. - 17번째 줄
@RequestMapping Annotation을 사용해 /greeting로의 요청을 greeting() 메소드와 매핑합니다.
위 예제에서는 HTTP 요청 메소드를 지정하지 않았기 때문에 모든 메소드에 대해 응답합니다.
@RequestMapping(method=GET)과 같이 선언하면 지정한 메소드에 대해서만 응답하도록 할 수 있습니다. - 18번째 줄 ~ 20번째 줄: greeting()
@RequestParam은 QueryString 파라미터를 정의합니다.
위 예제에서는 QueryString 파라미터 name의 값을 String형 변수 name에 담습니다.
만약 name 파라미터가 없다면 defaultValue에 지정된 "World"가 담깁니다.
greeting() 메소드는 id와 content를 멤버 변수로 가지고 있는 새 GreetingDTO 객체를 만들어 반환합니다.
이 때, HTTP 응답으로 GreetingDTO 객체의 데이터가 JSON 형태로 전달됩니다.
Spring의 MappingJackson2HttpMessageConverter가 Jackson 2를 이용해 Greeting 인스턴스를 자동으로 JSON으로 변환해주기 때문에 직접 변환할 필요가 없습니다.
디렉토리 구조
두 파일을 생성한 후 아래 이미지와 같은 구조를 가지게 됩니다.
/src/main/java 이하를 보시면 됩니다.
테스트
IDE 우측 상단의 ▶ Run 버튼을 누르거나 키보드의 ^R(Control + R) 버튼을 눌러서 서버를 실행합니다.
정상적으로 실행되면 Postman이나 curl 등의 도구를 이용해 다음 API를 호출합니다: http://localhost:8080/greeting
아래와 같은 응답을 받을 수 있습니다.
다음과 같이 QueryString 파라미터 name을 추가하면 이전과 다른 응답을 받을 수 있습니다: http://localhost:8080/greeting?name=jETA
예제 코드
본 포스트의 본 포스트의 예제 코드는 GitHub에 공개되어 있습니다.
https://github.com/jETA-Kor/sp-re-ing/tree/master/lab02
참고 문서
- Getting Started · Building a RESTful Web Service
- JAVA DAO, DTO, VO 차이
- 프로그래머의 블로그 :: DAO / VO / DTO란?
- OKKY - 스프링 웹 mvc에서 각 모듈별로 디렉토리 구조 어떻게 짤까요?
- Lombok을 이용해 Constructor/Getter/Setter/Builder에서 벗어나는법 :: 삐멜 소프트웨어 엔지니어