5.2 GET API 만들기
GET API : 웹 애플리케이션 서버에서 값을 가져올 때 사용하는 API
@RestController
@RequestMapping("/api/v1/get-api")
public class GetController {
}
컨트롤러에 @RestController와 @RequestMapping을 붙여 내부에 선언되는 메서드에서 사용할 공통 URL을 설정한다.
클래스 수준에서 @RequestMapping을 설정하면 내부에 선언한 메서드의 URL 리소스 앞에 @RequestMapping의 값이 공통 값으로 추가된다.
5.2.1 @RequestMapping으로 구현하기
@RequestMapping 어노테이션을 별다른 설정 없이 선언하면 HTTP의 모든 요청을 받는다.
GET 형식의 요청만 받기 위해서는 어노테이션에 별도 설정이 필요하다.
@RestController
@RequestMapping("/api/v1/get-api")
public class GetController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String getHello() {
return "Hello World";
}
}
@RequestMapping 어노테이션의 method 요소의 값을 RequestMethod.GET으로 설정하면 요청 형식을 GET으로만 설정할 수 있다.
스프링 4.3 버전 이후로는 @RequestMapping 어노테이션은 더 이상 사용되지 않는다. → 대신 @GetMapping 등을 사용
5.2.2 매개변수가 없는 GET 메서드 구현
@RestController
@RequestMapping("/api/v1/get-api")
public class GetController {
@GetMapping(value = "/name")
public String getName() {
return "Flature";
}
}
5.2.3 @PathVariable을 활용한 GET 메서드 구현
실무 환경에서는 매개변수를 받지 않는 메서드가 거의 쓰이지 않는다.
웹 통신의 기본 목적은 데이터를 주고받는 것이기 때문에 대부분 매개변수를 받는 메서드를 작성하게 된다.
매개변수를 받을 때 자주 쓰이는 방법 중 하나는 URL 자체에 값을 담아 요청하는 것이다.
@GetMapping(value = "/variable1/{variable}")
public String getVariable1(@PathVariable String variable) {
LOGGER.info("@PathVariable을 통해 들어온 값 : {}", variable);
return variable;
}
URL은 다음과 같다. → http://localhost:8080/api/v1/get-api/variable1/{String 값}
이 메서드는 중괄호로 표시된 위치의 값을 받아 요청한다. (실제 요청 시 중괄호는 들어가지 않으며 값만 존재함)
값을 간단히 전달할 때 주로 사용하는 방법이며, GET 요청에서 많이 사용된다.
이 방식으로 코드를 작성할 때는 몇 가지 지켜야 할 규칙이 있다.
- @GetMapping 어노테이션의 값으로 URL을 입력할 때 중괄호를 사용해 어느 위치에서 값을 받을지 지정해야 함
- 메서드의 매개변수와 그 값을 연결하기 위해 @PathVariable을 명시하여, @GetMapping 어노테이션과 @PathVariable에 지정된 변수의 이름을 동일하게 맞추어야 함
- @GetMapping 어노테이션에 지정한 변수의 이름과 메서드 매개변수의 이름을 동일하게 맞추기 어렵다면 @PathVariable 뒤에 괄호를 열어 @GetMapping 어노테이션의 변수명을 지정해야 함
@GetMapping(value = "/variable2/{variable}")
public String getVariable2(@PathVariable("variable") String var) {
return var;
}
5.2.4 @RequestParam을 활용한 GET 메서드 구현
URL 경로에 값을 담아 요청을 보내는 방법 외에도 쿼리 형식으로 값을 전달할 수도 있다.
즉, URI에서 '?'를 기준으로 우측에 '{키}={값}' 형태로 구성된 요청을 전송하는 방법이다.
이 같은 형식을 처리하려면 @RequestParam을 활용하면 된다.
@GetMapping(value = "/request1")
public String getRequestParam1(
@RequestParam String name,
@RequestParam String email,
@RequestParam String organization) {
return name + " " + email + " " + organization;
}
http://localhost:8080/api/v1/get-api/request2?
name=flature&email=thinkground.flature@gmail.com&organization=thinkground 와 같은 형식이 된다.
키와 @RequestParam 뒤에 적는 이름을 동일하게 설정하기 어렵다면 @PathVariable 예제에서 사용한 방법처럼 value 요소로 매핑한다.
만약 쿼리스트링에 어떤 값이 들어올지 모른다면 Map 객체를 활용할 수도 있다.
@GetMapping(value = "/request2")
public String getRequestParam2(@RequestParam Map<String, String> param) {
StringBuilder sb = new StringBuilder();
param.entrySet().forEach(map -> {
sb.append(map.getKey() + " : " + map.getValue() + "\n");
});
return sb.toString();
}
이런 형태로 코드를 작성하면 값에 상관없이 요청을 받을 수 있다.
e.g. 회원 가입 관련 API의 취미 선택 항목 (값을 기입하지 않을 가능성이 있는 경우)
5.2.5 DTO 객체를 활용한 GET 메서드 구현
DTO
- Data Transfer Object
- 다른 레이어 간의 데이터 교환에 활용
- 각 클래스 및 인터페이스를 호출하면서 전달하는 매개변수로 사용되는 데이터 객체
- 별도의 로직을 포함하지 않음
public class MemberDto {
private String name;
private String email;
private String organization;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getOrganization() {
return organization;
}
public void setOrganization(String organization) {
this.organization = organization;
}
@Override
public String toString() {
return "MemberDTO{" +
"name='" + name + '\'' +
", email='" + email + '\'' +
", organization='" + organization + '\'' +
'}';
}
}
DTO 클래스에는 전달하고자 하는 필드 객체를 선언하고 getter/setter 메서드를 구현한다. → 롬복 사용
DTO 클래스에 선언된 필드는 컨트롤러의 메서드에서 쿼리 파라미터의 키와 매핑된다.
즉, 쿼리스트링의 키가 정해져 있지만 받아야 할 파라미터가 많은 경우에는 DTO 객체를 활용하여 코드의 가독성을 높일 수 있다.
@GetMapping(value = "/request3")
public String getRequestParam3(MemberDto memberDTO) {
return memberDTO.toString();
}
5.3 POST API 만들기
POST API
- 웹 애플리케이션을 통해 데이터베이스 등의 저장소에 리소스를 저장할 때 사용되는 API
- 저장하고자 하는 리소스나 값을 HTTP 바디에 담아 서버에 전달
@RestController
@RequestMapping("/api/v1/post-api")
public class PostController {
}
5.3.1 @RequestMapping으로 구현하기
@RequestMapping(value = "/domain", method = RequestMethod.POST)
public String postExample() {
return "Hello Post API";
}
→ @PostMaiing 사용으로 대체
5.3.2 @RequestBody를 활용한 POST 메서드 구현
Body 영역에 작성되는 값은 일정한 형태를 취하는데, 일반적으로 JSON(JavaScript Object Notation) 형식으로 전송된다.
@PostMapping(value = "/member")
public String postMember(@RequestBody Map<String, Object> postData) {
StringBuilder sb = new StringBuilder();
postData.entrySet().forEach(map -> {
sb.append(map.getKey() + " : " + map.getValue() + "\n");
});
return sb.toString();
}
@RequestBody는 HTTP의 Body 내용을 해당 어노테이션이 지정된 객체에 매핑하는 역할을 한다.
Map 객체는 요청을 통해 어떤 값이 들어오게 될지 특정하기 어려울 때 주로 사용한다.
요청 메시지에 들어갈 값이 정해져 있다면 DTO 객체를 매개변수로 삼아 작성할 수 있다.
@PostMapping(value = "/member2")
public String postMemberDto(@RequestBody MemberDto memberDTO) {
return memberDTO.toString();
}
5.4 PUT API 만들기
PUT API
- 웹 애플리케이션 서버를 통해 데이터베이스 같은 저장소에 존재하는 리소스 값을 업데이트하는 데 사용
- 리소스를 서버에 전달하기 위해 HTTP Body를 활용해야 하기 때문에 구현 방법은 POST API와 거의 동일
@RestController
@RequestMapping("/api/v1/put-api")
public class PutController {
}
5.4.1 @RequestBody를 활용한 PUT 메서드 구현
// http://localhost:8080/api/v1/put-api/member
@PutMapping(value = "/member")
public String postMember(@RequestBody Map<String, Object> putData) {
StringBuilder sb = new StringBuilder();
putData.entrySet().forEach(map -> {
sb.append(map.getKey() + " : " + map.getValue() + "\n");
});
return sb.toString();
}
서버에 어떤 값이 들어올지 모르는 경우에는 Map 객체를 활용해값을 받을 수 있다.
대부분의 경우 API를 개발한 쪽에서 작성한 명세를 웹 사이트를 통해 클라이언트나 사용자에게 올바른 사용법을 안내한다.
만약 서버에 들어오는 요청에 담겨 있는 값이 정해여 있는 경우에는 DTO 객체를 활용해 구현한다.
// http://localhost:8080/api/v1/put-api/member1
@PutMapping(value = "/member1")
public String postMemberDto1(@RequestBody MemberDto memberDto) {
return memberDto.toString();
}
// http://localhost:8080/api/v1/put-api/member2
@PutMapping(value = "/member2")
public MemberDto postMemberDto2(@RequestBody MemberDto memberDto) {
return memberDto;
}
리턴 타입이 String일 때, context-type이 text/plain이기에 결과적으로 일반 문자열이 전달된다.
리턴 타입이 DTO일 때, context-type이 application/json이기에 JSON 형식으로 값을 변환하여 전달하게 된다.
5.4.2 ResponseEntity를 활용한 PUT 메서드 구현
HttpEntity는 헤더와 바디로 구성된 HTTP 요청과 응답을 구성하는 역할을 한다.
RequestEntity와 ResponseEntity는 HttpEntity를 상속받아 구현한 클래스인데, 그 중 ResponseEntity는서버에 들어온 요청에 대해 응답 데이터를 구성해 전달할 수 있게 한다.
ResponseEntity는 HttpEntity로부터 HttpHeaders와 Body를 가지고 자체적으로 HttpStatus를 구현한다.
이 클래스를 활용하면 응답 코드 변경은 물론 Header와 Body를 더욱 쉽게 구성할 수 있다.
// http://localhost:8080/api/v1/put-api/member3
@PutMapping(value = "/member3")
public ResponseEntity<MemberDto> postMemberDto3(@RequestBody MemberDto memberDto) {
return ResponseEntity
.status(HttpStatus.ACCEPTED)
.body(memberDto);
}
5.5 DELETE API 만들기
DELETE API
- 웹 애플리케이션 서버를 거쳐 데이터베이스 등의 저장소에 있는 리소스를 삭제할 때 사용
- GET 메서드와 같이 URI에 값을 넣어요청을 받는 형식으로 구현됨
@RestController
@RequestMapping("/api/v1/delete-api")
public class DeleteController {
}
5.5.1 @PathVariable과 @RequestParam을 활용한 DELETE 메서드 구현
// http://localhost:8080/api/v1/delete-api/{String 값}
@DeleteMapping(value = "/{variable}")
public String DeleteVariable(@PathVariable String variable) {
return variable;
}
마찬가지로 @DeleteMapping 어노테이션에 정의한 value의 이름과 메서드의 매개변수 이름을 동일하게 ㅓㄹ정해야 삭제할 값이 주입된다.
또는 @RequestParam 어노테이션을 통해 쿼리스트링 값도 받을 수있다.
// http://localhost:8080/api/v1/delete-api/request1?email=value
@DeleteMapping(value = "/request1")
public String getRequestParam1(@RequestParam String email) {
return "e-mail : " + email;
}
5.6 REST API 명세를 문서화하는 방법 - Swagger
Swagger 관련 내용은 따로 블로깅할 예정
5.7 로깅 라이브러리 - Logback
로깅
- 애플리케이션이 동작하는 동안 시스템의 상태나 동작 정보를 시간순으로 기록하는 것
- 비기능 요구사항이지만 문제 발생 시 원인 분석하는데 꼭 필요한 요소
- slf4j 활용(spring-boot-starter-web 라이브러리 내장)
Logback의 특징
- 크게 5개의 로그 레벨을 설정할 수 있음
- ERROR : 로직 수행 중에 시스템에 심각한 문제가 발생해서 애플리케이션의 작동이 불가능한 경우
- WARN : 시스템 에러의 원인이 될 수 있는 경고 레벨
- INFO : 애플리케이션의 상태 변경과 같은 정보 전달
- DEBUG : 애플리케이션의 디버깅을 위한 메시지 표시
- TRACE : DEBUG 레벨보다 더 상세한 메시지 표현
- 실제 운영 환경과 개발 환경에서 각각 다른 출력 레벨을 설정해서 로그를 확인할 수 있음
- Logback의 설정 파일을 일정 시간마다 스캔해서 애플리케이션을 재가동하지 않아도 설정을 변경할 수 있음
- 별도의 프로그램 지원 없이도 자체적으로 로그 파일을 압축할 수 있음
- 저장된 로그 파일에 대한 보관 기간 등을 설정해서 관리할 수 있음
5.7.1 Logback 설정
Appender 영역
로그의 형태를 설정하고 어떤 방법으로 출력할지를 설정하는 영역
- ConsoleAppender : 콘솔에 로그 출력
- FileAppender : 파일에 로그 저장
- RollingFileAppender : 여러 개의 파일을 순회하면서 로그 저장
- SMTPAppender : 메일로 로그 전송
- DBAppender : DB에 로그 저장
encoder 요소를통해 로그의 표현 형식을 패턴으로 정의할 수 있다.
패턴 | 의미 |
%Logger{length} | 로거의 이름 |
%-level | 로그 레벨. -5는 출력 고정폭의 값 |
%msg(%message) | 로그 메시지 |
%d | 로그 기록 시간 |
%p | 로깅 레벨 |
%F | 로깅이 발생한 애플리케이션 파일명 |
%M | 로깅이 발생한 메서드 이름 |
%I | 로깅이 발생한 호출지의 정보 |
%thread | 현재 스레드명 |
%t | 로깅이 발생한 스레드명 |
%c | 로깅이 발생한 카테고리 |
%C | 로깅이 발생한 클래스명 |
%m | 로그 메시지 |
%n | 줄바꿈 |
%r | 애플리케이션 실행후 로깅이 발생한 시점까지의 시간 |
%L | 로깅이 발생한 호출 지점의 라인 수 |
Root 영역
설정 파일에 정의된 Appender를 활용하려면 Root 영역에서 Appender를 참조해서 로깅 레벨을 설정한다.
만약 특정 패키지에 대해 다른로깅레벨을 설정하고 싶다면 root 대신 logger를 사용해 아래와 같이 지정할 수 있다.
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="INFO_LOG"/>
</root>
또는
<logger name="com.springboot.api.controller" level="DEBUG" additivity="false">
<appender-ref ref="console"/>
<appender-ref ref="INFO_LOG"/>
</logger>
logger 요소의 name 속성에는 패키지 단위로 로깅이 적용될 범위를 지정하고 level 속성으로 로그 레벨을 지정한다.
additivity 속성은 앞에서 지정한 패키지 범위에 하위 패키지를 포함할지 여부를 결정한다.
기본값은 true이며, 이 경우 하위 패키지를 모두 포함한다.
'Spring > 스프링 부트 핵심 가이드' 카테고리의 다른 글
[Spring] Ch.6 데이터베이스 연동 (0) | 2024.04.08 |
---|---|
[Spring] Ch.2 개발에 앞서 알면 좋은 기초 지식 (2) | 2024.03.24 |
[Spring] Ch.1 스프링 부트란? (1) | 2024.03.24 |