본문으로 바로가기

글 목록 조회 API 만들어 보기 - 5

category Spring boot 2024. 10. 2. 20:37
학습 목표

1. 글 전체 조회 기능을 만들 수 있다.
2. 응답 처리시 공통 DTO를 설계할 수 있다.
3. ExceptionHandler 개념을 이해하고 코드를 구성할 수 있다.
BlogService 파일에 게시글 전체 조회 기능 추가 하기 - 1
package com.example.demo._domain.blog.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo._domain.blog.dto.ArticleDTO;
import com.example.demo._domain.blog.entity.Article;
import com.example.demo._domain.blog.repository.PostRepository;

import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service // IoC (빈으로 등록)
public class BlogService {

	@Autowired // DI <--- 개발자들이 가독성 때문에 작성을 해 준다.
	private final PostRepository postRepository;

	@Transactional // 쓰기 지연 처리 까지
	public Article save(ArticleDTO dto) {
		// 비즈니스 로직이 필요하다면 작성 ...
		return postRepository.save(dto.toEntity());
	}

	// 전체 게시글 조회 기능
	public List<Article> findAll() {
		List<Article> articles = postRepository.findAll();
		return articles;
	}

}
 
 
공통 응답 DTO 만들기 - 2
package com.example.demo.common;

public class ApiUtil<T> {
	
	private Integer status; // 응답 상태 코드 저장 (200, 400, 500)
	private String msg;  // 응답 메시지 저장(성공, 실패 문자열) 
	private T body;  // 응답의 실제 데이터 저장(제네릭 활용) 
	
	// 성공적인 응답을 반환 할 때 사용하는 생성자 
	public ApiUtil(T  body) {
		this.status = 200; // 통신 성공 
		this.msg = "성공";  
		this.body = body;   
	}
	
	// 커스텀 상태코드와 메시지를 반환 시킬 때 사용하는 생성자 (예 : 에러 응답) 
	public ApiUtil(Integer status, String msg) {
		this.status = status; 
		this.msg = msg;
		this.body = null;
	}
	
	
}

 

 
 
BlogController 코드 추가 - 3
package com.example.demo._domain.blog.controller;

import java.util.List;

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo._domain.blog.dto.ArticleDTO;
import com.example.demo._domain.blog.entity.Article;
import com.example.demo._domain.blog.service.BlogService;
import com.example.demo.common.ApiUtil;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@RestController // @controller + @responsebody
public class BlogApiController {
	
	private final BlogService blogService;
		
	// URL , 즉, 주소 설계 - http://localhost:8080/api/article
	@PostMapping("/api/articles")
	public ResponseEntity<Article> addArticle(@RequestBody ArticleDTO dto) {
		// 1. 인증 검사
		// 2. 유효성 검사 
		Article savedArtilce = blogService.save(dto);
		return ResponseEntity.status(HttpStatus.CREATED).body(savedArtilce);
	}
	
	
	// URL , 즉, 주소 설계 - http://localhost:8080/api/articles
	@GetMapping(value = "/api/articles", produces = MediaType.APPLICATION_JSON_VALUE)
	public ApiUtil<List<Article>>  getAllArticles() {
		List<Article> articles = blogService.findAll();
		if(articles.isEmpty()) {
			return new ApiUtil<>(204, "조회된 게시글이 없습니다");
		}
		return new ApiUtil<>(articles);
	}
	
}

 

 
 

 

ExceptionHandler 만들기 - 4

 

 

 
사용자 정의 예외 클래스 만들기
package com.example.demo.common.errors;

public class Exception400 extends RuntimeException {
	
	public Exception400(String msg) {
		super(msg);
	}
}​

 

 
 
ExceptionHandler 만들기
package com.example.demo.common;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import com.example.demo.common.errors.Exception400;
import com.example.demo.common.errors.Exception401;
import com.example.demo.common.errors.Exception403;
import com.example.demo.common.errors.Exception404;
import com.example.demo.common.errors.Exception500;

