REST API
[클라이언트들]
PC / 모바일 / 태블릿 / IoT ...
│
│ HTTP 요청
▼
+------------------+
| REST API 서버 |
+------------------+
│ JSON 응답(뷰X, 데이터O) //서버는 화면이 아닌 데이터(JSON) 를 돌려준다.
▼
자원(RESOURCE): /articles
├─ GET /articles → 글 목록 조회
├─ GET /articles/{id} → 글 단건 조회
├─ POST /articles → 글 생성
├─ PATCH /articles/{id} → 글 부분 수정
└─ DELETE /articles/{id} → 글 삭제
JSON
{
"name": "망고",
"breeds": "골든리트리버",
"age": 2
}
연습용으로 Talend API Tester 이용해 HTTP 요청을 보내고 돌아온 응답을 확인하겠습니다. {JSON} Placeholder 사이트에서 자원들을 JSON 형식으로 받아 실습해 보겠습니다!
HTTP 상태 코드
HTTP 상태 코드는 클라이언트가 보낸 요청이 성공/실패했는지 알려주는 코드다. 코드는 100번대부터 500번대까지 5개 그룹으로 나눠 있다.
자세한 내용: MDN 웹 문서
| 1XX (정보) | 요청이 수신돼, 처리 중 |
|---|---|
| 2XX (성공) | 요청이 정상적으로 처리됨 |
| 3XX (리다이렉션) | 요청을 완료하려면 추가 행동이 필요함 |
| 4XX (클라이언트 요청 오류) | 클라이언트의 요청이 잘못됨 |
| 5XX (서버 응답 오류) | 서버 내부에 에러가 발생해 클라이언트의 요청에 답할 수 없음 |
GET 요청하고 응답받기

POST 요청하고 응답받기

PATCH 요청하고 응답받기

DELETE 요청하고 응답받기

Part 2에서 만든 firstproject를 이용해 데이터를 CRUD하기 위해 REST API를 구현해 보겠습니다!
GET 구현하기
// GET
@GetMapping("/api/articles")
public List<Article> index() {
return articleRepository.findAll();
}
@GetMapping("/api/articles/{id}")
public Article show(@PathVariable Long id) {
return articleRepository.findById(id).orElse(null);
}
POST 구현하기
- 주의점:
@RequestBody추가해야 본문(BODY)을 받아 올 수 있다!
// POST
@PostMapping("/api/articles")
public Article create(@RequestBody ArticleForm dto) {
Article article = dto.toEntity();
return articleRepository.save(article);
}
PATCH 구현하기
- 주의점: 일부만 수정한 경우:
patch메소드를 만들어야 한다
// PATCH
@PatchMapping("/api/articles/{id}")
public ResponseEntity<Article> update(@PathVariable Long id, @RequestBody ArticleForm dto) {
// 1. DTO -> 엔티티 변환하기
Article article = dto.toEntity();
// 2. 타깃 조회하기
Article target = articleRepository.findById(id).orElse(null);
// 3. 잘못된 요청 처리하기
if(target == null || id != article.getId()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
// 4. 업데이트 및 정상 응답(200)하기
target.patch(article); // 기존 데이터에 새 데이터 붙이기
Article updated = articleRepository.save(target); // 수정 내용 DB에 최종 저장
return ResponseEntity.status(HttpStatus.OK).body(updated);
}
DELETE 구현하기
// DELETE
@DeleteMapping("/api/articles/{id}")
public ResponseEntity<Article> delete(@PathVariable Long id) {
// 1. 대상 찾기
Article target = articleRepository.findById(id).orElse(null);
// 2. 잘못된 요청 찾기
if(target == null) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
// 3. 대상 삭제하기
articleRepository.delete(target);
return ResponseEntity.status(HttpStatus.OK).build();
}
ex:
이전 11장까지 만든 코드를 요청해 보겠습니다!
수정한 부분:
service package 만들기ArticleApiController 수정하기ex:
// controller
// POST
@PostMapping("/api/articles")
public Article create(ArticleForm dto) {
Article article = dto.toEntity();
return articleRepository.save(article);
}
// controller
// POST
@PostMapping("/api/articles")
public ResponseEntity<Article> create(@RequestBody ArticleForm dto) {
Article created = articleService.create(dto);
return (created != null) ?
ResponseEntity.status(HttpStatus.OK).body(created) :
ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
// service
public Article create(ArticleForm dto) {
Article article = dto.toEntity();
if (article.getId() != null) {
return null;
}
return articleRepository.save(article);
}
ArticleApiController @PostMapping("/api/transaction-test")
public ResponseEntity<List<Article>> transactionTest (@RequestBody List<ArticleForm> dtos) {
List<Article> createdList = articleService.createArticles(dtos);
return (createdList != null) ?
ResponseEntity.status(HttpStatus.OK).body(createdList) :
ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
ArticleService
@Transactional
public List<Article> createArticles(List<ArticleForm> dtos) {
// 1. dto 묶음을 엔티티 묶음으로 변환하기
List<Article> articleList = dtos.stream()
.map(dto -> dto.toEntity())
.collect(Collectors.toList());
// 2. 엔티티 묶음을 DB에 저장하기
articleList.stream()
.forEach(article -> articleRepository.save(article));
// 3. 강제 예외 발생시키기
articleRepository.findById(-1L)
.orElseThrow(() -> new IllegalArgumentException("결제 실패!"));
// 4. 결과 값 반환하기
return articleList;
}
→ @Transactional로 메서드 전체를 하나의 트랜잭션으로 묶어 DTO 목록을 엔티티로 변환해 저장한 뒤, 의도적으로 예외를 던져 롤백이 일어나는지 확인한다. 예외가 발생하면 이전에 저장된 작업이 모두 취소되어 DB에 반영되지 않는다!
테스트 코드는 보통 3단계로 작성한다:
메소드 오른쪽 누르고 Generate → Test 선택. JUnit5로 테스트를 만들겠습니다!

ArticleSeriveTest 생성 위치를 확인할 수 있다

테스트 코드 작성하기:
@SpringBootTest
class ArticleServiceTest {
@Autowired
ArticleService articleService;
@Test
void index() {
// 1. 예상 데이터
Article a = new Article(1L, "가가가가", "1111");
Article b = new Article(2L, "나나나나", "2222");
Article c = new Article(3L, "다다다다", "3333");
List<Article> expected = new ArrayList<Article>(Arrays.asList(a,b,c));
// 2. 실제 데이터
List<Article> articles = articleService.index();
// 3. 비교 및 검증
assertEquals(expected.toString(), articles.toString());
}
@Test
void show_성공() {
// 1. 예상 데이터
Long id = 1L;
Article expected = new Article(id, "가가가가", "1111");
// 2. 실제 데이터
Article article = articleService.show(id);
// 3. 비교 및 검증
assertEquals(expected.toString(), article.toString());
}
@Test
void show_실패() {
// 1. 예상 데이터
Long id = -1L;
Article expected = null;
// 2. 실제 데이터
Article article = articleService.show(id);
// 3. 비교 및 검증
assertEquals(expected, article);
}
}