📗 API 와 REST API
API
: 클라이언트-서버 간 상호작용하기 위한 매개체
⇨ 클라이언트 요청을 서버에 전달
⇨ 서버 결과물을 클라이언트에게 전달
📍 REST API (참고)
자원을 이름으로 구분해 자원의 상태를 주고받는 API 방식
- 웹 장점을 최대 활용한 API
- REST : Representational State Transfer
▷ 특징
- 서버/클라이언트 구조
- 무상태
- 캐시 처리 가능
- 계층화
- 인터페이스 일관성
▷ 장단점
👍
- URL만 보고도 무슨 행동하는 API인지 확인 가능
- 클라이언트와 서버 역할이 명확히 분리 (∵ 상태가 없으므로)
- HTTP 표준 사용하는 모든 플랫폼에서 사용 가능
👎
- HTTP 메소드 개수에 제한 있음 (GET, POST)
- 설계 위해 공식적으로 제공되는 표준 규약이 없음
▷ REST API 사용법
1. URL에 동사 X. 대신 자원 표시
예) id가 1인 학생 정보를 가져와라.
⇨ /students/1 (O)
⇨ /get-studnet?student_id=1 (X)
2. 동사는 HTTP 메소드로
: POST, GET, PUT, DELETE (추가, 조회, 수정, 삭제)
📗 블로그 개발 위한 엔티티 구성하기
📍 프로젝트 준비하기
- 아래글 따라하기
[SpringBoot] 00장 개발 환경 구축하기
- IntelliJ 23.02 - JDK: 17 - Build System: Gradle 🧩 Gradle vs Maven 소스 코드를 이용해 실행 가능한 애플리케이션을 생성하는 과정을 자동화하는 프로그램 - 의존성 내려받기, 코드 패키징, 컴파일, 테스트
bono039.tistory.com
📍 엔티티 구성하기
✱ 일련번호 : id BIGINT NOT NULL PRIMARY KEY (기본키)
✱ 게시물 제목 : title VARCHAR NOT NULL
✱내용 : content VARCHAR(255) NOT NULL
➕ 롬복에서 지원하는 애너테이션
@Builder
- 빌더 패턴 방식으로 객체 생성
- 👍 : 어느 필드에 어떤 값이 들어가는지 확인 가능, 가독성 좋음
(*빌더 패턴 : 객체를 유연하고 직관적으로 생성하는 디자인 패턴)
// 빌더 패턴 사용 X
new Article("abc", "def")
// 빌더 패턴 사용
Article.builder()
.title("abc")
.content("def")
.build();
@Getter
- 별도 코드 없이 모든 필드에 대한 접근자 메소드 생성
- 필드값 가져오는 게터 메소드들 (getTitle() 등)을 @NoArgsConstructor 애너테이션으로 대치
@NoArgsConstructor
- 별도 코드 없이 접근 제어자가 protected인 기본 생성자 생성
📍 리포지토리 만들기
📗 "블로그 글 작성" 위한 API 구현하기 ⭐
* 요청 : ③ 테스트 (POSTMAN 사용) / ④ 테스트 코드 작성 (BlogControllerTest.java)
📍 서비스 메소드 코드 작성하기 (AddArticleRequest.java)
DTO
: 계층끼리 데이터를 교환하가 위해 사용하는 객체
(+ DAO : DB와 연결되어 데이터를 조회하고 수정하는 데 사용하는 객체 - 비즈니스 로직 포함)
toEntity()
: 빌더 패턴 사용해 DTO -> 엔티티로 만드는 메소드
@RequiredArgsConstructor
: final 키워드나 @NotNull 이 붙은 필드의 생성자 추가
@Service
: 해당 클래스를 빈으로 서블릿 컨테이너에 등록
save() 메소드
: JpaRepository에서 지원하는 저장 메소드. AddArticleRequest 클래스에 저장된 값들을 article DB에 저장하는 역할
📍 컨트롤러 메소드 코드 작성하기 (BlogApiController.java)
- URL과 매핑하기 위한 컨트롤러 메소드
- 예) @GetMapping, @PostMapping, @PutMapping, @DeleteMapping ⇨ HTTP 메소드에 대응
@RestController
- HTTP 응답으로 객체 데이터를 JSON 형식으로 반환
@PostMapping
: HTTP 메소드가 POST일 때 요청받은 URL과 동일한 메소드로 매핑
@RequestBody
: HTTP를 요청할 때 응답에 해당하는 값을
@RequestBody에 애너테이션이 붙은 대상 객체인 AddArticleRequest에 매핑
@ResponseEntity.status().body()
: 응답 코드로 201 Created를 응답하고, 테이블에 저장된 객체 반환
📍 API 실행 테스트하기 (BlogApiController.java)
1. H2 콘솔을 활성화하자! ⇨ application.yml 파일 수정
spring:
jpa:
# 전송 쿼리 확인
show-sql: true
properties:
hibernate:
format_sql: true
# 테이블 생성 후, data.sql 실행
defer-datasource-initialization: true
# H2 서버 활성화
datasource:
url: jdbc:h2:mem:testdb
h2:
console:
enabled: true
2. 스프링 서버 부트 실행하고, POSTMAN 실행하고 아래와 같이
[POST http://localhost:8080/api/articles]
[raw] [JSON]
3. 웹 브라우저에서 localhost:8080/h2-console로 접속 (스프링 부트 서버는 켜두고!)
⇨ 스프링 부트 서버 안에 내장된 H2 데이터베이스에 접속해 데이터 확인 가능
📍 반복 작업 줄일 테스트 코드 작성하기
1. BlogApiController.java, [Alt + Enter] - [Create Test]
@SpringBootTest // 테스트용 애플리케이션 컨텍스트
@AutoConfigureMockMvc // MockMVC 생성 및 자동 구성
class BlogApiControllerTest {
@Autowired
protected MockMvc mockMvc;
@Autowired
protected ObjectMapper objectMapper; // 직렬화, 역직렬화 위한 클래스
@Autowired
private WebApplicationContext context;
@Autowired
BlogRepository blogRepository;
@BeforeEach
public void mockMvcSetUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
.build();
blogRepository.deleteAll();
}
}
@ObjectMapper
- 직렬화 / 역직렬화 할 때 사용
* 직렬화 : 자바 시스템 내부에서 사용되는 자바 객체를 외부에서 사용하도록 JSON 데이터로 변환하는 작업
* 역직렬화 : 외부에서 사용하는 JSON 데이터 ➞ 자바 객체 형태로 변환 (자바에서 사용하기 위해)
2. 블로그 글 생성 API 테스트 코드 작성하기 (코드)
➔ given : 블로그 글 추가에 필요한 요청 객체 생성
➔ when : 블로그 글 추가 API에 요청 보냄 (Type : JSON)
➔ then : 1) 응답 코드가 201 Created인지 확인, 2) 블로그를 전체 조회해 크기가 1인지 확인, 3) 실제로 저장된 데이터와 요청 값 비교
- writeValueAsString() : 객체를 JSON으로 직렬화
- contentType() : 요청 보낼 때 JSON, XML 등 다양한 타입 중 하나로 골라 요청 보냄
- assertThat() 관련 메소드 : https://bibi6666667.tistory.com/231
📗 "블로그 글 목록 조회" 위한 API 구성하기
📍 서비스 메소드 코드 작성하기 (BlogService.java)
@RequiredArgsConstructor // final이나 @NotNull이 붙은 필드의 생성자 추가
@Service // 빈으로 등록
public class BlogService {
private final BlogRepository blogRepository;
/*** ... ***/
// [블로그 글 목록 조회 메소드] DB에 저장된 글 모두 가져옴.
public List<Article> findAll() {
return blogRepository.findAll();
}
}
📍 컨트롤러 메소드 코드 작성하기
findAllArticles() : GET 요청이 오면 글 목록 조회
1. DTO 작성 (ArticleResponse.java)
@Getter
public class ArticleResponse {
private final String title;
private final String content;
// 생성자 (엔티티를 인수로 받음)
public ArticleResponse(Article article) {
this.title = article.getTitle();
this.content = article.getContent();
}
}
2. BlogApiController.java 파일에 findAllArticles() 메소드 추가
@RequiredArgsConstructor
@RestController // HTTP Response Body에 객체 데이터를 JSON 형식으로 반환하는 컨트롤러
public class BlogApiController {
private final BlogService blogService;
/* ... */
@GetMapping("/api/articles")
public ResponseEntity<List<ArticleResponse>> findAllArticles() {
List<ArticleResponse> articles = blogService.findAll()
.stream()
.map(ArticleResponse::new)
.toList();
return ResponseEntity.ok()
.body(articles);
}
}
- /api/articles GET 요청이 오면, 글 전체를 조회하는 메소드인 findAll() 메소드를 호출하고,
응답용 객체인 ArticleResponse로 파싱해 body에 담아 클라이언트에게 전송
3. 실행 테스트
- [resources - data.sql] 파일 생성
INSERT INTO article (title, content) VALUES ('제목 1', '내용 1')
INSERT INTO article (title, content) VALUES ('제목 2', '내용 2')
INSERT INTO article (title, content) VALUES ('제목 3', '내용 3')
- POSTMAN에서 아래와 같이 실행하면 초록색 결과 나옴
📍 테스트 코드 작성하기 (BlogApiControllerTest.java)
➔ given : 블로그 글 저장
➔ when : 목록 조회 API 호출
➔ then : 응답 코드가 200 OK & 반환받은 값 中 0번째 요소의 content와 title이 저장된 값과 같은지 확인
📗 "블로그 글 단건 조회" 위한 API 구성하기
📍 서비스 메소드 코드 작성하기 (BlogService.java)
- findById() 메소드 추가
public Article findById(long id) {
return blogRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("not found : " + id));
}
➔ findById 메소드 사용해 ID 받아 조회하고, 없으면 IllegalArgumentException 예외 발생시킨다는 의미
📍 컨트롤러 메소드 코드 작성하기 (BlogApiController.java)
- findArticle() 메소드 추가
// URL 경로에서 값 추출
@GetMapping("/api/articles/{id}")
public ResponseEntity<ArticleResponse> findArticle(@PathVariable long id) {
Article article = blogService.findById(id);
return ResponseEntity.ok()
.body(new ArticleResponse(article));
}
➔ @PathVariable : URL에서 값 가져오는 애너티에션
📍 테스트 코드 작성하기 (BlogApiControllerTest.java)
📗 "블로그 글 삭제" 위한 API 구성하기
📍 서비스 메소드 코드 작성하기 (BlogService.java)
public void delete(long id) {
blogRepository.deleteById(id);
}
📍 컨트롤러 메소드 코드 작성하기 (BlogApiController.java)
@DeleteMapping("/api/articles/{id}")
public ResponseEntity<Void> deleteArticle(@PathVariable long id) {
blogService.delete(id);
return ResponseEntity.ok()
.build();
}
📍 실행 테스트하기
- POSTMAN에서 HTTP 메소드를 [DELETE]로 설정하고 URL은 http://localhost:8080/api/articles/1
을 입력해 1번 글을 삭제한다.
- POSTMAN에서 HTTP 메소드를 [GET]으로 변경하고 글 전체 목록을 확인했을 때 1번 글은 삭제되어 2개만 뜨는 것을 볼 수 있다.
📍 테스트 코드 작성하기 (BlogApiControllerTest.java)
📗 "블로그 글 수정" 위한 API 구성하기
📍 서비스 메소드 코드 작성하기 (BlogService.java)
1) Article.java에 update 메소드 추가
public void update(String title, String content) {
this.title = title;
this.content = content;
}
2) 블로그 글 수정 요청 받을 DTO 작성 (UpdateArticleRequest.java)
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class UpdateArticleRequest {
private String title;
private String content;
}
3) BlogService.java 파일 열어 리포지토리 사용해 글 수정하는 메소드 추가
@Transactional
public Article update(long id, UpdateArticleRequest request) {
Article article = blogRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("not found : " + id));
article.update(request.getTitle(), request.getContent());
return article;
}
@Transactional
: 매칭한 메서드를 하나의 트랜잭션으로 묶는 역할
(* 트랜잭션 : DB의 데이터를 바꾸기 위해 묶은 작업 단위)
📍 컨트롤러 메소드 코드 작성하기 (BlogApiController.java)
@PutMapping("/api/articles/{id}")
public ResponseEntity<Article> updateArticle(@PathVariable long id,
@RequestBody UpdateArticleRequest request) {
Article updatedArticle = blogService.update(id, request);
return ResponseEntity.ok()
.body(updatedArticle); // body에 담아 전송
}
➔ /api/articles/{id} PUT 요청이 오면 실행
📍 실행 테스트하기
- POSTMAN에서 HTTP 메소드를 [PUT]으로 설정하고 URL은 http://localhost:8080/api/articles/1 을 입력해 1번 글을 수정하고 [Send]를 눌러 요청한다.
글 전체 목록에서 확인해봐도 수정된 사항을 확인할 수 있다.
📍 테스트 코드 작성하기
(해당 글 내용은 📗 스프링 부트 3 백엔드 개발자 되기 - 자바 편을 읽고 정리한 내용입니다.)
'📚 관련 독서 > 스프링 부트 3 백엔드 개발자 되기 - 자바 편' 카테고리의 다른 글
[SpringBoot] 08장 스프링 시큐리티 ver. 로그인/로그아웃, 회원 가입 구현 (0) | 2024.02.13 |
---|---|
[SpringBoot] 07장 블로그 화면 구성하기 (타임리프) (0) | 2024.02.08 |
[SpringBoot] 05장 데이터베이스 조작이 편해지는 ORM (0) | 2023.08.16 |
[SpringBoot] 04장 스프링 부트 3와 테스트 (0) | 2023.08.11 |
[SpringBoot] 03장 스프링 부트 3 구조 이해하기 (0) | 2023.08.11 |