// RuntimeException 터지면 해당 파일로 오류가 모인다. 
@RestControllerAdvice // C.A --> 뷰 에러페이, R.C.A 데이터 반환 (에러)
public class MyExceptionHandler {

	private static final Logger logger = LoggerFactory.getLogger(MyExceptionHandler.class);

	@ExceptionHandler(Exception400.class)
	public ResponseEntity<ApiUtil<Object>> ex400(Exception400 e) {
		logger.error("400 Error: {}", e.getMessage());
		ApiUtil<Object> apiUtil = new ApiUtil<>(400, e.getMessage());
		return new ResponseEntity<>(apiUtil, HttpStatus.BAD_REQUEST);
	}

	@ExceptionHandler(Exception401.class)
	public ResponseEntity<ApiUtil<Object>> ex401(Exception401 e) {
		logger.error("401 Error: {}", e.getMessage());
		ApiUtil<Object> apiUtil = new ApiUtil<>(401, e.getMessage());
		return new ResponseEntity<>(apiUtil, HttpStatus.UNAUTHORIZED);
	}

	@ExceptionHandler(Exception403.class)
	public ResponseEntity<ApiUtil<Object>> ex403(Exception403 e) {
		logger.error("403 Error: {}", e.getMessage());
		ApiUtil<Object> apiUtil = new ApiUtil<>(403, e.getMessage());
		return new ResponseEntity<>(apiUtil, HttpStatus.FORBIDDEN);
	}

	@ExceptionHandler(Exception404.class)
	public ResponseEntity<ApiUtil<Object>> ex404(Exception404 e) {
		logger.error("404 Error: {}", e.getMessage());
		ApiUtil<Object> apiUtil = new ApiUtil<>(404, e.getMessage());
		return new ResponseEntity<>(apiUtil, HttpStatus.NOT_FOUND);
	}

	@ExceptionHandler(Exception500.class)
	public ResponseEntity<ApiUtil<Object>> ex500(Exception500 e) {
		logger.error("500 Error: {}", e.getMessage());
		ApiUtil<Object> apiUtil = new ApiUtil<>(500, e.getMessage());
		return new ResponseEntity<>(apiUtil, HttpStatus.INTERNAL_SERVER_ERROR);
	}

}
 
 

 

Controller 코드 수정
package com.example.demo._domain.blog.controller;

import java.util.List;

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo._domain.blog.dto.ArticleDTO;
import com.example.demo._domain.blog.entity.Article;
import com.example.demo._domain.blog.service.BlogService;
import com.example.demo.common.ApiUtil;
import com.example.demo.common.errors.Exception400;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@RestController // @controller + @responsebody
public class BlogApiController {
	
	private final BlogService blogService;
		
	// URL , 즉, 주소 설계 - http://localhost:8080/api/article
	@PostMapping("/api/articles")
	public ResponseEntity<Article> addArticle(@RequestBody ArticleDTO dto) {
		// 1. 인증 검사
		// 2. 유효성 검사 
		Article savedArtilce = blogService.save(dto);
		return ResponseEntity.status(HttpStatus.CREATED).body(savedArtilce);
	}
	
	
	// URL , 즉, 주소 설계 - http://localhost:8080/api/articles
	@GetMapping(value = "/api/articles", produces = MediaType.APPLICATION_JSON_VALUE)
	public ApiUtil<?> getAllArticles() {
		List<Article> articles = blogService.findAll();
		if(articles.isEmpty()) {
			// return new ApiUtil<>(new Exception400("게시글이 없습니다."));
			throw new Exception400("게시글이 없습니다.");
		}
		return new ApiUtil<>(articles);
	}
	
}

 

'Spring boot' 카테고리의 다른 글

템플릿 엔진이란 뭘까?  (0) 2024.10.04
글 상세보기(조회) API 구현 - 6  (0) 2024.10.02
서비스, 컨트롤러 만들어 보기 - 4  (0) 2024.10.02
레포지토리 만들기 - 3  (3) 2024.10.02
블로그 엔티티 만들기 - 2  (1) 2024.10.01